/// <summary> /// Gets spoilers from slottype block and sets to cartypeinfo. /// </summary> /// <param name="db">Database of classes.</param> private static void E_Spoilers(Database.MostWanted db) { if (db.SlotTypes.Spoilers == null) { return; } var CNameList = new List <string>(); foreach (var car in db.CarTypeInfos.Collections) { CNameList.Add(car.CollectionName); } var AllSlots = db.SlotTypes.Spoilers.GetSpoilers(CNameList); if (AllSlots == null || AllSlots.Count == 0) { return; } foreach (var Slot in AllSlots) { var car = db.CarTypeInfos.FindCollection(Slot.CarTypeInfo); if (car == null) { continue; } car.Spoiler = Slot.Spoiler; } }
/// <summary> /// Writes all materials into the Global data. /// </summary> /// <param name="db">Database with classes.</param> /// <param name="bw">BinaryWriter for writing data.</param> private static void I_Materials(Database.MostWanted db, BinaryWriter bw) { foreach (var material in db.Materials.Collections) { bw.Write(material.Assemble()); } }
/// <summary> /// Gets the frontend group, decompresses it if needed, and plugs into Vector. /// </summary> /// <param name="byteptr_t">Pointer to the beginning of frontend group in Global data.</param> /// <param name="length">Length of the block to be read.</param> /// <param name="db">Database to which add classes.</param> private static unsafe void E_FNGroup(byte *byteptr_t, uint length, Database.MostWanted db) { // Copy data and decompress var data = new byte[length]; fixed(byte *dataptr_t = &data[0]) { for (int a1 = 0; a1 < data.Length; ++a1) { *(dataptr_t + a1) = *(byteptr_t + a1); } } data = Utils.EA.SAT.Decompress(data); var Class = new FNGroup(data, db); // Check whether this FEng class already exists in the database if (Class.Destroy) { return; } if (db.FNGroups.FindCollection(Class.CollectionName) != null) { return; } db.FNGroups.Collections.Add(Class); Bin.Hash(Class.CollectionName); }
/// <summary> /// Writes all collisions into the Global data. /// </summary> /// <param name="db">Database with classes.</param> /// <param name="bw">BinaryWriter for writing data.</param> private static unsafe void I_Collisions(Database.MostWanted db, BinaryWriter bw) { bw.Write(Global.Collisions); bw.Write(0xFFFFFFFF); // write temp size int initial_size = (int)bw.BaseStream.Position; // Copy all collisions by the internal names foreach (var info in db.CarTypeInfos.Collections) { if (info.CollisionExternalName == BaseArguments.NULL) { continue; } uint extkey = Vlt.Hash(info.CollisionExternalName); uint intkey = Vlt.Hash(info.CollisionInternalName); if (db.SlotTypes.Collisions.TryGetValue(intkey, out var collision)) { bw.Write(collision.GetData(extkey)); } } // Copy all unknown collisions foreach (var collision in db.SlotTypes.Collisions) { if (collision.Value.Unknown) { bw.Write(collision.Value.GetData(0)); } } // Fix size bw.BaseStream.Position = initial_size - 4; bw.Write((int)bw.BaseStream.Length - initial_size); bw.BaseStream.Position = bw.BaseStream.Length; }
/// <summary> /// Saves database data into GlobalA file. /// </summary> /// <param name="GlobalA_dir">Game directory.</param> /// <param name="db">Database of classes.</param> /// <returns>True if success.</returns> public static bool SaveGlobalA(string GlobalA_dir, Database.MostWanted db) { GlobalA_dir += @"\GLOBAL\GlobalA.bun"; using (var br = new BinaryReader(new MemoryStream(db._GlobalABUN))) using (var bw = new BinaryWriter(File.Open(GlobalA_dir, FileMode.Create))) { int tpkindex = 0; while (br.BaseStream.Position < br.BaseStream.Length) { // Set Offset, ID and Size values, read starting in the beginning of the file uint WriterSlotOffset = (uint)br.BaseStream.Position; uint WriterSlotID = br.ReadUInt32(); int WriterSlotSize = br.ReadInt32(); // If one of the necessary slots is reached, replace it switch (WriterSlotID) { case 0: uint key = br.ReadUInt32(); br.BaseStream.Position -= 4; if (key == Global.GlobalLib) { br.BaseStream.Position += WriterSlotSize; break; } else { goto default; } case Global.TPKBlocks: while (!db.TPKBlocks[tpkindex].InGlobalA) { ++tpkindex; } I_TPKBlock(db, bw, ref tpkindex); br.BaseStream.Position += WriterSlotSize; break; case Global.FEngFiles: case Global.FNGCompress: br.BaseStream.Position += WriterSlotSize; break; default: bw.Write(WriterSlotID); bw.Write(WriterSlotSize); bw.Write(br.ReadBytes(WriterSlotSize)); break; } } // Write all FEng files I_FNGroup(db, bw); } return(true); }
/// <summary> /// Writes all frontend groups into the Global data. /// </summary> /// <param name="db">Database with classes.</param> /// <param name="bw">BinaryWriter for writing data.</param> private static void I_FNGroup(Database.MostWanted db, BinaryWriter bw) { for (int a1 = 0; a1 < db.FNGroups.Length; ++a1) { I_GlobalLibBlock(bw); bw.Write(db.FNGroups[a1].Assemble()); } }
/// <summary> /// Writes all preset rides into the Global data. /// </summary> /// <param name="db">Database with classes.</param> /// <param name="bw">BinaryWriter for writing data.</param> private static void I_PresetRides(Database.MostWanted db, BinaryWriter bw) { bw.Write(Global.PresetRides); bw.Write(db.PresetRides.Length * 0x290); foreach (var ride in db.PresetRides.Collections) { bw.Write(ride.Assemble()); } }
// Default constructor: create new texture for memory cast public Texture(string CName, string _TPK, Database.MostWanted db) { this.Database = db; this._collection_name = CName; this._parent_TPK = _TPK; this.BinKey = Bin.Hash(CName); this.PaletteOffset = 0; this._padding = 1; }
public unsafe TPKBlock(byte *byteptr_t, int index, Database.MostWanted db) { if (index < 0) { this._use_current_cname = true; } this.Database = db; this.Index = index; this.Disassemble(byteptr_t); }
// Default constructor: disassemble texture public unsafe Texture(byte *byteptr_t, uint offset, uint size, string _TPK, Database.MostWanted db) { this.Database = db; this._located_at = (int)offset; this._size_of_block = (int)size; this._parent_TPK = _TPK; this.PaletteOffset = 0; this._padding = 1; this.Disassemble(byteptr_t + this._located_at); }
/// <summary> /// Decompile entire carparts block into separate elements. /// </summary> /// <param name="byteptr_t">Pointer to the beginning of carparts block in Global data.</param> /// <param name="length">Length of the block to be read.</param> /// <param name="db">Database to which add classes.</param> private static unsafe void E_CarParts(byte *byteptr_t, uint length, Database.MostWanted db) { uint offset = 0; uint ID = 0; uint size = 0; byte *part5ptr_t = byteptr_t; // pointer to the part5 of the block byte *part6ptr_t = byteptr_t; // pointer to the part6 of the block while (offset < length) { ID = *(uint *)(byteptr_t + offset); size = *(uint *)(byteptr_t + offset + 4); if (offset + size > length) { return; // in case of reading beyong the stream } switch (ID) { case CarParts.Part0: db.SlotTypes.Part0.Data = CPE_Part0(byteptr_t + offset, size + 8); goto default; case CarParts.Part1: db.SlotTypes.Part1.Data = CPE_Part1(byteptr_t + offset, size + 8); goto default; case CarParts.Part2: db.SlotTypes.Part2.Data = CPE_Part2(byteptr_t + offset, size + 8); goto default; case CarParts.Part3: db.SlotTypes.Part3.Data = CPE_Part3(byteptr_t + offset, size + 8); goto default; case CarParts.Part4: db.SlotTypes.Part4.Data = CPE_Part4(byteptr_t + offset, size + 8); goto default; case CarParts.Part5: part5ptr_t = byteptr_t + offset; goto default; case CarParts.Part6: part6ptr_t = byteptr_t + offset; goto default; default: offset += 8 + size; break; } } // Disassemble part5 and part6 CPE_Part56(part5ptr_t, part6ptr_t, db); }
// Default constructor: disassemble cartypeinfo public unsafe CarTypeInfo(IntPtr byteptr_t, string CName, Database.MostWanted db) { this.Database = db; this._collection_name = CName; this.OriginalName = CName; this.Disassemble((byte *)byteptr_t); if (this.Index <= (int)eBoundValues.MIN_INFO_MOSTWANTED) { this.Deletable = false; } this.Modified = false; }
/// <summary> /// Get the material name, initialize its class and inject into Vector. /// </summary> /// <param name="byteptr_t">Pointer to the beginning of material block in Global data.</param> /// <param name="db">Database to which add classes.</param> private static unsafe void E_Material(byte *byteptr_t, Database.MostWanted db) { // Get collection name of the material, starts at 0x14 string CName = ScriptX.NullTerminatedString(byteptr_t + 0x1C, 0x1C); CName = Resolve.GetPathFromCollection(CName); Resolve.GetWindowTintString(CName); Map.BinKeys[Bin.Hash(CName)] = CName; var Class = new Material((IntPtr)byteptr_t, CName, db); db.Materials.Collections.Add(Class); }
/// <summary> /// Writes all cartypeinfo into the Global data. /// </summary> /// <param name="db">Database with classes.</param> /// <param name="bw">BinaryWriter for writing data.</param> private static void I_CarTypeInfo(Database.MostWanted db, BinaryWriter bw) { int index = 0; bw.Write(Global.CarTypeInfo); bw.Write(db.CarTypeInfos.Length * 0xD0 + 8); bw.Write(0x1111111111111111); foreach (var car in db.CarTypeInfos.Collections) { car.Index = index++; bw.Write(car.Assemble()); } }
/// <summary> /// Extracts slottype block into memory. /// </summary> /// <param name="byteptr_t">Pointer to the ID of spoilerss block in Global data.</param> /// <param name="length">Length of the block to be read (including ID and size).</param> /// <param name="db">Database to which add classes.</param> private static unsafe void E_SlotType(byte *byteptr_t, uint length, Database.MostWanted db) { var Data = new byte[length]; fixed(byte *dataptr_t = &Data[0]) { for (int a1 = 0; a1 < length; ++a1) { *(dataptr_t + a1) = *(byteptr_t + a1); } } db.SlotTypes.Spoilers = new Spoilers(Data); }
// Default constructor: disassemble preset public unsafe PresetRide(IntPtr byteptr_t, string CName, Database.MostWanted db) { this.Database = db; this.data = new byte[0x290]; this._collection_name = CName; this.Exists = true; this.DECALS_FRONT_WINDOW = new DecalArray(); this.DECALS_REAR_WINDOW = new DecalArray(); this.DECALS_LEFT_DOOR = new DecalArray(); this.DECALS_RIGHT_DOOR = new DecalArray(); this.DECALS_LEFT_QUARTER = new DecalArray(); this.DECALS_RIGHT_QUARTER = new DecalArray(); this.Disassemble((byte *)byteptr_t); this.Modified = false; }
// Default constructor: create new cartypeinfo public CarTypeInfo(string CName, Database.MostWanted db) { this.Database = db; this.CollectionName = CName; this.ManufacturerName = "GENERIC"; this.Deletable = true; this.Modified = true; this.WhatGame = 1; this.WheelOuterRadius = 26; this.WheelInnerRadiusMin = 17; this.WheelInnerRadiusMax = 20; this.DefaultSkinNumber = 1; this.CollisionExternalName = CollectionName; this.CollisionInternalName = "CARRERAGT"; Map.BinKeys[Bin.Hash(CName)] = CName; }
// Default constructor: create new preset public PresetRide(string CName, Database.MostWanted db) { this.Database = db; this.CollectionName = CName; this.data = new byte[0x290]; this.MODEL = "SUPRA"; this.Frontend = "supra"; this.Pvehicle = "supra"; this.DECALS_FRONT_WINDOW = new DecalArray(); this.DECALS_REAR_WINDOW = new DecalArray(); this.DECALS_LEFT_DOOR = new DecalArray(); this.DECALS_RIGHT_DOOR = new DecalArray(); this.DECALS_LEFT_QUARTER = new DecalArray(); this.DECALS_RIGHT_QUARTER = new DecalArray(); Map.BinKeys[Bin.Hash(CName)] = CName; this.Modified = true; }
/// <summary> /// Writes all slottype into the Global data. /// </summary> /// <param name="db">Database with classes.</param> /// <param name="bw">BinaryWriter for writing data.</param> private static void I_SlotType(Database.MostWanted db, BinaryWriter bw) { var SetList = new List <CarSpoilerType>(); // Get all cartypeinfos with non-base spoilers foreach (var info in db.CarTypeInfos.Collections) { if (info.Spoiler != eSpoiler.SPOILER) { var Class = new CarSpoilerType(); Class.CarTypeInfo = info.CollectionName; Class.Spoiler = info.Spoiler; SetList.Add(Class); } } bw.Write(db.SlotTypes.Spoilers.SetSpoilers(SetList)); }
/// <summary> /// Decompile entire preset rides block into Vector of separate elements. /// </summary> /// <param name="byteptr_t">Pointer to the beginning of preset rides block in Global data.</param> /// <param name="length">Length of the block to be read.</param> /// <param name="db">Database to which add classes.</param> private static unsafe void E_PresetRides(byte *byteptr_t, uint length, Database.MostWanted db) { uint size = 0x290; for (uint loop = 0; loop < length / size; ++loop) { uint offset = loop * size; // current offset of the preset ride // Get CollectionName string CName = ScriptX.NullTerminatedString(byteptr_t + offset + 0x28, 0x20); CName = Resolve.GetPathFromCollection(CName); Map.BinKeys[Bin.Hash(CName)] = CName; var Class = new PresetRide((IntPtr)(byteptr_t + offset), CName, db); db.PresetRides.Collections.Add(Class); } }
/// <summary> /// Loads English file and disassembles its blocks /// </summary> /// <param name="Language_dir">Directory of the game.</param> /// <param name="db">Database of classes.</param> /// <returns>True if success.</returns> public static unsafe bool LoadLanguage(string Language_dir, Database.MostWanted db) { Language_dir += @"\LANGUAGES\"; // Get everything from language files try { db._LngGlobal = File.ReadAllBytes(Language_dir + "English.bin"); db._LngLabels = File.ReadAllBytes(Language_dir + "Labels.bin"); Log.Write("Reading data from English.bin..."); Log.Write("Reading data from Labels.bin..."); } catch (Exception e) { while (e.InnerException != null) { e = e.InnerException; } if (Process.MessageShow) { MessageBox.Show($"Error occured: {e.Message}", "Failure", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { Console.WriteLine(e.Message); } return(false); } // Decompress if compressed db._LngGlobal = JDLZ.Decompress(db._LngGlobal); db._LngLabels = JDLZ.Decompress(db._LngLabels); // Use pointers to speed up process fixed(byte *strptr = &db._LngGlobal[0], labptr = &db._LngLabels[0]) { db.STRBlocks.Collections.Add(new STRBlock(strptr, labptr, db._LngGlobal.Length, db._LngLabels.Length, db)); } return(true); }
/// <summary> /// Decompile entire cartypeinfo block into Vector of separate elements. /// </summary> /// <param name="byteptr_t">Pointer to the beginning of cartypeinfo block in Global data.</param> /// <param name="length">Length of the block to be read.</param> /// <param name="db">Database to which add classes.</param> private static unsafe void E_CarTypeInfo(byte *byteptr_t, uint length, Database.MostWanted db) { uint size = 0xD0; for (uint loop = 0; loop < length / size; ++loop) { uint offset = 8 + loop * size; // current offset of the cartypeinfo (padding included) // Get CollectionName string CName = ScriptX.NullTerminatedString(byteptr_t + offset, 0x10); CName = Resolve.GetPathFromCollection(CName); Map.BinKeys[Bin.Hash(CName)] = CName; if (!LibColBlockExists) { Map.CollisionMap[Vlt.Hash(CName)] = CName; } var Class = new CarTypeInfo((IntPtr)(byteptr_t + offset), CName, db); db.CarTypeInfos.Collections.Add(Class); } }
/// <summary> /// Saves database data into GlobalB file. /// </summary> /// <param name="GlobalB_dir">Game directory.</param> /// <param name="db">Database of classes.</param> /// <returns>True if success.</returns> public static bool SaveGlobalB(string GlobalB_dir, Database.MostWanted db) { GlobalB_dir += @"\GLOBAL\GlobalB.lzc"; using (var br = new BinaryReader(new MemoryStream(db._GlobalBLZC))) using (var bw = new BinaryWriter(File.Open(GlobalB_dir, FileMode.Create))) { int tpkindex = 0; I_Materials(db, bw); I_CollisionLibBlock(db, bw); while (br.BaseStream.Position < br.BaseStream.Length) { // Set Offset, ID and Size values, read starting in the beginning of the file uint WriterSlotOffset = (uint)br.BaseStream.Position; uint WriterSlotID = br.ReadUInt32(); int WriterSlotSize = br.ReadInt32(); // If one of the necessary slots is reached, replace it switch (WriterSlotID) { case 0: uint key = br.ReadUInt32(); br.BaseStream.Position -= 4; if (key == Global.GlobalLib) { br.BaseStream.Position += WriterSlotSize; break; } else { goto default; } case Global.Materials: br.BaseStream.Position += WriterSlotSize; break; case Global.TPKBlocks: while (db.TPKBlocks[tpkindex].InGlobalA) { ++tpkindex; } I_TPKBlock(db, bw, ref tpkindex); br.BaseStream.Position += WriterSlotSize; break; case Global.CarTypeInfo: I_CarTypeInfo(db, bw); br.BaseStream.Position += WriterSlotSize; break; case Global.PresetRides: I_PresetRides(db, bw); br.BaseStream.Position += WriterSlotSize; break; case Global.CarParts: I_CarParts(db, bw); br.BaseStream.Position += WriterSlotSize; break; case Global.Collisions: I_Collisions(db, bw); br.BaseStream.Position += WriterSlotSize; break; case Global.SlotTypes: I_SlotType(db, bw); br.BaseStream.Position += WriterSlotSize; break; default: bw.Write(WriterSlotID); bw.Write(WriterSlotSize); bw.Write(br.ReadBytes(WriterSlotSize)); break; } } } return(true); }
private static unsafe void E_Collisions(byte *byteptr_t, uint length, Database.MostWanted db) { db.SlotTypes.Collisions = new Dictionary <uint, Collision>(); // Make a map of vlt hash cartypeinfo and indexes var CNameToIndex = new Dictionary <uint, string>(); foreach (var car in db.CarTypeInfos.Collections) { CNameToIndex[car.VltKey] = car.CollectionName; } uint offset = 0; while (offset < length) { uint ID = *(uint *)(byteptr_t + offset); int size = *(int *)(byteptr_t + offset + 4); if (ID == CarParts.Collision) { uint intkey = *(uint *)(byteptr_t + offset + 8); uint extkey = *(uint *)(byteptr_t + offset + 16); // If internal key exists and map shows a string for it if (intkey != 0x11111111 && intkey != 0 && Map.CollisionMap.TryGetValue(intkey, out string CName)) { // If collision is not in the map, plug it in if (!db.SlotTypes.Collisions.ContainsKey(intkey)) { // Copy whole data, get it into collision map var data = new byte[size + 8]; fixed(byte *dataptr_t = &data[0]) { for (int a1 = 0; a1 < size + 8; ++a1) { *(dataptr_t + a1) = *(byteptr_t + offset + a1); } *(uint *)(dataptr_t + 16) = 0xFFFFFFFF; } var Class = new Collision(data, CName); db.SlotTypes.Collisions[intkey] = Class; } // Check if cartypeinfo with a set external key exists if (CNameToIndex.TryGetValue(extkey, out var name)) { var car = db.CarTypeInfos.FindCollection(name); car.CollisionExternalName = car.CollectionName; car.CollisionInternalName = CName; } } else { // Copy entire collision block var data = new byte[size + 8]; fixed(byte *dataptr_t = &data[0]) { for (int a1 = 0; a1 < size + 8; ++a1) { *(dataptr_t + a1) = *(byteptr_t + offset + a1); } *(uint *)(dataptr_t + 8) = extkey; *(uint *)(dataptr_t + 16) = 0xFFFFFFFF; } // If collision map has value for external key if (Map.CollisionMap.TryGetValue(extkey, out string ExName)) { var Class = new Collision(data, ExName); db.SlotTypes.Collisions[extkey] = Class; // Check if cartypeinfo with a set external key exists if (CNameToIndex.TryGetValue(extkey, out var name)) { var car = db.CarTypeInfos.FindCollection(name); car.CollisionExternalName = car.CollectionName; car.CollisionInternalName = car.CollectionName; } } else { var Class = new Collision(data, null); db.SlotTypes.Collisions[extkey] = Class; } } } offset += (uint)size + 8; } // New collision map based on real collisions Map.CollisionMap.Clear(); foreach (var collision in db.SlotTypes.Collisions) { if (!collision.Value.Unknown) { Map.CollisionMap[collision.Key] = collision.Value.BelongsTo; } } }
/// <summary> /// Loads GlobalB file and disassembles its blocks /// </summary> /// <param name="GlobalB_dir">Directory of the game.</param> /// <param name="db">Database of classes.</param> /// <returns>True if success.</returns> public static unsafe bool LoadGlobalB(string GlobalB_dir, Database.MostWanted db) { LibColBlockExists = false; GlobalB_dir += @"\GLOBAL\GlobalB.lzc"; // Get everything from GlobalB.lzc try { db._GlobalBLZC = File.ReadAllBytes(GlobalB_dir); Log.Write("Reading data from GlobalB.lzc..."); } catch (Exception e) { while (e.InnerException != null) { e = e.InnerException; } if (Process.MessageShow) { MessageBox.Show($"Error occured: {e.Message}", "Failure", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { Console.WriteLine(e.Message); } return(false); } // Decompress if compressed db._GlobalBLZC = JDLZ.Decompress(db._GlobalBLZC); // Use pointers to speed up process fixed(byte *byteptr_t = &db._GlobalBLZC[0]) { uint offset = 0; // to calculate current offset uint ID = 0; // to get the ID of the block being read uint size = 0; // to get the size of the block being read uint proff = 0; // offset of the preset rides block uint prsize = 0; // size of the preset rides block uint cpoff = 0; // offset of the carparts block uint cpsize = 0; // size of the carparts block uint cooff = 0; // offset of the collision block uint cosize = 0; // size of the collision block while (offset < db._GlobalBLZC.Length) { ID = *(uint *)(byteptr_t + offset); // read ID size = *(uint *)(byteptr_t + offset + 4); // read size if (offset + size > db._GlobalBLZC.Length) { if (Process.MessageShow) { MessageBox.Show("GlobalB: unable to read beyond the stream.", "Failure"); } else { Console.WriteLine("GlobalB: unable to read beyond the stream."); } return(false); } switch (ID) { case 0: if (*(uint *)(byteptr_t + offset + 8) == Global.GlobalLib) { E_GlobalLibBlock(byteptr_t + offset, size + 8); } break; case Global.Materials: E_Material(byteptr_t + offset, db); break; case Global.TPKBlocks: int count = db.TPKBlocks.Length; db.TPKBlocks.Collections.Add(new TPKBlock(byteptr_t + offset, count, db)); break; case Global.CarTypeInfo: E_CarTypeInfo(byteptr_t + offset + 8, size, db); break; case Global.PresetRides: proff = offset + 8; prsize = size; break; case Global.CarParts: cpoff = offset + 8; cpsize = size; break; case Global.SlotTypes: E_SlotType(byteptr_t + offset, size + 8, db); break; case Global.Collisions: cooff = offset + 8; cosize = size; break; case Global.FEngFiles: case Global.FNGCompress: E_FNGroup(byteptr_t + offset, size + 8, db); break; default: break; } offset += 8 + size; // advance in offset } // CarParts and Collisions blocks are the last ones to disassemble E_CarParts(byteptr_t + cpoff, cpsize, db); E_Collisions(byteptr_t + cooff, cosize, db); E_PresetRides(byteptr_t + proff, prsize, db); } // Disperse spoilers across cartypeinfo E_Spoilers(db); return(true); }
// Default constructor: disassemble string block public unsafe STRBlock(byte *strptr, byte *labptr, int strlen, int lablen, Database.MostWanted db) { this.Database = db; this.Disassemble(strptr, strlen); this.DisperseLabels(labptr, lablen); }
private static unsafe void CPE_Part56(byte *part5ptr_t, byte *part6ptr_t, Database.MostWanted db) { int len5 = *(int *)(part5ptr_t + 4) + 8; // size of part5 int len6 = *(int *)(part6ptr_t + 4); // size of part6 // Exclude padding while (*(int *)(part5ptr_t + len5 - 4) == 0) { len5 -= 4; } while (*(int *)(part6ptr_t + len6 + 4) == 0) { len6 -= 4; } len6 = len6 / 0xE * 0xE + 8; int off5 = 8; // offset in part5 int off6 = 8; // offset in part6 int size = 0; // size of one part in part6 // Validation check int check = *(part6ptr_t + len6 - 7) + 1; int total = (len5 - 8) / 4; if (check < total) { len5 = check * 4 + 8; } db.SlotTypes.Part56 = new List <Part56>(); var CarCNames = new List <uint>(); foreach (var car in db.CarTypeInfos.Collections) { CarCNames.Add(car.BinKey); } while (off5 < len5) { uint ckey = *(uint *)(part5ptr_t + off5); if (ckey == 0) { break; // padding means end } bool IsCar = false; byte current = 0; byte index = *(part6ptr_t + off6 + 7); while (true) { if (off6 + size + 7 >= len6) { break; } current = *(part6ptr_t + off6 + size + 7); if (current != index) { break; } else { size += 0xE; } } if (CarCNames.Contains(ckey)) { IsCar = true; } var Part = new Part56(ckey, part6ptr_t + off6, size, IsCar); db.SlotTypes.Part56.Add(Part); off5 += 4; off6 += size; size = 0; } }
/// <summary> /// Saves database data into English file. /// </summary> /// <param name="Language_dir">Game directory.</param> /// <param name="db">Database of classes.</param> /// <returns>True if success.</returns> public static bool SaveLanguage(string Language_dir, Database.MostWanted db) { Language_dir += @"\LANGUAGES\"; using (var br = new BinaryReader(new MemoryStream(db._LngGlobal))) using (var bw = new BinaryWriter(File.Open(Language_dir + "English.bin", FileMode.Create))) { bool finished = false; while (br.BaseStream.Position < br.BaseStream.Length) { // Set Offset, ID and Size values, read starting in the beginning of the file uint WriterSlotOffset = (uint)br.BaseStream.Position; uint WriterSlotID = br.ReadUInt32(); int WriterSlotSize = br.ReadInt32(); // If one of the necessary slots is reached, replace it switch (WriterSlotID) { case Global.STRBlocks: if (!finished) { bw.Write(db.STRBlocks[0].Assemble()); br.BaseStream.Position += WriterSlotSize; finished = true; break; } else { goto default; } default: bw.Write(WriterSlotID); bw.Write(WriterSlotSize); bw.Write(br.ReadBytes(WriterSlotSize)); break; } } } using (var br = new BinaryReader(new MemoryStream(db._LngLabels))) using (var bw = new BinaryWriter(File.Open(Language_dir + "Labels.bin", FileMode.Create))) { bool finished = false; while (br.BaseStream.Position < br.BaseStream.Length) { // Set Offset, ID and Size values, read starting in the beginning of the file uint WriterSlotOffset = (uint)br.BaseStream.Position; uint WriterSlotID = br.ReadUInt32(); int WriterSlotSize = br.ReadInt32(); // If one of the necessary slots is reached, replace it switch (WriterSlotID) { case Global.STRBlocks: if (!finished) { bw.Write(db.STRBlocks[0].ParseLabels()); br.BaseStream.Position += WriterSlotSize; finished = true; break; } else { goto default; } default: bw.Write(WriterSlotID); bw.Write(WriterSlotSize); bw.Write(br.ReadBytes(WriterSlotSize)); break; } } } return(true); }
/// <summary> /// Writes all car parts into the Global data. /// </summary> /// <param name="db">Database with classes.</param> /// <param name="bw">BinaryWriter for writing data.</param> private static void I_CarParts(Database.MostWanted db, BinaryWriter bw) { bw.Write(Global.CarParts); bw.Write(0xFFFFFFFF); // temp size int initial_size = (int)bw.BaseStream.Position; int CarIDOffset = initial_size + 0x30; int PartNumOffset = initial_size + 0x40; int padding = 0; var keylists = new List <uint>(); var Intermid56 = new List <Part56>(); var UsedPart56 = new List <Part56>(); // Copy for processing for (int a1 = 0; a1 < db.SlotTypes.Part56.Count; ++a1) { Intermid56.Add(db.SlotTypes.Part56[a1].MemoryCast()); } // Go through all cartypeinfo, set correct usagetype and keys foreach (var car in db.CarTypeInfos.Collections) { bool CarDoesExist = false; int index = 0; string CName = car.CollectionName; uint ckey = car.BinKey; uint okey = Bin.Hash(car.OriginalName); keylists.Add(ckey); for (index = 0; index < Intermid56.Count; ++index) { if (okey == Intermid56[index].Key) { CarDoesExist = true; break; } } if (CarDoesExist) { if (ckey != okey || car.Modified) { Intermid56[index] = new Part56(CName, (byte)index); } } else { var Class = new Part56(CName, (byte)index); Intermid56.Add(Class); } } // Make new list of only used Part56 to write byte curlength = 0; for (int a1 = 0; a1 < Intermid56.Count; ++a1) { if (Intermid56[a1].IsCar && !keylists.Contains(Intermid56[a1].Key)) { continue; } Intermid56[a1].SetIndex(curlength++); UsedPart56.Add(Intermid56[a1]); } // Write parts 1-4 bw.Write(db.SlotTypes.Part0.Data); bw.Write(db.SlotTypes.Part1.Data); bw.Write(db.SlotTypes.Part2.Data); bw.Write(db.SlotTypes.Part3.Data); bw.Write(db.SlotTypes.Part4.Data); // Write part 5 int part5size = UsedPart56.Count * 4; padding = 3 - UsedPart56.Count % 4; if (padding != 0) { part5size += padding * 4; } bw.Write(CarParts.Part5); bw.Write(part5size); for (int a1 = 0; a1 < UsedPart56.Count; ++a1) { bw.Write(UsedPart56[a1].Key); } for (int a1 = 0; a1 < padding * 4; ++a1) { bw.Write((byte)0); } // Write part 6 int part6size = 0; bw.Write(CarParts.Part6); int size6off = (int)bw.BaseStream.Position; bw.Write(0xFFFFFFFF); // temp size for (int a1 = 0; a1 < UsedPart56.Count; ++a1) { bw.Write(UsedPart56[a1].Data); part6size += UsedPart56[a1].Data.Length; } padding = 0x10 - ((part6size + 8) % 0x10); if (padding == 0x10) { padding = 0; } for (int a1 = 0; a1 < padding; ++a1) { bw.Write((byte)0); } part6size += padding; bw.BaseStream.Position = size6off; bw.Write(part6size); // Quick editing bw.BaseStream.Position = CarIDOffset; bw.Write(UsedPart56.Count); bw.BaseStream.Position = PartNumOffset; bw.Write(part6size / 0xE); bw.BaseStream.Position = initial_size - 4; bw.Write((int)bw.BaseStream.Length - initial_size); bw.BaseStream.Position = bw.BaseStream.Length; }
// Default constructor: disassemble frontend group public FNGroup(byte[] data, Database.MostWanted db) { this.Database = db; this.Disassemble(data); }