Skip to content
View in the app

A better way to browse. Learn more.

ResHax

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.
Help us keep the site running.
Zero Tolerance for Disrespect

Ben 10 (Aug 5, 2007 prototype) [.grv]

Featured Replies

Solved by zbirow

  • Supporter

Hard to tell. I'd suggest to start the exe from the prototype in a sandbox/virtual machine and examine its RAM contents.

  • Localization
  • Solution
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext
import struct
import os
import threading
import zlib

class GrvExtractorApp:
    def __init__(self, root):
        self.root = root
        self.root.title("GRV Extractor Tool")
        self.root.geometry("650x500")
        self.root.resizable(True, True)

        self.file_path = tk.StringVar()
        self.output_path = tk.StringVar()
        self.auto_decompress = tk.BooleanVar(value=True)

        self.create_widgets()

    def create_widgets(self):
        main_frame = tk.Frame(self.root, padx=10, pady=10)
        main_frame.pack(fill=tk.BOTH, expand=True)

        main_frame.columnconfigure(1, weight=1)

        tk.Label(main_frame, text="Select .grv File:").grid(row=0, column=0, sticky="w", pady=5)
        entry_file = tk.Entry(main_frame, textvariable=self.file_path, width=30)
        entry_file.grid(row=0, column=1, sticky="ew", padx=5, pady=5)
        tk.Button(main_frame, text="Browse", command=self.browse_file).grid(row=0, column=2, sticky="e", padx=5, pady=5)

        tk.Label(main_frame, text="Output Folder:").grid(row=1, column=0, sticky="w", pady=5)
        entry_out = tk.Entry(main_frame, textvariable=self.output_path, width=30)
        entry_out.grid(row=1, column=1, sticky="ew", padx=5, pady=5)
        tk.Button(main_frame, text="Browse", command=self.browse_folder).grid(row=1, column=2, sticky="e", padx=5, pady=5)

        chk_decompress = tk.Checkbutton(main_frame, text="Auto-decompress .zlib files", variable=self.auto_decompress)
        chk_decompress.grid(row=2, column=0, columnspan=3, sticky="w", pady=5)

        self.extract_btn = tk.Button(main_frame, text="Extract Files", command=self.start_extraction, bg="#dddddd", height=2)
        self.extract_btn.grid(row=3, column=0, columnspan=3, sticky="ew", pady=15)

        self.log_area = scrolledtext.ScrolledText(main_frame, height=15)
        self.log_area.grid(row=4, column=0, columnspan=3, sticky="nsew")

        main_frame.grid_rowconfigure(4, weight=1)

    def browse_file(self):
        filename = filedialog.askopenfilename(filetypes=[("GRV Files", "*.grv"), ("All Files", "*.*")])
        if filename:
            self.file_path.set(filename)

    def browse_folder(self):
        foldername = filedialog.askdirectory()
        if foldername:
            self.output_path.set(foldername)

    def log(self, message):
        self.log_area.insert(tk.END, message + "\n")
        self.log_area.see(tk.END)

    def read_string(self, f):
        chars = []
        while True:
            c = f.read(1)
            if not c or c == b'\x00':
                break
            chars.append(c)
        return b"".join(chars).decode('utf-8', errors='ignore')

    def start_extraction(self):
        input_file = self.file_path.get()
        output_dir = self.output_path.get()

        if not input_file or not os.path.exists(input_file):
            messagebox.showerror("Error", "Please select a valid input file.")
            return
        if not output_dir:
            messagebox.showerror("Error", "Please select an output folder.")
            return

        self.extract_btn.config(state=tk.DISABLED)
        self.log_area.delete(1.0, tk.END)
        
        thread = threading.Thread(target=self.run_extraction, args=(input_file, output_dir))
        thread.start()

    def run_extraction(self, filename, output_dir):
        try:
            with open(filename, 'rb') as f:
                self.log(f"Opening {filename}...")
                
                f.seek(19)
                
                count_bytes = f.read(2)
                if not count_bytes:
                    self.log("Error: Failed to read file count.")
                    return

                file_count = struct.unpack('>H', count_bytes)[0]
                self.log(f"Found {file_count} files.")
                self.log("-" * 40)

                files_list = []

                for _ in range(file_count):
                    meta_chunk = f.read(12)
                    if len(meta_chunk) < 12:
                        break

                    try:
                        offset_val = struct.unpack('>I', meta_chunk[4:8])[0]
                        size_val = struct.unpack('>I', meta_chunk[8:12])[0]
                    except struct.error:
                        self.log("Error parsing header.")
                        break

                    name = self.read_string(f)

                    files_list.append({
                        'name': name,
                        'offset': offset_val,
                        'size': size_val
                    })

                for item in files_list:
                    if not item['name'] or item['offset'] == 0:
                        continue

                    try:
                        f.seek(item['offset'])
                        raw_data = f.read(item['size'])
                        
                        final_name = item['name']
                        final_data = raw_data
                        decompressed = False

                        if self.auto_decompress.get() and final_name.lower().endswith('.zlib'):
                            try:
                                if len(raw_data) > 4:
                                    payload = raw_data[4:]
                                    final_data = zlib.decompress(payload)
                                    final_name = final_name[:-5]
                                    decompressed = True
                            except Exception as zlib_error:
                                self.log(f"Warning: Decompression failed for {final_name} ({zlib_error}). Saving original.")
                        
                        save_path = os.path.join(output_dir, final_name)
                        os.makedirs(os.path.dirname(save_path), exist_ok=True)
                        
                        with open(save_path, 'wb') as out_f:
                            out_f.write(final_data)
                        
                        status = " [Decompressed]" if decompressed else ""
                        self.log(f"Extracted: {final_name}{status}")

                    except Exception as e:
                        self.log(f"Error extracting {item['name']}: {e}")

                self.log("-" * 40)
                self.log("Done.")
                messagebox.showinfo("Success", "Extraction complete!")

        except Exception as e:
            self.log(f"Critical Error: {e}")
            messagebox.showerror("Error", str(e))
        finally:
            self.extract_btn.config(state=tk.NORMAL)

if __name__ == "__main__":
    root = tk.Tk()
    app = GrvExtractorApp(root)
    root.mainloop()

code for unpacking a file, with support for unpacking .zlib files. .jpg files are not .jpg image files because they have a different Magic header.374F7F8F-B91B-45DB-8712-7DDA4012A281.thumb.png.b333f620d72dc16d36985b0414b0ff46.png

  • 1 month later...

Create an account or sign in to comment

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.