Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

WZ New WzImage nodes?

Joined
Apr 5, 2008
Messages
663
Reaction score
537
When converting Etc.wz from the latest GMS, I came across some new wzimage nodes that didn't have the typical .img suffix and did not parse as a typical wzimage. Normally the first byte of wzimages is 115, but for these new wzimages the first byte is 1.

It appears that the entire contents is one big string, since the few bytes after the first byte are a valid string length which goes right up to the end of the img. However the string can't be decoded using the typical keys and algorithms.

Etc.wz/Script/BattleScene.lua
Etc.wz/Script/main.lua
Etc.wz/Script/Scene.lua

I have no idea how to decode these, so if anyone could help it would be appreciated. Poke poke @Elem8100

If anyone can investigate but doesn't want to download all of GMS, here are just those three nodes
 
Last edited:
Experienced Elementalist
Joined
Feb 15, 2010
Messages
201
Reaction score
296
The code is rip from someone's github. Haven't tested but should work.

Code:
private void ExtractLua()
        {
            while(this.WzFile.FileStream.Position < this.Offset + this.Size)
            {
                var flag = this.WzFile.BReader.ReadByte();

                switch (flag)
                {
                    case 0x01:
                        ExtractLuaValue(this.Node);
                        break;

                    default:
                        throw new Exception("读取Lua错误." + flag + " at Offset: " + this.WzFile.FileStream.Position);
                }
            }
        }

        private void ExtractLuaValue(Wz_Node parent)
        {
            int len = this.WzFile.ReadInt32();
            byte[] data = this.WzFile.BReader.ReadBytes(len);
            this.WzFile.WzStructure.encryption.keys.Decrypt(data, 0, data.Length);
            string luaCode = Encoding.UTF8.GetString(data);
            parent.Value = luaCode;
        }
 
Upvote 0
Joined
Aug 10, 2008
Messages
858
Reaction score
516
The code is rip from someone's github. Haven't tested but should work.

Code:
private void ExtractLua()
        {
            while(this.WzFile.FileStream.Position < this.Offset + this.Size)
            {
                var flag = this.WzFile.BReader.ReadByte();

                switch (flag)
                {
                    case 0x01:
                        ExtractLuaValue(this.Node);
                        break;

                    default:
                        throw new Exception("读取Lua错误." + flag + " at Offset: " + this.WzFile.FileStream.Position);
                }
            }
        }

        private void ExtractLuaValue(Wz_Node parent)
        {
            int len = this.WzFile.ReadInt32();
            byte[] data = this.WzFile.BReader.ReadBytes(len);
            this.WzFile.WzStructure.encryption.keys.Decrypt(data, 0, data.Length);
            string luaCode = Encoding.UTF8.GetString(data);
            parent.Value = luaCode;
        }

Does their ReadInt32() account for it possibly being a compressed integer? If not, this algorithm seems to be off by a byte from what I see.

Example from BattleScene.lua in raw format:
Code:
01 80 6C 10 01 00 <string data>
01 is the magic byte & 80 is signal for a compressed int -> 0x1106C is size (69740 bytes)

How I implemented handling of 0x01:
WzImage.java
Code:
        if (b == 0x01) {
            int length = in.readCompressedInteger();
            byte[] raw = in.readBytes(length);
            String data = in.decryptLua(raw);
            
            WzProperty<String> script = new WzProperty<>("script", data, WzProperty.Type.STRING);
            
            script.setParent(this);
            
            children.put(script.getName(), script);
            
            return;
        }

Made a fake child named "script" as to not add a new field to WzImage.

WzInputStream.java
Code:
    public String decryptLua(byte[] raw) {
        for (int i = 0; i < raw.length; i++) {
            raw[i] ^= key[i];
        }
        return new String(raw);
    }

Only problem is that I have to generate a key using the EMS locale IV in order for this to work (I'm assuming that this IV is joint with JMS/KMS). Also need a bigger key for the script since the BattleScene.lua script was bigger than 0xFFFF bytes -> size I was using before for normal string decode.

I feel like how I handle WzImage could be revisited a bit since it seems like this is also possible to do with a simple readStringBlock call -> assuming the 0x01 magic byte is unique to this image variety in GMS. If so, I could theoretically split the magic bytes via locale and not have to specify a key anymore since they'll all be specified for usage.

Code:
    public String readStringBlock(int offset) {
        int s = read();
        switch (s) {
            case 0x00:
            case 0x73: // magic byte for verifying image
                return readString();
            case 0x01: // magic byte for verifying lua script (?)
            case 0x1B:
                return readString(offset + readInteger());
            default:
                return Integer.toHexString(s);
        }
    }

Still a decent amount of research to be done...
 
Last edited:
Upvote 0
Junior Spellweaver
Joined
Aug 13, 2009
Messages
124
Reaction score
123
integrated some of it over to harepacker. thanks for the goodies here.


(4 years late, but its better than none)
 
Upvote 0
Back
Top