ENC/DEC Decoder Source 

Newbie Spellweaver
Joined
Jul 11, 2026
Messages
8
Reaction score
15
sharing this enc/dec decoder i built for editing files without need to open dec and decrypt again

it's using default xor, if needed change it on line 15

XOR_KEY = 0x57676592

View attachment 305596

Python:
#!/usr/bin/env python3
"""
CABAL Online .enc Editor v2.1
Visual editor for CABAL Online .enc files.
Open, edit, and save .enc files using XOR + zlib encryption.
"""

import struct
import zlib
import os
import tkinter as tk
from tkinter import filedialog, messagebox, ttk

XOR_KEY = 0x57676592
CHUNK_SIZE = 16384

XOR_BYTES = [(XOR_KEY >> (8 * i)) & 0xFF for i in range(4)]


def decrypt_enc(data: bytes) -> bytes:
    if len(data) < 4:
        raise ValueError("File is too small")

    compressed = bytearray()
    pos = 4
    first = True

    while pos < len(data):
        chunk = bytearray(data[pos:pos + CHUNK_SIZE])
        if first:
            for i in range(min(4, len(chunk))):
                chunk[i] ^= XOR_BYTES[i]
            first = False
        compressed.extend(chunk)
        pos += len(chunk)

    errors = []
    for wbits in [-zlib.MAX_WBITS, zlib.MAX_WBITS, -11, 11, -9, 9]:
        try:
            return zlib.decompress(bytes(compressed), wbits)
        except zlib.error as e:
            errors.append("wbits=%d: %s" % (wbits, e))

    raise ValueError("Failed to decompress. Attempts:\n" + "\n".join(errors))


def encrypt_enc(data: bytes, level: int = 9) -> bytes:
    compressor = zlib.compressobj(level, zlib.DEFLATED, -zlib.MAX_WBITS)
    compressed = compressor.compress(data) + compressor.flush()

    result = bytearray()
    result.extend(struct.pack('<I', len(data)))

    first = True
    pos = 0

    while pos < len(compressed):
        chunk = bytearray(compressed[pos:pos + CHUNK_SIZE])
        if first:
            for i in range(min(4, len(chunk))):
                chunk[i] ^= XOR_BYTES[i]
            first = False
        result.extend(chunk)
        pos += len(chunk)

    return bytes(result)


class EncEditor(tk.Tk):
    def __init__(self):
        super().__init__()

        self.title("CABAL .enc Editor")
        self.geometry("900x650")

        self._filepath = None
        self._original_size = 0
        self._current_data = None

        self._setup_ui()

    def _setup_ui(self):
        menubar = tk.Menu(self)
        self.config(menu=menubar)

        file_menu = tk.Menu(menubar, tearoff=0)
        file_menu.add_command(label="Open .enc...", command=self._open_file, accelerator="Ctrl+O")
        file_menu.add_command(label="Save (.enc)", command=self._save_file, accelerator="Ctrl+S")
        file_menu.add_command(label="Save As...", command=self._save_as)
        file_menu.add_separator()
        file_menu.add_command(label="Export (.txt)", command=self._export_txt)
        file_menu.add_command(label="Exit", command=self.destroy)

        menubar.add_cascade(label="File", menu=file_menu)

        self.bind_all("<Control-o>", lambda e: self._open_file())
        self.bind_all("<Control-s>", lambda e: self._save_file())
        self.bind_all("<Control-e>", lambda e: self._export_txt())
        self.bind_all("<Control-f>", lambda e: self._show_find())
        self.bind_all("<Control-z>", lambda e: self._text.edit_undo() if hasattr(self, '_text') else None)
        self.bind_all("<Control-y>", lambda e: self._text.edit_redo() if hasattr(self, '_text') else None)

        main_frame = ttk.Frame(self, padding=5)
        main_frame.pack(fill=tk.BOTH, expand=True)

        self._info_label = ttk.Label(main_frame, text="No file opened")
        self._info_label.pack(fill=tk.X)

        text_frame = ttk.Frame(main_frame)
        text_frame.pack(fill=tk.BOTH, expand=True)

        self._text = tk.Text(
            text_frame,
            wrap=tk.NONE,
            font=("Consolas", 11),
            bg="#1E1E1E",
            fg="#D4D4D4",
            insertbackground="white",
            padx=5,
            pady=5,
            undo=True
        )
        self._text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self._text.bind("<Control-f>", lambda e: self._show_find())

        scroll_y = ttk.Scrollbar(text_frame, orient=tk.VERTICAL, command=self._text.yview)
        scroll_y.pack(side=tk.RIGHT, fill=tk.Y)
        self._text.configure(yscrollcommand=scroll_y.set)

        scroll_x = ttk.Scrollbar(main_frame, orient=tk.HORIZONTAL, command=self._text.xview)
        scroll_x.pack(fill=tk.X)
        self._text.configure(xscrollcommand=scroll_x.set)

        status_frame = ttk.Frame(main_frame)
        status_frame.pack(fill=tk.X, pady=(5, 0))

        self._status_label = ttk.Label(status_frame, text="")
        self._status_label.pack(side=tk.LEFT)

    def _show_find(self):
        if hasattr(self, '_find_bar') and self._find_bar.winfo_ismapped():
            self._find_bar.destroy()
            return

        self._find_bar = ttk.Frame(self._text.master)

        ttk.Label(self._find_bar, text="Find:").pack(side=tk.LEFT, padx=(0, 5))

        entry = ttk.Entry(self._find_bar, width=30)
        entry.pack(side=tk.LEFT)

        entry.bind("<Return>", lambda e: self._do_find(entry.get()))
        entry.bind("<Escape>", lambda e: self._find_bar.destroy())

        ttk.Button(
            self._find_bar,
            text="Next",
            command=lambda: self._do_find(entry.get())
        ).pack(side=tk.LEFT, padx=5)

        ttk.Label(self._find_bar, text="").pack(side=tk.LEFT)

        self._find_bar.pack(fill=tk.X, before=self._text)

        entry.focus_set()
        self._find_result = None

    def _do_find(self, text):
        if not text:
            return

        self._text.tag_remove("find", 1.0, tk.END)

        start = self._text.index(tk.INSERT) if self._find_result else "1.0"
        pos = self._text.search(text, start, tk.END, nocase=True)

        if pos:
            self._text.tag_add("find", pos, "%s+%dc" % (pos, len(text)))
            self._text.tag_config("find", background="#264F78")
            self._text.see(pos)
            self._text.mark_set(tk.INSERT, pos)
            self._find_result = pos
        else:
            self._find_result = None

    def _open_file(self, event=None):
        path = filedialog.askopenfilename(
            title="Open .enc file",
            filetypes=[
                ("ENC files", "*.enc"),
                ("All files", "*.*")
            ]
        )

        if not path:
            return

        try:
            with open(path, 'rb') as f:
                raw = f.read()

            self._filepath = path
            self._original_size = len(raw)

            data = decrypt_enc(raw)
            self._current_data = data

            self._text.delete(1.0, tk.END)

            text = data.decode("utf-8", errors="replace")
            self._text.insert(1.0, text)

            fname = os.path.basename(path)

            self._info_label.config(
                text="%s (%d -> %d bytes)" % (fname, len(raw), len(data))
            )

            self._status_label.config(text="OK")

        except Exception as e:
            messagebox.showerror("Error", "Failed to open file:\n%s" % e)

    def _save_file(self, event=None):
        if not self._filepath:
            self._save_as()
            return

        self._do_save(self._filepath)

    def _save_as(self):
        path = filedialog.asksaveasfilename(
            title="Save as .enc",
            defaultextension=".enc",
            filetypes=[
                ("ENC files", "*.enc"),
                ("All files", "*.*")
            ]
        )

        if path:
            self._do_save(path)
            self._filepath = path

    def _do_save(self, path):
        try:
            text = self._text.get(1.0, tk.END)

            if text.endswith("\n"):
                text = text[:-1]

            raw_data = text.encode("utf-8")
            enc = encrypt_enc(raw_data)

            with open(path, "wb") as f:
                f.write(enc)

            ratio = len(enc) / len(raw_data) if raw_data else 0

            self._status_label.config(
                text="Saved: %d bytes (ratio %.2fx)" % (len(enc), ratio)
            )

            messagebox.showinfo("Success", "File saved!")

        except Exception as e:
            messagebox.showerror("Error", "%s" % e)

    def _export_txt(self):
        path = filedialog.asksaveasfilename(
            title="Export as .txt",
            defaultextension=".txt",
            filetypes=[
                ("Text files", "*.txt")
            ]
        )

        if not path:
            return

        try:
            text = self._text.get(1.0, tk.END)

            with open(path, "w", encoding="utf-8") as f:
                f.write(text)

            messagebox.showinfo("Success", "File exported!")

        except Exception as e:
            messagebox.showerror("Error", "%s" % e)


if __name__ == "__main__":
    app = EncEditor()
    app.mainloop()
 
Back