Build translations into separate files

This commit is contained in:
James Lambert 2023-10-26 22:00:02 -06:00
parent bd45d91445
commit 93d99d6cb5
9 changed files with 193 additions and 74 deletions

View file

@ -494,10 +494,33 @@ build/src/decor/decor_object_list.o: build/src/audio/clips.h
## Subtitles
####################
# TODO pull translations from vpk/Portal/portal/resource once the translations are loaded dynamically
SUBTITLE_LANGUAGES = english \
brazilian \
bulgarian \
czech \
danish \
dutch \
finnish \
french \
german \
hungarian \
italian \
latam \
norwegian \
polish \
portuguese \
romanian \
russian \
spanish \
swedish \
thai \
turkish
build/src/audio/subtitles.h build/src/audio/subtitles.c: resource/closecaption_english.txt tools/level_scripts/subtitle_generate.py
@python3 tools/level_scripts/subtitle_generate.py
SUBTITLE_SOURCES = $(SUBTITLE_LANGUAGES:%=build/src/audio/subtitles_%.c)
SUBTITLE_OBJECTS = $(SUBTITLE_LANGUAGES:%=build/src/audio/subtitles_%.o)
build/src/audio/subtitles.h build/src/audio/subtitles.c build/subtitles.ld: tools/level_scripts/subtitle_generate.py
python3 tools/level_scripts/subtitle_generate.py $(SUBTITLE_LANGUAGES)
####################
## Linking
@ -528,10 +551,10 @@ $(CODESEGMENT)_no_debug.o: $(CODEOBJECTS_NO_DEBUG)
$(LD) -o $(CODESEGMENT)_no_debug.o -r $(CODEOBJECTS_NO_DEBUG) $(LDFLAGS)
$(CP_LD_SCRIPT)_no_debug.ld: $(LD_SCRIPT) build/levels.ld build/dynamic_models.ld build/anims.ld
$(CP_LD_SCRIPT)_no_debug.ld: $(LD_SCRIPT) build/levels.ld build/dynamic_models.ld build/anims.ld build/subtitles.ld
cpp -P -Wno-trigraphs $(LCDEFS) -DCODE_SEGMENT=$(CODESEGMENT)_no_debug.o -o $@ $<
$(BASE_TARGET_NAME).z64: $(CODESEGMENT)_no_debug.o $(OBJECTS) $(DATA_OBJECTS) $(CP_LD_SCRIPT)_no_debug.ld
$(BASE_TARGET_NAME).z64: $(CODESEGMENT)_no_debug.o $(OBJECTS) $(DATA_OBJECTS) $(SUBTITLE_OBJECTS) $(CP_LD_SCRIPT)_no_debug.ld
$(LD) -L. -T $(CP_LD_SCRIPT)_no_debug.ld -Map $(BASE_TARGET_NAME)_no_debug.map -o $(BASE_TARGET_NAME).elf
$(OBJCOPY) --pad-to=0x100000 --gap-fill=0xFF $(BASE_TARGET_NAME).elf $(BASE_TARGET_NAME).z64 -O binary
makemask $(BASE_TARGET_NAME).z64
@ -546,10 +569,10 @@ endif
$(CODESEGMENT)_debug.o: $(CODEOBJECTS_DEBUG)
$(LD) -o $(CODESEGMENT)_debug.o -r $(CODEOBJECTS_DEBUG) $(LDFLAGS)
$(CP_LD_SCRIPT)_debug.ld: $(LD_SCRIPT) build/levels.ld build/dynamic_models.ld build/anims.ld
$(CP_LD_SCRIPT)_debug.ld: $(LD_SCRIPT) build/levels.ld build/dynamic_models.ld build/anims.ld build/subtitles.ld
cpp -P -Wno-trigraphs $(LCDEFS) -DCODE_SEGMENT=$(CODESEGMENT)_debug.o -o $@ $<
$(BASE_TARGET_NAME)_debug.z64: $(CODESEGMENT)_debug.o $(OBJECTS) $(DATA_OBJECTS) $(CP_LD_SCRIPT)_debug.ld
$(BASE_TARGET_NAME)_debug.z64: $(CODESEGMENT)_debug.o $(OBJECTS) $(DATA_OBJECTS) $(SUBTITLE_OBJECTS) $(CP_LD_SCRIPT)_debug.ld
$(LD) -L. -T $(CP_LD_SCRIPT)_debug.ld -Map $(BASE_TARGET_NAME)_debug.map -o $(BASE_TARGET_NAME)_debug.elf
$(OBJCOPY) --pad-to=0x100000 --gap-fill=0xFF $(BASE_TARGET_NAME)_debug.elf $(BASE_TARGET_NAME)_debug.z64 -O binary
makemask $(BASE_TARGET_NAME)_debug.z64

View file

@ -80,6 +80,7 @@ SECTIONS
#include "build/levels.ld"
#include "build/dynamic_models.ld"
#include "build/anims.ld"
#include "build/subtitles.ld"
/* Discard everything not specifically mentioned above. */
/DISCARD/ :

View file

@ -1,28 +1,29 @@
#include <ultra64.h>
#include <sched.h>
#include "audio/audio.h"
#include "audio/soundplayer.h"
#include "controls/controller_actions.h"
#include "controls/controller.h"
#include "controls/rumble_pak.h"
#include "defs.h"
#include "graphics/graphics.h"
#include "util/rom.h"
#include "scene/scene.h"
#include "menu/main_menu.h"
#include "util/time.h"
#include "util/memory.h"
#include "string.h"
#include "controls/controller.h"
#include "controls/controller_actions.h"
#include "scene/dynamic_scene.h"
#include "audio/soundplayer.h"
#include "audio/audio.h"
#include "scene/portal_surface.h"
#include "sk64/skelatool_defs.h"
#include "levels/cutscene_runner.h"
#include "levels/intro.h"
#include "menu/main_menu.h"
#include "menu/translations.h"
#include "savefile/savefile.h"
#include "scene/dynamic_scene.h"
#include "scene/portal_surface.h"
#include "scene/scene.h"
#include "sk64/skelatool_animator.h"
#include "sk64/skelatool_defs.h"
#include "string.h"
#include "util/dynamic_asset_loader.h"
#include "util/memory.h"
#include "util/profile.h"
#include "controls/rumble_pak.h"
#include "util/rom.h"
#include "util/time.h"
#include "levels/levels.h"
#include "savefile/checkpoint.h"
@ -240,6 +241,7 @@ static void gameProc(void* arg) {
rumblePakClipInit();
initAudio(fps);
soundPlayerInit();
translationsLoad(gSaveData.controls.subtitleLanguage);
skSetSegmentLocation(CHARACTER_ANIMATION_SEGMENT, (unsigned)_animation_segmentSegmentRomStart);
gSceneCallbacks->initCallback(gSceneCallbacks->data);
@ -264,6 +266,7 @@ static void gameProc(void* arg) {
portalSurfaceRevert(0);
portalSurfaceCleanupQueueInit();
heapInit(_heapStart, memoryEnd);
translationsLoad(gSaveData.controls.subtitleLanguage);
levelLoadWithCallbacks(levelGetQueued());
rumblePakClipInit();
cutsceneRunnerReset();

View file

@ -8,6 +8,7 @@
#include "../build/assets/materials/ui.h"
#include "../build/src/audio/clips.h"
#include "../build/src/audio/languages.h"
#include "./translations.h"
#define GAMEPLAY_Y 54
#define GAMEPLAY_WIDTH 252
@ -182,6 +183,7 @@ enum MenuDirection audioOptionsUpdate(struct AudioOptions* audioOptions) {
int temp = (int)((audioOptions->subtitles_language_temp * (1.0f/0xFFFF) * NUM_SUBTITLE_LANGUAGES));
temp = (int)minf(maxf(0.0, temp), NUM_SUBTITLE_LANGUAGES-1);
gSaveData.controls.subtitleLanguage = temp;
translationsReload(temp);
audioOptions->subtitlesLanguageDynamicText = menuBuildText(&gDejaVuSansFont, SubtitleLanguages[gSaveData.controls.subtitleLanguage], GAMEPLAY_X + 125, GAMEPLAY_Y + 88);
break;
case AudioOptionAudioLanguage:

48
src/menu/translations.c Normal file
View file

@ -0,0 +1,48 @@
#include "translations.h"
#include "../util/memory.h"
#include "../util/rom.h"
#include "../build/src/audio/subtitles.h"
char** gCurrentTranslations = NULL;
void translationsLoad(int language) {
if (NUM_SUBTITLE_LANGUAGES == 0) {
gCurrentTranslations = NULL;
return;
}
if (language < 0) {
language = 0;
}
if (language >= NUM_SUBTITLE_LANGUAGES) {
language = NUM_SUBTITLE_LANGUAGES - 1;
}
struct SubtitleBlock* block = &SubtitleLanguageBlocks[language];
int blockSize = (int)block->romEnd - (int)block->romStart;
char* blockStart = malloc(blockSize);
romCopy(block->romStart, blockStart, blockSize);
gCurrentTranslations = CALC_RAM_POINTER(block->values, blockStart);
for (int i = 0; i < NUM_SUBTITLE_MESSAGES; ++i) {
gCurrentTranslations[i] = CALC_RAM_POINTER(gCurrentTranslations[i], blockStart);
}
}
void translationsReload(int language) {
free(gCurrentTranslations);
translationsLoad(language);
}
char* translationsGet(int message) {
if (message < 0 || message >= NUM_SUBTITLE_MESSAGES || !gCurrentTranslations) {
return "";
}
return gCurrentTranslations[message];
}

9
src/menu/translations.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef __MENU_TRANSLATIONS_H__
#define __MENU_TRANSLATIONS_H__
void translationsLoad(int language);
void translationsReload(int language);
char* translationsGet(int message);
#endif

View file

@ -7,6 +7,7 @@
#include "../levels/levels.h"
#include "./scene.h"
#include "../savefile/savefile.h"
#include "../menu/translations.h"
#define HUD_CENTER_WIDTH 6
#define HUD_CENTER_HEIGHT 8
@ -48,7 +49,6 @@ void hudInit(struct Hud* hud) {
hud->subtitleOpacity = 0.0f;
hud->backgroundOpacity = 0.0f;
hud->subtitleFadeTime = SUBTITLE_SLOW_FADE_TIME;
hud->chosenLanguage = gSaveData.controls.subtitleLanguage;
hud->flags = 0;
hud->resolvedPrompts = 0;
hud->lastPortalIndexShot = -1;
@ -78,8 +78,6 @@ void hudUpdate(struct Hud* hud) {
}
}
hud->chosenLanguage = gSaveData.controls.subtitleLanguage;
float targetPromptOpacity = (hud->flags & HudFlagsShowingPrompt) ? 1.0 : 0.0f;
float targetSubtitleOpacity = ((hud->flags & HudFlagsShowingSubtitle) && (!(hud->flags & HudFlagsSubtitleQueued) || (hud->subtitleExpireTimer > 0.0f))) ? 0.85: 0.0f;
float targetBackgroundOpacity = (hud->flags & HudFlagsShowingSubtitle && (!(hud->flags & HudFlagsSubtitleQueued) || (hud->subtitleExpireTimer > 0.0f))) ? 0.45: 0.0f;
@ -365,6 +363,6 @@ void hudRender(struct Hud* hud, struct Player* player, struct RenderState* rende
}
if (hud->subtitleOpacity > 0.0f && (gSaveData.controls.flags & ControlSaveSubtitlesEnabled || gSaveData.controls.flags & ControlSaveAllSubtitlesEnabled) && hud->subtitleKey != SubtitleKeyNone) {
controlsRenderSubtitle(SubtitleLanguageValues[hud->chosenLanguage][hud->subtitleKey], hud->subtitleOpacity, hud->backgroundOpacity, renderState, hud->subtitleType);
controlsRenderSubtitle(translationsGet(hud->subtitleKey), hud->subtitleOpacity, hud->backgroundOpacity, renderState, hud->subtitleType);
}
}

View file

@ -25,7 +25,6 @@ enum HudFlags {
};
struct Hud {
int chosenLanguage;
enum CutscenePromptType promptType;
enum SubtitleKey subtitleKey;
enum SubtitleKey queuedSubtitleKey;

View file

@ -1,6 +1,14 @@
#!/usr/bin/env python3
import os
import re
import sys
def dump_lines(sourcefile_path, lines):
if not os.path.exists(os.path.dirname(os.path.abspath(sourcefile_path))):
os.makedirs(os.path.dirname(os.path.abspath(sourcefile_path)))
with open(sourcefile_path, "w") as f:
f.writelines(lines)
def get_caption_keys_values_language(lines):
language = "English"
@ -62,9 +70,16 @@ def make_overall_subtitles_header(all_header_lines, languages_list):
header_lines.append("#define __SUBTITLES_H__\n")
header_lines.append("\n")
header_lines.append(f"#define NUM_SUBTITLE_LANGUAGES {len(languages_list)}\n")
header_lines.append(f"#define NUM_SUBTITLE_MESSAGES 508\n")
header_lines.append("\n")
header_lines.append("struct SubtitleBlock {\n")
header_lines.append(" char* romStart;\n")
header_lines.append(" char* romEnd;\n")
header_lines.append(" char** values;\n")
header_lines.append("};\n")
header_lines.append("\n")
header_lines.append("extern char* SubtitleLanguages[];\n")
header_lines.append("extern char* SubtitleLanguageValues[][508];\n")
header_lines.append("extern struct SubtitleBlock SubtitleLanguageBlocks[];\n")
header_lines.append("\n")
if len(languages_list) > 0:
header_lines.extend(all_header_lines)
@ -77,12 +92,7 @@ def make_overall_subtitles_header(all_header_lines, languages_list):
header_lines.append("#endif")
headerfile_path = "build/src/audio/subtitles.h"
if not os.path.exists(os.path.dirname(os.path.abspath(headerfile_path))):
os.makedirs(os.path.dirname(os.path.abspath(headerfile_path)))
with open(headerfile_path, "w") as f:
f.writelines(header_lines)
dump_lines("build/src/audio/subtitles.h", header_lines)
def make_SubtitleKey_headerlines(keys):
header_lines = []
@ -96,33 +106,37 @@ def make_SubtitleKey_headerlines(keys):
header_lines.append("\n")
return header_lines
def make_SubtitleLanguageValues(values_list):
sourcefile_lines = []
if len(values_list) <= 0:
sourcefile_lines.append("\n")
sourcefile_lines.append("char* SubtitleLanguageValues[][508] =\n")
sourcefile_lines.append("{\n")
sourcefile_lines.append(" {\n")
for val in range(508):
sourcefile_lines.append(f' "",\n')
sourcefile_lines.append(" },\n")
sourcefile_lines.append("};\n")
sourcefile_lines.append("\n")
return sourcefile_lines
sourcefile_lines.append("\n")
sourcefile_lines.append("char* SubtitleLanguageValues[][508] =\n")
sourcefile_lines.append("{\n")
for lang in values_list:
sourcefile_lines.append(" {\n")
sourcefile_lines.append(' "",\n')
for value in lang:
sourcefile_lines.append(f' "{value}",\n')
sourcefile_lines.append(" },\n")
sourcefile_lines.append("};\n")
sourcefile_lines.append("\n")
return sourcefile_lines
def make_subtitle_for_language(lang_lines, lang_name):
lines = []
def make_overall_subtitles_sourcefile(other_sourcefile_lines, language_list):
lines.append("\n")
lines.append(f"char* gSubtitle{lang_name}[508] = {'{'}\n")
for value in lang_lines:
lines.append(f' "{value}",\n')
lines.append("};\n")
dump_lines(f"build/src/audio/subtitles_{lang_name}.c", lines)
def make_subtitle_ld(languages):
lines = []
for language in languages:
language_name = language['name']
lines.append(f" BEGIN_SEG(subtitles_{language_name}, 0x04000000)\n")
lines.append(" {\n")
lines.append(f" build/src/audio/subtitles_{language_name}.o(.data);\n")
lines.append(f" build/src/audio/subtitles_{language_name}.o(.bss);\n")
lines.append(" }\n")
lines.append(f" END_SEG(subtitles_{language_name})\n")
lines.append("\n")
dump_lines('build/subtitles.ld', lines)
def make_overall_subtitles_sourcefile(language_list):
sourcefile_lines = []
sourcefile_lines.append('#include "subtitles.h"\n')
@ -136,25 +150,38 @@ def make_overall_subtitles_sourcefile(other_sourcefile_lines, language_list):
sourcefile_lines.append(f' "{language.upper()}",\n')
sourcefile_lines.append("};\n")
sourcefile_lines.append("\n")
sourcefile_lines.extend(other_sourcefile_lines)
sourcefile_path = "build/src/audio/subtitles.c"
if not os.path.exists(os.path.dirname(os.path.abspath(sourcefile_path))):
os.makedirs(os.path.dirname(os.path.abspath(sourcefile_path)))
with open(sourcefile_path, "w") as f:
f.writelines(sourcefile_lines)
for language in language_list:
sourcefile_lines.append(f"extern char _subtitles_{language}SegmentRomStart[];\n")
sourcefile_lines.append(f"extern char _subtitles_{language}SegmentRomEnd[];\n")
sourcefile_lines.append(f"extern char* gSubtitle{language}[508];\n")
sourcefile_lines.append("\n")
def process_all_closecaption_files(dir):
sourcefile_lines.append("struct SubtitleBlock SubtitleLanguageBlocks[] = {\n")
for language in language_list:
sourcefile_lines.append(" {\n")
sourcefile_lines.append(f" _subtitles_{language}SegmentRomStart,\n")
sourcefile_lines.append(f" _subtitles_{language}SegmentRomEnd,\n")
sourcefile_lines.append(f" gSubtitle{language},\n")
sourcefile_lines.append(" },\n")
sourcefile_lines.append("};\n")
sourcefile_lines.append("\n")
dump_lines("build/src/audio/subtitles.c", sourcefile_lines)
def process_all_closecaption_files(dir, language_names):
values_list = []
header_lines = []
sourcefile_lines = []
language_list = []
language_with_values_list = []
SubtitleKey_generated = False
lst = os.listdir(dir)
lst.sort()
for filename in lst:
if "closecaption_" not in filename:
continue
for langauge_name in language_names:
filename = f"closecaption_{langauge_name}.txt"
try:
filepath = os.path.join(dir, filename)
lines = []
@ -174,14 +201,23 @@ def process_all_closecaption_files(dir):
header_lines = make_SubtitleKey_headerlines(k)
SubtitleKey_generated = True
language_list.append(l)
language_with_values_list.append({
'value': v,
'name': l,
})
print(filename, " - PASSED")
except:
print(filename, " - FAILED")
continue
sourcefile_lines = make_SubtitleLanguageValues(values_list)
for language in language_with_values_list:
make_subtitle_for_language(language['value'], language['name'])
make_subtitle_ld(language_with_values_list)
make_overall_subtitles_header(header_lines, language_list)
make_overall_subtitles_sourcefile(sourcefile_lines, language_list)
make_overall_subtitles_sourcefile(language_list)
process_all_closecaption_files("resource/")
process_all_closecaption_files("vpk/Portal/portal/resource", sys.argv[1:])