public int MarioFunction(SuperObject *spo, int *nodeInterp) { RaymanState state = RaymanState.Inactive; byte dsgVar16_value = 0; int * idleTimer = null; if (spo->PersoData->GetModelName(w) == "YLT_RaymanModel") { var dsgVars = spo->PersoData->GetDsgVarList(); dsgVar16_value = *(byte *)dsgVars[16].valuePtrCurrent.ToPointer(); byte dsgVar9_value = *(byte *)dsgVars[9].valuePtrCurrent.ToPointer(); state = (RaymanState)dsgVar9_value; idleTimer = (int *)dsgVars[24].valuePtrCurrent; } bool strafing = InputStructure.GetInputStructure()->GetEntryAction(EntryActionNames.Action_Strafe)->IsValidated(); if (spo->PersoData->GetInstanceName(w) != "Rayman" || dsgVar16_value != 1 || !AllowTankControlState(state)) { currentSpeed = 0; return(EngineFunctions.fn_p_stReadAnalogJoystickMario.Call(spo, nodeInterp)); } int result = OriginalScript(spo, nodeInterp); DoTankControls(spo, state, strafing, idleTimer); return(result); }
static InputStructure Parse(string fileName) { Console.WriteLine($"parsing file started: {fileName}"); var inputStructure = new InputStructure(); using (var sr = new StreamReader(fileName)) { var firstLine = sr.ReadLine().Split(' '); inputStructure.rows = Int32.Parse(firstLine[0]); inputStructure.columns = Int32.Parse(firstLine[1]); inputStructure.vehiclesNo = Int32.Parse(firstLine[2]); inputStructure.numberOfRides = Int32.Parse(firstLine[3]); inputStructure.perRideBonus = Int32.Parse(firstLine[4]); inputStructure.numberOfSteps = Int32.Parse(firstLine[5]); inputStructure.rides = new List <Ride>(); string line; int rideNo = 0; while ((line = sr.ReadLine()) != null) { var splitted = line.Split(' '); var ride = new Ride(); ride.rideNo = rideNo++; ride.rowStart = Int32.Parse(splitted[0]); ride.colStart = Int32.Parse(splitted[1]); ride.rowEnd = Int32.Parse(splitted[2]); ride.colEnd = Int32.Parse(splitted[3]); ride.earliestStart = Int32.Parse(splitted[4]); ride.latestEnd = Int32.Parse(splitted[5]); inputStructure.rides.Add(ride); } } Console.WriteLine($"parsing file finished: {fileName}"); return(inputStructure); }
async Task LoadDreamcast() { textures = new TextureInfo[0]; loadingState = "Loading fixed memory"; await WaitIfNecessary(); files_array[Mem.Fix].GotoHeader(); Reader reader = files_array[Mem.Fix].reader; Pointer off_base_fix = Pointer.Current(reader); uint base_language = reader.ReadUInt32(); //Pointer off_language = Pointer.Read(reader); reader.ReadUInt32(); uint num_text_language = reader.ReadUInt32(); reader.ReadUInt16(); reader.ReadUInt16(); reader.ReadUInt32(); // base Pointer off_text_general = Pointer.Read(reader); localization = FromOffsetOrRead <LocalizationStructure>(reader, off_text_general); Pointer off_inputStructure = Pointer.Read(reader); Pointer.DoAt(ref reader, off_inputStructure, () => { inputStruct = InputStructure.Read(reader, off_inputStructure); foreach (EntryAction ea in inputStruct.entryActions) { print(ea.ToString()); } }); await WaitIfNecessary(); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); Pointer.Read(reader); Pointer off_levelNames = Pointer.Read(reader); Pointer off_languages = Pointer.Read(reader); uint num_levelNames = reader.ReadUInt32(); uint num_languages = reader.ReadUInt32(); reader.ReadUInt32(); // same as num_levelNames Pointer.DoAt(ref reader, off_levelNames, () => { lvlNames = new string[num_levelNames]; for (uint i = 0; i < num_levelNames; i++) { lvlNames[i] = reader.ReadString(0x1E); } }); Pointer.DoAt(ref reader, off_languages, () => { ReadLanguages(reader, off_languages, num_languages); }); if (languages != null && localization != null) { for (int i = 0; i < num_languages; i++) { loadingState = "Loading text files: " + (i + 1) + "/" + num_languages; string langFilePath = gameDataBinFolder + "TEXTS/" + languages[i].ToUpper() + ".LNG"; await PrepareFile(langFilePath); files_array[2] = new DCDAT(languages[i], langFilePath, 2); ((DCDAT)files_array[2]).SetHeaderOffset(base_language); files_array[2].GotoHeader(); localization.ReadLanguageTableDreamcast(files_array[2].reader, i, (ushort)num_text_language); files_array[2].Dispose(); } } loadingState = "Loading fixed textures"; await WaitIfNecessary(); Pointer off_events_fix = Pointer.Read(reader); uint num_events_fix = reader.ReadUInt32(); uint num_textures_fix = reader.ReadUInt32(); Pointer off_textures_fix = Pointer.Read(reader); Pointer.DoAt(ref reader, off_textures_fix, () => { Array.Resize(ref textures, (int)num_textures_fix); for (uint i = 0; i < num_textures_fix; i++) { Pointer off_texture = Pointer.Read(reader); textures[i] = null; Pointer.DoAt(ref reader, off_texture, () => { textures[i] = TextureInfo.Read(reader, off_texture); }); } TEX tex = new TEX(texPaths[0]); for (uint i = 0; i < num_textures_fix; i++) { if (textures[i] != null && tex.Count > i) { textures[i].Texture = tex.textures[i]; } } }); loadingState = "Loading level memory"; await WaitIfNecessary(); files_array[Mem.Lvl].GotoHeader(); reader = files_array[Mem.Lvl].reader; // Animation stuff Pointer off_animationBank = Pointer.Current(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); reader.ReadUInt32(); Pointer.Read(reader); // Globals globals.off_actualWorld = Pointer.Read(reader); globals.off_dynamicWorld = Pointer.Read(reader); globals.off_inactiveDynamicWorld = Pointer.Read(reader); globals.off_fatherSector = Pointer.Read(reader); reader.ReadUInt32(); Pointer off_always = Pointer.Read(reader); Pointer.DoAt(ref reader, off_always, () => { globals.num_always = reader.ReadUInt32(); globals.spawnablePersos = LinkedList <Perso> .ReadHeader(reader, Pointer.Current(reader), LinkedList.Type.Double); globals.spawnablePersos.FillPointers(reader, globals.spawnablePersos.off_tail, globals.spawnablePersos.offset); globals.off_always_reusableSO = Pointer.Read(reader); // There are (num_always) empty SuperObjects starting with this one. }); Pointer.Read(reader); Pointer off_objectTypes = Pointer.Read(reader); Pointer.DoAt(ref reader, off_objectTypes, () => { // 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); } }); Pointer.Read(reader); Pointer off_mainChar = Pointer.Read(reader); reader.ReadUInt32(); uint num_persoInFixPointers = reader.ReadUInt32(); Pointer off_persoInFixPointers = Pointer.Read(reader); //Pointer[] persoInFixPointers = new Pointer[num_persoInFixPointers]; Pointer.DoAt(ref reader, off_persoInFixPointers, () => { for (int i = 0; i < num_persoInFixPointers; i++) { Pointer off_perso = Pointer.Read(reader); Pointer off_so = Pointer.Read(reader); byte[] unk = reader.ReadBytes(4); Pointer off_matrix = Pointer.Current(reader); // It's better to change the pointer instead of the data as that is reused in some places byte[] matrixData = reader.ReadBytes(0x68); byte[] soFlags = reader.ReadBytes(4); byte[] brothersAndParent = reader.ReadBytes(12); Pointer.DoAt(ref reader, off_perso, () => { reader.ReadUInt32(); Pointer off_stdGame = Pointer.Read(reader); if (off_stdGame != null && off_so != null) { ((DCDAT)off_stdGame.file).OverwriteData(off_stdGame.FileOffset + 0xC, off_so.offset); } }); if (off_so != null) { ((DCDAT)off_so.file).OverwriteData(off_so.FileOffset + 0x14, brothersAndParent); ((DCDAT)off_so.file).OverwriteData(off_so.FileOffset + 0x20, off_matrix.offset); ((DCDAT)off_so.file).OverwriteData(off_so.FileOffset + 0x30, soFlags); } } }); await WaitIfNecessary(); Pointer.Read(reader); // contains a pointer to the camera SO Pointer off_cameras = Pointer.Read(reader); // Double linkedlist of cameras Pointer off_families = Pointer.Read(reader); Pointer.DoAt(ref reader, off_families, () => { families = LinkedList <Family> .ReadHeader(reader, Pointer.Current(reader), type: LinkedList.Type.Double); families.FillPointers(reader, families.off_tail, families.off_head); }); Pointer.Read(reader); // At this pointer: a double linkedlist of fix perso's with headers (soptr, next, prev, hdr) Pointer.Read(reader); // Rayman reader.ReadUInt32(); Pointer.Read(reader); // Camera reader.ReadUInt32(); reader.ReadUInt32(); loadingState = "Loading level textures"; await WaitIfNecessary(); uint num_textures_lvl = reader.ReadUInt32(); uint num_textures_total = num_textures_fix + num_textures_lvl; Pointer off_textures_lvl = Pointer.Read(reader); Pointer.DoAt(ref reader, off_textures_lvl, () => { Array.Resize(ref textures, (int)num_textures_total); for (uint i = num_textures_fix; i < num_textures_total; i++) { Pointer off_texture = Pointer.Read(reader); textures[i] = null; Pointer.DoAt(ref reader, off_texture, () => { textures[i] = TextureInfo.Read(reader, off_texture); }); } TEX tex = new TEX(texPaths[1]); for (uint i = 0; i < num_textures_lvl; i++) { if (textures[num_textures_fix + i] != null && tex.Count > i) { textures[num_textures_fix + i].Texture = tex.textures[i]; } } }); loadingState = "Loading families"; await WaitIfNecessary(); ReadFamilies(reader); loadingState = "Loading animation banks"; await WaitIfNecessary(); Pointer.DoAt(ref reader, off_animationBank, () => { animationBanks = new AnimationBank[2]; animationBanks[0] = AnimationBank.ReadDreamcast(reader, off_animationBank, off_events_fix, num_events_fix); animationBanks[1] = animationBanks[0]; }); loadingState = "Loading superobject hierarchy"; await WaitIfNecessary(); ReadSuperObjects(reader); loadingState = "Loading always structure"; await WaitIfNecessary(); ReadAlways(reader); loadingState = "Filling in cross-references"; await WaitIfNecessary(); ReadCrossReferences(reader); loadingState = "Loading behavior copies"; await WaitIfNecessary(); ReadBehaviorCopies(reader); await WaitIfNecessary(); /*print("Sectors: " + sectors.Count); * for (int i = 0; i < sectors.Count; i++) { * print("Sector " + i + "\t" + sectors[i].persos.Count + "\t" + sectors[i].staticLights.Count); * } * print("Persos: " + persos.Count); * print("World: " + superObjects.Count(so => so.type == SuperObject.Type.World)); * print("IPOs: " + superObjects.Count(so => so.type == SuperObject.Type.IPO || so.type == SuperObject.Type.IPO_2)); * print("POs: " + superObjects.Count(so => so.type == SuperObject.Type.PO)); * print("SOs: " + superObjects.Count); * print("Families: " + families.Count); * print("Always: " + globals.num_always + " - Spawnables: " + globals.spawnablePersos.Count);*/ // Parse transformation matrices and other settings for fix characters /*if (off_mainChar != null && off_matrix_mainChar != null) { * SuperObject so = SuperObject.FromOffset(off_mainChar); * Pointer.DoAt(ref reader, off_matrix_mainChar, () => { * reader.ReadUInt32(); * reader.ReadUInt32(); * reader.ReadUInt32(); * reader.ReadUInt32(); * reader.ReadUInt32(); * reader.ReadUInt32(); * reader.ReadUInt32(); * reader.ReadUInt32(); * reader.ReadUInt32(); * Pointer off_matrix = Pointer.Current(reader); * Matrix mat = Matrix.Read(reader, off_matrix); * 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); * } * } * }); * }*/ }
async UniTask LoadFIXSNA() { loadingState = "Loading fixed memory"; await WaitIfNecessary(); files_array[Mem.Fix].GotoHeader(); Reader reader = files_array[Mem.Fix].reader; print("FIX GPT offset: " + Pointer.Current(reader)); SNA sna = (SNA)files_array[Mem.Fix]; if (Settings.s.engineVersion <= Settings.EngineVersion.TT) { // Tonic Trouble inputStruct = new InputStructure(null); uint stringCount = Settings.s.game == Settings.Game.TTSE ? 351 : (uint)gameDsb.textFiles.Sum(t => t.strings.Count); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); if (Settings.s.game == Settings.Game.TTSE) { for (int i = 0; i < 50; i++) { Pointer.Read(reader); } } else { for (int i = 0; i < 100; i++) { Pointer.Read(reader); } } reader.ReadUInt32(); // 0x35 if (Settings.s.game != Settings.Game.TTSE) { reader.ReadBytes(0x80); // contains strings like MouseXPos, input related. first dword of this is a pointer to inputstructure probably } reader.ReadBytes(0x90); Pointer.Read(reader); reader.ReadUInt32(); // 0x28 reader.ReadUInt32(); // 0x1 if (Settings.s.game == Settings.Game.TTSE) { Pointer.Read(reader); } for (int i = 0; i < 100; i++) { Pointer.Read(reader); } for (int i = 0; i < 100; i++) { Pointer.Read(reader); } reader.ReadUInt32(); // 0x1 if (Settings.s.game == Settings.Game.TTSE) { reader.ReadBytes(0xB4); } else { if (stringCount != 598) // English version and probably other versions have 603 strings. It's a hacky way to check which version. { reader.ReadBytes(0x2CC); } else // French version: 598 { reader.ReadBytes(0x2C0); } } reader.ReadBytes(0x1C); // Init strings reader.ReadUInt32(); // 0 reader.ReadUInt32(); // 1 reader.ReadUInt32(); // ??? Pointer.Read(reader); for (int i = 0; i < stringCount; i++) { Pointer.Read(reader); // read num_loaded_strings pointers here } reader.ReadBytes(0xC); // dword_51A728. probably a table of some sort: 2 ptrs and a number if (Settings.s.game != Settings.Game.TTSE) // There's more but what is even the point in reading all this { reader.ReadUInt32(); Pointer.Read(reader); reader.ReadBytes(0x14); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); reader.ReadUInt32(); // 0, so can be pointer too reader.ReadUInt32(); // 0, so can be pointer too reader.ReadUInt32(); // 0, so can be pointer too reader.ReadUInt32(); // 0, so can be pointer too reader.ReadUInt32(); // 0, so can be pointer too reader.ReadUInt32(); // 0, so can be pointer too reader.ReadUInt32(); // 0, so can be pointer too reader.ReadUInt32(); // 0, so can be pointer too reader.ReadBytes(0x30); reader.ReadBytes(0x960); } } else if (Settings.s.engineVersion == Settings.EngineVersion.Montreal) { uint num_strings = 0; inputStruct = new InputStructure(null); // SDA Pointer.DoAt(ref reader, sna.SDA, () => { print(Pointer.Current(reader)); reader.ReadUInt32(); reader.ReadUInt32(); // same as next num_strings = reader.ReadUInt32(); uint indexOfTextGlobal = reader.ReadUInt32(); // dword_6EEE78 uint dword_83EC58 = reader.ReadUInt32(); print(num_strings + " - " + Pointer.Current(reader)); }); // DLG Pointer.DoAt(ref reader, sna.DLG, () => { Pointer off_strings = Pointer.Read(reader); for (int i = 0; i < num_strings; i++) { Pointer.Read(reader); } reader.ReadUInt32(); }); // GPT sna.GotoHeader(); Pointer.Read(reader); Pointer off_mainLight = Pointer.Read(reader); uint lpPerformanceCount = reader.ReadUInt32(); Pointer.Read(reader); Pointer off_defaultMaterial = Pointer.Read(reader); Pointer off_geometricObject1 = Pointer.Read(reader); Pointer off_geometricObject2 = Pointer.Read(reader); Pointer off_geometricObject3 = Pointer.Read(reader); reader.ReadBytes(0x90); // FON_ related reader.ReadBytes(0x3D54); // FON_ related for (int i = 0; i < 100; i++) { Pointer.Read(reader); // matrix in stack } uint matrixInStack = reader.ReadUInt32(); // number of matrix in stack reader.ReadBytes(0xC); reader.ReadBytes(0x20); reader.ReadUInt32(); reader.ReadUInt32(); Pointer.Read(reader); Pointer.Read(reader); for (int i = 0; i < num_strings; i++) { Pointer.Read(reader); } LinkedList <int> fontDefinitions = LinkedList <int> .ReadHeader(reader, Pointer.Current(reader)); Pointer.Read(reader); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); Pointer off_geometricObject4 = Pointer.Read(reader); Pointer off_geometricObject5 = Pointer.Read(reader); Pointer off_geometricObject6 = Pointer.Read(reader); Pointer off_visualmaterial1 = Pointer.Read(reader); Pointer off_visualmaterial2 = Pointer.Read(reader); for (int i = 0; i < 10; i++) { Pointer off_texture = Pointer.Read(reader); } Pointer off_visualmaterial3 = Pointer.Read(reader); Pointer off_gamematerial = Pointer.Read(reader); uint geometricElementIndexGlobal = reader.ReadUInt32(); Pointer off_texture2 = Pointer.Read(reader); Pointer off_geometricObject7 = Pointer.Read(reader); for (uint i = 0; i < 7; i++) { Pointer.Read(reader); // Material for stencils. Order: corner, border, center, side, redarrow, bullet, and another one } Pointer dword_5DCB9C = Pointer.Read(reader); // Now comes INV_fn_vSnaMultilanguageLoading print(Pointer.Current(reader)); } else { Pointer off_identityMatrix = Pointer.Read(reader); reader.ReadBytes(50 * 4); uint matrixInStack = reader.ReadUInt32(); Pointer off_collisionGeoObj = Pointer.Read(reader); Pointer off_staticCollisionGeoObj = Pointer.Read(reader); loadingState = "Loading input structure"; await WaitIfNecessary(); for (int i = 0; i < Settings.s.numEntryActions; i++) { Pointer.Read(reader); // 3DOS_EntryActions } Pointer off_IPT_keyAndPadDefine = Pointer.Read(reader); ReadKeypadDefine(reader, off_IPT_keyAndPadDefine); inputStruct = InputStructure.Read(reader, Pointer.Current(reader)); foreach (EntryAction ea in inputStruct.entryActions) { print(ea.ToString()); } print("Num entractions: " + inputStruct.num_entryActions); print("Off entryactions: " + inputStruct.off_entryActions); Pointer off_IPT_entryElementList = Pointer.Read(reader); print("Off entryelements: " + off_IPT_entryElementList); loadingState = "Loading text"; await WaitIfNecessary(); localization = FromOffsetOrRead <LocalizationStructure>(reader, Pointer.Current(reader), inline: true); // FON_g_stGeneral loadingState = "Loading fixed animation bank"; await WaitIfNecessary(); animationBanks = new AnimationBank[2]; // 1 in fix, 1 in lvl animationBanks[0] = AnimationBank.Read(reader, Pointer.Current(reader), 0, 1, files_array[Mem.FixKeyFrames])[0]; print("Fix animation bank: " + animationBanks[0].off_header); Pointer off_fixInfo = Pointer.Read(reader); } // Read PTX loadingState = "Loading fixed 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) { Pointer off_current = Pointer.Goto(ref reader, sna.PTX); await ReadTexturesFix(reader, Pointer.Current(reader)); Pointer.Goto(ref reader, off_current); } /*Pointer.DoAt(ref reader, sna.PTX, () => { * ReadTexturesFix(reader, Pointer.Current(reader)); * });*/ }
async Task LoadFIX() { loadingState = "Loading fixed memory"; await WaitIfNecessary(); files_array[Mem.Fix].GotoHeader(); Reader reader = files_array[Mem.Fix].reader; // Read fix header //reader.ReadUInt32(); 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(); } } Pointer off_identityMatrix = Pointer.Read(reader); loadingState = "Loading text"; await WaitIfNecessary(); localization = FromOffsetOrRead <LocalizationStructure>(reader, Pointer.Current(reader), inline: true); uint num_lvlNames = reader.ReadUInt32(); uint num_fixEntries1 = reader.ReadUInt32(); // Read tables under header for (uint i = 0; i < num_fixEntries1; i++) { string savName = new string(reader.ReadChars(0xC)); } for (uint i = 0; i < num_fixEntries1; i++) { string savMapName = new string(reader.ReadChars(0xC)); } ReadLevelNames(reader, Pointer.Current(reader), num_lvlNames); 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) { reader.ReadChars(0x1E); reader.ReadChars(0x1E); // two zero entries } string firstMapName = new string(reader.ReadChars(0x1E)); //print(firstMapName); if (reader.BaseStream.Position % 4 == 0) { reader.ReadUInt32(); } else { reader.ReadUInt16(); } uint num_languages = reader.ReadUInt32(); Pointer off_languages = Pointer.Read(reader); Pointer.DoAt(ref reader, off_languages, () => { ReadLanguages(reader, off_languages, num_languages); }); loadingState = "Loading fixed textures"; print("Fix textures address: " + Pointer.Current(reader)); await ReadTexturesFix(reader, Pointer.Current(reader)); // Defaults for Rayman 3 PC. Sizes are hardcoded in the exes and might differ for versions too :/ int sz_entryActions = 0x100; int sz_randomStructure = 0xDC; int sz_videoStructure = 0x18; int sz_musicMarkerSlot = 0x28; int sz_binDataForMenu = 0x020C; int num_menuPages = 35; if (Settings.s.mode == Settings.Mode.Rayman3GC) { sz_entryActions = 0xE8; sz_binDataForMenu = 0x01F0; } else if (Settings.s.mode == Settings.Mode.RaymanArenaGC || Settings.s.mode == Settings.Mode.RaymanArenaGCDemo) { sz_entryActions = 0xC4; } else if (Settings.s.mode == Settings.Mode.RaymanArenaPC || Settings.s.mode == Settings.Mode.RaymanMPC) { sz_entryActions = 0xDC; } else if (Settings.s.mode == Settings.Mode.DinosaurPC) { sz_entryActions = 0xD8; sz_randomStructure = 0xE0; } else if (Settings.s.mode == Settings.Mode.DonaldDuckPKGC) { sz_entryActions = 0xC0; } else if (Settings.s.mode == Settings.Mode.RaymanArenaXbox) { sz_entryActions = 0xF0; } if (Settings.s.platform == Settings.Platform.Xbox) { sz_videoStructure = 0x108; sz_binDataForMenu = 0x1AC; } else if (Settings.s.platform == Settings.Platform.Xbox360) { sz_videoStructure = 0x108; sz_entryActions = 0x108; sz_binDataForMenu = 0x33C; num_menuPages = 96; } else if (Settings.s.platform == Settings.Platform.PS3) { sz_videoStructure = 0x108; sz_entryActions = 0x108; sz_binDataForMenu = 0x348; num_menuPages = 96; } loadingState = "Loading input structure"; await WaitIfNecessary(); inputStruct = InputStructure.Read(reader, Pointer.Current(reader)); foreach (EntryAction ea in inputStruct.entryActions) { print(ea.ToString()); } 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_IPT_keyAndPadDefine = Pointer.Read(reader); ReadKeypadDefine(reader, off_IPT_keyAndPadDefine); } reader.ReadBytes(sz_entryActions); // 3DOS_EntryActions uint num_persoInFix = reader.ReadUInt32(); persoInFix = new Pointer[num_persoInFix]; for (int i = 0; i < num_persoInFix; i++) { persoInFix[i] = Pointer.Read(reader); } reader.ReadBytes(sz_randomStructure); uint soundEventTableIndexInFix = reader.ReadUInt32(); Pointer off_soundEventTable = Pointer.Read(reader); fonts = FromOffsetOrRead <FontStructure>(reader, Pointer.Current(reader), inline: true); reader.ReadBytes(sz_videoStructure); // Contains amount of videos and pointer to video filename table print(Pointer.Current(reader)); if (Settings.s.game == Settings.Game.R3) { uint num_musicMarkerSlots = reader.ReadUInt32(); for (int i = 0; i < num_musicMarkerSlots; i++) { reader.ReadBytes(sz_musicMarkerSlot); } reader.ReadBytes(sz_binDataForMenu); 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_bgMaterialForMenu2D = Pointer.Read(reader); Pointer off_fixMaterialForMenu2D = Pointer.Read(reader); Pointer off_fixMaterialForSelectedFilms = Pointer.Read(reader); Pointer off_fixMaterialForArcadeAndFilms = Pointer.Read(reader); for (int i = 0; i < num_menuPages; i++) { Pointer off_menuPage = Pointer.Read(reader); } } } /*loadingState = "Loading fixed animation bank"; * await WaitIfNecessary();*/ if (Settings.s.game != Settings.Game.Dinosaur) { off_animBankFix = Pointer.Read(reader); // Note: only one 0x104 bank in fix. //print(Pointer.Current(reader)); print("Fix animation bank address: " + off_animBankFix); } /*if (off_animBankFix.file == files_array[Mem.Lvl]) { * animationBanks = new AnimationBank[4]; // 1 in fix, 4 in lvl * Pointer.DoAt(ref reader, off_animBankFix, () => { * animationBanks[0] = AnimationBank.Read(reader, off_animBankFix, 0, 1, files_array[Mem.FixKeyFrames])[0]; * }); * } else { * animationBanks = new AnimationBank[5]; // 1 in fix, 4 in lvl * Pointer.DoAt(ref reader, off_animBankFix, () => { * animationBanks[0] = AnimationBank.Read(reader, off_animBankFix, 0, 1, files_array[Mem.FixKeyFrames])[0]; * }); * }*/ }
private void DoTankControls(SuperObject *spo, RaymanState state, bool strafing, int *idleTimer) { EntryAction *leftAction = *(EntryAction **)0x4B9B90; EntryAction *rightAction = *(EntryAction **)0x4B9B94; EntryAction *forwardAction = *(EntryAction **)0x4B9B88; EntryAction *backAction = *(EntryAction **)0x4B9B8C; EntryAction *shiftAction = *(EntryAction **)0x4B9B98; rayman = spo; var transformationMatrix = rayman->PersoData->dynam->DynamicsAsBigDynamics->matrixA.TransformationMatrix; var rotation = Quaternion.CreateFromRotationMatrix(transformationMatrix); float rotationSpeedTarget = ((leftAction->IsValidated() ? 1f : 0) + (rightAction->IsValidated() ? -1f : 0)) * maxRotationSpeed * (shiftAction->IsValidated()?0.75f:1f); if (state == RaymanState.Swimming || state == RaymanState.Sliding) { rotationSpeedTarget *= 8.0f; } // Interpolate to target speed rotationSpeed += (rotationSpeedTarget - rotationSpeed) * rotationSpeedLerp; if (state == RaymanState.LedgeGrab) { if (forwardAction->IsValidated()) { currentSpeed = 10; } else if (backAction->IsValidated()) { currentSpeed = -10; } else { currentSpeed = 0; } } else if (strafing && state != RaymanState.Swimming && state != RaymanState.Sliding) { float strafeX = ((leftAction->IsValidated() ? 1f : 0) + (rightAction->IsValidated() ? -1f : 0)); float strafeY = ((forwardAction->IsValidated() ? 1f : 0) + (backAction->IsValidated() ? -1f : 0)); float strafeMagnitude = (float)Math.Sqrt(strafeX * strafeX + strafeY * strafeY); if (strafeMagnitude == 0 || float.IsNaN(strafeMagnitude) || float.IsInfinity(strafeMagnitude)) { strafeMagnitude = 1.0f; } // Normalize and set length to 100 strafeX *= 100.0f / strafeMagnitude; strafeY *= 100.0f / strafeMagnitude; HandleStrafing(rotation, rayman, strafeX, strafeY); return; } else { // Can be turning? if (state == RaymanState.Idle) { var entryActions = InputStructure.GetInputStructure()->EntryActions; int[] bannedActions = new int[] { (int)EntryActionNames.Action_Tirer, (int)EntryActionNames.Action_Sauter }; bool usingBannedAction = bannedActions.Where(a => entryActions[a]->IsValidated()).Count() > 0; if (!usingBannedAction) { if (rightAction->IsValidated() && !leftAction->IsValidated()) { if (rayman->PersoData->GetStateIndex() != 70) { rayman->PersoData->SetState(70); } playTurnAnimation = true; } else if (leftAction->IsValidated() && !rightAction->IsValidated()) { if (rayman->PersoData->GetStateIndex() != 71) { rayman->PersoData->SetState(71); } playTurnAnimation = true; } else { playTurnAnimation = false; } } } rotation *= Quaternion.CreateFromYawPitchRoll(0, 0, rotationSpeed); // Add rotation float targetSpeed = (forwardAction->IsValidated() ? 100f : 0f) * (shiftAction->IsValidated() ? 0.5f : 1f); currentSpeed += (targetSpeed - currentSpeed) * 0.1f; } if (currentSpeed > 0 && currentSpeed < 0.5f) { currentSpeed = 0; } if (currentSpeed < 70 && Math.Abs(rotationSpeed) > 0.01f && state == RaymanState.Sliding) { currentSpeed = 70; } WriteVariables(rotation, rotationSpeed, transformationMatrix, rayman); }
// 16 - 11237747 // 32 - 11428838 // 48 - 11560597 // 64 - 11629076 // 96 - 11639283 // 100 - 11652814 // 128 - 11563526 // 158 - 11367549 static OutputStructure Solve(InputStructure inputStructure, bool metropolisHax = false) { var os = new OutputStructure(); var vehicles = new List <Vehicle>(); for (int i = 0; i < inputStructure.vehiclesNo; i++) { vehicles.Add(new Vehicle() { bonusPointsIfOnTime = inputStructure.perRideBonus, vehicleNo = i }); } var allVehiclesAtAll = vehicles.ToList(); var ridesLeft = inputStructure.rides.ToList(); List <Ride> ridesForTheEnd = null; if (metropolisHax) { ridesLeft = ridesLeft .Where(x => x.rowStart > 1600) .Where(x => x.rowEnd < 4000) .Where(x => x.colStart > 0) .Where(x => x.colEnd < 2500) .ToList(); ridesForTheEnd = inputStructure.rides.ToList(); ridesForTheEnd.RemoveAll(x => ridesLeft.Any(y => x.rideNo == y.rideNo)); } while (ridesLeft.Count > 0 && vehicles.Count > 0) { var currVehicle = vehicles.MinElement(x => x.availableAtTime); var availableRides = ridesLeft.Where(ride => currVehicle.canFulfill(ride)).ToList(); if (availableRides.Count == 0 && ridesForTheEnd != null) { ridesLeft.AddRange(ridesForTheEnd); availableRides = ridesLeft.Where(ride => currVehicle.canFulfill(ride)).ToList(); // HAX ridesForTheEnd = null; } if (currVehicle.vehicleNo < 100 && metropolisHax && ridesForTheEnd != null) { availableRides = ridesForTheEnd.Where(ride => currVehicle.canFulfill(ride)).ToList(); } if (availableRides.Count == 0) { vehicles.Remove(currVehicle); continue; } var bestRide = availableRides.MaxElement(ride => ((double)currVehicle.pointsFor(ride)) / currVehicle.timeSpent(ride)); var pointsForRide = currVehicle.pointsFor(bestRide); var timeSpent = currVehicle.timeSpent(bestRide); ridesLeft.Remove(bestRide); if (ridesForTheEnd != null) { ridesForTheEnd.RemoveAll(x => x.rideNo == bestRide.rideNo); } if (currVehicle.pointsFor(bestRide) == 0) { throw new ApplicationException(); } currVehicle.executeRide(bestRide); if (!os.ridesPerVehicle.ContainsKey(currVehicle)) { os.ridesPerVehicle.Add(currVehicle, new RidesPerVehicle() { vehicle = currVehicle, rides = new List <Ride>() }); } os.ridesPerVehicle[currVehicle].rides.Add(bestRide); } return(os); }
async Task LoadFIX() { textures = new TextureInfo[0]; loadingState = "Loading fixed memory"; await WaitIfNecessary(); files_array[Mem.Fix].GotoHeader(); Reader reader = files_array[Mem.Fix].reader; reader.ReadUInt32(); // Offset of languages byte num_lvlNames = reader.ReadByte(); reader.ReadByte(); reader.ReadByte(); reader.ReadByte(); ReadLevelNames(reader, Pointer.Current(reader), num_lvlNames); if (Settings.s.platform == Settings.Platform.PC) { reader.ReadChars(0x1E); reader.ReadChars(0x1E); // two zero entries } string firstMapName = new string(reader.ReadChars(0x1E)); byte num_languages_subtitles = reader.ReadByte(); byte num_languages_voice = reader.ReadByte(); reader.ReadByte(); reader.ReadByte(); print(Pointer.Current(reader)); Pointer off_languages_subtitles = Pointer.Read(reader); Pointer off_languages_voice = Pointer.Read(reader); Pointer.DoAt(ref reader, off_languages_subtitles, () => { ReadLanguages(reader, off_languages_subtitles, num_languages_subtitles); }); Pointer.DoAt(ref reader, off_languages_voice, () => { ReadLanguages(reader, off_languages_voice, num_languages_voice); }); int sz_entryActions = 0xC0; reader.ReadBytes(sz_entryActions); // 3DOS_EntryActions reader.ReadUInt16(); ushort num_matrices = reader.ReadUInt16(); for (int i = 0; i < 4; i++) { reader.ReadBytes(0x101); } loadingState = "Loading input structure"; await WaitIfNecessary(); inputStruct = InputStructure.Read(reader, Pointer.Current(reader)); foreach (EntryAction ea in inputStruct.entryActions) { print(ea.ToString()); } reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); ushort num_unk2 = reader.ReadUInt16(); reader.ReadUInt16(); Pointer off_unk2 = Pointer.Read(reader); Pointer off_entryActions = Pointer.Read(reader); Pointer[] unkMatrices = new Pointer[2]; for (int i = 0; i < 2; i++) { unkMatrices[i] = Pointer.Read(reader); } fonts = FromOffsetOrRead <FontStructure>(reader, Pointer.Current(reader), inline: true); Pointer off_matrices = Pointer.Read(reader); Pointer off_specialEntryAction = Pointer.Read(reader); Pointer off_identityMatrix = Pointer.Read(reader); Pointer off_unk = Pointer.Read(reader); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadBytes(0xc8); reader.ReadUInt16(); reader.ReadUInt16(); reader.ReadByte(); reader.ReadByte(); reader.ReadByte(); reader.ReadByte(); Pointer.Read(reader); Pointer off_haloTexture = Pointer.Read(reader); Pointer off_material1 = Pointer.Read(reader); Pointer off_material2 = Pointer.Read(reader); for (int i = 0; i < 10; i++) { reader.ReadBytes(0xcc); } }
async Task LoadPS2() { await WaitIfNecessary(); textures = new TextureInfo[0]; loadingState = "Loading fixed memory"; await WaitIfNecessary(); files_array[Mem.Fix].GotoHeader(); Reader reader = files_array[Mem.Fix].reader; Pointer off_base_fix = Pointer.Current(reader); loadingState = "Loading input struct"; await WaitIfNecessary(); for (int i = 0; i < Settings.s.numEntryActions; i++) { Pointer.Read(reader); // 3DOS_EntryActions } inputStruct = InputStructure.Read(reader, Pointer.Current(reader)); foreach (EntryAction ea in inputStruct.entryActions) { print(ea.ToString()); } localization = FromOffsetOrRead <LocalizationStructure>(reader, Pointer.Current(reader), inline: true); /* * Pointer off_inputStructure = Pointer.Read(reader); * Pointer.DoAt(ref reader, off_inputStructure, () => { * inputStruct = InputStructure.Read(reader, off_inputStructure); * foreach (EntryAction ea in inputStruct.entryActions) { * print(ea.ToString()); * } * });*/ /*uint base_language = reader.ReadUInt32(); //Pointer off_language = Pointer.Read(reader); * reader.ReadUInt32(); * uint num_text_language = reader.ReadUInt32(); * reader.ReadUInt16(); * reader.ReadUInt16(); * reader.ReadUInt32(); // base * Pointer off_text_general = Pointer.Read(reader); * Pointer.DoAt(ref reader, off_text_general, () => { * fontStruct = FontStructure.Read(reader, off_text_general); * }); * Pointer off_inputStructure = Pointer.Read(reader); * Pointer.DoAt(ref reader, off_inputStructure, () => { * inputStruct = InputStructure.Read(reader, off_inputStructure); * foreach (EntryAction ea in inputStruct.entryActions) { * print(ea.ToString()); * } * }); * * await WaitIfNecessary(); * Pointer.Read(reader); * Pointer.Read(reader); * Pointer.Read(reader); * Pointer.Read(reader); * Pointer.Read(reader); * Pointer.Read(reader); * Pointer.Read(reader); * Pointer.Read(reader); * Pointer.Read(reader); * Pointer.Read(reader); * Pointer.Read(reader); * Pointer.Read(reader); * reader.ReadUInt32(); * reader.ReadUInt32(); * reader.ReadUInt32(); * reader.ReadUInt32(); * reader.ReadUInt32(); * reader.ReadUInt32(); * reader.ReadUInt32(); * reader.ReadUInt32(); * reader.ReadUInt32(); * reader.ReadUInt32(); * Pointer.Read(reader); * Pointer off_levelNames = Pointer.Read(reader); * Pointer off_languages = Pointer.Read(reader); * uint num_levelNames = reader.ReadUInt32(); * uint num_languages = reader.ReadUInt32(); * reader.ReadUInt32(); // same as num_levelNames * Pointer.DoAt(ref reader, off_levelNames, () => { * lvlNames = new string[num_levelNames]; * for (uint i = 0; i < num_levelNames; i++) { * lvlNames[i] = reader.ReadString(0x1E); * } * }); * Pointer.DoAt(ref reader, off_languages, () => { * ReadLanguages(reader, off_languages, num_languages); * }); * if (languages != null && fontStruct != null) { * for (int i = 0; i < num_languages; i++) { * loadingState = "Loading text files: " + (i+1) + "/" + num_languages; * string langFilePath = gameDataBinFolder + "TEXTS/" + languages[i].ToUpper() + ".LNG"; * await PrepareFile(langFilePath)); * files_array[2] = new DCDAT(languages[i], langFilePath, 2); * ((DCDAT)files_array[2]).SetHeaderOffset(base_language); * files_array[2].GotoHeader(); * fontStruct.ReadLanguageTableDreamcast(files_array[2].reader, i, (ushort)num_text_language); * files_array[2].Dispose(); * } * } * * loadingState = "Loading fixed textures"; * await WaitIfNecessary(); * Pointer off_events_fix = Pointer.Read(reader); * uint num_events_fix = reader.ReadUInt32(); * uint num_textures_fix = reader.ReadUInt32(); * Pointer off_textures_fix = Pointer.Read(reader); * Pointer.DoAt(ref reader, off_textures_fix, () => { * Array.Resize(ref textures, (int)num_textures_fix); * for (uint i = 0; i < num_textures_fix; i++) { * Pointer off_texture = Pointer.Read(reader); * textures[i] = null; * Pointer.DoAt(ref reader, off_texture, () => { * textures[i] = TextureInfo.Read(reader, off_texture); * }); * } * TEX tex = new TEX(tplPaths[0]); * for (uint i = 0; i < num_textures_fix; i++) { * if (textures[i] != null && tex.Count > i) { * textures[i].Texture = tex.textures[i]; * } * } * });*/ loadingState = "Loading level memory"; await WaitIfNecessary(); files_array[Mem.Lvl].GotoHeader(); reader = files_array[Mem.Lvl].reader; string build = reader.ReadString(0x20); reader.ReadUInt32(); reader.ReadUInt32(); // 0xc reader.ReadUInt32(); // 0 Pointer.Read(reader); Pointer.Read(reader); // Globals globals.off_actualWorld = Pointer.Read(reader); globals.off_dynamicWorld = Pointer.Read(reader); globals.off_fatherSector = Pointer.Read(reader); globals.num_always = reader.ReadUInt32(); globals.spawnablePersos = LinkedList <Perso> .ReadHeader(reader, Pointer.Current(reader), LinkedList.Type.Double); //globals.spawnablePersos.FillPointers(reader, globals.spawnablePersos.off_tail, globals.spawnablePersos.offset); Pointer.Read(reader); // format: (0x4 number, number * 0x4: null) globals.off_always_reusableSO = Pointer.Read(reader); // There are (num_always) empty SuperObjects starting with this one. Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); LinkedList <Perso> cameras = LinkedList <Perso> .ReadHeader(reader, Pointer.Current(reader), LinkedList.Type.Double); families = LinkedList <Family> .ReadHeader(reader, Pointer.Current(reader), type : LinkedList.Type.Double); LinkedList <Perso> mainChars = LinkedList <Perso> .ReadHeader(reader, Pointer.Current(reader), LinkedList.Type.Double); Pointer.Read(reader); // Rayman reader.ReadUInt32(); globals.off_camera = Pointer.Read(reader); // Camera Pointer.Read(reader); Pointer.Read(reader); Pointer.Read(reader); uint numMeshes = (uint)files_array[Mem.Lvl].extraData["numMeshes"]; uint numMaterials = (uint)files_array[Mem.Lvl].extraData["numMaterials"]; uint numTextures = (uint)files_array[Mem.Lvl].extraData["numTextures"]; uint numLightmappedObjects = (uint)files_array[Mem.Lvl].extraData["numLightmappedObjects"]; //print("numTextures " + numTextures + " - " + txds[0].Count + " - " + numMeshes + " - " + ato.numAtomics + " - " + numLightmappedObjects); textures = new TextureInfo[numTextures]; Pointer[] off_meshes = new Pointer[numMeshes]; off_materials = new Pointer[numMaterials]; for (int i = 0; i < numMeshes; i++) { off_meshes[i] = Pointer.Read(reader); } for (int i = 0; i < numMaterials; i++) { off_materials[i] = Pointer.Read(reader); } for (int i = 0; i < numTextures; i++) { Pointer off_textureInfo = Pointer.Read(reader); int texture_index = reader.ReadInt32(); Pointer.DoAt(ref reader, off_textureInfo, () => { textures[i] = TextureInfo.Read(reader, off_textureInfo); textures[i].Texture = txds[0].Lookup(texture_index.ToString("D3")); //textures[i].Texture = txds[0].textures[txds[0].Count - 1 - texture_index]; }); } Pointer.Read(reader); reader.ReadUInt32(); reader.ReadUInt32(); Pointer.Read(reader); uint num_unk = reader.ReadUInt32(); for (int i = 0; i < num_unk; i++) { Pointer.Read(reader); } uint num_unk2 = reader.ReadUInt32(); for (int i = 0; i < num_unk2; i++) { Pointer.Read(reader); } Pointer.Read(reader); reader.ReadSingle(); // a bounding volume most likely reader.ReadSingle(); reader.ReadSingle(); reader.ReadSingle(); reader.ReadSingle(); reader.ReadSingle(); Pointer.Read(reader); reader.ReadUInt32(); // 2? uint num_poTable = reader.ReadUInt32(); Pointer off_poTable = Pointer.Read(reader); reader.ReadUInt32(); // 1. 10x 0 reader.ReadUInt32(); // 2 reader.ReadUInt32(); // 3 reader.ReadUInt32(); // 4 reader.ReadUInt32(); // 5 reader.ReadUInt32(); // 6 reader.ReadUInt32(); // 7 reader.ReadUInt32(); // 8 reader.ReadUInt32(); // 9 reader.ReadUInt32(); // 10 uint num_lightCookies = reader.ReadUInt32(); lightCookieColors = new Color[num_lightCookies]; for (int i = 0; i < num_lightCookies; i++) { reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); byte b = reader.ReadByte(); byte g = reader.ReadByte(); byte r = reader.ReadByte(); reader.ReadByte(); lightCookieColors[i] = new Color(r / 255f, g / 255f, b / 255f, 1f); reader.ReadUInt32(); reader.ReadInt32(); reader.ReadUInt32(); } for (int i = 0; i < num_lightCookies; i++) { reader.ReadByte(); } reader.Align(0x4); Pointer off_lightCookieMaterial = Pointer.Read(reader); lightCookieMaterial = VisualMaterial.FromOffsetOrRead(off_lightCookieMaterial, reader); off_lightmapUV = new Pointer[numLightmappedObjects]; for (int i = 0; i < numLightmappedObjects; i++) { reader.ReadUInt32(); reader.ReadUInt32(); reader.ReadUInt32(); off_lightmapUV[i] = Pointer.Read(reader); } for (int i = 0; i < numMaterials; i++) { VisualMaterial.FromOffsetOrRead(off_materials[i], reader); } for (int i = 0; i < numMeshes; i++) { Pointer.DoAt(ref reader, off_meshes[i], () => { GeometricObject mesh = GeometricObject.Read(reader, off_meshes[i]); meshObjects.Add(mesh); //print("Mesh " + i + ": " + mesh.num_vertices + " - " + mesh.subblock_types[0] + " - " + mesh.num_subblocks); }); } loadingState = "Loading families"; await WaitIfNecessary(); ReadFamilies(reader); //print("Families: " + families.Count); loadingState = "Loading superobject hierarchy"; await WaitIfNecessary(); ReadSuperObjects(reader); loadingState = "Loading always structure"; await WaitIfNecessary(); ReadAlways(reader); loadingState = "Filling in cross-references"; await WaitIfNecessary(); ReadCrossReferences(reader); await WaitIfNecessary(); }