Au, acabat

This commit is contained in:
2026-01-27 17:35:30 +01:00
parent e05713ae2a
commit 39e5e8f70c
21 changed files with 505 additions and 65908 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
import sys
# Rango del checksum
desp = 0x2000
numBytes = 0x2000
firstByte = desp + 0x200
lastByte = desp + numBytes - 1
checksum1 = desp + 0x270
checksum2 = desp + 0x271
# Bloque de armarios
ARM_START = 0x02930
ARM_END = 0x0293B
# Armario que quieres dejar cerrado
TARGET_BYTE = 0x02934
TARGET_BIT = 0x80 # bit 7
def calc_checksum(buffer):
total = 0
for i in range(firstByte, lastByte + 1):
if i not in (checksum1, checksum2):
total += buffer[i]
total += 3328
buffer[checksum1] = total % 256
buffer[checksum2] = (total >> 8) % 256
if len(sys.argv) != 3:
print("Uso: python3 abrir_armarios.py save_in.srm save_out.srm")
sys.exit(1)
infile = sys.argv[1]
outfile = sys.argv[2]
with open(infile, "rb") as f:
buf = bytearray(f.read())
# Abrir todos los armarios
for off in range(ARM_START, ARM_END + 1):
buf[off] = 0xFF
# Cerrar solo el armario objetivo
buf[TARGET_BYTE] &= ~TARGET_BIT # FF sin el bit 7 → 7E
# Recalcular checksum
calc_checksum(buf)
with open(outfile, "wb") as f:
f.write(buf)
print("Hecho:", outfile)
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
import sys
# Rango del checksum
desp = 0x2000
numBytes = 0x2000
firstByte = desp + 0x200
lastByte = desp + numBytes - 1
checksum1 = desp + 0x270
checksum2 = desp + 0x271
# Rango ampliado de armarios
ARM_START = 0x02920
ARM_END = 0x0293F
# Armario que quieres dejar cerrado
TARGET_BYTE = 0x02934
TARGET_BIT = 0x80 # bit 7
def calc_checksum(buffer):
total = 0
for i in range(firstByte, lastByte + 1):
if i not in (checksum1, checksum2):
total += buffer[i]
total += 3328
buffer[checksum1] = total % 256
buffer[checksum2] = (total >> 8) % 256
if len(sys.argv) != 3:
print("Uso: python3 abrir_armarios_extendido.py save_in.srm save_out.srm")
sys.exit(1)
infile = sys.argv[1]
outfile = sys.argv[2]
with open(infile, "rb") as f:
buf = bytearray(f.read())
# Abrir todos los armarios del rango ampliado
for off in range(ARM_START, ARM_END + 1):
buf[off] = 0xFF
# Cerrar solo el armario objetivo
buf[TARGET_BYTE] &= ~TARGET_BIT # FF sin el bit 7 → 7E
# Recalcular checksum
calc_checksum(buf)
with open(outfile, "wb") as f:
f.write(buf)
print("Hecho:", outfile)
+85
View File
@@ -0,0 +1,85 @@
import os
import sys
def load_saves(path):
files = sorted(os.listdir(path))
saves = []
size = None
for f in files:
full = os.path.join(path, f)
if not os.path.isfile(full):
continue
with open(full, "rb") as fh:
data = fh.read()
if size is None:
size = len(data)
elif len(data) != size:
print("Tamaños inconsistentes, abortando.")
sys.exit(1)
saves.append(data)
print(f"Cargados {len(saves)} saves de {size} bytes.")
return saves, size
def is_one_way_flag(seq):
"""Devuelve True si el valor nunca vuelve a 0 después de ser 1."""
seen_one = False
for v in seq:
if v == 1:
seen_one = True
if seen_one and v == 0:
return False
return True
def main():
if len(sys.argv) != 2:
print("Uso: python3 detect_armarios.py <directorio>")
sys.exit(1)
saves, size = load_saves(sys.argv[1])
num_saves = len(saves)
# Transponer: valores por offset
flags = [] # lista de (offset, seq)
for off in range(size):
seq = [s[off] for s in saves]
values = set(seq)
# Solo nos interesan offsets con valores 0/1
if values.issubset({0, 1}) and len(values) > 1:
if is_one_way_flag(seq):
flags.append(off)
print(f"\nEncontrados {len(flags)} offsets que parecen flags de armarios.\n")
# Agrupar offsets contiguos
if not flags:
return
blocks = []
start = flags[0]
prev = flags[0]
for off in flags[1:]:
if off == prev + 1:
prev = off
else:
blocks.append((start, prev))
start = off
prev = off
blocks.append((start, prev))
print("Bloques sospechosos:")
for a, b in blocks:
count = b - a + 1
print(f" 0x{a:05X} 0x{b:05X} ({count} bytes)")
print("\nSi ves un bloque de ~80 bytes, probablemente son los armarios.")
if __name__ == "__main__":
main()
View File
+23
View File
@@ -0,0 +1,23 @@
import sys
def load(path):
with open(path, "rb") as f:
return f.read()
if len(sys.argv) != 4:
print("Uso: python3 diff3.py A.srm B.srm C.srm")
sys.exit(1)
A = load(sys.argv[1])
B = load(sys.argv[2])
C = load(sys.argv[3])
if not (len(A) == len(B) == len(C)):
print("Los saves no tienen el mismo tamaño.")
sys.exit(1)
print("Offsets que cambian SOLO al abrir el armario:\n")
for i, (a, b, c) in enumerate(zip(A, B, C)):
if a != c and a == b:
print(f"Offset 0x{i:05X}: {a:02X}{c:02X}")
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env python3
import sys
if len(sys.argv) != 3:
print("Uso: python3 diff_saves.py save_antes.srm save_despues.srm")
sys.exit(1)
path_a = sys.argv[1]
path_b = sys.argv[2]
with open(path_a, "rb") as f:
a = f.read()
with open(path_b, "rb") as f:
b = f.read()
if len(a) != len(b):
print(f"Los saves tienen tamaños distintos: {len(a)} vs {len(b)} bytes")
sys.exit(1)
print(f"Comparando {len(a)} bytes...\n")
for i, (x, y) in enumerate(zip(a, b)):
if x != y:
print(f"Offset 0x{i:05X}: {x:02X}{y:02X}")
+149
View File
@@ -0,0 +1,149 @@
===========================================
DOCUMENTACIÓN DEL SISTEMA DE ARMARIOS
===========================================
Juego: [Nombre del juego]
Autor del análisis: Sergio
Colaborador técnico: Copilot
Fecha: [poner fecha]
===========================================
1. INTRODUCCIÓN
===========================================
Este documento resume todo lo descubierto durante el análisis del sistema
de guardado relacionado con los armarios (contenedores) del juego.
Incluye:
- Localización del contador global de armarios abiertos
- Identificación de flags de evento relacionados
- Confirmación de qué offsets NO son armarios
- Estado actual de la investigación
- Próximos pasos recomendados
El objetivo final es permitir manipular el save para:
- abrir o cerrar armarios individualmente
- forzar el logro de “abrir todos los armarios”
- depurar el comportamiento del juego
===========================================
2. CONTADOR GLOBAL DE ARMARIOS ABIERTOS
===========================================
Offset: 0x0295C
Tipo: contador simple (1 byte)
Función: almacena cuántos armarios ha abierto el jugador
Ejemplo:
- 0x3B = 59 armarios abiertos
- 0x3C = 60 armarios abiertos
- 0x50 = 80 armarios abiertos (valor máximo para el logro)
Este contador:
- aumenta en +1 cada vez que se abre un armario
- NO depende del guardado
- NO es un bitfield
- NO controla si un armario está abierto o cerrado
- solo refleja el total acumulado
IMPORTANTE:
Modificar este contador NO abre ni cierra armarios reales.
Solo afecta al número que muestra el juego y a la lógica del logro
(si el logro depende únicamente del contador).
===========================================
3. FLAG DE EVENTO RELACIONADO (NO ES ARMARIO)
===========================================
Offset: 0x02934
Cambio observado: EE → FE al abrir un armario concreto
Bit afectado: bit 7 (0x80)
Este byte:
- cambia al abrir el armario de prueba
- revertirlo hace que el armario vuelva a aparecer cerrado
- PERO no forma parte del bloque real de armarios
- es un flag de evento o script asociado al acto de abrir ese armario
Conclusión:
0x02934 NO es el bitfield de armarios.
Es un flag de evento que se activa al abrir ese armario concreto.
===========================================
4. BLOQUE REAL DE ARMARIOS (AÚN NO LOCALIZADO)
===========================================
El juego tiene 80 armarios → se necesitan 80 bits → 10 bytes.
El bloque real:
- NO está en 0x029300x0293F
- NO incluye 0x02934
- NO se ve afectado al poner FF en ese rango
- debe contener exactamente 59 bits activos en el save analizado
- debe cambiar un solo bit al abrir un armario
- debe permanecer estable al guardar sin abrir nada
Estado actual:
El bloque real aún no ha sido identificado, pero sabemos que:
- está cerca del rango 0x028000x02B00
- debe ser un bloque compacto de 10 bytes
- debe tener valores tipo: 00, 01, 03, 07, 0F, 1F, 3F, 7F, FF
- debe contener 59 bits activos en el save actual
===========================================
5. RESUMEN DE HALLAZGOS
===========================================
✔ 0x0295C = contador global de armarios abiertos
✔ 0x02934 = flag de evento asociado al armario de prueba
✔ revertir 0x02934 cierra el armario visualmente
✔ el contador global NO depende de 0x02934
✔ poner FF en 0x029200x0293F NO abre armarios reales
✔ el bloque real de armarios NO está en esa zona
✔ el contador global coincide exactamente con el número mostrado por el juego
===========================================
6. PRÓXIMOS PASOS RECOMENDADOS
===========================================
1. Volcar el rango 0x028000x02A00 para localizar el bloque real.
2. Buscar un bloque de 10 bytes con ~59 bits activos.
3. Confirmar qué byte cambia al abrir un armario nuevo.
4. Mapear los 80 bits → 80 armarios.
5. Crear un editor que:
- abra/cierre armarios individualmente
- actualice el contador global
- recalcule el checksum automáticamente
===========================================
7. CHECKSUM
===========================================
El checksum del save se encuentra en:
- 0x2270 (LSB)
- 0x2271 (MSB)
Se calcula sumando:
- todos los bytes desde 0x2200 hasta 0x3FFF
- excepto los dos bytes del propio checksum
- más un offset fijo de 3328
===========================================
8. ESTADO FINAL
===========================================
El sistema de armarios está parcialmente resuelto:
- Contador global: ✔ localizado y confirmado
- Flag de evento del armario de prueba: ✔ localizado
- Bloque real de armarios: ❌ pendiente de localizar
Una vez identificado el bloque real, se podrá:
- abrir/cerrar armarios a voluntad
- fabricar saves para logros
- documentar completamente el formato
===========================================
FIN DEL DOCUMENTO
===========================================
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env python3
import sys
desp = 0x2000
numBytes = 0x2000
firstByte = desp + 0x200
lastByte = desp + numBytes - 1
checksum1 = desp + 0x270
checksum2 = desp + 0x271
def calc_checksum(buffer):
total = 0
for i in range(firstByte, lastByte + 1):
if i not in (checksum1, checksum2):
total += buffer[i]
total += 3328
buffer[checksum1] = total % 256
buffer[checksum2] = (total >> 8) % 256
if len(sys.argv) != 3:
print("Uso: python3 revert_2934.py save_in.srm save_out.srm")
sys.exit(1)
infile = sys.argv[1]
outfile = sys.argv[2]
with open(infile, "rb") as f:
buf = bytearray(f.read())
# revertir el posible flag del armario
buf[0x02934] = 0xEE # valor original
calc_checksum(buf)
with open(outfile, "wb") as f:
f.write(buf)
print("Hecho:", outfile)
+38
View File
@@ -0,0 +1,38 @@
import sys
desp = 0x2000
numBytes = 0x2000
firstByte = desp + 0x200
lastByte = desp + numBytes - 1
checksum1 = desp + 0x270
checksum2 = desp + 0x271
def calc_checksum(buffer):
total = 0
for i in range(firstByte, lastByte + 1):
if i not in (checksum1, checksum2):
total += buffer[i]
total += 3328
buffer[checksum1] = total % 256
buffer[checksum2] = (total >> 8) % 256
if len(sys.argv) != 3:
print("Uso: python3 revert_armario.py save_in.srm save_out.srm")
sys.exit(1)
infile = sys.argv[1]
outfile = sys.argv[2]
with open(infile, "rb") as f:
buf = bytearray(f.read())
# revertir el byte del armario
buf[0x0295C] = 0x3B
# recalcular checksum
calc_checksum(buf)
with open(outfile, "wb") as f:
f.write(buf)
print("Hecho. Guarda generado:", outfile)
BIN
View File
Binary file not shown.
-7486
View File
File diff suppressed because it is too large Load Diff
-58422
View File
File diff suppressed because it is too large Load Diff
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env python3
import sys
# Rango del checksum
desp = 0x2000
numBytes = 0x2000
firstByte = desp + 0x200
lastByte = desp + numBytes - 1
checksum1 = desp + 0x270
checksum2 = desp + 0x271
# Offset del contador de armarios
CONTADOR = 0x0295C
def calc_checksum(buffer):
total = 0
for i in range(firstByte, lastByte + 1):
if i not in (checksum1, checksum2):
total += buffer[i]
total += 3328
buffer[checksum1] = total % 256
buffer[checksum2] = (total >> 8) % 256
if len(sys.argv) != 3:
print("Uso: python3 set_contador_armarios.py save_in.srm save_out.srm")
sys.exit(1)
infile = sys.argv[1]
outfile = sys.argv[2]
with open(infile, "rb") as f:
buf = bytearray(f.read())
# Poner el contador a 0x80
buf[CONTADOR] = 0x4F
# Recalcular checksum
calc_checksum(buf)
with open(outfile, "wb") as f:
f.write(buf)
print("Hecho:", outfile)