Build translations into separate files
This commit is contained in:
parent
bd45d91445
commit
93d99d6cb5
37
Makefile
37
Makefile
|
@ -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
|
||||
|
|
|
@ -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/ :
|
||||
|
|
31
src/main.c
31
src/main.c
|
@ -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();
|
||||
|
|
|
@ -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
48
src/menu/translations.c
Normal 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
9
src/menu/translations.h
Normal 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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -25,7 +25,6 @@ enum HudFlags {
|
|||
};
|
||||
|
||||
struct Hud {
|
||||
int chosenLanguage;
|
||||
enum CutscenePromptType promptType;
|
||||
enum SubtitleKey subtitleKey;
|
||||
enum SubtitleKey queuedSubtitleKey;
|
||||
|
|
|
@ -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:])
|
Loading…
Reference in a new issue