Jump to content

Recommended Posts

  • Sentinel changed the title to Majin And The Forsaken Kingdom pak file
  • Engineers
Posted (edited)

Guess, this request is better addressed in compressed files forum.

Anyways, using offzip I got a  2 MB dat file (besides 5 *.kap).

The contents of the .dat ("gfxfontlibD"), a blurred picture, gives me a déjà-vu but I have no idea what exactly.

majin-us-pak.png.dbcfc8f7f7db09a5824915e8fc8a951d.png

Edited by shak-otay
  • Supporter
Posted
1 hour ago, shak-otay said:

Guess, this request is better addressed in compressed files forum.

Anyways, using offzip I got a  2 MB dat file (besides 5 *.kap).

The contents of the .dat ("gfxfontlibD"), a blurred picture, gives me a déjà-vu but I have no idea what exactly.

majin-us-pak.png.dbcfc8f7f7db09a5824915e8fc8a951d.png

This file has only two compression blocks, both of which are zlib compressed.

The first compressed block needs to use the -15 parameter, the second compressed block has a 78DA compression header and can be decompressed directly

The first metadata contains multiple file name indexes, but there is only one compressed data. Could it be that the decompressed data contains multiple file data?

  • Like 1
  • Engineers
Posted (edited)
1 hour ago, wq223 said:

The first compressed block needs to use the -15 parameter, the second compressed block has a 78DA compression header and can be decompressed directly

What do you mean by "directly"?

(78DA can be found 32 times in the pak, btw.)

Edited by shak-otay
  • Supporter
Posted
37 minutes ago, shak-otay said:

What do you mean by "directly"?

(78DA can be found 32 times in the pak, btw.)

My script includes 78DA in the compressed data

78DA is the best compression. It should have this compressed data part and can be decompressed directly without parameters, because there is no offset to prove whether the original file detached from this file starts with CFX, 78DA, or subsequent compressed data. But for the data starting with the first compressed data AC97, I must use parameters to decompress, otherwise my script cannot recognize that this is a zlib compressed data

.

  • Engineers
Posted (edited)

This is not encrypted:

Quote

Of critical importance to defeating
the darkness is the trust
between our hero and the Majin.

 

Edited by shak-otay
  • Thanks 1
  • Members
Posted


I analyzed the text data blocks. Below you can see their structure, along with a simple Python preview tool. (The first few dozen entries are empty, so you have to scroll down quite a bit.) That's all I can help with.

Structures:
4  MAGIC
2  padding
2  ID1
13 UNK
4  UNK2
8  UNK3
2  padding
2  ID2
5  UNK4
4  LENGTH (BE)
TEXT (UTF-16LE)
2  00 00
8  FF

 

import tkinter as tk
from tkinter import filedialog, ttk
import struct

MAGIC = b"\x17\xD2\x25\xE3"

class ParserGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("Binary Text Parser")

        self.records = []

        btn = tk.Button(root, text="Load File", command=self.load_file)
        btn.pack(pady=5)

        self.tree = ttk.Treeview(root, columns=("offset", "id1", "id2", "len"), show="headings")
        self.tree.heading("offset", text="OFFSET")
        self.tree.heading("id1", text="ID1")
        self.tree.heading("id2", text="ID2")
        self.tree.heading("len", text="TEXT LEN")
        self.tree.pack(fill="both", expand=True)
        self.tree.bind("<<TreeviewSelect>>", self.show_record)

        self.tabs = ttk.Notebook(root)
        self.tabs.pack(fill="both", expand=True)

        self.fields = {}
        for name in ["ID1", "UNK", "UNK2", "UNK3", "ID2", "UNK4", "TEXT"]:
            frame = ttk.Frame(self.tabs)
            self.tabs.add(frame, text=name)
            txt = tk.Text(frame, wrap="word")
            txt.pack(fill="both", expand=True)
            self.fields[name] = txt

        self.log = tk.Text(root, height=12, bg="black", fg="lime")
        self.log.pack(fill="both", expand=False)

    def log_print(self, msg):
        self.log.insert("end", msg + "\n")
        self.log.see("end")

    def load_file(self):
        path = filedialog.askopenfilename(title="Select a binary file")
        if not path:
            return

        with open(path, "rb") as f:
            data = f.read()

        self.records.clear()
        self.tree.delete(*self.tree.get_children())
        self.log.delete("1.0", "end")

        self.parse(data)

    def parse(self, data):
        offset = 0
        magic_count = 0

        while True:
            pos = data.find(MAGIC, offset)
            if pos == -1:
                break

            magic_count += 1
            self.log_print(f"\n--- MAGIC found @ 0x{pos:X} ---")

            try:
                cur = pos + 4

                padding1 = data[cur:cur+2]
                cur += 2

                id1 = data[cur:cur+2]
                cur += 2

                hash1 = data[cur:cur+13]
                cur += 13

                A = struct.unpack(">I", data[cur:cur+4])[0]
                cur += 4

                hash2 = data[cur:cur+8]
                cur += 8

                padding2 = data[cur:cur+2]
                cur += 2

                id2 = data[cur:cur+2]
                cur += 2

                hash3 = data[cur:cur+5]
                cur += 5

                length = struct.unpack(">I", data[cur:cur+4])[0]
                cur += 4

                self.log_print(f"ID1={id1.hex().upper()} ID2={id2.hex().upper()} LENGTH={length}")

                if length <= 0 or length > 10000:
                    self.log_print(" Suspicious length — skipping record")
                    offset = pos + 4
                    continue

                text_bytes = data[cur:cur + (length * 2)]
                cur += length * 2

                text = text_bytes.decode("utf-16be", errors="ignore").replace("\x00", "")

                if data[cur:cur+2] == b"\x00\x00":
                    cur += 2

                if data[cur:cur+8] == b"\xFF"*8:
                    cur += 8

                record = {
                    "offset": pos,
                    "ID1": id1.hex().upper(),
                    "UNK": hash1.hex().upper(),
                    "UNK2": A,
                    "UNK3": hash2.hex().upper(),
                    "ID2": id2.hex().upper(),
                    "UNK4": hash3.hex().upper(),
                    "TEXT": text
                }

                self.records.append(record)

            except Exception as e:
                self.log_print(f" Parsing error: {e}")

            offset = pos + 4

        self.log_print(f"\nMAGIC occurrences found: {magic_count}")
        self.log_print(f"Records parsed: {len(self.records)}")

        for r in self.records:
            self.tree.insert("", "end", values=(
                hex(r["offset"]),
                r["ID1"],
                r["ID2"],
                len(r["TEXT"])
            ))

    def show_record(self, event):
        sel = self.tree.selection()
        if not sel:
            return

        index = self.tree.index(sel[0])
        record = self.records[index]

        for key in self.fields:
            self.fields[key].delete("1.0", "end")
            self.fields[key].insert("1.0", str(record[key]))

if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("1000x800")
    app = ParserGUI(root)
    root.mainloop()

 

  • Thanks 1
  • Members
Posted
10 hours ago, zbirow said:


I analyzed the text data blocks. Below you can see their structure, along with a simple Python preview tool. (The first few dozen entries are empty, so you have to scroll down quite a bit.) That's all I can help with.

Structures:
4  MAGIC
2  padding
2  ID1
13 UNK
4  UNK2
8  UNK3
2  padding
2  ID2
5  UNK4
4  LENGTH (BE)
TEXT (UTF-16LE)
2  00 00
8  FF

 

import tkinter as tk
from tkinter import filedialog, ttk
import struct

MAGIC = b"\x17\xD2\x25\xE3"

class ParserGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("Binary Text Parser")

        self.records = []

        btn = tk.Button(root, text="Load File", command=self.load_file)
        btn.pack(pady=5)

        self.tree = ttk.Treeview(root, columns=("offset", "id1", "id2", "len"), show="headings")
        self.tree.heading("offset", text="OFFSET")
        self.tree.heading("id1", text="ID1")
        self.tree.heading("id2", text="ID2")
        self.tree.heading("len", text="TEXT LEN")
        self.tree.pack(fill="both", expand=True)
        self.tree.bind("<<TreeviewSelect>>", self.show_record)

        self.tabs = ttk.Notebook(root)
        self.tabs.pack(fill="both", expand=True)

        self.fields = {}
        for name in ["ID1", "UNK", "UNK2", "UNK3", "ID2", "UNK4", "TEXT"]:
            frame = ttk.Frame(self.tabs)
            self.tabs.add(frame, text=name)
            txt = tk.Text(frame, wrap="word")
            txt.pack(fill="both", expand=True)
            self.fields[name] = txt

        self.log = tk.Text(root, height=12, bg="black", fg="lime")
        self.log.pack(fill="both", expand=False)

    def log_print(self, msg):
        self.log.insert("end", msg + "\n")
        self.log.see("end")

    def load_file(self):
        path = filedialog.askopenfilename(title="Select a binary file")
        if not path:
            return

        with open(path, "rb") as f:
            data = f.read()

        self.records.clear()
        self.tree.delete(*self.tree.get_children())
        self.log.delete("1.0", "end")

        self.parse(data)

    def parse(self, data):
        offset = 0
        magic_count = 0

        while True:
            pos = data.find(MAGIC, offset)
            if pos == -1:
                break

            magic_count += 1
            self.log_print(f"\n--- MAGIC found @ 0x{pos:X} ---")

            try:
                cur = pos + 4

                padding1 = data[cur:cur+2]
                cur += 2

                id1 = data[cur:cur+2]
                cur += 2

                hash1 = data[cur:cur+13]
                cur += 13

                A = struct.unpack(">I", data[cur:cur+4])[0]
                cur += 4

                hash2 = data[cur:cur+8]
                cur += 8

                padding2 = data[cur:cur+2]
                cur += 2

                id2 = data[cur:cur+2]
                cur += 2

                hash3 = data[cur:cur+5]
                cur += 5

                length = struct.unpack(">I", data[cur:cur+4])[0]
                cur += 4

                self.log_print(f"ID1={id1.hex().upper()} ID2={id2.hex().upper()} LENGTH={length}")

                if length <= 0 or length > 10000:
                    self.log_print(" Suspicious length — skipping record")
                    offset = pos + 4
                    continue

                text_bytes = data[cur:cur + (length * 2)]
                cur += length * 2

                text = text_bytes.decode("utf-16be", errors="ignore").replace("\x00", "")

                if data[cur:cur+2] == b"\x00\x00":
                    cur += 2

                if data[cur:cur+8] == b"\xFF"*8:
                    cur += 8

                record = {
                    "offset": pos,
                    "ID1": id1.hex().upper(),
                    "UNK": hash1.hex().upper(),
                    "UNK2": A,
                    "UNK3": hash2.hex().upper(),
                    "ID2": id2.hex().upper(),
                    "UNK4": hash3.hex().upper(),
                    "TEXT": text
                }

                self.records.append(record)

            except Exception as e:
                self.log_print(f" Parsing error: {e}")

            offset = pos + 4

        self.log_print(f"\nMAGIC occurrences found: {magic_count}")
        self.log_print(f"Records parsed: {len(self.records)}")

        for r in self.records:
            self.tree.insert("", "end", values=(
                hex(r["offset"]),
                r["ID1"],
                r["ID2"],
                len(r["TEXT"])
            ))

    def show_record(self, event):
        sel = self.tree.selection()
        if not sel:
            return

        index = self.tree.index(sel[0])
        record = self.records[index]

        for key in self.fields:
            self.fields[key].delete("1.0", "end")
            self.fields[key].insert("1.0", str(record[key]))

if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("1000x800")
    app = ParserGUI(root)
    root.mainloop()

 

Thanks a lot

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...