def open_rom(self, rom: Union[NintendoDSRom, BinaryIO, bytes, str, ndspy.rom.NintendoDSRom]): rom_last_filename = self.rom_last_filename if isinstance(rom, str): # Filename rom_last_filename = rom rom = NintendoDSRom.fromFile(rom) elif isinstance(rom, BinaryIO): # File rom = NintendoDSRom(rom.read()) elif isinstance(rom, bytes): # Raw bytes rom = NintendoDSRom(rom) elif isinstance(rom, ndspy.rom.NintendoDSRom): rom = NintendoDSRom(rom.save()) elif isinstance(rom, NintendoDSRom): rom = rom # Load language from arm9 if rom.name == b"LAYTON2": arm9 = rom.loadArm9() lang_address = 0x02000d3c - arm9.ramAddress lang_id = rom.arm9[lang_address] lang_table = ["jp", "en", "sp", "fr", "it", "ge", "du", "ko", "ch"] try: conf.LANG = lang_table[lang_id] except IndexError: # US version? # TODO: Figure out how to read it properly conf.LANG = "en" print(f"Game language: {conf.LANG}") if conf.LANG == "jp": error_dialog = wx.MessageDialog( self, "Japanese is not currently supported", style=wx.ICON_ERROR | wx.OK) error_dialog.ShowModal() return else: warning_game_dialog = wx.MessageDialog( self, "Warning: LaytonEditor is specifically made to edit " "Layton 2, and support with other games is not guaranteed.", style=wx.ICON_WARNING | wx.OK) warning_game_dialog.ShowModal() conf.LANG = "en" # After checking language self.rom = rom self.rom_last_filename = rom_last_filename RomSingleton(rom=self.rom) # Only open the main filesystem page. self.le_editor_pages.DeleteAllPages() menus_to_remove = [] for menu in self.le_menu.Menus: if menu[1] != "File": menus_to_remove.append(menu[1]) for menu_to_remove in menus_to_remove: self.remove_menu(menu_to_remove) self.open_filesystem_page("Rom FS")
def insertIntoRom(files, path, compress=False, username=None): """ Insert a CodeFiles object into a rom. """ with open(path, 'rb') as f: romData = f.read() rom = ndspy.rom.NintendoDSRom(romData) if compress: print('Compressing ARM9') rom.arm9 = files.arm9.save(compress) # ARM7 is never compressed. rom.arm7 = files.arm7.save() for id, ov in files.arm9Overlays.items(): if compress: print(f'Compressing overlay {id} / {max(files.arm9Overlays)}') rom.files[ov.fileID] = ov.save(compress) rom.arm9OverlayTable = files.makeArm9Ovt() rom.arm7OverlayTable = files.makeArm7Ovt() # Try to overwrite an existing BUILDTIME file if possible. # Sadly, we can't insert a new BUILDTIME without potentially messing # up existing file IDs. buildtimeData = files.makeBuildtime(username=username) if 'BUILDTIME' in rom.filenames: rom.files[rom.filenames['BUILDTIME']] = buildtimeData newRomData = rom.save() with open(path, 'wb') as f: f.write(newRomData)
def open_rom(self, rom: Union[NintendoDSRom, BinaryIO, bytes, str, ndspy.rom.NintendoDSRom]): if isinstance(rom, str): # Filename self.rom_last_filename = rom self.rom = NintendoDSRom.fromFile(rom) elif isinstance(rom, BinaryIO): # File self.rom = NintendoDSRom(rom.read()) elif isinstance(rom, bytes): # Raw bytes self.rom = NintendoDSRom(rom) elif isinstance(rom, ndspy.rom.NintendoDSRom): self.rom = NintendoDSRom(rom.save()) elif isinstance(rom, NintendoDSRom): self.rom = rom RomSingleton(rom=self.rom) # Only open the main filesystem page. self.le_editor_pages.DeleteAllPages() menus_to_remove = [] for menu in self.le_menu.Menus: if menu[1] != "File": menus_to_remove.append(menu[1]) for menu_to_remove in menus_to_remove: self.remove_menu(menu_to_remove) self.open_filesystem_page("Rom FS")
def makeImages(): imgFNs = [ ('ts-0', '3089 BASE.enpg'), ('ts-1', '3090 WORLD1.enpg'), ('ts-2', '3091 WORLD2.enpg'), ('ts-3', '3092 WORLD3.enpg'), ('ts-4', '3093 WORLD4.enpg'), ('ts-5', '3094 WORLD5.enpg'), ('ts-6', '3095 WORLD6.enpg'), ('ts-7', '3096 WORLD7.enpg'), ('ts-8', '3097 WORLD8.enpg'), ] FIRST_FILE_ID = 3089 imgs = [] for fn, _ in imgFNs: imgs.append(QtGui.QImage(f'{fn}.png')) imgs[0] = addVersionNumber(imgs[0]) converted = convertAllToEnpg(imgs) with open('Newer Super Mario Bros. DS.nds', 'rb') as f: rom = ndspy.rom.NintendoDSRom(f.read()) for i, ((fn, gamefn), enpg) in enumerate(zip(imgFNs, converted)): compressed = ndspy.lz10.compress(enpg) with open(f'out-enpg/{gamefn}', 'wb') as f: f.write(enpg) with open(f'out-enpg-lz/{gamefn}', 'wb') as f: f.write(compressed) rom.files[FIRST_FILE_ID + i] = compressed enpgToImage(enpg).save(f'out-enpg-png/{gamefn}.png') with open('Newer Super Mario Bros. DS.nds', 'wb') as f: f.write(rom.save())
def makeImages(): resources = loadResources() with open('config.json', 'r', encoding='utf-8') as f: config = json.load(f, object_pairs_hook=collections.OrderedDict) with open('Newer Super Mario Bros. DS Orig.nds', 'rb') as f: rom = ndspy.rom.NintendoDSRom(f.read()) for i in range(2128, 2488): rom.filenames['zc_crsin'].files[i - rom.filenames['zc_crsin'].firstID] = f'{i} Dummy' rom.files[i] = b'DUMMY' fileIdMap = {} def fileIdGen(): fid = 2128 while True: yield fid fid += 2 fileId = fileIdGen() # Make the top-screen images bottomImagesToMake = [] topPairs = [] for levelName, levelConfig in config['levels'].items(): theme = config['themes'][levelConfig['theme']] main, aux = makeTopScreenIntroGraphics( resources = resources, title = levelConfig.get('title'), name = levelConfig.get('name'), background1 = hex2QColor(theme['background1']), background2 = hex2QColor(theme['background2']), banner1 = hex2QColor(theme['banner1']), banner2 = hex2QColor(theme['banner2']), preview = QtGui.QPixmap('previews/' + levelConfig['preview']), ) topId = next(fileId) mainFn = '%d_%s_main' % (topId, levelName) auxFn = '%d_%s_aux' % (topId + 1, levelName) saveImagePair(main, aux, mainFn, auxFn, rom, topId) topPairs.append([mainFn, auxFn]) bottomImageId = (levelConfig['theme'], levelConfig['bottom']) if bottomImageId not in bottomImagesToMake: bottomImagesToMake.append(bottomImageId) fileIdMap[(levelConfig['world'] - 1) * 24 + levelConfig['number']] = \ (topId, bottomImagesToMake.index(bottomImageId)) # Make the bottom-screen images bottomFileIds = [] bottomPairs = [] for i, (themeName, btm) in enumerate(bottomImagesToMake): print(f'Rendering bottom-screen graphics for {btm.split(".")[0]}' f' with the "{themeName}" theme ({i+1}/{len(bottomImagesToMake)})...') theme = config['themes'][themeName] main, aux = makeBottomScreenIntroGraphics( resources = resources, background1 = hex2QColor(theme['background1']), background2 = hex2QColor(theme['background2']), banner1 = hex2QColor(theme['banner1']), banner2 = hex2QColor(theme['banner2']), icon = QtGui.QPixmap('bottoms/' + btm), ) btmId = next(fileId) bottomFileIds.append(btmId) mainFn = '_'.join([str(btmId), 'btm', btm.split('.')[0], themeName, 'main']) auxFn = '_'.join([str(btmId + 1), 'btm', btm.split('.')[0], themeName, 'aux']) saveImagePair(main, aux, mainFn, auxFn, rom, btmId) bottomPairs.append([mainFn, auxFn]) print('Saving everything...') fileIdData = [0] * 2 * (max(fileIdMap) + 1) for idx, (topId, btmIdx) in fileIdMap.items(): fileIdData[idx * 2] = topId fileIdData[idx * 2 + 1] = bottomFileIds[btmIdx] fileIdBytes = struct.pack('<%dH' % len(fileIdData), *fileIdData) fileIdBytesComp = ndspy.lz10.compress(fileIdBytes) with open('fileIDs.nerds', 'wb') as f: f.write(fileIdBytes) with open('fileIDs.nerds.lz', 'wb') as f: f.write(fileIdBytesComp) rom.files[2127] = fileIdBytesComp rom.filenames['zc_crsin'].files[0] = f'2127 fileIDs.nerds' with open('conversionInfo.json', 'w', encoding='utf-8') as f: json.dump({'top': topPairs, 'bottom': bottomPairs}, f) with open('Newer Super Mario Bros. DS.nds', 'wb') as f: f.write(rom.save()) print('Done! :D')
def replace(rom_path, i_path, save_path, confirm=True, only_modified=False, debug_log=True): # Re-inserts files! rom = ndspy.rom.NintendoDSRom.fromFile(rom_path) if debug_log: print(rom) if confirm: inp = input("Insert ROM Contents from \"" + os.path.abspath(i_path) + "\" into \"" + os.path.abspath(rom_path) + "\" and save to file \"" + os.path.abspath(save_path) + "\"? [y/n]") if inp != "y": print("Insertion cancelled!") return for i, file in enumerate(rom.files): try: rom_internal_file_name = rom.filenames[i] #print(rom_internal_file_name) path_wf = i_path + rom_internal_file_name #print(path_wf) if os.path.isfile(path_wf): with open(path_wf, 'rb') as f: bin_file = f.read() if only_modified: h = hashlib.md5(bin_file).hexdigest() h_romfile = hashlib.md5(file).hexdigest() if not (h == h_romfile): # Replace File! rom.setFileByName(rom_internal_file_name, bin_file) if debug_log: print("[Inserted] " + rom_internal_file_name + " <- " + path_wf) else: pass #print("File hasn't been modified!") else: rom.setFileByName(rom_internal_file_name, bin_file) if debug_log: print("[Inserted] " + rom_internal_file_name + " <- " + path_wf) else: pass #print("File skipped - File doesn't exist!") except KeyError: if debug_log: print("[Ignored] ID " + str(i) + " has no filename!") if debug_log: print("Saving rom...") with open(save_path, 'wb') as f: f.write(rom.save()) if debug_log: print("Done!")
def main(): """ Main function for FNT Tool. """ parser = argparse.ArgumentParser( description='FNT Tool: Extract or insert filename tables to or' ' from ROM or NARC files, either as binary or JSON' ' files.') parser.add_argument('infile', help='the file to be converted') parser.add_argument('outfile', help='the output filename') group = parser.add_mutually_exclusive_group(required=True) group.add_argument('-ib', '--input_bin', action='store_true', help='the input file is a raw filename table file') group.add_argument('-ij', '--input_json', action='store_true', help='the input file is a JSON file') group.add_argument('-ir', '--input_rom', action='store_true', help='the input file is a ROM') group.add_argument('-in', '--input_narc', action='store_true', help='the input file is a NARC file') group = parser.add_mutually_exclusive_group(required=True) group.add_argument('-ob', '--output_bin', action='store_true', help='save the filename table to a raw filename' ' table file') group.add_argument('-oj', '--output_json', action='store_true', help='save the filename table to a JSON file') group.add_argument('-or', '--output_rom', action='store_true', help='insert the filename table into a ROM' ' (the ROM file must already exist)') group.add_argument('-on', '--output_narc', action='store_true', help='insert the filename table into a NARC file' ' (the NARC file must already exist)') args = parser.parse_args() # Make sure that outfile exists if we're going to insert if (args.output_rom or args.output_narc) and not os.path.exists(args.outfile): raise ValueError( 'To use -or or -on, the output file must already exist!') # Open the input file with open(args.infile, 'rb') as f: ind = f.read() # Load the filename table from it print(f'Loading filenames from {args.infile}...') fnt = None if args.input_bin: fnt = ndspy.fnt.load(ind) elif args.input_json: fnt = _common.jsonToFnt(ind.decode('utf-8')) elif args.input_rom: fnt = ndspy.rom.NintendoDSRom(ind).filenames elif args.input_narc: fnt, _ = ndspy.narc.load(ind) assert fnt is not None # Open the output file, if it exists existing = None if os.path.isfile(args.outfile): with open(args.outfile, 'rb') as f: existing = f.read() # Create the output data in the requested format if args.output_rom or args.output_narc: print(f'Inserting filenames into {args.outfile}...') else: print(f'Saving filenames to {args.outfile}...') outd = None if args.output_bin: outd = ndspy.fnt.save(fnt) elif args.output_json: outd = _common.fntToJson(fnt).encode('utf-8') elif args.output_rom: rom = ndspy.rom.NintendoDSRom(existing) rom.filenames = fnt outd = rom.save() elif args.output_narc: _, files = ndspy.narc.load(existing) outd = ndspy.narc.save(fnt, files) assert outd is not None # Save it with open(args.outfile, 'wb') as f: f.write(outd) print('Done.')