diff --git a/Generators/GBX.NET.Generators/ChunkL/MemberSerializationWriter.cs b/Generators/GBX.NET.Generators/ChunkL/MemberSerializationWriter.cs index ac9d054e3..2f2446ebe 100644 --- a/Generators/GBX.NET.Generators/ChunkL/MemberSerializationWriter.cs +++ b/Generators/GBX.NET.Generators/ChunkL/MemberSerializationWriter.cs @@ -301,32 +301,42 @@ private void AppendRead(int indent, ChunkProperty chunkProperty) var contextual = archives.TryGetValue(chunkProperty.Type.PrimaryType, out var archiveModel) && archiveModel.ChunkLDefinition?.Properties.ContainsKey("contextual") == true; + var comma = false; + if (contextual) { sb.Append('n'); + comma = true; } if (chunkProperty.Properties?.TryGetValue("version", out var version) == true) { - if (contextual) + if (comma) { sb.Append(", "); } sb.Append("version: "); sb.Append(version); + + comma = true; } if (chunkProperty.Properties?.ContainsKey("external") == true && !chunkProperty.Type.IsArray) { - sb.Append(", out "); + if (comma) + { + sb.Append(", "); + } + + sb.Append("out "); if (!self && !isUnknown) { sb.Append("n."); } - sb.Append(name); + sb.Append(char.ToLowerInvariant(name[0]) + name.Substring(1)); sb.Append("File"); } diff --git a/Generators/GBX.NET.Generators/ClassChunkLMixedGenerator.cs b/Generators/GBX.NET.Generators/ClassChunkLMixedGenerator.cs index 8ae9cd0bf..bce4d30da 100644 --- a/Generators/GBX.NET.Generators/ClassChunkLMixedGenerator.cs +++ b/Generators/GBX.NET.Generators/ClassChunkLMixedGenerator.cs @@ -230,7 +230,7 @@ public virtual void Initialize(IncrementalGeneratorInitializationContext context var classIdContents = context.AdditionalTextsProvider .Where(static file => { - return file.Path.EndsWith("ClassId.txt") && Path.GetDirectoryName(file.Path).EndsWith("Resources"); + return (file.Path.EndsWith("ClassId.txt") || file.Path.EndsWith("ClassIdManual.txt")) && Path.GetDirectoryName(file.Path).EndsWith("Resources"); }) .Select((additionalText, cancellationToken) => { diff --git a/Generators/GBX.NET.Generators/Utils/ClassIdParser.cs b/Generators/GBX.NET.Generators/Utils/ClassIdParser.cs index aa06feeeb..6d2454088 100644 --- a/Generators/GBX.NET.Generators/Utils/ClassIdParser.cs +++ b/Generators/GBX.NET.Generators/Utils/ClassIdParser.cs @@ -67,7 +67,14 @@ public static Dictionary Parse(TextReader reader) // combine engine and class like EECCC000 var classId = (uint)((currentEngineByte << 24) | (classPart << 12)); - result.Add(classId, currentClassName); + if (!result.TryGetValue(classId, out var curName) || string.IsNullOrEmpty(curName)) + { + result[classId] = currentClassName; + } + else + { + throw new Exception($"Duplicate class id {classId:X8} with names '{curName}' and '{currentClassName}'."); + } } else { diff --git a/README.md b/README.md index a231d4916..1bc498ece 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ For any questions, open an issue, join the [GameBox Sandbox Discord server](http Many *essential* Gbx files from many games are supported: -- **Trackmania (2020)**, October 2024 update +- **Trackmania (2020)**, December 2024 update - **ManiaPlanet 4**(.1), TM2/SM - **Trackmania Turbo** - ManiaPlanet 3, TM2/SM @@ -87,7 +87,7 @@ Here are some of the known file types to start with: | LightMapCache.Gbx | [CHmsLightMapCache](Src/GBX.NET/Engines/Hms/CHmsLightMapCache.chunkl) | No | No | SystemConfig.Gbx | [CSystemConfig](Src/GBX.NET/Engines/System/CSystemConfig.chunkl) | Yes | Yes | FidCache.Gbx | [CMwRefBuffer](Src/GBX.NET/Engines/MwFoundations/CMwRefBuffer.chunkl) | Yes | Yes -| Scores.Gbx | [CGamePlayerScore](Src/GBX.NET/Engines/Game/CGamePlayerScore.chunkl) | Yes | No +| Scores.Gbx | [CGamePlayerScore](Src/GBX.NET/Engines/Game/CGamePlayerScore.chunkl) | No | No **Full list of supported file types is available in the [SUPPORTED GBX FILE TYPES](SUPPORTED_GBX_FILE_TYPES.md)**. diff --git a/Resources/ClassIdManual.txt b/Resources/ClassIdManual.txt new file mode 100644 index 000000000..233e1b255 Binary files /dev/null and b/Resources/ClassIdManual.txt differ diff --git a/Resources/Wrap.txt b/Resources/Wrap.txt index eb8f0f632..06a6104c2 100644 --- a/Resources/Wrap.txt +++ b/Resources/Wrap.txt @@ -24,11 +24,9 @@ 08050000 090E5000 08051000 09126000 08053000 090BF000 -08055000 09125000 0805A000 090B2000 0805B000 090B3000 0900D000 0900F000 -09012000 09047000 09063000 09026000 09068000 09026000 090E3000 2E006000 diff --git a/SUPPORTED_GBX_FILE_TYPES.md b/SUPPORTED_GBX_FILE_TYPES.md index fcc973d4b..7812ebce3 100644 --- a/SUPPORTED_GBX_FILE_TYPES.md +++ b/SUPPORTED_GBX_FILE_TYPES.md @@ -15,7 +15,7 @@ Older extensions | Latest extension | Class | Read (whole) | Write | Read (heade | | Shape.Gbx | [CPlugSurface](Src/GBX.NET/Engines/Plug/CPlugSurface.chunkl) | Yes | Yes | | Macroblock.Gbx | [CGameCtnMacroBlockInfo](Src/GBX.NET/Engines/Game/CGameCtnMacroBlockInfo.chunkl) | Yes | Yes | Yes | | SystemConfig.Gbx | [CSystemConfig](Src/GBX.NET/Engines/System/CSystemConfig.chunkl) | Yes | Yes -| | FidCache.Gbx | [CMwRefBuffer](Src/GBX.NET/Engines/MwFoundations/CMwRefBuffer.chunkl) | Yes | Yes +| RefBuffer.Gbx | FidCache.Gbx | [CMwRefBuffer](Src/GBX.NET/Engines/MwFoundations/CMwRefBuffer.chunkl) | Yes | Yes | | Profile.Gbx | [CGamePlayerProfile](Src/GBX.NET/Engines/Game/CGamePlayerProfile.chunkl) | Up to TMF | Up to TMF | Yes | | Spawn.Gbx | [CGameSpawnModel](Src/GBX.NET/Engines/GameData/CGameSpawnModel.chunkl) | Yes | Yes | ConstructionCampaign.Gbx | Campaign.Gbx | [CGameCtnCampaign](Src/GBX.NET/Engines/Game/CGameCtnCampaign.chunkl) | Yes | Yes @@ -24,6 +24,7 @@ Older extensions | Latest extension | Class | Read (whole) | Write | Read (heade | TMDecorationSize.Gbx | DecorationSize.Gbx | [CGameCtnDecorationSize](Src/GBX.NET/Engines/Game/CGameCtnDecorationSize.chunkl) | Yes | Yes | Yes | TMDecorationMood.Gbx | DecorationMood.Gbx | [CGameCtnDecorationMood](Src/GBX.NET/Engines/Game/CGameCtnDecorationMood.chunkl) | Yes | Yes | Yes | TMDecorationAudio.Gbx | DecorationAudio.Gbx | [CGameCtnDecorationAudio](Src/GBX.NET/Engines/Game/CGameCtnDecorationAudio.chunkl) | Yes | Yes | Yes +| | Scene3d.Gbx | [CSceneLayout](Src/GBX.NET/Engines/Scene/CSceneLayout.chunkl) | Yes | Yes | TMEDClassic.Gbx | EDClassic.Gbx | [CGameCtnBlockInfoClassic](Src/GBX.NET/Engines/Game/CGameCtnBlockInfoClassic.chunkl) | Yes | Yes | Yes | TMEDClip.Gbx | EDClip.Gbx | [CGameCtnBlockInfoClip](Src/GBX.NET/Engines/Game/CGameCtnBlockInfoClip.chunkl) | Yes | Yes | Yes | TMEDFlat.Gbx | EDFlat.Gbx | [CGameCtnBlockInfoFlat](Src/GBX.NET/Engines/Game/CGameCtnBlockInfoFlat.chunkl) | Yes | Yes | Yes @@ -39,13 +40,29 @@ Older extensions | Latest extension | Class | Read (whole) | Write | Read (heade | | MotionManagerWeathers.Gbx | [CPlugWeatherModel](Src/GBX.NET/Engines/Plug/CPlugWeatherModel.chunkl) | Up to TMUF | Yes | | RallyLeafManager.Gbx | [CMotionManagerLeaves](Src/GBX.NET/Engines/Motion/CMotionManagerLeaves.chunkl) | Yes | Yes | | GameSkin.Gbx | [CPlugGameSkin](Src/GBX.NET/Engines/Plug/CPlugGameSkin.chunkl) | Yes | Yes -| | VehicleTunings.Gbx | [CPlugVehiclePhyTunings](Src/GBX.NET/Engines/Plug/CPlugVehiclePhyTunings.chunkl) | Up to TM2 | Up to TM2 | Yes +| | VehicleTunings.Gbx | [CPlugVehiclePhyTunings](Src/GBX.NET/Engines/Plug/CPlugVehiclePhyTunings.chunkl) | Up to TM2 | Up to TM2 +| | CtrlCam.Gbx | [CGameControlCamera](Src/GBX.NET/Engines/Game/CGameControlCamera.chunkl) | Yes | Yes +| | CtrlCamTarget.Gbx | [CGameControlCameraTarget](Src/GBX.NET/Engines/Game/CGameControlCameraTarget.chunkl) | Yes | Yes +| | CtrlCamOrbital3d.Gbx | [CGameControlCameraOrbital3d](Src/GBX.NET/Engines/Game/CGameControlCameraOrbital3d.chunkl) | Yes | Yes +| | CtrlCamTmRace.Gbx | [CGameControlCameraTrackManiaRace](Src/GBX.NET/Engines/TrackMania/CGameControlCameraTrackManiaRace.chunkl) | Yes | Yes +| | CtrlCamTmRace2.Gbx | [CGameControlCameraTrackManiaRace2](Src/GBX.NET/Engines/TrackMania/CGameControlCameraTrackManiaRace2.chunkl) | Yes | Yes +| | CtrlCamTmRace3.Gbx | [CGameControlCameraTrackManiaRace3](Src/GBX.NET/Engines/TrackMania/CGameControlCameraTrackManiaRace3.chunkl) | Yes | Yes +| | DecoSolid.Gbx | [CPlugDecoratorSolid](Src/GBX.NET/Engines/CPlug/CPlugDecoratorSolid.chunkl) | Yes | Yes +| | TrackManiaVehicle.Gbx | [CSceneVehicleCar](Src/GBX.NET/Engines/Scene/CSceneVehicleCar.chunkl) | Only TMUF | Only TMUF +| TMVehicle.Gbx | ConstructionVehicle.Gbx | [CGameItemModel](Src/GBX.NET/Engines/GameData/CGameItemModel.chunkl) | Yes | Yes | Yes +| | ParticleModel.Gbx | [CPlugParticleEmitterModel](Src/GBX.NET/Engines/CPlug/CPlugParticleEmitterModel.chunkl) | Up to TMUF | Up to TMUF +| | VehicleStruct.Gbx | [CPlugVehicleVisModelShared](Src/GBX.NET/Engines/GameData/CPlugVehicleVisModelShared.chunkl) | Yes | Yes +| | FuncClouds.Gbx | [CPlugClouds](Src/GBX.NET/Engines/Plug/CPlugClouds.chunkl) | Yes | Yes +| | FuncShader.Gbx | [CFuncShaderLayerUV](Src/GBX.NET/Engines/Func/CFuncShaderLayerUV.chunkl) | Yes | Yes +| | AmbientOcc.Gbx | [CHmsAmbientOcc](Src/GBX.NET/Engines/Hms/CHmsAmbientOcc.chunkl) | Yes | Yes | | ObjectInfo.Gbx | [CGameItemModel](Src/GBX.NET/Engines/GameData/CGameItemModel.chunkl) | Yes | Yes | Yes | | Mobil.Gbx | [CSceneMobil](Src/GBX.NET/Engines/Scene/CSceneMobil.chunkl) | Yes | Yes | | Solid.Gbx | [CPlugSolid](Src/GBX.NET/Engines/Plug/CPlugSolid.cs) | Yes | Yes | | Material.Gbx | [CPlugMaterial](Src/GBX.NET/Engines/Plug/CPlugMaterial.cs) | Up to TM2 | No +| | Shader.Gbx | [CPlugShader](Src/GBX.NET/Engines/Plug/CPlugShader.cs) | Up to TMUF | No | | Texture.Gbx | [CPlugBitmap](Src/GBX.NET/Engines/Plug/CPlugBitmap.cs) | Up to TMUF | No | | Light.Gbx | [CPlugLight](Src/GBX.NET/Engines/Plug/CPlugLight.cs) | Yes | Yes | | Prefab.Gbx | [CPlugPrefab](Src/GBX.NET/Engines/Plug/CPlugPrefab.cs) | Yes | Yes +| | Wagon.Gbx | [CPlugTrainWagonModel](Src/GBX.NET/Engines/Plug/CPlugTrainWagonModel.cs) | Yes | Yes - 1Safety reasons. Consider extracting `CGameCtnGhost` from `CGameCtnReplayRecord`, transfer it over to `CGameCtnMediaBlockGhost`, add it to `CGameCtnMediaClip`, and save it as `.Clip.Gbx`, which you can then import in MediaTracker. \ No newline at end of file diff --git a/Src/GBX.NET.PAK/Exceptions/NotAPakException.cs b/Src/GBX.NET.PAK/Exceptions/NotAPakException.cs index e4a2abef6..e7e14874d 100644 --- a/Src/GBX.NET.PAK/Exceptions/NotAPakException.cs +++ b/Src/GBX.NET.PAK/Exceptions/NotAPakException.cs @@ -1,9 +1,9 @@ -namespace GBX.NET.Exceptions; +namespace GBX.NET.PAK.Exceptions; [Serializable] public class NotAPakException : Exception { - public NotAPakException() : base("Pak data stream was not identified.") { } - public NotAPakException(string message) : base(message) { } - public NotAPakException(string message, Exception? innerException) : base(message, innerException) { } + public NotAPakException() : base("Pak data stream was not identified.") { } + public NotAPakException(string message) : base(message) { } + public NotAPakException(string message, Exception? innerException) : base(message, innerException) { } } diff --git a/Src/GBX.NET.PAK/GBX.NET.PAK.csproj b/Src/GBX.NET.PAK/GBX.NET.PAK.csproj index 1790e32d3..a5cd2dc7a 100644 --- a/Src/GBX.NET.PAK/GBX.NET.PAK.csproj +++ b/Src/GBX.NET.PAK/GBX.NET.PAK.csproj @@ -2,7 +2,7 @@ GBX.NET.PAK - 2.0.0 + 2.1.0 BigBang1112 Support for Pak (NadeoPak) package files, integrated with GBX.NET. Copyright (c) 2024 Petr Pivoňka @@ -45,7 +45,7 @@ all runtime; build; native; contentfiles; analyzers - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Src/GBX.NET.PAK/Pak.cs b/Src/GBX.NET.PAK/Pak.cs index 1f976f2b7..284906c50 100644 --- a/Src/GBX.NET.PAK/Pak.cs +++ b/Src/GBX.NET.PAK/Pak.cs @@ -1,5 +1,7 @@ -using GBX.NET.Crypto; +using GBX.NET.Components; +using GBX.NET.Crypto; using GBX.NET.Exceptions; +using GBX.NET.PAK.Exceptions; using GBX.NET.Serialization; using NativeSharpZlib; using System.Collections.Immutable; @@ -9,7 +11,7 @@ namespace GBX.NET.PAK; -public sealed partial class Pak : IDisposable +public partial class Pak : IDisposable #if NET5_0_OR_GREATER , IAsyncDisposable #endif @@ -22,37 +24,46 @@ public sealed partial class Pak : IDisposable private const string PakListFileName = "packlist.dat"; private readonly Stream stream; - private readonly byte[] key; - private readonly byte[] headerMD5; - private readonly int metadataStart; - private readonly int dataStart; + private readonly byte[]? key; + private readonly byte[]? secondKey; + + private int metadataStart; + private int dataStart; public int Version { get; } - public int Flags { get; } - public ImmutableDictionary Files { get; } - - private Pak(Stream stream, - byte[] key, - int version, - byte[] headerMD5, - int metadataStart, - int dataStart, - int flags, - ImmutableDictionary files) + + public ulong HeaderIV { get; private set; } + public byte[]? HeaderMD5 { get; private set; } + public int Flags { get; private set; } + + public ImmutableDictionary Files { get; private set; } = ImmutableDictionary.Empty; + + internal Pak(Stream stream, byte[]? key, byte[]? secondKey, int version) { this.stream = stream; this.key = key; + this.secondKey = secondKey; Version = version; - this.headerMD5 = headerMD5; - this.metadataStart = metadataStart; - this.dataStart = dataStart; - Flags = flags; - Files = files; } + /// + /// Parses the Pak file from the stream. Should be disposed after use, as it keeps the file open (currently at least). + /// + /// Stream. + /// Key for main decryption, or only the header part for newer Pak format. + /// Alternative key to use for decrypting individual files. Ignored for TMF Pak (v3) format and older. + /// Cancellation token. + /// A task. The task result contains the parsed Pak format. + /// is null. + /// Stream is not Pak-formatted. [Zomp.SyncMethodGenerator.CreateSyncVersion] - public static async Task ParseAsync(Stream stream, byte[] key, CancellationToken cancellationToken = default) + public static async Task ParseAsync(Stream stream, byte[]? key, byte[]? secondKey = null, CancellationToken cancellationToken = default) { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + var r = new GbxReader(stream); if (!r.ReadPakMagic()) @@ -62,55 +73,103 @@ public static async Task ParseAsync(Stream stream, byte[] key, Cancellation var version = r.ReadInt32(); - if (version > 5) + var pak = await CreateBasePak(stream, r, key, secondKey, version, cancellationToken); + pak.HeaderIV = r.ReadUInt64(); + + if (key is not null) { - throw new Exception($"Version >5 (actual: {version}) is not supported. Use Pak6 for this file."); + await pak.ReadEncryptedAsync(stream, cancellationToken); } - var headerIV = r.ReadUInt64(); - - var decryptStream = new BlowfishStream(stream, key, headerIV); - var decryptReader = new GbxReader(decryptStream); - - return await ParseEncryptedAsync(decryptReader, stream, version, key, cancellationToken); + return pak; } - public static async Task ParseAsync(string filePath, byte[] key, CancellationToken cancellationToken = default) + /// + /// Parses the Pak file from file path. Should be disposed after use, as it keeps the file open (currently at least). + /// + /// File path. + /// Key for main decryption, or only the header part for newer Pak format. + /// Alternative key to use for decrypting individual files. Ignored for TMF Pak (v3) format and older. + /// Cancellation token. + /// A task. The task result contains the parsed Pak format. + /// is null. + /// Stream is not Pak-formatted. + public static async Task ParseAsync(string filePath, byte[] key, byte[]? secondKey = null, CancellationToken cancellationToken = default) { var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true); - return await ParseAsync(fs, key, cancellationToken); + return await ParseAsync(fs, key, secondKey, cancellationToken); } - public static Pak Parse(string filePath, byte[] key) + /// + /// Parses the Pak file from file path. Should be disposed after use, as it keeps the file open (currently at least). + /// + /// File path. + /// Key for main decryption, or only the header part for newer Pak format. + /// Alternative key to use for decrypting individual files. Ignored for TMF Pak (v3) format and older. + /// Parsed Pak format. + /// is null. + /// Stream is not Pak-formatted. + public static Pak Parse(string filePath, byte[] key, byte[]? secondKey = null) { var fs = File.OpenRead(filePath); - return Parse(fs, key); + return Parse(fs, key, secondKey); } [Zomp.SyncMethodGenerator.CreateSyncVersion] - private static async Task ParseEncryptedAsync( - GbxReader r, - Stream originalStream, - int version, - byte[] key, - CancellationToken cancellationToken) + private static async ValueTask CreateBasePak(Stream stream, GbxReader r, byte[]? key, byte[]? secondKey, int version, CancellationToken cancellationToken) { - var headerMD5 = await r.ReadBytesAsync(16, cancellationToken); - var metadataStart = r.ReadInt32(); // offset to metadata section - var dataStart = r.ReadInt32(); + if (version < 6) + { + return new Pak(stream, key, secondKey, version); + } + + var pak6 = new Pak6(stream, headerKey: key, bodyKey: secondKey, version); + await pak6.ReadUnencryptedHeaderAsync(r, version, cancellationToken); + return pak6; + } - if (version >= 2) + [Zomp.SyncMethodGenerator.CreateSyncVersion] + protected async Task ReadEncryptedAsync(Stream stream, CancellationToken cancellationToken) + { + if (key is null) + { + throw new Exception("Encryption key is missing"); + } + + var decryptStream = new BlowfishStream(stream, key, HeaderIV); + var r = new GbxReader(decryptStream); + + HeaderMD5 = await r.ReadBytesAsync(16, cancellationToken); + metadataStart = r.ReadInt32(); // offset to metadata section + dataStart = r.ReadInt32(); + + if (Version >= 2) { var gbxHeadersSize = r.ReadInt32(); var gbxHeadersComprSize = r.ReadInt32(); } - if (version >= 3) + if (Version >= 14) + { + r.SkipData(16); // unused + + if (Version >= 16) + { + var fileSize = r.ReadUInt32(); + } + } + + if (Version >= 3) { r.SkipData(16); // unused + + if (Version == 6 && this is Pak6 pak6) + { + pak6.ReadAuthorInfo(r); + } } - var flags = r.ReadInt32(); + Flags = r.ReadInt32(); var allFolders = ReadAllFolders(r); @@ -120,9 +179,7 @@ private static async Task ParseEncryptedAsync( ((IEncryptionInitializer)r.BaseStream).Initialize(nameBytes, 4, 4); } - var files = ReadAllFiles(r, allFolders); - - return new Pak(originalStream, key, version, headerMD5, metadataStart, dataStart, flags, files); + Files = ReadAllFiles(r, allFolders); } private static PakFolder[] ReadAllFolders(GbxReader r) @@ -146,7 +203,7 @@ private static PakFolder[] ReadAllFolders(GbxReader r) return allFolders; } - private static ImmutableDictionary ReadAllFiles(GbxReader r, PakFolder[] allFolders) + private ImmutableDictionary ReadAllFiles(GbxReader r, PakFolder[] allFolders) { var files = ImmutableDictionary.CreateBuilder(); @@ -160,6 +217,9 @@ private static ImmutableDictionary ReadAllFiles(GbxReader r, Pa var compressedSize = r.ReadInt32(); var offset = r.ReadInt32(); var classId = r.ReadUInt32(); // indicates the type of the file + var size = Version >= 17 ? r.ReadInt32() : default(int?); + var checksum = Version >= 14 ? r.ReadUInt128() : default(UInt128?); + var fileFlags = r.ReadUInt64(); var folderPath = string.Join(Path.DirectorySeparatorChar, RecurseFoldersToParent(folderIndex, allFolders) @@ -167,7 +227,7 @@ private static ImmutableDictionary ReadAllFiles(GbxReader r, Pa .Select(f => f.Name.TrimEnd('\\'))); var filePath = Path.Combine(folderPath, name); - var file = new PakFile(name, folderPath, classId, offset, uncompressedSize, compressedSize, fileFlags); + var file = new PakFile(name, folderPath, classId, offset, uncompressedSize, compressedSize, size, checksum, fileFlags); files[filePath] = file; } @@ -198,6 +258,11 @@ private static IEnumerable RecurseFoldersToParent(int folderIndex, Pa public Stream OpenFile(PakFile file, out EncryptionInitializer encryptionInitializer) { + if (key is null) + { + throw new Exception("Encryption key is missing"); + } + stream.Position = dataStart + file.Offset; var ivBuffer = new byte[8]; @@ -219,11 +284,75 @@ public Stream OpenFile(PakFile file, out EncryptionInitializer encryptionInitial return new NativeZlibStream(blowfish, CompressionMode.Decompress); } + /// + /// Attempts to open the Gbx file from Pak. If the file is not a Gbx file, is thrown. + /// + /// + /// + /// + /// + /// + /// [Zomp.SyncMethodGenerator.CreateSyncVersion] - public async Task OpenGbxFileAsync(PakFile file, GbxReadSettings settings = default, CancellationToken cancellationToken = default) + public async Task OpenGbxFileAsync(PakFile file, GbxReadSettings settings = default, bool importExternalNodesFromRefTable = false, IDictionary? fileHashes = default, CancellationToken cancellationToken = default) { using var stream = OpenFile(file, out var encryptionInitializer); - return await Gbx.ParseAsync(stream, settings with { EncryptionInitializer = encryptionInitializer }, cancellationToken); + + var settingsWithEncryption = settings with { EncryptionInitializer = encryptionInitializer }; + + var gbx = await Gbx.ParseAsync(stream, settingsWithEncryption, cancellationToken); + + if (gbx.RefTable is not null && importExternalNodesFromRefTable) + { + // this can miss some files from other Pak files + ImportExternalNodesFromRefTable(this, file, gbx.RefTable, settingsWithEncryption, fileHashes); + } + + return gbx; + } + + private static void ImportExternalNodesFromRefTable(Pak pak, PakFile file, GbxRefTable refTable, GbxReadSettings settings, IDictionary? fileHashes) + { + var ancestor = string.Join('\\', Enumerable.Repeat("..", refTable.AncestorLevel)); + var currentFileName = fileHashes?.TryGetValue(file.Name, out var resolvedFileName) == true ? resolvedFileName : file.Name; + var currentFileFolderPath = Path.GetDirectoryName(currentFileName); + var currentPakFileFolderPath = string.IsNullOrEmpty(currentFileFolderPath) ? file.FolderPath : Path.Combine(file.FolderPath, currentFileFolderPath); + + foreach (var refTableFile in refTable.Files) + { + var filePath = Path.GetRelativePath(Directory.GetCurrentDirectory(), Path.Combine(currentPakFileFolderPath, ancestor, refTableFile.FilePath)).Replace('/', '\\'); + + if (!pak.Files.TryGetValue(filePath, out var refTableFileInPak)) + { + var directoryPath = Path.GetDirectoryName(filePath); + var fileName = Path.GetFileName(filePath); + + while (true) + { + var hash = MD5.Compute136(fileName); + + if (pak.Files.TryGetValue(directoryPath + "\\" + hash, out refTableFileInPak)) + { + break; + } + + fileName = Path.Combine(Path.GetFileName(directoryPath)!, fileName); // maybe can rarely fail here? + directoryPath = Path.GetDirectoryName(directoryPath); + + if (string.IsNullOrEmpty(directoryPath)) + { + break; + } + } + } + + if (refTableFileInPak is null) + { + continue; + } + + refTable.ExternalNodes.Add(refTableFile.FilePath, () => pak.OpenGbxFile(refTableFileInPak, settings)); + } } [Zomp.SyncMethodGenerator.CreateSyncVersion] @@ -260,7 +389,14 @@ public static async Task> BruteforceFileHashesAsync( bool onlyUsedHashes = true, CancellationToken cancellationToken = default) { - var pakList = await PakList.ParseAsync(Path.Combine(directoryPath, PakListFileName), game, cancellationToken); + var pakListFilePath = Path.Combine(directoryPath, PakListFileName); + + if (!File.Exists(pakListFilePath)) + { + return []; + } + + var pakList = await PakList.ParseAsync(pakListFilePath, game, cancellationToken); var allPossibleFileHashes = new Dictionary(); @@ -332,7 +468,7 @@ public static async Task> BruteforceFileHashesAsync( continue; } - using var pak = await ParseAsync(fullFileName, pakInfo.Value.Key, cancellationToken); + using var pak = await ParseAsync(fullFileName, pakInfo.Value.Key, cancellationToken: cancellationToken); foreach (var file in pak.Files.Values) { diff --git a/Src/GBX.NET.PAK/Pak6.cs b/Src/GBX.NET.PAK/Pak6.cs index 5bb20b5ff..a5382a2b9 100644 --- a/Src/GBX.NET.PAK/Pak6.cs +++ b/Src/GBX.NET.PAK/Pak6.cs @@ -1,9 +1,138 @@ -namespace GBX.NET.PAK; +using GBX.NET.Serialization; -internal sealed class Pak6 +namespace GBX.NET.PAK; + +internal sealed partial class Pak6 : Pak { - public static Task ParseAsync(Stream stream, byte[] key, CancellationToken cancellationToken = default) + public byte[] Checksum { get; private set; } = []; + public uint HeaderFlags { get; private set; } + public int? HeaderMaxSize { get; private set; } + public int AuthorVersion { get; private set; } + public string AuthorLogin { get; private set; } = string.Empty; + public string AuthorNickname { get; private set; } = string.Empty; + public string AuthorZone { get; private set; } = string.Empty; + public string AuthorExtraInfo { get; private set; } = string.Empty; + public string Comments { get; private set; } = string.Empty; + public string CreationBuildInfo { get; private set; } = string.Empty; + public string AuthorUrl { get; private set; } = string.Empty; + public string? ManialinkUrl { get; private set; } + public string? DownloadUrl { get; private set; } + public DateTime? CreationDate { get; private set; } + public string? Xml { get; private set; } + public string? TitleId { get; private set; } + public string? UsageSubDir { get; private set; } + public IncludedPackHeader[] IncludedPacks { get; private set; } = []; + + internal Pak6(Stream stream, byte[]? headerKey, byte[]? bodyKey, int version) : base(stream, headerKey, bodyKey, version) + { + } + + [Zomp.SyncMethodGenerator.CreateSyncVersion] + internal async Task ReadUnencryptedHeaderAsync(GbxReader r, int version, CancellationToken cancellationToken) + { + Checksum = await r.ReadBytesAsync(32, cancellationToken); + HeaderFlags = r.ReadUInt32(); + + if (version >= 15) + { + HeaderMaxSize = r.ReadInt32(); + } + + if (version >= 7) + { + ReadAuthorInfo(r); + + if (version < 9) + { + Comments = r.ReadString(); + r.SkipData(16); + + if (version == 8) + { + CreationBuildInfo = r.ReadString(); + AuthorUrl = r.ReadString(); + } + } + else + { + ManialinkUrl = r.ReadString(); + + if (version >= 13) + { + DownloadUrl = r.ReadString(); + } + + CreationDate = r.ReadFileTime(); + Comments = r.ReadString(); + + if (version >= 12) + { + Xml = r.ReadString(); + TitleId = r.ReadString(); + } + + UsageSubDir = r.ReadString(); + CreationBuildInfo = r.ReadString(); + r.SkipData(16); + + if (version >= 10) + { + IncludedPacks = new IncludedPackHeader[r.ReadUInt32()]; + + for (var i = 0; i < IncludedPacks.Length; i++) + { + IncludedPacks[i] = await IncludedPackHeader.DeserializeAsync(r, version, cancellationToken); + } + } + } + } + } + + internal void ReadAuthorInfo(GbxReader r) { - return Task.FromResult(new Pak6()); + AuthorVersion = r.ReadInt32(); + AuthorLogin = r.ReadString(); + AuthorNickname = r.ReadString(); + AuthorZone = r.ReadString(); + AuthorExtraInfo = r.ReadString(); + } + + public sealed partial class IncludedPackHeader + { + public byte[] ContentsChecksum { get; private set; } = []; + public string Name { get; private set; } = string.Empty; + public int AuthorVersion { get; private set; } + public string AuthorLogin { get; private set; } = string.Empty; + public string AuthorNickName { get; private set; } = string.Empty; + public string AuthorZone { get; private set; } = string.Empty; + public string AuthorExtraInfo { get; private set; } = string.Empty; + public string InfoManialinkUrl { get; private set; } = string.Empty; + public DateTime CreationDate { get; private set; } + public uint IncludeDepth { get; private set; } + + [Zomp.SyncMethodGenerator.CreateSyncVersion] + internal static async Task DeserializeAsync(GbxReader r, int version, CancellationToken cancellationToken) + { + var includedPacksHeaders = new IncludedPackHeader + { + ContentsChecksum = await r.ReadBytesAsync(32, cancellationToken), + Name = r.ReadString(), + AuthorVersion = r.ReadInt32(), + AuthorLogin = r.ReadString(), + AuthorNickName = r.ReadString(), + AuthorZone = r.ReadString(), + AuthorExtraInfo = r.ReadString(), + InfoManialinkUrl = r.ReadString(), + CreationDate = r.ReadFileTime() + }; + includedPacksHeaders.Name = r.ReadString(); + + if (version >= 11) + { + includedPacksHeaders.IncludeDepth = r.ReadUInt32(); + } + + return includedPacksHeaders; + } } } diff --git a/Src/GBX.NET.PAK/PakFile.cs b/Src/GBX.NET.PAK/PakFile.cs index d7eff5bc2..df316a37b 100644 --- a/Src/GBX.NET.PAK/PakFile.cs +++ b/Src/GBX.NET.PAK/PakFile.cs @@ -8,6 +8,8 @@ public sealed class PakFile public int Offset { get; } public int UncompressedSize { get; } public int CompressedSize { get; } + public int? Size { get; } + public UInt128? Checksum { get; } public ulong Flags { get; } public bool IsCompressed => (Flags & 0x7C) != 0; @@ -18,7 +20,9 @@ public PakFile( uint classId, int offset, int uncompressedSize, - int compressedSize, + int compressedSize, + int? size, + UInt128? checksum, ulong flags) { Name = name; @@ -27,6 +31,8 @@ public PakFile( Offset = offset; UncompressedSize = uncompressedSize; CompressedSize = compressedSize; + Size = size; + Checksum = checksum; Flags = flags; } diff --git a/Src/GBX.NET.Tool.CLI/GBX.NET.Tool.CLI.csproj b/Src/GBX.NET.Tool.CLI/GBX.NET.Tool.CLI.csproj index 367c3f7b1..8b4a9e3c7 100644 --- a/Src/GBX.NET.Tool.CLI/GBX.NET.Tool.CLI.csproj +++ b/Src/GBX.NET.Tool.CLI/GBX.NET.Tool.CLI.csproj @@ -2,7 +2,7 @@ GBX.NET.Tool.CLI - 0.5.0 + 0.5.1 BigBang1112 CLI implementation for the GBX.NET tool framework using Spectre.Console. Copyright (c) 2024 Petr Pivoňka diff --git a/Src/GBX.NET.Tool.CLI/InputArguments/DirectoryInputArgument.cs b/Src/GBX.NET.Tool.CLI/InputArguments/DirectoryInputArgument.cs index 35c364c6a..e953cc4a3 100644 --- a/Src/GBX.NET.Tool.CLI/InputArguments/DirectoryInputArgument.cs +++ b/Src/GBX.NET.Tool.CLI/InputArguments/DirectoryInputArgument.cs @@ -1,11 +1,12 @@  using GBX.NET.Exceptions; +using Microsoft.Extensions.Logging; namespace GBX.NET.Tool.CLI.InputArguments; public sealed record DirectoryInputArgument(string DirectoryPath) : InputArgument { - public override async Task ResolveAsync(CancellationToken cancellationToken) + public override Task ResolveAsync(ILogger logger, CancellationToken cancellationToken) { var files = Directory.EnumerateFiles(DirectoryPath, "*.*", SearchOption.AllDirectories); @@ -13,6 +14,7 @@ public sealed record DirectoryInputArgument(string DirectoryPath) : InputArgumen { try { + logger.LogInformation("Reading file {File}...", Path.GetFileName(file)); await using var stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, useAsync: true); return await Gbx.ParseAsync(stream, cancellationToken: cancellationToken); } @@ -20,8 +22,13 @@ public sealed record DirectoryInputArgument(string DirectoryPath) : InputArgumen { return await File.ReadAllBytesAsync(file, cancellationToken); } + catch (Exception ex) + { + logger.LogError(ex, "Failed to read file {File}.", Path.GetFileName(file)); + return null; + } }); - return await Task.WhenAll(tasks); + return Task.FromResult((object?)tasks); } } diff --git a/Src/GBX.NET.Tool.CLI/InputArguments/FileInputArgument.cs b/Src/GBX.NET.Tool.CLI/InputArguments/FileInputArgument.cs index 89b929bff..e089a7ce9 100644 --- a/Src/GBX.NET.Tool.CLI/InputArguments/FileInputArgument.cs +++ b/Src/GBX.NET.Tool.CLI/InputArguments/FileInputArgument.cs @@ -1,14 +1,16 @@  using GBX.NET.Exceptions; +using Microsoft.Extensions.Logging; namespace GBX.NET.Tool.CLI.InputArguments; public sealed record FileInputArgument(string FilePath) : InputArgument { - public override async Task ResolveAsync(CancellationToken cancellationToken) + public override async Task ResolveAsync(ILogger logger, CancellationToken cancellationToken) { try { + logger.LogInformation("Reading file {File}...", Path.GetFileName(FilePath)); await using var stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, useAsync: true); return await Gbx.ParseAsync(stream, cancellationToken: cancellationToken); } diff --git a/Src/GBX.NET.Tool.CLI/InputArguments/InputArgument.cs b/Src/GBX.NET.Tool.CLI/InputArguments/InputArgument.cs index 4f34967b9..7926e88e0 100644 --- a/Src/GBX.NET.Tool.CLI/InputArguments/InputArgument.cs +++ b/Src/GBX.NET.Tool.CLI/InputArguments/InputArgument.cs @@ -1,7 +1,9 @@  +using Microsoft.Extensions.Logging; + namespace GBX.NET.Tool.CLI.InputArguments; public abstract record InputArgument { - public abstract Task ResolveAsync(CancellationToken cancellationToken); + public abstract Task ResolveAsync(ILogger logger, CancellationToken cancellationToken); } diff --git a/Src/GBX.NET.Tool.CLI/InputArguments/StandardInputArgument.cs b/Src/GBX.NET.Tool.CLI/InputArguments/StandardInputArgument.cs index 53f4b3430..9e02e8619 100644 --- a/Src/GBX.NET.Tool.CLI/InputArguments/StandardInputArgument.cs +++ b/Src/GBX.NET.Tool.CLI/InputArguments/StandardInputArgument.cs @@ -1,8 +1,10 @@ -namespace GBX.NET.Tool.CLI.InputArguments; +using Microsoft.Extensions.Logging; + +namespace GBX.NET.Tool.CLI.InputArguments; public sealed record StandardInputArgument(Stream Stream) : InputArgument { - public override Task ResolveAsync(CancellationToken cancellationToken) + public override Task ResolveAsync(ILogger logger, CancellationToken cancellationToken) { return Task.FromResult((object?)Stream); } diff --git a/Src/GBX.NET.Tool.CLI/InputArguments/StandardTextInputArgument.cs b/Src/GBX.NET.Tool.CLI/InputArguments/StandardTextInputArgument.cs index 09fe0d487..da873df46 100644 --- a/Src/GBX.NET.Tool.CLI/InputArguments/StandardTextInputArgument.cs +++ b/Src/GBX.NET.Tool.CLI/InputArguments/StandardTextInputArgument.cs @@ -1,8 +1,10 @@ -namespace GBX.NET.Tool.CLI.InputArguments; +using Microsoft.Extensions.Logging; + +namespace GBX.NET.Tool.CLI.InputArguments; public sealed record StandardTextInputArgument(TextReader Reader) : InputArgument { - public override Task ResolveAsync(CancellationToken cancellationToken) + public override Task ResolveAsync(ILogger logger, CancellationToken cancellationToken) { return Task.FromResult((object?)Reader); } diff --git a/Src/GBX.NET.Tool.CLI/InputArguments/UriInputArgument.cs b/Src/GBX.NET.Tool.CLI/InputArguments/UriInputArgument.cs index f75539ac0..ab022d1af 100644 --- a/Src/GBX.NET.Tool.CLI/InputArguments/UriInputArgument.cs +++ b/Src/GBX.NET.Tool.CLI/InputArguments/UriInputArgument.cs @@ -1,23 +1,28 @@  using GBX.NET.Exceptions; +using Microsoft.Extensions.Logging; namespace GBX.NET.Tool.CLI.InputArguments; public sealed record UriInputArgument(HttpClient Http, Uri Uri) : InputArgument { - public override async Task ResolveAsync(CancellationToken cancellationToken) + public override async Task ResolveAsync(ILogger logger, CancellationToken cancellationToken) { + logger.LogInformation("Downloading {Uri}...", Uri); + using var response = await Http.GetAsync(Uri, cancellationToken); response.EnsureSuccessStatusCode(); try { + logger.LogInformation("Reading {Uri} as Gbx...", Uri); await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken); return await Gbx.ParseAsync(stream, cancellationToken: cancellationToken); } catch (NotAGbxException) { + logger.LogInformation("Reading {Uri} as data...", Uri); return await response.Content.ReadAsByteArrayAsync(cancellationToken); } } diff --git a/Src/GBX.NET.Tool.CLI/SpectreConsoleLogger.cs b/Src/GBX.NET.Tool.CLI/SpectreConsoleLogger.cs index 372730402..17363a2a7 100644 --- a/Src/GBX.NET.Tool.CLI/SpectreConsoleLogger.cs +++ b/Src/GBX.NET.Tool.CLI/SpectreConsoleLogger.cs @@ -30,6 +30,11 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except } AnsiConsole.MarkupLine($" {GetLevelString(logLevel)} {Markup.Escape(formatter(state, exception))}"); + + if (exception is not null) + { + AnsiConsole.WriteException(exception); + } } private static string GetLevelString(LogLevel level) => level switch diff --git a/Src/GBX.NET.Tool.CLI/ToolInstanceMaker.cs b/Src/GBX.NET.Tool.CLI/ToolInstanceMaker.cs index 5f08e8c2e..c60bf1709 100644 --- a/Src/GBX.NET.Tool.CLI/ToolInstanceMaker.cs +++ b/Src/GBX.NET.Tool.CLI/ToolInstanceMaker.cs @@ -282,7 +282,7 @@ private async IAsyncEnumerable EnumerateResolvedObjectsAsync([Enumerator // Resolve objects from input and cache them if (!resolvedInputs.TryGetValue(input, out var resolvedObject)) { - resolvedObject = await input.ResolveAsync(cancellationToken); + resolvedObject = await input.ResolveAsync(logger, cancellationToken); resolvedInputs.Add(input, resolvedObject); } @@ -303,7 +303,14 @@ private async IAsyncEnumerable EnumerateResolvedObjectsAsync([Enumerator continue; } - yield return obj; + if (obj is Task task) + { + yield return await task; + } + else + { + yield return obj; + } } } else diff --git a/Src/GBX.NET/Components/GbxBody.cs b/Src/GBX.NET/Components/GbxBody.cs index acd255498..dd1c29615 100644 --- a/Src/GBX.NET/Components/GbxBody.cs +++ b/Src/GBX.NET/Components/GbxBody.cs @@ -1,4 +1,7 @@ using System.Collections.Immutable; +#if NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif using System.Text; namespace GBX.NET.Components; @@ -94,6 +97,9 @@ internal void Write(GbxWriter writer, MemoryStream uncompressedInputStream, GbxC new GbxBodyWriter(this, writer).Write(uncompressedInputStream, compressionOfBody); } +#if NET8_0_OR_GREATER + [Experimental("GBXNET10001")] +#endif public GbxBody DeepClone() => new() { RawData = RawData, diff --git a/Src/GBX.NET/Components/GbxHeaderOfT.cs b/Src/GBX.NET/Components/GbxHeaderOfT.cs index 31fc233cb..3549c2588 100644 --- a/Src/GBX.NET/Components/GbxHeaderOfT.cs +++ b/Src/GBX.NET/Components/GbxHeaderOfT.cs @@ -1,4 +1,7 @@ using GBX.NET.Managers; +#if NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif namespace GBX.NET.Components; @@ -15,6 +18,9 @@ public sealed class GbxHeader(GbxHeaderBasic basic) : GbxHeader(basic) where classId ??= ClassManager.GetId() ?? throw new Exception("Class ID not available"); #endif +#if NET8_0_OR_GREATER + [Experimental("GBXNET10001")] +#endif #if NETSTANDARD2_0 public override GbxHeader DeepClone() => new GbxHeader(Basic); #else diff --git a/Src/GBX.NET/Components/GbxHeaderUnknown.cs b/Src/GBX.NET/Components/GbxHeaderUnknown.cs index 487e8de02..8515f8a24 100644 --- a/Src/GBX.NET/Components/GbxHeaderUnknown.cs +++ b/Src/GBX.NET/Components/GbxHeaderUnknown.cs @@ -1,4 +1,7 @@ using GBX.NET.Managers; +#if NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif namespace GBX.NET.Components; @@ -12,6 +15,9 @@ public override string ToString() return $"GbxHeader ({ClassManager.GetName(ClassId)}, 0x{ClassId:X8}, unknown)"; } +#if NET8_0_OR_GREATER + [Experimental("GBXNET10001")] +#endif #if NETSTANDARD2_0 public override GbxHeader DeepClone() #else diff --git a/Src/GBX.NET/Components/GbxRefTable.cs b/Src/GBX.NET/Components/GbxRefTable.cs index 4cae30a80..19ccb017b 100644 --- a/Src/GBX.NET/Components/GbxRefTable.cs +++ b/Src/GBX.NET/Components/GbxRefTable.cs @@ -1,4 +1,7 @@ using Microsoft.Extensions.Logging; +#if NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif namespace GBX.NET.Components; @@ -16,10 +19,10 @@ public sealed class GbxRefTable /// public IReadOnlyCollection Resources { get; internal set; } = []; - public Dictionary ExternalData { get; } = []; + public Dictionary> ExternalNodes { get; set; } = []; /// - /// Directory path of the Gbx file used to resolve external files via the file system. File system is not used if this is null, instead, is used. + /// Directory path of the Gbx file used to resolve external files via the file system. File system is not used if this is null, instead, is used. /// public string? FileSystemPath { get; init; } @@ -38,6 +41,7 @@ private string GetFilePath(string filePath) public string GetFullFilePath(GbxRefTableFile file) => Path.GetFullPath(GetFilePath(file)); public string GetFullFilePath(UnlinkedGbxRefTableFile file) => Path.GetFullPath(GetFilePath(file)); + // TODO: should also have async variant in the future public CMwNod? LoadNode(GbxRefTableFile file, GbxReadSettings settings = default, bool exceptions = false) { if (file is null) @@ -88,37 +92,9 @@ private string GetFilePath(string filePath) } } - if (ExternalData.TryGetValue(file, out var data)) + if (ExternalNodes?.TryGetValue(file.FilePath, out var nodFunc) == true) { - using var ms = new MemoryStream(data); - - var scope = logger?.BeginScope("External [{Length} bytes]", data.Length); - - try - { - var gbx = Gbx.Parse(ms, settings); - - scope?.Dispose(); - - if (gbx.Node is null) - { - logger?.LogWarning("Failed to load node from External [{Length} bytes] ({Gbx})", data.Length, gbx); - } - - return gbx.Node; - } - catch (Exception ex) - { - scope?.Dispose(); - logger?.LogError(ex, "Failed to load node from file: {Length} bytes", data.Length); - - if (exceptions) - { - throw; - } - - return default; - } + return nodFunc(); } return default; @@ -139,6 +115,9 @@ internal bool Write(GbxWriter writer, GbxHeader header, bool rawBody) return new GbxRefTableWriter(this, header, writer).Write(rawBody); } +#if NET8_0_OR_GREATER + [Experimental("GBXNET10001")] +#endif public GbxRefTable DeepClone() { var refTable = new GbxRefTable @@ -149,11 +128,9 @@ public GbxRefTable DeepClone() FileSystemPath = FileSystemPath }; - foreach (var pair in ExternalData) + foreach (var pair in ExternalNodes) { - // TODO: Deep clone file or not? - var file = new GbxRefTableFile(refTable, pair.Key.Flags, pair.Key.UseFile, pair.Key.FilePath); - refTable.ExternalData.Add(file, pair.Value.ToArray()); + refTable.ExternalNodes.Add(pair.Key, pair.Value); } return refTable; diff --git a/Src/GBX.NET/Engines/Function/CFuncLight.chunkl b/Src/GBX.NET/Engines/Function/CFuncLight.chunkl index e65178a82..890e9a6eb 100644 --- a/Src/GBX.NET/Engines/Function/CFuncLight.chunkl +++ b/Src/GBX.NET/Engines/Function/CFuncLight.chunkl @@ -1,3 +1,12 @@ CFuncLight 0x05018000 - inherits: CFuncPlug -- abstract \ No newline at end of file +- abstract + +0x000 + int FctType + float FlickPeriod + int FlickCount + +enum EFctType + Sinus + Flick \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Function/CFuncLightColor.chunkl b/Src/GBX.NET/Engines/Function/CFuncLightColor.chunkl new file mode 100644 index 000000000..0004c0dc8 --- /dev/null +++ b/Src/GBX.NET/Engines/Function/CFuncLightColor.chunkl @@ -0,0 +1,10 @@ +CFuncLightColor 0x05019000 +- inherits: CFuncLight + +0x001 + vec4 Color0 + vec4 Color1 + +0x002 (base: 0x001) + base + CPlugFileImg Image (external) \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Function/CFuncShaderLayerUV.chunkl b/Src/GBX.NET/Engines/Function/CFuncShaderLayerUV.chunkl index 17f978898..8478d4118 100644 --- a/Src/GBX.NET/Engines/Function/CFuncShaderLayerUV.chunkl +++ b/Src/GBX.NET/Engines/Function/CFuncShaderLayerUV.chunkl @@ -18,13 +18,22 @@ CFuncShaderLayerUV 0x05015000 vec2 vec2 +0x011 + int + int + bool + bool + bool + CMwNod + 0x012 int int +0x013 (base: 0x00A) + 0x014 - float - float + vec2 float float diff --git a/Src/GBX.NET/Engines/Function/CFuncTreeTranslate.chunkl b/Src/GBX.NET/Engines/Function/CFuncTreeTranslate.chunkl new file mode 100644 index 000000000..4f74e606b --- /dev/null +++ b/Src/GBX.NET/Engines/Function/CFuncTreeTranslate.chunkl @@ -0,0 +1,9 @@ +CFuncTreeTranslate 0x0500D000 +- inherits: CFuncTree + +0x000 + vec3 StartPoint + vec3 EndPoint + +0x001 + uint \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Game/CGameControlCamera.chunkl b/Src/GBX.NET/Engines/Game/CGameControlCamera.chunkl new file mode 100644 index 000000000..9d1c662d5 --- /dev/null +++ b/Src/GBX.NET/Engines/Game/CGameControlCamera.chunkl @@ -0,0 +1,82 @@ +CGameControlCamera 0x0306B000 +- inherits: CSceneController + +0x001 + vec3 RelativeTargetPos + bool IsFirstPerson + bool CanCameraMove + bool IsFollowing + float MaxSpeed + float PlaneDist + float MinDist + float MaxDist + float Fov + float DefaultFov + bool UseForcedLocation + iso4 ForcedLocation + +0x002 + bool UseOnlyFollowedMobilPosition + +0x003 + id Name + +0x004 + string Name + +0x009 + vec3 RelativeFollowedPos + bool IsFirstPerson + bool CanCameraMove + bool IsFollowing + float MaxSpeed + float PlaneDist + float MinDist + float MaxDist + float Fov + float DefaultFov + bool UseForcedLocation + iso4 ForcedLocation + bool UseOnlyFollowedMobilPosition + id Name + string Name + float MinFov + float MaxFov + bool UseForcedUp + vec3 ForcedUp + float DefaultNearZ + float MinNearZ + float MaxNearZ + float DefaultFarZ + float MinFarZ + float MaxFarZ + bool + +0x00A + id Name + +0x00B + vec3 RelativeFollowedPos + bool IsFirstPerson + bool CanCameraMove + bool IsFollowing + float MaxSpeed + float PlaneDist + float MinDist + float MaxDist + float Fov + float DefaultFov + bool UseForcedLocation + iso4 ForcedLocation + bool UseOnlyFollowedMobilPosition + float MinFov + float MaxFov + bool UseForcedUp + vec3 ForcedUp + float DefaultNearZ + float MinNearZ + float MaxNearZ + float DefaultFarZ + float MinFarZ + float MaxFarZ + bool \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Game/CGameControlCameraOrbital3d.chunkl b/Src/GBX.NET/Engines/Game/CGameControlCameraOrbital3d.chunkl new file mode 100644 index 000000000..7605f695e --- /dev/null +++ b/Src/GBX.NET/Engines/Game/CGameControlCameraOrbital3d.chunkl @@ -0,0 +1,56 @@ +CGameControlCameraOrbital3d 0x0306E000 +- inherits: CGameControlCameraTarget + +0x001 + vec3 RadiusScale + vec2 RotateSpeed + bool OcclusionIsEnable + bool + bool + float MouseBorderMoveSize + float + float + float OcclusionTargetRadius + float OcclusionDistFromHit + float Radius + float Latitude + float Longitude + float RadiusMin + float RadiusMax + float LatitudeMin + float LatitudeMax + +0x002 + float + +0x003 + float WheelSensitivity + +0x004 + float FovKeySensitivity + +0x005 + vec3 RadiusScale + vec2 RotateSpeed + bool OcclusionIsEnable + bool + bool + float MouseBorderMoveSize + float OcclusionTargetRadius + float OcclusionDistFromHit + float Radius + float Latitude + float Longitude + float RadiusMin + float RadiusMax + float LatitudeMin + float LatitudeMax + float + float WheelSensitivity + float FovKeySensitivity + +0x006 + float ZoomKeySensitivity + +0x007 + float DefaultRadius \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Game/CGameControlCameraTarget.chunkl b/Src/GBX.NET/Engines/Game/CGameControlCameraTarget.chunkl new file mode 100644 index 000000000..079950c6c --- /dev/null +++ b/Src/GBX.NET/Engines/Game/CGameControlCameraTarget.chunkl @@ -0,0 +1,11 @@ +CGameControlCameraTarget 0x03072000 +- inherits: CGameControlCamera + +0x001 + iso4 Location + bool IsUpLinked + bool Interpolate + float LookAtFactor + +0x002 + bool CanUseRelativeTargetLocation \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Game/CGameCtnBlockInfo.chunkl b/Src/GBX.NET/Engines/Game/CGameCtnBlockInfo.chunkl index 2ff0a10dc..3612cb610 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnBlockInfo.chunkl +++ b/Src/GBX.NET/Engines/Game/CGameCtnBlockInfo.chunkl @@ -122,4 +122,9 @@ enum EMultiDir OpposedDirOnly PerpendicularDirsOnly NextDirOnly - PreviousDirOnly \ No newline at end of file + PreviousDirOnly + +enum ESelection + Random + Obsolete + AutoRotate \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Game/CGameCtnBlockInfo.cs b/Src/GBX.NET/Engines/Game/CGameCtnBlockInfo.cs index da2bec6e5..9d0a0f600 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnBlockInfo.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnBlockInfo.cs @@ -4,10 +4,15 @@ namespace GBX.NET.Engines.Game; public partial class CGameCtnBlockInfo { + public ESelection Selection { get; set; } + private CGameCtnBlockInfoClassic? pillar; + [AppliedWithChunk] + public CGameCtnBlockInfoClassic? Pillar { get => pillarFile?.GetNode(ref pillar) ?? pillar; set => pillar = value; } private GbxRefTableFile? pillarFile; - public CGameCtnBlockInfoClassic? Pillar { get => pillar; set => pillar = value; } - + public GbxRefTableFile? PillarFile { get => pillarFile; set => pillarFile = value; } + public CGameCtnBlockInfoClassic? GetPillar(GbxReadSettings settings = default, bool exceptions = false) => pillarFile?.GetNode(ref pillar, settings, exceptions) ?? pillar; + private CGameCtnBlockUnitInfo[]? groundBlockUnitInfos; public CGameCtnBlockUnitInfo[]? GroundBlockUnitInfos { get => groundBlockUnitInfos; set => groundBlockUnitInfos = value; } @@ -38,7 +43,6 @@ public partial class Chunk0304E005 public int U02; public int U03; public int U04; - public int U05; public int U06; public byte U07; public int U08; @@ -53,7 +57,7 @@ public override void Read(CGameCtnBlockInfo n, GbxReader r) U03 = r.ReadInt32(); // always 0? U04 = r.ReadInt32(); // always 0? n.isPillar = r.ReadBoolean(); - U05 = r.ReadInt32(); // always 0? + n.Selection = (ESelection)r.ReadInt32(); U06 = r.ReadInt32(); // always 0? // @@ -88,7 +92,7 @@ public override void Write(CGameCtnBlockInfo n, GbxWriter w) w.Write(U03); w.Write(U04); w.Write(n.isPillar); - w.Write(U05); + w.Write((int)n.Selection); w.Write(U06); // diff --git a/Src/GBX.NET/Engines/Game/CGameCtnChallenge.cs b/Src/GBX.NET/Engines/Game/CGameCtnChallenge.cs index fcf08713b..e68f2a7f4 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnChallenge.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnChallenge.cs @@ -276,6 +276,7 @@ public UInt128? HashedPassword private bool hasLightmaps; [AppliedWithChunk] + [AppliedWithChunk] public bool HasLightmaps { get => hasLightmaps; set => hasLightmaps = value; } [AppliedWithChunk(sinceVersion: 9)] diff --git a/Src/GBX.NET/Engines/Game/CGameCtnCollection.chunkl b/Src/GBX.NET/Engines/Game/CGameCtnCollection.chunkl index bbd10d461..9151dd644 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnCollection.chunkl +++ b/Src/GBX.NET/Engines/Game/CGameCtnCollection.chunkl @@ -1,10 +1,10 @@ CGameCtnCollection 0x03033000 -0x000 // old header +0x000 (header, struct: SOldHeaderDesc) // old header id -0x001 // desc - versionb version +0x001 (header, struct: SHeaderDesc) // desc + versionb id Collection bool NeedUnlock v1+ @@ -36,6 +36,25 @@ CGameCtnCollection 0x03033000 v10+ bool? IsEditable +0x002 (header, struct: SHeaderCollectorFolders) + versionb + string FolderBlockInfo + string FolderItem + string FolderDecoration + v1+ + v2- + string + v2+ + string FolderCardEventInfo + v3+ + string FolderMacroBlockInfo + v4+ + string FolderMacroDecals + +0x003 (header, struct: SHeaderMenuIconsFolders) + versionb + string FolderMenusIcons + 0x008 // DefaultDecoration CGameCtnDecoration DefaultDecoration (external) @@ -64,7 +83,7 @@ CGameCtnCollection 0x03033000 CPlugBitmap CollectionIconFid (external) 0x00E - int + int SortIndex 0x011 int @@ -84,7 +103,7 @@ CGameCtnCollection 0x03033000 float ColorVertexMax 0x018 - int + CMwNod rect vec2 vec2 @@ -119,8 +138,8 @@ CGameCtnCollection 0x03033000 float CameraMinHeight bool -0x01F - int[] // ParticleEmitterModelsFids? +0x01F // ParticleEmitterModelsFids + int[] ParticleEmitterModelsFids 0x020 // folders string FolderBlockInfo @@ -134,8 +153,8 @@ CGameCtnCollection 0x03033000 0x022 // is water multi-height bool IsWaterMultiHeight -0x023 - bool +0x023 // CarCanBeDirty + bool? CarCanBeDirty 0x024 int @@ -215,13 +234,13 @@ CGameCtnCollection 0x03033000 float WaterGRefracPertub v7+ int // not seen in code - float CameraMinHeight - v3- - bool // CSystemFidsFolder something? - bool IsWaterMultiHeight - v2- - float - float WaterFogClampAboveDist + float CameraMinHeight + v3- + bool // CSystemFidsFolder something? + bool IsWaterMultiHeight + v2- + float + float WaterFogClampAboveDist 0x039 version @@ -230,7 +249,7 @@ CGameCtnCollection 0x03033000 float? float? v1+ - int? + int? // VehicleStyles? v2+ CMwNod ItemPlacementGroups v3+ @@ -318,6 +337,6 @@ archive Water v3+ float? v2+ - int? // NodeRef? + CMwNod v7+ int? // not seen in code diff --git a/Src/GBX.NET/Engines/Game/CGameCtnDecoration.chunkl b/Src/GBX.NET/Engines/Game/CGameCtnDecoration.chunkl index edf6f2a4b..c14505038 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnDecoration.chunkl +++ b/Src/GBX.NET/Engines/Game/CGameCtnDecoration.chunkl @@ -1,9 +1,11 @@ CGameCtnDecoration 0x03038000 - inherits: CGameCtnCollector -0x001 // LightMap +0x000 (header, struct: SMoodRemaping) + +0x001 (header, struct: SLightMap) // LightMap versionb version - int + int // CHmsLightMap Tech 0x011 // DecoSize CGameCtnDecorationSize DecoSize (external) diff --git a/Src/GBX.NET/Engines/Game/CGameCtnDecoration.cs b/Src/GBX.NET/Engines/Game/CGameCtnDecoration.cs index f953c8f1b..e038fc747 100644 --- a/Src/GBX.NET/Engines/Game/CGameCtnDecoration.cs +++ b/Src/GBX.NET/Engines/Game/CGameCtnDecoration.cs @@ -2,6 +2,44 @@ public partial class CGameCtnDecoration { + /// + /// Refers from . + /// + public string RemapFolder { get; set; } = ""; + + /// + /// Refers from . + /// + public CPlugGameSkin? Remapping { get; set; } + + public partial class HeaderChunk03038000 + { + public byte U01; + + public override void ReadWrite(CGameCtnDecoration n, GbxReaderWriter rw) + { + rw.Byte(ref U01); + n.RemapFolder = rw.String(n.RemapFolder); + + if (rw.Reader is not null) + { + n.Remapping = new CPlugGameSkin(); + var headerChunk = new CPlugGameSkin.HeaderChunk090F4000 { Node = n.Remapping }; + n.Remapping.Chunks.Add(headerChunk); + headerChunk.ReadWrite(n.Remapping, rw); + } + + if (rw.Writer is not null) + { + var remapping = n.Remapping ?? new(); + (remapping.Chunks + .OfType() + .FirstOrDefault() ?? new CPlugGameSkin.HeaderChunk090F4000()) + .ReadWrite(remapping, rw); + } + } + } + public override IHeaderChunk? CreateHeaderChunk(uint chunkId) { if (chunkId == 0x090F4000) diff --git a/Src/GBX.NET/Engines/GameData/CGameCtnCollector.chunkl b/Src/GBX.NET/Engines/GameData/CGameCtnCollector.chunkl index b853c68af..14a640c38 100644 --- a/Src/GBX.NET/Engines/GameData/CGameCtnCollector.chunkl +++ b/Src/GBX.NET/Engines/GameData/CGameCtnCollector.chunkl @@ -7,7 +7,7 @@ CGameCtnCollector 0x2E001000 // Collector. Something that can have an icon. v5= id v4+ - id + id ParentCollectorId v3+ int Flags short CatalogPosition @@ -32,7 +32,7 @@ CGameCtnCollector 0x2E001000 // Collector. Something that can have an icon. int 0x007 [TM10, TMSX, TMF] - bool + bool IsInternal bool int CatalogPosition int diff --git a/Src/GBX.NET/Engines/GameData/CGameCtnCollector.cs b/Src/GBX.NET/Engines/GameData/CGameCtnCollector.cs index f612f4f1f..825f250ea 100644 --- a/Src/GBX.NET/Engines/GameData/CGameCtnCollector.cs +++ b/Src/GBX.NET/Engines/GameData/CGameCtnCollector.cs @@ -112,8 +112,6 @@ public override void Write(CGameCtnCollector n, GbxWriter w) public partial class Chunk2E001009 { - public string? U01; - public override void ReadWrite(CGameCtnCollector n, GbxReaderWriter rw) { rw.String(ref n.pageName); @@ -123,7 +121,7 @@ public override void ReadWrite(CGameCtnCollector n, GbxReaderWriter rw) rw.NodeRef(ref n.iconFid, ref n.iconFidFile); } - rw.Id(ref U01); + rw.Id(ref n.parentCollectorId); } } diff --git a/Src/GBX.NET/Engines/GameData/CGameItemModel.chunkl b/Src/GBX.NET/Engines/GameData/CGameItemModel.chunkl index 0e3217165..8786fd064 100644 --- a/Src/GBX.NET/Engines/GameData/CGameItemModel.chunkl +++ b/Src/GBX.NET/Engines/GameData/CGameItemModel.chunkl @@ -7,23 +7,64 @@ CGameItemModel 0x2E002000 0x001 (header, struct: SHeaderFileVersion) // file version version -0x006 - int +0x000 + CSceneMobil Vehicle (external) -0x008 // Nadeo skin fids - CMwNod?[] NadeoSkinFids +0x002 // old race interface fid + CScene2d RaceInterfaceFid -0x009 // cameras - CMwNod[]_deprec Cameras +0x003 // LowQualitySolid + CPlugSolid LowQualitySolid (external) -0x00A +0x004 CMwNod -0x00C // race interface fid - CMwNod RaceInterfaceFid +0x006 // DefaultCamIndex + int DefaultCamIndex -0x010 - CMwNod +0x007 // old materials + CPlugMaterial MaterialSkin (external) + CPlugMaterial MaterialGlass (external) + CPlugMaterial MaterialDetails (external) + +0x008 // Nadeo skin fids + CMwNod?[] NadeoSkinFids (external) + +0x009 // Cameras + CMwNod[]_deprec Cameras (external) + +0x00A // DecoratorSolid + CPlugDecoratorSolid DecoratorSolid (external) + +0x00B // stem materials + CPlugMaterial StemMaterial (external) + CPlugMaterial StemBumpMaterial (external) + +0x00C // race interface fid + CScene2d RaceInterfaceFid + +0x00D (ForcedSkinsFids) + CMwNod[] ForcedSkinsFids + +0x00E // StadiumCar materials + CPlugMaterial MaterialSkin (external) + CPlugMaterial MaterialGlass (external) + CPlugMaterial MaterialDetails (external) + CPlugMaterial MaterialPilot (external) + +0x010 (BannerProfileFid) + CPlugBitmap BannerProfileFid (external) + +0x011 // materials + CPlugMaterial MaterialSkin (external) + CPlugMaterial MaterialGlass (external) + CPlugMaterial MaterialDetails (external) + CPlugMaterial MaterialPilot (external) + CPlugLight FrontLight (external) + CPlugLight FrontLightSmall (external) + CPlugLight RearLight (external) + CPlugLight ProjShadow (external) + CPlugLight ProjFront (external) 0x012 vec3 GroundPoint @@ -32,13 +73,13 @@ CGameItemModel 0x2E002000 float OrbitalRadiusBase float OrbitalPreviewAngle -0x013 - CPlugAudioEnvironment +0x013 (AudioEnvironmentInCar) + CPlugAudioEnvironment AudioEnvironmentInCar (external) 0x014 CMwNod -0x015 // item type e +0x015 // ItemTypeE int ItemTypeE 0x019 // model diff --git a/Src/GBX.NET/Engines/Graphic/GxFog.chunkl b/Src/GBX.NET/Engines/Graphic/GxFog.chunkl index ffb0d6a25..612f689a6 100644 --- a/Src/GBX.NET/Engines/Graphic/GxFog.chunkl +++ b/Src/GBX.NET/Engines/Graphic/GxFog.chunkl @@ -1 +1,14 @@ -GxFog 0x04004000 \ No newline at end of file +GxFog 0x04004000 + +0x000 + bool + int + float + float + float + float + float + float + float + float + float \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Graphic/GxLightAmbient.chunkl b/Src/GBX.NET/Engines/Graphic/GxLightAmbient.chunkl new file mode 100644 index 000000000..7522fa4b0 --- /dev/null +++ b/Src/GBX.NET/Engines/Graphic/GxLightAmbient.chunkl @@ -0,0 +1,6 @@ +GxLightAmbient 0x04005000 +- inherits: GxLight + +0x000 + float ShadeMinY + float ShadeMaxY \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Graphic/GxLightDirectional.chunkl b/Src/GBX.NET/Engines/Graphic/GxLightDirectional.chunkl new file mode 100644 index 000000000..6b1e3aad1 --- /dev/null +++ b/Src/GBX.NET/Engines/Graphic/GxLightDirectional.chunkl @@ -0,0 +1,22 @@ +GxLightDirectional 0x04007000 +- inherits: GxLightNotAmbient + +0x001 + bool UseBoundaryHint + vec3 BoundaryHintPos + +0x002 (base: 0x001) + base + float DazzleAngleMax + float DazzleIntensity + +0x003 + vec3 DblSidedRGB + +0x004 + vec3 ReverseRGB + float ReverseIntens + +0x005 + float EmittAngularSize + float FlareAngularSize \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Hms/CHmsAmbientOcc.chunkl b/Src/GBX.NET/Engines/Hms/CHmsAmbientOcc.chunkl index 3959dd683..da22c8c6f 100644 --- a/Src/GBX.NET/Engines/Hms/CHmsAmbientOcc.chunkl +++ b/Src/GBX.NET/Engines/Hms/CHmsAmbientOcc.chunkl @@ -1 +1,9 @@ -CHmsAmbientOcc 0x06026000 \ No newline at end of file +CHmsAmbientOcc 0x06026000 + +0x000 + float + float + int + float + float + float \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Hms/CHmsFogPlane.chunkl b/Src/GBX.NET/Engines/Hms/CHmsFogPlane.chunkl new file mode 100644 index 000000000..b7004dc40 --- /dev/null +++ b/Src/GBX.NET/Engines/Hms/CHmsFogPlane.chunkl @@ -0,0 +1 @@ +CHmsFogPlane 0x06017000 \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Hms/CHmsLight.chunkl b/Src/GBX.NET/Engines/Hms/CHmsLight.chunkl new file mode 100644 index 000000000..955ba9b66 --- /dev/null +++ b/Src/GBX.NET/Engines/Hms/CHmsLight.chunkl @@ -0,0 +1,17 @@ +CHmsLight 0x0600C000 +- inherits: CHmsPocEmitter + +0x000 + int + GxLight MainGxLight + +0x001 (base: 0x000) + base + +0x002 (base: 0x001) + base + CPlugBitmap BitmapFlare (external) + +0x003 (base: 0x002) + base + CPlugBitmap BitmapSprite (external) \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Hms/CHmsPocEmitter.chunkl b/Src/GBX.NET/Engines/Hms/CHmsPocEmitter.chunkl new file mode 100644 index 000000000..ee7cf65b4 --- /dev/null +++ b/Src/GBX.NET/Engines/Hms/CHmsPocEmitter.chunkl @@ -0,0 +1,2 @@ +CHmsPocEmitter 0x0600B000 +- inherits: CHmsPoc \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Hms/CHmsPrecalcRender.chunkl b/Src/GBX.NET/Engines/Hms/CHmsPrecalcRender.chunkl new file mode 100644 index 000000000..abeb943fe --- /dev/null +++ b/Src/GBX.NET/Engines/Hms/CHmsPrecalcRender.chunkl @@ -0,0 +1 @@ +CHmsPrecalcRender 0x06011000 \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Hms/CHmsZone.chunkl b/Src/GBX.NET/Engines/Hms/CHmsZone.chunkl new file mode 100644 index 000000000..27552552e --- /dev/null +++ b/Src/GBX.NET/Engines/Hms/CHmsZone.chunkl @@ -0,0 +1,17 @@ +CHmsZone 0x06004000 + +0x002 + CHmsFogPlane[]_deprec FogPlanes + data[28] + +0x003 + bool U01 + if U01 + vec3 MRPoint + vec3 MRNormal + +0x005 + CHmsPrecalcRender[]_deprec PrecalcRenders + +0x006 + CPlugBitmap BitmapCubeReflectHardSpecA (external) \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Motion/CMotionTrackMobilPitchin.chunkl b/Src/GBX.NET/Engines/Motion/CMotionTrackMobilPitchin.chunkl new file mode 100644 index 000000000..bff62c795 --- /dev/null +++ b/Src/GBX.NET/Engines/Motion/CMotionTrackMobilPitchin.chunkl @@ -0,0 +1,12 @@ +CMotionTrackMobilPitchin 0x08041000 +- inherits: CMotionTrack + +0x002 + CMwNod + float + float + float + float + int + float + float \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Motion/CMotionWeather.chunkl b/Src/GBX.NET/Engines/Motion/CMotionWeather.chunkl new file mode 100644 index 000000000..30537b4de --- /dev/null +++ b/Src/GBX.NET/Engines/Motion/CMotionWeather.chunkl @@ -0,0 +1,2 @@ +CMotionWeather 0x08054000 +- inherits: CMotionManaged \ No newline at end of file diff --git a/Src/GBX.NET/Engines/MwFoundations/CMwNod.cs b/Src/GBX.NET/Engines/MwFoundations/CMwNod.cs index 46a335137..c398f4362 100644 --- a/Src/GBX.NET/Engines/MwFoundations/CMwNod.cs +++ b/Src/GBX.NET/Engines/MwFoundations/CMwNod.cs @@ -3,6 +3,9 @@ using Microsoft.Extensions.Logging; using System.Diagnostics; using System.Text; +#if NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif namespace GBX.NET.Engines.MwFoundations; @@ -642,6 +645,9 @@ public virtual void ReadWrite(GbxReaderWriter rw) return chunk; } +#if NET8_0_OR_GREATER + [Experimental("GBXNET10001")] +#endif public virtual CMwNod DeepClone() { var clone = (CMwNod)MemberwiseClone(); @@ -649,6 +655,9 @@ public virtual CMwNod DeepClone() return clone; } +#if NET8_0_OR_GREATER + [Experimental("GBXNET10001")] +#endif IClass IClass.DeepClone() { var clone = (IClass)MemberwiseClone(); @@ -656,6 +665,9 @@ IClass IClass.DeepClone() return clone; } +#if NET8_0_OR_GREATER + [Experimental("GBXNET10001")] +#endif protected void DeepCloneChunks(IClass dest) { if (chunks is null) diff --git a/Src/GBX.NET/Engines/Plug/CPlugAudioEnvironment.chunkl b/Src/GBX.NET/Engines/Plug/CPlugAudioEnvironment.chunkl index 19f5e9d62..25d8413b3 100644 --- a/Src/GBX.NET/Engines/Plug/CPlugAudioEnvironment.chunkl +++ b/Src/GBX.NET/Engines/Plug/CPlugAudioEnvironment.chunkl @@ -1,5 +1,36 @@ CPlugAudioEnvironment 0x09039000 - inherits: CPlugAudio +0x000 + int + float + float + float + float + float + float + float + float + float + float + float + float + float + float + float + float + float + float + float + float + bool + bool + bool + bool + bool + bool + bool + bool + 0x001 float \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Plug/CPlugBitmap.chunkl b/Src/GBX.NET/Engines/Plug/CPlugBitmap.chunkl index bc39b9df1..027c6fa4d 100644 --- a/Src/GBX.NET/Engines/Plug/CPlugBitmap.chunkl +++ b/Src/GBX.NET/Engines/Plug/CPlugBitmap.chunkl @@ -6,7 +6,7 @@ CPlugBitmap 0x09011000 float MipMapLowerAlpha float BumpScaleFactor float MipMapLodBiasDefault - int + int BorderRGB int 0x017 @@ -19,7 +19,7 @@ CPlugBitmap 0x09011000 float MipMapLowerAlpha float BumpScaleFactor float MipMapLodBiasDefault - int + int BorderRGB if Image != null CMwNod @@ -36,7 +36,7 @@ CPlugBitmap 0x09011000 int16 0x01E - Vec2[] + vec2[] AtlasCountUVs 0x01F uint diff --git a/Src/GBX.NET/Engines/Plug/CPlugClouds.chunkl b/Src/GBX.NET/Engines/Plug/CPlugClouds.chunkl index 49043229b..e02138c73 100644 --- a/Src/GBX.NET/Engines/Plug/CPlugClouds.chunkl +++ b/Src/GBX.NET/Engines/Plug/CPlugClouds.chunkl @@ -1,9 +1,9 @@ CPlugClouds 0x09180000 0x000 + CPlugSolid[] SolidFids (external) float BottomNearZ float BottomFarZ - float CPlugFileImg ImageColorMin (external) CPlugFileImg ImageColorMax (external) int Lighting diff --git a/Src/GBX.NET/Engines/Plug/CPlugFileGPU.chunkl b/Src/GBX.NET/Engines/Plug/CPlugFileGPU.chunkl new file mode 100644 index 000000000..9c2d0eae7 --- /dev/null +++ b/Src/GBX.NET/Engines/Plug/CPlugFileGPU.chunkl @@ -0,0 +1,3 @@ +CPlugFileGPU 0x09040000 +- inherits: CPlugFileText +- abstract \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Plug/CPlugFileGPUP.chunkl b/Src/GBX.NET/Engines/Plug/CPlugFileGPUP.chunkl new file mode 100644 index 000000000..0c704b283 --- /dev/null +++ b/Src/GBX.NET/Engines/Plug/CPlugFileGPUP.chunkl @@ -0,0 +1,3 @@ +CPlugFileGPUP 0x09076000 +- inherits: CPlugFileGPU +- abstract \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Plug/CPlugFileGPUV.chunkl b/Src/GBX.NET/Engines/Plug/CPlugFileGPUV.chunkl new file mode 100644 index 000000000..dff591676 --- /dev/null +++ b/Src/GBX.NET/Engines/Plug/CPlugFileGPUV.chunkl @@ -0,0 +1,3 @@ +CPlugFileGPUV 0x09075000 +- inherits: CPlugFileGPU +- abstract \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Plug/CPlugFileGen.chunkl b/Src/GBX.NET/Engines/Plug/CPlugFileGen.chunkl new file mode 100644 index 000000000..03602bc86 --- /dev/null +++ b/Src/GBX.NET/Engines/Plug/CPlugFileGen.chunkl @@ -0,0 +1,2 @@ +CPlugFileGen 0x0902F000 +- inherits: CPlugFileImg \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Plug/CPlugFilePsh.chunkl b/Src/GBX.NET/Engines/Plug/CPlugFilePsh.chunkl new file mode 100644 index 000000000..ab81b523b --- /dev/null +++ b/Src/GBX.NET/Engines/Plug/CPlugFilePsh.chunkl @@ -0,0 +1,2 @@ +CPlugFilePsh 0x09045000 +- inherits: CPlugFileGPUP \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Plug/CPlugFileText.chunkl b/Src/GBX.NET/Engines/Plug/CPlugFileText.chunkl new file mode 100644 index 000000000..e0988db42 --- /dev/null +++ b/Src/GBX.NET/Engines/Plug/CPlugFileText.chunkl @@ -0,0 +1,2 @@ +CPlugFileText 0x09041000 +- inherits: CPlugFile \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Plug/CPlugFileVHlsl.chunkl b/Src/GBX.NET/Engines/Plug/CPlugFileVHlsl.chunkl new file mode 100644 index 000000000..9af9f45a2 --- /dev/null +++ b/Src/GBX.NET/Engines/Plug/CPlugFileVHlsl.chunkl @@ -0,0 +1,2 @@ +CPlugFileVHlsl 0x09074000 +- inherits: CPlugFileGPUV \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Plug/CPlugGameSkin.chunkl b/Src/GBX.NET/Engines/Plug/CPlugGameSkin.chunkl index ea7ca9792..816051318 100644 --- a/Src/GBX.NET/Engines/Plug/CPlugGameSkin.chunkl +++ b/Src/GBX.NET/Engines/Plug/CPlugGameSkin.chunkl @@ -15,6 +15,8 @@ CPlugGameSkin 0x090F4000 string v7+ int + v8+ + byte 0x003 string diff --git a/Src/GBX.NET/Engines/Plug/CPlugMaterialCustom.chunkl b/Src/GBX.NET/Engines/Plug/CPlugMaterialCustom.chunkl index edc8aa1b6..32ad78393 100644 --- a/Src/GBX.NET/Engines/Plug/CPlugMaterialCustom.chunkl +++ b/Src/GBX.NET/Engines/Plug/CPlugMaterialCustom.chunkl @@ -63,9 +63,9 @@ CPlugMaterialCustom 0x0903A000 string archive Bitmap - id + id Name int - CMwNod (external) + CMwNod Texture (external) v1+ int int diff --git a/Src/GBX.NET/Engines/Plug/CPlugShaderApply.chunkl b/Src/GBX.NET/Engines/Plug/CPlugShaderApply.chunkl index eac6c8e64..7e4d6a0fe 100644 --- a/Src/GBX.NET/Engines/Plug/CPlugShaderApply.chunkl +++ b/Src/GBX.NET/Engines/Plug/CPlugShaderApply.chunkl @@ -25,6 +25,9 @@ CPlugShaderApply 0x09026000 0x006 int +0x007 + int + 0x008 int int diff --git a/Src/GBX.NET/Engines/Plug/CPlugSolid2Model.chunkl b/Src/GBX.NET/Engines/Plug/CPlugSolid2Model.chunkl index 46c183aa6..363b7d5d2 100644 --- a/Src/GBX.NET/Engines/Plug/CPlugSolid2Model.chunkl +++ b/Src/GBX.NET/Engines/Plug/CPlugSolid2Model.chunkl @@ -36,7 +36,7 @@ archive Light id U01 bool U02 if U02 - CPlugLight U03 + CPlugLight U03 (external) if !U02 string U04 iso4 U05 diff --git a/Src/GBX.NET/Engines/Plug/CPlugSound.chunkl b/Src/GBX.NET/Engines/Plug/CPlugSound.chunkl index 85c7f0e4f..4df110762 100644 --- a/Src/GBX.NET/Engines/Plug/CPlugSound.chunkl +++ b/Src/GBX.NET/Engines/Plug/CPlugSound.chunkl @@ -2,4 +2,52 @@ CPlugSound 0x0901A000 - inherits: CPlugAudio 0x000 - CMwNod (external) \ No newline at end of file + CMwNod PlugFile (external) // CPlugFileSnd + +0x002 + id + +0x009 + int Mode + float + bool IsLooping + bool IsContinuous + float Priority + +0x00B + int SoundKind + int InsideConeANgle + int OutsideConeAngle + float ConeOutsideAttenuation + float + +0x00C + float RefDistance + float MaxDistance + bool EnableDoppler + float + float + float + float + float DopplerFactor + float RolloffFactor + float RoomRolloffFactor + float AirAbsorptionFactor + +0x00D + int Mode + float + bool IsLooping + bool IsContinuous + float Priority + +enum ESoundKind + Point + Directional + +enum EMode + Direct + Direct_w__attenuation + Spatialised + Spacialised_Omni + ForceHard3d \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Plug/CPlugVehicleMaterial.chunkl b/Src/GBX.NET/Engines/Plug/CPlugVehicleMaterial.chunkl index d9491a464..fd14d59e9 100644 --- a/Src/GBX.NET/Engines/Plug/CPlugVehicleMaterial.chunkl +++ b/Src/GBX.NET/Engines/Plug/CPlugVehicleMaterial.chunkl @@ -1,5 +1,43 @@ CPlugVehicleMaterial 0x090F1000 +0x004 + CPlugBitmap (external) + vec2 + +0x005 + float + float + float + float + +0x009 + byte + float + float + bool + CMwNod (external) + CPlugParticleEmitterModel (external) + CPlugParticleEmitterModel (external) + CPlugParticleEmitterModel (external) + CPlugParticleEmitterModel (external) + +0x00A + CPlugParticleEmitterModel (external) + +0x00B + CPlugParticleEmitterModel (external) + +0x00C + CPlugParticleEmitterModel (external) + +0x00D + CPlugParticleEmitterModel (external) + +0x00E + byte + float + float + 0x00F float float diff --git a/Src/GBX.NET/Engines/Plug/CPlugVehicleMaterialGroup.chunkl b/Src/GBX.NET/Engines/Plug/CPlugVehicleMaterialGroup.chunkl new file mode 100644 index 000000000..120f31869 --- /dev/null +++ b/Src/GBX.NET/Engines/Plug/CPlugVehicleMaterialGroup.chunkl @@ -0,0 +1,4 @@ +CPlugVehicleMaterialGroup 0x090E9000 + +0x000 + int[] \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Plug/CPlugVehiclePhyTuning.chunkl b/Src/GBX.NET/Engines/Plug/CPlugVehiclePhyTuning.chunkl index 8e302726b..164e9140b 100644 --- a/Src/GBX.NET/Engines/Plug/CPlugVehiclePhyTuning.chunkl +++ b/Src/GBX.NET/Engines/Plug/CPlugVehiclePhyTuning.chunkl @@ -1,7 +1,7 @@ CPlugVehiclePhyTuning 0x090EB000 0x000 - id Name + id Name = empty CFuncKeysReal float diff --git a/Src/GBX.NET/Engines/Plug/CPlugVehiclePhyTuning.cs b/Src/GBX.NET/Engines/Plug/CPlugVehiclePhyTuning.cs new file mode 100644 index 000000000..2ca8ef5a3 --- /dev/null +++ b/Src/GBX.NET/Engines/Plug/CPlugVehiclePhyTuning.cs @@ -0,0 +1,31 @@ +using System.Text; + +namespace GBX.NET.Engines.Plug; + +public partial class CPlugVehiclePhyTuning +{ + public partial class Chunk090EB000 + { + public override void ReadWrite(CPlugVehiclePhyTuning n, GbxReaderWriter rw) + { + rw.Id(ref n.name); + rw.NodeRef(ref U01); + rw.Single(ref U02); + + if (rw.Reader?.Settings.EncryptionInitializer is not null) + { + if (n.name is null || n.name.Length < 4) + { + throw new InvalidOperationException("Name length must be at least 4 characters."); + } + + rw.Reader.Settings.EncryptionInitializer.Initialize(Encoding.ASCII.GetBytes(n.name.Substring(0, 4)), 0, 4); + } + } + } + + public override string ToString() + { + return Name; + } +} diff --git a/Src/GBX.NET/Engines/Plug/CPlugVehicleVisEmitterModel.chunkl b/Src/GBX.NET/Engines/Plug/CPlugVehicleVisEmitterModel.chunkl new file mode 100644 index 000000000..1e8ea673f --- /dev/null +++ b/Src/GBX.NET/Engines/Plug/CPlugVehicleVisEmitterModel.chunkl @@ -0,0 +1,42 @@ +CPlugVehicleVisEmitterModel 0x090E6000 + +0x002 + bool + +0x003 + float + float + float + float + float + float + +0x004 + int + CPlugParticleEmitterModel (external) + CPlugParticleEmitterModel (external) + CPlugParticleEmitterModel (external) + int + int + int + bool + bool + bool + iso4 + float + float + float + float + float + float + float + float + float + float + float + float + float + float + +0x005 + bool \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Plug/CPlugVehicleVisModelShared.chunkl b/Src/GBX.NET/Engines/Plug/CPlugVehicleVisModelShared.chunkl new file mode 100644 index 000000000..88bfbeb3d --- /dev/null +++ b/Src/GBX.NET/Engines/Plug/CPlugVehicleVisModelShared.chunkl @@ -0,0 +1,85 @@ +CPlugVehicleVisModelShared 0x090E8000 + +0x005 + SimulationWheel[] SimulationWheels + +0x006 + +0x009 + +0x00A + +0x00C + +0x00D + +0x00F + +0x010 + +0x012 + CPlugVehicleMaterialGroup[]_deprec VehicleMaterialGroups + +0x013 + CFuncKeysReal + CFuncKeysReal + CFuncKeysReal + +0x014 + CPlugVehicleVisEmitterModel[]_deprec VehicleEmitters + +archive SimulationWheel + bool + bool + id Name + +archive VisualArm + VisualId + VisualId + VisualId + bool + bool + int + +archive VisualLight + VisualId + bool + +archive VisualWheel + VisualId + VisualId + VisualId + VisualId + int + bool + +archive VisualId + id Name + bool + +archive Emitter + int + CPlugParticleEmitterModel EmitterModel (external) + int + int + int + bool + float + float + float + float + float + float + float + float + float + float + float + float + float + float + float + float + float + float + float \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Plug/CPlugVehicleVisModelShared.cs b/Src/GBX.NET/Engines/Plug/CPlugVehicleVisModelShared.cs new file mode 100644 index 000000000..0f061151d --- /dev/null +++ b/Src/GBX.NET/Engines/Plug/CPlugVehicleVisModelShared.cs @@ -0,0 +1,217 @@ +namespace GBX.NET.Engines.Plug; + +public partial class CPlugVehicleVisModelShared +{ + public VisualVehicle[] VisualVehicles { get; set; } = []; + + public partial class Chunk090E8006 + { + public override void Read(CPlugVehicleVisModelShared n, GbxReader r) + { + n.VisualVehicles = new VisualVehicle[r.ReadInt32()]; + } + + public override void Write(CPlugVehicleVisModelShared n, GbxWriter w) + { + w.Write(n.VisualVehicles.Length); + } + } + + public partial class Chunk090E8009 + { + public override void Read(CPlugVehicleVisModelShared n, GbxReader r) + { + for (int i = 0; i < n.VisualVehicles.Length; i++) + { + n.VisualVehicles[i] ??= new VisualVehicle(); + n.VisualVehicles[i].VisualArms = r.ReadArrayReadable(); + } + } + + public override void Write(CPlugVehicleVisModelShared n, GbxWriter w) + { + for (int i = 0; i < n.VisualVehicles.Length; i++) + { + w.WriteArrayWritable(n.VisualVehicles[i].VisualArms); + } + } + } + + public partial class Chunk090E800A + { + public override void Read(CPlugVehicleVisModelShared n, GbxReader r) + { + for (int i = 0; i < n.VisualVehicles.Length; i++) + { + n.VisualVehicles[i] ??= new VisualVehicle(); + n.VisualVehicles[i].VisualLights = r.ReadArrayReadable(); + } + } + + public override void Write(CPlugVehicleVisModelShared n, GbxWriter w) + { + for (int i = 0; i < n.VisualVehicles.Length; i++) + { + w.WriteArrayWritable(n.VisualVehicles[i].VisualLights); + } + } + } + + public partial class Chunk090E800C + { + public int[][]? U01; + + public override void Read(CPlugVehicleVisModelShared n, GbxReader r) + { + var count = r.ReadInt32(); + U01 = new int[count][]; + for (int i = 0; i < count; i++) + { + U01[i] = r.ReadArray(); + } + } + + public override void Write(CPlugVehicleVisModelShared n, GbxWriter w) + { + if (U01 is null) + { + w.Write(0); + return; + } + + w.Write(U01.Length); + for (int i = 0; i < U01.Length; i++) + { + w.WriteArray(U01[i]); + } + } + } + + public partial class Chunk090E800D + { + public override void Read(CPlugVehicleVisModelShared n, GbxReader r) + { + for (int i = 0; i < n.VisualVehicles.Length; i++) + { + n.VisualVehicles[i] ??= new VisualVehicle(); + n.VisualVehicles[i].Emitters = r.ReadArrayReadable(); + } + } + + public override void Write(CPlugVehicleVisModelShared n, GbxWriter w) + { + for (int i = 0; i < n.VisualVehicles.Length; i++) + { + w.WriteArrayWritable(n.VisualVehicles[i].Emitters); + } + } + } + + public partial class Chunk090E800F + { + public override void Read(CPlugVehicleVisModelShared n, GbxReader r) + { + for (int i = 0; i < n.VisualVehicles.Length; i++) + { + n.VisualVehicles[i] ??= new VisualVehicle(); + n.VisualVehicles[i].VisualWheels = r.ReadArrayReadable(); + } + } + + public override void Write(CPlugVehicleVisModelShared n, GbxWriter w) + { + for (int i = 0; i < n.VisualVehicles.Length; i++) + { + w.WriteArrayWritable(n.VisualVehicles[i].VisualWheels); + } + } + } + + public partial class Chunk090E8010 + { + public override void Read(CPlugVehicleVisModelShared n, GbxReader r) + { + for (int i = 0; i < n.VisualVehicles.Length; i++) + { + n.VisualVehicles[i] ??= new VisualVehicle(); + n.VisualVehicles[i].U01 = r.ReadReadable(); + n.VisualVehicles[i].U02 = r.ReadReadable(); + n.VisualVehicles[i].U03 = r.ReadReadable(); + n.VisualVehicles[i].U04 = r.ReadReadable(); + n.VisualVehicles[i].U05 = r.ReadInt32(); + } + } + + public override void Write(CPlugVehicleVisModelShared n, GbxWriter w) + { + for (int i = 0; i < n.VisualVehicles.Length; i++) + { + w.WriteWritable(n.VisualVehicles[i].U01); + w.WriteWritable(n.VisualVehicles[i].U02); + w.WriteWritable(n.VisualVehicles[i].U03); + w.WriteWritable(n.VisualVehicles[i].U04); + w.Write(n.VisualVehicles[i].U05); + } + } + } + + public partial class SimulationWheel + { + public override string ToString() + { + return $"{Name} (U01: {U01}, U02: {U01})"; + } + } + + [ArchiveGenerationOptions(StructureKind = StructureKind.SeparateReadAndWrite)] + public partial class VisualId + { + public override string ToString() + { + return $"{Name} (U01: {U01})"; + } + } + + [ArchiveGenerationOptions(StructureKind = StructureKind.SeparateReadAndWrite)] + public partial class VisualArm + { + public override string ToString() + { + return $"{U01}, {U02}, {U03} [U04: {U04}, U05: {U05}, U06: {U06}]"; + } + } + + [ArchiveGenerationOptions(StructureKind = StructureKind.SeparateReadAndWrite)] + public partial class VisualLight + { + public override string ToString() + { + return $"{U01} [U02: {U02}]"; + } + } + + [ArchiveGenerationOptions(StructureKind = StructureKind.SeparateReadAndWrite)] + public partial class VisualWheel + { + public override string ToString() + { + return $"{U01}, {U02}, {U03}, {U04} [U05: {U05}, U06: {U06}]"; + } + } + + [ArchiveGenerationOptions(StructureKind = StructureKind.SeparateReadAndWrite)] + public partial class Emitter; + + public sealed class VisualVehicle + { + public VisualArm[] VisualArms { get; set; } = []; + public VisualLight[] VisualLights { get; set; } = []; + public VisualWheel[] VisualWheels { get; set; } = []; + public Emitter[] Emitters { get; set; } = []; + public VisualId? U01 { get; set; } + public VisualId? U02 { get; set; } + public VisualId? U03 { get; set; } + public VisualId? U04 { get; set; } + public int U05 { get; set; } + } +} diff --git a/Src/GBX.NET/Engines/Scene/CScene.chunkl b/Src/GBX.NET/Engines/Scene/CScene.chunkl new file mode 100644 index 000000000..e59103ecd --- /dev/null +++ b/Src/GBX.NET/Engines/Scene/CScene.chunkl @@ -0,0 +1,18 @@ +CScene 0x0A001000 +- abstract + +0x003 + CSceneConfig SceneConfig + +0x004 + uint[] + +0x005 + CMotionManager[]_deprec MotionManagers (external) + +0x006 (ignore) + OldSceneMobil[] OldMobils + +archive OldSceneMobil + CMwNod (external) + CMwNod (external) \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Scene/CScene2d.chunkl b/Src/GBX.NET/Engines/Scene/CScene2d.chunkl new file mode 100644 index 000000000..71ee0b4aa --- /dev/null +++ b/Src/GBX.NET/Engines/Scene/CScene2d.chunkl @@ -0,0 +1,2 @@ +CScene2d 0x0A002000 +- inherits: CScene \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Scene/CSceneController.chunkl b/Src/GBX.NET/Engines/Scene/CSceneController.chunkl new file mode 100644 index 000000000..1e533d54a --- /dev/null +++ b/Src/GBX.NET/Engines/Scene/CSceneController.chunkl @@ -0,0 +1 @@ +CSceneController 0x0A00C000 \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Scene/CSceneFx.chunkl b/Src/GBX.NET/Engines/Scene/CSceneFx.chunkl new file mode 100644 index 000000000..3bd2f0371 --- /dev/null +++ b/Src/GBX.NET/Engines/Scene/CSceneFx.chunkl @@ -0,0 +1,2 @@ +CSceneFx 0x0A072000 +- abstract \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Scene/CSceneFxNod.chunkl b/Src/GBX.NET/Engines/Scene/CSceneFxNod.chunkl new file mode 100644 index 000000000..0a2877e46 --- /dev/null +++ b/Src/GBX.NET/Engines/Scene/CSceneFxNod.chunkl @@ -0,0 +1,8 @@ +CSceneFxNod 0x0A03A000 + +0x000 + CSceneFx Fx + +0x001 + CSceneFxNod[]_deprec NodInputs + int \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Scene/CSceneGate.chunkl b/Src/GBX.NET/Engines/Scene/CSceneGate.chunkl new file mode 100644 index 000000000..de3865311 --- /dev/null +++ b/Src/GBX.NET/Engines/Scene/CSceneGate.chunkl @@ -0,0 +1 @@ +CSceneGate 0x0A006000 \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Scene/CSceneLayout.chunkl b/Src/GBX.NET/Engines/Scene/CSceneLayout.chunkl index e40896e7e..839e1c7ab 100644 --- a/Src/GBX.NET/Engines/Scene/CSceneLayout.chunkl +++ b/Src/GBX.NET/Engines/Scene/CSceneLayout.chunkl @@ -1 +1,57 @@ CSceneLayout 0x0A003000 +- inherits: CScene + +0x00C + int + +0x010 + float CameraFarZ + vec3 CameraClearColor + +0x014 + int + bool + box + iso4 + +0x017 (ignore) + +0x018 + CSceneSector[]_deprec Sectors + version + SceneMobil[] Scene + SceneLoc[] SceneLocations + CSceneObject[]_deprec + SceneLoc[] + CSceneLight[]_deprec Lights (external) + SceneLoc[] LightLocations + CSceneObject[]_deprec Sounds + SceneLoc[] SoundLocations + CSceneObject[]_deprec Locations + SceneLoc[] LocationLocations + CSceneObject[]_deprec Fields + SceneLoc[] FieldLocations + CSceneGate[]_deprec Gates + CScenePath[]_deprec Paths + CSceneTrafficGraph TrafficGraph + CSceneTrafficPath[]_deprec TrafficPaths + CSceneFxNod SceneFxNod (external) + +archive SceneMobil + int U01 + if U01 == -2 + CMwNod Mobil + +archive SceneLoc + CMwNod + iso4 + +archive Unknown + CMwNod + id + int + CMwNod + id + int + iso4 + iso4 \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Scene/CSceneLight.chunkl b/Src/GBX.NET/Engines/Scene/CSceneLight.chunkl new file mode 100644 index 000000000..155c8a89f --- /dev/null +++ b/Src/GBX.NET/Engines/Scene/CSceneLight.chunkl @@ -0,0 +1,5 @@ +CSceneLight 0x0A00B000 +- inherits: CScenePoc + +0x000 + CHmsLight Light (direct) \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Scene/CScenePath.chunkl b/Src/GBX.NET/Engines/Scene/CScenePath.chunkl new file mode 100644 index 000000000..051ba3716 --- /dev/null +++ b/Src/GBX.NET/Engines/Scene/CScenePath.chunkl @@ -0,0 +1 @@ +CScenePath 0x0A008000 \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Scene/CSceneSector.chunkl b/Src/GBX.NET/Engines/Scene/CSceneSector.chunkl new file mode 100644 index 000000000..1c35f1f1c --- /dev/null +++ b/Src/GBX.NET/Engines/Scene/CSceneSector.chunkl @@ -0,0 +1,14 @@ +CSceneSector 0x0A004000 + +0x000 + int Scene + CHmsZone Zone + +0x001 + iso4 + +0x002 + id + +0x004 + box \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Scene/CSceneTrafficGraph.chunkl b/Src/GBX.NET/Engines/Scene/CSceneTrafficGraph.chunkl new file mode 100644 index 000000000..290efb43c --- /dev/null +++ b/Src/GBX.NET/Engines/Scene/CSceneTrafficGraph.chunkl @@ -0,0 +1,5 @@ +CSceneTrafficGraph 0x0A062000 + +0x004 + +0x005 \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Scene/CSceneTrafficGraph.cs b/Src/GBX.NET/Engines/Scene/CSceneTrafficGraph.cs new file mode 100644 index 000000000..26ab95741 --- /dev/null +++ b/Src/GBX.NET/Engines/Scene/CSceneTrafficGraph.cs @@ -0,0 +1,31 @@ +namespace GBX.NET.Engines.Scene; + +public partial class CSceneTrafficGraph +{ + public partial class Chunk0A062004 + { + public override void ReadWrite(CSceneTrafficGraph n, GbxReaderWriter rw) + { + var count1 = rw.Int32(); + var count2 = rw.Int32(); + + if (count1 > 0) + { + throw new Exception("CSceneTrafficGraph.Chunk0A062004: count1 > 0"); + } + + if (count2 > 0) + { + throw new Exception("CSceneTrafficGraph.Chunk0A062004: count2 > 0"); + } + } + } + + public partial class Chunk0A062005 + { + public override void ReadWrite(CSceneTrafficGraph n, GbxReaderWriter rw) + { + // for loop on count1 + } + } +} diff --git a/Src/GBX.NET/Engines/Scene/CSceneTrafficPath.chunkl b/Src/GBX.NET/Engines/Scene/CSceneTrafficPath.chunkl new file mode 100644 index 000000000..7ecbc0bb0 --- /dev/null +++ b/Src/GBX.NET/Engines/Scene/CSceneTrafficPath.chunkl @@ -0,0 +1 @@ +CSceneTrafficPath 0x0A063000 \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Scene/CSceneVehicle.chunkl b/Src/GBX.NET/Engines/Scene/CSceneVehicle.chunkl index bc17043c2..d148198fa 100644 --- a/Src/GBX.NET/Engines/Scene/CSceneVehicle.chunkl +++ b/Src/GBX.NET/Engines/Scene/CSceneVehicle.chunkl @@ -1,2 +1,9 @@ CSceneVehicle 0x0A060000 -- inherits: CSceneMobil \ No newline at end of file +- inherits: CSceneMobil + +0x000 + CPlugVehiclePhyTunings VehicleTunings (external) + CMwRefBuffer VehicleMaterials (external) + CSceneVehicleEnvironment Environment + CMwNod + CPlugVehicleVisModelShared VehicleStruct (external) \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Scene/CSceneVehicleCar.chunkl b/Src/GBX.NET/Engines/Scene/CSceneVehicleCar.chunkl index e7c79c472..ed12a745f 100644 --- a/Src/GBX.NET/Engines/Scene/CSceneVehicleCar.chunkl +++ b/Src/GBX.NET/Engines/Scene/CSceneVehicleCar.chunkl @@ -1,2 +1,7 @@ CSceneVehicleCar 0x0A02B000 -- inherits: CSceneVehicle \ No newline at end of file +- inherits: CSceneVehicle + +0x00C + float + float + box \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Scene/CSceneVehicleCarMarksSamples.chunkl b/Src/GBX.NET/Engines/Scene/CSceneVehicleCarMarksSamples.chunkl index d796966e4..323d05ccc 100644 --- a/Src/GBX.NET/Engines/Scene/CSceneVehicleCarMarksSamples.chunkl +++ b/Src/GBX.NET/Engines/Scene/CSceneVehicleCarMarksSamples.chunkl @@ -13,6 +13,16 @@ CSceneVehicleCarMarksSamples 0x0A083000 version v1+ throw + Sample[] Samples + +archive Sample // SSceneVehicleCarkMarkSample typo lol int - if U01 > 0 - throw + int + int + int + int + int + int + int + int + int \ No newline at end of file diff --git a/Src/GBX.NET/Engines/Scene/CSceneVehicleEnvironment.chunkl b/Src/GBX.NET/Engines/Scene/CSceneVehicleEnvironment.chunkl new file mode 100644 index 000000000..9a27c5c70 --- /dev/null +++ b/Src/GBX.NET/Engines/Scene/CSceneVehicleEnvironment.chunkl @@ -0,0 +1,7 @@ +CSceneVehicleEnvironment 0x0A033000 + +0x000 + bool + +0x002 + CPlugMaterial[] Materials (external) \ No newline at end of file diff --git a/Src/GBX.NET/Engines/TrackMania/CGameControlCameraTrackManiaRace.chunkl b/Src/GBX.NET/Engines/TrackMania/CGameControlCameraTrackManiaRace.chunkl new file mode 100644 index 000000000..e52d55701 --- /dev/null +++ b/Src/GBX.NET/Engines/TrackMania/CGameControlCameraTrackManiaRace.chunkl @@ -0,0 +1,18 @@ +CGameControlCameraTrackManiaRace 0x24085000 +- inherits: CGameControlCameraTarget + +0x000 + float ConeAperture + float ConeMinSpeed + float ConeMaxSpeed + bool UseSpeedDir + float CarCameraHeight + float CarCameraDistance + float CarCameraTargetDistance + float CarCameraAlign + bool IsSegmentCast + float SegmentCastMinDist + float SegmentCastLength + float + float + float \ No newline at end of file diff --git a/Src/GBX.NET/Engines/TrackMania/CGameControlCameraTrackManiaRace2.chunkl b/Src/GBX.NET/Engines/TrackMania/CGameControlCameraTrackManiaRace2.chunkl new file mode 100644 index 000000000..3232f8c73 --- /dev/null +++ b/Src/GBX.NET/Engines/TrackMania/CGameControlCameraTrackManiaRace2.chunkl @@ -0,0 +1,69 @@ +CGameControlCameraTrackManiaRace2 0x24086000 +- inherits: CGameControlCameraTarget + +0x000 + float InputGasDistDelta + int InputGasDistTimeUp + int InputGasDistTimeDown + float InputBrakeDistDelta + int InputBrakeDistTimeUp + int InputBrakeDistTimeDown + float InputSteerDistDelta + int InputSteerDistTimeUp + int InputSteerDistTimeDown + float EventTurboFovDelta + int EventTurboFovTimeUp + int EventTurboFovTimeDown + float EventTurboDistDelta + int EventTurboDistTimeUp + int EventTurboDistTimeDown + float EventChangeGearDistDelta + int EventChangeGearDistTimeUp + int EventChangeGearDistTimeDown + float EventBurningLookAtFactorDelta + int EventBurningLookAtFactorTimeUp + int EventBurningLookAtFactorTimeDown + float EventBurningDistDelta + int EventBurningDistTimeUp + int EventBurningDistTimeDown + float StateFlyingDistDelta + int StateFlyingDistTimeUp + int StateFlyingDistTimeDown + float StateFlyingPlaneDistDelta + int StateFlyingPlaneDistTimeUp + int StateFlyingPlaneDistTimeDown + float StateFlyingLookAtFactorDelta + int StateFlyingLookAtFactorTimeUp + int StateFlyingLookAtFactorTimeDown + float InputLeftSteerRollDelta + int InputLeftSteerRollTimeUp + int InputLeftSteerRollTimeDown + float InputRightSteerRollDelta + int InputRightSteerRollTimeUp + int InputRightSteerRollTimeDown + float InputLeftSteerYawDelta + int InputLeftSteerYawTimeUp + int InputLeftSteerYawTimeDown + float InputRightSteerYawDelta + int InputRightSteerYawTimeUp + int InputRightSteerYawTimeDown + float StateFlyingLookAtStep + float MinSpeed + int StateFlyingDurationBeforeFlyingMode + int StateFlyingDurationBeforeCameraMove + int InputSteerDurationBeforeBurnoutShowView + int InputSteerDurationBeforeAnticipatingTurnTriggered + int InputSteerDurationBeforeRollTriggered + int InputNoSteerDurationBeforeReset + float MaxDeltaPlaneDistStep + float MaxDeltaFovStep + CFuncKeysReal LookAtFactorFromUpSpeedRatio + CFuncKeysReal YawFromSpeed + CFuncKeysReal RollFromSpeed + float DeltaDistDamperKi + float DeltaDistDamperKa + float DeltaLookAtFactorDamperKi + float DeltaLookAtFactorDamperKa + +0x001 + bool IsRollFromInput \ No newline at end of file diff --git a/Src/GBX.NET/Engines/TrackMania/CGameControlCameraTrackManiaRace3.chunkl b/Src/GBX.NET/Engines/TrackMania/CGameControlCameraTrackManiaRace3.chunkl new file mode 100644 index 000000000..f10bdd222 --- /dev/null +++ b/Src/GBX.NET/Engines/TrackMania/CGameControlCameraTrackManiaRace3.chunkl @@ -0,0 +1,75 @@ +CGameControlCameraTrackManiaRace3 0x24087000 +- inherits: CGameControlCameraTarget + +0x000 + float SlerpSpeed + float Up + float Far + int FlyDurationBeforeFlyingBehavior + int InputNoSteerDurationBeforeReset + int InputSteerDurationBeforeBurnoutShowView + float MinSpeed + float MinSpeed2 + float SlerpTargetPosNormalBehaviorDelta + int SlerpTargetPosNormalBehaviorTimeUp + int SlerpTargetPosNormalBehaviorTimeDown + float SlerpTargetPosFlyingDelta + int SlerpTargetPosFlyingTimeUp + int SlerpTargetPosFlyingTimeDown + float SlerpTargetPosDelta + int SlerpTargetPosTimeUp + int SlerpTargetPosTimeDown + float SlerpTargetCamUpDelta + int SlerpTargetCamUpTimeUp + int SlerpTargetCamUpTimeDown + float SlerpSpeedFlyingBehavior + float SlerpSpeedDelta + int SlerpSpeedTimeUp + int SlerpSpeedTimeDown + float SlerpSpeedCamUp + float SlerpSpeedCamUpFlyingBehavior + float SlerpSpeedCamUpDelta + int SlerpSpeedCamUpTimeUp + int SlerpSpeedCamUpTimeDown + float StateFlyingLookAtFactorDelta + int StateFlyingLookAtFactorTimeUp + int StateFlyingLookAtFactorTimeDown + float StateFlyingLookAtStep + float StateFlyingRadiusDelta + int StateFlyingRadiusTimeUp + int StateFlyingRadiusTimeDown + float ConstantFlyingLookDownFactor + float FlyingLookDownFactorKi + float FlyingLookDownFactorKa + float RadiusDamperKi + float RadiusDamperKa + float InputGasFarDelta + int InputGasFarTimeUp + int InputGasFarTimeDown + float InputBrakeFarDelta + int InputBrakeFarTimeUp + int InputBrakeFarTimeDown + float InputSteerFarDelta + int InputSteerFarTimeUp + int InputSteerFarTimeDown + float EventTurboFovDelta + int EventTurboFovTimeUp + int EventTurboFovTimeDown + float EventTurboFarDelta + int EventTurboFarTimeUp + int EventTurboFarTimeDown + float EventChangeGearFarDelta + int EventChangeGearFarTimeUp + int EventChangeGearFarTimeDown + float EventBurningLookAtFactorDelta + int + int EventBurningLookAtFactorTimeDown + float EventBurningRadiusDelta + int EventBurningRadiusTimeUp + int EventBurningRadiusTimeDown + CFuncKeysReal SlerpSpeedModulationFromSpeed + CFuncKeysReal LookAtFactorFromUpSpeedRatio + CFuncKeysReal FlyingLookDownFactorFromSpeedRatio + +0x001 + float \ No newline at end of file diff --git a/Src/GBX.NET/Engines/VirtualSkipper/CVskCollection.chunkl b/Src/GBX.NET/Engines/VirtualSkipper/CVskCollection.chunkl new file mode 100644 index 000000000..8f98a63ee --- /dev/null +++ b/Src/GBX.NET/Engines/VirtualSkipper/CVskCollection.chunkl @@ -0,0 +1,12 @@ +CVskCollection 0x21085000 +- inherits: CGameCtnCollection + +0x000 + CMwNod + CMwNod + +0x001 + float + float + float + float \ No newline at end of file diff --git a/Src/GBX.NET/Extensions/Exporters/ObjExporter.cs b/Src/GBX.NET/Extensions/Exporters/ObjExporter.cs index 2e4df25d7..2e986f547 100644 --- a/Src/GBX.NET/Extensions/Exporters/ObjExporter.cs +++ b/Src/GBX.NET/Extensions/Exporters/ObjExporter.cs @@ -163,6 +163,8 @@ public static void Export(CPlugSolid solid, TextWriter objWriter, TextWriter mtl var positionsDict = mergeVerticesDigitThreshold.HasValue ? new Dictionary(new Vec3EqualityComparer(mergeVerticesDigitThreshold.Value)) : []; + var unknownMaterialDict = new Dictionary(); + foreach (var (t, loc) in tree.GetAllChildrenWithLocation(lod)) { if (t.Visual is null) @@ -175,7 +177,20 @@ public static void Export(CPlugSolid solid, TextWriter objWriter, TextWriter mtl continue; } - var materialName = t.ShaderFile is null ? "Unknown" : GbxPath.GetFileNameWithoutExtension(t.ShaderFile.FilePath); + string materialName; + if (t.ShaderFile is not null) + { + materialName = GbxPath.GetFileNameWithoutExtension(t.ShaderFile.FilePath); + } + else if (t.Shader is not null) + { + unknownMaterialDict[t.Shader] = unknownMaterialDict.Count; + materialName = "Unknown" + unknownMaterialDict.Count; + } + else + { + materialName = "Unknown"; + } if (!materials.Contains(materialName)) { @@ -304,17 +319,24 @@ public static void Export(CPlugSolid solid, TextWriter objWriter, TextWriter mtl continue; } - if (t.ShaderFile is null) + if (visual.IndexBuffer is null) { continue; } - if (visual.IndexBuffer is null) + string materialName; + if (t.ShaderFile is not null) { - continue; + materialName = GbxPath.GetFileNameWithoutExtension(t.ShaderFile.FilePath); + } + else if (t.Shader is not null) + { + materialName = "Unknown" + unknownMaterialDict[t.Shader]; + } + else + { + materialName = "Unknown"; } - - var materialName = GbxPath.GetFileNameWithoutExtension(t.ShaderFile.FilePath); objWriter.WriteLine("g {0}", materialName); objWriter.WriteLine("usemtl {0}", materialName); diff --git a/Src/GBX.NET/GBX.NET.csproj b/Src/GBX.NET/GBX.NET.csproj index 1e79e7a15..3984b02a7 100644 --- a/Src/GBX.NET/GBX.NET.csproj +++ b/Src/GBX.NET/GBX.NET.csproj @@ -2,7 +2,7 @@ GBX.NET - 2.1.0 + 2.1.1 BigBang1112 General purpose library for Gbx files - data from Nadeo games like Trackmania or Shootmania. It supports high performance serialization and deserialization of 200+ Gbx classes. Copyright (c) 2024 Petr Pivoňka @@ -89,6 +89,7 @@ + diff --git a/Src/GBX.NET/Gbx.cs b/Src/GBX.NET/Gbx.cs index c178e88ed..3765d021b 100644 --- a/Src/GBX.NET/Gbx.cs +++ b/Src/GBX.NET/Gbx.cs @@ -70,8 +70,9 @@ static abstract TVersion ParseHeaderNode(Stream stream, GbxRea static abstract TVersion ParseHeaderNode(string filePath, GbxReadSettings settings = default) where TClass : CMwNod, TVersion, new() where TVersion : IClassVersion; -#endif + [Experimental("GBXNET10001")] +#endif IGbx DeepClone(); } @@ -161,6 +162,9 @@ public override string ToString() return GbxPath.GetFileNameWithoutExtension(FilePath); } +#if NET8_0_OR_GREATER + [Experimental("GBXNET10001")] +#endif public virtual Gbx DeepClone() => new(Header.DeepClone(), Body.DeepClone()) { FilePath = FilePath, @@ -173,6 +177,9 @@ public override string ToString() ClassIdRemapMode = ClassIdRemapMode }; +#if NET8_0_OR_GREATER + [Experimental("GBXNET10001")] +#endif IGbx IGbx.DeepClone() => DeepClone(); [Zomp.SyncMethodGenerator.CreateSyncVersion] @@ -1007,6 +1014,9 @@ public override string ToString() return $"Gbx<{typeof(T).Name}> ({ClassManager.GetName(Header.ClassId)}, 0x{Header.ClassId:X8})"; } +#if NET8_0_OR_GREATER + [Experimental("GBXNET10001")] +#endif #if NETSTANDARD2_0 public override Gbx DeepClone() => new Gbx((GbxHeader)Header.DeepClone(), Body.DeepClone(), (T)Node.DeepClone()) #else diff --git a/Src/GBX.NET/README.md b/Src/GBX.NET/README.md index 44c6c7941..4443d7a93 100644 --- a/Src/GBX.NET/README.md +++ b/Src/GBX.NET/README.md @@ -11,6 +11,7 @@ For more details, see the main README. Due to the recently paced evolution of .NET, framework support has been limited only to a few ones compared to GBX.NET 1: +- .NET 9 - .NET 8 - .NET 6 - .NET Standard 2.0 @@ -180,13 +181,13 @@ Some of the common types to start with (a lot more are supported): | SystemConfig.Gbx | CSystemConfig | Yes | Yes | FidCache.Gbx | CMwRefBuffer | Yes | Yes | Profile.Gbx | CGamePlayerProfile | Up to TMF | Up to TMF -| Scores.Gbx | CGamePlayerScore | Yes | No +| Scores.Gbx | CGamePlayerScore | No | No ## Supported games Many *essential* Gbx files from many games are supported: -- **Trackmania (2020)**, July 2024 update +- **Trackmania (2020)**, December 2024 update - **ManiaPlanet 4**(.1), TM2/SM - **Trackmania Turbo** - ManiaPlanet 3, TM2/SM @@ -223,4 +224,4 @@ Without these people, this project wouldn't be what it is today (ordered by impa - Mika Kuijpers (TheMrMiku) - donadigo -And many thanks to every bug reporter! \ No newline at end of file +And many thanks to every bug reporter! diff --git a/Src/GBX.NET/Serialization/Chunking/Chunk.cs b/Src/GBX.NET/Serialization/Chunking/Chunk.cs index 2d35c8418..eaf980c69 100644 --- a/Src/GBX.NET/Serialization/Chunking/Chunk.cs +++ b/Src/GBX.NET/Serialization/Chunking/Chunk.cs @@ -1,4 +1,7 @@ using GBX.NET.Managers; +#if NET8_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif namespace GBX.NET.Serialization.Chunking; @@ -15,6 +18,9 @@ public interface IChunk bool Ignore { get; } GameVersion GameVersion { get; } +#if NET8_0_OR_GREATER + [Experimental("GBXNET10001")] +#endif IChunk DeepClone(); } @@ -49,8 +55,14 @@ public virtual void Read(IClass n, GbxReader r) { } /// public virtual void Write(IClass n, GbxWriter w) { } +#if NET8_0_OR_GREATER + [Experimental("GBXNET10001")] +#endif public abstract Chunk DeepClone(); +#if NET8_0_OR_GREATER + [Experimental("GBXNET10001")] +#endif IChunk IChunk.DeepClone() => DeepClone(); public override string ToString() => $"{ClassManager.GetName(Id & 0xFFFFF000)} chunk 0x{Id:X8}{(this is IVersionable v ? $" [v{v.Version}]" : "")}"; diff --git a/Src/GBX.NET/Serialization/GbxHeaderReader.cs b/Src/GBX.NET/Serialization/GbxHeaderReader.cs index 83c6480f5..46ccfc018 100644 --- a/Src/GBX.NET/Serialization/GbxHeaderReader.cs +++ b/Src/GBX.NET/Serialization/GbxHeaderReader.cs @@ -95,11 +95,11 @@ private uint ReadClassId() if (rawClassId == classId) { - logger?.LogDebug("Class ID: 0x{ClassId:X8} ({ClassName})", classId, ClassManager.GetName(classId) ?? "unknown class"); + logger?.LogInformation("Class ID: 0x{ClassId:X8} ({ClassName})", classId, ClassManager.GetName(classId) ?? "unknown class"); } else { - logger?.LogDebug("Class ID: 0x{ClassId:X8} (raw: 0x{RawClassId:X8}, {RawClassName} -> {ClassName})", + logger?.LogInformation("Class ID: 0x{ClassId:X8} (raw: 0x{RawClassId:X8}, {RawClassName} -> {ClassName})", classId, rawClassId, ClassManager.GetName(rawClassId) ?? "unknown class", ClassManager.GetName(classId) ?? "unknown class"); diff --git a/Src/GBX.NET/Serialization/GbxReader.cs b/Src/GBX.NET/Serialization/GbxReader.cs index 100186a85..3144389c2 100644 --- a/Src/GBX.NET/Serialization/GbxReader.cs +++ b/Src/GBX.NET/Serialization/GbxReader.cs @@ -2069,15 +2069,13 @@ internal bool TryInitializeDecryption(IClass node) { parentClassId = 0x07001000; } - - if (node is CPlugSurfaceGeom) + else if (baseType == typeof(CGameCtnBlockInfo)) { - parentClassId = 0x0902B000; + parentClassId = 0x24005000; } - - if (baseType == typeof(CGameCtnBlockInfo)) + else if (baseType == typeof(CPlugVehiclePhyTuning)) { - parentClassId = 0x24005000; + parentClassId = 0x0A02E000; } var parentClassIDBytes = BitConverter.GetBytes(parentClassId); diff --git a/Tools/GbxExplorerOld/Client/Components/ChunkViewer.razor b/Tools/GbxExplorerOld/Client/Components/ChunkViewer.razor index 8de1cf3bf..f334887f6 100644 --- a/Tools/GbxExplorerOld/Client/Components/ChunkViewer.razor +++ b/Tools/GbxExplorerOld/Client/Components/ChunkViewer.razor @@ -36,15 +36,14 @@ } } - var chunkId = Chunk.Id & 0xFFF; var isSameBaseId = true; }
+ @onclick="async () => await OpenOrCloseChunkAsync(chunkType)" + @onmouseover="() => over = true" + @onmouseout="() => over = false"> @if (chunkType.IsGenericType) { @@ -54,7 +53,7 @@ if (classId.HasValue && (classId.Value & 0xFFFFF000) == (Chunk.Id & 0xFFFFF000)) { - @name + @name } else { @@ -65,12 +64,17 @@ } else { - @chunkType.DeclaringType?.Name + var className = chunkType.DeclaringType?.Name ?? GBX.NET.Managers.ClassManager.GetName(Chunk.Id & 0xFFFFF000); + + if (!string.IsNullOrEmpty(className)) + { + @className + } } @if (isSameBaseId) { - 0x@(chunkId.ToString("X3") ?? "XXX") + 0x@(Chunk.Id.ToString("X8")) } @if (Chunk is IVersionable chunkVersionable) @@ -80,7 +84,7 @@ @if (!string.IsNullOrWhiteSpace(atts?.Description)) { - (@atts.Description) + (@atts.Description) } @if (atts is null) diff --git a/Tools/GbxExplorerOld/Client/Components/NodeTree.razor b/Tools/GbxExplorerOld/Client/Components/NodeTree.razor index d829cbae6..de6c043f3 100644 --- a/Tools/GbxExplorerOld/Client/Components/NodeTree.razor +++ b/Tools/GbxExplorerOld/Client/Components/NodeTree.razor @@ -50,7 +50,7 @@ if (declaringTypeGroups.Count > 1) { -
  • +
  • @(sameDeclaringTypes || group.Key?.DeclaringType is null ? "" : $"{group.Key.DeclaringType.Name}.")@group.Key?.Name
  • } diff --git a/Tools/PakToZip/Program.cs b/Tools/PakToZip/Program.cs index ac6be1339..4d8b00437 100644 --- a/Tools/PakToZip/Program.cs +++ b/Tools/PakToZip/Program.cs @@ -18,14 +18,22 @@ var hashes = await Pak.BruteforceFileHashesAsync(directoryPath, game, onlyUsedHashes: false); +var key = default(byte[]); + var packlistFileName = Path.Combine(directoryPath, "packlist.dat"); -var packlist = await PakList.ParseAsync(packlistFileName, game); -var key = packlist[Path.GetFileNameWithoutExtension(pakFileName).ToLowerInvariant()].Key; +if (File.Exists(packlistFileName)) +{ + Console.WriteLine("Reading packlist..."); + var packlist = await PakList.ParseAsync(packlistFileName, game); + key = packlist[Path.GetFileNameWithoutExtension(pakFileName).ToLowerInvariant()].Key; +} await using var fs = File.OpenRead(pakFileName); await using var pak = await Pak.ParseAsync(fs, key); +File.Delete(Path.ChangeExtension(pakFileName, ".zip")); + using var zip = ZipFile.Open(Path.ChangeExtension(pakFileName, ".zip"), ZipArchiveMode.Create); foreach (var file in pak.Files.Values) @@ -39,7 +47,7 @@ try { - var gbx = await pak.OpenGbxFileAsync(file); + var gbx = await pak.OpenGbxFileAsync(file, fileHashes: hashes); using var stream = entry.Open();