Last active
August 29, 2015 13:56
-
-
Save AmauryCarrade/9038110 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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