The Dark Heart of Uukrul — Informe de autopsia técnica¶
Eriosthé, formato de mapas, transiciones, saves y mecanismo de integridad¶
Proyecto: RPG Archaeology
Fecha de cierre provisional: 23-08-2026
Estado: investigación cerrada provisionalmente con varios subsistemas confirmados y un último mecanismo de validación pendiente de resolución exacta.

1. Resumen ejecutivo¶
La investigación empezó con una pregunta relativamente modesta: ¿podemos reconstruir los mapas de Eriosthé a partir de los datos originales de The Dark Heart of Uukrul?
La respuesta es sí.
A partir de los archivos MZ*.LEV, del ejecutable, de UUKRUL.LIB, UUKRUL.OVR, tablas auxiliares, saves controlados y sesiones de depuración en DOSBox-X, se reconstruyó con bastante precisión:
- el formato físico de los niveles;
- la topología exacta de paredes;
- la representación de posición y orientación en runtime;
- la separación entre estado de grupo (
.GMI) y snapshot del mundo (.IMG); - parte de la arquitectura de teleports/transiciones;
- un parser/editor seguro de saves;
- y la existencia de un mecanismo real de integridad/anti-tamper que rechaza modificaciones aparentemente válidas.
No se consiguió cerrar por completo:
- la enumeración exacta de todas las transiciones MZ→MZ;
- la semántica completa del bytecode de eventos de los
.LEV; - ni el algoritmo final que hace que un save modificado sea rechazado.
La investigación sí consiguió localizar el último tramo relevante de ese validador y llegó a instrumentar el ejecutable para intentar observar el valor devuelto en runtime.
2. Filosofía de la autopsia¶
La regla operativa fue distinguir siempre entre:
- CONFIRMED — demostrado por código, datos o runtime;
- STRONG — inferencia muy apoyada;
- SPECULATIVE — hipótesis útil pero no cerrada;
- UNKNOWN — no resuelto;
- RETRACTED — interpretación anterior corregida por nueva evidencia.
Este criterio resultó especialmente importante porque varias hipótesis tempranas fueron descartadas más tarde. La investigación no debe leerse como una cadena lineal de descubrimientos perfectos, sino como una sucesión de hipótesis, pruebas, correcciones y cortes.
PARTE I — ERIOSTHɶ
3. Formato de los archivos MZ*.LEV¶
Los niveles estudiados usan archivos .LEV de 1024 bytes exactos.
Estructura reconstruida:
| Offset | Tamaño | Significado | Confianza |
|---|---|---|---|
0x000 |
0x320 |
geometría | CONFIRMED |
0x320 |
0xE0 |
bloque de eventos/script | STRONG |
| total | 0x400 |
archivo completo | CONFIRMED |
La geometría ocupa:
- 25 filas;
- 16 columnas;
- 2 bytes por celda.
Fórmula de acceso:
cell = DS:0x3816 + Y*0x20 + X*2
con:
Y = 0..24
X = 0..15
El loader copia el archivo completo al buffer de nivel en runtime.
4. Topología bit-exact de paredes¶
El byte bajo de cada celda contiene los cuatro muros cardinales:
0x01 = EAST
0x04 = SOUTH
0x10 = WEST
0x40 = NORTH
Semántica:
bit set = muro / bloqueado
Los cuatro bits restantes del byte bajo:
0x02
0x08
0x20
0x80
son flags especiales cuya semántica completa no quedó resuelta.
El byte alto funciona como tipo/feature adicional, también preservado como desconocido cuando no existía evidencia suficiente.
La asignación cardinal se sostuvo mediante varias señales independientes:
- redundancia entre lados opuestos de celdas vecinas;
- simetría estadística;
- conectividad global;
- y correspondencia con el código de LOS/movimiento.
El runtime usa direcciones:
dir0 = +X = EAST
dir1 = +Y = SOUTH
dir2 = -X = WEST
dir3 = -Y = NORTH
5. Filosofía espacial del motor¶
No existe evidencia de arrays independientes de edges N/S y E/W.
La topología está codificada por celda.
Movimiento y LOS consultan el estado direccional de una celda mediante helpers del motor. El resultado no-cero equivale a dirección bloqueada.
Esto produce una representación muy compacta:
2 bytes/celda
→ conectividad
→ tipo/feature
→ flags especiales
Una conclusión interesante para diseño: los mapas son pequeños en dimensiones absolutas, pero cada celda puede contener bastante información jugable.
6. Bloque de eventos de los LEV¶
Los últimos 224 bytes muestran comportamiento estructurado y no parecen relleno.
Se observaron tokens recurrentes como:
01 00
3E 00
81 B8 66
La interpretación más fuerte es:
0x320..0x3FF = stream compacto de eventos/comandos por nivel
La semántica exacta de todos los opcodes quedó abierta.
7. MZREG2¶
MZREG2.LEV aparece referenciado en tablas/nombres del programa pero el archivo no fue localizado en los datos de juego usados durante la investigación.
Esto apunta a un slot previsto y no distribuido o contenido retirado/inacabado.
No se debe inferir automáticamente qué habría contenido ese nivel.
PARTE II — TRANSICIONES Y TELEPORTS¶
8. TPORTS.TBL¶
TPORTS.TBL mostró estructura interna reconocible.
Se identificaron registros de cuatro bytes con fuerte regularidad y grupos de zonas con etiquetas como:
A B C D
X Y Z
1 2 3 4 5
! * ?
Los registros forman relaciones topológicas entre zonas y coordenadas, pero el binding completo:
source MZ → port → destination MZ/X/Y
no llegó a cerrarse estáticamente.
9. Cadena de teleport confirmada¶
La mecánica general sí pudo reconstruirse:
evento / port
→ selección mediante [0x3C1E]
→ fila de 12 caracteres
→ decodificación de destino
→ dstX
→ dstY
→ dstLevel
→ SetLevel
→ carga del nuevo MZ
Se localizó además un SetLevel invocable en runtime mediante una llamada far literal, lo que permitió establecer un breakpoint fiable durante las pruebas.
El obstáculo final fue localizar de forma estática el corpus exacto de filas de teleport generado/consultado mediante helpers residentes/overlay.
10. Resultado del world graph¶
La arquitectura de transición quedó demostrada.
La enumeración completa de edges directos MZ→MZ quedó PARTIAL.
Por tanto, cualquier grafo final debe distinguir:
- nodos confirmados;
- relaciones de zona;
- edges directos realmente demostrados;
- edges todavía inferidos.
PARTE III — ARQUITECTURA DE SAVES¶
11. Dos archivos, dos responsabilidades¶
La investigación con saves controlados mostró una separación clara.
GMI¶
Contiene principalmente estado estructurado del grupo.
Hallazgos:
party base = 0x80
stride = 0x60
4 registros
Campos identificados:
status
current_hp
max_hp
name
inventory_count
inventory_raw
El parser construido conserva los bytes desconocidos.
IMG¶
Contiene un snapshot de estado runtime/mundo.
Mapeo confirmado durante los experimentos:
IMG +0x0A → Y
IMG +0x0C → X
IMG +0x0E → LEVEL
IMG +0x14 → FACING
Esto fue demostrado con saves controlados:
- A = estado base;
- B = giro;
- C = movimiento.
Los .GMI de esos controles permanecían byte-idénticos mientras .IMG reflejaba movimiento/orientación.
12. CONTROL_A / B / C¶
Los experimentos controlados fueron decisivos.
Giro¶
CONTROL_A_BASE → CONTROL_B_TURN
cambios observados en IMG:
0x14 FACING
0x24E estado irregular
Movimiento¶
CONTROL_A_BASE → CONTROL_C_MOVE
cambios observados:
0x0A Y
0x14 FACING
0x24E estado irregular
0x655 estado de mapa/paso aún no cerrado
El word en 0x24E cambia con acciones legítimas y dejó de considerarse un checksum simple.
Interpretación final prudente:
0x24E = RNG/action/runtime state
No se demostró que sea el mecanismo de integridad.
PARTE IV — SAVE ARCHAEOLOGY TOOL¶
13. Phase I — core headless¶
Se construyó una herramienta de parsing/edición segura.
Capacidades:
GMI¶
Editable:
name
status
current_hp
max_hp
IMG¶
Lectura:
level
x
y
facing
Se preservan bytes desconocidos.
No-op roundtrip:
GMI: byte-identical
IMG: byte-identical
La herramienta usa copy-on-write y evita sobrescribir el original.
14. Phase II — world editing¶
Se añadió soporte experimental para edición de:
LEVEL
X
Y
FACING
Los tests verificaban que un cambio aislado sólo modificaba los bytes esperados y mantenía intacto el resto del archivo.
Se generó un laboratorio con:
FACING 0 → 1
Los tests pasaban.
A nivel binario, el save parecía perfectamente coherente.
A nivel runtime, Uukrul lo rechazó.
PARTE V — EL HALLAZGO DEL ANTI-TAMPER¶
15. Experimento runtime¶
Dos manipulaciones independientes fueron probadas.
A — GMI¶
Cambio:
HP 42 → 40
Estado perfectamente válido desde el punto de vista jugable.
Resultado:
save rechazado
B — IMG¶
Cambio:
FACING 0 → 1
También un estado perfectamente válido.
Resultado:
save rechazado
El juego mostró un mensaje equivalente a:
The game saved on this disk is faulty. Use another saved game or another disk.
La frase exacta vista en runtime relacionada con tampering no apareció como un literal completo en los binarios investigados; el mensaje almacenado localizado en el EXE fue la variante de "faulty".
Conclusión fuerte:
Uukrul valida algo más que la plausibilidad semántica de los campos editados.
Existe una invariante adicional de integridad/consistencia.
16. Qué NO se demostró¶
No quedó probado que el mecanismo sea:
- DRM;
- anti-cheat deliberado;
- un checksum convencional;
- una suma/XOR simple;
- o exclusivamente protección contra corrupción.
La terminología recomendable es:
save-integrity / anti-tamper mechanism
La interpretación histórica de anti-cheat deliberado es plausible, pero requiere más evidencia sobre intención y algoritmo.
PARTE VI — VALIDACIÓN DEL LOADER¶
17. Ruta de carga localizada¶
La investigación estática localizó una función de carga y un tramo de validación.
El patrón relevante terminó reducido a:
save loader
→ validation/dispatch
→ handler dinámico
→ retorno en AX
→ CMP AX, 0x0097
→ rama posterior
Una pieza especialmente relevante es:
ljmp cs:[0x05DE]
El destino de cs:[0x05DE] se configura en runtime mediante una cadena de punteros vinculada a memoria baja.
Esto explica buena parte de la dificultad del análisis puramente estático.
18. Corrección importante: XOR AH,AH¶
Durante la investigación apareció muchas veces:
30 E4
Inicialmente se interpretó erróneamente como una operación XOR entre bytes relacionada con checksum.
La interpretación fue corregida:
XOR AH,AH
es decir, limpiar AH / zero-extension.
Esta hipótesis de checksum quedó RETRACTED.
El caso es un buen ejemplo de por qué las conclusiones intermedias deben conservar su estado epistemológico.
19. CMP AX,0x0097¶
El loader contiene una comparación:
CMP AX,0x0097
después de la ruta de validación.
Lo que no quedó demostrado de forma definitiva:
- qué representa
AX; - qué representa
0x97; - qué valor produce exactamente un save bueno;
- qué valor produce el save modificado;
- y cuál es la primera divergencia causal.
Por tanto:
0x97 NO debe llamarse checksum.
Es sólo una constante relevante dentro del gate observado.
PARTE VII — INTENTO DE RUNTIME TRACING¶
20. DOSBox-X¶
Se configuró DOSBox-X con debugger y logging.
El problema práctico fue que el agente no consiguió controlar de forma fiable la ventana interactiva del debugger mediante SendKeys/PowerShell.
Esto llevó a una escalada instrumental.
21. Instrumentación del EXE¶
El último agente propuso —y llegó a aplicar— una instrumentación experimental del ejecutable.
Idea:
interceptar justo antes del CMP AX,0x97
→ capturar AX
→ emitir AL/AH por puertos de I/O
→ continuar por el flujo original
Se conservaron backups del ejecutable original antes de la modificación.
Se calcularon saltos relativos para que la instrumentación no dependiera del load segment del proceso.
El experimento no llegó a producir una captura final documentada de:
AX_good
AX_corrupted
antes de cerrar la investigación.
Por ello esta rama debe tratarse como:
INSTRUMENTATION IMPLEMENTED / FINAL MEASUREMENT PENDING
y no como algoritmo de integridad resuelto.
PARTE VIII — ESTADO FINAL DE LA AUTOPSIA¶
22. Confirmado¶
Mundo¶
.LEVde 1024 bytes.- grid 16×25.
- 2 bytes/celda.
- muro cardinal bit-exact:
- E
0x01 - S
0x04 - W
0x10 - N
0x40 - set = bloqueado.
- buffer runtime y coordenadas identificadas.
- eventos en tail separado.
Saves¶
.GMIy.IMGtienen responsabilidades distintas.- party records en GMI.
- estado espacial en IMG.
- offsets X/Y/LEVEL/FACING identificados.
- parser seguro y round-trip byte-perfect.
- edición binaria controlada funcional.
Integridad¶
- modificaciones mínimas y semánticamente válidas son rechazadas por el juego original.
- existe un mecanismo adicional de validación/consistencia.
- ruta del loader reducida hasta un handler dinámico y un
CMP AX,0x97. 0x24Eno es suficiente para explicar el rechazo.- las búsquedas de checksum suma/XOR simple no resolvieron el mecanismo.
23. Abierto¶
- significado completo de flags especiales de celda.
- bytecode completo de eventos LEV.
- tabla final de transiciones MZ→MZ.
- semántica completa del handler dinámico de validación.
- significado de AX y
0x97. - algoritmo exacto de integridad.
- generación de un save modificado aceptado por el ejecutable original.
PARTE IX — CONCLUSIÓN ARQUEOLÓGICA¶
24. Sobre Uukrul¶
La imagen obtenida es la de un RPG extremadamente compacto pero muy estructurado.
Sus mapas no necesitan grandes dimensiones para producir densidad:
celda
→ geometría
→ orientación
→ feature
→ eventos
→ estado de exploración
El juego emplea una representación espacial pequeña y económica, acompañada por sistemas auxiliares que convierten esas pocas celdas en contenido significativo.
La arquitectura de saves también resulta más sofisticada de lo que aparenta:
GMI = party / estado estructurado
IMG = snapshot runtime / mundo
y las modificaciones externas no son aceptadas simplemente por mantener valores plausibles.
25. Sobre el mecanismo de integridad¶
La conclusión prudente es:
The Dark Heart of Uukrul contiene un mecanismo real de validación de partidas capaz de detectar modificaciones manuales de campos perfectamente legítimos.
No se ha demostrado todavía si su motivación primaria era:
- corrupción de disco;
- anti-cheat;
- o ambas.
La frase de error y el comportamiento observado hacen especialmente interesante esa cuestión histórica.
PARTE X — LO QUE APRENDIMOS DEL PROCESO¶
26. Investigación con agentes¶
La autopsia acabó siendo también un experimento de trabajo con agentes.
Se observaron dos clases de tarea muy distintas:
Implementación acotada¶
Cuando el objetivo era:
implementa parser
añade tests
preserva bytes
los agentes trabajaron de manera eficiente.
Investigación abierta¶
Cuando el objetivo era:
averigua qué significa esto
aparecieron:
- crecimiento masivo de contexto;
- hipótesis simultáneas;
- necesidad de retractar conclusiones;
- compactaciones repetidas;
- degradación del estado global;
- y finalmente agotamiento del contexto.
27. Research Loop útil¶
La práctica más efectiva fue externalizar el estado.
Formato recomendado:
OBJECTIVE
CONFIRMED
STRONG
SPECULATIVE
REJECTED / RETRACTED
BLOCKED
ARTIFACTS
NEXT QUESTION
Cada corte debe reducir una incertidumbre concreta.
Cuando el análisis estático deja de producir información, debe cambiarse de método:
static → runtime
analysis → controlled experiment
agent automation → human-in-the-loop
28. Human-in-the-loop¶
Un aprendizaje especialmente claro:
No merece la pena gastar decenas de minutos intentando automatizar una interacción que el humano puede realizar en cinco segundos.
Para futuras investigaciones:
HUMAN_ACTION_REQUIRED
debería ser un estado operativo legítimo.
Ejemplo:
Agent:
"Breakpoint preparado. Metal: pulsa P y confirma."
Human:
"Hecho."
Agent:
"Capturo registros y continúo."
29. División de modelos¶
La investigación sugiere una separación práctica:
Modelo fuerte¶
Útil para:
- reducir incertidumbre;
- comparar hipótesis;
- decidir el siguiente experimento;
- mantener arquitectura conceptual.
Modelo rápido/local/peleón¶
Útil para:
- búsquedas;
- scripts;
- diffs;
- xrefs;
- desensamblado;
- tests;
- experimentos concretos.
El recurso caro no tiene por qué ser toda la ejecución: puede ser la dirección intelectual del siguiente corte.
PARTE XI — CIERRE¶
30. Estado¶
ERIOSTHÉ MAP FORMAT CONFIRMED
CARDINAL WALL TOPOLOGY CONFIRMED
SAVE SPLIT GMI/IMG CONFIRMED
SAVE TOOL CORE COMPLETE
WORLD EDIT BINARY IMPLEMENTED
WORLD EDIT ORIGINAL GAME REJECTED BY INTEGRITY
SAVE INTEGRITY EXISTENCE CONFIRMED
INTEGRITY ALGORITHM UNRESOLVED
FULL WORLD GRAPH PARTIAL
31. Futuro corte opcional¶
Si se decide volver:
Save Integrity Final Cut
Único objetivo:
capturar AX_good y AX_corrupted
en el gate CMP AX,0x0097
Después:
seguir hacia atrás sólo hasta la primera divergencia demostrable
No volver a abrir toda la investigación.
32. Conclusión final¶
La autopsia no necesita declararse incompleta por no haber vencido el último gate.
Se respondió con evidencia sólida a varias preguntas más importantes que la broma original del save editor:
- cómo representa Uukrul sus mapas;
- cómo almacena paredes y features;
- cómo conserva mundo y party;
- cómo restaura posición;
- y que el juego posee una invariante de integridad adicional que detecta modificaciones externas.
La última puerta permanece cerrada:
¿qué calcula exactamente el validador antes de CMP AX,0x0097?
Y queda perfectamente localizada para una futura excavación.
Fuentes de sesión utilizadas¶
SES2.md— sesión larga previa: mapas, teleports, save archaeology tool, Phase I/II.laguna_round2_fight.md— investigación estática del save-integrity gate y dispatch dinámico.Pegado text.txt— último estado de runtime/instrumentación y parche experimental.
Las conclusiones anteriores corregidas durante las sesiones se han tratado como retractadas y no como hechos finales.