• Publicado el 27/12/2016 Esto es un post en el blog de: Jesús Alonso Abad @kencho Offline Bio: Apasionado de los videojuegos (desarrollarlos), intentando abrirme paso en la industria poco a poco. Ir a su perfil completo

    Hacking the Dragons (3)

    (Continúa desde la parte 2)

    Siguiendo con el tema del bloqueo, está el problema de que en este juego el bloqueo está temporizado. Cuando pasamos a modo defensivo, es por un tiempo determinado, y para colmo no tenemos un feedback que nos indique cuándo vamos a bajar la guardia. Así que vamos a parchearlo para que la guardia se mantenga mientras pulsemos el botón, al estilo de Mortal Kombat o Soul Calibur.

    Identificar el temporizador
    La intuición (y la lógica) nos dice que este temporizador tiene que ser algo específico de cada personaje, y por tanto, estará en su segmento de memoria. Vamos a ver qué pasa cuando pulsamos el bloqueo.

    ¿Lo habéis visto? ¿Esos números que cambian rápidamente en 7e:nn17? Por lo que he podido sacar, ese byte se utiliza temporalmente para distintas cosas, según la acción del personaje. Lo que está más o menos claro es que cuando llega a 10h (16, en decimal), deja de bloquear. Por tanto, tenemos que buscar en qué punto del programa lee este valor, cuando éste tiene un valor de 10h. Añadimos un breakpoint en bsnes para vigilar nn17 (en mi caso, 1117), poniendo como condición que el valor sea 10 y sea una lectura (R).

    Debería detenerse en 05af05:

    05af05 lda $17 [001117] …

    Como siempre, habría que ver algunas instrucciones posteriores o anteriores para ver lo que hace (ya que se podría leer de ahí un 10 en varios puntos para otras cosas), pero en este caso hemos tenido suerte. Veamos las tres instrucciones a partir de esta:

    lda $17
    cmp #$10
    bcc aeb3

    La primera carga en el registro A el valor en D+17 (ahora debería ser 10). La segunda, compara A con 10; si A < 10 pone los flags Z=0 y C=0, si A=10, Z=1 y C=1, y si A > 10, Z=0 y C=1. La tercera instrucción saltará a 05aeb3 si C=1, es decir, si A es mayor o igual a 10. Hemos localizado la comprobación de salida del bloqueo.

    Cambiar la condición
    En vez de salir cuando termine el temporizador queremos salir cuando dejemos de pulsar el bloqueo. Del capítulo anterior sabemos que en el desplazamiento 46, cuando el bloqueo (B) está pulsado, se activa el tercer bit, 4. Luego cualquier valor que tenga este valor activo nos dirá que el bloqueo está pulsado (4, 5, 6, 7, 12…). Ahora viene el problema del espacio. Las tres instrucciones originales usan exactamente 7 bytes (2+2+3), y nosotros no podemos excedernos de ese tamaño sin tener problemas. Con la primera instrucción no hay problemas, ya que hay que reemplazar el 17 por el 46 para que lea A de otro byte. Para la comparación, en vez de usar CMP, usaremos AND, que aplica una máscara al registro A, y además pondrá el flag Z=0 si A tenía el bit indicado a 0 (en otras palabras, A=0 y Z=0 si no está pulsado el bloqueo). Esta instrucción son otros dos bytes. Todo bien por ahora. Por suerte, para el salto lo único que cambia es la condición de salto, que ahora es sólo si Z=0 (instrucción BNE), luego siguen siendo 3 bytes. Luego nuestro nuevo código es:

    lda $46
    and #$04
    bne aeb3

    o en no$sns:

    lda 46
    and #04
    bne aeb3

    Lo cambiamos y probamos que en efecto el bloqueo ahora se activa sólo al mantener pulsado el botón.

    Modificar la rom y crear el parche
    Como en la parte anterior, con el editor hexadecimal y la ayuda de Lunar Address, vamos a la posición correspondiente; la primera instrucción está en 05af05 pero no es necesario cambiar ni el opcode de la primera ni el parámetro de la tercera. Luego reemplazamos los 4 bytes a partir de 05af06 por los valores de las nuevas instrucciones: 46 29 04 D0. Guardamos y si queremos, creamos un parche aparte con Lunar IPS.



    En la próxima entrada haremos algo igual de sencillo, pero luego se complicará un poco más ;)
    3
Loader
Arriba