This commit is contained in:
2025-11-07 19:19:17 -06:00
parent 1ce1fdff8d
commit 12c1fb6000
11 changed files with 149 additions and 3 deletions

View File

@@ -5,7 +5,11 @@
set(DUSK_GAME_ASSETS_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL ${DUSK_CACHE_TARGET}) set(DUSK_GAME_ASSETS_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL ${DUSK_CACHE_TARGET})
add_subdirectory(palette)# Palette asset needs to be added before any images. # Palette asset needs to be added before any images.
add_subdirectory(palette)
# Languages need to be added before anything that uses text.
add_subdirectory(locale)
add_subdirectory(entity) add_subdirectory(entity)
add_subdirectory(map) add_subdirectory(map)

View File

@@ -0,0 +1,6 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
add_asset(LANGUAGE en_US.po)

10
assets/locale/en_US.po Normal file
View File

@@ -0,0 +1,10 @@
msgid ""
msgstr ""
"Language: us\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "test.test"
msgstr "Hello this is a test."

View File

@@ -16,9 +16,14 @@ typedef enum {
ASSET_TYPE_COUNT, ASSET_TYPE_COUNT,
} assettype_t; } assettype_t;
typedef enum {
ASSET_LOAD_STRAT_ENTIRE,
} assetloadstrat_t;
typedef struct { typedef struct {
const char_t *header; const char_t *header;
const size_t dataSize; const size_t dataSize;
const assetloadstrat_t loadStrategy;
errorret_t (*load)(void *data, void *output); errorret_t (*load)(void *data, void *output);
} assettypedef_t; } assettypedef_t;
@@ -29,12 +34,14 @@ static const assettypedef_t ASSET_TYPE_DEFINITIONS[ASSET_TYPE_COUNT] = {
[ASSET_TYPE_PALETTE_IMAGE] = { [ASSET_TYPE_PALETTE_IMAGE] = {
.header = "DPI", .header = "DPI",
.loadStrategy = ASSET_LOAD_STRAT_ENTIRE,
.dataSize = sizeof(assetpaletteimage_t), .dataSize = sizeof(assetpaletteimage_t),
.load = assetPaletteImageLoad .load = assetPaletteImageLoad
}, },
[ASSET_TYPE_ALPHA_IMAGE] = { [ASSET_TYPE_ALPHA_IMAGE] = {
.header = "DAI", .header = "DAI",
.loadStrategy = ASSET_LOAD_STRAT_ENTIRE,
.dataSize = sizeof(assetalphaimage_t), .dataSize = sizeof(assetalphaimage_t),
.load = assetAlphaImageLoad .load = assetAlphaImageLoad
}, },

View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "locale/language/keys.h"
#pragma pack(push, 1)
typedef struct {
uint32_t chunk;
uint32_t offset;
uint32_t length;
} assetlanguagestring_t;
#pragma pack(pop)
#pragma pack(push, 1)
typedef struct {
assetlanguagestring_t strings[LANG_KEY_COUNT];
} assetlanguageheader_t;
#pragma pack(pop)

View File

@@ -0,0 +1,18 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#define LANG_CHUNK_STRING_COUNT 64
#define LANG_CHUNK_STRING_MAX_LENGTH DUSK_LANG_STRING_MAX_LENGTH
#pragma pack(push, 1)
typedef struct {
char_t
} languagechunk_t;
#pragma pack(pop)

View File

@@ -74,7 +74,7 @@ int32_t stringFormatVA(
char_t *dest, char_t *dest,
const size_t destSize, const size_t destSize,
const char_t *format, const char_t *format,
const va_list args va_list args
) { ) {
assertNotNull(format, "format must not be NULL"); assertNotNull(format, "format must not be NULL");

View File

@@ -97,7 +97,7 @@ int32_t stringFormatVA(
char_t *dest, char_t *dest,
const size_t destSize, const size_t destSize,
const char_t *format, const char_t *format,
const va_list args va_list args
); );
/** /**

View File

@@ -3,6 +3,7 @@ from args import inputAssets, args
from processasset import processAsset from processasset import processAsset
from processpalette import processPaletteList from processpalette import processPaletteList
from processtileset import processTilesetList from processtileset import processTilesetList
from processlanguage import processLanguageList
from assethelpers import getBuiltAssetsRelativePath from assethelpers import getBuiltAssetsRelativePath
import zipfile import zipfile
@@ -31,6 +32,7 @@ with zipfile.ZipFile(outputFileName, 'w') as zipf:
# Generate additional headers. # Generate additional headers.
processPaletteList() processPaletteList()
processTilesetList() processTilesetList()
processLanguageList()
# Finalize build # Finalize build
if args.build_type == 'header': if args.build_type == 'header':

View File

@@ -4,6 +4,7 @@ from processimage import processImage
from processpalette import processPalette from processpalette import processPalette
from processtileset import processTileset from processtileset import processTileset
from processmap import processMap from processmap import processMap
from processlanguage import processLanguage
processedAssets = [] processedAssets = []
@@ -22,6 +23,8 @@ def processAsset(asset):
return processTileset(asset) return processTileset(asset)
elif t == 'map': elif t == 'map':
return processMap(asset) return processMap(asset)
elif t == 'language':
return processLanguage(asset)
else: else:
print(f"Error: Unknown asset type '{asset['type']}' for path '{asset['path']}'") print(f"Error: Unknown asset type '{asset['type']}' for path '{asset['path']}'")
sys.exit(1) sys.exit(1)

View File

@@ -0,0 +1,73 @@
import sys
import os
from args import args
from assetcache import assetCache, assetGetCache
from assethelpers import getAssetRelativePath
import polib
import re
LANGUAGE_DATA = {}
LANGUAGE_KEYS = []
def processLanguageList():
# Language keys header data
headerKeys = "// Auto-generated language keys header file.\n"
headerKeys += "#pragma once\n"
headerKeys += "#include \"dusk.h\"\n\n"
keyIndex = 0
for key in LANGUAGE_KEYS:
headerKeys += f"#define {getLanguageVariableName(key)} {keyIndex}\n"
keyIndex += 1
for lang in LANGUAGE_DATA:
if key not in LANGUAGE_DATA[lang]:
print(f"Warning: Missing translation for key '{key}' in language '{lang}'")
sys.exit(1)
headerKeys += f"\n#define LANG_KEY_COUNT {len(LANGUAGE_KEYS)}\n"
# Write out the language keys header file
outputFile = os.path.join(args.headers_dir, "locale", "language", "keys.h")
os.makedirs(os.path.dirname(outputFile), exist_ok=True)
with open(outputFile, "w") as f:
f.write(headerKeys)
def getLanguageVariableName(languageKey):
# Take the language key, prepend LANG_, uppercase, replace any non symbols
# with _
key = languageKey.strip().upper()
key = re.sub(r'[^A-Z0-9]', '_', key)
return f"LANG_{key}"
def processLanguage(asset):
cache = assetGetCache(asset['path'])
if cache is not None:
return cache
# Load PO File
po = polib.pofile(asset['path'])
langName = po.metadata.get('Language')
if langName not in LANGUAGE_DATA:
LANGUAGE_DATA[langName] = {}
for entry in po:
key = entry.msgid
val = entry.msgstr
if key not in LANGUAGE_KEYS:
LANGUAGE_KEYS.append(key)
if key not in LANGUAGE_DATA[langName]:
LANGUAGE_DATA[langName][key] = val
else:
print(f"Error: Duplicate translation key '{key}' in language '{langName}'")
sys.exit(1)
outLanguageData = {
'data': po,
'path': asset['path'],
'files': []
}
return assetCache(asset['path'], outLanguageData)