Language finished.

This commit is contained in:
2025-11-08 11:12:04 -06:00
parent ab534bb998
commit bc4776f096
9 changed files with 218 additions and 16 deletions

View File

@@ -1,6 +1,6 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Language: us\n" "Language: en_US\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

View File

@@ -100,7 +100,7 @@ errorret_t assetLoad(const char_t *filename, void *output) {
.zipFile = file, .zipFile = file,
.output = output .output = output
}; };
errorChain(def->custom(&customData)); errorChain(def->custom(customData));
break; break;
default: default:

View File

@@ -35,7 +35,7 @@ typedef struct {
const assetloadstrat_t loadStrategy; const assetloadstrat_t loadStrategy;
union { union {
errorret_t (*entire)(void *data, void *output); errorret_t (*entire)(void *data, void *output);
errorret_t (*custom)(assetcustom_t *custom); errorret_t (*custom)(assetcustom_t custom);
}; };
} assettypedef_t; } assettypedef_t;
@@ -61,6 +61,6 @@ static const assettypedef_t ASSET_TYPE_DEFINITIONS[ASSET_TYPE_COUNT] = {
[ASSET_TYPE_LANGUAGE] = { [ASSET_TYPE_LANGUAGE] = {
.header = "DLF", .header = "DLF",
.loadStrategy = ASSET_LOAD_STRAT_CUSTOM, .loadStrategy = ASSET_LOAD_STRAT_CUSTOM,
.custom = assetLanguageInit .custom = assetLanguageHandler
} }
}; };

View File

@@ -6,9 +6,108 @@
*/ */
#include "asset/asset.h" #include "asset/asset.h"
#include "assert/assert.h"
#include "locale/localemanager.h"
errorret_t assetLanguageInit(assetcustom_t *custom) { errorret_t assetLanguageHandler(assetcustom_t custom) {
zip_fclose(custom->zipFile); assertNotNull(custom.zipFile, "Custom asset zip file cannot be NULL");
assertNotNull(custom.output, "Custom asset output cannot be NULL");
assetlanguage_t *lang = (assetlanguage_t *)custom.output;
errorChain(assetLanguageInit(lang, custom.zipFile));
errorOk(); errorOk();
}
errorret_t assetLanguageInit(
assetlanguage_t *lang,
zip_file_t *zipFile
) {
assertNotNull(lang, "Language asset cannot be NULL");
assertNotNull(zipFile, "Zip file cannot be NULL");
assertNull(lang->zip, "Language asset zip file must be NULL.");
assertTrue(zip_file_is_seekable(zipFile), "Language file must be seekable.");
// We now own the zip file handle.
lang->zip = zipFile;
printf("Initialized language asset.\n");
// Read in the header.
zip_int64_t bytesRead = zip_fread(
lang->zip,
&lang->header,
sizeof(assetlanguageheader_t)
);
if(bytesRead != sizeof(assetlanguageheader_t)) {
zip_fclose(lang->zip);
errorThrow("Failed to read language asset header.");
}
lang->chunksOffset = zip_ftell(lang->zip);
if(lang->chunksOffset <= 0) {
zip_fclose(lang->zip);
errorThrow("Failed to get language asset chunks offset.");
}
errorOk();
}
errorret_t assetLanguageRead(
assetlanguage_t *lang,
const uint32_t key,
char_t *buffer,
const uint32_t bufferSize,
uint32_t *outLength
) {
assertNotNull(lang, "Language asset cannot be NULL");
assertNotNull(lang->zip, "Language asset zip file cannot be NULL");
assertTrue(key < LANG_KEY_COUNT, "Language key out of bounds.");
// Find the string entry
assetlanguagestring_t *str = &lang->header.strings[LANG_MAP_TEST];
// If buffer is NULL, return the string length
if(buffer == NULL) {
assertNotNull(outLength, "Output length pointer cannot be NULL.");
*outLength = str->length;
errorOk();
}
// Ensure buffer is large enough
assertTrue(
bufferSize >= str->length + 1,
"Provided buffer is too small for language string."
);
// Determine the file position
zip_int64_t seekTo = lang->chunksOffset + (
(str->chunk * ASSET_LANG_CHUNK_CHAR_COUNT) + str->offset
);
// Seek
zip_int64_t result = zip_fseek(lang->zip, seekTo, SEEK_SET);
if(result != 0) {
errorThrow("Failed to seek to language string in asset.");
}
// Read
zip_int64_t readTest = zip_fread(lang->zip, buffer, str->length);
if(readTest != str->length) {
errorThrow("Failed to read test string from language asset.");
}
buffer[str->length] = '\0';
// Set str length if requested
if(outLength != NULL) *outLength = str->length;
errorOk();
}
void assetLanguageDispose(assetlanguage_t *lang) {
assertNotNull(lang, "Language asset cannot be NULL");
if(lang->zip) {
zip_fclose(lang->zip);
}
printf("Disposed language asset.\n");
} }

View File

@@ -6,10 +6,12 @@
*/ */
#pragma once #pragma once
#include "error/error.h"
#include "locale/language/keys.h" #include "locale/language/keys.h"
#include "error/error.h"
#include <zip.h>
#define ASSET_LANG_CHUNK_CHAR_COUNT 6 * 1024 // 6 KB per chunk #define ASSET_LANG_CHUNK_CHAR_COUNT 6 * 1024 // 6 KB per chunk
#define ASSET_LANG_CHUNK_CACHE 4 // Number of chunks to cache in memory
#pragma pack(push, 1) #pragma pack(push, 1)
typedef char assetlanguagechunk_t[ASSET_LANG_CHUNK_CHAR_COUNT]; typedef char assetlanguagechunk_t[ASSET_LANG_CHUNK_CHAR_COUNT];
@@ -29,12 +31,57 @@ typedef struct {
} assetlanguageheader_t; } assetlanguageheader_t;
#pragma pack(pop) #pragma pack(pop)
typedef struct {
zip_file_t *zip;
assetlanguageheader_t header;
zip_int64_t chunksOffset;
// Chunk cache
assetlanguagechunk_t chunks[ASSET_LANG_CHUNK_CACHE];
uint32_t chunkIndices[ASSET_LANG_CHUNK_CACHE];
} assetlanguage_t;
typedef struct assetcustom_s assetcustom_t; typedef struct assetcustom_s assetcustom_t;
/** /**
* Receiving function from the asset manager to initialize language assets. * Receiving function from the asset manager to handle language assets.
* *
* @param custom Pointer to custom asset loading data. * @param custom Custom asset loading data.
* @return Error code. * @return Error code.
*/ */
errorret_t assetLanguageInit(assetcustom_t *custom); errorret_t assetLanguageHandler(assetcustom_t custom);
/**
* Initializes a language asset and loads the header data into memory.
*
* @param lang Language asset to initialize.
* @param zipFile Zip file handle for the language asset.
* @return Error code.
*/
errorret_t assetLanguageInit(assetlanguage_t *lang, zip_file_t *zipFile);
/**
* Reads a string from the language asset into the provided buffer.
*
* @param lang Language asset to read from.
* @param key Language key to read.
* @param buffer Buffer to read the string into.
* @param bufferSize Size of the provided buffer.
* @param outLength Pointer to store the length of the string read.
* @return Error code.
*/
errorret_t assetLanguageRead(
assetlanguage_t *lang,
const uint32_t key,
char_t *buffer,
const uint32_t bufferSize,
uint32_t *outLength
);
/**
* Disposes of language asset resources.
*
* @param custom Custom asset loading data.
* @return Error code.
*/
void assetLanguageDispose(assetlanguage_t *lang);

View File

@@ -54,6 +54,7 @@ void engineExit(void) {
} }
errorret_t engineDispose(void) { errorret_t engineDispose(void) {
localeManagerDispose();
sceneManagerDispose(); sceneManagerDispose();
rpgDispose(); rpgDispose();
uiDispose(); uiDispose();

View File

@@ -8,12 +8,29 @@
#include "localemanager.h" #include "localemanager.h"
#include "util/memory.h" #include "util/memory.h"
#include "asset/asset.h" #include "asset/asset.h"
#include "assert/assert.h"
localemanager_t LOCALE; localemanager_t LOCALE;
errorret_t localeManagerInit() { errorret_t localeManagerInit() {
memoryZero(&LOCALE, sizeof(localemanager_t)); memoryZero(&LOCALE, sizeof(localemanager_t));
errorChain(localeManagerSetLocale(DUSK_LOCALE_EN_US));
errorChain(assetLoad("language/us.dlf", &LOCALE));
errorOk(); errorOk();
}
errorret_t localeManagerSetLocale(const dusklocale_t locale) {
assertTrue(locale < DUSK_LOCALE_COUNT, "Invalid locale.");
LOCALE.locale = locale;
char_t languageFile[FILENAME_MAX];
snprintf(
languageFile, FILENAME_MAX, "language/%s.dlf", LOCALE_INFOS[locale].file
);
assetLanguageDispose(&LOCALE.language);
memoryZero(&LOCALE.language, sizeof(assetlanguage_t));
errorChain(assetLoad(languageFile, &LOCALE.language));
errorOk();
}
void localeManagerDispose() {
assetLanguageDispose(&LOCALE.language);
} }

View File

@@ -6,12 +6,30 @@
*/ */
#pragma once #pragma once
#include "error/error.h" #include "asset/asset.h"
typedef enum {
DUSK_LOCALE_EN_US,
DUSK_LOCALE_COUNT
} dusklocale_t;
typedef struct { typedef struct {
void *nothing; dusklocale_t locale;
assetlanguage_t language;
} localemanager_t; } localemanager_t;
typedef struct {
const char_t *file;
} localeinfo_t;
static const localeinfo_t LOCALE_INFOS[DUSK_LOCALE_COUNT] = {
[DUSK_LOCALE_EN_US] = {
.file = "en_US"
}
};
extern localemanager_t LOCALE; extern localemanager_t LOCALE;
/** /**
@@ -19,4 +37,17 @@ extern localemanager_t LOCALE;
* *
* @return An error code if a failure occurs. * @return An error code if a failure occurs.
*/ */
errorret_t localeManagerInit(); errorret_t localeManagerInit();
/**
* Set the current locale.
*
* @param locale The locale to set.
* @return An error code if a failure occurs.
*/
errorret_t localeManagerSetLocale(const dusklocale_t locale);
/**
* Dispose of the locale system.
*/
void localeManagerDispose();

View File

@@ -60,6 +60,8 @@ def processLanguageList():
# Now we can generate the language string chunks. # Now we can generate the language string chunks.
nextChunkIndex = max(desiredChunkGroups.values()) + 1 nextChunkIndex = max(desiredChunkGroups.values()) + 1
files = []
for lang in LANGUAGE_DATA: for lang in LANGUAGE_DATA:
langData = LANGUAGE_DATA[lang] langData = LANGUAGE_DATA[lang]
@@ -102,7 +104,6 @@ def processLanguageList():
# We have now chunked all the keys for this language! # We have now chunked all the keys for this language!
langBuffer = b"" langBuffer = b""
files = []
# Write header info # Write header info
langBuffer += b'DLF' # Dusk Language File langBuffer += b'DLF' # Dusk Language File
@@ -127,6 +128,12 @@ def processLanguageList():
strData = langData[key].encode('utf-8') strData = langData[key].encode('utf-8')
langBuffer += strData langBuffer += strData
# Now pad the chunk to full size
curLen = chunkInfo['len']
if curLen < LANGUAGE_CHUNK_CHAR_COUNT:
padSize = LANGUAGE_CHUNK_CHAR_COUNT - curLen
langBuffer += b'\0' * padSize
# Write out the language data file # Write out the language data file
outputFile = os.path.join(args.output_assets, "language", f"{lang}.dlf") outputFile = os.path.join(args.output_assets, "language", f"{lang}.dlf")
files.append(outputFile) files.append(outputFile)