From d3826d76621068d1ba479b4a5c499d09fefe818e Mon Sep 17 00:00:00 2001 From: Lorenzo Carletti Date: Wed, 19 Aug 2020 12:30:28 +0200 Subject: [PATCH] Lumine Hall's VWF --- build.ps1 | 3 +- src/c/ext.c | 1 + src/c/luminehall.c | 305 +++++++++++++++++++++++++++++++ src/c/luminehall.h | 36 ++++ src/data/lumine-char-tilemap.bin | Bin 16384 -> 0 bytes src/data/lumine-table.tbl | 117 ++++++++++++ src/data/lumine-text.asm | 8 + src/data/luminesquaretable.bin | 1 + src/m2-hack.asm | 14 +- 9 files changed, 479 insertions(+), 6 deletions(-) create mode 100644 src/c/luminehall.c create mode 100644 src/c/luminehall.h delete mode 100644 src/data/lumine-char-tilemap.bin create mode 100644 src/data/lumine-table.tbl create mode 100644 src/data/lumine-text.asm create mode 100644 src/data/luminesquaretable.bin 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 ac15881529f5208d1dc23efbabb8545300c863a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmds-TXN$#3`LWVe57i!|1FFYuglrS;NtR-5-3^9*FKK>T=gZ61bM|NqJ-t1eN5nls>Lnj&?BGLJJd&e7 zo}Mj;<0{2tWGsp2_-xslKlD1ky@Ak1;vOM+PzQ)zrQw4|a`Z->SbQ%M_Xzj0zz#Wd z#UnZT==~HD_Xx=cJ?Q_1FSj-q|Ce>V17Ckp*IA^0zP3K9{;I4-*M&AB*BTw47x!ja zxQ}0-K-Xq@aIGI&+y3VG&Y;`LUQyEj!e8@?{n@(FqmO6}<&*2~<2Cz>do-)A(em3% z$n2k9xBfaFNdKg+v#5QAVqCQTrhnH%T=B)Z zwAJ}N#sz(JnnhS=e(`rve|U{vuDml2HuxPh`yl6m2juii)vNpK<6Fi-kG_7kZuHWB ztD9IJhqljJMNu?8O-BV=-PGT(jY{Nsjad>`jKY?kC9b?e#S;Rqb_GMXnRn}f4LH1t5V_gb=vS2@j|3SBXRrl}sJ>FtJB}vOJv>gYm`=49vIHXIRuAaQ!J70B5 zNBzlp`-B92Y2#q2?9l`58F}6RSU>(wcGw#)?VKFl(0SzF5ij`J9}?s4t$kmB-@5-v z%NTd+*SKrM=&kT&U+NF75w}0jk`Hlt&b|KZ;>5vEXdl#%pRm}hUwvPB(f_Nz+W)<) zTht$YSbe$H?|%O9K)>&OoIXuoZ_no%-%Dg3+1DeVq{v!yJI^=1Y4z;JZ~oX%4u76U zw<9(J*h(pW9|x<`J5%)6?`_*ecOU7z=!-Vwj%v#d+D zmB!zauY35v9hY_8tH0K{X9pj|4tfvK`zhSZqw?o`yxyXR9J(SaNaa@=KU>&(SX=4i zo%G695I&u4bk5c>SNb*n9<5Hi&dF~g`pBRwdKDY*Rh2{IXGZrijiXn3pQp8=HM*fr z&9Y{v0n+KPHxl;<6=SSD^1rU%r_}xWsOKmSdNZ<8l0O+RF45s4D^cXxI$|3y^y3_y z(hlGRy?4`V0$ zwa4=(WQUmwMsVKKL8)l9%ZINnK}A`wM;35x)au-*r^y@7O+( zNzORv1Sr}_^~yy2k+=1AV!yuG!cW2vA1`94Vfo@+XtbMiKF?uI}5S;%81EI-Ot&rN>^A3wnh@9?Fkek~e1Wae2u zGzQHMVy$m0@}#b_NEY9Y`Aj29#Wo6G75{AA=zUe(KgADqoy9*T@2|>(y7AX(%{%5^ z%(>2Q^UwViE2Gy}cxw$PFy7bn4h>@caa>0~Yd`982AzJ2>hpSw&gJj?CDVRJzV$n} zV+`~O#&q~vgk2;;NR%6TUE%!~qVLwH@B`y6}zq@}~5>H&q#q&phSNybJ?|+n` zGOqPUesTFbZ|bLCfm&x(hiSe^_Gk<~a@T1}cJ9N{(`EhE`#YU{{~q4su(~t;>v+*Y z(z8%^vEJuNmV9zd7Uw`kkZjZ+e#0_&tNO8DBG0dnN7k~=XQ|&RZS^j@@<-}t9K*(* znm=fDed)OWFXKzU_J94ZBW>!BE~y>dRDbs4?B{%AKG5-?O-VoF$!68Zoptu+1>bb* z|Czu2v7cP}Q@-pwbmtKn{onN1OOT)QvEHcHQfS`(TkQ5Vb2>j_e&k}_RU5`X;+*oQ z=f~xdgZdcOd}>{t&UrsZq4RqdAB+2^YS+3tUFv5x``4t!dy;c>(Ab06LGK~oUZPK& z@ixUg)_c-F{{AcX%AF@UdIL4~mA{uq<&y_|=W~5gbmwB^+c@|m?~uovDH?P>));*J zI9K?Jq&U`=Z0!%Z>cg+|8ub%bh#q-T*I8`wv;O$soblUYWY#d^E@)Av<9$snajY%A zxHDgJhzYFI& z?sI-ub;sG>uXdImd;DD*bA|UgzpJ|AZ0}b)OOHMNE{(au`<(Bq&T(h|4{gPrC0m^P z+0VX?^s9U;=I40p2(vHAXYL+n?QN#2-K=q+Q|&)X*J!gpY0TUsPW202?ckrqYX5n9 z8F|Z_Ry7sq!Pb+JBy2M%?ly`4;~k zZ|QS=KU=NkN7QWpdCrKr