mirror of https://github.com/nmlgc/ReC98.git
[C decompilation] [th02/maine] High score screen
MAIN.EXE shares most of the code in this segment, but I can't remove it from there right now due to the weird ordering of the data segments in that executable… And yes, once again, those three seemingly random type casts in here are *necessary* to build a bit-perfect binary.
This commit is contained in:
parent
37fc899c42
commit
87b1fb9e14
|
@ -44,7 +44,7 @@ bin\th02\op.exe: bin\th02\op.obj th02\op_03.c th02\op_05.c th02\op_06.c
|
|||
$**
|
||||
|
|
||||
|
||||
bin\th02\maine.exe: bin\th02\maine.obj th02\maine_05.c
|
||||
bin\th02\maine.exe: bin\th02\maine.obj th02\maine_04.c th02\maine_05.c
|
||||
$(CC) $(CFLAGS) -ml -3 -Z -DGAME=2 -nbin\th02\ -eMAINE.EXE @&&|
|
||||
$**
|
||||
|
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
void pascal near score_load(void)
|
||||
{
|
||||
int byte;
|
||||
file_ropen(SCORE_FN);
|
||||
file_seek(rank * sizeof(hi), 0);
|
||||
file_read(&hi, sizeof(hi));
|
||||
for(byte = 0; byte < sizeof(hi.score); byte++) {
|
||||
*((unsigned char*)(&hi.score) + byte) -= 0x12;
|
||||
}
|
||||
file_close();
|
||||
}
|
|
@ -0,0 +1,290 @@
|
|||
/* ReC98
|
||||
* -----
|
||||
* Code segment #4 of TH02's MAINE.EXE
|
||||
*/
|
||||
|
||||
#include <master.h>
|
||||
#include "th02\th02.h"
|
||||
|
||||
#include "th02\strings\score.c"
|
||||
|
||||
extern char rank;
|
||||
extern long score;
|
||||
|
||||
long score_highest;
|
||||
score_file_t hi;
|
||||
|
||||
void pascal sub_B4B7(void);
|
||||
|
||||
void pascal score_defaults_set(void)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < SCORE_PLACES; i++) {
|
||||
int c;
|
||||
hi.score.points[i] = 10000 - (i * 1000);
|
||||
hi.score.stage[i] = 5 - (i >> 1);
|
||||
for(c = 0; c < SCORE_NAME_LEN; c++) {
|
||||
hi.score.g_name[i][c] = gs_BULLET;
|
||||
}
|
||||
hi.score.g_name[i][SCORE_NAME_LEN] = 0;
|
||||
hi.score.date[i].da_year = 1900;
|
||||
hi.score.date[i].da_day = 1;
|
||||
hi.score.date[i].da_mon = 1;
|
||||
hi.score.shottype[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#include "th02\formats\scorelod.c"
|
||||
|
||||
#define score_init() \
|
||||
if(!file_exist(SCORE_FN)) { \
|
||||
score_defaults_set(); \
|
||||
} else { \
|
||||
score_load(); \
|
||||
}
|
||||
|
||||
// Slightly differs from the same function in OP.EXE!
|
||||
void pascal score_points_put(unsigned y, long points, unsigned atrb)
|
||||
{
|
||||
int x;
|
||||
int digit;
|
||||
long divisor = 10000000;
|
||||
long result;
|
||||
char putting = 0;
|
||||
for(x = 26; x < 26 + (8 * 2); x += 2) {
|
||||
result = (points / divisor) % 10;
|
||||
divisor /= 10;
|
||||
digit = result + GB_DIGITS;
|
||||
if(result) {
|
||||
putting = 1;
|
||||
}
|
||||
if(putting) {
|
||||
gaiji_putca(x, y, digit, atrb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define ALPHABET_PUTCA(col, row, atrb) \
|
||||
gaiji_putca(10 + (col * 2), 18 + row, gALPHABET[row][col], atrb);
|
||||
|
||||
#define ATRB_SET \
|
||||
if(i == place_to_highlight) { \
|
||||
atrb = TX_GREEN; \
|
||||
} else { \
|
||||
atrb = TX_WHITE; \
|
||||
}
|
||||
|
||||
void pascal near scores_put(int place_to_highlight)
|
||||
{
|
||||
unsigned atrb = TX_WHITE;
|
||||
int i;
|
||||
int col;
|
||||
gaiji_putsa(20, 2, gbHI_SCORE, TX_GREEN);
|
||||
gaiji_putsa(12, 4, gbNAME, TX_GREEN);
|
||||
gaiji_putsa(28, 4, gbPOINT, TX_GREEN);
|
||||
gaiji_putsa(42, 4, gbST, TX_GREEN);
|
||||
if(place_to_highlight != -1) {
|
||||
for(i = 0; i < ALPHABET_ROWS; i++) {
|
||||
for(col = 0; col < ALPHABET_COLS; col++) {
|
||||
ALPHABET_PUTCA(col, i, TX_WHITE);
|
||||
}
|
||||
}
|
||||
ALPHABET_PUTCA(0, 0, TX_GREEN | TX_REVERSE);
|
||||
}
|
||||
for(i = 0; i < SCORE_PLACES; i++) {
|
||||
ATRB_SET;
|
||||
gaiji_putsa(10, 6+i, (const char*)hi.score.g_name[i], atrb);
|
||||
score_points_put(6+i, hi.score.points[i], atrb);
|
||||
if(hi.score.stage[i] != STAGE_ALL) {
|
||||
gaiji_putca(44, 6+i, hi.score.stage[i] + GB_DIGITS, atrb);
|
||||
} else {
|
||||
gaiji_putca(44, 6+i, gs_ALL, atrb);
|
||||
}
|
||||
}
|
||||
for(i = 0; i < SCORE_PLACES; i++) {
|
||||
ATRB_SET;
|
||||
if(i != 9) {
|
||||
gaiji_putca(6, 6+i, GB_DIGITS+i+1, atrb);
|
||||
} else {
|
||||
gaiji_putca(4, 15, gb_1_, atrb);
|
||||
gaiji_putca(6, 15, gb_0_, atrb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pascal near alphabet_putca(int col, int row, unsigned atrb)
|
||||
{
|
||||
ALPHABET_PUTCA(col, row, atrb);
|
||||
}
|
||||
|
||||
void pascal near score_name_puts(int place, int char_to_highlight)
|
||||
{
|
||||
gaiji_putsa(10, 6 + place, (const char*)hi.score.g_name[place], TX_GREEN);
|
||||
gaiji_putca(
|
||||
10 + (char_to_highlight * 2),
|
||||
6 + place,
|
||||
hi.score.g_name[place][char_to_highlight],
|
||||
TX_GREEN | TX_REVERSE
|
||||
);
|
||||
}
|
||||
|
||||
void pascal score_save(void)
|
||||
{
|
||||
int i;
|
||||
hi.score.points_sum = 0;
|
||||
hi.score.g_name_first_sum = 0;
|
||||
hi.score.stage_sum = 0;
|
||||
hi.score_sum = 0;
|
||||
for(i = 0; i < SCORE_PLACES; i++) {
|
||||
hi.score.points_sum += hi.score.points[i];
|
||||
hi.score.g_name_first_sum += hi.score.g_name[i][0];
|
||||
hi.score.stage_sum += hi.score.stage[i];
|
||||
}
|
||||
for(i = 0; i < sizeof(hi.score); i++) {
|
||||
hi.score_sum += *((unsigned char*)(&hi.score) + i);
|
||||
*((unsigned char*)(&hi.score) + i) += 0x12;
|
||||
}
|
||||
file_append(SCORE_FN);
|
||||
file_seek(rank * sizeof(hi), 0);
|
||||
file_write(&hi, sizeof(hi));
|
||||
file_close();
|
||||
}
|
||||
|
||||
void pascal score_enter(void)
|
||||
{
|
||||
int name_pos = 0;
|
||||
int place;
|
||||
int shift;
|
||||
int c;
|
||||
int row;
|
||||
int col;
|
||||
int input_locked;
|
||||
unsigned char input_delay;
|
||||
score_init();
|
||||
if(hi.score.points[SCORE_PLACES - 1] > score) {
|
||||
scores_put(-1);
|
||||
sub_B4B7();
|
||||
return;
|
||||
}
|
||||
for(place = SCORE_PLACES - 1; place > 0; place--) {
|
||||
if(hi.score.points[place-1] > score) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(shift = SCORE_PLACES - 1; shift > place; shift--) {
|
||||
hi.score.points[shift] = hi.score.points[shift-1];
|
||||
for(c = 0; c < SCORE_NAME_LEN; c++) {
|
||||
hi.score.g_name[shift][c] = hi.score.g_name[shift-1][c];
|
||||
}
|
||||
hi.score.stage[shift] = hi.score.stage[shift-1];
|
||||
hi.score.date[shift].da_year = hi.score.date[shift-1].da_year;
|
||||
hi.score.date[shift].da_mon = hi.score.date[shift-1].da_mon;
|
||||
hi.score.date[shift].da_day = hi.score.date[shift-1].da_day;
|
||||
hi.score.shottype[shift] = hi.score.shottype[shift-1];
|
||||
}
|
||||
hi.score.points[place] = score;
|
||||
hi.score.stage[place] = STAGE_ALL;
|
||||
getdate(&hi.score.date[place]);
|
||||
hi.score.shottype[place] = mikoconfig->shottype;
|
||||
for(c = 0; c < SCORE_NAME_LEN; c++) {
|
||||
hi.score.g_name[shift][c] = gs_BULLET;
|
||||
}
|
||||
scores_put(place);
|
||||
|
||||
col = 0;
|
||||
row = 0;
|
||||
input = 0;
|
||||
input_locked = 1;
|
||||
input_delay = 0;
|
||||
|
||||
#define ALPHABET_CURSOR_MOVE(coord, max, direction) \
|
||||
alphabet_putca(col, row, TX_WHITE); \
|
||||
RING_##direction(coord, ALPHABET_##max - 1); \
|
||||
alphabet_putca(col, row, TX_GREEN | TX_REVERSE);
|
||||
|
||||
// Otherwise, this leads to more levels of indentation than I would like.
|
||||
#define INPUTS if(input & INPUT_UP) { \
|
||||
ALPHABET_CURSOR_MOVE(row, ROWS, DEC); \
|
||||
} \
|
||||
if(input & INPUT_DOWN) { \
|
||||
ALPHABET_CURSOR_MOVE(row, ROWS, INC); \
|
||||
} \
|
||||
if(input & INPUT_LEFT) { \
|
||||
ALPHABET_CURSOR_MOVE(col, COLS, DEC); \
|
||||
} \
|
||||
if(input & INPUT_RIGHT) { \
|
||||
ALPHABET_CURSOR_MOVE(col, COLS, INC); \
|
||||
} \
|
||||
if(input & INPUT_SHOT || input & INPUT_OK) { \
|
||||
/* Yeah, it sucks that ZUN checks against the indices into the
|
||||
* alphabet structure rather than against the gaiji values. */ \
|
||||
if(row != 2 || col < 13) { \
|
||||
hi.score.g_name[place][name_pos] = gALPHABET[row][col]; \
|
||||
if(name_pos == 5) { \
|
||||
alphabet_putca(col, row, TX_WHITE); \
|
||||
col = 16; \
|
||||
row = 2; \
|
||||
alphabet_putca(col, row, TX_GREEN | TX_REVERSE); \
|
||||
} \
|
||||
CLAMP_INC(name_pos, 5); \
|
||||
} else if(col == 13) { \
|
||||
hi.score.g_name[place][name_pos] = gb_SP; \
|
||||
CLAMP_INC(name_pos, 5); \
|
||||
} else if(col == 14) { \
|
||||
CLAMP_DEC(name_pos, 0); \
|
||||
hi.score.g_name[place][name_pos] = gb_SP; \
|
||||
} else if(col == 15) { \
|
||||
CLAMP_INC(name_pos, 5); \
|
||||
} else if(col == 16) { \
|
||||
break; \
|
||||
} \
|
||||
score_name_puts(place, name_pos); \
|
||||
} \
|
||||
if(input & INPUT_BOMB) { \
|
||||
hi.score.g_name[place][name_pos] = gb_SP; \
|
||||
CLAMP_DEC(name_pos, 0); \
|
||||
score_name_puts(place, name_pos); \
|
||||
} \
|
||||
if(input & INPUT_CANCEL) { \
|
||||
break; \
|
||||
}
|
||||
|
||||
do {
|
||||
input_sense();
|
||||
if(!input_locked) {
|
||||
INPUTS;
|
||||
}
|
||||
frame_delay(1);
|
||||
input_locked = input;
|
||||
if(input_locked) {
|
||||
input_delay++;
|
||||
if(input_delay > 30 && (input_delay & 1) == 0) {
|
||||
input_locked = 0;
|
||||
}
|
||||
} else {
|
||||
input_delay = 0;
|
||||
}
|
||||
} while(1);
|
||||
score_save();
|
||||
}
|
||||
|
||||
void pascal score_highest_get(void)
|
||||
{
|
||||
score_init();
|
||||
score_highest = hi.score.points[0] >= score ? hi.score.points[0] : score;
|
||||
}
|
||||
|
||||
int pascal score_extra_unlocked(void)
|
||||
{
|
||||
int game_clear_constants[SHOTTYPE_COUNT] = GAME_CLEAR_CONSTANTS;
|
||||
char rank_save = rank;
|
||||
for(rank = 0; (int)rank < SHOTTYPE_COUNT; rank++) {
|
||||
score_load();
|
||||
if(game_clear_constants[rank] != hi.score.cleared) {
|
||||
rank = rank_save;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
rank = rank_save;
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
const char *SCORE_FN = "huuhi.dat";
|
||||
const unsigned char gbHI_SCORE[] = {
|
||||
gb_H_, gb_I_, gb_SP, gb_S_, gb_C_, gb_O_, gb_R_, gb_E_, 0
|
||||
};
|
||||
|
||||
#define ALPHABET_ROWS 3
|
||||
#define ALPHABET_COLS 17
|
||||
const unsigned char gALPHABET[ALPHABET_ROWS][ALPHABET_COLS] = {
|
||||
gb_A_, gb_B_, gb_C_, gb_D_, gb_E_, gb_F_, gb_G_, gb_H_, gb_I_, gb_J_,
|
||||
gb_K_, gb_L_, gb_M_, gb_N_, gb_O_, gb_P_, gb_Q_, gb_R_, gb_S_, gb_T_,
|
||||
gb_U_, gb_V_, gb_W_, gb_X_, gb_Y_, gb_Z_,
|
||||
gs_BULLET, gs_PERIOD, gs_EXCLAMATION, gs_QUESTION, gs_ELLIPSIS,
|
||||
gs_HEART, gs_YINYANG, gs_BOMB,
|
||||
gb_0_, gb_1_, gb_2_, gb_3_, gb_4_, gb_5_, gb_6_, gb_7_, gb_8_, gb_9_,
|
||||
gs_SKULL, gs_GHOST, gs_SIDDHAM_HAM, gs_SPACE,
|
||||
gs_ARROW_LEFT, gs_ARROW_RIGHT, gs_END
|
||||
};
|
||||
|
||||
const unsigned char gbNAME[] = {gb_N_, gb_A_, gb_M_, gb_E_, 0};
|
||||
const unsigned char gbPOINT[] = {gb_P_, gb_O_, gb_I_, gb_N_, gb_T_, 0};
|
||||
const unsigned char gbST[] = {gb_S_, gb_T_, 0};
|
110
th02/th02.h
110
th02/th02.h
|
@ -24,6 +24,76 @@ void pi_slot_put(int x, int y, int slot);
|
|||
// Hardware
|
||||
void graph_putsa_fx(int x, int y, int color, const char *str);
|
||||
|
||||
// Gaiji characters
|
||||
/* ZUN messed up and swapped M and N in MIKOFT.BFT for both regular and bold
|
||||
* fonts. Therefore, other code shouldn't really use the straightforward
|
||||
* solution of just adding char literals to a defined start offset, as it may
|
||||
* suggest that this also works for M and N (which it doesn't). So...
|
||||
*/
|
||||
typedef enum {
|
||||
GB_DIGITS = 160,
|
||||
gb_0_ = GB_DIGITS,
|
||||
gb_1_,
|
||||
gb_2_,
|
||||
gb_3_,
|
||||
gb_4_,
|
||||
gb_5_,
|
||||
gb_6_,
|
||||
gb_7_,
|
||||
gb_8_,
|
||||
gb_9_,
|
||||
|
||||
GB_LETTERS = 105,
|
||||
gb_A_ = GB_LETTERS + 'A',
|
||||
gb_B_,
|
||||
gb_C_,
|
||||
gb_D_,
|
||||
gb_E_,
|
||||
gb_F_,
|
||||
gb_G_,
|
||||
gb_H_,
|
||||
gb_I_,
|
||||
gb_J_,
|
||||
gb_K_,
|
||||
gb_L_,
|
||||
gb_M_ = GB_LETTERS + 'N',
|
||||
gb_N_ = GB_LETTERS + 'M',
|
||||
gb_O_ = GB_LETTERS + 'O',
|
||||
gb_P_,
|
||||
gb_Q_,
|
||||
gb_R_,
|
||||
gb_S_,
|
||||
gb_T_,
|
||||
gb_U_,
|
||||
gb_V_,
|
||||
gb_W_,
|
||||
gb_X_,
|
||||
gb_Y_,
|
||||
gb_Z_,
|
||||
|
||||
gb_SP = 207,
|
||||
} gaiji_bold_t;
|
||||
|
||||
typedef enum {
|
||||
gs_YINYANG = 2, // ☯
|
||||
gs_BOMB, // ◉? ⦿? 🎯? 🖸? Or simply 💣?
|
||||
gs_BULLET = 218, // •
|
||||
gs_PERIOD, // .
|
||||
gs_EXCLAMATION, // !
|
||||
gs_QUESTION, // ?
|
||||
gs_ELLIPSIS, // …
|
||||
gs_COPYRIGHT, // ©
|
||||
gs_HEART, // 🎔
|
||||
gs_SKULL, // 💀
|
||||
gs_GHOST, // 👻
|
||||
gs_SIDDHAM_HAM, // Siddhaṃ seed syllable HĀṂ (I don't even)
|
||||
gs_SPACE, // ␠
|
||||
gs_ARROW_LEFT, // ←
|
||||
gs_ARROW_RIGHT, // →
|
||||
gs_END, // "End"
|
||||
gs_ALL = 240 // "All"
|
||||
} gaiji_symbols_t;
|
||||
|
||||
typedef enum {
|
||||
INPUT_UP = 0x1,
|
||||
INPUT_DOWN = 0x2,
|
||||
|
@ -96,3 +166,43 @@ typedef struct {
|
|||
extern mikoconfig_t *mikoconfig;
|
||||
|
||||
#define SHOTTYPE_COUNT 3
|
||||
|
||||
// Highscores
|
||||
#define SCORE_PLACES 10
|
||||
#define SCORE_NAME_LEN 6 /* excluding the terminating 0 */
|
||||
#define EXTRA_CLEAR_FLAGS {1, 2, 4}
|
||||
#define GAME_CLEAR_CONSTANTS {318, 118, 218}
|
||||
#define STAGE_ALL 127
|
||||
|
||||
typedef struct {
|
||||
/* For ranks (and therefore, structure instances) #0, #1 and #2 (Easy,
|
||||
* Normal and Hard), this is either GAME_CLEAR_CONSTANTS[rank] or 0,
|
||||
* and indicates whether the main 5 stages have been cleared with the
|
||||
* *shot type* associated with the rank's index, in any difficulty.
|
||||
* Yes, ZUN uses a field in a rank-specific structure to store a
|
||||
* shot type-specific value.
|
||||
*
|
||||
* For rank #3, this is instead interpreted as a bit field using the
|
||||
* EXTRA_CLEAR_FLAGS to indicate whether the Extra Stage has been
|
||||
* cleared with the respective shot type.
|
||||
* Yes, ZUN stores what is technically information about the Extra
|
||||
* rank in the structure of the Lunatic rank.
|
||||
*
|
||||
* For rank #4, this field is unused.
|
||||
*/
|
||||
int cleared;
|
||||
|
||||
long points[SCORE_PLACES];
|
||||
long points_sum;
|
||||
unsigned char g_name[SCORE_PLACES][SCORE_NAME_LEN + 1];
|
||||
unsigned char g_name_first_sum;
|
||||
unsigned char stage[SCORE_PLACES];
|
||||
unsigned char stage_sum;
|
||||
struct date date[SCORE_PLACES];
|
||||
unsigned char shottype[SCORE_PLACES];
|
||||
} score_t;
|
||||
|
||||
typedef struct {
|
||||
score_t score;
|
||||
long score_sum; // Sum of all bytes in score, pre-encraption
|
||||
} score_file_t;
|
||||
|
|
1057
th02_maine.asm
1057
th02_maine.asm
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue