Example #1
0
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))
Example #2
0
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')
Example #3
0
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)))
Example #4
0
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.")
Example #5
0
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
Example #6
0
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"