Cómo desempaquetar un ejecutable autocontenido de Godot (tomando "Succulent Nap" como ejemplo)
Este documento registra la práctica real de desempaquetar el juego de Steam "Succulent Break" el 2026-09-11.

Renderizando...
Este documento registra la práctica real de desempaquetar el juego de Steam "Succulent Break" (App ID **4536070**) el **2026-09-11**. No es un tutorial de cracking genérico, solo se aplica a: ya has instalado legalmente, los paquetes de recursos **no están cifrados**, y el objetivo es leer la tabla de enciclopedia y los iconos que trae el propio juego.
Los productos del desempaquetado se utilizarán como datos de la enciclopedia en el sitio web de **main gamers MGs**: [Succulent Break Encyclopedia - Main Gamers](https://www.maingamers.com/zh/games/succulent-break)
---
## Resumen de Pasos de Desempaquetado
No se utilizaron GDRE Tools, AssetStudio, FModel, ni depuradores adicionales.
El proceso es:
1. Observar la estructura de archivos del directorio de instalación para determinar si el motor es **Godot**.
2. Encontrar el **PCK** (paquete de recursos) incrustado al final de `succulent-break.exe`.
3. Analizar la estructura de archivos según el formato de paquete **4** de Godot 4.7.
4. Extraer `assets/data/*.json` (la enciclopedia en texto plano).
5. Seguir `.png.import` para encontrar `.ctex`, y recortar el **WebP** incrustado desde GST2 como icono.
Todos los números de la enciclopedia provienen de estos JSON, no son adivinados ni leídos de los archivos de guardado del jugador.
---
## 1. Primero Identifica el Motor, Luego Decide el Desempaquetador
El directorio de instalación de Steam de "Succulent Break" solo tiene tres archivos:
| Archivo | Propósito |
| --- | --- |
| `succulent-break.exe` (aprox. 110 MB) | Motor + recursos del juego empaquetados en un paquete autocontenido |
| `steam_api64.dll` | Steamworks |
| `libgodotsteam.windows.template_debug.x86_64.dll` | Plugin GodotSteam |
El nombre del último archivo expone directamente el motor: **Godot + GodotSteam**. Un solo exe, sin `data.win` / `Game_Data` / `Paks` / `UnityPlayer.dll`, también puede indicar indirectamente que no es Unity / Unreal.
El significado de identificar el motor:
| Motor | Ubicación Común de Recursos | Precedentes en este Repositorio |
| --- | --- | --- |
| Exportación autocontenida de Godot | PCK incrustado al final del exe, o `.pck` independiente al lado | Este documento / `scrape-pck.mjs` |
| Unity | `*_Data/sharedassets*`, Addressables | A través de UnityFS como "El Libro de la Tapa" |
| Unreal | `*.pak` / `*.utoc` | FModel |
| Tauri / Electron | JS incrustado en el exe | Bull Run Clicker `scrape-exe.mjs` |
Identificar el motor incorrecto te llevará a buscar `globalgamemanagers` inexistentes. Este paso solo mira nombres de archivo y directorios, no requiere ejecutar el juego.
---
## 2. ¿Dónde Esconde Godot Autocontenido el PCK?
Cuando Godot exporta "PCK incrustado", la disposición es:
```
[ Archivo ejecutable PE (motor) | Paquete de recursos PCK | Pie de página de 12 bytes ]
```
El pie de página es fijo de **12 bytes**, little-endian:
| Desplazamiento (relativo al final del archivo) | Tipo | Significado |
| --- | --- | --- |
| `-12 .. -4` | `uint64` | Número de bytes del PCK (sin incluir este pie de página de 12 bytes) |
| `-4 .. 0` | `uint32` | Número mágico `GDPC` (`0x43504447`) |
Prueba práctica de este juego:
```
Tamaño del archivo = 115247400
Últimos 4 bytes ASCII = GDPC
Tamaño del PCK = 12264220
Inicio del PCK = 115247400 - 12 - 12264220 = 102983168
```
Código correspondiente en Node:
```js
const magic = buffer.readUInt32LE(buffer.length - 4); // Debe ser 0x43504447
const pckSize = Number(buffer.readBigUInt64LE(buffer.length - 12));
const offset = buffer.length - 12 - pckSize;
```
Si los últimos 4 bytes no son `GDPC`, verifica si hay un `succulent-break.pck` independiente en el mismo directorio. Si no hay ninguno de los dos, entonces se entra en la rama de "recursos cifrados / no Godot".
---
## 3. Analizar la Cabecera del PCK y el Directorio de Archivos
Desde el inicio del PCK, lee otro conjunto de cabeceras (Godot 4.x, formato ≥ 2):
| Campo | Tamaño | Este Juego |
| --- | --- | --- |
| magic | 4 | Otro `GDPC` |
| pack format | 4 | **4** (Godot 4.5+ movió el directorio al final del paquete) |
| Versión principal/secundaria/parche del motor | 4+4+4 | **4.7.1** |
| flags | 4 | `2` = `PACK_REL_FILEBASE` (desplazamiento del archivo relativo a `file_base`) |
| file_base | 8 | `112` |
| reserved[16] | 64 | `reserved[0] = 12181696`, es decir, **desplazamiento del directorio relativo al inicio del PCK** |
Paquetes antiguos de formato 2: el directorio sigue inmediatamente después de `reserved`.
Formatos 3/4: el directorio está al final del paquete, y su desplazamiento se coloca en `reserved[0]`. Si este juego interpreta los 4 bytes después de `reserved` como `file_count`, leerá **0**, lo cual es una señal de que el directorio se ha movido, no te detengas.
Entrada del directorio (cada archivo):
```
uint32 path_len
bytes path // UTF-8, ruta de recursos de Godot, sin prefijo "res://"
uint64 offset
uint64 size
bytes md5[16]
uint32 flags // Marca de cifrado / eliminación; en este paquete, todas son 0
```
Este paquete tiene **862** archivos. Al leer el contenido real:
```
Desplazamiento absoluto = inicio_pck + (PACK_REL_FILEBASE ? file_base : 0) + entrada.offset
```
La distribución de extensiones te dice directamente dónde están los datos:
| Extensión | Cantidad Aproximada | Significado |
| --- | --- | --- |
| `.ctex` / `.import` | Más de trescientos cada uno | Texturas importadas y sidecars |
| `.gdc` / `.remap` | Decenas | GDScript compilado, no se lee en esta ronda |
| `.json` | **7** | Fuente real de la enciclopedia |
| `.scn` / `.sample` / audio | El resto | Escenas y efectos de sonido |
Los juegos independientes suelen tener "tablas en JSON / CSV / Tres, código en scripts". Busca primero por ruta `assets/data/`, `data/`, `json`, no descompiles primero.
---
## 4. Extraer JSON de la Enciclopedia
Las siete tablas de este paquete se encuentran en `assets/data/`:
| Archivo | Propósito |
| --- | --- |
| `succulents.json` | Suculentas: paleta de colores, dificultad de coloración, plantas viejas, cadena de desbloqueo, probabilidad de esqueje de hoja |
| `pots.json` | Macetas: series, precio en tienda |
| `soils.json` | Suelos: factor de secado, efectos especiales |
| `shelves.json` | Estantes / mesas: ranuras, precio |
| `leaf.json` | Color y precio de las hojas |
| `weather_tools.json` | Herramientas climáticas |
| `translations.json` | Nombres y descripciones oficiales **zh / en** |
Los JSON de Godot permiten **comas finales**, lo que hará que `JSON.parse` falle. Primero elimina las comas antes de `}` / `]` y luego analiza:
```js
JSON.parse(text.replace(/,(\s*[}\]])/g, "$1"))
```
El original se coloca tal cual en `admin/resources/succulent-break/raw/assets/data/`; después de eliminar la coma final y agregar los nombres en chino e inglés, se escribe como `catalog.json`. No modifiques los valores en el original.
Los archivos de guardado del jugador son otra cosa:
```
%APPDATA%\Godot\app_userdata\Succulent Break\succulentbreak.json
```
Eso es progreso (qué plantas tienes, oro, selecciones desbloqueadas), **no** la definición de la enciclopedia. La recopilación de la enciclopedia no debe tratar el archivo de guardado como fuente principal, ni enviarlo a git.
---
## 5. Iconos: `.import` → `.ctex` → WebP
En el proyecto exportado, los PNG originales a menudo ya no existen, solo quedan:
1. `assets/sprites/ui/succulents/campfire.png.import` (sidecar de texto)
2. `.godot/imported/campfire.png-<hash>.ctex` (textura comprimida de Godot)
En `.import` hay una línea:
```
path="res://.godot/imported/campfire.png-98faa0cccb7f279e7901ae49e55b370b.ctex"
```
Eliminando `res://` puedes encontrar el `.ctex` en el directorio PCK.
`.ctex` comienza con `GST2` (Godot Stream Texture 2). Las imágenes de UI de este juego son de 32x32 y **embeben WebP dentro de GST2** (`DATA_FORMAT_WEBP`): busca `RIFF....WEBP` en el blob, y recorta el WebP completo según la longitud `uint32` de RIFF.
```js
const start = blob.indexOf(Buffer.from("RIFF"));
const size = blob.readUInt32LE(start + 4); // Longitud de la carga útil de RIFF (sin incluir el encabezado de 8 bytes)
const webp = blob.subarray(start, start + 8 + size);
```
Solo extrae los iconos de la UI de la enciclopedia (`assets/sprites/ui/...`), no trates los retratos de las etapas de crecimiento o los fotogramas de animación (como `orange_left` / `orange_right` de las hojas) como iconos de entrada. Los iconos de UI de esta ronda son **69**, faltan 0.
Si `.ctex` no contiene `RIFF`/`WEBP`, podría ser carga útil PNG, Basis, o compresión VRAM (DXT/BPTC). Esos requieren decodificación según el formato `Image::Format` de Godot, no se pueden aplicar estos recortes.
---
## 6. Cadena de Procesamiento Completa
```mermaid
flowchart TD
A["Directorio de Instalación de Steam"] --> B["Ver libgodotsteam / exe único"]
B --> C["Leer pie de página GDPC al final del exe"]
C --> D["Cabecera PCK: Godot 4.7.1 format 4"]
D --> E["reserved[0] localiza el directorio"]
E --> F["Lista de 862 rutas"]
F --> G["assets/data/*.json"]
F --> H["ui/*.png.import"]
G --> I["Eliminar coma final + traducciones"]
I --> J["catalog.json"]
H --> K["GST2 embebido WebP"]
K --> L["icons-raw/"]
```
Reejecución con un solo comando en el repositorio (sin iniciar el juego, sin escribir en la base de datos):
```bash
cd admin
node scripts/succulent-break/scrape-pck.mjs
```
Ruta predeterminada: `D:\SteamLibrary\steamapps\common\Succulent Break\succulent-break.exe`
Sobrescribir: variable de entorno `SUCCULENT_BREAK_EXE`, o la ruta del exe como primer argumento.
Los productos se encuentran en `admin/resources/succulent-break/` (ignorado por git):
| Ruta | Contenido |
| --- | --- |
| `raw/assets/data/*.json` | Original del PCK |
| `raw/catalog.json` | Tabla combinada analizable |
| `raw/pck-manifest.json` | Lista de 862 rutas |
| `icons-raw/{type}/*.webp` | Iconos de UI |
---
## 7. Cosas Claras que No se Hicieron
| Acción | Por qué no se hizo |
| --- | --- |
| Restaurar proyecto con GDRE Tools | Solo se necesitan tablas e iconos, no es necesario restaurar `.tscn` / proyecto del editor |
| Descompilar `.gdc` | El script de este paquete es bytecode compilado (avatar `GDSC`), no descifrado; por lo tanto, las fórmulas de riego / coloración se marcan como "no resueltas" |
| Leer memoria / adjuntar a un proceso en ejecución | El PCK estático es suficiente |
| Usar como fuente principal de la enciclopedia para leer archivos de guardado | Los archivos de guardado son progreso |
| Traducción del sistema o traducción IA al japonés | El juego solo tiene zh/en (G11) |
| Escribir `game_data_*` | Solo se recopila en esta ronda |
Los logros de Steam (6 en la página de la tienda) no están en el JSON del PCK, sino que siguen la canalización compartida `GetSchemaForGame`, ver `docs/060-steam-achievements-game-hub.md`.
---
## 8. Cómo Reutilizarlo para Otro Juego de Godot
Siguiendo esta lista, se puede reutilizar el mismo análisis:
1. El directorio de instalación tiene dll `*godot*`, o el final del exe es `GDPC`.
2. El PCK **no está cifrado** (la entrada del directorio `flags` no contiene el bit de cifrado; los JSON extraídos se pueden abrir como texto).
3. Las tablas están en `json` / `csv` / `tres`, en lugar de solo estar escritas en constantes de script.
Lo que normalmente necesita cambiarse es: la ruta predeterminada del exe, si `assets/data/` tiene un nombre diferente, y el prefijo `.import` de los iconos.
Los siguientes casos **no** deben aplicar este script:
- Unity / Unreal / RPG Maker / GameMaker.
- Directorio PCK cifrado (`PACK_DIR_ENCRYPTED`) o archivo único cifrado (`PACK_FILE_ENCRYPTED`).
- La enciclopedia solo existe en constantes `.gdc`, sin JSON.
- `.ctex` no es carga útil WebP/PNG (compresión VRAM real).
Para esos casos, se necesitan herramientas diferentes (desempaquetador de pak del motor correspondiente, GDRE, o un paquete de depuración exportado oficialmente), no se deben aplicar parches duros a este script.
---
## 10. Recordatorio de Cumplimiento
¡Se recomienda desempaquetar solo clientes comprados e instalados por usted mismo, y utilizar el contenido desempaquetado de manera legal y conforme!Comentario
Inicia sesión para ver y publicar comentarios
Ir a iniciar sesión