diff --git a/README.md b/README.md index 336ade4..f1b61b3 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | OGG | `audio/ogg` | [`patterns/ogg.hexpat`](patterns/ogg.hexpat) | OGG Audio format | | PAK | | [`patterns/xgspak.hexpat`](patterns/xgspak.hexpat) | Exient XGS Engine Pak files | | PCAP | `application/vnd.tcpdump.pcap` | [`patterns/pcap.hexpat`](patterns/pcap.hexpat) | pcap header and packets | +| PCK | | [`patterns/pck.hexpat`](patterns/pck.hexpat) | Minecraft Legacy Console Edition .pck file | | PCX | `application/x-pcx` | [`patterns/pcx.hexpat`](patterns/pcx.hexpat) | PCX Image format | | PE | `application/x-dosexec` `application/x-msdownload` | [`patterns/pe.hexpat`](patterns/pe.hexpat) | PE header, COFF header, Standard COFF fields and Windows Specific fields | | PP | | [`patterns/selinuxpp.hexpat`](patterns/selinuxpp.pat) | SE Linux package | diff --git a/patterns/pck.hexpat b/patterns/pck.hexpat new file mode 100644 index 0000000..08b1510 --- /dev/null +++ b/patterns/pck.hexpat @@ -0,0 +1,187 @@ +#pragma author nullptr +#pragma description Minecraft LCE .pck files + +import std.io; +import std.array; +import std.core; +import std.sys; +import std.string; +import std.mem; +import type.size; + +bool littleEndian in; + +if (littleEndian) + std::core::set_endian(std::mem::Endian::Little); +else + std::core::set_endian(std::mem::Endian::Big); + +namespace format +{ + // TODO: find a way to access look up table differently + fn look_up_str(s32 index) + { + return pck.lut.table[index].value; + }; + + fn property(ref auto p) + { + return std::format("{}: {}", format::look_up_str(p.key), p.value); + }; + + fn assets(ref auto assets) + { + return std::format("Assets: {}", assets.count); + }; + + fn properties(ref auto p) + { + return std::format("count: {}", p.propertyCount); + }; + + fn string(ref auto string) + { + return std::string::to_string(string.value); + }; + + fn asset_entry(ref auto e) + { + return std::string::to_string(e.type); + }; +} + +namespace transform +{ + fn assets(ref auto asstes) + { + std::unimplemented(); + }; +} + +struct string +{ + std::string::SizedString16 value; + padding[4]; +} [[sealed, format("format::string"), transform("format::string")]]; + +struct LookUpTableValue +{ + s32 index; + string value; +} [[sealed, name(value), value(index)]]; + +struct LookUpTable +{ + s32 count; + LookUpTableValue table[count]; +} [[sealed]]; + +fn lutContains(ref LookUpTable lut, str value) +{ + for (s32 i = 0, i < lut.count, i = i + 1) + { + if (std::string::contains(lut.table[i].value, value)) + return true; + } + return false; +}; + +enum AssetType : s32 +{ + SkinFile = 0, // *.png + CapeFile = 1, // *.png + TextureFile = 2, // *.png + //! Unused ! + UIDataFile = 3, + //! "0" file + InfoFile = 4, + //! (x16|x32|x64)Info.pck + TexturePackInfoFile = 5, + //! languages.loc/localisation.loc + LocalisationFile = 6, + //! GameRules.grf + GameRulesFile = 7, + //! audio.pck + AudioFile = 8, + //! colours.col + ColourTableFile = 9, + //! GameRules.grh + GameRulesHeader = 10, + //! Skins.pck + SkinDataFile = 11, + //! models.bin + ModelsFile = 12, + //! behaviours.bin + BehavioursFile = 13, + //! entityMaterials.bin + MaterialFile = 14, +}; + +struct Property +{ + s32 key [[format("format::look_up_str")]]; + string value; +} [[format("format::property")]]; + +struct AssetProperties +{ + s32 propertyCount[[hidden]]; + Property properties[propertyCount] [[inline]]; +} [[inline, format("format::properties")]]; + +struct AssetEntry +{ + type::Size32 size; + AssetType type; + string path; +} [[sealed, name(path), format("format::asset_entry")]]; + +struct AssetVisualizer +{ + u8 data[size] [[hidden]]; +} [[sealed, hex::visualize(visualizer_name, data)]]; + +using AssetTexture = AssetVisualizer; + +using Pck; +struct Asset +{ + u64 index = std::core::array_index(); + AssetProperties properties; + s32 size = parent.assetEntries[index].size; + AssetType type = parent.assetEntries[index].type; + match (parent.assetEntries[index].type) + { + (AssetType::SkinFile | AssetType::CapeFile | AssetType::TextureFile): AssetTexture data; + (AssetType::TexturePackInfoFile | AssetType::SkinDataFile): Pck data; + (_): u8 data[size]; // hex::visualize("hex_viewer", data); causes crash ._. + } +} [[name(std::string::to_string(parent.assetEntries[index].path)), format("format::asset_entry")]]; + +struct Assets +{ + s32 count [[hidden]]; + AssetEntry assetEntries[count] [[hidden]]; + Asset assetData[count] [[inline]]; +} [[format("format::assets"), transform_entities("transform::assets")]]; + +struct Pck +{ + s32 version; + if (version >> 24 & 0xff != 0) + { + if (!littleEndian) + std::warning("pck is likely to be little endian. Enable 'littleEndian' in the 'settings' tab"); + else + std::warning("pck is likely to be big endian. Disable 'littleEndian' in the 'settings' tab"); + break; + } + LookUpTable lut [[hidden]]; + if (lutContains(lut, "XMLVERSION")) + { + s32 xmlVersion; + } + Assets assets; +}; + +Pck pck @ 0x00; \ No newline at end of file diff --git a/tests/patterns/test_data/pck.hexpat.pck b/tests/patterns/test_data/pck.hexpat.pck new file mode 100644 index 0000000..41f82a8 Binary files /dev/null and b/tests/patterns/test_data/pck.hexpat.pck differ