Sí llevas poco tiempo con el RH, es muy probable que este tutorial no sea para tí. Se sobrentiende que tienes conocimientos básicos en edición hexadecimal, inserción de imágenes comprimidas (aka usar el Unlz Gba), inserción de paletas (Aka usar el APE)
y lo mínimo sobre el manejo de variables y ASM.
y lo mínimo sobre el manejo de variables y ASM.
Tengo que dar las gracias al creador de la rutina que aparece en este vídeo (no se si el creador es la misma persona que subió el vídeo, ya que no lo pone en la descripción y el foro al que hace referencia está caído). Si bien no he utilizado absolutamente nada del código o del método de esta, ha sido una parte importante en el proceso de ingeniería inversa.
(Está rutina no es un buen método para cargar mugshots, ya que no me gusta, aún después de arreglar varios fallos que tiene [como no destruir nunca las instancias de la OAM, generando otra y otra y otra, aka memory leak]. Por como genera la transparencia, hace imposible llamar cualquier cuadro multichoice mientras se muestra el mugshot, y de hacerlo crasheará el juego, cosa que no me convence en absoluto).
Decir también que finalmente, lo que he hecho, ha sido portear la rutina de Kyoko1 para cargar mugshots en FR a EM. He respetado mayormente el formato original por respeto al creador, pero he modificado un par de cosas. Entre ellas una que causaba que el sistema fuese incompatible con DNS (ambas rutinas utilizaban la misma dirección, 0x0203C000 para guardar el status byte y bueno, os podéis hacer una idea de lo que pasaba).
(Está rutina no es un buen método para cargar mugshots, ya que no me gusta, aún después de arreglar varios fallos que tiene [como no destruir nunca las instancias de la OAM, generando otra y otra y otra, aka memory leak]. Por como genera la transparencia, hace imposible llamar cualquier cuadro multichoice mientras se muestra el mugshot, y de hacerlo crasheará el juego, cosa que no me convence en absoluto).
Decir también que finalmente, lo que he hecho, ha sido portear la rutina de Kyoko1 para cargar mugshots en FR a EM. He respetado mayormente el formato original por respeto al creador, pero he modificado un par de cosas. Entre ellas una que causaba que el sistema fuese incompatible con DNS (ambas rutinas utilizaban la misma dirección, 0x0203C000 para guardar el status byte y bueno, os podéis hacer una idea de lo que pasaba).
- Un editor hexadecimal (Recomendado HxD).
- Un editor de texto plano (Recomendado Notepad++).
Para insertar las imágenes y las paletas podéis utilizar lo que os venga en gana, aunque recomiendo usar Unlz Gba y APE.
RUTINA LOAD_MUGSHOT:
Esta rutina lo que hará es crear una instancia(un objeto) en la OAM (región de memoria comprendida de 0x07000000 en adelante), a este objeto se le asignarán unos tiles cargados en la VRAM+ObjTiles (0x0601000) y una paleta cargada en la región de memoria correspondiende (0x05000000).
Tanto las imágenes como las paletas han de encontrarse insertadas dentro del rom. Y para que la rutina pueda acceder a estas de forma sencilla, dejaremos sus pointers organizados en una tabla.
Rutina REMOVE_MUGSHOT:
Esta segunda rutina carga el buffer de la dirección de OAM en la que se encuentra nuestro mugshot y ejecuta la función predeterminada del ROM para borrar objetos de OAM.
Aquí tenemos la guía en pasos de como instalar la rutina:
Podéis insertarlo donde más os guste, simplemente anotad la dirección. En mi caso inserté la imágen en "A10000" y la paleta en "A10300".
NOTA: La imagen de 64x64 y la paleta de 16 colores.
NOTA: La imagen de 64x64 y la paleta de 16 colores.
En esta tabla almacenaremos los punteros a las imágenes y paletas de nuestros mugshots. Buscamos una dirección con 0x180 bytes de espacio libre y en ella insertamos una tabla con el siguiente formato (en mi caso la insertaré en A00000).
Siendo:
Para los más trabajadores os dejo la tabla hecha:
Código:
XX XX XX 08 YY YY YY 08 ZZ 00 00 00
- "XX XX XX 08" el pointer permutado del mugshot.
- "YY YY YY 08" el pointer permutado de la paleta.
- "ZZ" el espacio del bloque de paletas en la que se cargará (recomendado 0E).
Código:
FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00 FF FF FF 08 FF FF FF 08 00 00 00 00
Yo inserte la imagen en "08A10000" y la paleta en "08A10300", estos offsets, al permutarlos quedan "0000A108" y "0003A108". Además, quiero que la paleta se cargue en la paleta 14 (0E) del foreground.
Voy a la tabla y cambio los valores, dejándolo así:
(Vosotros guardad los cambios, no me seáis gilipollas).
Voy a la tabla y cambio los valores, dejándolo así:
(Vosotros guardad los cambios, no me seáis gilipollas).
En primer lugar hay que decidir dónde vamos a insertar la rutina (unos 0x110 bytes). En mi caso la voy a insertar en "08F00000".
Una vez hecho esto tenemos que editar la rutina para que "conozca" la dirección de la tabla que contiene los pointers y la dirección en la que vamos a insertarla.
Concrétamente hemos de editar:
Cuando hayamos terminado de modificar la rutina, la compilamos e insertamos el código hexadecimal obtenido en la dirección que habíamos decidido previamente.
Una vez hecho esto tenemos que editar la rutina para que "conozca" la dirección de la tabla que contiene los pointers y la dirección en la que vamos a insertarla.
Concrétamente hemos de editar:
- El valor del word "TABLE", introduciendo el offset de la tabla (En mi caso 0x08A00000)
- Donde pone "0x08[OFFSET DE ESTA RUTINA]" debemos poner la dirección donde insertaremos la rutina (en mi caso 0x08F00000)
- Sustitur "NumeroVariableX" por el número de las variables que queréis utilizar, en mi caso "8000", "8001" y "8002".
Código:
.align 2
.thumb
@/* VAR_0 = Número del sprite en la table [0-1F] */@
@/* VAR_1 = Izquierda o derecha [0-1] */@
@/* VAR_2 = Slot del mugshot [0-1] */@
@/* TABLE: [IMAGE][PALETTE][PAL#][00 00 00] */@
main:
push {r0-r7, lr}
ldrh r0, VAR_0 @Número del mugshot en la tabla 0-1F
ldrb r5, [r0]
mov r2, #0xC
mul r5, r2
ldr r1, TABLE @Carga la diracción de la tabla
add r5, r5, r1
ldr r6, [r5]
ldr r0, VAR_2 @Slot del mugshot en el script (si hay más de un mugshot)
ldrb r0, [r0]
mov r7, r0
mov r2, #0x80
lsl r2, r2, #0x4
mul r0, r0, r2
ldr r3, DEST @Dirección de la VRAM en la que se cargará el mugshot
add r3, r3, r0
mov r0, r6
mov r1, r3
swi 0x12 @Extrae la imagen comprimida Lz77 en la VRAM(@0601000 - región ObjTiles)
add r5, #0x4
ldr r0, [r5]
add r5, #0x4
ldrb r1, [r5]
ldr r5, OAMNUM @Dirección de la WRAM en la que se almacena el número del objeto en la OAM
add r5, #0x4D @Se le suma 4D para obtener la dirección de la WRAM donde se almacena el byte de la paleta
strb r1, [r5]
mov r2, #0x20
mul r1, r2
ldr r3, PALBUFFER @Dirección de los buffer de las foreground palletes en la WRAM
add r1, r1, r3 @Calcula la dirección del buffer de la paleta que se está usando.
lsr r2, r2, #0x2
swi 0xC @CpuFastSet
objPosition:
ldr r0, VAR_1 @Lado en el que se posiciona el mugshot [izquierda-derecha]-[0-1]
ldr r5, [r0]
mov r3, #0x88
mul r5, r3
mov r6, #0x30
add r1, r5, r6
cmp r7, #0x0 @comprueba si el slot es "0" o "1"
bne mugshotSlot_1
ldr r0, DATA @Carga Data, correspondiente al slot 0.
b createObj
mugshotSlot_1:
ldr r0, DATA2 @Carga Data2, correspondiente al slot1.
createObj:
mov r2, #0x53
mov r3, #0x0
ldr r4, CREATEOAM @Dirección de la rutina para crear obj en OAM.
bl execute @Ejecuta la rutina predefinida en el ROM para crear instancias en OAM.
ldr r1, OAMNUM @Dirección de la WRAM donde se almacena el número de objeto en la OAM.
add r1, r1, r7
strb r0, [r1]
ldr r2, OAMBUFFER @Dirección de la WRAM donde se encuentra el buffer de los objetos de OAM.
mov r1, #0x44
mul r0, r1
add r0, r0, r2
add r0, #0x5
ldr r3, OAMNUM
add r3, #0x4D @Dirección del statusbyte de la paleta.
ldrb r1, [r3]
lsl r1, r1, #0x4
strb r1, [r0]
end:
pop {r0-r7, pc}
execute: @label para hacer branch y ejecutar la rutina que se encuentre en r4
bx r4
.align 2
@Variables utilizadas (Por defecto podrías usar 8000, 8001 y 8002)
VAR_0:
.word 0x020275D8 + (2 * NumeroVariable0)
VAR_1:
.word 0x020275D8 + (2 * NumeroVariable1)
VAR_2:
.word 0x020275D8 + (2 * NumeroVariable2)
OAMNUM:
.word 0x0203C002 @Dirección de memoria usada para el número de obj en OAM y el número de paleta
TABLE:
.word 0x08[OFFSET DE LA TABLA] @Tabla donde se encuentran los pointer a los mugshots etc.
DEST:
.word 0x06014000 @Dirección de la ObjTile VRAM a partir de la cual se escribirán los mugshots (lo podéis cambiar).
@Buffers a partir de los cuales los datos son rescritos en la RAM (tras cada uno de los ciclos DMA).
PALBUFFER:
.word 0x02037D14 @Buffer de las paletas del foreground
OAMBUFFER:
.word 0x02020630 @Buffer de los objetos de la OAM.
CREATEOAM:
.word 0x08006DF5 @Rutina del ROM para crear instancias en la OAM.
@Estructuras de DATA y DATA2
.align 2
DATA: .word BUFFERDATA + 0x08[OFFSET DE ESTA RUTINA]
BUFFERDATA:
.hword 0x2
.hword 0x1
.word OAMDATA + 0x08[OFFSET DE ESTA RUTINA]
.word ANIMATION + 0x08[OFFSET DE ESTA RUTINA]
.word 0x0
.word 0x08231CFC
.word 0x080EE4DD
.byte 0xFF
.align 2
ANIMATION: .word OAMANIM + 0x08[OFFSET DE ESTA RUTINA]
OAMANIM:
.hword 0x201
.hword 0xA
.hword 0xFFFE
.hword 0x0
.align 2
OAMDATA:
.byte 0x0
.byte 0x0
.byte 0x0
.byte 0xC0
.hword 0x1
.hword 0x0
.align 2
DATA2: .word BUFFERDATA2 + 0x08[OFFSET DE ESTA RUTINA]
BUFFERDATA2:
.hword 0x2
.hword 0x1
.word OAMDATA2 + 0x08[OFFSET DE ESTA RUTINA]
.word ANIMATION2 + 0x08[OFFSET DE ESTA RUTINA]
.word 0x0
.word 0x08231CFC
.word 0x080EE4DD
.byte 0xFF
.align 2
ANIMATION2: .word OAMANIM2 + 0x08[OFFSET DE ESTA RUTINA]
OAMANIM2:
.hword 0x241
.hword 0xA
.hword 0xFFFE
.hword 0x0
.align 2
OAMDATA2:
.byte 0x0
.byte 0x0
.byte 0x0
.byte 0xC0
.hword 0x1
.hword 0x0
Código:
.align 2
.thumb
@/* VAR_0 = Número del sprite en la table [0-1F] */@
@/* VAR_1 = Izquierda o derecha [0-1] */@
@/* VAR_2 = Slot del mugshot [0-1] */@
@/* TABLE: [IMAGE][PALETTE][PAL#][00 00 00] */@
main:
push {r0-r7, lr}
ldrh r0, VAR_0 @Número del mugshot en la tabla 0-1F
ldrb r5, [r0]
mov r2, #0xC
mul r5, r2
ldr r1, TABLE @Carga la diracción de la tabla
add r5, r5, r1
ldr r6, [r5]
ldr r0, VAR_2 @Slot del mugshot en el script (si hay más de un mugshot)
ldrb r0, [r0]
mov r7, r0
mov r2, #0x80
lsl r2, r2, #0x4
mul r0, r0, r2
ldr r3, DEST @Dirección de la VRAM en la que se cargará el mugshot
add r3, r3, r0
mov r0, r6
mov r1, r3
swi 0x12 @Extrae la imagen comprimida Lz77 en la VRAM(@0601000 - región ObjTiles)
add r5, #0x4
ldr r0, [r5]
add r5, #0x4
ldrb r1, [r5]
ldr r5, OAMNUM @Dirección de la WRAM en la que se almacena el número del objeto en la OAM
add r5, #0x4D @Se le suma 4D para obtener la dirección de la WRAM donde se almacena el byte de la paleta
strb r1, [r5]
mov r2, #0x20
mul r1, r2
ldr r3, PALBUFFER @Dirección de los buffer de las foreground palletes en la WRAM
add r1, r1, r3 @Calcula la dirección del buffer de la paleta que se está usando.
lsr r2, r2, #0x2
swi 0xC @CpuFastSet
objPosition:
ldr r0, VAR_1 @Lado en el que se posiciona el mugshot [izquierda-derecha]-[0-1]
ldr r5, [r0]
mov r3, #0x88
mul r5, r3
mov r6, #0x30
add r1, r5, r6
cmp r7, #0x0 @comprueba si el slot es "0" o "1"
bne mugshotSlot_1
ldr r0, DATA @Carga Data, correspondiente al slot 0.
b createObj
mugshotSlot_1:
ldr r0, DATA2 @Carga Data2, correspondiente al slot1.
createObj:
mov r2, #0x53
mov r3, #0x0
ldr r4, CREATEOAM @Dirección de la rutina para crear obj en OAM.
bl execute @Ejecuta la rutina predefinida en el ROM para crear instancias en OAM.
ldr r1, OAMNUM @Dirección de la WRAM donde se almacena el número de objeto en la OAM.
add r1, r1, r7
strb r0, [r1]
ldr r2, OAMBUFFER @Dirección de la WRAM donde se encuentra el buffer de los objetos de OAM.
mov r1, #0x44
mul r0, r1
add r0, r0, r2
add r0, #0x5
ldr r3, OAMNUM
add r3, #0x4D @Dirección del statusbyte de la paleta.
ldrb r1, [r3]
lsl r1, r1, #0x4
strb r1, [r0]
end:
pop {r0-r7, pc}
execute: @label para hacer branch y ejecutar la rutina que se encuentre en r4
bx r4
.align 2
@Variables utilizadas (Por defecto podrías usar 8000, 8001 y 8002)
VAR_0:
.word 0x020275D8 + (2 * 8000)
VAR_1:
.word 0x020275D8 + (2 * 8001)
VAR_2:
.word 0x020275D8 + (2 * 8002)
OAMNUM:
.word 0x0203C002 @Dirección de memoria usada para el número de obj en OAM y el número de paleta
TABLE:
.word 0x08A00000 @Tabla donde se encuentran los pointer a los mugshots etc.
DEST:
.word 0x06014000 @Dirección de la ObjTile VRAM a partir de la cual se escribirán los mugshots (lo podéis cambiar).
@Buffers a partir de los cuales los datos son rescritos en la RAM (tras cada uno de los ciclos DMA).
PALBUFFER:
.word 0x02037D14 @Buffer de las paletas del foreground
OAMBUFFER:
.word 0x02020630 @Buffer de los objetos de la OAM.
CREATEOAM:
.word 0x08006DF5 @Rutina del ROM para crear instancias en la OAM.
@Estructuras de DATA y DATA2
.align 2
DATA: .word BUFFERDATA + 0x08F00000
BUFFERDATA:
.hword 0x2
.hword 0x1
.word OAMDATA + 0x08F00000
.word ANIMATION + 0x08F00000
.word 0x0
.word 0x08231CFC
.word 0x080EE4DD
.byte 0xFF
.align 2
ANIMATION: .word OAMANIM + 0x08F00000
OAMANIM:
.hword 0x201
.hword 0xA
.hword 0xFFFE
.hword 0x0
.align 2
OAMDATA:
.byte 0x0
.byte 0x0
.byte 0x0
.byte 0xC0
.hword 0x1
.hword 0x0
.align 2
DATA2: .word BUFFERDATA2 + 0x08F00000
BUFFERDATA2:
.hword 0x2
.hword 0x1
.word OAMDATA2 + 0x08F00000
.word ANIMATION2 + 0x08F00000
.word 0x0
.word 0x08231CFC
.word 0x080EE4DD
.byte 0xFF
.align 2
ANIMATION2: .word OAMANIM2 + 0x08F00000
OAMANIM2:
.hword 0x241
.hword 0xA
.hword 0xFFFE
.hword 0x0
.align 2
OAMDATA2:
.byte 0x0
.byte 0x0
.byte 0x0
.byte 0xC0
.hword 0x1
.hword 0x0
Esta rutina se encargará de borrar las instancias que hemos creado en la OAM para los mugshots.
No hay que modificarla, por lo que únicamente la tenéis que compilar e insertar donde prefiráis (en mi caso 0x08F00200).
No hay que modificarla, por lo que únicamente la tenéis que compilar e insertar donde prefiráis (en mi caso 0x08F00200).
Código:
.thumb
.align 2
main:
push {r0-r5, lr}
ldr r1, OAMNUM @Comprueba que el OAMNUM sea != 0
ldrb r0, [r1]
cmp r0, #0x0
beq end
mov r2, #0x44
mul r0, r2
ldr r5, OAMBUFFER @Carga el OAMBUFFER en r5
add r0, r0, r5
ldr r4, DELOAM @Carda la dirección de la rutina DELOAM en r4
bl execute
ldr r1, OAMNUM @vuelve a realizar lo mismo para un posible segundo mugshot
add r1, r1, #0x1
ldrb r0, [r1]
cmp r0, #0x0
beq end
mov r2, #0x44
mul r0, r2
ldr r5, OAMBUFFER
add r0, r0, r5
bl execute
end:
pop {r0-r5, pc}
execute:
bx r4 @branch para ejecutar la rutina predefinida por el ROM para destruir instancias de la OAM (DELOAM)
.align 2
OAMNUM:
.word 0x0203C002
OAMBUFFER:
.word 0x02020630 @Buffer de la OAM en WRAM
DELOAM:
.word 0x080080E9 @Función para destruir instancias de la OAM.
Para utilizar la rutina debemos crear un script en el que llamemos a la rutina LOAD_MUGSHOT cuando queramos mostrarlo y a la rutina REMOVE_MUGSHOT cuando deseemos eliminarlo. Antes de llamar a la "rutina constructora" debemos establecer los valores de las 3 variables que hemos escogido (8000, 8001 y 8002) para definir como se va a ejecutar:
Yo en este ejemplo voy a mostrar el mugshot número "0" a la derecha "1", con slot "0".
Y bueno, así es como queda:
¿Qué?¿Cómo que qué es esa mierda? Venga, no me toques los cojones, que es un placeholder.
- VAR_0: Define el número del sprite en la tabla.
- VAR_1: Colocará el mugshot a la izquierda (0) o a la derecha (1)
- VAR_2: Definirá el slot del mugshot (en caso de que deseemos mostrar más de uno)
Yo en este ejemplo voy a mostrar el mugshot número "0" a la derecha "1", con slot "0".
Código:
#dynamic 0xF00300
#org @start
lock
faceplayer
setvar 0x8000 0x0
setvar 0x8001 0x1
setvar 0x8002 0x0
callasm 0x08[Pointer de LOAD_MUGSHOT+1]
msgbox @txt 0x6
waitmsg
callasm 0x08[Poiner de REMOVE_MUGSHOT+1]
release
end
#org @txt
= Me gusta la creepy chicken.
¿Qué?¿Cómo que qué es esa mierda? Venga, no me toques los cojones, que es un placeholder.
Créditos a:
- Kyoko1, por la rutina original en FR (L).
- Knizz, por ayudarle con el funcionamiento de OAM.
- ~Ruki!, por traer la rutina a este foro.
- A mi culo, por aguantar sin cagar mientras termino de escribir este post.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Creo que es importante decir que he encontrado ciertas direcciones importantes para Emerald durante mi "investigación", por lo que voy a aprovechar para exponerlo aquí de forma algo resumida (si le sirve a alguien perfecto):
- El Buffer de las paletas:
- BufferOAM:
Al igual que con la paleta, si se desea realizar cualquier cambio sobre una instancia del OAM, este debe hacerse en su buffer, o será reescrito.
- Rutinas internas del juego "CreateOAM" y "DeleteOAM":
Por último, creo que es importante decir algo:
POR FAVOR, COMENTAD VUESTRO CÓDIGO. En serio, sobretodo si es assembly, comentadlo, si no lo hacéis es muchísimo más difícil modificar cualquier rutina (básicamente porque es complicado saber que cojones hace). Poned comentarios aún si no vais a hacer pública la rutina, porque si no, cuando la vayáis a editar en unos meses no vais a saber que mierdas era lo que hicisteis.
PD: los comentarios en el compilador que tengo yo aquí son con "@".
POR FAVOR, COMENTAD VUESTRO CÓDIGO. En serio, sobretodo si es assembly, comentadlo, si no lo hacéis es muchísimo más difícil modificar cualquier rutina (básicamente porque es complicado saber que cojones hace). Poned comentarios aún si no vais a hacer pública la rutina, porque si no, cuando la vayáis a editar en unos meses no vais a saber que mierdas era lo que hicisteis.
PD: los comentarios en el compilador que tengo yo aquí son con "@".
Última edición: