def extract(file): """ Extrct the given archive """ with open(file, "rb") as inf: inb = inf.read() while libyaz0.IsYazCompressed(inb): inb = libyaz0.decompress(inb) name = os.path.splitext(file)[0] ext = SarcLib.guessFileExt(inb) if ext != ".sarc": with open(''.join([name, ext]), "wb") as out: out.write(inb) else: arc = SarcLib.SARC_Archive() arc.load(inb) root = os.path.join(os.path.dirname(file), name) if not os.path.isdir(root): os.mkdir(root) files = [] def getAbsPath(folder, path): nonlocal root nonlocal files for checkObj in folder.contents: if isinstance(checkObj, SarcLib.File): files.append( ["/".join([path, checkObj.name]), checkObj.data]) else: path_ = os.path.join(root, "/".join([path, checkObj.name])) if not os.path.isdir(path_): os.mkdir(path_) getAbsPath(checkObj, "/".join([path, checkObj.name])) for checkObj in arc.contents: if isinstance(checkObj, SarcLib.File): files.append([checkObj.name, checkObj.data]) else: path = os.path.join(root, checkObj.name) if not os.path.isdir(path): os.mkdir(path) getAbsPath(checkObj, checkObj.name) for file, fileData in files: print(file) with open(os.path.join(root, file), "wb") as out: out.write(fileData)
def addFileToLayout(arc, folderName, name, data): fileAdded = False for folder in arc.contents: if isinstance(folder, SarcLib.Folder) and folder.name == "lyt_root": for lytFolder in folder.contents: if isinstance(lytFolder, SarcLib.Folder) and lytFolder.name == folderName: for file in lytFolder.contents: if isinstance(file, SarcLib.File) and file.name == name: lytFolder.removeFile(file) break file = SarcLib.File() file.name = name file.data = data lytFolder.addFile(file) fileAdded = True if fileAdded: break if fileAdded: break if fileAdded: print("Added: %s" % name) else: print("Something went wrong while adding %s!" % name) return arc
def saveSarc(self): arcContents = self.editor.getDataFromWidgets() newArchive = SarcLib.SARC_Archive(endianness='<') for file in arcContents: newArchive.addFile(file) outFile = newArchive.save()[0] with open(self.currentFilePath, 'wb+') as f: f.write(outFile)
def save(self): arc = SarcLib.SARC_Archive() self.addSpriteFiles() # Look up every tileset used in each area tilesets_names = [] for area_SARC in self.areas: if area_SARC.tileset0 not in ('', None): tilesets_names.append(area_SARC.tileset0) if area_SARC.tileset1 not in ('', None): tilesets_names.append(area_SARC.tileset1) if area_SARC.tileset2 not in ('', None): tilesets_names.append(area_SARC.tileset2) if area_SARC.tileset3 not in ('', None): tilesets_names.append(area_SARC.tileset3) tilesets_names = tuple(set(tilesets_names)) # Add each tileset to our archive for tileset_name in tilesets_names: if tileset_name not in self.szsData: if os.path.isfile( os.path.join(globals.mod_path, 'Stage/Texture/%s.szs' % tileset_name)): with open( os.path.join(globals.mod_path, 'Stage/Texture/%s.szs' % tileset_name), "rb") as inf: self.szsData[tileset_name] = DecompYaz0(inf.read()) else: print("Tileset %s not found!" % tileset_name) for file in self.szsData: arc.addFile(SarcLib.File(file, self.szsData[file])) return arc.save()[0]
def saveSarc(self): if self.currentFileType == 'map': arcContents = self.editor.getDataFromWidgets() newArchive = SarcLib.SARC_Archive() for file in arcContents: newArchive.addFile(file) outFile = newArchive.save()[0] CompYaz0(outFile, self.currentFilePath) else: outFile = self.editor.getCamData() with open(self.currentFilePath, 'wb+') as f: f.write(outFile)
def saveSarcAs(self): arcContents = self.editor.getDataFromWidgets() newArchive = SarcLib.SARC_Archive(endianness='<') for file in arcContents: newArchive.addFile(file) outFile = newArchive.save()[0] fileName = QtWidgets.QFileDialog.getSaveFileName( self, 'Save file', '', 'SARC files (*.sarc)')[0] if fileName == '': return with open(fileName, 'wb+') as f: f.write(outFile)
def save(self): """ Save the level back to a file """ # Make a new archive newArchive = SarcLib.SARC_Archive() # Create a folder within the archive courseFolder = SarcLib.Folder('course') newArchive.addFolder(courseFolder) # Go through the areas, save them and add them back to the archive for areanum, area in enumerate(self.areas): course, L0, L1, L2 = area.save() if course is not None: courseFolder.addFile( SarcLib.File('course%d.bin' % (areanum + 1), course)) if L0 is not None: courseFolder.addFile( SarcLib.File('course%d_bgdatL0.bin' % (areanum + 1), L0)) if L1 is not None: courseFolder.addFile( SarcLib.File('course%d_bgdatL1.bin' % (areanum + 1), L1)) if L2 is not None: courseFolder.addFile( SarcLib.File('course%d_bgdatL2.bin' % (areanum + 1), L2)) outerArchive = SarcLib.SARC_Archive() outerArchive.addFile(SarcLib.File('level', newArchive.save()[0])) outerArchive.addFile(SarcLib.File('levelname', b'level')) return outerArchive.save()[0]
def loadSarc(self): fileName = QtWidgets.QFileDialog.getOpenFileName( self, 'Open file', '', 'SARC files (*.sarc)')[0] if fileName == '': return self.currentFilePath = fileName with open(fileName, 'rb') as fileObj: data = fileObj.read() archive = SarcLib.SARC_Archive(data) archive.load(data) self.editor.loadData(archive.contents) self.saveFile.setDisabled(False) self.saveAsFile.setDisabled(False) self.closeFile.setDisabled(False) self.editor.setDisabled(False)
def loadSarc(self): fileName = QtWidgets.QFileDialog.getOpenFileName( self, 'Open file', settings.value("LastPath", ""), 'All Files (*.szs *.bin);;Route Archives (*.szs);;Map Camera Settings (*.bin)' )[0] if fileName == '': return self.currentFilePath = fileName self.setWindowTitle('RouteEdit NSMBU - %s' % self.currentFilePath) settings.setValue("LastPath", self.currentFilePath) with open(fileName, 'rb') as fileObj: data = fileObj.read() if fileName.endswith('.bin'): if len(data) != 0x1500: return self.editor.loadCamData(data) self.currentFileType = 'cam' else: if data[:4] != b'Yaz0': return data = DecompYaz0(data) if data[:4] != b'SARC': return archive = SarcLib.SARC_Archive(data) archive.load(data) self.editor.loadRouteData(archive.contents) self.currentFileType = 'map' self.saveFile.setDisabled(False) self.saveAsFile.setDisabled(False) self.closeFile.setDisabled(False) self.editor.setDisabled(False)
def pack(root, endianness, level, outname): """ Pack the files and folders in the root folder. """ if "\\" in root: root = "/".join(root.split("\\")) if root[-1] == "/": root = root[:-1] arc = SarcLib.SARC_Archive(endianness=endianness) lenroot = len(root.split("/")) for path, dirs, files in os.walk(root): if "\\" in path: path = "/".join(path.split("\\")) lenpath = len(path.split("/")) if lenpath == lenroot: path = "" else: path = "/".join(path.split("/")[lenroot - lenpath:]) for file in files: if path: filename = ''.join([path, "/", file]) else: filename = file print(filename) fullname = ''.join([root, "/", filename]) i = 0 for folder in filename.split("/")[:-1]: if not i: exec( "folder%i = SarcLib.Folder(folder + '/'); arc.addFolder(folder%i)" .replace('%i', str(i))) else: exec( "folder%i = SarcLib.Folder(folder + '/'); folder%m.addFolder(folder%i)" .replace('%i', str(i)).replace('%m', str(i - 1))) i += 1 with open(fullname, "rb") as f: inb = f.read() hasFilename = True if file[:5] == "hash_": hasFilename = False if not i: arc.addFile(SarcLib.File(file, inb, hasFilename)) else: exec("folder%m.addFile(SarcLib.File(file, inb, hasFilename))". replace('%m', str(i - 1))) data, maxAlignment = arc.save() if level != -1: outData = libyaz0.compress(data, maxAlignment, level) del data if not outname: outname = ''.join([root, ".szs"]) else: outData = data if not outname: outname = ''.join([root, ".sarc"]) with open(outname, "wb+") as output: output.write(outData)
def load(self, data, areaNum, progress=None): """ Loads a NSMBU level from bytes data. """ super().load(data, areaNum, progress) arc = SarcLib.SARC_Archive() arc.load(data) try: courseFolder = arc['course'] except: return False # Sort the area data areaData = {} for file in courseFolder.contents: name, val = file.name, file.data if val is None: continue if not name.startswith('course'): continue if not name.endswith('.bin'): continue if '_bgdatL' in name: # It's a layer file if len(name) != 19: continue try: thisArea = int(name[6]) laynum = int(name[14]) except ValueError: continue if not (0 < thisArea < 5): continue if thisArea not in areaData: areaData[thisArea] = [None] * 4 areaData[thisArea][laynum + 1] = val else: # It's the course file if len(name) != 11: continue try: thisArea = int(name[6]) except ValueError: continue if not (0 < thisArea < 5): continue if thisArea not in areaData: areaData[thisArea] = [None] * 4 areaData[thisArea][0] = val # Create area objects self.areas = [] thisArea = 1 while thisArea in areaData: course = areaData[thisArea][0] L0 = areaData[thisArea][1] L1 = areaData[thisArea][2] L2 = areaData[thisArea][3] import area if thisArea == areaNum: newarea = area.Area_NSMBU() globals.Area = newarea SLib.Area = globals.Area else: newarea = area.AbstractArea() newarea.areanum = thisArea newarea.load(course, L0, L1, L2, progress) self.areas.append(newarea) thisArea += 1 return True
def saveNewArea(self, innerfilename, course_new, L0_new, L1_new, L2_new): """ Save the level back to a file (when adding a new or deleting an existing Area) """ # Make a new archive newArchive = SarcLib.SARC_Archive() # Create a folder within the archive courseFolder = SarcLib.Folder('course') newArchive.addFolder(courseFolder) # Go through the areas, save them and add them back to the archive for areanum, area in enumerate(self.areas): course, L0, L1, L2 = area.save(True) if course is not None: courseFolder.addFile( SarcLib.File('course%d.bin' % (areanum + 1), course)) if L0 is not None: courseFolder.addFile( SarcLib.File('course%d_bgdatL0.bin' % (areanum + 1), L0)) if L1 is not None: courseFolder.addFile( SarcLib.File('course%d_bgdatL1.bin' % (areanum + 1), L1)) if L2 is not None: courseFolder.addFile( SarcLib.File('course%d_bgdatL2.bin' % (areanum + 1), L2)) if course_new is not None: courseFolder.addFile( SarcLib.File('course%d.bin' % (len(self.areas) + 1), course_new)) if L0_new is not None: courseFolder.addFile( SarcLib.File('course%d_bgdatL0.bin' % (len(self.areas) + 1), L0_new)) if L1_new is not None: courseFolder.addFile( SarcLib.File('course%d_bgdatL1.bin' % (len(self.areas) + 1), L1_new)) if L2_new is not None: courseFolder.addFile( SarcLib.File('course%d_bgdatL2.bin' % (len(self.areas) + 1), L2_new)) # Here we have the new inner-SARC savedata innersarc = newArchive.save()[0] # Now make an outer SARC outerArchive = SarcLib.SARC_Archive() # Add the innersarc to it outerArchive.addFile(SarcLib.File(innerfilename, innersarc)) # Make it easy for future Miyamotos to pick out the innersarc level name outerArchive.addFile( SarcLib.File('levelname', innerfilename.encode('utf-8'))) # Add all the other stuff, too for szsThingName in globals.szsData: if szsThingName in [globals.levelNameCache, 'levelname']: continue outerArchive.addFile( SarcLib.File(szsThingName, globals.szsData[szsThingName])) # Save the outer sarc and return it return outerArchive.save()[0]
def save(self, innerfilename): """ Save the level back to a file """ # Make a new archive newArchive = SarcLib.SARC_Archive() # Create a folder within the archive courseFolder = SarcLib.Folder('course') newArchive.addFolder(courseFolder) # Go through the areas, save them and add them back to the archive for areanum, area in enumerate(self.areas): course, L0, L1, L2 = area.save() if course is not None: courseFolder.addFile( SarcLib.File('course%d.bin' % (areanum + 1), course)) if L0 is not None: courseFolder.addFile( SarcLib.File('course%d_bgdatL0.bin' % (areanum + 1), L0)) if L1 is not None: courseFolder.addFile( SarcLib.File('course%d_bgdatL1.bin' % (areanum + 1), L1)) if L2 is not None: courseFolder.addFile( SarcLib.File('course%d_bgdatL2.bin' % (areanum + 1), L2)) # Here we have the new inner-SARC savedata innersarc = newArchive.save()[0] globals.szsData[innerfilename] = innersarc # Now make an outer SARC outerArchive = SarcLib.SARC_Archive() # Add the innersarc to it outerArchive.addFile(SarcLib.File(innerfilename, innersarc)) # Make it easy for future Miyamotos to pick out the innersarc level name outerArchive.addFile( SarcLib.File('levelname', innerfilename.encode('utf-8'))) globals.szsData['levelname'] = innerfilename.encode('utf-8') # Save all the tilesets if globals.TilesetEdited or globals.OverrideTilesetSaving: if globals.Area.tileset1: tilesetData = SaveTileset(1) if tilesetData: globals.szsData[globals.Area.tileset1] = tilesetData if globals.Area.tileset2: tilesetData = SaveTileset(2) if tilesetData: globals.szsData[globals.Area.tileset2] = tilesetData if globals.Area.tileset3: tilesetData = SaveTileset(3) if tilesetData: globals.szsData[globals.Area.tileset3] = tilesetData # Add all the other stuff, too if os.path.isdir(globals.miyamoto_path + '/data'): szsNewData = {} szsNewData[innerfilename] = innersarc szsNewData['levelname'] = innerfilename.encode('utf-8') paths = [ globals.miyamoto_path + '/miyamotodata/spriteresources.xml' ] for path in globals.gamedef.recursiveFiles('spriteresources'): if path: paths.append( os.path.join( globals.miyamoto_path, path if isinstance(path, str) else path.path)) sprites_xml = {} for path in paths: # Read the sprites resources xml tree = etree.parse(path) root = tree.getroot() # Get all sprites' filenames and add them to a list for sprite in root.iter('sprite'): id = int(sprite.get('id')) name = [] for id2 in sprite: name.append(id2.get('name')) sprites_xml[id] = list(name) # Look up every sprite and tileset used in each area sprites_SARC = [] tilesets_names = [] for area_SARC in globals.Level.areas: for sprite in area_SARC.sprites: sprites_SARC.append(sprite.type) if area_SARC.tileset0 not in ('', None): tilesets_names.append(area_SARC.tileset0) if area_SARC.tileset1 not in ('', None): tilesets_names.append(area_SARC.tileset1) if area_SARC.tileset2 not in ('', None): tilesets_names.append(area_SARC.tileset2) if area_SARC.tileset3 not in ('', None): tilesets_names.append(area_SARC.tileset3) sprites_SARC = list(set(sprites_SARC)) tilesets_names = list(set(tilesets_names)) # Sort the filenames for each "used" sprite sprites_names = [] for sprite in sprites_SARC: if sprite in sprites_xml: for sprite_name in sprites_xml[sprite]: sprites_names.append(sprite_name) sprites_names = list(set(sprites_names)) # Look up each needed file and add it to our archive for sprite_name in sprites_names: # Get it from inside the original archive if not globals.OverwriteSprite and sprite_name in globals.szsData: outerArchive.addFile( SarcLib.File(sprite_name, globals.szsData[sprite_name])) szsNewData[sprite_name] = globals.szsData[sprite_name] # Get it from the "custom" data folder elif os.path.isfile(globals.miyamoto_path + '/data/custom/' + sprite_name): with open( globals.miyamoto_path + '/data/custom/' + sprite_name, 'rb') as f: f1 = f.read() outerArchive.addFile(SarcLib.File(sprite_name, f1)) szsNewData[sprite_name] = f1 # Get it from the data folder elif os.path.isfile(globals.miyamoto_path + '/data/' + sprite_name): with open(globals.miyamoto_path + '/data/' + sprite_name, 'rb') as f: f1 = f.read() outerArchive.addFile(SarcLib.File(sprite_name, f1)) szsNewData[sprite_name] = f1 # Throw a warning because the file was not found... else: print("WARNING: Could not find the file: %s" % sprite_name) print("Expect the level to crash ingame...") # Add each tileset to our archive for tileset_name in tilesets_names: if tileset_name in globals.szsData: outerArchive.addFile( SarcLib.File(tileset_name, globals.szsData[tileset_name])) szsNewData[tileset_name] = globals.szsData[tileset_name] globals.szsData = szsNewData else: # data folder not found, copy the files for szsThingName in globals.szsData: if szsThingName in [ globals.levelNameCache, innerfilename, 'levelname' ]: continue outerArchive.addFile( SarcLib.File(szsThingName, globals.szsData[szsThingName])) # Save the outer sarc and return it return outerArchive.save()[0]
def patchLayouts(): """ Patch all the layouts """ if not os.path.isdir( os.path.join(globals.patchpath, 'content/Common/layout')): os.mkdir(os.path.join(globals.patchpath, 'content/Common/layout')) for layout in globals.Layouts: if os.path.isdir(os.path.join(globals.mod_path, 'Layouts/' + layout)): if not os.path.isfile( os.path.join(globals.mod_path, 'Layouts/%s/settings.xml' % layout)): continue settings = XmlToDict( os.path.join(globals.mod_path, 'Layouts/%s/settings.xml' % layout)) if not settings: continue imgsSettings = [] lans = [] lyts = [] for setting in settings: if settings[setting]: if setting[:3] == "Img": imgsSettings.append(settings[setting]) elif setting[:3] == "Lan": if "Name" not in settings[setting]: continue name = settings[setting]["Name"] if not name.endswith(".bflan"): continue elif not os.path.isfile( os.path.join(globals.mod_path, 'Layouts/%s/%s' % (layout, name))): continue lans.append(name) elif setting[:3] == "Lyt": if "Name" not in settings[setting]: continue name = settings[setting]["Name"] if not name.endswith(".bflyt"): continue elif not os.path.isfile( os.path.join(globals.mod_path, 'Layouts/%s/%s' % (layout, name))): continue lyts.append(name) if not (imgsSettings or lans or lyts): continue imgs = {} for imgSettings in imgsSettings: name = "" bflimname = "" tileMode = 4 swizzle = 0 SRGB = "False" for setting in imgSettings: if setting == "Name": name = imgSettings[setting] elif setting == "BFLIMName": bflimname = imgSettings[setting] elif imgSettings[setting]: if setting == "TileMode": try: tileMode = int(imgSettings[setting], 0) except ValueError: tileMode = 4 else: if tileMode > 16 or tileMode < 0: tileMode = 4 elif setting == "Swizzle": try: swizzle = int(imgSettings[setting], 0) except ValueError: swizzle = 0 else: if swizzle > 7 or swizzle < 0: swizzle = 0 elif setting == "SRGB": SRGB = imgSettings[setting] if SRGB not in ["True", "False"]: SRGB = "False" if not name: continue elif not name.endswith(".dds"): continue elif not os.path.isfile( os.path.join(globals.mod_path, 'Layouts/%s/%s' % (layout, name))): continue if not bflimname: bflimname = name[:-3] + "bflim" data = writeFLIM( os.path.join(globals.mod_path, 'Layouts/%s/%s' % (layout, name)), tileMode, swizzle, SRGB == "True", ) if not data: print( "Something went wrong while converting %s to BFLIM!" % name) continue imgs[name] = { "BFLIMName": bflimname, "Data": data, } print("\nPatching: %s.szs\n" % layout) szsname = os.path.join(globals.gamepath, 'Common/layout/%s.szs' % layout) if not os.path.isfile(szsname): print('Something went wrong while reading %s.szs!' % layout) continue else: with open(szsname, 'rb') as inf: inb = inf.read() arc = SarcLib.SARC_Archive(DecompYaz0(inb)) for name in imgs: bflimname = imgs[name]["BFLIMName"] data = imgs[name]["Data"] arc = addFileToLayout(arc, "timg", bflimname, data) for name in lans: with open( os.path.join(globals.mod_path, 'Layouts/%s/%s' % (layout, name)), "rb") as inf: data = inf.read() arc = addFileToLayout(arc, "anim", name, data) for name in lyts: with open( os.path.join(globals.mod_path, 'Layouts/%s/%s' % (layout, name)), "rb") as inf: data = inf.read() arc = addFileToLayout(arc, "blyt", name, data) with open( os.path.join(globals.patchpath, 'content/Common/layout/%s.sarc' % layout), "wb+") as out: out.write(arc.save()[0]) print('\nCompressing: %s.szs' % layout) if not CompYaz0( os.path.join(globals.patchpath, 'content/Common/layout/%s.sarc' % layout), os.path.join(globals.patchpath, 'content/Common/layout/%s.szs' % layout), ): print('Something went wrong while compressing %s.szs!' % layout) else: print('Patched: %s.szs' % layout) os.remove( os.path.join(globals.patchpath, 'content/Common/layout/%s.sarc' % layout))
def load(self, files): self.szsData = files self.szsData['levelname'] = self.name.encode('utf-8') arc = SarcLib.SARC_Archive() arc.load(self.szsData[self.name]) try: courseFolder = arc['course'] except: return False areaData = {} for file in courseFolder.contents: name, val = file.name, file.data if val is None: continue if not name.startswith('course'): continue if not name.endswith('.bin'): continue if '_bgdatL' in name: # It's a layer file if len(name) != 19: continue try: thisArea = int(name[6]) laynum = int(name[14]) except ValueError: continue if not (0 < thisArea < 5): continue if thisArea not in areaData: areaData[thisArea] = [None] * 4 areaData[thisArea][laynum + 1] = val else: # It's the course file if len(name) != 11: continue try: thisArea = int(name[6]) except ValueError: continue if not (0 < thisArea < 5): continue if thisArea not in areaData: areaData[thisArea] = [None] * 4 areaData[thisArea][0] = val # Create area objects self.areas = [] thisArea = 1 while thisArea in areaData: course = areaData[thisArea][0] L0 = areaData[thisArea][1] L1 = areaData[thisArea][2] L2 = areaData[thisArea][3] newarea = Area() newarea.areanum = thisArea newarea.load(course, L0, L1, L2) self.areas.append(newarea) thisArea += 1 return True
def _LoadTileset(idx, name): """ Load in a tileset into a specific slot """ # if this file's not found, return if name not in globals.szsData: return sarcdata = globals.szsData[name] sarc = SarcLib.SARC_Archive() sarc.load(sarcdata) tileoffset = idx * 256 # Decompress the textures try: comptiledata = sarc['BG_tex/%s.gtx' % name].data nmldata = sarc['BG_tex/%s_nml.gtx' % name].data colldata = sarc['BG_chk/d_bgchk_%s.bin' % name].data except KeyError: QtWidgets.QMessageBox.warning( None, globals.trans.string('Err_CorruptedTilesetData', 0), globals.trans.string('Err_CorruptedTilesetData', 1, '[file]', name), ) return False # load in the textures img = loadGTX(comptiledata) nml = loadGTX(nmldata) # Divide it into individual tiles and # add collisions at the same time def getTileFromImage(tilemap, xtilenum, ytilenum): return tilemap.copy((xtilenum * 64) + 2, (ytilenum * 64) + 2, 60, 60) dest = QtGui.QPixmap.fromImage(img) dest2 = QtGui.QPixmap.fromImage(nml) sourcex = 0 sourcey = 0 for i in range(tileoffset, tileoffset + 256): T = TilesetTile(getTileFromImage(dest, sourcex, sourcey), getTileFromImage(dest2, sourcex, sourcey)) T.setCollisions(struct.unpack_from('<Q', colldata, (i - tileoffset) * 8)[0]) globals.Tiles[i] = T sourcex += 1 if sourcex >= 32: sourcex = 0 sourcey += 1 def exists(fn): nonlocal sarc try: sarc[fn] except KeyError: return False return True # Load the tileset animations, if there are any if idx == 0: tileoffset = idx * 256 hatena_anime = None block_anime = None tuka_coin_anime = None belt_conveyor_anime = None fn = 'BG_tex/hatena_anime.gtx' found = exists(fn) if found: hatena_anime = loadGTX(sarc[fn].data) fn = 'BG_tex/block_anime.gtx' found = exists(fn) if found: block_anime = loadGTX(sarc[fn].data) fn = 'BG_tex/tuka_coin_anime.gtx' found = exists(fn) if found: tuka_coin_anime = loadGTX(sarc[fn].data) fn = 'BG_tex/belt_conveyor_anime.gtx' found = exists(fn) if found: belt_conveyor_anime = loadGTX(sarc[fn].data, True) for i in range(tileoffset, tileoffset + 256): if globals.Tiles[i].coreType == 7: if hatena_anime: globals.Tiles[i].addAnimationData(hatena_anime) elif globals.Tiles[i].coreType == 6: if block_anime: globals.Tiles[i].addAnimationData(block_anime) elif globals.Tiles[i].coreType == 2: if tuka_coin_anime: globals.Tiles[i].addAnimationData(tuka_coin_anime) elif globals.Tiles[i].coreType == 17: if belt_conveyor_anime: for x in range(2): if i == 144 + x * 16: globals.Tiles[i].addConveyorAnimationData(belt_conveyor_anime, 0, True) elif i == 145 + x * 16: globals.Tiles[i].addConveyorAnimationData(belt_conveyor_anime, 1, True) elif i == 146 + x * 16: globals.Tiles[i].addConveyorAnimationData(belt_conveyor_anime, 2, True) elif i == 147 + x * 16: globals.Tiles[i].addConveyorAnimationData(belt_conveyor_anime, 0) elif i == 148 + x * 16: globals.Tiles[i].addConveyorAnimationData(belt_conveyor_anime, 1) elif i == 149 + x * 16: globals.Tiles[i].addConveyorAnimationData(belt_conveyor_anime, 2) for tile in globals.Overrides: if tile.coreType == 7: if hatena_anime: tile.addAnimationData(hatena_anime) elif tile.coreType == 6: if block_anime: tile.addAnimationData(block_anime) # Load the object definitions defs = [None] * 256 indexfile = sarc['BG_unt/%s_hd.bin' % name].data deffile = sarc['BG_unt/%s.bin' % name].data objcount = len(indexfile) // 6 indexstruct = struct.Struct('>HBBH') for i in range(objcount): data = indexstruct.unpack_from(indexfile, i * 6) obj = ObjectDef() obj.width = data[1] obj.height = data[2] obj.randByte = data[3] obj.load(deffile, data[0]) defs[i] = obj globals.ObjectDefinitions[idx] = defs ProcessOverrides(name)
def SaveTileset(name, tilesetObj): """ Saves a tileset from a specific slot """ defs = tilesetObj.defs if defs is None: return False colldata = tilesetObj.colldata deffile = b'' indexfile = b'' for obj in defs: if obj is None: break indexfile += struct.pack('>HBBxB', len(deffile), obj.width, obj.height, obj.randByte) for row in obj.rows: for tile in row: if len(tile) == 3: byte2 = tile[2] << 2 byte2 |= (tile[1] >> 8) & 3 # Slot deffile += bytes([tile[0], tile[1] & 0xFF, byte2]) else: deffile += bytes(tile) deffile += b'\xFE' deffile += b'\xFF' arc = SarcLib.SARC_Archive() tex = SarcLib.Folder('BG_tex') arc.addFolder(tex) tex.addFile(SarcLib.File('%s.gtx' % name, writeGTX(*tilesetObj.img))) tex.addFile(SarcLib.File('%s_nml.gtx' % name, writeGTX(*tilesetObj.nml))) if tilesetObj.hatena_anime: tex.addFile(SarcLib.File('hatena_anime.gtx', writeGTX(*tilesetObj.hatena_anime))) if tilesetObj.block_anime: tex.addFile(SarcLib.File('block_anime.gtx', writeGTX(*tilesetObj.block_anime))) if tilesetObj.hatena_anime_L: tex.addFile(SarcLib.File('hatena_anime_L.gtx', writeGTX(*tilesetObj.hatena_anime_L))) if tilesetObj.block_anime_L: tex.addFile(SarcLib.File('block_anime_L.gtx', writeGTX(*tilesetObj.block_anime_L))) if tilesetObj.tuka_coin_anime: tex.addFile(SarcLib.File('tuka_coin_anime.gtx', writeGTX(*tilesetObj.tuka_coin_anime))) if tilesetObj.belt_conveyor_anime: tex.addFile(SarcLib.File('belt_conveyor_anime.gtx', writeGTX(*tilesetObj.belt_conveyor_anime))) chk = SarcLib.Folder('BG_chk') arc.addFolder(chk) chk.addFile(SarcLib.File('d_bgchk_%s.bin' % name, colldata)) unt = SarcLib.Folder('BG_unt') arc.addFolder(unt) unt.addFile(SarcLib.File('%s.bin' % name, deffile)) unt.addFile(SarcLib.File('%s_hd.bin' % name, indexfile)) return arc.save()[0]
def LoadTileset(idx, name): """ Load in a tileset into a specific slot """ if not name: return None global TilesetPath if not TilesetPath: TilesetPath = input('Enter the path to the "Unit" folder, e.g. "C:\\NSMBUDX\\romfs\\Unit": ') path = TilesetPath found = False if path: sarcname = os.path.join(os.path.dirname(path), 'Unit', name + '.szs') if os.path.isfile(sarcname): found = True if not found: raise RuntimeError("Tileset %s not found!" % name) # get the data with open(sarcname, 'rb') as fileobj: sarcdata = fileobj.read() if sarcdata[:4] != b'Yaz0': raise RuntimeError("Tileset is not Yaz0 compressed!") sarcdata = DecompYaz0(sarcdata) sarc = SarcLib.SARC_Archive() sarc.load(sarcdata) def exists(fn): nonlocal sarc try: sarc[fn] except KeyError: return False return True # Decompress the textures try: bfresdata = sarc['output.bfres'].data colldata = sarc['BG_chk/d_bgchk_%s.bin' % name].data except KeyError: raise RuntimeError("Looks like tileset is corrupted...") tileset = Tileset() # load in the textures bntx = loadBNTXFromBFRES(bfresdata) tileset.img = loadTexFromBNTX(bntx, name) tileset.nml = loadTexFromBNTX(bntx, name + "_nml") # Load the tileset animations, if there are any if idx == 0: tileoffset = idx * 256 try: tileset.hatena_anime = loadTexFromBNTX(bntx, "hatena_anime") except: pass try: tileset.block_anime = loadTexFromBNTX(bntx, "block_anime") except: pass try: tileset.hatena_anime_L = loadTexFromBNTX(bntx, "hatena_anime_L") except: pass try: tileset.block_anime_L = loadTexFromBNTX(bntx, "block_anime_L") except: pass try: tileset.tuka_coin_anime = loadTexFromBNTX(bntx, "tuka_coin_anime") except: pass try: tileset.belt_conveyor_anime = loadTexFromBNTX(bntx, "belt_conveyor_anime") except: pass # Load the object definitions defs = [None] * 256 indexfile = sarc['BG_unt/%s_hd.bin' % name].data deffile = sarc['BG_unt/%s.bin' % name].data objcount = len(indexfile) // 6 indexstruct = struct.Struct('<HBBH') for i in range(objcount): data = indexstruct.unpack_from(indexfile, i * 6) obj = ObjectDef() obj.width = data[1] obj.height = data[2] obj.randByte = data[3] obj.load(deffile, data[0]) defs[i] = obj tileset.defs = defs tileset.colldata = colldata return tileset
def save(self): """ Save the level back to a file """ # Make a new archive newArchive = SarcLib.SARC_Archive() # Create a folder within the archive courseFolder = SarcLib.Folder('course') newArchive.addFolder(courseFolder) outerArchive = SarcLib.SARC_Archive() # Go through the areas, save them and add them back to the archive for areanum, area in enumerate(self.areas): course, L0, L1, L2 = area.save() if course is not None: courseFolder.addFile( SarcLib.File('course%d.bin' % (areanum + 1), course)) if L0 is not None: courseFolder.addFile( SarcLib.File('course%d_bgdatL0.bin' % (areanum + 1), L0)) if L1 is not None: courseFolder.addFile( SarcLib.File('course%d_bgdatL1.bin' % (areanum + 1), L1)) if L2 is not None: courseFolder.addFile( SarcLib.File('course%d_bgdatL2.bin' % (areanum + 1), L2)) # I need to kick in tileset saving here if area.tileset0 and area.tileset0Obj and not exists( outerArchive, area.tileset0): outerArchive.addFile( SarcLib.File( area.tileset0, SaveTileset(area.tileset0, area.tileset0Obj))) if area.tileset1 and area.tileset1Obj and not exists( outerArchive, area.tileset1): outerArchive.addFile( SarcLib.File( area.tileset1, SaveTileset(area.tileset1, area.tileset1Obj))) if area.tileset2 and area.tileset2Obj and not exists( outerArchive, area.tileset2): outerArchive.addFile( SarcLib.File( area.tileset2, SaveTileset(area.tileset2, area.tileset2Obj))) if area.tileset3 and area.tileset3Obj and not exists( outerArchive, area.tileset3): outerArchive.addFile( SarcLib.File( area.tileset3, SaveTileset(area.tileset3, area.tileset3Obj))) outerArchive.addFile(SarcLib.File(self.name, newArchive.save()[0])) return outerArchive.save()[0]
def change_water(path, water_type=0): water_types = [ 'Normal Water', 'Hot Water', 'Poison', 'Lava', 'Ice Water', 'Mud', 'Clear Water', 'Sea Water' ] for parent_dir, dirs, files in os.walk(path): for file in files: current_path = '{0}{1}'.format(parent_dir, file) if 'water.extm.sstera' not in current_path: continue with open(current_path, 'rb') as infile: infile_binary = infile.read() while libyaz0.IsYazCompressed(infile_binary): infile_binary = libyaz0.decompress(infile_binary) path, extension = os.path.splitext(current_path) filename = os.path.basename(current_path) if infile_binary[0x00:0x04] != b'SARC': print('Not a sarc. :(') sarc = SarcLib.SARC_Archive() sarc.load(infile_binary) with tempfile.TemporaryDirectory() as temp_dir: for sarc_file in sarc.contents: if isinstance(sarc_file, SarcLib.File): pos = 0 data = bytearray(sarc_file.data) while pos + 8 <= len(sarc_file.data): height, x_axis_flow_rate, z_axis_flow_rate, mate_check, mate = \ struct.unpack('<3H2B', data[pos:pos + 0x08]) # height = 0x24cc # x_axis_flow_rate = 0x8125 # z_axis_flow_rate = 0x8125 mate = water_type mate_check = mate + 3 data[pos:pos + 0x08] = struct.pack( '<3H2B', height, x_axis_flow_rate, z_axis_flow_rate, mate_check, mate) pos += 0x08 with open( '{0}/{1}'.format(temp_dir, sarc_file.name), 'wb+') as outfile: outfile.write(data) sarc = SarcLib.SARC_Archive(endianness='>') for path, dirs, files in os.walk(temp_dir): for file in files: with open('{0}/{1}'.format(path, file), 'rb') as infile: sarc.addFile( SarcLib.File(file, infile.read(), True)) data, alignment = sarc.save() data = libyaz0.compress(data, alignment, 5) destination = '{0}/output/MainField - {1}'.format( os.path.dirname(__file__), water_types[water_type]) if not os.path.exists('{0}/output/'.format( os.path.dirname(__file__))): os.makedirs('{0}/output/'.format( os.path.dirname(__file__))) if not os.path.exists('{0}/output/MainField - {1}'.format( os.path.dirname(__file__), water_types[water_type])): os.makedirs('{0}/output/MainField - {1}'.format( os.path.dirname(__file__), water_types[water_type])) with open('{0}/{1}'.format(destination, filename), 'wb+') as outfile: print('saving {0}...'.format(filename)) outfile.write(data)