December 20, 2025Dec 20 I am trying to rip the images from Hayarigami 1.2.3 on switch and it uses a .dat file format here is a file from Hayarigami 3 from the 1.2.3 Collection https://drive.google.com/file/d/1zdSfck87_f5XrwQpK0tP8FJ6kLh6re-x/view?usp=sharing Edited December 20, 2025Dec 20 by Sirduckdude
December 27, 2025Dec 27 Localization Solution import tkinter as tk from tkinter import filedialog, messagebox, scrolledtext import struct import os import threading class GameArchiveExtractor: def __init__(self, root): self.root = root self.root.title("Game Archive Extractor") self.root.geometry("600x500") self.root.resizable(False, False) self.file_path = tk.StringVar() self.output_path = tk.StringVar() self._create_widgets() def _create_widgets(self): file_frame = tk.LabelFrame(self.root, text="Input File (.dat)", padx=10, pady=10) file_frame.pack(fill="x", padx=10, pady=5) tk.Entry(file_frame, textvariable=self.file_path, state='readonly', width=50).pack(side="left", padx=5) tk.Button(file_frame, text="Browse...", command=self._browse_file).pack(side="left") out_frame = tk.LabelFrame(self.root, text="Output Directory", padx=10, pady=10) out_frame.pack(fill="x", padx=10, pady=5) tk.Entry(out_frame, textvariable=self.output_path, state='readonly', width=50).pack(side="left", padx=5) tk.Button(out_frame, text="Browse...", command=self._browse_output).pack(side="left") btn_frame = tk.Frame(self.root, pady=10) btn_frame.pack(fill="x", padx=10) self.extract_btn = tk.Button(btn_frame, text="Analyze and Extract", command=self._start_extraction_thread, height=2, bg="#dddddd") self.extract_btn.pack(fill="x") log_frame = tk.LabelFrame(self.root, text="Analyzer Log", padx=10, pady=10) log_frame.pack(fill="both", expand=True, padx=10, pady=5) self.log_text = scrolledtext.ScrolledText(log_frame, state='disabled', font=("Consolas", 9)) self.log_text.pack(fill="both", expand=True) def _browse_file(self): filename = filedialog.askopenfilename(filetypes=[("DAT Files", "*.dat"), ("All Files", "*.*")]) if filename: self.file_path.set(filename) def _browse_output(self): dirname = filedialog.askdirectory() if dirname: self.output_path.set(dirname) def _log(self, message): self.log_text.config(state='normal') self.log_text.insert(tk.END, message + "\n") self.log_text.see(tk.END) self.log_text.config(state='disabled') def _start_extraction_thread(self): if not self.file_path.get() or not self.output_path.get(): messagebox.showwarning("Error", "Please select both input file and output directory.") return self.extract_btn.config(state='disabled') self.log_text.config(state='normal') self.log_text.delete(1.0, tk.END) self.log_text.config(state='disabled') threading.Thread(target=self._process_archive, daemon=True).start() def _process_archive(self): dat_path = self.file_path.get() out_dir = self.output_path.get() try: with open(dat_path, 'rb') as f: header = f.read(16) magic = header[:8] file_count = struct.unpack('<H', header[8:10])[0] self._log(f"Magic Header: {magic}") self._log(f"File Count: {file_count}") self._log("-" * 65) self._log(f"{'FILENAME':<35} | {'OFFSET':<12} | {'SIZE':<10}") self._log("-" * 65) if magic != b'PS_FS_V1': self._log("WARNING: Magic header mismatch!") files_to_extract = [] for _ in range(file_count): entry_data = f.read(64) if len(entry_data) < 64: break raw_name = entry_data[:48] file_name = raw_name.split(b'\x00')[0].decode('ascii', errors='ignore') file_size = struct.unpack('<I', entry_data[48:52])[0] file_offset = struct.unpack('<I', entry_data[56:60])[0] if not file_name: continue self._log(f"{file_name:<35} | {hex(file_offset):<12} | {file_size:<10}") files_to_extract.append({ 'name': file_name, 'offset': file_offset, 'size': file_size }) self._log("-" * 65) self._log(f"Starting extraction of {len(files_to_extract)} files...") for item in files_to_extract: f.seek(item['offset']) data = f.read(item['size']) full_path = os.path.join(out_dir, item['name']) os.makedirs(os.path.dirname(full_path), exist_ok=True) with open(full_path, 'wb') as out_f: out_f.write(data) self._log("Extraction completed successfully.") messagebox.showinfo("Success", "Extraction completed!") except Exception as e: self._log(f"Error: {str(e)}") messagebox.showerror("Error", str(e)) finally: self.extract_btn.config(state='normal') if __name__ == "__main__": root = tk.Tk() app = GameArchiveExtractor(root) root.mainloop()
January 4Jan 4 Author Tried using QuickBMS but doesn't seem to work it seems to be giving an command line error when extracting but i'm stupid and its a python GUI lol Thank you so much for this!!
Create an account or sign in to comment