mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2025-12-07 00:22:27 +00:00
380 lines
14 KiB
C
380 lines
14 KiB
C
#include "sm64.h"
|
|
#include "types.h"
|
|
#include "course_table.h"
|
|
#include "game/memory.h"
|
|
#include "game/ingame_menu.h"
|
|
#include "game/save_file.h"
|
|
#include "game/segment2.h"
|
|
#include "game/level_info.h"
|
|
#include "pc/pc_main.h"
|
|
#include "../smlua.h"
|
|
#include "smlua_level_utils.h"
|
|
#include "smlua_text_utils.h"
|
|
#include "pc/dialog_table.h"
|
|
|
|
#ifdef VERSION_EU
|
|
extern s32 gInGameLanguage;
|
|
#include "eu_translation.h"
|
|
#endif
|
|
|
|
#define INVALID_COURSE_NUM(courseNum) (smlua_level_util_get_info_from_course_num(courseNum) == NULL && !COURSE_IS_VALID_COURSE(courseNum))
|
|
|
|
/*
|
|
---------------------------------------------------
|
|
Mapping gReplacedCourseActNameTable <-> seg2 tables
|
|
---------------------------------------------------
|
|
|
|
For courseNum from COURSE_BOB to COURSE_RR:
|
|
- gReplacedCourseActNameTable[courseNum].courseName <-> seg2_course_name_table[courseNum - 1]
|
|
- gReplacedCourseActNameTable[courseNum].actName[0..5] <-> seg2_act_name_table[(courseNum - 1) * 6 + (0..5)]
|
|
(7th act name doesn't map to anything, but can be retrieved with `smlua_text_utils_act_name_get`)
|
|
|
|
For courseNum from COURSE_BITDW to COURSE_CAKE_END:
|
|
- gReplacedCourseActNameTable[courseNum].courseName <-> seg2_course_name_table[courseNum - 1]
|
|
- gReplacedCourseActNameTable[courseNum].actName[0..6] don't map to anything, but they can be retrieved with `smlua_text_utils_act_name_get`
|
|
|
|
For castle secret stars and extra text:
|
|
- gReplacedCourseActNameTable[COURSE_END].courseName <-> seg2_course_name_table[COURSE_MAX] (which is seg2_course_name_table_castle_secret_stars)
|
|
- gReplacedCourseActNameTable[COURSE_END].actName[0..6] <-> seg2_act_name_table[COURSE_STAGES_MAX * 6 + (0..6)] (which are extra_text_0~6)
|
|
*/
|
|
struct CourseActNames gReplacedCourseActNameTable[COURSE_END + 1] = { 0 };
|
|
|
|
static bool sSmluaTextUtilsInited = false;
|
|
|
|
#define smlua_text_utils_init_from_vanilla(obj, tableFunc, tableOrigFunc, tableOffset) { \
|
|
char buffer[50]; \
|
|
convert_string_sm64_to_ascii(buffer, tableOrigFunc()[tableOffset]); \
|
|
snprintf(obj.name.value, sizeof(obj.name.value), "%s", buffer); \
|
|
obj.name.get_table = tableFunc; \
|
|
obj.name.offset = tableOffset; \
|
|
snprintf(obj.orig.value, sizeof(obj.orig.value), "%s", buffer); \
|
|
obj.orig.get_table = tableOrigFunc; \
|
|
obj.orig.offset = tableOffset; \
|
|
tableFunc()[tableOffset] = tableOrigFunc()[tableOffset]; \
|
|
}
|
|
|
|
// Save all vanilla act names and course names
|
|
void smlua_text_utils_init(void) {
|
|
dialog_table_init();
|
|
memset(gReplacedCourseActNameTable, 0, sizeof(gReplacedCourseActNameTable));
|
|
|
|
// Vanilla courses
|
|
for (s16 courseNum = COURSE_MIN; courseNum <= COURSE_MAX; ++courseNum) {
|
|
smlua_text_utils_init_from_vanilla(
|
|
gReplacedCourseActNameTable[courseNum].courseName,
|
|
get_course_name_table,
|
|
get_course_name_table_original,
|
|
courseNum - 1
|
|
);
|
|
|
|
// Vanilla acts
|
|
if (COURSE_IS_MAIN_COURSE(courseNum)) {
|
|
for (s16 actIndex = 0; actIndex < MAX_ACTS; ++actIndex) {
|
|
smlua_text_utils_init_from_vanilla(
|
|
gReplacedCourseActNameTable[courseNum].actName[actIndex],
|
|
get_act_name_table,
|
|
get_act_name_table_original,
|
|
(courseNum - 1) * MAX_ACTS + actIndex
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Vanilla castle secrets and extras
|
|
smlua_text_utils_init_from_vanilla(
|
|
gReplacedCourseActNameTable[COURSE_END].courseName,
|
|
get_course_name_table,
|
|
get_course_name_table_original,
|
|
COURSE_MAX
|
|
);
|
|
for (s16 actIndex = 0; actIndex < MAX_ACTS_AND_100_COINS; ++actIndex) {
|
|
smlua_text_utils_init_from_vanilla(
|
|
gReplacedCourseActNameTable[COURSE_END].actName[actIndex],
|
|
get_act_name_table,
|
|
get_act_name_table_original,
|
|
COURSE_STAGES_MAX * MAX_ACTS + actIndex
|
|
);
|
|
}
|
|
|
|
sSmluaTextUtilsInited = true;
|
|
}
|
|
|
|
void smlua_text_utils_shutdown(void) {
|
|
if (sSmluaTextUtilsInited) {
|
|
sSmluaTextUtilsInited = false;
|
|
}
|
|
}
|
|
|
|
static u8* smlua_text_utils_convert(const char* str) {
|
|
return convert_string_ascii_to_sm64(NULL, str, false);
|
|
}
|
|
|
|
// Checks the first 3 characters
|
|
static bool str_starts_with_spaces(const char* str) {
|
|
for (u8 i = 0; i < 3; i++) {
|
|
if (str[i] != ' ') { return false; }
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void smlua_text_utils_reset_course_or_act_name(struct ReplacedName *name) {
|
|
if (name->name.get_table && name->orig.get_table) {
|
|
void **tblName = name->name.get_table() + name->name.offset;
|
|
void **tblOrig = name->orig.get_table() + name->orig.offset;
|
|
if (*tblName != *tblOrig) {
|
|
free(*tblName);
|
|
*tblName = *tblOrig;
|
|
}
|
|
}
|
|
memcpy(name->name.value, name->orig.value, sizeof(name->name.value));
|
|
name->modNum = 0;
|
|
}
|
|
|
|
static void smlua_text_utils_replace_course_or_act_name(struct ReplacedName *name, const char *replacement, s32 modIndex, bool removeLeadingSpaces) {
|
|
if (removeLeadingSpaces) { replacement += 3 * str_starts_with_spaces(replacement); }
|
|
if (name->name.get_table && name->orig.get_table) {
|
|
void **tblName = name->name.get_table() + name->name.offset;
|
|
void **tblOrig = name->orig.get_table() + name->orig.offset;
|
|
if (*tblName != *tblOrig) {
|
|
free(*tblName);
|
|
}
|
|
*tblName = smlua_text_utils_convert(replacement);
|
|
}
|
|
snprintf(name->name.value, sizeof(name->name.value), "%s", replacement);
|
|
name->modNum = modIndex + 1;
|
|
}
|
|
|
|
void smlua_text_utils_reset_all(void) {
|
|
dialog_table_reset();
|
|
|
|
if (sSmluaTextUtilsInited) {
|
|
for (s16 courseNum = 0; courseNum <= COURSE_END; courseNum++) {
|
|
|
|
// Restore vanilla course names
|
|
struct ReplacedName *courseName = &gReplacedCourseActNameTable[courseNum].courseName;
|
|
smlua_text_utils_reset_course_or_act_name(courseName);
|
|
|
|
// Restore vanilla act names
|
|
for (s16 actIndex = 0; actIndex < MAX_ACTS_AND_100_COINS; actIndex++) {
|
|
struct ReplacedName *actName = &gReplacedCourseActNameTable[courseNum].actName[actIndex];
|
|
smlua_text_utils_reset_course_or_act_name(actName);
|
|
}
|
|
}
|
|
} else {
|
|
dialog_table_shutdown();
|
|
}
|
|
}
|
|
|
|
struct DialogEntry* smlua_text_utils_dialog_get(enum DialogId dialogId) {
|
|
struct DialogEntry* dialog = dialog_table_get(dialogId);
|
|
return dialog;
|
|
}
|
|
|
|
const struct DialogEntry* smlua_text_utils_dialog_get_unmodified(enum DialogId dialogId) {
|
|
if (!IS_VALID_VANILLA_DIALOG(dialogId)) { return NULL; }
|
|
|
|
void **dialogTableOrg;
|
|
|
|
#ifdef VERSION_EU
|
|
switch (gInGameLanguage) {
|
|
case LANGUAGE_ENGLISH:
|
|
dialogTableOrg = segmented_to_virtual(dialog_table_eu_en_original);
|
|
break;
|
|
case LANGUAGE_FRENCH:
|
|
dialogTableOrg = segmented_to_virtual(dialog_table_eu_fr_original);
|
|
break;
|
|
case LANGUAGE_GERMAN:
|
|
dialogTableOrg = segmented_to_virtual(dialog_table_eu_de_original);
|
|
break;
|
|
}
|
|
#else
|
|
dialogTableOrg = segmented_to_virtual(seg2_dialog_original);
|
|
#endif
|
|
|
|
return segmented_to_virtual(dialogTableOrg[dialogId]);
|
|
}
|
|
|
|
void smlua_text_utils_dialog_replace(enum DialogId dialogId, UNUSED u32 unused, s8 linesPerBox, s16 leftOffset, s16 width, const char* str) {
|
|
if (!IS_VALID_DIALOG(dialogId)) { return; }
|
|
|
|
struct DialogEntry *dialog = smlua_text_utils_dialog_get(dialogId);
|
|
|
|
if (!dialog) { return; }
|
|
|
|
if (dialog->replaced) {
|
|
free((u8*)dialog->str);
|
|
}
|
|
|
|
free(dialog->text);
|
|
|
|
dialog->unused = unused;
|
|
dialog->linesPerBox = linesPerBox;
|
|
dialog->leftOffset = leftOffset;
|
|
dialog->width = width;
|
|
dialog->str = smlua_text_utils_convert(str);
|
|
dialog->text = strdup(str);
|
|
dialog->replaced = true;
|
|
}
|
|
|
|
void smlua_text_utils_dialog_restore(enum DialogId dialogId) {
|
|
if (!IS_VALID_VANILLA_DIALOG(dialogId)) { return; }
|
|
|
|
struct DialogEntry *dialog = smlua_text_utils_dialog_get(dialogId);
|
|
|
|
if (!dialog->replaced) return;
|
|
|
|
const struct DialogEntry *dialogOrig = smlua_text_utils_dialog_get_unmodified(dialogId);
|
|
|
|
free((u8*)dialog->str);
|
|
free(dialog->text);
|
|
|
|
memcpy(dialog, dialogOrig, sizeof(struct DialogEntry));
|
|
dialog->text = convert_string_sm64_to_ascii(NULL, dialog->str);
|
|
}
|
|
|
|
bool smlua_text_utils_dialog_is_replaced(enum DialogId dialogId) {
|
|
if (!IS_VALID_DIALOG(dialogId)) { return false; }
|
|
|
|
struct DialogEntry *dialog = dialog_table_get(dialogId);
|
|
return dialog->replaced;
|
|
}
|
|
|
|
s32 smlua_text_utils_allocate_dialog(void) {
|
|
s32 dialogId;
|
|
struct DialogEntry* dialog = dialog_table_alloc(&dialogId);
|
|
|
|
if (dialog) {
|
|
dialog->replaced = true;
|
|
}
|
|
|
|
return dialogId;
|
|
}
|
|
|
|
void smlua_text_utils_course_acts_replace(s16 courseNum, const char* courseName, const char* act1, const char* act2, const char* act3, const char* act4, const char* act5, const char* act6) {
|
|
if (!COURSE_IS_VALID_COURSE(courseNum)) { return; }
|
|
|
|
struct CourseActNames *courseActNames = &gReplacedCourseActNameTable[courseNum];
|
|
smlua_text_utils_replace_course_or_act_name(&courseActNames->courseName, courseName + (3 * (strlen(courseName) > 3)), gLuaActiveMod->index, false);
|
|
|
|
#define REPLACE_ACT_NAME(i) { \
|
|
smlua_text_utils_replace_course_or_act_name(&courseActNames->actName[i - 1], act##i, gLuaActiveMod->index, false); \
|
|
}
|
|
|
|
REPLACE_ACT_NAME(1);
|
|
REPLACE_ACT_NAME(2);
|
|
REPLACE_ACT_NAME(3);
|
|
REPLACE_ACT_NAME(4);
|
|
REPLACE_ACT_NAME(5);
|
|
REPLACE_ACT_NAME(6);
|
|
}
|
|
|
|
void smlua_text_utils_course_name_replace(s16 courseNum, const char* name) {
|
|
if (!COURSE_IS_VALID_COURSE(courseNum)) { return; }
|
|
|
|
struct CourseActNames *courseActNames = &gReplacedCourseActNameTable[courseNum];
|
|
smlua_text_utils_replace_course_or_act_name(&courseActNames->courseName, name, gLuaActiveMod->index, false);
|
|
}
|
|
|
|
const char* smlua_text_utils_course_name_get(s16 courseNum) {
|
|
if (!COURSE_IS_VALID_COURSE(courseNum)) { return NULL; }
|
|
|
|
return gReplacedCourseActNameTable[courseNum].courseName.name.value;
|
|
}
|
|
|
|
s32 smlua_text_utils_course_name_mod_index(s16 courseNum) {
|
|
if (!COURSE_IS_VALID_COURSE(courseNum)) { return -1; }
|
|
|
|
return (s32) gReplacedCourseActNameTable[courseNum].courseName.modNum - 1;
|
|
}
|
|
|
|
void smlua_text_utils_course_name_reset(s16 courseNum) {
|
|
if (!COURSE_IS_VALID_COURSE(courseNum)) { return; }
|
|
|
|
struct CourseActNames *courseActNames = &gReplacedCourseActNameTable[courseNum];
|
|
smlua_text_utils_reset_course_or_act_name(&courseActNames->courseName);
|
|
}
|
|
|
|
void smlua_text_utils_act_name_replace(s16 courseNum, u8 actNum, const char* name) {
|
|
if (!COURSE_IS_VALID_COURSE(courseNum)) { return; }
|
|
if (actNum < 1 || actNum > MAX_ACTS_AND_100_COINS) { return; }
|
|
|
|
struct CourseActNames *courseActNames = &gReplacedCourseActNameTable[courseNum];
|
|
smlua_text_utils_replace_course_or_act_name(&courseActNames->actName[actNum - 1], name, gLuaActiveMod->index, false);
|
|
}
|
|
|
|
const char* smlua_text_utils_act_name_get(s16 courseNum, u8 actNum) {
|
|
if (!COURSE_IS_VALID_COURSE(courseNum)) { return NULL; }
|
|
if (actNum < 1 || actNum > MAX_ACTS_AND_100_COINS) { return NULL; }
|
|
|
|
return gReplacedCourseActNameTable[courseNum].actName[actNum - 1].name.value;
|
|
}
|
|
|
|
s32 smlua_text_utils_act_name_mod_index(s16 courseNum, u8 actNum) {
|
|
if (!COURSE_IS_VALID_COURSE(courseNum)) { return -1; }
|
|
if (actNum < 1 || actNum > MAX_ACTS_AND_100_COINS) { return -1; }
|
|
|
|
return (s32) gReplacedCourseActNameTable[courseNum].actName[actNum - 1].modNum - 1;
|
|
}
|
|
|
|
void smlua_text_utils_act_name_reset(s16 courseNum, u8 actNum) {
|
|
if (!COURSE_IS_VALID_COURSE(courseNum)) { return; }
|
|
if (actNum < 1 || actNum > MAX_ACTS_AND_100_COINS) { return; }
|
|
|
|
struct CourseActNames *courseActNames = &gReplacedCourseActNameTable[courseNum];
|
|
smlua_text_utils_reset_course_or_act_name(&courseActNames->actName[actNum - 1]);
|
|
}
|
|
|
|
void smlua_text_utils_secret_star_replace(s16 courseNum, const char* courseName) {
|
|
if (courseNum <= COURSE_STAGES_MAX || courseNum > COURSE_MAX) { return; }
|
|
|
|
struct CourseActNames *courseActNames = &gReplacedCourseActNameTable[courseNum];
|
|
smlua_text_utils_replace_course_or_act_name(&courseActNames->courseName, courseName, gLuaActiveMod->index, true);
|
|
}
|
|
|
|
void smlua_text_utils_castle_secret_stars_replace(const char* name) {
|
|
struct CourseActNames *courseActNames = &gReplacedCourseActNameTable[COURSE_END];
|
|
smlua_text_utils_replace_course_or_act_name(&courseActNames->courseName, name, gLuaActiveMod->index, false);
|
|
}
|
|
|
|
const char* smlua_text_utils_castle_secret_stars_get() {
|
|
return gReplacedCourseActNameTable[COURSE_END].courseName.name.value;
|
|
}
|
|
|
|
s32 smlua_text_utils_castle_secret_stars_mod_index() {
|
|
return (s32) gReplacedCourseActNameTable[COURSE_END].courseName.modNum - 1;
|
|
}
|
|
|
|
void smlua_text_utils_castle_secret_stars_reset() {
|
|
struct CourseActNames *courseActNames = &gReplacedCourseActNameTable[COURSE_END];
|
|
smlua_text_utils_reset_course_or_act_name(&courseActNames->courseName);
|
|
}
|
|
|
|
void smlua_text_utils_extra_text_replace(s16 index, const char* text) {
|
|
if (index < 0 || index > MAX_ACTS_AND_100_COINS) { return; }
|
|
|
|
struct CourseActNames *courseActNames = &gReplacedCourseActNameTable[COURSE_END];
|
|
smlua_text_utils_replace_course_or_act_name(&courseActNames->actName[index], text, gLuaActiveMod->index, false);
|
|
}
|
|
|
|
const char* smlua_text_utils_extra_text_get(s16 index) {
|
|
if (index < 0 || index > MAX_ACTS_AND_100_COINS) { return NULL; }
|
|
|
|
return gReplacedCourseActNameTable[COURSE_END].actName[index].name.value;
|
|
}
|
|
|
|
s32 smlua_text_utils_extra_text_mod_index(s16 index) {
|
|
if (index < 0 || index > MAX_ACTS_AND_100_COINS) { return -1; }
|
|
|
|
return (s32) gReplacedCourseActNameTable[COURSE_END].actName[index].modNum - 1;
|
|
}
|
|
|
|
void smlua_text_utils_extra_text_reset(s16 index) {
|
|
if (index < 0 || index > MAX_ACTS_AND_100_COINS) { return; }
|
|
|
|
struct CourseActNames *courseActNames = &gReplacedCourseActNameTable[COURSE_END];
|
|
smlua_text_utils_reset_course_or_act_name(&courseActNames->actName[index]);
|
|
}
|
|
|
|
const char* smlua_text_utils_get_language(void) {
|
|
return configLanguage;
|
|
}
|