game: Add remaster translation support

This commit is contained in:
Denis Pauk 2024-09-14 20:59:33 +03:00
parent 51d092660b
commit 1f0e67621c
11 changed files with 254 additions and 7 deletions

View File

@ -963,6 +963,7 @@ GAME_OBJS_ = \
src/game/g_sphere.o \
src/game/g_svcmds.o \
src/game/g_target.o \
src/game/g_translate.o \
src/game/g_trigger.o \
src/game/g_turret.o \
src/game/g_utils.o \

View File

@ -2618,7 +2618,7 @@ door_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
self->touch_debounce_time = level.time + 5.0;
gi.centerprintf(other, "%s", self->message);
gi.centerprintf(other, "%s", LocalizationMessage(self->message));
gi.sound(other, CHAN_AUTO, gi.soundindex("misc/talk1.wav"), 1, ATTN_NORM, 0);
}

View File

@ -228,7 +228,7 @@ secret_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurfa
if (self->message)
{
gi.centerprintf(other, self->message);
gi.centerprintf(other, LocalizationMessage(self->message));
}
}

View File

@ -865,8 +865,8 @@ SP_worldspawn(edict_t *ent)
/* make some data visible to the server */
if (ent->message && ent->message[0])
{
gi.configstring(CS_NAME, ent->message);
Q_strlcpy(level.level_name, ent->message, sizeof(level.level_name));
Q_strlcpy(level.level_name, LocalizationMessage(ent->message), sizeof(level.level_name));
gi.configstring(CS_NAME, level.level_name);
}
else
{

218
src/game/g_translate.c Normal file
View File

@ -0,0 +1,218 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (c) ZeniMax Media Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Localization logic.
*
* =======================================================================
*/
#include "header/local.h"
localmessages_t *localmessages = NULL;
int nlocalmessages = 0;
static int
LocalizationSort(const void *p1, const void *p2)
{
localmessages_t *msg1, *msg2;
msg1 = (localmessages_t*)p1;
msg2 = (localmessages_t*)p2;
return Q_stricmp(msg1->key, msg2->key);
}
void
LocalizationInit(void)
{
byte *raw = NULL;
int len;
localmessages = NULL;
nlocalmessages = 0;
/* load the file */
len = gi.FS_LoadFile("localization/loc_english.txt", (void **)&raw);
if (len > 1)
{
char *buf, *curr;
int i;
buf = malloc(len + 1);
memcpy(buf, raw, len);
buf[len] = 0;
/* get lines count */
curr = buf;
while(*curr)
{
size_t linesize = 0;
linesize = strcspn(curr, "\n\r");
if (*curr && strncmp(curr, "//", 2) &&
*curr != '\n' && *curr != '\r')
{
nlocalmessages ++;
}
curr += linesize;
if (curr >= (buf + len))
{
break;
}
/* skip our endline */
curr++;
}
if (nlocalmessages)
{
localmessages = gi.TagMalloc(nlocalmessages * sizeof(*localmessages), TAG_GAME);
memset(localmessages, 0, nlocalmessages * sizeof(*localmessages));
}
/* parse lines */
curr = buf;
i = 0;
while(*curr)
{
size_t linesize = 0;
if (i == nlocalmessages)
{
break;
}
linesize = strcspn(curr, "\n\r");
curr[linesize] = 0;
if (*curr && strncmp(curr, "//", 2) &&
*curr != '\n' && *curr != '\r')
{
char *sign;
sign = strchr(curr, '=');
/* clean up end of key */
if (sign)
{
char *signend;
signend = sign - 1;
while ((*signend == ' ' || *signend == '\t') &&
(signend > curr))
{
*signend = 0;
/* go back */
signend --;
}
*sign = 0;
sign ++;
}
/* skip value prefix */
if (sign && *sign)
{
size_t valueskip;
valueskip = strcspn(sign, " \t");
sign += valueskip;
if (*sign)
{
sign++;
}
}
/* start real value */
if (sign && *sign == '"')
{
char *currend;
sign ++;
currend = sign;
while (*currend && *currend != '"')
{
if (*currend == '\\')
{
/* escaped */
*currend = ' ';
currend++;
if (*currend == 'n')
{
/* new line */
*currend = '\n';
}
}
currend++;
}
if (*currend == '"')
{
/* mark as end of string */
*currend = 0;
}
localmessages[i].key = gi.TagMalloc(strlen(curr) + 2, TAG_GAME);
localmessages[i].key[0] = '$';
strcpy(localmessages[i].key + 1, curr);
localmessages[i].value = gi.TagMalloc(strlen(sign) + 1, TAG_GAME);
strcpy(localmessages[i].value, sign);
i ++;
}
}
curr += linesize;
if (curr >= (buf + len))
{
break;
}
/* skip our endline */
curr++;
}
nlocalmessages = i;
/* sort messages */
qsort(localmessages, nlocalmessages, sizeof(localmessages_t), LocalizationSort);
gi.FS_FreeFile(raw);
free(buf);
}
}
const char*
LocalizationMessage(const char *message)
{
if (!message || !localmessages || !nlocalmessages)
{
return message;
}
if (message[0] == '$')
{
int i;
for (i = 0; i < nlocalmessages; i++)
{
if (!strcmp(localmessages[i].key, message))
{
return localmessages[i].value;
}
}
}
return message;
}

View File

@ -321,7 +321,7 @@ G_UseTargets(edict_t *ent, edict_t *activator)
/* print the message */
if (activator && (ent->message) && !(activator->svflags & SVF_MONSTER))
{
gi.centerprintf(activator, "%s", ent->message);
gi.centerprintf(activator, "%s", LocalizationMessage(ent->message));
if (ent->noise_index)
{

View File

@ -189,6 +189,15 @@ typedef struct
void (*AddCommandString)(const char *text);
void (*DebugGraph)(float value, int color);
/* Extended to classic Quake2 API.
files will be memory mapped read only
the returned buffer may be part of a larger pak file,
or a discrete file from anywhere in the quake search path
a -1 return means the file does not exist
NULL can be passed for buf to just determine existance */
int (*FS_LoadFile) (const char *name, void **buf);
void (*FS_FreeFile) (void *buf);
} game_import_t;
/* functions exported by the game subsystem */

View File

@ -994,6 +994,18 @@ void SV_AddGravity(edict_t *ent);
void SaveClientData(void);
void EndDMLevel(void);
/* g_translate.c */
typedef struct
{
char *key;
char *value;
} localmessages_t;
extern localmessages_t *localmessages;
extern int nlocalmessages;
void LocalizationInit(void);
const char* LocalizationMessage(const char *message);
/* g_chase.c */
void UpdateChaseCam(edict_t *ent);
void ChaseNext(edict_t *ent);

View File

@ -421,8 +421,8 @@ HelpComputerMessage(edict_t *ent)
"xv 50 yv 172 string2 \"%3i/%3i %i/%i %i/%i\" ",
sk,
level.level_name,
game.helpmessage1,
game.helpmessage2,
LocalizationMessage(game.helpmessage1),
LocalizationMessage(game.helpmessage2),
level.killed_monsters, level.total_monsters,
level.found_goals, level.total_goals,
level.found_secrets, level.total_secrets);

View File

@ -264,6 +264,9 @@ InitGame(void)
g_quick_weap = gi.cvar("g_quick_weap", "1", CVAR_ARCHIVE);
g_swap_speed = gi.cvar("g_swap_speed", "1", 0);
/* initilize localization */
LocalizationInit();
/* items */
InitItems();

View File

@ -453,6 +453,10 @@ SV_InitGameProgs(void)
import.SetAreaPortalState = CM_SetAreaPortalState;
import.AreasConnected = CM_AreasConnected;
/* Extension to classic Quake2 API */
import.FS_LoadFile = FS_LoadFile;
import.FS_FreeFile = FS_FreeFile;
ge = (game_export_t *)Sys_GetGameAPI(&import);
if (!ge)