public static Pointer Read(EndianBinaryReader reader) { MapLoader l = MapLoader.Loader; uint current_off = (uint)(reader.BaseStream.Position); uint value = reader.ReadUInt32(); FileWithPointers file = l.GetFileByReader(reader); if (file == null) { throw new FormatException("Reader wasn't recognized."); } uint fileOff = (uint)(current_off - file.baseOffset); if (!file.pointers.ContainsKey(fileOff)) { if (value == 0) { return(null); } if (!l.allowDeadPointers) { throw new FormatException("Not a valid pointer in file " + file.name + " at position " + current_off); } return(null); } return(file.pointers[fileOff]); }
public static Pointer Current(Writer writer) { MapLoader l = MapLoader.Loader; uint curPos = (uint)writer.BaseStream.Position; FileWithPointers curFile = l.GetFileByWriter(writer); return(new Pointer((uint)(curPos - curFile.baseOffset), curFile)); }
public static Pointer Current(EndianBinaryReader reader) { MapLoader l = MapLoader.Loader; uint curPos = (uint)reader.BaseStream.Position; FileWithPointers curFile = l.GetFileByReader(reader); return(new Pointer((uint)(curPos - curFile.baseOffset), curFile)); }
public static void Write(Writer writer, Pointer pointer) { MapLoader l = MapLoader.Loader; uint current_off = (uint)(writer.BaseStream.Position); FileWithPointers file = l.GetFileByWriter(writer); if (file == null) { throw new FormatException("Writer wasn't recognized."); } file.WritePointer(pointer); }
public static Pointer GetPointerAtOffset(Pointer pointer) { MapLoader l = MapLoader.Loader; Pointer ptr = null; if (pointer.file.pointers.ContainsKey(pointer.offset)) { ptr = pointer.file.pointers[pointer.offset]; if (ptr.offset == 0) { return(null); } return(ptr); } else if (pointer.file.allowUnsafePointers) { Reader reader = pointer.file.reader; Pointer.DoAt(ref reader, pointer, () => { uint current_off = (uint)(reader.BaseStream.Position); uint value = reader.ReadUInt32(); FileWithPointers file = pointer.file; uint fileOff = (uint)(current_off - file.baseOffset); if (file.pointers.ContainsKey(fileOff)) { ptr = file.pointers[fileOff]; } else { if (value == 0 || value == 0xFFFFFFFF) { ptr = null; } else { ptr = file.GetUnsafePointer(value); } } }); return(ptr); } return(null); }
public static Pointer Read(Reader reader, bool allowMinusOne = false) { MapLoader l = MapLoader.Loader; uint current_off = (uint)(reader.BaseStream.Position); Pointer readFrom = Pointer.Current(reader); uint value = reader.ReadUInt32(); FileWithPointers file = l.GetFileByReader(reader); if (file == null) { throw new PointerException("Reader wasn't recognized.", "Pointer.Read"); } uint fileOff = (uint)(current_off - file.baseOffset); if (!file.pointers.ContainsKey(fileOff)) { if (value == 0 || (allowMinusOne && value == 0xFFFFFFFF)) { return(null); } if (!l.allowDeadPointers && !file.allowUnsafePointers) { throw new PointerException("Not a valid pointer at " + (Pointer.Current(reader) - 4) + ": " + value, "Pointer.Read"); } if (file.allowUnsafePointers) { Pointer ptr = file.GetUnsafePointer(value); if (ptr == null) { throw new PointerException("Not a valid pointer at " + (Pointer.Current(reader) - 4) + ": " + value, "Pointer.Read"); } return(LogPointer(ptr, readFrom, l)); } return(null); } // Hack for R3GC US if (l.allowDeadPointers && file.name == "test" && file.pointers[fileOff].file.name == "fix") { return(null); } return(LogPointer(file.pointers[fileOff], readFrom, l)); }
async UniTask LoadLVLSNA() { loadingState = "Loading level memory"; await WaitIfNecessary(); Reader reader = files_array[Mem.Lvl].reader; Pointer off_current; SNA sna = (SNA)files_array[Mem.Lvl]; // First read GPT files_array[Mem.Lvl].GotoHeader(); reader = files_array[Mem.Lvl].reader; print("LVL GPT offset: " + Pointer.Current(reader)); if (Settings.s.engineVersion == Settings.EngineVersion.Montreal) { // SDA /*sna.GotoSDA(); * print(Pointer.Current(reader)); * reader.ReadUInt32(); * reader.ReadUInt32(); // same as next * uint num_strings = reader.ReadUInt32(); * uint indexOfTextGlobal = reader.ReadUInt32(); // dword_6EEE78 * uint dword_83EC58 = reader.ReadUInt32(); * print(num_strings + " - " + Pointer.Current(reader)); * * // DLG * sna.GotoDLG(); * Pointer off_strings = Pointer.Read(reader); * for (int i = 0; i < num_strings; i++) { * Pointer.Read(reader); * } * reader.ReadUInt32();*/ // GPT sna.GotoHeader(); if (Settings.s.game != Settings.Game.PlaymobilLaura) { Pointer.Read(reader); // sound related } Pointer.Read(reader); Pointer.Read(reader); reader.ReadUInt32(); } if (Settings.s.engineVersion != Settings.EngineVersion.Montreal) { loadingState = "Reading settings for persos in fix"; await WaitIfNecessary(); // Fill in fix -> lvl pointers for perso's in fix uint num_persoInFixPointers = reader.ReadUInt32(); Pointer[] persoInFixPointers = new Pointer[num_persoInFixPointers]; for (int i = 0; i < num_persoInFixPointers; i++) { Pointer off_perso = Pointer.Read(reader); if (off_perso != null) { off_current = Pointer.Goto(ref reader, off_perso); reader.ReadUInt32(); Pointer off_stdGame = Pointer.Read(reader); if (off_stdGame != null) { if (Settings.s.engineVersion > Settings.EngineVersion.TT) { Pointer.Goto(ref reader, off_stdGame); reader.ReadUInt32(); // type 0 reader.ReadUInt32(); // type 1 reader.ReadUInt32(); // type 2 Pointer off_superObject = Pointer.Read(reader); Pointer.Goto(ref reader, off_current); if (off_superObject == null) { continue; } } else { Pointer.Goto(ref reader, off_current); } // First read everything from the GPT Pointer off_newSuperObject = null, off_nextBrother = null, off_prevBrother = null, off_father = null; byte[] matrixData = null, floatData = null, renderBits = null; if (Settings.s.engineVersion > Settings.EngineVersion.TT) { off_newSuperObject = Pointer.Read(reader); matrixData = reader.ReadBytes(0x58); renderBits = reader.ReadBytes(4); floatData = reader.ReadBytes(4); off_nextBrother = Pointer.Read(reader); off_prevBrother = Pointer.Read(reader); off_father = Pointer.Read(reader); } else { matrixData = reader.ReadBytes(0x58); off_newSuperObject = Pointer.Read(reader); Pointer.DoAt(ref reader, off_stdGame + 0xC, () => { ((SNA)off_stdGame.file).AddPointer(off_stdGame.offset + 0xC, off_newSuperObject); }); } // Then fill everything in off_current = Pointer.Goto(ref reader, off_newSuperObject); uint newSOtype = reader.ReadUInt32(); Pointer off_newSOengineObject = Pointer.Read(reader); if (SuperObject.GetSOType(newSOtype) == SuperObject.Type.Perso) { persoInFixPointers[i] = off_newSOengineObject; Pointer.Goto(ref reader, off_newSOengineObject); Pointer off_p3dData = Pointer.Read(reader); if (Settings.s.game == Settings.Game.R2Demo) { ((SNA)off_p3dData.file).OverwriteData(off_p3dData.FileOffset + 0x1C, matrixData); } else { ((SNA)off_p3dData.file).OverwriteData(off_p3dData.FileOffset + 0x18, matrixData); } if (Settings.s.engineVersion > Settings.EngineVersion.TT) { FileWithPointers file = off_newSuperObject.file; file.AddPointer(off_newSuperObject.FileOffset + 0x14, off_nextBrother); file.AddPointer(off_newSuperObject.FileOffset + 0x18, off_prevBrother); file.AddPointer(off_newSuperObject.FileOffset + 0x1C, off_father); ((SNA)file).OverwriteData(off_newSuperObject.FileOffset + 0x30, renderBits); ((SNA)file).OverwriteData(off_newSuperObject.FileOffset + 0x38, floatData); } } else { persoInFixPointers[i] = null; } } Pointer.Goto(ref reader, off_current); } } } loadingState = "Loading globals"; await WaitIfNecessary(); if (Settings.s.engineVersion > Settings.EngineVersion.Montreal) { globals.off_actualWorld = Pointer.Read(reader); globals.off_dynamicWorld = Pointer.Read(reader); globals.off_inactiveDynamicWorld = Pointer.Read(reader); globals.off_fatherSector = Pointer.Read(reader); globals.off_firstSubMapPosition = Pointer.Read(reader); } else { globals.off_actualWorld = Pointer.Read(reader); globals.off_dynamicWorld = Pointer.Read(reader); globals.off_fatherSector = Pointer.Read(reader); uint soundEventIndex = reader.ReadUInt32(); // In Montreal version this is a pointer, also sound event related if (Settings.s.game == Settings.Game.PlaymobilLaura) { Pointer.Read(reader); } } globals.num_always = reader.ReadUInt32(); globals.spawnablePersos = LinkedList <Perso> .ReadHeader(reader, Pointer.Current(reader), LinkedList.Type.Double); globals.off_always_reusableSO = Pointer.Read(reader); // There are (num_always) empty SuperObjects starting with this one. if (Settings.s.engineVersion > Settings.EngineVersion.Montreal) { globals.off_always_reusableUnknown1 = Pointer.Read(reader); // (num_always) * 0x2c blocks globals.off_always_reusableUnknown2 = Pointer.Read(reader); // (num_always) * 0x4 blocks } else { reader.ReadUInt32(); // 0x6F. In Montreal version this is a pointer to a pointer table for always globals.spawnablePersos.FillPointers(reader, globals.spawnablePersos.off_tail, globals.spawnablePersos.offset); } if (Settings.s.game == Settings.Game.DD) { reader.ReadUInt32(); } if (Settings.s.engineVersion > Settings.EngineVersion.Montreal) { Pointer dword_4A6B1C_always_header = Pointer.Read(reader); Pointer dword_4A6B20_always_last = Pointer.Read(reader); Pointer v28 = Pointer.Read(reader); Pointer v31 = Pointer.Read(reader); Pointer v32 = Pointer.Read(reader); Pointer v33 = Pointer.Read(reader); // These things aren't parsed, but in raycap they're null. This way we'll notice when they aren't. if (v28 != null) { print("v28 is not null, it's " + v28); } if (v31 != null) { print("v31 is not null, it's " + v31); } if (v32 != null) { print("v32 is not null, it's " + v32); } if (v33 != null) { print("v33 is not null, it's " + v33); } // Fill in pointers for the unknown table related to "always". FillLinkedListPointers(reader, dword_4A6B20_always_last, dword_4A6B1C_always_header); } // Fill in pointers for the object type tables and read them objectTypes = new ObjectType[3][]; for (uint i = 0; i < 3; i++) { Pointer off_names_header = Pointer.Current(reader); Pointer off_names_first = Pointer.Read(reader); Pointer off_names_last = Pointer.Read(reader); uint num_names = reader.ReadUInt32(); FillLinkedListPointers(reader, off_names_last, off_names_header); ReadObjectNamesTable(reader, off_names_first, num_names, i); } // Begin of engineStructure loadingState = "Loading engine structure"; await WaitIfNecessary(); print("Start of EngineStructure: " + Pointer.Current(reader)); if (Settings.s.engineVersion > Settings.EngineVersion.Montreal) { reader.ReadByte(); string mapName = reader.ReadString(0x1E); reader.ReadChars(0x1E); string mapName2 = reader.ReadString(0x1E); reader.ReadByte(); reader.ReadBytes(0x178); // don't know what this data is } else { reader.ReadByte(); string mapName = reader.ReadString(0x104); reader.ReadChars(0x104); string mapName2 = reader.ReadString(0x104); if (Settings.s.game == Settings.Game.PlaymobilLaura) { reader.ReadChars(0x104); reader.ReadChars(0x104); } string mapName3 = reader.ReadString(0x104); if (Settings.s.game == Settings.Game.TT) { reader.ReadBytes(0x47F7); // don't know what this data is } else if (Settings.s.game == Settings.Game.TTSE) { reader.ReadBytes(0x240F); } else if (Settings.s.game == Settings.Game.PlaymobilLaura) { reader.ReadBytes(0x240F); // don't know what this data is } else // Hype & Alex { reader.ReadBytes(0x2627); // don't know what this data is } } Pointer off_unknown_first = Pointer.Read(reader); Pointer off_unknown_last = Pointer.Read(reader); uint num_unknown = reader.ReadUInt32(); families = LinkedList <Family> .ReadHeader(reader, Pointer.Current(reader), type : LinkedList.Type.Double); families.FillPointers(reader, families.off_tail, families.off_head); if (Settings.s.game == Settings.Game.PlaymobilLaura) { LinkedList <int> .ReadHeader(reader, Pointer.Current(reader), type : LinkedList.Type.Double); } LinkedList <SuperObject> alwaysActiveCharacters = LinkedList <SuperObject> .ReadHeader(reader, Pointer.Current(reader), type : LinkedList.Type.Double); if (Settings.s.engineVersion > Settings.EngineVersion.Montreal) { reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); if (Settings.s.game == Settings.Game.RedPlanet || Settings.s.game == Settings.Game.R2Demo) { reader.ReadUInt32(); } Pointer off_languages = Pointer.Read(reader); reader.ReadUInt32(); Pointer.DoAt(ref reader, off_languages, () => { ReadLanguages(reader, off_languages, localization.num_languages); }); for (uint i = 0; i < 2; i++) { Pointer off_matrix = Pointer.Current(reader); Matrix mat = Matrix.Read(reader, off_matrix); } reader.ReadUInt32(); reader.ReadUInt16(); ReadLevelNames(reader, Pointer.Current(reader), 80); uint num_mapNames = reader.ReadUInt32(); Array.Resize(ref levels, (int)num_mapNames); reader.ReadUInt16(); reader.ReadUInt32(); reader.ReadUInt32(); if (Settings.s.game == Settings.Game.DD) { reader.ReadUInt32(); } // End of engineStructure Pointer off_light = Pointer.Read(reader); // the offset of a light. It's just an ordinary light. Pointer off_mainChar = Pointer.Read(reader); // superobject Pointer off_characterLaunchingSoundEvents = Pointer.Read(reader); if (Settings.s.game == Settings.Game.DD) { globals.off_backgroundGameMaterial = Pointer.Read(reader); } Pointer off_shadowPolygonVisualMaterial = Pointer.Read(reader); Pointer off_shadowPolygonGameMaterialInit = Pointer.Read(reader); Pointer off_shadowPolygonGameMaterial = Pointer.Read(reader); Pointer off_textureOfTextureShadow = Pointer.Read(reader); Pointer off_col_taggedFacesTable = Pointer.Read(reader); for (int i = 0; i < 10; i++) { Pointer off_elementForShadow = Pointer.Read(reader); Pointer off_geometricShadowObject = Pointer.Read(reader); } Pointer.Read(reader); // DemoSOList if (Settings.s.game == Settings.Game.R2Demo || Settings.s.game == Settings.Game.RedPlanet || Settings.s.mode == Settings.Mode.DonaldDuckPCDemo) { Pointer.Read(reader); } if (Settings.s.mode == Settings.Mode.DonaldDuckPCDemo) { reader.ReadUInt32(); reader.ReadUInt32(); } loadingState = "Loading level animation bank"; //print("Animation bank: " + Pointer.Current(reader)); await WaitIfNecessary(); AnimationBank.Read(reader, Pointer.Current(reader), 0, 1, files_array[Mem.LvlKeyFrames], append: true); animationBanks[1] = animationBanks[0]; } if (FileSystem.mode != FileSystem.Mode.Web) { string levelsFolder = gameDataBinFolder + ConvertPath(gameDsb.levelsDataPath) + "/"; ((SNA)files_array[0]).CreateMemoryDump(levelsFolder + "fix.dmp", true); ((SNA)files_array[1]).CreateMemoryDump(levelsFolder + lvlName + "/" + lvlName + ".dmp", true); } // Read PTX loadingState = "Loading level textures"; await WaitIfNecessary(); // Can't yield inside a lambda, so we must do it the old fashioned way, with off_current if (sna.PTX != null) { off_current = Pointer.Goto(ref reader, sna.PTX); await ReadTexturesLvl(reader, Pointer.Current(reader)); Pointer.Goto(ref reader, off_current); } /*Pointer.DoAt(ref reader, sna.PTX, () => { * ReadTexturesLvl(reader, Pointer.Current(reader)); * });*/ // Read background game material (DD only) globals.backgroundGameMaterial = GameMaterial.FromOffsetOrRead(globals.off_backgroundGameMaterial, reader); // Parse actual world & always structure loadingState = "Loading families"; await WaitIfNecessary(); ReadFamilies(reader); loadingState = "Creating animation bank"; await WaitIfNecessary(); if (Settings.s.engineVersion == Settings.EngineVersion.Montreal) { animationBanks = new AnimationBank[2]; animationBanks[0] = new AnimationBank(null) { animations = new Animation.Component.AnimA3DGeneral[0] }; animationBanks[1] = animationBanks[0]; } else if (Settings.s.engineVersion <= Settings.EngineVersion.TT) { uint maxAnimIndex = 0; foreach (State s in states) { if (s.anim_ref != null && s.anim_ref.anim_index > maxAnimIndex) { maxAnimIndex = s.anim_ref.anim_index; } } animationBanks = new AnimationBank[2]; animationBanks[0] = new AnimationBank(null) { animations = new Animation.Component.AnimA3DGeneral[maxAnimIndex + 1] }; foreach (State s in states) { if (s.anim_ref != null) { animationBanks[0].animations[s.anim_ref.anim_index] = s.anim_ref.a3d; } } animationBanks[1] = animationBanks[0]; } loadingState = "Loading superobject hierarchy"; await WaitIfNecessary(); await ReadSuperObjects(reader); loadingState = "Loading always structure"; await WaitIfNecessary(); ReadAlways(reader); loadingState = "Filling in cross-references"; await WaitIfNecessary(); ReadCrossReferences(reader); // TODO: Make more generic if (Settings.s.game == Settings.Game.R2) { loadingState = "Filling in comport names"; await WaitIfNecessary(); string path = gameDataBinFolder + "R2DC_Comports.json"; if (!FileSystem.FileExists(path)) { path = "Assets/StreamingAssets/R2DC_Comports.json"; // Offline, the json doesn't exist, so grab it from StreamingAssets } Stream stream = FileSystem.GetFileReadStream(path); if (stream != null) { ReadAndFillComportNames(stream); } } }
public Pointer(uint offset, FileWithPointers file) { this.offset = offset; this.file = file; }
async Task LoadLVL() { loadingState = "Loading level memory"; await WaitIfNecessary(); files_array[Mem.Lvl].GotoHeader(); Reader reader = files_array[Mem.Lvl].reader; long totalSize = reader.BaseStream.Length; //reader.ReadUInt32(); if (Settings.s.game == Settings.Game.R3 && (Settings.s.platform == Settings.Platform.PC || Settings.s.platform == Settings.Platform.Xbox || Settings.s.platform == Settings.Platform.Xbox360 || Settings.s.platform == Settings.Platform.PS3)) { reader.ReadUInt32(); // fix checksum? } reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); if (Settings.s.platform == Settings.Platform.PC || Settings.s.platform == Settings.Platform.Xbox || Settings.s.platform == Settings.Platform.Xbox360 || Settings.s.platform == Settings.Platform.PS3) { if (Settings.s.game == Settings.Game.R3) { string timeStamp = reader.ReadString(0x18); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); } else if (Settings.s.game == Settings.Game.RM || Settings.s.game == Settings.Game.RA || Settings.s.game == Settings.Game.Dinosaur) { reader.ReadUInt32(); reader.ReadUInt32(); } } reader.ReadBytes(0x104); // vignette if (Settings.s.game != Settings.Game.Dinosaur) { reader.ReadUInt32(); } loadingState = "Loading level textures"; await ReadTexturesLvl(reader, Pointer.Current(reader)); if ((Settings.s.platform == Settings.Platform.PC || Settings.s.platform == Settings.Platform.Xbox || Settings.s.platform == Settings.Platform.Xbox360 || Settings.s.platform == Settings.Platform.PS3) && !hasTransit && Settings.s.game != Settings.Game.Dinosaur) { Pointer off_lightMapTexture = Pointer.Read(reader); // g_p_stLMTexture Pointer.DoAt(ref reader, off_lightMapTexture, () => { lightmapTexture = TextureInfo.Read(reader, off_lightMapTexture); }); if (Settings.s.game == Settings.Game.R3) { Pointer off_overlightTexture = Pointer.Read(reader); // *(_DWORD *)(GLI_BIG_GLOBALS + 370068) Pointer.DoAt(ref reader, off_overlightTexture, () => { overlightTexture = TextureInfo.Read(reader, off_overlightTexture); }); } } Pointer off_animBankLvl = null; if (Settings.s.game == Settings.Game.Dinosaur) { // animation bank is read right here. off_animBankLvl = Pointer.Current(reader); // Note: only one 0x104 bank in fix. print("Lvl animation bank address: " + off_animBankLvl); animationBanks = new AnimationBank[5]; AnimationBank[] banks = AnimationBank.Read(reader, off_animBankLvl, 0, 1, files_array[Mem.LvlKeyFrames]); animationBanks[0] = banks[0]; } loadingState = "Loading globals"; await WaitIfNecessary(); globals.off_transitDynamicWorld = null; globals.off_actualWorld = Pointer.Read(reader); globals.off_dynamicWorld = Pointer.Read(reader); if (Settings.s.game == Settings.Game.R3 && (Settings.s.platform == Settings.Platform.PC || Settings.s.platform == Settings.Platform.Xbox || Settings.s.platform == Settings.Platform.Xbox360 || Settings.s.platform == Settings.Platform.PS3)) { reader.ReadUInt32(); // ??? } globals.off_inactiveDynamicWorld = Pointer.Read(reader); globals.off_fatherSector = Pointer.Read(reader); // It is I, Father Sector. globals.off_firstSubMapPosition = Pointer.Read(reader); globals.num_always = reader.ReadUInt32(); globals.spawnablePersos = LinkedList <Perso> .ReadHeader(reader, Pointer.Current(reader), LinkedList.Type.Double); globals.off_always_reusableSO = Pointer.Read(reader); // There are (num_always) empty SuperObjects starting with this one. globals.off_always_reusableUnknown1 = Pointer.Read(reader); // (num_always) * 0x2c blocks globals.off_always_reusableUnknown2 = Pointer.Read(reader); // (num_always) * 0x4 blocks // Read object types objectTypes = new ObjectType[3][]; for (uint i = 0; i < 3; i++) { Pointer off_names_header = Pointer.Current(reader); Pointer off_names_first = Pointer.Read(reader); Pointer off_names_last = Pointer.Read(reader); uint num_names = reader.ReadUInt32(); ReadObjectNamesTable(reader, off_names_first, num_names, i); } Pointer off_light = Pointer.Read(reader); // the offset of a light. It's just an ordinary light. Pointer off_characterLaunchingSoundEvents = Pointer.Read(reader); Pointer off_collisionGeoObj = Pointer.Read(reader); Pointer off_staticCollisionGeoObj = Pointer.Read(reader); if (!hasTransit) { reader.ReadUInt32(); // viewport related <--- cameras in here } Pointer off_unknown_first = Pointer.Read(reader); Pointer off_unknown_last = Pointer.Read(reader); uint num_unknown = reader.ReadUInt32(); families = LinkedList <Family> .ReadHeader(reader, Pointer.Current(reader), type : LinkedList.Type.Double); Pointer off_alwaysActiveCharacters_first = Pointer.Read(reader); Pointer off_alwaysActiveCharacters_last = Pointer.Read(reader); uint num_alwaysActiveChars = reader.ReadUInt32(); if (!hasTransit) { Pointer off_mainCharacters_first = Pointer.Read(reader); Pointer off_mainCharacters_last = Pointer.Read(reader); uint num_mainCharacters_entries = reader.ReadUInt32(); } reader.ReadUInt32(); // only used if there was no transit in the previous lvl. Always 00165214 in R3GC? reader.ReadUInt32(); // related to "SOL". What is this? Good question. reader.ReadUInt32(); // same if (Settings.s.game != Settings.Game.Dinosaur) { reader.ReadUInt32(); // same } Pointer off_cineManager = Pointer.Read(reader); byte unk = reader.ReadByte(); byte IPO_numRLItables = reader.ReadByte(); reader.ReadUInt16(); Pointer off_COL_taggedFacesTable = Pointer.Read(reader); uint num_COL_maxTaggedFaces = reader.ReadUInt32(); off_collisionGeoObj = Pointer.Read(reader); off_staticCollisionGeoObj = Pointer.Read(reader); // The ptrsTable seems to be related to sound events. Perhaps cuuids. reader.ReadUInt32(); if (Settings.s.game == Settings.Game.Dinosaur) { for (int i = 0; i < 50; i++) { reader.ReadUInt32(); } // Actually, the previous uint is an amount for this array of uints, but it's padded to always be 50 long } uint num_ptrsTable = reader.ReadUInt32(); if (Settings.s.game == Settings.Game.R3) { uint bool_ptrsTable = reader.ReadUInt32(); } Pointer off_ptrsTable = Pointer.Read(reader); uint num_internalStructure = num_ptrsTable; if (Settings.s.mode == Settings.Mode.Rayman3GC) { reader.ReadUInt32(); } Pointer off_internalStructure_first = Pointer.Read(reader); Pointer off_internalStructure_last = Pointer.Read(reader); if (!hasTransit && Settings.s.game == Settings.Game.R3) { uint num_geometric = reader.ReadUInt32(); Pointer off_array_geometric = Pointer.Read(reader); Pointer off_array_geometric_RLI = Pointer.Read(reader); Pointer off_array_transition_flags = Pointer.Read(reader); } else if (Settings.s.game == Settings.Game.RA || Settings.s.game == Settings.Game.RM || Settings.s.game == Settings.Game.Dinosaur || Settings.s.game == Settings.Game.DDPK) { uint num_unk = reader.ReadUInt32(); Pointer unk_first = Pointer.Read(reader); if (Settings.s.game != Settings.Game.Dinosaur) { Pointer unk_last = Pointer.Read(reader); } } uint num_visual_materials = reader.ReadUInt32(); Pointer off_array_visual_materials = Pointer.Read(reader); if (Settings.s.mode != Settings.Mode.RaymanArenaGC && Settings.s.mode != Settings.Mode.RaymanArenaGCDemo && Settings.s.mode != Settings.Mode.DonaldDuckPKGC) { Pointer off_dynamic_so_list = Pointer.Read(reader); // Parse SO list Pointer.DoAt(ref reader, off_dynamic_so_list, () => { LinkedList <SuperObject> .ReadHeader(reader, off_dynamic_so_list); /*Pointer off_so_list_first = Pointer.Read(reader); * Pointer off_so_list_last = Pointer.Read(reader); * Pointer off_so_list_current = off_so_list_first; * uint num_so_list = reader.ReadUInt32();*/ /*if (experimentalObjectLoading) { * for (uint i = 0; i < num_so_list; i++) { * R3Pointer.Goto(ref reader, off_so_list_current); * R3Pointer off_so_list_next = R3Pointer.Read(reader); * R3Pointer off_so_list_prev = R3Pointer.Read(reader); * R3Pointer off_so_list_start = R3Pointer.Read(reader); * R3Pointer off_so = R3Pointer.Read(reader); * R3Pointer.Goto(ref reader, off_so); * ParseSuperObject(reader, off_so, true, true); * off_so_list_current = off_so_list_next; * } * }*/ }); } // Parse materials list loadingState = "Loading visual materials"; await WaitIfNecessary(); Pointer.DoAt(ref reader, off_array_visual_materials, () => { for (uint i = 0; i < num_visual_materials; i++) { Pointer off_material = Pointer.Read(reader); Pointer.DoAt(ref reader, off_material, () => { //print(Pointer.Current(reader)); visualMaterials.Add(VisualMaterial.Read(reader, off_material)); }); } }); if (hasTransit) { loadingState = "Loading transit memory"; await WaitIfNecessary(); Pointer off_transit = new Pointer(16, files_array[Mem.Transit]); // It's located at offset 20 in transit Pointer.DoAt(ref reader, off_transit, () => { if (Settings.s.platform == Settings.Platform.PC || Settings.s.platform == Settings.Platform.Xbox || Settings.s.platform == Settings.Platform.Xbox360 || Settings.s.platform == Settings.Platform.PS3) { Pointer off_lightMapTexture = Pointer.Read(reader); // g_p_stLMTexture Pointer.DoAt(ref reader, off_lightMapTexture, () => { lightmapTexture = TextureInfo.Read(reader, off_lightMapTexture); }); if (Settings.s.game == Settings.Game.R3) { Pointer off_overlightTexture = Pointer.Read(reader); // *(_DWORD *)(GLI_BIG_GLOBALS + 370068) Pointer.DoAt(ref reader, off_overlightTexture, () => { overlightTexture = TextureInfo.Read(reader, off_overlightTexture); }); } } globals.off_transitDynamicWorld = Pointer.Read(reader); globals.off_actualWorld = Pointer.Read(reader); globals.off_dynamicWorld = Pointer.Read(reader); globals.off_inactiveDynamicWorld = Pointer.Read(reader); }); } // Parse actual world & always structure loadingState = "Loading families"; await WaitIfNecessary(); ReadFamilies(reader); loadingState = "Loading superobject hierarchy"; await WaitIfNecessary(); ReadSuperObjects(reader); loadingState = "Loading always structure"; await WaitIfNecessary(); ReadAlways(reader); Pointer.DoAt(ref reader, off_cineManager, () => { cinematicsManager = CinematicsManager.Read(reader, off_cineManager); }); // off_current should be after the dynamic SO list positions. // Parse transformation matrices and other settings(state? :o) for fix characters loadingState = "Loading settings for persos in fix"; await WaitIfNecessary(); uint num_perso_with_settings_in_fix = (uint)persoInFix.Length; if (Settings.s.game == Settings.Game.R3) { num_perso_with_settings_in_fix = reader.ReadUInt32(); } for (int i = 0; i < num_perso_with_settings_in_fix; i++) { Pointer off_perso_so_with_settings_in_fix = null, off_matrix = null; SuperObject so = null; Matrix mat = null; if (Settings.s.game == Settings.Game.R3) { off_perso_so_with_settings_in_fix = Pointer.Read(reader); off_matrix = Pointer.Current(reader); mat = Matrix.Read(reader, off_matrix); reader.ReadUInt32(); // is one of these the state? doesn't appear to change tho reader.ReadUInt32(); so = SuperObject.FromOffset(off_perso_so_with_settings_in_fix); } else if (Settings.s.game == Settings.Game.RA || Settings.s.game == Settings.Game.RM || Settings.s.game == Settings.Game.Dinosaur) { off_matrix = Pointer.Current(reader); mat = Matrix.Read(reader, off_matrix); so = superObjects.FirstOrDefault(s => s.off_data == persoInFix[i]); } if (so != null) { so.off_matrix = off_matrix; so.matrix = mat; if (so.Gao != null) { so.Gao.transform.localPosition = mat.GetPosition(convertAxes: true); so.Gao.transform.localRotation = mat.GetRotation(convertAxes: true); so.Gao.transform.localScale = mat.GetScale(convertAxes: true); } } } if (Settings.s.platform == Settings.Platform.GC) { reader.ReadBytes(0x800); // floats } loadingState = "Loading animation banks"; await WaitIfNecessary(); if (Settings.s.game != Settings.Game.Dinosaur) { off_animBankLvl = Pointer.Read(reader); // Note: 4 0x104 banks in lvl. print("Lvl animation bank address: " + off_animBankLvl); animationBanks = new AnimationBank[5]; if (off_animBankFix != off_animBankLvl) { Pointer.DoAt(ref reader, off_animBankFix, () => { animationBanks[0] = AnimationBank.Read(reader, off_animBankFix, 0, 1, files_array[Mem.FixKeyFrames])[0]; }); } Pointer.DoAt(ref reader, off_animBankLvl, () => { AnimationBank[] banks = AnimationBank.Read(reader, off_animBankLvl, 1, 4, files_array[Mem.LvlKeyFrames]); for (int i = 0; i < 4; i++) { animationBanks[1 + i] = banks[i]; } }); if (off_animBankFix == off_animBankLvl) { animationBanks[0] = animationBanks[1]; } } // Load additional animation banks string extraAnimFolder = "Anim/"; if (Settings.s.mode == Settings.Mode.RaymanArenaGCDemo) { extraAnimFolder = lvlName + "/"; } for (int i = 0; i < families.Count; i++) { if (families[i] != null && families[i].animBank > 4 && objectTypes[0][families[i].family_index].id != 0xFF) { int animBank = families[i].animBank; loadingState = "Loading additional animation bank " + animBank; await WaitIfNecessary(); int animFileID = objectTypes[0][families[i].family_index].id; if (Settings.s.mode == Settings.Mode.RaymanArenaGCDemo) { animFileID = animBank - 5; } string animName = extraAnimFolder + "ani" + animFileID.ToString(); string kfName = extraAnimFolder + "key" + animFileID.ToString() + "kf"; //print(animBank + " - " + objectTypes[0][families[i].family_index].id); int fileID = animBank + 102; int kfFileID = animBank + 2; // Anim bank will start at 5, so this will start at 7 if (Settings.s.game == Settings.Game.RM) { fileID = animBank; } // Prepare files for WebGL await PrepareFile(gameDataBinFolder + animName + ".lvl"); if (FileSystem.FileExists(gameDataBinFolder + animName + ".lvl")) { await PrepareFile(gameDataBinFolder + animName + ".ptr"); } await PrepareFile(gameDataBinFolder + kfName + ".lvl"); if (FileSystem.FileExists(gameDataBinFolder + kfName + ".lvl")) { await PrepareFile(gameDataBinFolder + kfName + ".ptr"); } FileWithPointers animFile = InitExtraLVL(animName, fileID); FileWithPointers kfFile = InitExtraLVL(kfName, fileID); if (animFile != null) { if (animBank >= animationBanks.Length) { Array.Resize(ref animationBanks, animBank + 1); } Pointer off_animBankExtra = new Pointer(0, animFile); Pointer.DoAt(ref reader, off_animBankExtra, () => { int alignBytes = reader.ReadInt32(); if (alignBytes > 0) { reader.Align(4, alignBytes); } off_animBankExtra = Pointer.Current(reader); animationBanks[animBank] = AnimationBank.Read(reader, off_animBankExtra, (uint)animBank, 1, kfFile)[0]; }); } } } loadingState = "Filling in cross-references"; await WaitIfNecessary(); ReadCrossReferences(reader); }