Si me llegan a decir hace dos años que volvería a hacer un tutorial con algo relacionado con el ROMHacking, no me lo habría creido... Pero como no me lo dijo nadie, aquí estamos. No diré de vuelta en acción porque sería adelantarme demasiado, pero sí al menos con curiosidad por estas descompilaciones de los juegos de tercera generación. En mi caso, la que más me ha llamado la atención es, sin duda alguna, la descompilación de Pokémon Esmeralda puesto que se trata de la base más avanzada dentro de la tercera generación, a la cual podemos aplicarle ahora un sinfín de cambios de manera mucho más directa y sencilla.
Sin más preámbulo, vamos con el tutorial:
Mostrar la hora al desplegar el menú
El objetivo del tutorial es, como su nombre indica, conseguir mostrar en el menú de pausa, una cajita extra con la hora actual, basándonos en el RTC y no en el sistema de hora interno del juego. Esto por sí sólo no es más que replicar en la pantalla un dato, pero con algo de creatividad y ampliando el código un poco, puede utilizarse también para crear eventos dependientes de la hora del día a la que se juegue (porque el sistema está enlazado a la hora del dispositivo con el que juguemos, y no la interna del propio juego).
Finalmente, obtendremos algo como esto:
Para ello tendremos que modificar los siguientes archivos:
Y con esa última modificación ya habríamos terminado. Aunque me ha llevado un buen rato sacarlo, el principal problema ha sido comprender cómo funciona más o menos el RTC (no el reloj del juego) para poder mostrar los datos de forma correcta.
Espero que os sirva de ayuda y os anime un poquito a trabajar con pokeemerald :awesome:
Un saludo, BLAx501!
P.D. Agradecimientos a @Sayer301! y a @Dani_SR_17 que han estado conmigo por Discord y me han ayudado a entender la fórmula que usaba el RTC para sacar valores tan raros en las horas y minutos.
Sin más preámbulo, vamos con el tutorial:
Mostrar la hora al desplegar el menú
El objetivo del tutorial es, como su nombre indica, conseguir mostrar en el menú de pausa, una cajita extra con la hora actual, basándonos en el RTC y no en el sistema de hora interno del juego. Esto por sí sólo no es más que replicar en la pantalla un dato, pero con algo de creatividad y ampliando el código un poco, puede utilizarse también para crear eventos dependientes de la hora del día a la que se juegue (porque el sistema está enlazado a la hora del dispositivo con el que juguemos, y no la interna del propio juego).
Finalmente, obtendremos algo como esto:
Para ello tendremos que modificar los siguientes archivos:
- rtc.h que se encuentra en la carpeta include
- rtc.c que se encuentra en la carpeta src
- start_menu.c que se encuentra en la carpeta src
rtc.h: En este archivo es donde menos cosas tenemos que tocar. Únicamente es necesario añadir la definición de las funciones que crearemos dentro de rtc.c. Así que, al final del archivo, antes de la línea donde pone:
Colocamos lo siguiente:
Personalmente, recomiendo que añadáis un comentario para indicar la fecha en que habéis añadido dicho cambio a la rom para evitar posibles problemas a futuro.
Con eso terminamos de editar este fichero.
Código:
[PLAIN]#endif // GUARD_RTC_UTIL_H[/PLAIN]
Código:
[PLAIN]
u8 Rtc_GetCurrentHour(void);
u8 Rtc_GetCurrentMinute(void);
void FormatDecimalTimeWOSeconds(u8 *dest, u8 hour, u8 minute);
[/PLAIN]
Con eso terminamos de editar este fichero.
rtc.c: En este archivo tenemos que crear las funciones que hemos definido en su fichero de cabecera, así que, de nuevo, al final del fichero escribimos lo siguiente
Las dos primeras funciones toman el valor en crudo del RTC y lo convierten a un número legible. Si os preguntáis por la chapuza de los ifs, es para corregir el dato que nos propociona el RTC, porque realiza una operación muy rara.
El valor proporcionado por el RTC en horas y minutos viene dado por una fórmula un tanto extraña según he podido comprobar en varias pruebas:
Siendo X1X2 los dígitos de decenas y unidades en las dos cifras, horas y minutos, el valor que devuelve el RTC es 6*X1+(X1+X2).
Así, cuando eran las 20:12 en la vida real, para el RTC eran las 32:18; una hora un poco rara, vaya.
La tercera función es una copia modificada de una función presente en el código del juego que permite colocar en un puntero a u8 (es decir, el formato de los 4 buffers de texto) los datos de horas y minutos en formato legible, separados por dos puntos.
Código:
[PLAIN]
u8 Rtc_GetCurrentHour(void){ // Toma el valor de la hora actual del RTC
RtcGetInfo(&sRtc);
if(sRtc.hour>25){
return sRtc.hour-12;
}
else if(sRtc.hour>9){
return sRtc.hour-6;
}
return sRtc.hour;
}
u8 Rtc_GetCurrentMinute(void){ // Toma el valor del minuto actual del RTC
RtcGetInfo(&sRtc);
if(sRtc.minute>73){
return sRtc.minute-30;
}
else if(sRtc.minute>57){
return sRtc.minute-24;
}
else if(sRtc.minute>41){
return sRtc.minute-18;
}
else if(sRtc.minute>25){
return sRtc.minute-12;
}
else if(sRtc.minute>9){
return sRtc.minute-6;
}
return sRtc.minute;
}
void FormatDecimalTimeWOSeconds(u8 *dest, u8 hour, u8 minute) // Función para obtener los datos de hora y minutos del RTC
{
dest = ConvertIntToDecimalStringN(dest, hour, STR_CONV_MODE_LEADING_ZEROS, 2);
*dest++ = CHAR_COLON;
dest = ConvertIntToDecimalStringN(dest, minute, STR_CONV_MODE_LEADING_ZEROS, 2);
*dest = EOS;
}
[/PLAIN]
El valor proporcionado por el RTC en horas y minutos viene dado por una fórmula un tanto extraña según he podido comprobar en varias pruebas:
Siendo X1X2 los dígitos de decenas y unidades en las dos cifras, horas y minutos, el valor que devuelve el RTC es 6*X1+(X1+X2).
Así, cuando eran las 20:12 en la vida real, para el RTC eran las 32:18; una hora un poco rara, vaya.
La tercera función es una copia modificada de una función presente en el código del juego que permite colocar en un puntero a u8 (es decir, el formato de los 4 buffers de texto) los datos de horas y minutos en formato legible, separados por dos puntos.
start_menu.c: Y llegamos al meollo del asunto. En este fichero es donde más modificaciones hay que hacer, y no es para menos, pues es donde se realizan las llamadas a las funciones creadas antes y se trabajan los gráficos del menú de pausa.
En primer lugar vamos a hacer un include de rtc.h para poder llamar a las funciones creadas en esos ficheros desde este, así que al comienzo del documento, donde están todos los include añadimos el nuestro tal que así:
A continuación vamos a realizar dos definiciones en el fichero. Primero tenemos que crear la estructura de la ventana extra que vamos a crear, así que buscamos en el fichero la siguiente línea:
Y justo debajo de ella pegamos lo siguiente:
Lo segundo que tenemos que definir es es la función que crearemos a continuación. Esta función estará definida en el ámbito de este fichero y nada más, por lo que realizamos la definición en el propio fichero de código y no es su cabecera como hicimos antes en los ficheros del rtc. Así que buscamos el trozo de código donde están definidas todas las funciones que se usan en este fichero (podemos buscar por // Local functions y las localizaremos rápido) y al final de todas colocamos lo siguiente:
Ahora crearemos la función estática, así que, como antes, nos vamos al final del fichero (o a algún sitio que reconozcáis fácilmente) y colocamos lo siguiente:
Esta función es la encargada de crear la ventana auxiliar, tomar los datos del RTC mediante las funciones que creamos antes, formatearlo para que se pueda leer y finalmente añadirlo a la ventana que hemos creado.
Para acabar, tenemos que colocar en alguna parte del código esta función que hemos creado. Buscaremos en el fichero la siguiente función:
Y añadimos al final de la función, antes de la llave que la cierra una llamada a la función creada antes:
Pero con esto no basta ya que si lo dejásemos aquí al cerrar el menú de pausa no se quitaría la pequeña cajita que hemos añadido. Por lo que navegaremos hasta la siguiente función:
Y para poder borrar nuestra caja tendremos que añadir el siguiente fragmento de código modificando el existente para que quede así:
En primer lugar vamos a hacer un include de rtc.h para poder llamar a las funciones creadas en esos ficheros desde este, así que al comienzo del documento, donde están todos los include añadimos el nuestro tal que así:
Código:
[PLAIN]
#include "rtc.h"
[/PLAIN]
Código:
[PLAIN]
static const struct WindowTemplate sSafariBallsWindowTemplate = {0, 1, 1, 9, 4, 0xF, 8};
[/PLAIN]
Código:
[PLAIN]
static const struct WindowTemplate sStartMenuWindowTemplate = {0, 1, 1, 4, 2, 0xF, 8}; // Parámetros de la ventana extra
[/PLAIN]
Código:
[PLAIN]
static void ShowStartMenuExtraWindow(void);
[/PLAIN]
Código:
[PLAIN]
static void ShowStartMenuExtraWindow(void) // Función que carga una ventana auxiliar en el menú de pausa.
{
sSafariBallsWindowId = AddWindow(&sStartMenuWindowTemplate);
PutWindowTilemap(sSafariBallsWindowId);
NewMenuHelpers_DrawStdWindowFrame(sSafariBallsWindowId, FALSE);
FormatDecimalTimeWOSeconds(gStringVar4, Rtc_GetCurrentHour(), Rtc_GetCurrentMinute());
AddTextPrinterParameterized(sSafariBallsWindowId, 1, gStringVar4, 0, 1, 0xFF, NULL);
CopyWindowToVram(sSafariBallsWindowId, 2);
}
[/PLAIN]
Para acabar, tenemos que colocar en alguna parte del código esta función que hemos creado. Buscaremos en el fichero la siguiente función:
Código:
[PLAIN]
static void BuildNormalStartMenu(void)
[/PLAIN]
Código:
[PLAIN]
ShowStartMenuExtraWindow();
[/PLAIN]
Código:
[PLAIN]
static void RemoveExtraStartMenuWindows(void)
[/PLAIN]
Código:
[PLAIN]
static void RemoveExtraStartMenuWindows(void) //Modificado el 23/1/2019
{
if (GetSafariZoneFlag())
{
sub_8198070(sSafariBallsWindowId, FALSE);
CopyWindowToVram(sSafariBallsWindowId, 2);
RemoveWindow(sSafariBallsWindowId);
}else if (InBattlePyramid()) //Antes eran dos if separados
{
sub_8198070(sBattlePyramidFloorWindowId, FALSE);
RemoveWindow(sBattlePyramidFloorWindowId);
}
else{ //Borra de la pantalla la venta auxiliar de la hora
sub_8198070(sSafariBallsWindowId, FALSE);
RemoveWindow(sSafariBallsWindowId);
}
}
[/PLAIN]
Y con esa última modificación ya habríamos terminado. Aunque me ha llevado un buen rato sacarlo, el principal problema ha sido comprender cómo funciona más o menos el RTC (no el reloj del juego) para poder mostrar los datos de forma correcta.
Espero que os sirva de ayuda y os anime un poquito a trabajar con pokeemerald :awesome:
Un saludo, BLAx501!
P.D. Agradecimientos a @Sayer301! y a @Dani_SR_17 que han estado conmigo por Discord y me han ayudado a entender la fórmula que usaba el RTC para sacar valores tan raros en las horas y minutos.