From 75b1548f55a6294c778854b3039c6a1cb671de25 Mon Sep 17 00:00:00 2001 From: bird_egop Date: Thu, 4 Dec 2025 03:50:29 +0300 Subject: [PATCH] Material parsing and viewing --- Common/Extensions.cs | 8 + MaterialLib/Interpolate.c | 82 +++ MaterialLib/MaterialFile.cs | 137 +++++ MaterialLib/MaterialLib.csproj | 5 + MaterialLib/MaterialParser.cs | 176 +++++++ MaterialLib/decompiled.c | 290 +++++++++++ NResUI/App.cs | 1 + NResUI/ImGuiUI/MaterialExplorerPanel.cs | 291 +++++++++++ NResUI/ImGuiUI/NResExplorerPanel.cs | 26 +- NResUI/Models/MaterialViewModel.cs | 26 + NResUI/NResUI.csproj | 1 + ParkanPlayground.slnx | 1 + ParkanPlayground/Msh02.cs | 4 +- ParkanPlayground/ParkanPlayground.csproj | 1 + ParkanPlayground/Program.cs | 44 -- README.md | 34 +- VarsetLib/BinaryVarsetItem.cs | 1 + material_field_analysis.csv | 629 +++++++++++++++++++++++ 18 files changed, 1694 insertions(+), 63 deletions(-) create mode 100644 MaterialLib/Interpolate.c create mode 100644 MaterialLib/MaterialFile.cs create mode 100644 MaterialLib/MaterialLib.csproj create mode 100644 MaterialLib/MaterialParser.cs create mode 100644 MaterialLib/decompiled.c create mode 100644 NResUI/ImGuiUI/MaterialExplorerPanel.cs create mode 100644 NResUI/Models/MaterialViewModel.cs delete mode 100644 ParkanPlayground/Program.cs create mode 100644 material_field_analysis.csv diff --git a/Common/Extensions.cs b/Common/Extensions.cs index 0785ded..5a8f698 100644 --- a/Common/Extensions.cs +++ b/Common/Extensions.cs @@ -21,6 +21,14 @@ public static class Extensions return BinaryPrimitives.ReadUInt32LittleEndian(buf); } + public static ushort ReadUInt16LittleEndian(this Stream fs) + { + Span buf = stackalloc byte[2]; + fs.ReadExactly(buf); + + return BinaryPrimitives.ReadUInt16LittleEndian(buf); + } + public static float ReadFloatLittleEndian(this Stream fs) { Span buf = stackalloc byte[4]; diff --git a/MaterialLib/Interpolate.c b/MaterialLib/Interpolate.c new file mode 100644 index 0000000..a26bbcd --- /dev/null +++ b/MaterialLib/Interpolate.c @@ -0,0 +1,82 @@ + +void __cdecl +Interpolate(MaterialExternal *src1_ptr,MaterialExternal *src2_ptr,float progress, + MaterialExternal *dst_ptr,uint targetFlags) + +{ + if ((targetFlags & 2) == 0) { + (dst_ptr->stage).diffuse.R = (src1_ptr->stage).diffuse.R; + (dst_ptr->stage).diffuse.G = (src1_ptr->stage).diffuse.G; + (dst_ptr->stage).diffuse.B = (src1_ptr->stage).diffuse.B; + } + else { + (dst_ptr->stage).diffuse.R = + ((src2_ptr->stage).diffuse.R - (src1_ptr->stage).diffuse.R) * progress + + (src1_ptr->stage).diffuse.R; + (dst_ptr->stage).diffuse.G = + ((src2_ptr->stage).diffuse.G - (src1_ptr->stage).diffuse.G) * progress + + (src1_ptr->stage).diffuse.G; + (dst_ptr->stage).diffuse.B = + ((src2_ptr->stage).diffuse.B - (src1_ptr->stage).diffuse.B) * progress + + (src1_ptr->stage).diffuse.B; + } + if ((targetFlags & 1) == 0) { + (dst_ptr->stage).ambient.R = (src1_ptr->stage).ambient.R; + (dst_ptr->stage).ambient.G = (src1_ptr->stage).ambient.G; + (dst_ptr->stage).ambient.B = (src1_ptr->stage).ambient.B; + } + else { + (dst_ptr->stage).ambient.R = + ((src2_ptr->stage).ambient.R - (src1_ptr->stage).ambient.R) * progress + + (src1_ptr->stage).ambient.R; + (dst_ptr->stage).ambient.G = + ((src2_ptr->stage).ambient.G - (src1_ptr->stage).ambient.G) * progress + + (src1_ptr->stage).ambient.G; + (dst_ptr->stage).ambient.B = + ((src2_ptr->stage).ambient.B - (src1_ptr->stage).ambient.B) * progress + + (src1_ptr->stage).ambient.B; + } + if ((targetFlags & 4) == 0) { + (dst_ptr->stage).specular.R = (src1_ptr->stage).specular.R; + (dst_ptr->stage).specular.G = (src1_ptr->stage).specular.G; + (dst_ptr->stage).specular.B = (src1_ptr->stage).specular.B; + } + else { + (dst_ptr->stage).specular.R = + ((src2_ptr->stage).specular.R - (src1_ptr->stage).specular.R) * progress + + (src1_ptr->stage).specular.R; + (dst_ptr->stage).specular.G = + ((src2_ptr->stage).specular.G - (src1_ptr->stage).specular.G) * progress + + (src1_ptr->stage).specular.G; + (dst_ptr->stage).specular.B = + ((src2_ptr->stage).specular.B - (src1_ptr->stage).specular.B) * progress + + (src1_ptr->stage).specular.B; + } + if ((targetFlags & 8) == 0) { + (dst_ptr->stage).emissive.R = (src1_ptr->stage).emissive.R; + (dst_ptr->stage).emissive.G = (src1_ptr->stage).emissive.G; + (dst_ptr->stage).emissive.B = (src1_ptr->stage).emissive.B; + } + else { + (dst_ptr->stage).emissive.R = + ((src2_ptr->stage).emissive.R - (src1_ptr->stage).emissive.R) * progress + + (src1_ptr->stage).emissive.R; + (dst_ptr->stage).emissive.G = + ((src2_ptr->stage).emissive.G - (src1_ptr->stage).emissive.G) * progress + + (src1_ptr->stage).emissive.G; + (dst_ptr->stage).emissive.B = + ((src2_ptr->stage).emissive.B - (src1_ptr->stage).emissive.B) * progress + + (src1_ptr->stage).emissive.B; + } + if ((targetFlags & 0x10) != 0) { + (dst_ptr->stage).ambient.A = + ((src2_ptr->stage).ambient.A - (src1_ptr->stage).ambient.A) * progress + + (src1_ptr->stage).ambient.A; + (dst_ptr->stage).Power = (src1_ptr->stage).Power; + return; + } + (dst_ptr->stage).ambient.A = (src1_ptr->stage).ambient.A; + (dst_ptr->stage).Power = (src1_ptr->stage).Power; + return; +} + diff --git a/MaterialLib/MaterialFile.cs b/MaterialLib/MaterialFile.cs new file mode 100644 index 0000000..64928c4 --- /dev/null +++ b/MaterialLib/MaterialFile.cs @@ -0,0 +1,137 @@ +namespace MaterialLib; + +public class MaterialFile +{ + // === Metadata (not from file content) === + public string FileName { get; set; } + public int Version { get; set; } // From NRes ElementCount + public int Magic1 { get; set; } // From NRes Magic1 + + // === Derived from Version/ElementCount === + public int MaterialRenderingType { get; set; } // (Version >> 2) & 0xF - 0=Standard, 1=Special, 2=Particle + public bool SupportsBumpMapping { get; set; } // (Version & 2) != 0 + public int IsParticleEffect { get; set; } // Version & 40 - 0=Normal, 8=Particle/Effect + + // === File Content (in read order) === + // Read order: StageCount (ushort), AnimCount (ushort), then conditionally blend modes and params + + // Global Blend Modes (read if Magic1 >= 2) + public BlendMode SourceBlendMode { get; set; } // Default: Unknown (0xFF) + public BlendMode DestBlendMode { get; set; } // Default: Unknown (0xFF) + + // Global Parameters (read if Magic1 > 2 and > 3 respectively) + public float GlobalAlphaMultiplier { get; set; } // Default: 1.0 (always 1.0 in all 628 materials) + public float GlobalEmissiveIntensity { get; set; } // Default: 0.0 (0=no glow, rare values: 1000, 10000) + + public List Stages { get; set; } = new(); + public List Animations { get; set; } = new(); +} + +public enum BlendMode : byte +{ + Zero = 1, + One = 2, + SrcColor = 3, + InvSrcColor = 4, + SrcAlpha = 5, + InvSrcAlpha = 6, + DestAlpha = 7, + InvDestAlpha = 8, + DestColor = 9, + InvDestColor = 10, + SrcAlphaSat = 11, + BothSrcAlpha = 12, + BothInvSrcAlpha = 13, + + // Custom or Unknown + Unknown = 0xFF +} + +public class MaterialStage +{ + // === FILE READ ORDER (34 bytes per stage) === + // This matches the order bytes are read from the file (decompiled.c lines 159-217) + // NOT the C struct memory layout (which is Diffuse, Ambient, Specular, Emissive) + + // 1. Ambient Color (4 bytes, read first from file) + public float AmbientR; + public float AmbientG; + public float AmbientB; + public float AmbientA; // Scaled by 0.01 when read from file + + // 2. Diffuse Color (4 bytes, read second from file) + public float DiffuseR; + public float DiffuseG; + public float DiffuseB; + public float DiffuseA; + + // 3. Specular Color (4 bytes, read third from file) + public float SpecularR; + public float SpecularG; + public float SpecularB; + public float SpecularA; + + // 4. Emissive Color (4 bytes, read fourth from file) + public float EmissiveR; + public float EmissiveG; + public float EmissiveB; + public float EmissiveA; + + // 5. Power (1 byte → float, read fifth from file) + public float Power; + + // 6. Texture Stage Index (1 byte, read sixth from file) + // 255 = not set/default, 0-47 = reference to specific texture stage + public int TextureStageIndex; + + // 7. Texture Name (16 bytes, read seventh from file) + public string TextureName { get; set; } +} + +public class MaterialAnimation +{ + // === File Read Order === + + // Combined field (4 bytes): bits 3-31 = Target, bits 0-2 = LoopMode + public AnimationTarget Target; + public AnimationLoopMode LoopMode; + + // Key count (2 bytes), then keys + public List Keys { get; set; } = new(); + + // Cached description for UI (computed once during parsing) + public string TargetDescription { get; set; } = string.Empty; +} + + +[Flags] +public enum AnimationTarget : int +{ + // NOTE: This is a BITSET (flags enum). Multiple flags can be combined. + // When a flag is SET, that component is INTERPOLATED between stages. + // When a flag is NOT SET, that component is COPIED from the source stage (no interpolation). + // If ALL flags are 0, the ENTIRE stage is copied without any interpolation. + + Ambient = 1, // 0x01 - Interpolates Ambient RGB (Interpolate.c lines 23-37) + Diffuse = 2, // 0x02 - Interpolates Diffuse RGB (Interpolate.c lines 7-21) + Specular = 4, // 0x04 - Interpolates Specular RGB (Interpolate.c lines 39-53) + Emissive = 8, // 0x08 - Interpolates Emissive RGB (Interpolate.c lines 55-69) + Power = 16 // 0x10 - Interpolates Ambient.A and sets Power (Interpolate.c lines 71-76) +} + + +public enum AnimationLoopMode : int +{ + Loop = 0, + PingPong = 1, + Clamp = 2, + Random = 3 +} + +public struct AnimKey +{ + // === File Read Order (6 bytes per key) === + public ushort StageIndex; // Read first + public ushort DurationMs; // Read second + public ushort InterpolationCurve; // Read third - Always 0 (linear interpolation) in all 1848 keys +} diff --git a/MaterialLib/MaterialLib.csproj b/MaterialLib/MaterialLib.csproj new file mode 100644 index 0000000..415fc6f --- /dev/null +++ b/MaterialLib/MaterialLib.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/MaterialLib/MaterialParser.cs b/MaterialLib/MaterialParser.cs new file mode 100644 index 0000000..b8804d4 --- /dev/null +++ b/MaterialLib/MaterialParser.cs @@ -0,0 +1,176 @@ +using System.Buffers.Binary; +using System.Text; + +namespace MaterialLib; + +public static class MaterialParser +{ + public static MaterialFile ReadFromStream(Stream fs, string fileName, int elementCount, int magic1) + { + var file = new MaterialFile + { + FileName = fileName, + Version = elementCount, + Magic1 = magic1 + }; + + // Derived fields + file.MaterialRenderingType = elementCount >> 2 & 0xf; + file.SupportsBumpMapping = (elementCount & 2) != 0; + file.IsParticleEffect = elementCount & 40; + + // Reading content + var stageCount = fs.ReadUInt16LittleEndian(); + var animCount = fs.ReadUInt16LittleEndian(); + + uint magic = (uint)magic1; + + // Defaults found in C code + file.GlobalAlphaMultiplier = 1.0f; // field8_0x15c + file.GlobalEmissiveIntensity = 0.0f; // field9_0x160 + file.SourceBlendMode = BlendMode.Unknown; // field6_0x154 + file.DestBlendMode = BlendMode.Unknown; // field7_0x158 + + if (magic >= 2) + { + file.SourceBlendMode = (BlendMode)fs.ReadByte(); + file.DestBlendMode = (BlendMode)fs.ReadByte(); + } + + if (magic > 2) + { + file.GlobalAlphaMultiplier = fs.ReadFloatLittleEndian(); + } + + if (magic > 3) + { + file.GlobalEmissiveIntensity = fs.ReadFloatLittleEndian(); + } + + // --- 2. Material Stages --- + const float Inv255 = 1.0f / 255.0f; + const float Field7Mult = 0.01f; + Span textureNameBuffer = stackalloc byte[16]; + + for (int i = 0; i < stageCount; i++) + { + var stage = new MaterialStage(); + + // === FILE READ ORDER (matches decompiled.c lines 159-217) === + + // 1. Ambient (4 bytes, A scaled by 0.01) - Lines 159-168 + stage.AmbientR = fs.ReadByte() * Inv255; + stage.AmbientG = fs.ReadByte() * Inv255; + stage.AmbientB = fs.ReadByte() * Inv255; + stage.AmbientA = fs.ReadByte() * Field7Mult; // 0.01 scaling + + // 2. Diffuse (4 bytes) - Lines 171-180 + stage.DiffuseR = fs.ReadByte() * Inv255; + stage.DiffuseG = fs.ReadByte() * Inv255; + stage.DiffuseB = fs.ReadByte() * Inv255; + stage.DiffuseA = fs.ReadByte() * Inv255; + + // 3. Specular (4 bytes) - Lines 183-192 + stage.SpecularR = fs.ReadByte() * Inv255; + stage.SpecularG = fs.ReadByte() * Inv255; + stage.SpecularB = fs.ReadByte() * Inv255; + stage.SpecularA = fs.ReadByte() * Inv255; + + // 4. Emissive (4 bytes) - Lines 195-204 + stage.EmissiveR = fs.ReadByte() * Inv255; + stage.EmissiveG = fs.ReadByte() * Inv255; + stage.EmissiveB = fs.ReadByte() * Inv255; + stage.EmissiveA = fs.ReadByte() * Inv255; + + // 5. Power (1 byte → float) - Line 207 + stage.Power = (float)fs.ReadByte(); + + // 6. Texture Stage Index (1 byte) - Line 210 + stage.TextureStageIndex = fs.ReadByte(); + + // 7. Texture Name (16 bytes) - Lines 212-217 + textureNameBuffer.Clear(); + fs.ReadExactly(textureNameBuffer); + stage.TextureName = Encoding.ASCII.GetString(textureNameBuffer).TrimEnd('\0'); + + file.Stages.Add(stage); + } + + // --- 3. Animations --- + for (int i = 0; i < animCount; i++) + { + var anim = new MaterialAnimation(); + + uint typeAndParams = fs.ReadUInt32LittleEndian(); + anim.Target = (AnimationTarget)(typeAndParams >> 3); + anim.LoopMode = (AnimationLoopMode)(typeAndParams & 7); + + ushort keyCount = fs.ReadUInt16LittleEndian(); + + for (int k = 0; k < keyCount; k++) + { + var key = new AnimKey + { + StageIndex = fs.ReadUInt16LittleEndian(), + DurationMs = fs.ReadUInt16LittleEndian(), + InterpolationCurve = fs.ReadUInt16LittleEndian() + }; + anim.Keys.Add(key); + } + + // Precompute description for UI to avoid per-frame allocations + anim.TargetDescription = ComputeTargetDescription(anim.Target); + + file.Animations.Add(anim); + } + + return file; + } + + private static string ComputeTargetDescription(AnimationTarget target) + { + // Precompute the description once during parsing + if ((int)target == 0) + return "No interpolation - entire stage is copied as-is (Flags: 0x0)"; + + var parts = new List(); + + if ((target & AnimationTarget.Ambient) != 0) + parts.Add("Ambient RGB"); + if ((target & AnimationTarget.Diffuse) != 0) + parts.Add("Diffuse RGB"); + if ((target & AnimationTarget.Specular) != 0) + parts.Add("Specular RGB"); + if ((target & AnimationTarget.Emissive) != 0) + parts.Add("Emissive RGB"); + if ((target & AnimationTarget.Power) != 0) + parts.Add("Ambient.A + Power"); + + return $"Interpolates: {string.Join(", ", parts)} | Other components copied (Flags: 0x{(int)target:X})"; + } +} + +// Helper extensions +internal static class StreamExtensions +{ + public static float ReadFloatLittleEndian(this Stream stream) + { + Span buffer = stackalloc byte[4]; + stream.ReadExactly(buffer); + return BinaryPrimitives.ReadSingleLittleEndian(buffer); + } + + public static ushort ReadUInt16LittleEndian(this Stream stream) + { + Span buffer = stackalloc byte[2]; + stream.ReadExactly(buffer); + return BinaryPrimitives.ReadUInt16LittleEndian(buffer); + } + + public static uint ReadUInt32LittleEndian(this Stream stream) + { + Span buffer = stackalloc byte[4]; + stream.ReadExactly(buffer); + return BinaryPrimitives.ReadUInt32LittleEndian(buffer); + } +} diff --git a/MaterialLib/decompiled.c b/MaterialLib/decompiled.c new file mode 100644 index 0000000..db4ceca --- /dev/null +++ b/MaterialLib/decompiled.c @@ -0,0 +1,290 @@ + +/* returns index into g_MaterialGlobalDescriptors */ + +int __fastcall LoadMaterialByName(char *itemname) + +{ + char *pcVar1; + undefined1 *puVar2; + byte *pbVar3; + uint uVar4; + undefined4 *puVar5; + int iVar6; + int iVar7; + MaterialGlobalDescriptor_ptr_4_undefined4 piVar8; + nres_metadata_item *pnVar8; + int iVar9; + MaterialStageWorldDllInternal *pMVar10; + int iVar11; + ITexture **ppIVar12; + MaterialAnimationKey *pMVar13; + MaterialGlobalDescriptor *pMVar14; + float unaff_EBX; + MaterialStageExternal *pfVar16; + ushort unaff_DI; + MaterialAnimation *pMVar15; + uint uVar16; + int iStack_90; + uint uStack_8c; + char acStack_88 [8]; + int iStack_80; + char acStack_58 [88]; + + iVar6 = (*(*g_material_resfile)->get_index_in_file_by_itemname)(g_material_resfile,itemname); + if (iVar6 < 0) { + sprintf(acStack_58,s_Material_%s_not_found._100246f8,itemname); + /* WARNING: Subroutine does not return */ + write_error_to_file_and_msgbox(acStack_58,(void *)0x0); + } + iVar7 = 0; + if (0 < g_MaterialGlobalDescriptor_count) { + pMVar14 = g_MaterialGlobalDescriptors; + do { + if (pMVar14->index_in_file == iVar6) { + if (-1 < iVar7) { + g_MaterialGlobalDescriptors[iVar7].RefCount = + g_MaterialGlobalDescriptors[iVar7].RefCount + 1; + return iVar7; + } + break; + } + iVar7 += 1; + pMVar14 = pMVar14 + 1; + } while (iVar7 < g_MaterialGlobalDescriptor_count); + } + iVar7 = 0; + if (0 < g_MaterialGlobalDescriptor_count) { + piVar8 = &g_MaterialGlobalDescriptors[0].RefCount; + do { + if (ADJ(piVar8)->RefCount == 0) break; + iVar7 += 1; + piVar8 = piVar8 + 0x5c; + } while (iVar7 < g_MaterialGlobalDescriptor_count); + } + if (iVar7 == g_MaterialGlobalDescriptor_count) { + g_MaterialGlobalDescriptor_count += 1; + } + iStack_80 = iVar7; + g_currentMaterialFileData_ptr = + (*(*g_material_resfile)->get_item_data_ptr_by_index)(g_material_resfile,iVar6,1); + pnVar8 = (*(*g_material_resfile)->get_metadata_ptr)(g_material_resfile); + uVar16 = 0; + uVar4 = pnVar8[iVar6].magic1; + if ((pnVar8[iVar6].element_count_or_version & 1) != 0) { + uVar16 = 0x200000; + } + g_MaterialGlobalDescriptors[iVar7].extra_meta.field0_0x0 = 0; + g_MaterialGlobalDescriptors[iVar7].extra_meta.field2_0x8 = 0; + g_MaterialGlobalDescriptors[iVar7].extra_meta.field1_0x4 = + pnVar8[iVar6].element_count_or_version >> 2 & 0xf; + iVar9 = g_bumpmapping_enabled_value; + if (((pnVar8[iVar6].element_count_or_version & 2) != 0) && + ((g_MaterialGlobalDescriptors[iVar7].extra_meta.field0_0x0 = 1, iVar9 == 0 || + (g_supports_texture_mode_6 == 0)))) { + uVar16 |= 0x80000; + } + if ((pnVar8[iVar6].element_count_or_version & 0x40) != 0) { + g_MaterialGlobalDescriptors[iVar7].extra_meta.field2_0x8 = 1; + } + iVar9 = 0; + g_MaterialGlobalDescriptors[iVar7].RefCount = g_MaterialGlobalDescriptors[iVar7].RefCount + 1; + puVar5 = g_currentMaterialFileData_ptr; + g_MaterialGlobalDescriptors[iVar7].index_in_file = iVar6; + iVar6 = 0; + do { + pcVar1 = (char *)(iVar9 + (int)puVar5); + iVar9 += 1; + acStack_88[iVar6 + -0x1c] = *pcVar1; + iVar6 += 1; + } while (iVar6 < 2); + g_MaterialGlobalDescriptors[iVar7].stageCount = (uint)unaff_DI; + iVar6 = 0; + do { + iVar11 = iVar9; + iVar9 = iVar11 + 1; + acStack_88[iVar6 + -0x1c] = *(char *)(iVar11 + (int)puVar5); + iVar6 += 1; + } while (iVar6 < 2); + DAT_10128674 = iVar9; + g_MaterialGlobalDescriptors[iVar7].animCount = (uint)unaff_DI; + if (0x13 < unaff_DI) { + /* WARNING: Subroutine does not return */ + write_error_to_file_and_msgbox(s_Too_many_animations_for_material_100246cc,(void *)0x0); + } + g_MaterialGlobalDescriptors[iVar7].field8_0x15c = 1.0; + g_MaterialGlobalDescriptors[iVar7].field9_0x160 = 0; + if (uVar4 < 2) { + g_MaterialGlobalDescriptors[iVar7].field6_0x154 = 0xff; + g_MaterialGlobalDescriptors[iVar7].field7_0x158 = 0xff; + } + else { + g_MaterialGlobalDescriptors[iVar7].field6_0x154 = (uint)*(byte *)(iVar9 + (int)puVar5); + iVar6 = iVar11 + 3; + DAT_10128674 = iVar6; + g_MaterialGlobalDescriptors[iVar7].field7_0x158 = (uint)*(byte *)(iVar11 + 2 + (int)puVar5); + if (2 < uVar4) { + iVar9 = 0; + do { + puVar2 = (undefined1 *)(iVar6 + (int)puVar5); + iVar6 += 1; + (&stack0xffffff68)[iVar9] = *puVar2; + iVar9 += 1; + } while (iVar9 < 4); + DAT_10128674 = iVar6; + g_MaterialGlobalDescriptors[iVar7].field8_0x15c = unaff_EBX; + if (3 < uVar4) { + iVar9 = 0; + do { + puVar2 = (undefined1 *)(iVar6 + (int)puVar5); + iVar6 += 1; + (&stack0xffffff68)[iVar9] = *puVar2; + iVar9 += 1; + } while (iVar9 < 4); + DAT_10128674 = iVar6; + g_MaterialGlobalDescriptors[iVar7].field9_0x160 = unaff_EBX; + } + } + } + pMVar10 = (MaterialStageWorldDllInternal *) + _malloc(g_MaterialGlobalDescriptors[iVar7].stageCount * 0x4c); + g_MaterialGlobalDescriptors[iVar7].stages = pMVar10; + iVar6 = 0; + if (0 < g_MaterialGlobalDescriptors[iVar7].stageCount) { + iVar9 = 0; + do { + pfVar16 = (MaterialStageExternal *) + ((int)&((g_MaterialGlobalDescriptors[iVar7].stages)->diffuse).R + iVar9); + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + (pfVar16->ambient).R = (float)*pbVar3 * 1/255f; + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + (pfVar16->ambient).G = (float)*pbVar3 * 1/255f; + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + (pfVar16->ambient).B = (float)*pbVar3 * 1/255f; + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + (pfVar16->ambient).A = (float)*pbVar3 * 0.01; + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + (pfVar16->diffuse).R = (float)*pbVar3 * 1/255f; + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + (pfVar16->diffuse).G = (float)*pbVar3 * 1/255f; + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + (pfVar16->diffuse).B = (float)*pbVar3 * 1/255f; + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + (pfVar16->diffuse).A = (float)*pbVar3 * 1/255f; + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + (pfVar16->specular).R = (float)*pbVar3 * 1/255f; + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + (pfVar16->specular).G = (float)*pbVar3 * 1/255f; + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + (pfVar16->specular).B = (float)*pbVar3 * 1/255f; + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + (pfVar16->specular).A = (float)*pbVar3 * 1/255f; + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + (pfVar16->emissive).R = (float)*pbVar3 * 1/255f; + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + (pfVar16->emissive).G = (float)*pbVar3 * 1/255f; + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + (pfVar16->emissive).B = (float)*pbVar3 * 1/255f; + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + (pfVar16->emissive).A = (float)*pbVar3 * 1/255f; + pbVar3 = (byte *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + pfVar16->Power = (float)(uint)*pbVar3; + pcVar1 = (char *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 = DAT_10128674 + 1; + pfVar16->current_m_LL_ITexture = (ITexture **)(int)*pcVar1; + iVar11 = 0; + do { + pcVar1 = (char *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + acStack_88[iVar11] = *pcVar1; + iVar11 += 1; + } while (iVar11 < 0x10); + if (acStack_88[0] == '\0') { + pfVar16->m_LL_ITexture = (ITexture **)0xffffffff; + pfVar16->current_m_LL_ITexture = (ITexture **)0xffffffff; + } + else { + ppIVar12 = (ITexture **)LoadAndCacheTexture(acStack_88,uVar16); + pfVar16->m_LL_ITexture = ppIVar12; + } + iVar6 += 1; + iVar9 += 0x4c; + } while (iVar6 < g_MaterialGlobalDescriptors[iVar7].stageCount); + } + iVar6 = 0; + if (0 < g_MaterialGlobalDescriptors[iVar7].animCount) { + pMVar15 = g_MaterialGlobalDescriptors[iVar7].animations; + do { + puVar5 = g_currentMaterialFileData_ptr; + iVar9 = 0; + do { + pcVar1 = (char *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + acStack_88[iVar9 + -4] = *pcVar1; + iVar9 += 1; + } while (iVar9 < 4); + pMVar15->FieldSelector = (int)uStack_8c >> 3; + pMVar15->loop_mode = uStack_8c & 7; + iVar9 = 0; + do { + pcVar1 = (char *)(DAT_10128674 + (int)puVar5); + DAT_10128674 += 1; + acStack_88[iVar9 + -0x1c] = *pcVar1; + iVar9 += 1; + } while (iVar9 < 2); + pMVar15->keyCount = (uint)unaff_DI; + pMVar13 = (MaterialAnimationKey *)_malloc((uint)unaff_DI << 3); + pMVar15->keys = pMVar13; + iVar9 = 0; + if (0 < (int)pMVar15->keyCount) { + do { + iVar11 = 0; + do { + pcVar1 = (char *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + acStack_88[iVar11 + -0x1c] = *pcVar1; + iVar11 += 1; + } while (iVar11 < 2); + pMVar15->keys[iVar9].stage_index = (uint)unaff_DI; + iVar11 = 0; + do { + pcVar1 = (char *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + acStack_88[iVar11 + -0x1c] = *pcVar1; + iVar11 += 1; + } while (iVar11 < 2); + pMVar15->keys[iVar9].duration_ms = unaff_DI; + iVar11 = 0; + do { + pcVar1 = (char *)(DAT_10128674 + (int)g_currentMaterialFileData_ptr); + DAT_10128674 += 1; + acStack_88[iVar11 + -0x1c] = *pcVar1; + iVar11 += 1; + } while (iVar11 < 2); + pMVar15->keys[iVar9].field2_0x6 = unaff_DI; + iVar9 += 1; + } while (iVar9 < (int)pMVar15->keyCount); + } + iVar6 += 1; + pMVar15 = pMVar15 + 1; + } while (iVar6 < g_MaterialGlobalDescriptors[iVar7].animCount); + } + return iStack_90; +} + diff --git a/NResUI/App.cs b/NResUI/App.cs index bda3894..99ed89c 100644 --- a/NResUI/App.cs +++ b/NResUI/App.cs @@ -60,6 +60,7 @@ public class App serviceCollection.AddSingleton(new ScrViewModel()); serviceCollection.AddSingleton(new VarsetViewModel()); serviceCollection.AddSingleton(new CpDatSchemeViewModel()); + serviceCollection.AddSingleton(new MaterialViewModel()); var serviceProvider = serviceCollection.BuildServiceProvider(); diff --git a/NResUI/ImGuiUI/MaterialExplorerPanel.cs b/NResUI/ImGuiUI/MaterialExplorerPanel.cs new file mode 100644 index 0000000..aedc8b6 --- /dev/null +++ b/NResUI/ImGuiUI/MaterialExplorerPanel.cs @@ -0,0 +1,291 @@ +using System.Numerics; +using ImGuiNET; +using MaterialLib; +using NResUI.Abstractions; +using NResUI.Models; + +namespace NResUI.ImGuiUI; + +public class MaterialExplorerPanel : IImGuiPanel +{ + private readonly MaterialViewModel _viewModel; + + public MaterialExplorerPanel(MaterialViewModel viewModel) + { + _viewModel = viewModel; + } + + public void OnImGuiRender() + { + if (ImGui.Begin("Material Explorer")) + { + if (!_viewModel.HasFile) + { + ImGui.Text("No Material file opened"); + } + else if (_viewModel.Error != null) + { + ImGui.TextColored(new Vector4(1, 0, 0, 1), $"Error: {_viewModel.Error}"); + } + else + { + var mat = _viewModel.MaterialFile!; + + ImGui.Text($"File: {_viewModel.FilePath}"); + ImGui.Separator(); + + // === FILE READ SEQUENCE === + + if (ImGui.CollapsingHeader("1. Metadata & Derived Fields", ImGuiTreeNodeFlags.DefaultOpen)) + { + ImGui.TextDisabled("(Not from file content)"); + ImGui.Text($"File Name: {mat.FileName}"); + ImGui.Text($"Version (ElementCount): {mat.Version}"); + ImGui.Text($"Magic1: {mat.Magic1}"); + + ImGui.Separator(); + ImGui.TextDisabled("(Derived from Version)"); + ImGui.Text($"Material Rendering Type: {mat.MaterialRenderingType}"); + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip("0=Standard, 1=Special, 2=Particle/Effect"); + } + ImGui.Text($"Supports Bump Mapping: {mat.SupportsBumpMapping}"); + ImGui.Text($"Is Particle Effect: {mat.IsParticleEffect}"); + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip("0=Normal material, 8=Particle/Effect (e.g., jet engines)"); + } + } + + if (ImGui.CollapsingHeader("2. File Header (Read Order)", ImGuiTreeNodeFlags.DefaultOpen)) + { + ImGui.Text($"Stage Count: {mat.Stages.Count}"); + ImGui.SameLine(); + ImGui.TextDisabled("(2 bytes, ushort)"); + + ImGui.Text($"Animation Count: {mat.Animations.Count}"); + ImGui.SameLine(); + ImGui.TextDisabled("(2 bytes, ushort)"); + + ImGui.Separator(); + ImGui.TextDisabled($"(Read if Magic1 >= 2)"); + ImGui.Text($"Source Blend Mode: {mat.SourceBlendMode}"); + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(GetBlendModeDescription(mat.SourceBlendMode)); + } + ImGui.SameLine(); + ImGui.TextDisabled("(1 byte)"); + + ImGui.Text($"Dest Blend Mode: {mat.DestBlendMode}"); + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(GetBlendModeDescription(mat.DestBlendMode)); + } + ImGui.SameLine(); + ImGui.TextDisabled("(1 byte)"); + + ImGui.Separator(); + ImGui.TextDisabled($"(Read if Magic1 > 2)"); + ImGui.Text($"Global Alpha Multiplier: {mat.GlobalAlphaMultiplier:F3}"); + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip("Always 1.0 in all 628 materials - global alpha multiplier"); + } + ImGui.SameLine(); + ImGui.TextDisabled("(4 bytes, float)"); + + ImGui.Separator(); + ImGui.TextDisabled($"(Read if Magic1 > 3)"); + ImGui.Text($"Global Emissive Intensity: {mat.GlobalEmissiveIntensity:F3}"); + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip("0=no glow (99.4%), rare values: 1000, 10000"); + } + ImGui.SameLine(); + ImGui.TextDisabled("(4 bytes, float)"); + } + + if (ImGui.CollapsingHeader($"3. Stages ({mat.Stages.Count}) - 34 bytes each", ImGuiTreeNodeFlags.DefaultOpen)) + { + for (int i = 0; i < mat.Stages.Count; i++) + { + var stage = mat.Stages[i]; + if (ImGui.TreeNode($"Stage {i}: {stage.TextureName}")) + { + ImGui.TextDisabled("=== File Read Order (34 bytes) ==="); + + // 1. Ambient (4 bytes) + ImGui.Text("1. Ambient Color:"); + ImGui.SameLine(); + ImGui.TextDisabled("(4 bytes, A scaled by 0.01)"); + var ambient = new Vector4(stage.AmbientR, stage.AmbientG, stage.AmbientB, stage.AmbientA); + ImGui.ColorEdit4("##Ambient", ref ambient, ImGuiColorEditFlags.NoInputs); + ImGui.SameLine(); + ImGui.Text($"RGBA: ({stage.AmbientR:F3}, {stage.AmbientG:F3}, {stage.AmbientB:F3}, {stage.AmbientA:F3})"); + + // 2. Diffuse (4 bytes) + ImGui.Text("2. Diffuse Color:"); + ImGui.SameLine(); + ImGui.TextDisabled("(4 bytes)"); + var diffuse = new Vector4(stage.DiffuseR, stage.DiffuseG, stage.DiffuseB, stage.DiffuseA); + ImGui.ColorEdit4("##Diffuse", ref diffuse, ImGuiColorEditFlags.NoInputs); + ImGui.SameLine(); + ImGui.Text($"RGBA: ({stage.DiffuseR:F3}, {stage.DiffuseG:F3}, {stage.DiffuseB:F3}, {stage.DiffuseA:F3})"); + + // 3. Specular (4 bytes) + ImGui.Text("3. Specular Color:"); + ImGui.SameLine(); + ImGui.TextDisabled("(4 bytes)"); + var specular = new Vector4(stage.SpecularR, stage.SpecularG, stage.SpecularB, stage.SpecularA); + ImGui.ColorEdit4("##Specular", ref specular, ImGuiColorEditFlags.NoInputs); + ImGui.SameLine(); + ImGui.Text($"RGBA: ({stage.SpecularR:F3}, {stage.SpecularG:F3}, {stage.SpecularB:F3}, {stage.SpecularA:F3})"); + + // 4. Emissive (4 bytes) + ImGui.Text("4. Emissive Color:"); + ImGui.SameLine(); + ImGui.TextDisabled("(4 bytes)"); + var emissive = new Vector4(stage.EmissiveR, stage.EmissiveG, stage.EmissiveB, stage.EmissiveA); + ImGui.ColorEdit4("##Emissive", ref emissive, ImGuiColorEditFlags.NoInputs); + ImGui.SameLine(); + ImGui.Text($"RGBA: ({stage.EmissiveR:F3}, {stage.EmissiveG:F3}, {stage.EmissiveB:F3}, {stage.EmissiveA:F3})"); + + // 5. Power (1 byte) + ImGui.Text($"5. Power: {stage.Power:F3}"); + ImGui.SameLine(); + ImGui.TextDisabled("(1 byte -> float)"); + + // 6. Texture Stage Index (1 byte) + ImGui.Text($"6. Texture Stage Index: {stage.TextureStageIndex}"); + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip("255 = not set/default, 0-47 = reference to specific texture stage"); + } + ImGui.SameLine(); + ImGui.TextDisabled("(1 byte)"); + + // 7. Texture Name (16 bytes) + ImGui.Text($"7. Texture Name: {stage.TextureName}"); + ImGui.SameLine(); + ImGui.TextDisabled("(16 bytes, ASCII)"); + + ImGui.TreePop(); + } + } + } + + if (ImGui.CollapsingHeader($"4. Animations ({mat.Animations.Count})")) + { + for (int i = 0; i < mat.Animations.Count; i++) + { + var anim = mat.Animations[i]; + if (ImGui.TreeNode($"Anim {i}: {anim.Target} ({anim.LoopMode})")) + { + ImGui.TextDisabled("=== File Read Order ==="); + + // Combined field (4 bytes) + ImGui.Text("1. Target & Loop Mode:"); + ImGui.SameLine(); + ImGui.TextDisabled("(4 bytes combined)"); + + ImGui.Indent(); + ImGui.Text($"Target (bits 3-31): {anim.Target}"); + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(anim.TargetDescription); + } + + ImGui.Text($"Loop Mode (bits 0-2): {anim.LoopMode}"); + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip(GetAnimationLoopModeDescription(anim.LoopMode)); + } + ImGui.Unindent(); + + // Key count (2 bytes) + ImGui.Text($"2. Key Count: {anim.Keys.Count}"); + ImGui.SameLine(); + ImGui.TextDisabled("(2 bytes, ushort)"); + + // Keys (6 bytes each) + ImGui.Text($"3. Keys ({anim.Keys.Count} × 6 bytes):"); + if (ImGui.BeginTable($"keys_{i}", 4, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg)) + { + ImGui.TableSetupColumn("#"); + ImGui.TableSetupColumn("Stage Index (2 bytes)"); + ImGui.TableSetupColumn("Duration ms (2 bytes)"); + ImGui.TableSetupColumn("Interpolation (2 bytes)"); + ImGui.TableHeadersRow(); + + int keyIdx = 0; + foreach (var key in anim.Keys) + { + ImGui.TableNextRow(); + ImGui.TableNextColumn(); + ImGui.Text(keyIdx.ToString()); + ImGui.TableNextColumn(); + ImGui.Text(key.StageIndex.ToString()); + ImGui.TableNextColumn(); + ImGui.Text(key.DurationMs.ToString()); + ImGui.TableNextColumn(); + ImGui.Text(key.InterpolationCurve.ToString()); + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip("Always 0 = linear interpolation"); + } + keyIdx++; + } + ImGui.EndTable(); + } + ImGui.TreePop(); + } + } + } + } + } + ImGui.End(); + } + + private static string GetBlendModeDescription(BlendMode mode) + { + return mode switch + { + BlendMode.Zero => "D3DBLEND_ZERO: Blend factor is (0, 0, 0, 0)", + BlendMode.One => "D3DBLEND_ONE: Blend factor is (1, 1, 1, 1)", + BlendMode.SrcColor => "D3DBLEND_SRCCOLOR: Blend factor is (Rs, Gs, Bs, As)", + BlendMode.InvSrcColor => "D3DBLEND_INVSRCCOLOR: Blend factor is (1-Rs, 1-Gs, 1-Bs, 1-As)", + BlendMode.SrcAlpha => "D3DBLEND_SRCALPHA: Blend factor is (As, As, As, As)", + BlendMode.InvSrcAlpha => "D3DBLEND_INVSRCALPHA: Blend factor is (1-As, 1-As, 1-As, 1-As)", + BlendMode.DestAlpha => "D3DBLEND_DESTALPHA: Blend factor is (Ad, Ad, Ad, Ad)", + BlendMode.InvDestAlpha => "D3DBLEND_INVDESTALPHA: Blend factor is (1-Ad, 1-Ad, 1-Ad, 1-Ad)", + BlendMode.DestColor => "D3DBLEND_DESTCOLOR: Blend factor is (Rd, Gd, Bd, Ad)", + BlendMode.InvDestColor => "D3DBLEND_INVDESTCOLOR: Blend factor is (1-Rd, 1-Gd, 1-Bd, 1-Ad)", + BlendMode.SrcAlphaSat => "D3DBLEND_SRCALPHASAT: Blend factor is (f, f, f, 1); f = min(As, 1-Ad)", + BlendMode.BothSrcAlpha => "D3DBLEND_BOTHSRCALPHA: Source blend factor is (As, As, As, As) and destination blend factor is (1-As, 1-As, 1-As, 1-As)", + BlendMode.BothInvSrcAlpha => "D3DBLEND_BOTHINVSRCALPHA: Source blend factor is (1-As, 1-As, 1-As, 1-As) and destination blend factor is (As, As, As, As)", + BlendMode.Unknown => "Unknown/Default (0xFF): No blending or opaque", + _ => "Unknown blend mode" + }; + } + + private static string GetAnimationTargetDescription(AnimationTarget target) + { + // NOTE: This method is now only used for fallback/debugging + // The actual UI uses MaterialAnimation.TargetDescription which is precomputed during parsing + return $"Target flags: 0x{(int)target:X}"; + } + + private static string GetAnimationLoopModeDescription(AnimationLoopMode mode) + { + return mode switch + { + AnimationLoopMode.Loop => "Loop: Animation repeats continuously from start to end", + AnimationLoopMode.PingPong => "PingPong: Animation plays forward then backward repeatedly", + AnimationLoopMode.Clamp => "Clamp: Animation plays once and holds at the final value", + AnimationLoopMode.Random => "Random: Animation selects random keyframes", + _ => "Unknown loop mode" + }; + } +} diff --git a/NResUI/ImGuiUI/NResExplorerPanel.cs b/NResUI/ImGuiUI/NResExplorerPanel.cs index d526860..b9d38ee 100644 --- a/NResUI/ImGuiUI/NResExplorerPanel.cs +++ b/NResUI/ImGuiUI/NResExplorerPanel.cs @@ -18,9 +18,10 @@ public class NResExplorerPanel : IImGuiPanel private readonly CpDatSchemeViewModel _cpDatSchemeViewModel; private readonly MissionTmaViewModel _missionTmaViewModel; private readonly ScrViewModel _scrViewModel; + private readonly MaterialViewModel _materialViewModel; public NResExplorerPanel(NResExplorerViewModel viewModel, TexmExplorerViewModel texmExplorerViewModel, - VarsetViewModel varsetViewModel, CpDatSchemeViewModel cpDatSchemeViewModel, MissionTmaViewModel missionTmaViewModel, ScrViewModel scrViewModel) + VarsetViewModel varsetViewModel, CpDatSchemeViewModel cpDatSchemeViewModel, MissionTmaViewModel missionTmaViewModel, ScrViewModel scrViewModel, MaterialViewModel materialViewModel) { _viewModel = viewModel; _texmExplorerViewModel = texmExplorerViewModel; @@ -28,6 +29,7 @@ public class NResExplorerPanel : IImGuiPanel _cpDatSchemeViewModel = cpDatSchemeViewModel; _missionTmaViewModel = missionTmaViewModel; _scrViewModel = scrViewModel; + _materialViewModel = materialViewModel; } int contextMenuRow = -1; @@ -272,6 +274,28 @@ public class NResExplorerPanel : IImGuiPanel _scrViewModel.SetParseResult(parseResult, Path.Combine(_viewModel.Path!, file.FileName)); Console.WriteLine("Read .scr from context menu"); } + + if (ImGui.MenuItem("Open as Material")) + { + using var fs = new FileStream(_viewModel.Path!, FileMode.Open, FileAccess.Read, FileShare.Read); + fs.Seek(file.OffsetInFile, SeekOrigin.Begin); + + var buffer = new byte[file.FileLength]; + fs.ReadExactly(buffer, 0, file.FileLength); + using var ms = new MemoryStream(buffer); + + try + { + var parseResult = MaterialLib.MaterialParser.ReadFromStream(ms, file.FileName, (int)file.ElementCount, file.Magic1); + _materialViewModel.SetParseResult(parseResult, Path.Combine(_viewModel.Path!, file.FileName)); + Console.WriteLine("Read Material from context menu"); + } + catch (Exception ex) + { + Console.WriteLine($"Error reading material: {ex}"); + _materialViewModel.SetError(ex.Message); + } + } } ImGui.EndPopup(); diff --git a/NResUI/Models/MaterialViewModel.cs b/NResUI/Models/MaterialViewModel.cs new file mode 100644 index 0000000..b6316ac --- /dev/null +++ b/NResUI/Models/MaterialViewModel.cs @@ -0,0 +1,26 @@ +using MaterialLib; + +namespace NResUI.Models; + +public class MaterialViewModel +{ + public MaterialFile? MaterialFile { get; private set; } + public string? FilePath { get; private set; } + public string? Error { get; private set; } + + public bool HasFile => MaterialFile != null; + + public void SetParseResult(MaterialFile materialFile, string filePath) + { + MaterialFile = materialFile; + FilePath = filePath; + Error = null; + } + + public void SetError(string error) + { + MaterialFile = null; + FilePath = null; + Error = error; + } +} diff --git a/NResUI/NResUI.csproj b/NResUI/NResUI.csproj index 09340c6..5728421 100644 --- a/NResUI/NResUI.csproj +++ b/NResUI/NResUI.csproj @@ -23,6 +23,7 @@ + diff --git a/ParkanPlayground.slnx b/ParkanPlayground.slnx index 370f813..0ffc6ee 100644 --- a/ParkanPlayground.slnx +++ b/ParkanPlayground.slnx @@ -6,6 +6,7 @@ + diff --git a/ParkanPlayground/Msh02.cs b/ParkanPlayground/Msh02.cs index a68b58e..48e502b 100644 --- a/ParkanPlayground/Msh02.cs +++ b/ParkanPlayground/Msh02.cs @@ -185,9 +185,9 @@ public static class Msh02 public Vector3 BottomFrontRight { get; set; } public Vector3 BottomBackRight { get; set; } public Vector3 BottomBackLeft { get; set; } - public Vector3 TopFrontLeft { get; set; } - public Vector3 TopFrontRight { get; set; } public Vector3 TopBackRight { get; set; } + public Vector3 TopFrontRight { get; set; } public Vector3 TopBackLeft { get; set; } + public Vector3 TopFrontLeft { get; set; } } } \ No newline at end of file diff --git a/ParkanPlayground/ParkanPlayground.csproj b/ParkanPlayground/ParkanPlayground.csproj index ed750c5..8dc9cf8 100644 --- a/ParkanPlayground/ParkanPlayground.csproj +++ b/ParkanPlayground/ParkanPlayground.csproj @@ -10,6 +10,7 @@ + diff --git a/ParkanPlayground/Program.cs b/ParkanPlayground/Program.cs deleted file mode 100644 index 7449e01..0000000 --- a/ParkanPlayground/Program.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Buffers.Binary; -using Common; -using MissionTmaLib.Parsing; -using NResLib; -using PalLib; -using ParkanPlayground; -using VarsetLib; - -// var cpDatEntryConverter = new CpDatEntryConverter(); -// cpDatEntryConverter.Convert(); - -// var converter = new MshConverter(); - -// converter.Convert("E:\\ParkanUnpacked\\fortif.rlb\\133_fr_m_bunker.msh"); -// converter.Convert("C:\\Program Files (x86)\\Nikita\\Iron Strategy\\DATA\\MAPS\\SC_1\\Land.msh"); -// converter.Convert("E:\\ParkanUnpacked\\fortif.rlb\\73_fr_m_brige.msh"); -// converter.Convert("E:\\ParkanUnpacked\\intsys.rlb\\277_MESH_o_pws_l_01.msh"); -// converter.Convert("E:\\ParkanUnpacked\\static.rlb\\2_MESH_s_stn_0_01.msh"); -// converter.Convert("E:\\ParkanUnpacked\\bases.rlb\\25_MESH_R_H_02.msh"); - -var fs = new FileStream("E:\\ParkanUnpacked\\sys.lib\\1_Palm_PAL.PAL", FileMode.Open, - FileAccess.Read, FileShare.Read); - -var palFile = PalParser.ReadFromStream(fs, "1_Palm_PAL.PAL"); - -palFile.SaveAsPng("pal.png"); - -// foreach (var path in Directory.EnumerateFiles("E:\\ParkanUnpacked\\behpsp.res")) -// { -// using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); -// -// var file = BinaryVarsetFileParser.Parse(fs); -// -// _ = 5; -// } -// -// { -// var fs = new FileStream("E:\\ParkanUnpacked\\behpsp.res\\31_00 00 00 00_prof_generator.var", FileMode.Open, -// FileAccess.Read, FileShare.Read); -// -// var file = BinaryVarsetFileParser.Parse(fs); -// -// _ = 5; -// } \ No newline at end of file diff --git a/README.md b/README.md index aa793d0..7f5a3cf 100644 --- a/README.md +++ b/README.md @@ -187,18 +187,20 @@ IComponent ** LoadSomething(undefined4, undefined4, undefined4, undefined4) На основе файла `Comp.ini` и первом вызове внутри функции `World3D.dll/CreateObject` ремаппинг id: -| Class ID | ClassName | Function | -|:----------:|:-------------:|--------------------------------| -| 1 | Landscape | `terrain.dll LoadLandscape` | -| 2 | Agent | `animesh.dll LoadAgent` | -| 3 | Building | `terrain.dll LoadBuilding` | -| 4 | Agent | `animesh.dll LoadAgent` | -| 5 | Camera | `terrain.dll LoadCamera` | -| 7 | Atmospehere | `terrain.dll CreateAtmosphere` | -| 9 | Agent | `animesh.dll LoadAgent` | -| 10 | Agent | `animesh.dll LoadAgent` | -| 11 | Research | `misload.dll LoadResearch` | -| 12 | Agent | `animesh.dll LoadAgent` | + + +| Logic ID | ClassName | Function | +|:--------:|:------------:|--------------------------------| +| 1 | Landscape | `terrain.dll LoadLandscape` | +| 2 | Agent | `animesh.dll LoadAgent` | +| 3 | Building | `terrain.dll LoadBuilding` | +| 4 | Agent | `animesh.dll LoadAgent` | +| 5 | Camera | `terrain.dll LoadCamera` | +| 7 | Atmosphere | `terrain.dll CreateAtmosphere` | +| 9 | Agent | `animesh.dll LoadAgent` | +| 10 | Agent | `animesh.dll LoadAgent` | +| 11 | Research | `misload.dll LoadResearch` | +| 12 | Agent | `animesh.dll LoadAgent` | Будет дополняться по мере реверса. @@ -366,7 +368,7 @@ color - `4` - IShader - `5` - ITerrain - `6` - IGameObject -- `7` - ISettings скорее всего +- `7` - ISettings - `8` - ICamera - `9` - IQueue - `10` - IControl @@ -388,14 +390,14 @@ color - `0x20` - IJointMesh - `0x21` - IShadowProcessor (придумал сам implemented by CShade) - `0x22` - unknown (implement by CLandscape) -- `0x23` - IGameSettings +- `0x23` - IGameSettingsRoot - `0x24` - IGameObject2 - `0x25` - unknown (implemented by CAniMesh) -- `0x26` - unknown (implemented by CAniMesh and CControl) +- `0x26` - unknown (implemented by CAniMesh and CControl and CWizard) - `0x28` - ICollObject - `0x29` - IPhysicalModel - `0x101` - I3DRender -- `0x102` - ITexture +- `0x102` - ITexture (writable) - `0x103` - IColorLookup - `0x104` - IBitmapFont - `0x105` - INResFile diff --git a/VarsetLib/BinaryVarsetItem.cs b/VarsetLib/BinaryVarsetItem.cs index 7d41cbf..f15da9c 100644 --- a/VarsetLib/BinaryVarsetItem.cs +++ b/VarsetLib/BinaryVarsetItem.cs @@ -26,6 +26,7 @@ public record BinaryVarsetItem( public enum BinaryVarsetValueType { + Float0 = 0, Int = 1, Bool = 2, Float = 3, diff --git a/material_field_analysis.csv b/material_field_analysis.csv new file mode 100644 index 0000000..1556010 --- /dev/null +++ b/material_field_analysis.csv @@ -0,0 +1,629 @@ +FileName,MaterialTypeMaybe,IsTerrainMaybe,SupportsTextureMode6,GlobalTransparencyMaybe,GlobalSelfIlluminationMaybe,StageCount,AnimCount,HasField18NonZero +B_PG3,0,0,True,1,000,0,000,1,1,True +B_PG4,0,0,True,1,000,0,000,1,1,True +B_PG6,0,0,True,1,000,0,000,1,1,True +B_PG2,1,0,False,1,000,0,000,1,1,True +B_PG5,0,0,True,1,000,0,000,1,1,True +B_GEN_05,0,0,True,1,000,0,000,1,1,True +B_GEN_06,0,0,True,1,000,0,000,1,1,True +B_GEN_07,0,0,True,1,000,0,000,1,1,True +B_GEN_08,0,0,True,1,000,0,000,1,1,True +B_DD1CL,0,0,True,1,000,0,000,8,1,True +B_DD1DK,0,0,True,1,000,0,000,1,1,True +B_DD2,0,0,True,1,000,0,000,1,1,True +B_RL_06,0,0,True,1,000,0,000,1,1,True +B_RL_02,0,0,True,1,000,0,000,1,1,True +B_RL_03,0,0,True,1,000,0,000,1,1,True +B_RL_19,0,0,True,1,000,0,000,1,1,True +B_RL_20,0,0,True,1,000,0,000,1,1,True +B_RL_21,0,0,True,1,000,0,000,1,1,True +B_S12,0,0,True,1,000,0,000,1,1,True +B_S0,0,0,True,1,000,0,000,1,1,True +B_S0A1,0,0,True,1,000,0,000,1,1,True +B_S1,0,0,True,1,000,0,000,1,1,True +B_S10,0,0,True,1,000,0,000,1,1,True +B_S2,0,0,True,1,000,0,000,1,1,True +B_S13,0,0,True,1,000,0,000,4,1,True +B_S14,0,0,True,1,000,0,000,1,1,True +B_S6,0,0,True,1,000,0,000,1,1,True +B_S3,0,0,True,1,000,0,000,1,1,True +B_S4,0,0,True,1,000,0,000,1,1,True +B_S5,1,0,False,1,000,0,000,1,1,True +B_S7,0,0,True,1,000,0,000,1,1,True +B_S0A2,0,0,True,1,000,0,000,1,1,True +R_PG3,0,0,True,1,000,0,000,1,1,True +R_PG4,0,0,True,1,000,0,000,1,1,True +R_PG6,0,0,True,1,000,0,000,1,1,True +R_PG7,0,0,True,1,000,0,000,1,1,True +R_PG2,1,0,False,1,000,0,000,1,1,True +R_PG5,0,0,True,1,000,0,000,1,1,True +R_GEN_05,0,0,True,1,000,0,000,1,1,True +R_GEN_06,0,0,True,1,000,0,000,1,1,True +R_GEN_07,0,0,True,1,000,0,000,1,1,True +R_GEN_08,0,0,True,1,000,0,000,1,1,True +R_DD1,0,0,True,1,000,0,000,1,1,True +R_DD1CL,0,0,True,1,000,0,000,1,1,True +R_DD1DK,0,0,True,1,000,0,000,1,1,True +R_DD2,0,0,True,1,000,0,000,1,1,True +R_RL_06,0,0,True,1,000,0,000,1,1,True +R_RL_02,0,0,True,1,000,0,000,1,1,True +R_RL_03,0,0,True,1,000,0,000,1,1,True +R_RL_20,0,0,True,1,000,0,000,1,1,True +R_RL_21,0,0,True,1,000,0,000,1,1,True +R_S12,0,0,True,1,000,0,000,1,1,True +R_S0,0,0,True,1,000,0,000,1,1,True +R_S0A1,0,0,True,1,000,0,000,1,1,True +R_S1,0,0,True,1,000,0,000,1,1,True +R_S10,0,0,True,1,000,0,000,1,1,True +R_S2,0,0,True,1,000,0,000,1,1,True +R_S6,0,0,True,1,000,0,000,1,1,True +R_S3,0,0,True,1,000,0,000,1,1,True +R_S4,0,0,True,1,000,0,000,1,1,True +R_S5,1,0,False,1,000,0,000,1,1,True +R_S7,0,0,True,1,000,0,000,1,1,True +R_S0A2,0,0,True,1,000,0,000,1,1,True +R_S13,0,0,True,1,000,0,000,4,1,True +R_S14,0,0,True,1,000,0,000,1,1,True +SKY1,1,0,False,1,000,0,000,1,1,True +B_RLW_4,0,0,True,1,000,0,000,1,1,True +R_RLW_4,0,0,True,1,000,0,000,1,1,True +DEFAULT,1,0,False,1,000,0,000,1,1,True +EXPL1,1,0,False,1,000,0,000,16,1,True +JET1,2,8,False,1,000,0,000,6,1,True +JET2,2,8,False,1,000,0,000,4,1,True +SHOOT1,1,0,False,1,000,0,000,4,1,True +JET3,2,8,False,1,000,0,000,1,1,True +SUN1,1,0,False,1,000,0,000,1,1,True +JET4,2,8,False,1,000,0,000,4,1,True +CLOUD1,1,0,False,1,000,0,000,15,1,True +SPLASH1,1,0,False,1,000,0,000,5,1,True +TRACE1,1,0,False,1,000,0,000,4,1,True +TRACE2,1,0,False,1,000,0,000,4,1,True +B_COMP_2,0,0,True,1,000,0,000,1,1,True +R_COMP_2,0,0,True,1,000,0,000,1,1,True +EXPL2,1,0,False,1,000,0,000,16,1,True +JET5,2,8,False,1,000,0,000,1,1,True +B_COMP_1,0,0,True,1,000,0,000,4,1,True +B_COMP_3,1,0,False,1,000,0,000,4,1,True +LIGHT1,2,8,False,1,000,0,000,1,1,True +BLACK,0,0,False,1,000,0,000,1,1,True +R_RLW_5,0,0,True,1,000,0,000,2,1,True +JET6,2,8,False,1,000,0,000,6,1,True +SHOOT2,1,0,False,1,000,0,000,4,1,True +JET7,2,8,False,1,000,0,000,4,1,True +SPLASH2,1,0,False,1,000,0,000,5,1,True +CLOUD2,1,0,False,1,000,0,000,15,1,True +R_MN_01,0,0,True,1,000,0,000,1,1,True +B_MN_01,0,0,True,1,000,0,000,1,1,True +B_MN_02,0,0,True,1,000,0,000,1,1,True +B_COMP_3G,1,0,False,1,000,0,000,4,1,True +B_COMP_3R,1,0,False,1,000,0,000,4,1,True +B_COMP_3B,1,0,False,1,000,0,000,4,1,True +JET8,2,8,False,1,000,0,000,6,1,True +JET9,2,8,False,1,000,0,000,4,1,True +B_MN_01R,0,0,True,1,000,0,000,4,1,True +B_MN_01G,0,0,True,1,000,0,000,4,1,True +B_MN_01B,0,0,True,1,000,0,000,4,1,True +STONE00,0,0,True,1,000,0,000,1,1,True +STONE01,0,0,True,1,000,0,000,1,1,True +TREE00,1,0,False,1,000,0,000,1,1,True +TREE01,0,0,False,1,000,0,000,1,1,True +TREE02,0,0,False,1,000,0,000,1,1,True +TREE03,1,0,False,1,000,0,000,1,1,True +TREE04,0,0,False,1,000,0,000,1,1,True +TREE05,0,0,False,1,000,0,000,1,1,True +R_RL_26,0,0,True,1,000,0,000,4,1,True +R_RL_26REV,0,0,True,1,000,0,000,4,1,True +R_RL_27,0,0,True,1,000,0,000,4,1,True +R_RL_27REV,0,0,True,1,000,0,000,4,1,True +R_RL_25,0,0,True,1,000,0,000,4,1,True +R_RL_25REV,0,0,True,1,000,0,000,4,1,True +R_RL_24,0,0,True,1,000,0,000,1,1,True +TREE06,1,0,False,1,000,0,000,1,1,True +TREE07,1,0,False,1,000,0,000,1,1,True +B_HNG_01,0,0,True,1,000,0,000,1,1,True +B_HNG_02,1,0,False,1,000,0,000,1,1,True +R_PG11,0,0,True,1,000,0,000,1,1,True +B_PG11,0,0,True,1,000,0,000,1,1,True +B_LIGHT,0,0,False,1,000,0,000,1,1,True +B_PG12,0,0,True,1,000,0,000,1,1,True +R_PG12,0,0,True,1,000,0,000,1,1,True +B_MTP_01,0,0,True,1,000,0,000,1,1,True +B_MTP_02,0,0,True,1,000,0,000,3,1,True +R_RL_19,0,0,True,1,000,0,000,1,1,True +B_RB_04,0,0,True,1,000,0,000,1,1,True +R_RB_04,0,0,True,1,000,0,000,1,1,True +R_RBW_03,0,0,True,1,000,0,000,1,1,True +DUST,1,0,False,1,000,0,000,4,1,True +SMOKE,1,0,False,1,000,0,000,4,1,True +FIRE_SMOKE,1,0,False,1,000,0,000,26,1,True +DARK_CLOUD,1,0,False,1,000,0,000,4,1,True +LASER_G,2,8,False,1,000,0,000,4,1,True +LASER_R,2,8,False,1,000,0,000,4,1,True +GLOW,1,0,False,1,000,0,000,1,1,True +LASER_Y,2,8,False,1,000,0,000,4,1,True +B_EFFECT_001,1,0,False,1,000,0,000,1,1,True +R_RMW_9,0,0,True,1,000,0,000,3,1,True +R_PG14,0,0,True,1,000,0,000,1,1,True +SHIELD1,2,8,False,1,000,0,000,7,1,True +B_LT_01,0,0,True,1,000,0,000,1,1,False +B_LT_03,0,0,True,1,000,0,000,1,1,True +B_LBL_01,0,0,True,1,000,0,000,8,8,True +CUTTER,1,0,False,1,000,0,000,4,1,True +GLOW_B,1,0,False,1,000,0,000,1,1,True +GLOW_G,1,0,False,1,000,0,000,1,1,True +GLOW_R,2,8,False,1,000,0,000,1,1,True +TREE08,1,0,False,1,000,0,000,1,1,True +TREE09,1,0,False,1,000,0,000,1,1,True +TREE10,1,0,False,1,000,0,000,1,1,True +TREE11,1,0,False,1,000,0,000,1,1,False +TREE12,1,0,False,1,000,0,000,1,1,True +TREE13,1,0,False,1,000,0,000,1,1,True +TREE14,1,0,False,1,000,0,000,1,1,True +TREE15,1,0,False,1,000,0,000,1,1,True +BALL_B,1,0,False,1,000,0,000,1,1,True +BALL_G,1,0,False,1,000,0,000,1,1,True +BALL_R,1,0,False,1,000,0,000,1,1,True +BALL_Y,1,0,False,1,000,0,000,1,1,True +BALL_W,1,0,False,1,000,0,000,1,1,True +GLOW_Y,1,0,False,1,000,0,000,1,1,True +GLOW_W,1,0,False,1,000,0,000,1,1,True +SPLASH3,1,0,False,1,000,0,000,5,1,True +LASER_B,2,8,False,1,000,0,000,4,1,True +LIGHT_W,1,0,False,1,000,0,000,1,1,True +SHOOT3,1,0,False,1,000,0,000,4,1,True +CUTTER_HIT,1,0,False,1,000,0,000,3,1,True +RAILGUN_HIT,2,8,False,1,000,0,000,3,1,True +EXPL3,1,0,False,1,000,0,000,24,1,True +R_PG9,0,0,True,1,000,0,000,1,1,True +BIRD_B,0,0,True,1,000,0,000,1,1,True +BIRD_T,0,0,True,1,000,0,000,1,1,True +BIRD_W,0,0,True,1,000,0,000,1,1,True +MATL,0,0,True,1,000,0,000,1,1,True +B_MTP_06,0,0,True,1,000,0,000,1,1,True +B_MTP_07,0,0,True,1,000,0,000,1,1,True +B_MTP_04,0,0,True,1,000,0,000,1,1,True +B_MTP_03,0,0,True,1,000,0,000,1,1,True +B_MTP_03R,0,0,True,1,000,0,000,1,1,True +B_MTP_03B,0,0,True,1,000,0,000,1,1,True +B_MTP_05,0,0,True,1,000,0,000,1,1,True +B_MTP_06B,0,0,True,1,000,0,000,1,1,True +B_MTP_06R,0,0,True,1,000,0,000,1,1,True +B_MTP_06G,0,0,True,1,000,0,000,1,1,True +B_MTP_06Y,0,0,True,1,000,0,000,1,1,True +B_MTP_07Y,0,0,True,1,000,0,000,1,1,True +B_MTP_07R,0,0,True,1,000,0,000,1,1,True +B_MTP_07G,0,0,True,1,000,0,000,1,1,True +B_MTP_07B,0,0,True,1,000,0,000,1,1,True +R_PG17,0,0,True,1,000,0,000,1,1,True +R_PG18,0,0,True,1,000,0,000,1,1,True +R_PG19,0,0,True,1,000,0,000,1,1,True +R_PG20,0,0,True,1,000,0,000,1,1,True +R_SLD_POD,0,0,True,1,000,0,000,1,1,True +R_SLD_GL,1,0,False,1,000,0,000,1,1,True +BIRD_U,0,0,True,1,000,0,000,1,1,True +SHIELD_R,2,8,False,1,000,0,000,10,1,True +SHIELD_G,2,8,False,1,000,0,000,10,1,True +SHIELD_B,2,8,False,1,000,0,000,10,1,True +B_MTP_07E,0,0,True,1,000,0,000,1,1,True +B_MTP_08,0,0,True,1,000,0,000,3,1,True +LASER_R_HIT,2,8,False,1,000,0,000,6,1,True +LASER_G_HIT,2,8,False,1,000,0,000,6,1,True +LASER_B_HIT,2,8,False,1,000,0,000,6,1,True +LASER_Y_HIT,2,8,False,1,000,0,000,6,1,True +B_P00,0,0,True,1,000,0,000,1,1,True +AIGRETTE_Y,1,0,False,1,000,0,000,3,1,True +AIGRETTE_R,1,0,False,1,000,0,000,3,1,True +AIGRETTE_G,1,0,False,1,000,0,000,3,1,True +AIGRETTE_B,1,0,False,1,000,0,000,3,1,True +B_LT_01R,0,0,True,1,000,0,000,1,1,False +B_LT_01G,0,0,True,1,000,0,000,1,1,False +B_LT_01B,0,0,True,1,000,0,000,1,1,False +B_LT_01Y,0,0,True,1,000,0,000,1,1,False +B_COMP_4,0,0,True,1,000,0,000,1,1,True +B_COMP_4R,0,0,True,1,000,0,000,1,1,True +B_COMP_4G,0,0,True,1,000,0,000,1,1,True +B_COMP_4B,0,0,True,1,000,0,000,1,1,True +B_COMP_4Y,0,0,True,1,000,0,000,1,1,True +B_LT_03B,0,0,True,1,000,0,000,1,1,True +B_LT_03R,0,0,True,1,000,0,000,1,1,True +B_LT_03G,0,0,True,1,000,0,000,1,1,True +B_LT_03Y,0,0,True,1,000,0,000,1,1,True +B_MN_03,0,0,True,1,000,0,000,1,1,True +B_MN_03R,0,0,True,1,000,0,000,1,1,True +B_MN_03G,0,0,True,1,000,0,000,1,1,True +B_MN_03B,0,0,True,1,000,0,000,1,1,True +B_MN_03Y,0,0,True,1,000,0,000,1,1,True +B_LT_04,0,0,True,1,000,0,000,1,1,True +CLOUD3,1,0,False,1,000,0,000,15,1,True +B_GEN_09,0,0,True,1,000,0,000,1,1,True +R_GEN_09,0,0,True,1,000,0,000,1,1,True +B_GEN_08L,0,0,True,1,000,0,000,1,1,True +B_S3L,0,0,True,1,000,0,000,1,1,True +B_S4L,0,0,True,1,000,0,000,1,1,True +PORTAL_001,1,0,False,1,000,0,000,2,1,False +PORTAL_004,1,0,False,1,000,0,000,2,1,True +B_GEN_10,0,0,True,1,000,0,000,1,1,True +B_GEN_11,0,0,True,1,000,0,000,1,1,True +HWOY01,1,0,False,1,000,0,000,1,1,True +FTREE1,1,0,False,1,000,0,000,1,1,True +HTREE1,1,0,False,1,000,0,000,1,1,True +KORA,0,0,True,1,000,0,000,1,1,True +KORA2,0,0,True,1,000,0,000,1,1,True +SKIN02,0,0,True,1,000,0,000,1,1,True +B_LT_02,0,0,True,1,000,0,000,1,1,True +EXPL4,1,0,False,1,000,0,000,29,1,True +SHOOT4,1,0,False,1,000,0,000,4,1,True +SPLASH4,1,0,False,1,000,0,000,6,1,True +EXPL5,1,0,False,1,000,0,000,4,1,True +EXPL6,1,0,False,1,000,0,000,8,1,True +SPLASH5,1,0,False,1,000,0,000,6,1,True +SPLASH6,1,0,False,1,000,0,000,6,1,True +B_MTP_02L,0,0,True,1,000,0,000,3,1,True +B_MTP_01L,0,0,True,1,000,0,000,1,1,True +B_GEN_05L,0,0,True,1,000,0,000,1,1,True +B_P08,1,0,False,1,000,0,000,2,1,True +B_P10,1,0,False,1,000,0,000,2,1,True +B_P11,1,0,False,1,000,0,000,2,1,True +B_P13,1,0,False,1,000,0,000,2,1,True +LAMP,1,0,False,1,000,0,000,1,1,True +LASER_W_HIT,2,8,False,1,000,0,000,6,1,True +SPITTLE_R,1,0,False,1,000,0,000,16,1,True +SPITTLE_G,1,0,False,1,000,0,000,16,1,True +SHOOT5,1,0,False,1,000,0,000,4,1,True +ALIEN_BALL,1,0,False,1,000,0,000,1,1,True +ALIENSKIN,0,0,True,1,000,0,000,1,1,True +CLOUD_G,1,0,False,1,000,0,000,15,1,True +CLOUD_R,1,0,False,1,000,0,000,15,1,True +CLOUD_B,1,0,False,1,000,0,000,15,1,True +BUILD_SPHERE,1,0,False,1,000,0,000,4,1,True +BUILD_MIRACLE,1,0,False,1,000,0,000,4,1,True +BUILD_GALO,1,0,False,1,000,0,000,4,1,True +SHIELD_Y,2,8,False,1,000,0,000,10,1,True +PLAS_BUL,2,8,False,1,000,0,000,4,1,True +PLAS_SHOOT,2,8,False,1,000,0,000,6,1,True +GLOW_V,1,0,False,1,000,0,000,1,1,True +LASER_V,2,8,False,1,000,0,000,4,1,True +LASER_V_HIT,2,8,False,1,000,0,000,6,1,True +ENV_MOON,1,0,False,1,000,0,000,1,1,True +ENV_STARS,1,0,False,1,000,0,000,1,1,True +ENV_FLARE_00,2,8,False,1,000,0,000,1,1,True +ENV_FLARE_01,2,8,False,1,000,0,000,1,1,True +SHOOK,1,0,False,1,000,0,000,4,1,True +SHOOK_B,1,0,False,1,000,0,000,4,1,True +ENV_CLOUDS,1,0,False,1,000,0,000,1,1,True +BUILD_SHOOT,1,0,False,1,000,0,000,1,1,True +BUILD_FLY,1,0,False,1,000,0,000,8,1,True +ENV_SUN,1,0,False,1,000,0,000,1,1,False +FIRE_SMOKE_W,1,0,False,1,000,0,000,23,1,True +B_MN_01Y,0,0,True,1,000,0,000,4,1,True +R_LBL_01,0,0,True,1,000,0,000,8,8,True +TREE16,1,0,False,1,000,0,000,1,1,True +B_RBW_3,0,0,True,1,000,0,000,1,1,True +SNOWFLAKE,1,0,False,1,000,0,000,1,1,True +FIRESTORM,1,0,False,1,000,0,000,8,1,True +RAIN_DROP,1,0,False,1,000,0,000,1,1,True +B_TELEPORT,1,0,False,1,000,0,000,3,1,True +B_TELEPORT2,1,0,False,1,000,0,000,2,1,True +WATER,0,0,False,1,000,0,000,1,1,True +WATER_DROP,1,0,False,1,000,0,000,6,1,True +WATER_DROP_R,1,0,False,1,000,0,000,6,1,True +WATER_DROP_G,1,0,False,1,000,0,000,6,1,True +WATER_DROP_B,1,0,False,1,000,0,000,6,1,True +WATER_M,0,0,False,1,000,0,000,10,1,True +B_MTP_04G,0,0,True,1,000,0,000,1,1,True +TASER_SHOOT,1,0,False,1,000,0,000,6,1,True +SHOOK2,1,0,False,1,000,0,000,4,1,True +SHOOK2_B,1,0,False,1,000,0,000,4,1,True +TASER_HIT,1,0,False,1,000,0,000,6,1,True +B_MTP_07BT,0,0,True,1,000,0,000,3,1,True +B_LT_04_PG7,0,0,True,1,000,0,000,1,1,True +B_LT_04R_PG7,0,0,True,1,000,0,000,1,1,True +B_LT_04G_PG7,0,0,True,1,000,0,000,1,1,True +B_LT_04B_PG7,0,0,True,1,000,0,000,1,1,True +B_LT_04Y_PG7,0,0,True,1,000,0,000,1,1,True +B_RB_04L,0,0,True,1,000,0,000,1,1,True +B_PG4L,0,0,True,1,000,0,000,1,1,True +B_S6L,0,0,True,1,000,0,000,1,1,True +B_PG11L,0,0,True,1,000,0,000,1,1,True +B_RBW_3L,0,0,True,1,000,0,000,1,1,True +B_RL_06L,0,0,True,1,000,0,000,1,1,True +B_DD1DK_DAM,0,0,True,1,000,1000,000,1,1,True +B_S0_DAM,0,0,True,1,000,1000,000,1,1,True +B_PLT_01,0,0,True,1,000,0,000,1,1,True +B_PLT_02,0,0,True,1,000,0,000,1,1,True +B_PLT_03,0,0,True,1,000,0,000,1,1,True +B_PLT_05,0,0,True,1,000,0,000,1,1,False +B_PLT_06,0,0,True,1,000,0,000,1,1,True +B_PLT_04,0,0,True,1,000,0,000,2,1,True +ENV_LAVA,1,0,False,1,000,0,000,1,1,True +ENV_LAVA_M,1,0,False,1,000,0,000,4,1,True +DARK_SMOKE,1,0,False,1,000,0,000,17,1,True +ENV_CLOUDS_2,1,0,False,1,000,0,000,1,1,True +ENV_SUN_2,1,0,False,1,000,0,000,1,1,True +ENV_FLARE_01_2,2,8,False,1,000,0,000,1,1,True +ENV_FLARE_00_2,2,8,False,1,000,0,000,1,1,True +ENV_NEBULA_2,1,0,False,1,000,0,000,1,1,True +ENV_MOON_2,1,0,False,1,000,0,000,1,1,True +ENV_CLOUDS_3,1,0,False,1,000,0,000,1,1,True +ENV_CLOUDS_4,1,0,False,1,000,0,000,1,1,True +FIRE_SMOKE_Y,1,0,False,1,000,0,000,23,1,True +APKORA,0,0,True,1,000,0,000,1,1,True +NTREE1,1,0,False,1,000,0,000,1,1,True +B_RU01,0,0,True,1,000,0,000,1,1,True +B_RU02,0,0,True,1,000,0,000,1,1,True +B_RU03,0,0,True,1,000,0,000,1,1,True +FIRE,1,0,False,1,000,0,000,23,1,True +PG24,1,0,False,1,000,0,000,1,1,True +PG25,1,0,False,1,000,0,000,1,1,True +PG26,0,0,True,1,000,0,000,1,1,True +ICE2,1,0,False,1,000,0,000,1,1,True +ENV_MOON_3,1,0,False,1,000,0,000,1,1,True +ENV_MOON_4,1,0,False,1,000,0,000,1,1,True +ENV_MOON_5,1,0,False,1,000,0,000,1,1,True +ENV_SUN_1,1,0,False,1,000,0,000,1,1,False +B_PLT_07,0,0,True,1,000,0,000,1,1,True +ENV_CLOUDS_5,1,0,False,1,000,0,000,1,1,True +MAT_DROP,1,0,False,1,000,0,000,1,1,True +R_TPG02,0,0,True,1,000,0,000,1,1,True +R_TPG01,0,0,True,1,000,0,000,1,1,True +R_LAUSE2,0,0,True,1,000,0,000,1,1,True +R_LAUSE,0,0,True,1,000,0,000,1,1,True +R_LAULEG1,0,0,True,1,000,0,000,1,1,True +ENV_CLOUDS_6,1,0,False,1,000,0,000,1,1,True +R_AIM,0,0,True,1,000,0,000,1,1,True +B_FOUND,1,0,False,1,000,0,000,1,1,True +ENV_NEBULA_0,1,0,False,1,000,0,000,1,1,True +ENV_NEBULA_1,1,0,False,1,000,0,000,1,1,True +ENV_NEBULA_3,1,0,False,1,000,0,000,1,1,True +ENV_SUN_6,1,0,False,1,000,0,000,1,1,True +ENV_SUN_3,1,0,False,1,000,0,000,1,1,False +ENV_SUN_4,1,0,False,1,000,0,000,1,1,True +ENV_SUN_5,1,0,False,1,000,0,000,1,1,True +B_PLT_06_NS,0,0,True,1,000,0,000,1,1,True +SNOWFLAKE_B,1,0,False,1,000,0,000,1,1,True +B_A_BRIGE,1,0,False,1,000,0,000,4,1,True +B_PLT_08,0,0,True,1,000,0,000,1,1,True +R_NP01,0,0,True,1,000,0,000,1,1,True +R_NP03,0,0,True,1,000,0,000,1,1,True +R_NP04,0,0,True,1,000,0,000,1,1,True +R_TB08,0,0,True,1,000,0,000,1,1,False +R_TB09,0,0,True,1,000,0,000,1,1,True +R_TB10,0,0,True,1,000,0,000,1,1,True +R_TB12,0,0,True,1,000,0,000,1,1,True +R_TB19,0,0,True,1,000,0,000,1,1,True +R_NP05,0,0,True,1,000,0,000,1,1,True +ENV_LIGHTNING,2,8,False,1,000,0,000,1,1,True +MINERALS,1,0,False,1,000,0,000,4,1,True +MINERAL_CIRCLE,1,0,False,1,000,0,000,1,1,True +MINERAL_RAY,1,0,False,1,000,0,000,4,1,True +R_NP06,0,0,True,1,000,0,000,1,1,True +R_BG_15,0,0,True,1,000,0,000,1,1,True +R_BG_10,0,0,True,1,000,0,000,1,1,False +R_BG_11,0,0,True,1,000,0,000,1,1,True +R_MG_01,0,0,True,1,000,0,000,1,1,True +R_MG_02,0,0,True,1,000,0,000,1,1,True +R_LG_01,0,0,True,1,000,0,000,1,1,True +R_LG_02,0,0,True,1,000,0,000,1,1,True +R_TG_01,0,0,True,1,000,0,000,1,1,True +R_TG_02,0,0,True,1,000,0,000,1,1,True +R_NP07,0,0,True,1,000,0,000,1,1,True +R_NP08,0,0,True,1,000,0,000,1,1,True +R_NP09,0,0,True,1,000,0,000,1,1,True +R_TB32,0,0,True,1,000,0,000,1,1,True +R_TB33,0,0,True,1,000,0,000,1,1,True +R_TB34,0,0,True,1,000,0,000,1,1,True +R_TB26,0,0,True,1,000,0,000,1,1,False +R_GB05,0,0,True,1,000,0,000,1,1,False +R_GB10,0,0,True,1,000,0,000,1,1,True +R_GB09,0,0,True,1,000,0,000,1,1,True +R_FL_PG,0,0,True,1,000,0,000,1,1,True +R_NP10,0,0,True,1,000,0,000,1,1,True +R_GB17,0,0,True,1,000,0,000,1,1,True +R_GB19,0,0,True,1,000,0,000,1,1,True +R_GB18,0,0,True,1,000,0,000,1,1,True +R_GB16,0,0,True,1,000,0,000,4,1,True +R_GB24,0,0,True,1,000,0,000,1,1,True +R_GB27,0,0,True,1,000,0,000,1,1,True +R_GB33,0,0,True,1,000,0,000,1,1,True +R_GB34,0,0,True,1,000,0,000,1,1,False +R_GB46,0,0,True,1,000,0,000,1,1,True +R_GB45,0,0,True,1,000,0,000,1,1,True +R_GB47,0,0,True,1,000,0,000,1,1,True +R_GB48,0,0,True,1,000,0,000,1,1,True +R_GB50,0,0,True,1,000,0,000,4,1,True +R_GB51,0,0,True,1,000,0,000,4,1,True +R_GB52,0,0,True,1,000,0,000,4,1,True +R_ENG_PG,0,0,True,1,000,0,000,1,1,True +R_NM_PG,0,0,True,1,000,0,000,1,1,True +R_NRS_PG,0,0,True,1,000,0,000,1,1,True +R_NSS_PG,0,0,True,1,000,0,000,1,1,True +L00,0,0,False,1,000,0,000,2,2,True +L01,0,0,False,1,000,0,000,2,2,True +L02,0,0,False,1,000,0,000,2,2,True +L03,0,0,False,1,000,0,000,2,2,True +L04,0,0,False,1,000,0,000,2,2,True +L05,0,0,False,1,000,0,000,2,2,True +L06,0,0,False,1,000,0,000,2,2,True +L07,0,0,False,1,000,0,000,2,2,True +L08,0,0,False,1,000,0,000,2,1,True +L09,0,0,False,1,000,0,000,2,2,True +L10,0,0,False,1,000,0,000,2,2,True +L11,0,0,False,1,000,0,000,2,2,True +L13,0,0,False,1,000,0,000,2,2,True +L14,0,0,False,1,000,0,000,2,2,True +L15,0,0,False,1,000,0,000,2,2,True +L16,0,0,False,1,000,0,000,2,2,True +L17,0,0,False,1,000,0,000,2,2,True +L18,0,0,False,1,000,0,000,2,2,True +L19,0,0,False,1,000,0,000,2,2,True +L20,0,0,False,1,000,0,000,2,2,True +L21,0,0,False,1,000,0,000,2,2,True +L22,0,0,False,1,000,0,000,2,2,True +L23,0,0,False,1,000,0,000,2,2,True +L24,0,0,False,1,000,0,000,2,2,True +L25,0,0,False,1,000,0,000,2,2,True +L26,0,0,False,1,000,0,000,2,2,True +L27,0,0,False,1,000,0,000,2,2,True +L28,0,0,False,1,000,0,000,2,2,True +L29,0,0,False,1,000,0,000,2,2,True +L30,0,0,False,1,000,0,000,2,2,True +L31,0,0,False,1,000,0,000,2,2,True +L32,0,0,False,1,000,0,000,2,1,True +L33,0,0,False,1,000,0,000,2,2,True +L34,0,0,False,1,000,0,000,2,2,True +L35,0,0,False,1,000,0,000,2,2,True +L36,0,0,False,1,000,0,000,2,2,True +L37,0,0,False,1,000,0,000,2,2,True +L38,0,0,False,1,000,0,000,2,2,True +L39,0,0,False,1,000,0,000,2,2,True +L40,0,0,False,1,000,0,000,2,2,True +L41,0,0,False,1,000,0,000,2,2,True +L42,0,0,False,1,000,0,000,2,2,True +L43,0,0,False,1,000,0,000,2,2,True +R_NP12,0,0,True,1,000,0,000,1,1,True +R_NP11,0,0,True,1,000,0,000,1,1,True +R_TM05,0,0,True,1,000,0,000,1,1,False +R_TM13,0,0,True,1,000,0,000,1,1,True +R_TM12,0,0,True,1,000,0,000,1,1,True +R_TT05,0,0,True,1,000,0,000,1,1,False +R_NTT05,0,0,True,1,000,0,000,4,1,True +R_NTT08,0,0,True,1,000,0,000,1,1,True +R_NSM_PG,0,0,True,1,000,0,000,1,1,True +R_NP13,0,0,True,1,000,0,000,1,1,True +R_NTT22,0,0,True,1,000,0,000,1,1,False +B_PG30,0,0,True,1,000,0,000,1,1,True +B_PG31,0,0,True,1,000,0,000,1,1,True +R_PG30,0,0,True,1,000,0,000,1,1,True +R_PG31,0,0,True,1,000,0,000,1,1,True +ELKA,1,0,False,1,000,0,000,1,1,False +TRCH02,0,0,True,1,000,0,000,1,1,False +GRASS,1,0,False,1,000,0,000,1,1,True +ICE01,1,0,False,1,000,0,000,1,1,True +LEAF01,1,0,False,1,000,0,000,1,1,False +SUNFLOWR,0,0,True,1,000,0,000,1,1,True +BAO01,1,0,False,1,000,0,000,1,1,True +DHORNO1,0,0,True,1,000,0,000,1,1,True +DHORN01,0,0,True,1,000,0,000,1,1,True +TRCH01,1,0,False,1,000,0,000,1,1,True +TRCH03,0,0,True,1,000,0,000,1,1,True +TRCH04,0,0,True,1,000,0,000,1,1,True +TRCH05,0,0,True,1,000,0,000,1,1,True +DEFL_B,1,0,False,1,000,0,000,4,1,True +ENV_WAVE,2,8,False,1,000,0,000,1,1,True +DEFL_G,1,0,False,1,000,0,000,4,1,True +SHOOK_BC,1,0,False,1,000,0,000,1,1,True +SMOKE_W,1,0,False,1,000,0,000,10,1,True +ENV_LAVA_BOT,0,0,False,1,000,10000,000,2,2,True +WATER_BOT,0,0,False,1,000,10000,000,2,2,True +ENV_WAVE_B,2,8,False,1,000,0,000,1,1,True +ENV_WAVE_G,2,8,False,1,000,0,000,1,1,True +HLP_RED_ME,0,0,True,1,000,0,000,1,1,True +B_COMP_1SI,0,0,True,1,000,0,000,4,1,True +HLP_GLOW_B,1,0,False,1,000,0,000,3,1,True +HLP_LAMP_B,0,0,True,1,000,0,000,3,1,True +HLP_PLACE_B,0,0,True,1,000,0,000,3,1,True +HLP_RAY_B,1,0,False,1,000,0,000,3,1,True +HLP_SIGN_B,1,0,False,1,000,0,000,3,1,True +ENV_WAVE_R,2,8,False,1,000,0,000,1,1,True +R_NRS_PGL,0,0,True,1,000,0,000,3,1,True +R_NM_RGL,0,0,True,1,000,0,000,3,1,True +R_PG_RGL,0,0,True,1,000,0,000,3,1,True +R_FL_RGL,0,0,True,1,000,0,000,3,1,True +DEFL_BL,2,8,False,1,000,0,000,4,1,True +CLOUD2L,2,8,False,1,000,0,000,15,1,True +ENV_NLAVA,1,0,False,1,000,0,000,8,1,True +ENV_NLAVA_M,1,0,False,1,000,0,000,8,1,True +GLOW_ENG,2,8,False,1,000,0,000,1,1,True +FIRE_ADD,2,8,False,1,000,0,000,23,1,True +SMOKE_ADD,2,8,False,1,000,0,000,4,1,True +OK_TEST1,0,0,True,1,000,0,000,1,1,True +SMOKE_Y,1,0,False,1,000,0,000,16,1,True +SMOKE_Y_ADD,2,8,False,1,000,0,000,16,1,True +DUST_Y,1,0,False,1,000,0,000,4,1,True +FIRE_SMOKE_G,1,0,False,1,000,0,000,23,1,True +FIRE_SMOKE_GADD,2,8,False,1,000,0,000,23,1,True +SMOKE_YG,1,0,False,1,000,0,000,16,1,True +SMOKE_YG_ADD,2,8,False,1,000,0,000,16,1,True +SMOKE_G,1,0,False,1,000,0,000,16,1,True +SMOKE_G_ADD,2,8,False,1,000,0,000,16,1,True +SPITTLE_G_ADD,2,8,False,1,000,0,000,16,1,True +WD_G_ADD,2,8,False,1,000,0,000,6,1,True +TOK41,1,0,False,1,000,0,000,1,1,True +DUST_ADD,2,8,False,1,000,0,000,4,1,True +TOK31,0,0,True,1,000,0,000,1,1,True +TOK31DARK,0,0,True,1,000,0,000,1,1,True +TOK32,1,0,False,1,000,0,000,4,1,True +TOK42,0,0,True,1,000,0,000,1,1,True +BUILD_SPHERE_A,1,0,False,1,000,0,000,4,1,True +LASER_RN,1,0,False,1,000,0,000,4,1,True +TOK43,0,0,True,1,000,0,000,1,1,True +TOK43DARK,0,0,True,1,000,0,000,1,1,True +TOK43_2,0,0,True,1,000,0,000,4,1,True +TOK43_2D,0,0,True,1,000,0,000,1,1,False +TOK43A,0,0,True,1,000,0,000,1,1,True +SMOKE_R,1,0,False,1,000,0,000,16,1,True +SMOKE_R_ADD,2,8,False,1,000,0,000,16,1,True +TOK41_RAY,2,8,False,1,000,0,000,8,1,True +SMOKE_GG,1,0,False,1,000,0,000,16,1,True +SMOKE_GG_ADD,2,8,False,1,000,0,000,16,1,True +SMOKE_W_ADD,2,8,False,1,000,0,000,10,1,True +TOK41_PLC,1,0,False,1,000,0,000,4,1,True +TOK33_BASE,2,8,False,1,000,0,000,4,1,True +TOK44_BASE,2,8,False,1,000,0,000,4,1,True +SMOKE_W1_ADD,2,8,False,1,000,0,000,10,1,True +TOK42W,0,0,True,1,000,0,000,1,1,True +TOK42_W4,0,0,True,1,000,0,000,3,1,True +TOK42_W1E,0,0,True,1,000,0,000,2,1,True +TOK42_W1M,0,0,True,1,000,0,000,2,1,True +TOK42_W5,0,0,True,1,000,0,000,2,1,True +SMOKE_R1_ADD,2,8,False,1,000,0,000,16,1,True +DUST_WORM_ADD,2,8,False,1,000,0,000,4,1,True +DUST_WORM,1,0,False,1,000,0,000,4,1,True +TOK11,0,0,True,1,000,0,000,1,1,True +TOK11_H,0,0,True,1,000,0,000,3,1,True +TOK11_Y,0,0,True,1,000,0,000,3,1,True +R_RLW_5L,0,0,True,1,000,0,000,1,1,True +PMAP_5,0,0,True,1,000,0,000,1,1,True +PMAP_42,0,0,True,1,000,0,000,1,1,False +PMAP_1,0,0,True,1,000,0,000,1,1,True +PMAP_2,0,0,True,1,000,0,000,1,1,True +PMAP_3,0,0,True,1,000,0,000,1,1,True +PMAP_4,0,0,True,1,000,0,000,1,1,True +PMAP_18,0,0,True,1,000,0,000,1,1,True +PMAP_31,0,0,True,1,000,0,000,1,1,False +PMAP_14,0,0,True,1,000,0,000,1,1,False +PMAP_15,0,0,True,1,000,0,000,1,1,True +PMAP_16,0,0,True,1,000,0,000,1,1,True +PMAP_17,0,0,True,1,000,0,000,1,1,True +HLP_GLOW_G,1,0,False,1,000,0,000,3,1,True +HLP_LAMP_G,0,0,True,1,000,0,000,3,1,True +HLP_PLACE_G,0,0,True,1,000,0,000,3,1,True +HLP_RAY_G,1,0,False,1,000,0,000,3,1,True +HLP_SIGN_G,1,0,False,1,000,0,000,3,1,True +HLP_GLOW_R,1,0,False,1,000,0,000,3,1,True +HLP_LAMP_R,0,0,True,1,000,0,000,3,1,True +HLP_PLACE_R,0,0,True,1,000,0,000,3,1,True +HLP_RAY_R,1,0,False,1,000,0,000,3,1,True +R_SRL_25,0,0,True,1,000,0,000,1,1,True +R_SRL_25REV,0,0,True,1,000,0,000,1,1,True +R_SRL_26,0,0,True,1,000,0,000,1,1,False +R_SRL_26REV,0,0,True,1,000,0,000,1,1,True +R_SRL_27,0,0,True,1,000,0,000,1,1,True +R_SRL_27REV,0,0,True,1,000,0,000,1,1,True +DEFL_Y_ADD,2,8,False,1,000,0,000,4,1,True +DEFL_Y1_ADD,2,8,False,1,000,0,000,4,1,True +TOK51,1,0,False,1,000,0,000,1,1,True +TF2,1,0,False,1,000,0,000,1,1,True +TF1,0,0,True,1,000,0,000,1,1,True +TF1_05,0,0,True,1,000,0,000,1,1,False +TF1_06A,0,0,True,1,000,0,000,4,1,True +TF1_BLACK,0,0,True,1,000,0,000,1,1,True +TF1_06,0,0,True,1,000,0,000,1,1,True +LASER_R_ADD,2,8,False,1,000,0,000,6,1,True +TOK76_UP,1,0,False,1,000,0,000,3,1,True +TOK76_DN,1,0,False,1,000,0,000,1,1,True +TOK41_RAYR,2,8,False,1,000,0,000,8,1,True +SMOKE_R2_ADD,2,8,False,1,000,0,000,10,1,True +JET_R,2,8,False,1,000,0,000,1,1,True +JET_G,2,8,False,1,000,0,000,1,1,True