Santa Posted March 27, 2021 Posted March 27, 2021 Hello,Hoping someone might have some info regarding the newer conquer maps. Not sure what patch they show up in but looks like they have a new revision of their puzzle files. A dmap can either reference a traditional ".pul" file or this new ".pux" file. Was pretty straight forward grabbing the format of the file out of the client, but how it's all used is, so far, a mystery. For the most part it just allows for puzzle pieces to be defined in multiple ani files, instead of a single one. Another large difference is that the "puzzle grid" in a ".pul" file is simple an array of ID's, being the puzzle piece number, that is the size of the width*height. In a ".pux" file it is a little different. It is an array of objects the size of width*height. The object is a "PuzzleUnitData" consisting of an array of IDs and an unknown uint. uint puzzGridCnt = br.ReadUInt32(); wr.WriteLine("PuzzleUnitData Size: {0}", puzzGridCnt); for (int i = 0; i < puzzGridCnt; i++) { byte dataCnt = br.ReadByte(); if (dataCnt != 0) { for (int j = 0; j < dataCnt; j++) { ushort aniID = br.ReadUInt16(); uint unk2 = br.ReadUInt32(); wr.WriteLine("PuzzleUnitLayer({2} - {3}): {0}, {1:X8}", aniID, unk2, i, j); } } } I kind of get the feeling that they might be blending multiple textures together to get more reusability out of assets, but this is pure speculation at this point. I've not logged into the official servers to actually see if I can identify places on the map that corelate to my findings. Here is a sample of what I'm dumping out of the data grid portion of the ".pux" file. PuzzleUnitLayer(639 - 0): 280, 01800000 PuzzleUnitLayer(640 - 0): 280, 00300000 PuzzleUnitLayer(761 - 0): 241, 01000000 PuzzleUnitLayer(762 - 0): 241, 00700000 PuzzleUnitLayer(762 - 1): 241, 01800000 PuzzleUnitLayer(762 - 2): 280, 00004300 PuzzleUnitLayer(763 - 0): 241, 00010000 PuzzleUnitLayer(763 - 1): 242, 00620000 PuzzleUnitLayer(763 - 2): 241, 00100000 PuzzleUnitLayer(763 - 3): 280, 018C7DE0 PuzzleUnitLayer(764 - 0): 241, 00800000 PuzzleUnitLayer(764 - 1): 280, 017FFF18 PuzzleUnitLayer(764 - 2): 277, 00000200 looking at the first item, 639 is the index of the grid, 0 is the index of the unit data item, 280 is the puzzle ID, and 01800000 is an unknown. I'm taking a wild guess and thinking it has something to do with blending, perhaps a per channel deal, but haven't had any break throughs. Wonder if this kind of thing, blending multiple images with channel weights (or something), has been seen by anyone around here?Been trying to do some static analysis on the latest client but its not going so well. Would love to do some dynamic analysis but of the sources I've found on sketchy sites, they still don't target a high enough client to have these files. Quote
Spirited Posted March 27, 2021 Posted March 27, 2021 The game they copied all those maps from (Tian Yuan / TY) used PUX files in a folder called PuzzleSaves. I never got around to reversing them, but I guess it makes sense that they'd just copy the format instead of trying to convert it. Looking at the output you have above... it doesn't look any different than the standard PUZZLE2 format - besides the ability to use multiple ANI files if desired. Are you sure those aren't the RollX / RollY values? Quote
Santa Posted March 27, 2021 Author Posted March 27, 2021 They do live in a PuzzleSaves folder. I can't seem to find any information on this Tian Yuan game do you have any more information on that? (edit: found it, another netdragon game, there any dev forums or anything for it you know of?). Yeah, fairly sure they aren't roll values. The images that belong to a "UnitData" are far to different for them to be interchanged and not look messed up. Here is the complete snippet to read a pux file. using (TextWriter wr = new StreamWriter(File.OpenWrite(@"C:\Temp\puxFile.txt"))) using (BinaryReader br = new BinaryReader(Stream)) { this.PuzzleType = br.ReadASCIIString(16); wr.WriteLine(PuzzleType); uint token1000 = br.ReadUInt32();//1000 if (token1000 != 1000) Debug.WriteLine("Unknown Token in .pux file is not 1000"); uint unkWidth = br.ReadUInt32();//52 uint unkHeight = br.ReadUInt32();//28 wr.WriteLine("Size: {0},{1}", unkWidth, unkHeight); //CAoxPuzzle::LoadTextureGroup uint unk5 = br.ReadUInt32();//1000 ushort numTextureGroups = br.ReadUInt16(); wr.WriteLine("Num of TextureGroups: {0}", numTextureGroups); for (int i = 0; i < numTextureGroups; i++) { ushort unkLength = br.ReadUInt16(); byte[] unkBytes = br.ReadBytes(unkLength); ushort aniLength = br.ReadUInt16(); string aniFile = br.ReadASCIIString(aniLength); ushort puzpieceLen = br.ReadUInt16(); string puzPiece = br.ReadASCIIString(puzpieceLen); uint unk6 = br.ReadUInt32(); uint unk7 = br.ReadUInt32(); uint unk8 = br.ReadUInt32(); uint unk9 = br.ReadUInt32(); uint max = br.ReadUInt32(); wr.WriteLine("Texture Group: {0} AniFile: {1}, PuzzleEntry: {2}, {3}, {4}, {5}, {6}, {7}", unkBytes.GetString(), aniFile, puzPiece, unk6, unk7, unk8, unk9, max); } //End LoadtextureGroup //CAoxPuzzle::LoadEdgeGroup uint unk10 = br.ReadUInt32();//1000 ushort numEdgeGroups = br.ReadUInt16(); wr.WriteLine("Num of EdgeGroups: {0}", numEdgeGroups); for (int i = 0; i < numEdgeGroups; i++) { ushort unkLength = br.ReadUInt16(); byte[] unkBytes = br.ReadBytes(unkLength); ushort aniLength = br.ReadUInt16(); string aniFile = br.ReadASCIIString(aniLength); ushort puzpieceLen = br.ReadUInt16(); string puzPiece = br.ReadASCIIString(puzpieceLen); uint unk6 = br.ReadUInt32(); uint unk7 = br.ReadUInt32(); uint unk8 = br.ReadUInt32(); uint unk9 = br.ReadUInt32(); uint max = br.ReadUInt32(); wr.WriteLine("Edge Group: {0} AniFile: {1}, PuzzleEntry: {2}, {3}, {4}, {5}, {6}, {7}", unkBytes.GetString(), aniFile, puzPiece, unk6, unk7, unk8, unk9, max); } //End LoadEdgeGroup //PuzzleUnitData uint puzzGridCnt = br.ReadUInt32(); wr.WriteLine("PuzzleUnitData Size: {0}", puzzGridCnt); for (int i = 0; i < puzzGridCnt; i++) { byte dataCnt = br.ReadByte(); if (dataCnt != 0) { for (int j = 0; j < dataCnt; j++) { ushort aniID = br.ReadUInt16(); uint unk2 = br.ReadUInt32(); wr.WriteLine("PuzzleUnitLayer({2} - {3}): {0}, {1:X8}", aniID, unk2, i, j); } } } uint numEdgeData = br.ReadUInt32(); wr.WriteLine("EdgeLayer Size: {0}", numEdgeData); for (int i = 0; i < numEdgeData; i++) { uint unk = br.ReadUInt32(); byte[] unkBytes = br.ReadBytes(4); wr.WriteLine("EdgeLayer: {0}, {1}, {2}, {3}, {4}", unk, unkBytes[0], unkBytes[1], unkBytes[2], unkBytes[3]); } Debug.WriteLine("Finished reading pux"); } Quote
Spirited Posted March 29, 2021 Posted March 29, 2021 Interesting. I wish I spent more time looking into these, but I'm a bit clueless as it stands. I don't know if this will sound dumb or not, but I remember seeing references to a triangle based puzzle file rather than a square one. I wonder if some of those values could be the bounds of the defining shape. Have you tried debugging the client while it's loading one of these files? That could help in reverse engineering the format. Sorry I'm not of more help. Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.