diff --git a/build.ps1 b/build.ps1 index f1de5ea..a53e3ed 100644 --- a/build.ps1 +++ b/build.ps1 @@ -19,7 +19,8 @@ $input_c_files = "src/c/status.c", "src/c/battle.c", "src/c/equip.c", - "src/c/psi.c" + "src/c/psi.c", + "src/c/luminehall.c" $base_c_address = 0x83755B8; $scripttool_cmd = "bin/ScriptTool/ScriptTool.dll" diff --git a/src/c/ext.c b/src/c/ext.c index bfc5e83..708baac 100644 --- a/src/c/ext.c +++ b/src/c/ext.c @@ -3,6 +3,7 @@ void __attribute__((naked)) cpufastset(void *source, void *dest, int mode) {} void __attribute__((naked)) cpuset(void *source, void *dest, int mode) {} byte* __attribute__((naked)) m2_strlookup(int *offset_table, byte *strings, int index) {} +unsigned short* __attribute__((naked)) m2_get_hall_address() {} int __attribute__((naked)) bin_to_bcd(int value, int* digit_count) {} int __attribute__((naked)) m2_drawwindow(WINDOW* window) {} int __attribute__((naked)) m2_resetwindow(WINDOW* window, bool skip_redraw) {} diff --git a/src/c/luminehall.c b/src/c/luminehall.c new file mode 100644 index 0000000..9151d72 --- /dev/null +++ b/src/c/luminehall.c @@ -0,0 +1,305 @@ +#include "luminehall.h" +#include "locs.h" + +void writeLumineHallText() +{ + //Function that writes out the Lumine text in the form of arrangements. + //It adds a VWF to what the base game did + byte *lumineText_curr_ptr = luminetext; + int *hallLineSize = &m2_hall_line_size; + int length = 0; + int currLenInTile = 0; + int currPos = 0; + int Tiles[4]; + setTilesToBlank(Tiles); + //First things first, it calculates the total length of the text in pixels + while((*lumineText_curr_ptr) != END) + { + length += getCharWidth(*lumineText_curr_ptr); + lumineText_curr_ptr++; + } + //It then gets the length in arrangements. It also makes sure it's a number divisible by 8. + //This avoids having empty tiles showing + length = (length & 8 == 0 ? length : (length + (8 - (length & 7)))) >> 1; + (*hallLineSize) = length; + lumineText_curr_ptr = luminetext; + unsigned short *hallAddress = m2_get_hall_address(); + //Prints out the characters + while((*lumineText_curr_ptr) != END) + { + readLumineCharacter(*lumineText_curr_ptr, Tiles, hallAddress, length, &currPos, &currLenInTile); + lumineText_curr_ptr++; + } + //Avoid having tiles that were not entirely printed + while((currPos*4) < length) + { + printVoidLumineTiles(hallAddress, length, currPos); + currPos++; + } +} + +void readLumineCharacter(byte chr, int *Tiles, unsigned short *hallAddress, int length, int *currPos, int *currLen) +{ + //Reads a character. Handles special cases. + //The valid characters are printed to the Tiles 1bpp buffer that stores the VWF form of the text. + //This is then converted (when it makes sense to do so) to arrangements by printLumineTiles. + int tileWidth = 2; + int tileHeight = 2; + int chosenLen; + int renderedLen; + byte *glyphRows; + int AlternativeTiles[4]; + switch(chr) + { + case END: + return; + case ENDL: + printEmptyLumineTile(Tiles, hallAddress, length, *currPos, *currLen); + (*currPos)++; + return; + case BLANK: + printEmptyLumineTile(Tiles, hallAddress, length, *currPos, *currLen); + for(int i = 1; i < 8; i++) + printVoidLumineTiles(hallAddress, length, (*currPos) + i); + (*currPos) += 8; + return; + case PC_START: + case PC_START+1: + case PC_START+2: + case PC_START+3: + readLumineCharacterName(pc_names+(chr-PC_START)*PC_NAME_SIZE, Tiles, AlternativeTiles, hallAddress, length, currPos, currLen); + return; + default: + chosenLen = m2_widths_table[0][chr] & 0xFF; + renderedLen = m2_widths_table[0][chr] >> 8; + glyphRows = &m2_font_table[0][chr * tileWidth * tileHeight * 8]; + } + if(chosenLen <= 0) + return; + if(((*currLen) + chosenLen) >= 8) + { + setTilesToBlank(AlternativeTiles); + if(renderedLen > 0) + printLumineCharacterInMultiTiles(Tiles, AlternativeTiles, glyphRows, renderedLen, *currLen); + printLumineTiles(Tiles, hallAddress, length, *currPos); + (*currPos)++; + copyTiles(Tiles, AlternativeTiles); + (*currLen) = ((*currLen) + chosenLen) & 7; + } + else + { + if(renderedLen > 0) + printLumineCharacterInSingleTiles(Tiles, glyphRows, renderedLen, *currLen); + (*currLen) += chosenLen; + } +} + +void printEmptyLumineTile(int *Tiles, unsigned short *hallAddress, int length, int currPos, int currLen) +{ + //Prints either an entirely empty tile or what's been done in the current tile + if(currLen == 0) + printVoidLumineTiles(hallAddress, length, currPos); + else + { + printLumineTiles(Tiles, hallAddress, length, currPos); + setTilesToBlank(Tiles); + } +} + +void readLumineCharacterName(byte* str, int *Tiles, int* AlternativeTiles, unsigned short *hallAddress, int length, int *currPos, int *currLen) +{ + //Reads a playable character's name. + //The characters are printed to the Tiles 1bpp buffer that stores the VWF form of the text. + //This is then converted (when it makes sense to do so) to arrangements by printLumineTiles. + //This is separate in order to avoid recursive issues caused by user's tinkering + int tileWidth = 2; + int tileHeight = 2; + int chosenLen; + int renderedLen; + byte *glyphRows; + for(int i = 0; i < PC_NAME_SIZE; i++) + { + if(*(str + 1) == 0xFF) + return; + byte chr = decode_character(*(str++)); + chosenLen = m2_widths_table[0][chr] & 0xFF; + renderedLen = m2_widths_table[0][chr] >> 8; + glyphRows = &m2_font_table[0][chr * tileWidth * tileHeight * 8]; + if(chosenLen <= 0) + continue; + if(((*currLen) + chosenLen) >= 8) + { + setTilesToBlank(AlternativeTiles); + if(renderedLen > 0) + printLumineCharacterInMultiTiles(Tiles, AlternativeTiles, glyphRows, renderedLen, *currLen); + printLumineTiles(Tiles, hallAddress, length, *currPos); + (*currPos)++; + copyTiles(Tiles, AlternativeTiles); + (*currLen) = ((*currLen) + chosenLen) & 7; + } + else + { + if(renderedLen > 0) + printLumineCharacterInSingleTiles(Tiles, glyphRows, renderedLen, *currLen); + (*currLen) += chosenLen; + } + } + +} + +void printLumineCharacterInMultiTiles(int *Tiles, int *AlternativeTiles, byte *glyphRows, int glyphLen, int currLen) +{ + //Prints a character to the tiles 1bpp buffer. + //The part that goes beyond the tiles buffer will be printed to the AlternativeTiles buffer + int tileWidth = 2; + int tileHeight = 2; + int startY = START_Y; + + for(int dTileY = 0; dTileY < tileHeight; dTileY++) + { + int tileIndex = dTileY * 2; + for(int half = 0; half < 2; half++) + { + int tile = Tiles[tileIndex + half]; + int alternativeTile = AlternativeTiles[tileIndex + half]; + int endingTile = 0; + int endingAlternativeTile = 0; + for(int row = 0; row < 4; row++) + { + unsigned short canvasRow = ((tile >> (8 * row))&0xFF) | (((alternativeTile >> (8 * row))&0xFF) << 8); + unsigned short glyphRow = 0; + if(row + (half * 4) - startY >= 0) + glyphRow = glyphRows[row + (half * 4) - startY + (dTileY * 8 * tileWidth)] << currLen; + else if(dTileY == 1) + glyphRow = glyphRows[row + (half * 4) - startY + 8] << currLen; + canvasRow |= glyphRow; + endingTile |= (canvasRow & 0xFF) << (8 * row); + endingAlternativeTile |= ((canvasRow >> 8) & 0xFF) << (8 * row); + } + Tiles[tileIndex + half] = endingTile; + AlternativeTiles[tileIndex + half] = endingAlternativeTile; + } + } +} + +void printLumineCharacterInSingleTiles(int *Tiles, byte *glyphRows, int glyphLen, int currLen) +{ + //Prints a character to the tiles 1bpp buffer. + //We know this won't go outside of the buffer's range, so we avoid some checks + int tileWidth = 2; + int tileHeight = 2; + int startY = START_Y; + + for(int dTileY = 0; dTileY < tileHeight; dTileY++) + { + int tileIndex = dTileY * 2; + for(int half = 0; half < 2; half++) + { + int tile = Tiles[tileIndex + half]; + int endingTile = 0; + for(int row = 0; row < 4; row++) + { + byte canvasRow = ((tile >> (8 * row))&0xFF); + byte glyphRow = 0; + if(row + (half * 4) - startY >= 0) + glyphRow = glyphRows[row + (half * 4) - startY + (dTileY * 8 * tileWidth)] << currLen; + else if(dTileY == 1) + glyphRow = glyphRows[row + (half * 4) - startY + 8] << currLen; + canvasRow |= glyphRow; + endingTile |= canvasRow << (8 * row); + } + Tiles[tileIndex + half] = endingTile; + } + } +} + +void copyTiles(int *Tiles, int* AlternativeTiles) +{ + for(int i = 0; i < 4; i++) + Tiles[i] = AlternativeTiles[i]; +} + +void setTilesToBlank(int *Tiles) +{ + for(int i = 0; i < 4; i++) + Tiles[i] = 0; +} + +void printLumineTiles(int *Tiles, unsigned short *hallAddress, int length, int currPos) +{ + //Converts what is written in 1bpp in the Tiles buffer to arrangements + unsigned short *start = hallAddress + (currPos * 4); + int currHalfTile; + int value; + for(int k = 0; k < 4; k++) + { + for(int i = 0; i < 2; i++) + { + currHalfTile = (Tiles[k] >> i*16)&0xFFFF; + for(int j = 0; j < 4; j++) + { + value = (currHalfTile&3)+((currHalfTile>>6)&0xC); + start[j] = luminesquaretable[value]; + currHalfTile = currHalfTile >> 2; + } + start += length; + } + } + +} + +void printVoidLumineTiles(unsigned short *hallAddress, int length, int currPos) +{ + //Prints empty arrangements fast + unsigned short *start = hallAddress + (currPos*4); + for(int k = 0; k < 4; k++) + { + for(int i = 0; i < 2; i++) + { + for(int j = 0; j < 4; j++) + { + start[j] = luminesquaretable[0]; + } + start += length; + } + } + +} + +int getCharWidth(byte chr) +{ + //Gets the length for a character. Also handles special cases + switch(chr) + { + case END: + return 0; + case ENDL: + return ENDL_SIZE; + case BLANK: + return BLANK_SIZE; + case PC_START: + case PC_START+1: + case PC_START+2: + case PC_START+3: + return getPCWidth(pc_names+(chr-PC_START)*PC_NAME_SIZE); + default: + return m2_widths_table[0][chr] & 0xFF; + } +} + +int getPCWidth(byte* pc_ptr) +{ + //Gets the length for a playable character's name. + //This is separate in order to avoid recursive issues caused by user's tinkering + int length = 0; + byte chr; + for(int i = 0; i < PC_NAME_SIZE; i++) + { + chr = *(pc_ptr+i+1); + if(chr == 0xFF) + return length; + chr = decode_character(*(pc_ptr+i)); + length += m2_widths_table[0][chr] & 0xFF; + } + return length; +} \ No newline at end of file diff --git a/src/c/luminehall.h b/src/c/luminehall.h new file mode 100644 index 0000000..acb7840 --- /dev/null +++ b/src/c/luminehall.h @@ -0,0 +1,36 @@ +#ifndef HEADER_LUMINE_INCLUDED +#define HEADER_LUMINE_INCLUDED + +#include "types.h" +#include "locs.h" +#include "vwf.h" + +#define START_Y 2 +#define END 0xFF +#define ENDL 0xFE +#define ENDL_SIZE 0x8 +#define BLANK 0xFD +#define BLANK_SIZE 0x40 +#define PC_START 0xF9 +#define PC_NAME_SIZE 5 + +void writeLumineHallText(); +int getCharWidth(byte chr); +int getPCWidth(byte* pc_ptr); +void setTilesToBlank(int *Tiles); +void printLumineTiles(int *Tiles, unsigned short *hallAddress, int length, int currPos); +void printVoidLumineTiles(unsigned short *hallAddress, int length, int currPos); +void printEmptyLumineTile(int *Tiles, unsigned short *hallAddress, int length, int currPos, int currLen); +void readLumineCharacter(byte chr, int *Tiles, unsigned short *hallAddress, int length, int *currPos, int *currLen); +void readLumineCharacterName(byte* str, int *Tiles, int* AlternativeTiles, unsigned short *hallAddress, int length, int *currPos, int *currLen); +void printLumineCharacterInMultiTiles(int *Tiles, int *AlternativeTiles, byte *glyphRows, int glyphLen, int currLen); +void printLumineCharacterInSingleTiles(int *Tiles, byte *glyphRows, int glyphLen, int currLen); +void copyTiles(int *Tiles, int* AlternativeTiles); + + +extern unsigned short* m2_get_hall_address(); + +extern int m2_hall_line_size; +extern byte luminetext[]; +extern unsigned short luminesquaretable[]; +#endif \ No newline at end of file diff --git a/src/data/lumine-char-tilemap.bin b/src/data/lumine-char-tilemap.bin deleted file mode 100644 index ac15881..0000000 Binary files a/src/data/lumine-char-tilemap.bin and /dev/null differ diff --git a/src/data/lumine-table.tbl b/src/data/lumine-table.tbl new file mode 100644 index 0000000..272669d --- /dev/null +++ b/src/data/lumine-table.tbl @@ -0,0 +1,117 @@ +00=\x00 +01=\x01 +17=\x17 +1C=\x1C +26=\x26 +30=\x30 +3E=\x3E +44=\x44 +4A=\x4A +4C=\x4C +56=\x56 + +FF=\end +FE=\n +FD=\blank +00= +01=! +02=" +03=# +04=$ +05=% +06=& +07=' +08=( +09=) +0A=* +0B=+ +0C=, +0D=- +0E=. +0F=/ +10=0 +11=1 +12=2 +13=3 +14=4 +15=5 +16=6 +17=7 +18=8 +19=9 +1A=: +1B=; +1C=< +1D== +1E=> +1F=? +20=@ +21=A +22=B +23=C +24=D +25=E +26=F +27=G +28=H +29=I +2A=J +2B=K +2C=L +2D=M +2E=N +2F=O +30=P +31=Q +32=R +33=S +34=T +35=U +36=V +37=W +38=X +39=Y +3A=Z +3B=[8B] +3C=[8C] +3D=[8D] +3E=[8E] +3F=[8F] +40=` +41=a +42=b +43=c +44=d +45=e +46=f +47=g +48=h +49=i +4A=j +4B=k +4C=l +4D=m +4E=n +4F=o +50=p +51=q +52=r +53=s +54=t +55=u +56=v +57=w +58=x +59=y +5A=z +5B={ +5C=| +5D=} +5E=~ +5F=[AF] + +F9=\cness +FA=\cpaula +FB=\cjeff +FC=\cpoo +/00 \ No newline at end of file diff --git a/src/data/lumine-text.asm b/src/data/lumine-text.asm new file mode 100644 index 0000000..c58f8c6 --- /dev/null +++ b/src/data/lumine-text.asm @@ -0,0 +1,8 @@ +.loadtable "data/lumine-table.tbl" + +.strn "\blank" +.strn "I'm \cness....\nIt's been a long road getting here...\n" +.strn "Soon, I'll be...\nSoon, I'll be...\nSoon, I'll be...\n" +.strn "What will happen to us?\nW...what's happening?\n" +.strn "My thoughts are being written out on the wall...\nor are they?" +.str "\blank\end" diff --git a/src/data/luminesquaretable.bin b/src/data/luminesquaretable.bin new file mode 100644 index 0000000..e03a89b --- /dev/null +++ b/src/data/luminesquaretable.bin @@ -0,0 +1 @@ +æ¬ ¬ ª ¬ç«© ¬«ç© ª©©¨ \ No newline at end of file diff --git a/src/m2-hack.asm b/src/m2-hack.asm index 43db3ea..740e515 100644 --- a/src/m2-hack.asm +++ b/src/m2-hack.asm @@ -1712,11 +1712,7 @@ nop // Lumine Hall hacks //============================================================================== -.org 0x82DCF94 -lumine_char_tilemap: -.area 4000h,00h -.incbin "data/lumine-char-tilemap.bin" -.endarea +.org 0x800ECB2 :: bl writeLumineHallText //============================================================================== // Cartridge choosing screen hacks @@ -1822,6 +1818,12 @@ m2_nybbles_to_bits: m2_enemy_attributes: .incbin "data/m2-enemy-attributes.bin" +luminesquaretable: +.incbin "data/luminesquaretable.bin" + +luminetext: +.include "data/lumine-text.asm" + flyovertextYear: .include "data/flyover-text-year.asm" @@ -1882,6 +1884,7 @@ disclaimer_map: .definelabel buffer_subtractor ,0x0000800 .definelabel overworld_buffer ,0x200F200 +.definelabel m2_hall_line_size ,0x3000374 .definelabel m2_ness_data ,0x3001D54 .definelabel m2_ness_name ,0x3001F10 .definelabel m2_old_paula_name ,0x3001F16 @@ -1911,6 +1914,7 @@ disclaimer_map: .definelabel m2_change_naming_space ,0x8004E08 .definelabel m2_copy_name_temp_mem ,0x8004E34 .definelabel m2_insert_default_name ,0x8005708 +.definelabel m2_get_hall_address ,0x800D7BC .definelabel m12_dim_palette ,0x80137DC .definelabel m2_enable_script ,0x80A1F6C .definelabel m2_sub_a334c ,0x80A334C