sm64coopdx/src/pc/lua/utils/smlua_text_utils.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;
}