Skip to content

Instantly share code, notes, and snippets.

@AmauryCarrade
Last active August 29, 2015 13:56
Show Gist options
  • Save AmauryCarrade/9038110 to your computer and use it in GitHub Desktop.
Save AmauryCarrade/9038110 to your computer and use it in GitHub Desktop.
#!/usr/bin/python
import sys, os;
from path import path;
from datetime import datetime;
if len(sys.argv) < 2:
print('Usage: listIds.py <configDirectory> [reportFile] [lineBreaksInReportFile] [debugLevel]');
print('');
print('reportFile: a path where the report will be stored. If it is blank, the report will be saved in a file called report, in the config directory.');
print('An unexisting file will be created. An existing file will be overwritten.');
print('');
print('lineBreaksInReportFile: if it is set to true, the report will contains only one ID per line. All other value will be interpreted as "false". Default false.');
print('');
print('debugMode: 0 (output nothing except errors), 1 (a few data), 2 (more data), 3 (full debug). Default 1.');
print('');
sys.exit(1);
"""
Known bugs
- railcraft.cfg: some non-id numeric values in a "blocks" section. Unsolvable except with specific exceptions.
- forge multipart: ids of blocks are out of any section.
- forestry/pipes.conf: ids of items are out of any section.
"""
sectionsBlocks = ['blocks', 'block', 'camoblock', 'camoblocks'];
sectionsItems = ['items', 'item', 'camoitem', 'camoitems', '"multipart item ids"'];
vanillaBlocksIDs = range(174); # 1.6
# vanillaBlocksIDs = range(176) # 1.7
vanillaItemsIDs = range(256, 422) + range(2256, 2267); # 1.6 (second range for discs)
# vanillaItemsIDs = range(256, 423) + range(2256, 2267); # 1.7 (second range for discs)
completeSectionBlocks = [str(name) + '{' for name in sectionsBlocks];
completeSectionItems = [str(name) + '{' for name in sectionsItems];
configDir = path.getcwd() / path(sys.argv[1]);
if configDir.isdir() == False:
print('The configuration directory does not exists.');
sys.exit(1);
try:
reportFilePath = path.getcwd() / sys.argv[2];
except IndexError:
reportFilePath = configDir / 'report';
try:
if sys.argv[3].lower() == 'true':
lineBreaksInReportFile = True;
else:
lineBreaksInReportFile = False;
except:
lineBreaksInReportFile = False;
try:
debug = int(sys.argv[4]);
except:
debug = 1;
configFiles = list(configDir.walkfiles());
## List of IDs used
listOfIDsItems = {};
listOfIDsBlocks = {};
for vanillaId in vanillaBlocksIDs:
listOfIDsBlocks[vanillaId] = {'files':['vanilla']};
for vanillaId in vanillaItemsIDs:
listOfIDsItems[vanillaId] = {'files':['vanilla']};
if debug >= 3:
print('Initial lists (vanilla):')
print(' > Blocks: ' + str(listOfIDsBlocks));
print(' > Items: ' + str(listOfIDsItems));
inBlock = False;
deepInBlock = 0; # 0 out of an interesting block, 1 directly in the block, 2 in a sub block...
currentBlock = None;
for configFile in configFiles:
if str(configFile).endswith('.lang'): # Some config files are for languages.
if debug >= 2: print('File ' + str(configFile.realpath().normpath()) + ' skipped.');
continue;
if str(configFile).endswith('~'): # Archive.
if debug >= 2: print('File ' + str(configFile.realpath().normpath()) + ' skipped.');
continue;
if debug >= 2: print('Parsing ' + str(configFile.realpath().normpath()) + '...');
deepInBlock = 0; # reset
config = configFile.lines();
for line in config:
line = line.replace(' ', '').replace("\t", '').replace("\n", '');
if line in completeSectionBlocks and inBlock == False: # Starting an interesting section
if deepInBlock == 0: # some sections are named "blocks" or others, but are not
inBlock = True;
currentBlock = 'blocks';
deepInBlock += 1;
if debug >= 3: print(' > Section: ' + line + ' (deep ' + str(deepInBlock) + ')');
elif line in completeSectionItems and inBlock == False: # Ibid
inBlock = True;
currentBlock = 'items';
deepInBlock += 1;
if debug >= 3: print(' > Section: ' + line + ' (deep ' + str(deepInBlock) + ')');
elif line.endswith('{') and (line.startswith('#') == False): # New section of any kind
deepInBlock += 1;
if debug >= 3 and inBlock == True: print(' > Start of a subsection (deep ' + str(deepInBlock) + '): ' + line);
elif line == '}': # End of a section of any kind
deepInBlock -= 1;
if deepInBlock == 0 and inBlock == True: # End of an interesting section
inBlock = False;
currentBlock = None;
if debug >= 3: print(' > End of an interesting section');
else:
if debug >= 3: print(' > End of a section (deep ' + str(deepInBlock) + ')');
elif inBlock == True: # In an interesting section, or in the root "section" (because some mods place IDs out of any section)
# Comment? Empty line?
firstChar = None;
try:
firstChar = line[0];
except IndexError:
firstChar = None;
if(firstChar != '#' and line != ''): # A "real" line
if debug >= 3: print(' >> Line: ' + line);
try:
ID = line.split('=')[1];
# Real ID? Sometime, there are other things than IDs in these sections.
realId = True;
try:
int(ID);
except ValueError: # int(ID) return this error if ID can't be converted into an integer
realId = False;
if ID == '':
realId = False;
if(realId == True):
clearNameConfigFile = str(configFile.realpath().normpath()).replace(configDir.realpath().normpath(), '');
if currentBlock == 'blocks':
if ID in listOfIDsBlocks: # duplicate
listOfIDsBlocks[ID]['files'].append(clearNameConfigFile);
else: # not a duplicate (currently at least)
listOfIDsBlocks[ID] = {'files':[clearNameConfigFile]};
else:
if ID in listOfIDsItems: # duplicate
listOfIDsItems[ID]['files'].append(clearNameConfigFile);
else: # not a duplicate (currently at least)
listOfIDsItems[ID] = {'files':[clearNameConfigFile]};
except IndexError: # Can happend when there is a subcategory in a section (fail on the first line)
pass;
## List of IDs availables
# The maximal ID of a block is 4096.
blocksAvailables = range(4097);
for ID in listOfIDsBlocks.keys():
if ID in blocksAvailables:
blocksAvailables.pop(blocksAvailables.index(ID));
maxUsedBlockId = int(max(listOfIDsBlocks));
# The maximal ID of an item is not defined. In the list, the availables IDs under the maximal used ID.
maxItemId = int(max(listOfIDsItems.keys()));
itemsAvailables = range(maxItemId + 1);
for ID in listOfIDsItems:
if ID in itemsAvailables:
itemsAvailables.pop(itemsAvailables.index(ID));
if debug >= 1: print('Extraction of duplicated IDs...');
duplicatedItems = {};
duplicatedBlocks = {};
for ID in listOfIDsBlocks.keys():
if(len(listOfIDsBlocks[ID]['files']) > 1):
duplicatedBlocks[ID] = listOfIDsBlocks[ID];
for ID in listOfIDsItems.keys():
if(len(listOfIDsItems[ID]['files']) > 1):
duplicatedItems[ID] = listOfIDsItems[ID];
## Display, save
def displayDict(dictionnary, indentLevel = 0):
"""
Print a dictionnary with new lines and indentation.
indentLevel: the initial indent level, in spaces.
"""
display = '';
for key in dictionnary.keys():
display += "\n" + (' ' * indentLevel) + str(key) + ":";
if type(dictionnary[key]) == dict:
display += displayDict(dictionnary[key], indentLevel + 4);
else:
display += "\n" + (' ' * (indentLevel + 4)) + repr(dictionnary[key]);
return display;
if debug >= 3: print('Duplicated blocks (raw):');
if debug >= 3: print(displayDict(duplicatedBlocks));
if debug >= 3: print('');
if debug >= 3: print('Duplicated items (raw):');
if debug >= 3: print(displayDict(duplicatedItems));
exit;
if debug >= 2: print('');
if debug >= 1: print('Parsing done. ' + str(len(listOfIDsBlocks)) + ' blocks and ' + str(len(listOfIDsItems)) + ' items found.');
if debug >= 1: print('');
if debug >= 1: print('Writing report file...')
if debug >= 2: print('');
try:
reportFilePath.dirname().makedirs_p(); # Create the directory, if needed
reportFile = reportFilePath.open('w');
report = 'Report for ' + str(configDir.realpath().normpath()) + "\n"
report += "Date: " + datetime.now().strftime('%d/%m/%Y at %H:%M:%S') + "\n";
report += "Generated using https://gist.github.com/AmauryCarrade/9038110";
report += "\n\n\nConflicts (blocks)\n======================================\n\n";
report += displayDict(duplicatedBlocks);
report += "\n\n\nConflicts (items)\n======================================\n\n";
report += displayDict(duplicatedItems);
report += "\n\n\nAvaliables (blocks)\n======================================\n\n";
for ID in blocksAvailables:
report += str(ID);
if(lineBreaksInReportFile):
report += "\n";
else:
report += ' ';
report += "\nAll IDs from " + str(maxUsedBlockId) + " to 4096 are free.";
report += "\n\n\nAvaliables (items)\n======================================\n\n";
for ID in itemsAvailables:
report += str(ID);
if(lineBreaksInReportFile):
report += "\n";
else:
report += ' ';
report += "\n... and all IDs up to " + str(maxItemId) + '.';
report += "\n\n\nUsed (blocks)\n======================================\n\n";
for ID in listOfIDsBlocks:
report += str(ID);
if(lineBreaksInReportFile):
report += "\n";
else:
report += ' ';
report += "\n\n\nUsed (items)\n======================================\n\n";
for ID in listOfIDsItems:
report += str(ID);
if(lineBreaksInReportFile):
report += "\n";
else:
report += ' ';
report += "\n\nStatistics\n======================================\n\n";
report += str(len(listOfIDsBlocks)) + ' IDs (blocks)' + "\n";
report += str(len(listOfIDsItems)) + ' IDs (items)' + "\n";
reportFile.write(report);
reportFile.close();
if debug >= 1: print('Report written successfully in ' + str(reportFilePath.realpath().normpath()) + '.');
sys.exit(0);
except Exception as e:
print('An error occured while writing the report file. Are the file and the directory writables?');
print(e);
sys.exit(1);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment