From 1d680fbf5e36f1554e9d7f8177030f8a411c5a0b Mon Sep 17 00:00:00 2001 From: Miku-666 <74728189+NessieHax@users.noreply.github.com> Date: Sun, 17 Nov 2024 13:59:20 +0100 Subject: [PATCH] patterns: Added Minecraft LCE .pck format pattern (#310) --- README.md | 1 + patterns/pck.hexpat | 187 ++++++++++++++++++++++++ tests/patterns/test_data/pck.hexpat.pck | Bin 0 -> 2772 bytes 3 files changed, 188 insertions(+) create mode 100644 patterns/pck.hexpat create mode 100644 tests/patterns/test_data/pck.hexpat.pck 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 0000000000000000000000000000000000000000..41f82a8d3afbb955ec262fcf91a506ab20a21a37 GIT binary patch literal 2772 zcmb7E2{_bS8$V-YnJh!H#}uVYF=G;y-4K}>;)akhn8H}HBoU+ADtqP9t`Y z*^)67LmEj$+ZdJTj(g+U@|{1`w>?<=_1tz#g~(cew8Ys9zakiwqLsvNN2QdUu0KYf~AQ~dV z#)2>q2?7BfVhD(V7z;5HL;%CDJBtC!)_#p!0fL}E5HgvN7XkH_J_KM4h+q?p+7G?^ zme^vg5RrPQ!h8%EV+7S1ARc}nSoHko8vpO{i?N6l1`vQL)WiA0GxZh~ z!1uKnz=VVcGvWHNm-h~&!9JLP51d0C!kMaybMy-ovG ze2wTfhAi`t+&W#8s~P^O`;yz$u+-nOeTTcpA6;G7(25s4^qj@9h=LssID|q;XK&Z_3@T`ft8X!S+0gw_a(?HasDY=KgnAj?__G- zb~jeThfN_X^W61wLmm0i1A>lmoZ<3-B=qrn#f<2IcbwQ#bozA5^oybMp@k*k4JOTC zA*PQngVyzPohmm;(RkIuXNO#fFoQ;lK^{V4@QvoiN;4<1V{#k(ENY7sSj zL20O~-Kf0AYIESS!vX_!?px*7nSCaAjG$wr6avo@Q5GY^26~yydPj!PRQg3a>WIK@ zR`Mh|{p=ZfpyI|SydJ?(4hJu9=_yv_aDp4m2=qgFCG`S zNm*5+HyV9+vLC$t(~e1fsga#4e{r3K*u0r?X@2jVp2T084O3du3SpQtPfXrKl6MJ{ z3@aXb-R7pOmnRI0sR_EbDv)}aewsM%H$gyTj;>Sf@IRV;uAE@~ppKsYyto(dQ9wGQ zqw7kT$WMD)fUERtH@?qb-znKDaaUd*pDFveB&lP$`z5cUXp+0k?;Nr7*Y;W0p(0aM z*5+7hS4OK{qh6Vb@+IUv>WEbQS+8Pe-H=4xdJEJ3{T zVfv9M#<=M83Q0x7FXvIKRw_puaJ)n8WLHBfoYA++yDJs`<8 z<-ZeGP-;?Eun`}c)$hLW;{KMWbCjo_HCL^Yotm7Cjf%RQl_f7ClgaPK#wbnxu9saM zFHyIbc{tA}(>k}5m6z)mb}<|-c{yNRZ&sY z@dqE1DoR+9%2_Qu@*;SXU~2aB%uML@g2FN^!M&u4ILf z{Sy;?j@#Pg<>j$N-er5YL_|gHe7)bx+1c6K$A_4q6rfM&Eb7+8JEUnwhrCmL*xY<{ zXMJrg(I}r_pS#1pC7}Dw