def main(): sys.excepthook = show_exception_and_exit try: inputROM = sys.argv[1] except IndexError: pass # root = tk.Tk() # root.withdraw() # inputROM = filedialog.askopenfilename(filetypes=[("GBA files",".gba"),("All files",".*")],initialdir=os.getcwd(),title="Select ROM to rip data from") (dirname, filename) = os.path.split(inputROM) # if (dirname): # moduleList = glob.glob(dirname + '/**/*.nmm',recursive=True) #a list of all nightmare modules in the directory # else: # moduleList = glob.glob('**/*.nmm',recursive=True) moduleList = glob.glob('**/*.nmm',recursive=True) for inputNMM in moduleList: outputname = inputNMM.replace(".nmm",".csv") #let's just keep the same file name for now # print("Module:\t" + inputNMM) # print("Output:\t" + outputname) try: nmm = nightmare.NightmareTable(inputNMM) with open(inputROM, 'rb') as rom_file: rom = bytes(rom_file.read()) process(nmm, rom, outputname) except AssertionError as e: print("Error in " + inputNMM+":\n" + str(e))
def addToInstaller(csvList,installername): """Takes a list of csv files and adds them to the EA installer""" with open(installername,"w") as myfile: myfile.write("//EA Table Installation file generated by c2ea.exe\n\n") myfile.write('#include "Table Definitions.txt"\n\n') for csv in csvList: nmpath = csv.replace(".csv",".nmm") nmm = nightmare.NightmareTable(nmpath) filename = csv.replace(".csv",".event") #I don't wanna use .txt because it conflicts, but this is supposed to be a text file! filename = os.path.relpath(filename, os.path.dirname(installername)) # filename.replace(os.getcwd()+'\\','') with open(installername,'a') as myfile: #myfile.write("ORG " + hex(nmm.offset) + '\n') Don't put the offset here, have it in the dmp. myfile.write('#include "' + filename + '"\n\n')
def gen_lines(csvName, nmmName): nmm = nightmare.NightmareTable(nmmName) with open(csvName, 'r') as csvFile: table = csv.reader(csvFile) tableName = next(table)[0] yield '.global {}'.format(tableName) yield '{}:'.format(tableName) for row in table: if len(row) < (nmm.colNum + 1): sys.exit("{} contains a row with not enough entries!".format( csvName)) for entry, data in zip(nmm.columns, row[1:]): if (data == None) or (data == ''): sys.exit("{} contains a blank cell!".format(csvName)) if entry.length == 4: yield '.4byte {}'.format(data) elif entry.length == 2: yield '.2byte {}'.format(data) elif entry.length == 1: yield '.byte {}'.format(data) else: # Handle arbitrary field size bytelist = int(data, 0).to_bytes(entry.length, 'little', signed=entry.signed) yield '.byte {}'.format(', '.join( map(lambda x: '0x{:X}'.format(x), bytelist)))
def main(): import argparse sys.excepthook = showExceptionAndExit parser = argparse.ArgumentParser(description = 'Convert NMM files to CSV files using a ROM as reference.') # Input options parser.add_argument('rom', nargs='?', help = 'reference ROM.') parser.add_argument('-f', '--folder', help = 'folder to search for NMMs in.') # Entry List output options parser.add_argument('-e', '--enums', action = 'store_true', help = 'translates entry lists to C enums.') parser.add_argument('-d', '--defines', action = 'store_true', help = 'translates entry lists to defines.') parser.add_argument('-a', '--assigns', action = 'store_true', help = 'translates entry lists to `name = id` expressions.') args = parser.parse_args() if args.rom == None: import tkinter as tk from tkinter import filedialog root = tk.Tk() root.withdraw() args.rom = filedialog.askopenfilename( title = "Select ROM to rip data from", initialdir = os.getcwd(), filetypes = [ ("GBA files", ".gba"), ("All files", ".*") ] ) # generating module list if args.folder == None: moduleList = glob.glob('**/*.nmm', recursive = True) else: moduleList = glob.glob(args.folder + '/**/*.nmm', recursive = True) enableGenerateEntryLists = args.enums or args.defines or args.assigns # read ROM bytes with open(args.rom, 'rb') as f: romBytes = bytes(f.read()) for nmmFile in moduleList: csvFile = nmmFile.replace(".nmm", ".csv") #let's just keep the same file name for now try: nmm = nightmare.NightmareTable(nmmFile) except AssertionError as e: # NMM is malformed print("Couldn't parse NMM `{}`:\n {}".format(nmmFile, str(e))) continue if enableGenerateEntryLists: # Regen entry names to make them suitable as C/EA identifers nmm.entryNames = [x for x in genIdentifierEntries(nmm.entryNames)] entryFile = nmmFile.replace('.nmm', '.def') # Write entry list file with open(entryFile, 'w') as f: if args.enums: f.write('enum {\n') f.writelines(genEntryDefinitions(nmm, getEnumEntryDefinition)) f.write('};\n') elif args.defines: f.writelines(genEntryDefinitions(nmm, getDefineEntryDefinition)) elif args.assigns: f.writelines(genEntryDefinitions(nmm, getAssignEntryDefinition)) print("Wrote to `{}`".format(entryFile)) # Write CSV with open(csvFile, 'w') as f: wr = csv.writer(f, quoting = csv.QUOTE_ALL, lineterminator = '\n') wr.writerows(genTableRows(nmm, romBytes)) print("Wrote to `{}`".format(csvFile)) input("Press Enter to continue.")
def process(inputCSV, inputNMM, filename, rom): """Takes a csv and spits out an EA macro file (.event, but actually text). Requires a nmm with the same name in the same folder.""" #is it possible to tell if it's inline? global TABLE_INLINED macroName = "_C2EA_{}".format( os.path.split(os.path.splitext(inputCSV)[0])[1].replace(' ', '_')) nmm = nightmare.NightmareTable(inputNMM) rompath = rom macroArgs = [] #params for macro macroOutput = '' #expanded macro form outputlines = [] originalOffset = nmm.offset tableOffset = originalOffset #this gets changed to whatever is in the first cell of the csv actually currlen = '' #because we start with BYTE fillwithzero = None for x in range(nmm.colNum): argx = "arg" + '{0:03d}'.format(x) macroArgs.append(argx) #arg000, arg001, arg002 etc up to 1000 columns. arglen = getArgLength( nmm.columns[x]) #this gets the appropriate length string. if arglen != currlen: #only append if needed else just add arg. if currlen != '': #this should only be on the first line macroOutput += ';' #assuming arglen does not equal currlen macroOutput += (arglen + argx + ' ') currlen = arglen else: macroOutput += (argx + ' ') with open(inputCSV, 'r') as myfile: table = csv.reader(myfile) tableOffset = next(table)[0] for row in table: outputline = "{}(".format(macroName) items = zip(nmm.columns, row[1:]) for entry, data in items: thisentry = '' #output.extend(int(data, 0).to_bytes(entry.length, 'little', signed=entry.signed)) if data == '': if fillwithzero == None: fillwithzero = input( "Warning: " + inputCSV + " has a blank cell.\nContinue anyway? Fills cells with '0' (y/n)" ).strip().lower() == 'y' if fillwithzero == True: data = '0' else: input("Press Enter to quit.") sys.exit(-1) try: arglen = getArgLength(entry) if (arglen == "WORD ") | (arglen == "SHORT "): outputline += data + ',' else: dt = int(data, 0).to_bytes( entry.length, 'little', signed=entry.signed) #this is a string i guess for byte in dt: thisentry += (hex(byte) + ' ') outputline += thisentry[:-1] + ',' except ValueError: #if it's not a number, just add it directly outputline += (data + ',') outputline = outputline[:-1] + ')' outputlines.append(outputline) with open(filename, 'w') as dumpfile: inline = False dumpfile.write("#define {}(".format(macroName)) dumpfile.write( ','.join(macroArgs)) #turns list into 'arg000,arg001' etc dumpfile.write(') "') dumpfile.write(macroOutput + '"\n\n') #e.g. BYTE arg000, WORD arg001, etc if tableOffset.strip()[0:6] == "INLINE": from c2eaPfinder import pointerOffsets TABLE_INLINED = True if rompath == None: import tkinter as tk root = tk.Tk() root.withdraw() from tkinter import filedialog rompath = filedialog.askopenfilename( filetypes=[("GBA files", ".gba"), ("All files", ".*")], initialdir=os.getcwd(), title="Select ROM to use for repointing") label = tableOffset.replace("INLINE", '').strip() # Here we do *not* want to use PFinder dumpfile.write("PUSH\n") for offset in pointerOffsets(rompath, originalOffset | 0x8000000): dumpfile.write("ORG ${:X}\n".format(offset)) dumpfile.write("POIN {}\n".format(label)) dumpfile.write("POP\n") # There, much better :) dumpfile.write("ALIGN 4\n{}:\n".format(label)) inline = True else: dumpfile.write("PUSH\nORG " + tableOffset + "\n") dumpfile.write('\n'.join(outputlines)) if not inline: dumpfile.write("\nPOP") #dumpfile.write('\n}\n') print("Wrote to " + filename) return rompath
def processed_lines(csvName, nmmName, romName): """Takes a csv and generates corresponding event lines. Requires a nmm with the same name in the same folder.""" macroName = "_C2EA_{}".format( os.path.split(os.path.splitext(csvName)[0])[1].replace(' ', '_')) nmm = nightmare.NightmareTable(nmmName) currentLength = 0 # because we start with nothing macroArgs = [] macroCodes = [] for i, entry in enumerate(nmm.columns): argString = "_ARG{:03d}".format(i) macroArgs.append(argString) if entry.length != currentLength: currentLength = entry.length macroCodes.append(get_entry_ea_code(entry)) macroCodes[len(macroCodes) - 1] += ' {}'.format(argString) yield '#define {0}({1}) "{2}"'.format(macroName, ",".join(macroArgs), ";".join(macroCodes)) with open(csvName, 'r') as myfile: table = csv.reader(myfile) offsetCell = next(table)[0] inline = False repoint = False tableName = '' try: offset = int(offsetCell, base=0) # offsetCell contains a number yield "PUSH" yield "ORG ${:X}".format(offset) except ValueError: # offsetCell does not contain a number inline = True if offsetCell.strip()[0:6] == "INLINE": tableName = offsetCell[6:].strip() repoint = True else: tableName = offsetCell.strip() repoint = False yield 'ALIGN 4' yield '{}:'.format(tableName) for row in table: if len(row) < (nmm.colNum + 1): sys.exit("{} contains a row with not enough entries!".format( csvName)) line = "{}(".format(macroName) for entry, data in zip(nmm.columns, row[1:]): # output.extend(int(data, 0).to_bytes(entry.length, 'little', signed=entry.signed)) if data == '': sys.exit( "ERROR: `{}` contains a blank cell!".format(csvName)) # If it is a standard entry type, we can allow complex expressions and what not if (entry.length == 4) or (entry.length == 2) or (entry.length == 1): line += '({}),'.format(data) # If it is another kind of entry, we need to unpack the cell value into individula bytes else: bytelist = int(data, base=0).to_bytes(entry.length, 'little', signed=entry.signed) line += ' '.join(map(lambda x: '${:X}'.format(x), bytelist)) line += ',' yield line[:-1] + ')' if inline and repoint: yield "PUSH" for offset in pfind.pointer_offsets(romName, 0x8000000 | nmm.offset): yield "ORG ${:X}".format(offset) yield "POIN {}".format(tableName) if repoint or not inline: yield "POP"