Initial commit

This commit is contained in:
Eagle517
2025-02-17 23:17:30 -06:00
commit 7cad314c94
4726 changed files with 1145203 additions and 0 deletions

140
tools/langc/Getopt.cc Executable file
View File

@ -0,0 +1,140 @@
/*
** Slab NG - The Next Generation of Slab
** (c) Copyright 2002-2004 Tom Bampton
** All Rights Reserved.
**
** $Id: Getopt.cpp,v 1.1 2003/10/30 23:55:29 tom Exp $
**
** Filename: Getopt.cpp
** Author: Tom Bampton
** Created: 30/10/2003
** Purpose:
** Command Line Parser
**
*/
/*
* Based on getopt.c from FreeBSD, bearing the following copyright message:
*
* Copyright (c) 1987, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "platform/platform.h"
#include "Getopt.h"
#define EMSG ""
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
Getopt::Getopt(char *sOptions)
{
m_sOptions = sOptions;
m_sPlace = EMSG;
m_nOptInd = 1;
}
Getopt::~Getopt()
{
}
int Getopt::getopt(int nargc, char **nargv)
{
char *oli; /* option letter list index */
m_nargc = nargc;
m_nargv = nargv;
if (!*m_sPlace) { /* update scanning pointer */
if (m_nOptInd >= nargc || *(m_sPlace = nargv[m_nOptInd]) != '-') {
m_sPlace = EMSG;
return -1;
}
if (m_sPlace[1] && *++m_sPlace == '-') { /* found "--" */
++m_nOptInd;
m_sPlace = EMSG;
return -1;
}
} /* option letter okay? */
if ((m_nOptOpt = (int)*m_sPlace++) == (int)':' ||
!(oli = dStrchr(m_sOptions, m_nOptOpt))) {
/*
* if the user didn't specify '-' as an option,
* assume it means -1.
*/
if (m_nOptOpt == (int)'-')
return (-1);
if (!*m_sPlace)
++m_nOptInd;
if (*m_sOptions != ':' && m_nOptOpt != GO_BAD_CHAR)
return GO_INVALID_CHAR;
return (GO_BAD_CHAR);
}
if (*++oli != ':') { /* don't need argument */
m_sOptArg = NULL;
if (!*m_sPlace)
++m_nOptInd;
}
else { /* need an argument */
if (*m_sPlace) /* no white space */
m_sOptArg = m_sPlace;
else if (nargc <= ++m_nOptInd) { /* no arg */
m_sPlace = EMSG;
//if (*m_sOptions == ':')
return (GO_BAD_ARG);
//return (GO_BAD_CHAR);
}
else /* white space */
m_sOptArg = nargv[m_nOptInd];
m_sPlace = EMSG;
++m_nOptInd;
}
return (m_nOptOpt); /* dump back option letter */
}
void Getopt::Reset(void)
{
if (!*m_sPlace) { /* update scanning pointer */
if (m_nOptInd >= m_nargc || *(m_sPlace = m_nargv[m_nOptInd]) != '-') {
m_sPlace = EMSG;
return;
}
if (m_sPlace[1] && *++m_sPlace == '-') { /* found "--" */
++m_nOptInd;
m_sPlace = EMSG;
return;
}
}
}

115
tools/langc/Getopt.h Executable file
View File

@ -0,0 +1,115 @@
/*
** Slab NG - The Next Generation of Slab
** (c) Copyright 2002-2004 Tom Bampton
** All Rights Reserved.
**
** $Id: Getopt.h,v 1.1 2003/10/30 23:55:29 tom Exp $
**
** Filename: Getopt.h
** Author: Tom Bampton
** Created: 30/10/2003
** Purpose:
** Command Line Parser
**
*/
//////////////////////////////////////////////////////////////////////////
/// \file Getopt.h
/// \brief Header for Getopt
//////////////////////////////////////////////////////////////////////////
#ifndef SLAB_GETOPT_H
#define SLAB_GETOPT_H
#define GO_BAD_CHAR '?'
#define GO_BAD_ARG ':'
#define GO_INVALID_CHAR '!'
//////////////////////////////////////////////////////////////////////
/// \brief Command Line Parser
///
/// Getopt provides a command line parser similar to Unix's getopt()
///
/// Note that this version of getopt() will not print any messages to
/// the terminal, you will need to manage this yourself.
///
/// Parts of this section have been lifted from the getopt() manual page.
//////////////////////////////////////////////////////////////////////
class Getopt
{
private:
char *m_sOptions;
char *m_sPlace;
int m_nargc;
char **m_nargv;
public:
/*! \brief Current argv Index
*/
int m_nOptInd;
/*! \brief Current Option
*/
int m_nOptOpt;
/*! \brief Argument to current option if applicable
*/
char *m_sOptArg;
/*! \brief Construct a Getopt
The string tells Getopt what arguments this program takes. It may
contain the following elements: individual characters, and characters
followed by a colon to indicate an option argument is to follow. For
example, an option string "x" recognizes an option ``-x'', and an
option string "x:" recognizes an option and argu- ment ``-x argument''.
It does not matter to Getopt if a following argument has leading
white space.
\param sOptions Option string
\sa getopt(), Reset()
*/
Getopt(char *sOptions);
virtual ~Getopt();
/*! \brief Parse arguments
On return from getopt(), m_sOptArg points to an option argument, if it
is anticipated, and the variable m_nOptInd contains the index to the
next argv argument for a subsequent call to getopt(). The variable
m_nOptOpt saves the last known option character returned by getopt().
The m_nOptInd variable is set to 1, but may be set to another value
before a set of calls to getopt() in order to skip over more or less
argv entries.
In order to use getopt() to evaluate multiple sets of arguments, or to
evaluate a single set of arguments multiple times, call Reset() before
the second and each additional set of calls to getopt()
The getopt() function returns -1 when the argument list is exhausted,
or GO_INVALID_CHAR if a non-recognized option is encountered. You may
use m_nOptOpt to find the invalid character, and display a warning. If
an option takes an argument, but the user did not supply one on the
command line, getopt() returns GO_BAD_ARG. m_nOptOpt will then contain
the option, for printing of warning messages. The interpretation of
options in the argument list may be cancelled by the option `--'
(double dash) which causes getopt() to signal the end of argument
processing and return -1. When all options have been processed (i.e.,
up to the first non-option argument), getopt() returns -1.
\param nargc The argc from your main() function
\param nargv The argv from your main() function
\return -1 on end of processing, the character of the current option
or one of the error values as described above.
\sa Reset(), Getopt(char *sOptions)
*/
int getopt(int nargc, char **nargv);
/*! \brief Reset getopt() for subsequent calls
See the description of getopt() for information on Reset()
\sa getopt()
*/
void Reset(void);
};
#endif // SLAB_GETOPT_H

194
tools/langc/langc.cc Executable file
View File

@ -0,0 +1,194 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platform/event.h"
#include "platform/platformAssert.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "math/mathTypes.h"
#include "langc/langc.h"
#include "core/fileStream.h"
#include "i18n/lang.h"
#include "langcomp.h"
#include "Getopt.h"
#include <stdlib.h>
//////////////////////////////////////////////////////////////////////////
LangCGame GameObject;
// FOR SILLY LINK DEPENDANCY. REMOVE THIS AT YOUR PERIL.
bool gEditingMission = false;
//////////////////////////////////////////////////////////////////////////
static bool initLibraries()
{
// asserts should be created FIRST
PlatformAssert::create();
_StringTable::create();
// ResManager::create();
Con::init();
Math::init();
Platform::init(); // platform specific initialization
return(true);
}
static void shutdownLibraries()
{
// Purge any resources on the timeout list...
// if (ResourceManager)
// ResourceManager->purge();
// shut down
Platform::shutdown();
Con::shutdown();
// ResManager::destroy();
_StringTable::destroy();
// asserts should be destroyed LAST
PlatformAssert::destroy();
}
//////////////////////////////////////////////////////////////////////////
static void usage(void)
{
dPrintf("Usage: langc [options] <filename> <outbasename>\n\n");
dPrintf("Where options is one or more of:\n\n");
dPrintf(" -l Write Language File -h Write C++ Header\n");
dPrintf(" -s Write Script -d Write C++ Defaults\n");
dPrintf(" -t Compile a translation -r Write translation file\n");
dPrintf(" -e <filename> Specify english file when compiling translations\n");
dPrintf("\n");
dPrintf(" -S Don't strip leading spaces -T Strip trailing spaces\n");
dPrintf(" -I Don't warn for invalid chars -W Don't warn for empty identifiers\n");
dPrintf(" -q Quiet mode, no warnings at all\n");
dPrintf("\nMore information can be found in the documentation at:\n %s\n", I18N_DOC_URL);
}
S32 LangCGame::main(S32 argc, const char **argv)
{
S32 i, ch;
U32 flags = LCO_WARNNOSTRING;
Getopt opts("STIWqhsdlrte:");
char *englishFile = NULL;
if(! initLibraries())
return 0;
while((ch = opts.getopt(argc, (char **)argv)) != -1)
{
switch(ch)
{
case 't':
// Compile a translation
flags |= LCO_COMPILETRANSLATION;
break;
case 'e':
// Specify english file
englishFile = opts.m_sOptArg;
break;
case 'S':
// Don't strip spaces
flags |= LCO_DONTSTRIPSPACES;
break;
case 'T':
// Strip trailing space
flags |= LCO_STRIPTRAILINGSPACE;
break;
case 'I':
// Don't warn for invalid chars
flags |= LCO_DONTWARNINVALIDCHAR;
break;
case 'W':
// Don't warn for empty identifiers
flags &= ~LCO_WARNNOSTRING;
break;
case 'q':
// Quiet mode, no warnings at all
flags |= LCO_NOWARNINGS;
break;
case 'h':
// Write Header
flags |= LCO_WRITEHEADER;
break;
case 's':
// Write Script
flags |= LCO_WRITESCRIPT;
break;
case 'd':
// Write C++ Defaults
flags |= LCO_WRITECDEFAULTS;
break;
case 'l':
// Write Lang Table
flags |= LCO_WRITELANGTABLE;
break;
case 'r':
// Write translation
flags |= LCO_WRITETRANSLATION;
break;
case GO_BAD_ARG:
dPrintf("option %c requires an argument\n", opts.m_nOptOpt);
break;
case GO_INVALID_CHAR:
dPrintf("%c is an invalid option\n", opts.m_nOptOpt);
break;
case GO_BAD_CHAR:
usage();
shutdownLibraries();
return 0;
}
}
argc -= opts.m_nOptInd;
argv += opts.m_nOptInd;
if(argc < 2)
{
usage();
return 0;
}
LangComp c(flags);
dPrintf("Compiling ... \n");
c.Compile(argv[0], argv[1], englishFile);
shutdownLibraries();
return 0;
}
//////////////////////////////////////////////////////////////////////////
void GameReactivate()
{
}
void GameDeactivate( bool )
{
}

19
tools/langc/langc.h Executable file
View File

@ -0,0 +1,19 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#ifndef _LANGC_H_
#define _LANGC_H_
#include "platform/gameInterface.h"
#define I18N_DOC_URL "http://tdn.garagegames.com/wiki/TorqueLocalization"
class LangCGame : public GameInterface
{
public:
S32 main(S32 argc, const char **argv);
};
#endif // _LANGC_H_

702
tools/langc/langcomp.cc Executable file
View File

@ -0,0 +1,702 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platform/event.h"
#include "platform/platformAssert.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "math/mathTypes.h"
#include "langc/langc.h"
#include "core/fileStream.h"
#include "i18n/lang.h"
#include <stdlib.h>
#include "langcomp.h"
//////////////////////////////////////////////////////////////////////////
// Simple hash function for ID lookups in translations
//////////////////////////////////////////////////////////////////////////
static U32 hashString(const UTF8 *str)
{
S32 i;
U32 h = 0;
for(i = 0;str[i];i++)
{
h += (str[i] * i);
}
return h;
}
//////////////////////////////////////////////////////////////////////////
// LFileWriter Class
//////////////////////////////////////////////////////////////////////////
LFileWriter::LFileWriter(LangFile *langFile) : mLangFile(langFile)
{
}
bool LFileWriter::Open(const UTF8 *basename)
{
UTF8 filename[256];
GetFilename(basename, filename, sizeof(filename));
return mStream.open((const char*)filename, FileStream::Write);
}
void LFileWriter::Close()
{
mStream.close();
}
//////////////////////////////////////////////////////////////////////////
// Writer Classes
//////////////////////////////////////////////////////////////////////////
class LHeaderWriter : public LFileWriter
{
public:
LHeaderWriter(LangFile *l) : LFileWriter(l)
{
}
virtual void GetFilename(const UTF8 *basename, UTF8 *buf, U32 bufsize)
{
dSprintf(buf, bufsize, "%s.h", basename);
}
virtual void WriteHeader()
{
mStream.writeLine((U8 *)"// Automatically generated. DO NOT EDIT\n");
mStream.writeLine((U8 *)"#ifndef _TGE_I18N_AUTOGEN_H_");
mStream.writeLine((U8 *)"#define _TGE_I18N_AUTOGEN_H_\n");
}
virtual void WriteFooter()
{
UTF8 buf[LCL_MAXSTRINGLENGTH + 4];
dSprintf(buf, sizeof(buf), "\n#define I18N_NUM_STRINGS\t%d", mLangFile->getNumStrings());
mStream.writeLine((U8 *)&buf);
mStream.writeLine((U8 *)"\n#endif // _TGE_I18N_AUTOGEN_H_");
}
virtual void WriteString(UTF8 *idstr, U32 idnum, UTF8 *str)
{
UTF8 buf[LCL_MAXSTRINGLENGTH + 4];
dSprintf(buf, sizeof(buf), "// %s", str);
mStream.writeLine((U8 *)&buf);
dSprintf(buf, sizeof(buf), "#define %s\t%d", idstr, idnum);
mStream.writeLine((U8 *)&buf);
}
};
class LScriptWriter : public LFileWriter
{
public:
LScriptWriter(LangFile *l) : LFileWriter(l)
{
}
virtual void GetFilename(const UTF8 *basename, UTF8 *buf, U32 bufsize)
{
dSprintf(buf, bufsize, "%s.cs", basename);
}
virtual void WriteHeader()
{
mStream.writeLine((U8 *)"// Automatically generated. DO NOT EDIT\n");
}
virtual void WriteFooter()
{
}
virtual void WriteString(UTF8 *idstr, U32 idnum, UTF8 *str)
{
UTF8 buf[LCL_MAXSTRINGLENGTH + 4];
dSprintf(buf, sizeof(buf), "// %s", str);
mStream.writeLine((U8 *)&buf);
dSprintf(buf, sizeof(buf), "$%s = %d;", idstr, idnum);
mStream.writeLine((U8 *)&buf);
}
};
class LDefaultsWriter : public LFileWriter
{
public:
LDefaultsWriter(LangFile *l) : LFileWriter(l)
{
}
virtual void GetFilename(const UTF8 *basename, UTF8 *buf, U32 bufsize)
{
dSprintf(buf, bufsize, "%sDefaults.cc", basename);
}
virtual void WriteHeader()
{
mStream.writeLine((U8 *)"// Automatically generated. DO NOT EDIT\n");
mStream.writeLine((U8 *)"const UTF8 *gI18NDefaultStrings[] =\n{");
}
virtual void WriteFooter()
{
mStream.writeLine((U8 *)"};");
}
virtual void WriteString(UTF8 *idstr, U32 idnum, UTF8 *str)
{
UTF8 buf[LCL_MAXSTRINGLENGTH + 4];
dSprintf(buf, sizeof(buf), "\t// %s = %d", idstr, idnum);
mStream.writeLine((U8 *)&buf);
dSprintf(buf, sizeof(buf), "\t(const UTF8*)\"%s\",", str);
mStream.writeLine((U8 *)&buf);
}
};
class LTransWriter : public LFileWriter
{
public:
LTransWriter(LangFile *l) : LFileWriter(l)
{
}
virtual void GetFilename(const UTF8 *basename, UTF8 *buf, U32 bufsize)
{
dSprintf(buf, bufsize, "%s.tran", basename);
}
virtual void WriteHeader()
{
mStream.writeLine((U8 *)"# Automatically generated.\n");
}
virtual void WriteFooter()
{
}
virtual void WriteString(UTF8 *idstr, U32 idnum, UTF8 *str)
{
UTF8 buf[LCL_MAXSTRINGLENGTH + 4];
dSprintf(buf, sizeof(buf), "# [%s:%d] %s", idstr, idnum, str);
mStream.writeLine((U8 *)&buf);
dSprintf(buf, sizeof(buf), "%s=\n", idstr);
mStream.writeLine((U8 *)&buf);
}
};
//////////////////////////////////////////////////////////////////////////
// LString Class
//////////////////////////////////////////////////////////////////////////
LString::LString(UTF8 *i /* = NULL */, UTF8 *s /* = NULL */, UTF8 *sclean /* = NULL */) : str(NULL), strclean(NULL), id(NULL)
{
if(s)
{
str = new UTF8 [dStrlen(s) + 1];
dStrcpy(str, s);
}
if(sclean)
{
strclean = new UTF8 [dStrlen(sclean) + 1];
dStrcpy(strclean, sclean);
}
if(i)
{
id = new UTF8 [dStrlen(i) + 1];
dStrcpy(id, i);
}
}
LString::~LString()
{
if(str)
delete [] str;
if(id)
delete [] id;
}
//////////////////////////////////////////////////////////////////////////
// LangComp Class
//////////////////////////////////////////////////////////////////////////
LangComp::LangComp(U32 options /* = 0L */) : mOptions(options), mLangFile(NULL),
mCurrentFilename(NULL), mCurrentLine(0), mNumErrors(0), mNumWarn(0),
mTransLookup(LC_ID_LOOKUP_SIZE)
{
}
LangComp::~LangComp()
{
Free();
}
//////////////////////////////////////////////////////////////////////////
// Protected Methods
//////////////////////////////////////////////////////////////////////////
void LangComp::Free()
{
if(mLangFile)
{
delete mLangFile;
mLangFile = NULL;
}
if(mCurrentFilename)
{
delete [] mCurrentFilename;
mCurrentFilename = NULL;
}
mCurrentLine = 0;
S32 i;
for(i = 0;i < mFileWriters.size();i++)
{
delete mFileWriters[i];
}
mFileWriters.empty();
}
void LangComp::Error(const UTF8 *msg, ...)
{
UTF8 buf[512];
va_list va;
va_start(va, msg);
dVsprintf(buf, sizeof(buf), msg, va);
va_end(va);
if(mCurrentFilename)
dPrintf("error at %s:%d: %s\n", mCurrentFilename, mCurrentLine, buf);
else
dPrintf("error: %s\n", buf);
mNumErrors++;
}
void LangComp::Warn(const UTF8 *msg, ...)
{
UTF8 buf[512];
va_list va;
if(mOptions & LCO_NOWARNINGS)
return;
va_start(va, msg);
dVsprintf(buf, sizeof(buf), msg, va);
va_end(va);
if(mCurrentFilename)
dPrintf("warning at %s:%d: %s\n", mCurrentFilename, mCurrentLine, buf);
else
dPrintf("warning: %s\n", buf);
mNumWarn++;
}
LString * LangComp::ParseLine(UTF8 *line)
{
UTF8 *p, id[LCL_MAXIDLENGTH], str[LCL_MAXSTRINGLENGTH];
S32 i, warnCount;
i = dStrlen(line)-1;
if(line[i] == '\n') line[i] = 0;
i = dStrlen(line)-1;
if(line[i] == '\r') line[i] = 0;
p = line;
// Allowable comment delimiters: # ; //
if(*p == '#' || *p == ';' || (*p == '/' && *(p+1) == '/') || *p == 0)
return new LString();
i = 0;
while(*p)
{
if(*p == '=')
break;
if(i < (sizeof(id)-1))
{
if(dIsalnum(*p) || *p == '_')
{
id[i] = *p;
i++;
}
else if(! dIsspace(*p) && ! (mOptions & LCO_DONTWARNINVALIDCHAR))
Warn("invalid character ('%c') in identifier ignored", *p);
}
p++;
}
id[i] = 0;
p++;
// Identifiers cannot start with a number
if(dIsdigit(id[0]))
{
Error("identifiers cannot start with a number");
return NULL;
}
// Identifiers must be there
if(id[0] == 0)
{
Error("no identifier");
return NULL;
}
i = 0;
bool foundStart = (mOptions & LCO_DONTSTRIPSPACES);
while(*p)
{
if(i < (sizeof(str)-1))
{
if(!foundStart && ! dIsspace(*p))
foundStart = true;
if(foundStart && i < (sizeof(str)-1))
{
str[i] = *p;
i++;
}
}
p++;
}
str[i] = 0;
if(mOptions & LCO_STRIPTRAILINGSPACE)
{
p = dStrchr(str, 0);
while(dIsspace(*(--p)))
*p = 0;
}
if(mOptions & LCO_WARNNOSTRING && str[0] == 0)
{
Warn("identifier '%s' is empty", id);
}
UTF8 strbuf[LCL_MAXSTRINGLENGTH];
processSlashes(str, strbuf);
return new LString(id, strbuf, str);
}
void LangComp::WriteString(UTF8 *idstr, U32 idnum, UTF8 *str)
{
S32 i;
for(i = 0;i < mFileWriters.size();i++)
{
mFileWriters[i]->WriteString(idstr, idnum, str);
}
}
void LangComp::WriteFileHeader()
{
S32 i;
for(i = 0;i < mFileWriters.size();i++)
{
mFileWriters[i]->WriteHeader();
}
}
void LangComp::WriteFileFooter()
{
S32 i;
for(i = 0;i < mFileWriters.size();i++)
{
mFileWriters[i]->WriteFooter();
}
}
// [tom, 3/7/2005] There is no buffer size limit here as the size of the buffer
// in ParseLine() is the same size as the buffer the string is in. If this function
// is ripped for use elsewhere, I suggest adding a buffer size check.
UTF8 *LangComp::processSlashes(const UTF8 *string, UTF8 *buffer)
{
const UTF8 *s = string;
UTF8 *d = buffer;
while(*s)
{
if(*s == '\\')
{
s++;
switch(*s)
{
case 'n':
*d++ = '\n';
s++;
break;
case 'r':
*d++ = '\r';
s++;
break;
case 't':
*d++ = '\t';
s++;
break;
default:
*d++ = *s++;
break;
}
}
else
{
*d++ = *s++;
}
}
*d = 0;
return buffer;
}
// [tom, 3/17/2005] No buffer check here either, this time out of lazyness.
// [tom, 3/17/2005] This function isnt actually used anymore. I'm keeping it
// here in case its useful in the future.
UTF8 *LangComp::quoteString(const UTF8 *string, UTF8 *buffer)
{
static struct {
unsigned char c;
unsigned char q;
} quoteTab[]=
{
{ '\n', 'n' },
{ '\r', 'r' },
{ '\t', 't' },
{ '"', '"' },
{ '\'', '\'' },
{ '\\', '\\' },
{ 0, 0 }
};
const UTF8 *s = string;
UTF8 *d = buffer;
while(*s)
{
int i;
bool rep = false;
for(i = 0;quoteTab[i].c;i++)
{
if(*s == quoteTab[i].c)
{
*d++ = '\\';
*d++ = quoteTab[i].q;
rep = true;
break;
}
}
if(!rep)
*d++ = *s;
s++;
}
*d = 0;
return buffer;
}
bool LangComp::AddFileWriter(LFileWriter *lfw, const UTF8 *basename)
{
if(lfw->Open(basename))
{
mFileWriters.push_back(lfw);
return true;
}
UTF8 buf[256];
lfw->GetFilename(basename, buf, sizeof(buf));
Warn("Could not open file \"%s\"", buf);
delete lfw;
return false;
}
//////////////////////////////////////////////////////////////////////////
// Public Methods
//////////////////////////////////////////////////////////////////////////
bool LangComp::Compile(const UTF8 *filename, const UTF8 *outbasename, const UTF8 *englishTable /* = NULL */)
{
bool ret = true;
UTF8 lsoname[256];
Free();
if((mOptions & LCO_COMPILETRANSLATION) && englishTable == NULL)
{
Error("you must specify the english language file when compiling translations.");
return false;
}
if(mOptions & LCO_COMPILETRANSLATION)
{
if(! LoadLangForTranslation(englishTable))
{
Error("could not load %s", englishTable);
return false;
}
}
dSprintf(lsoname, sizeof(lsoname), "%s.lso", outbasename);
mCurrentFilename = new UTF8 [dStrlen(filename) + 1];
dStrcpy(mCurrentFilename, filename);
mLangFile = new LangFile;
FileStream fs;
if(fs.open(filename, FileStream::Read))
{
if(mOptions & LCO_WRITEHEADER)
AddFileWriter(new LHeaderWriter(mLangFile), outbasename);
if(mOptions & LCO_WRITESCRIPT)
AddFileWriter(new LScriptWriter(mLangFile), outbasename);
if(mOptions & LCO_WRITECDEFAULTS)
AddFileWriter(new LDefaultsWriter(mLangFile), outbasename);
if(mOptions & LCO_WRITETRANSLATION)
AddFileWriter(new LTransWriter(mLangFile), outbasename);
WriteFileHeader();
// This buffer must be able to hold the max lengths, the equals, end of line chars and a bit of leeway for spaces
UTF8 buf[LCL_MAXIDLENGTH + LCL_MAXSTRINGLENGTH + 28];
while(fs.getStatus() != Stream::EOS)
{
mCurrentLine++;
fs.readLine((U8 *)&buf[0], sizeof(buf));
LString *ls;
if((ls = ParseLine(buf)) == NULL)
break;
if(ls->id)
{
U32 idnum;
if(mOptions & LCO_COMPILETRANSLATION)
{
idnum = (U32)mTransLookup.retreive(hashString(ls->id));
if(idnum == 0)
Warn("id %s does not exist in the english table", ls->id);
else
idnum--;
mLangFile->setString(idnum, ls->str);
}
else
idnum = mLangFile->addString(ls->str);
WriteString(ls->id, idnum, ls->strclean);
}
delete ls;
}
WriteFileFooter();
S32 i;
for(i = 0;i < mFileWriters.size();i++)
{
mFileWriters[i]->Close();
}
fs.close();
}
if(mOptions & LCO_WRITELANGTABLE)
{
if(ret && fs.open(lsoname, FileStream::Write))
{
mLangFile->save(&fs);
fs.close();
}
}
dPrintf("%s - ", lsoname);
if(ret)
dPrintf("%d string(s), ", mLangFile->getNumStrings());
dPrintf("%d error(s), %d warning(s)\n", mNumErrors, mNumWarn);
return ret;
}
bool LangComp::LoadLangForTranslation(const UTF8 *filename)
{
bool ret = true;
FileStream fs;
UTF8 *fnBak = mCurrentFilename;
U32 lineBak = mCurrentLine;
mCurrentFilename = new UTF8 [dStrlen(filename) + 1];
dStrcpy(mCurrentFilename, filename);
mCurrentLine = 0;
if(fs.open(filename, FileStream::Read))
{
// This buffer must be able to hold the max lengths, the equals, end of line chars and a bit of leeway for spaces
UTF8 buf[LCL_MAXIDLENGTH + LCL_MAXSTRINGLENGTH + 28];
U32 lastID = 1; // [tom, 4/25/2005] 0 is used as an error indicator
while(fs.getStatus() != Stream::EOS)
{
mCurrentLine++;
fs.readLine((U8 *)&buf[0], sizeof(buf));
LString *ls;
if((ls = ParseLine(buf)) == NULL)
break;
if(ls->id)
{
U32 h = hashString(ls->id);
mTransLookup.insert((U32 *)lastID, h);
lastID++;
}
delete ls;
}
fs.close();
}
delete [] mCurrentFilename;
mCurrentFilename = fnBak;
mCurrentLine = lineBak;
return ret;
}

115
tools/langc/langcomp.h Executable file
View File

@ -0,0 +1,115 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "core/tVector.h"
#include "tSimpleHash.h"
#ifndef _LANGCOMP_H_
#define _LANGCOMP_H_
// Limits
#define LCL_MAXIDLENGTH 128 // Maximum ID length
#define LCL_MAXSTRINGLENGTH 512 // Maximum string length
class LFileWriter
{
protected:
FileStream mStream;
LangFile *mLangFile;
public:
LFileWriter(LangFile *langFile);
virtual bool Open(const UTF8 *basename);
virtual void Close();
virtual void GetFilename(const UTF8 *basename, UTF8 *buf, U32 bufsize) = 0;
virtual void WriteHeader() = 0;
virtual void WriteFooter() = 0;
virtual void WriteString(UTF8 *idstr, U32 idnum, UTF8 *str) = 0;
};
struct LString
{
UTF8 *str;
UTF8 *strclean;
UTF8 *id;
LString(UTF8 *i = NULL, UTF8 *s = NULL, UTF8 *sclean = NULL);
virtual ~LString();
};
// [tom, 4/25/2005] This is the size of the SimpleHash table. Its created in
// the SimpleHash ctor so there is a trade off here between performance and
// memory usage, as the array will always be created. In practice, the memory
// usage probably isnt really going to be that horrendous for an empty table.
// It works out to 12 bytes multiplied by whatever you set LC_ID_LOOKUP_SIZE to.
//
// Since SimpleHash doesnt rehash, the optimal size depends entirely on the
// amount of strings in the table. starter.fps typically has a few hundred,
// whilst the C++ code typically has a few thousand. Compiling translations is
// still going to be pretty fast regardless of how bad the performance of
// the SparseArry is, so on the whole this comment probably doesnt really matter.
#define LC_ID_LOOKUP_SIZE 128
class LangComp
{
protected:
U32 mOptions;
LangFile *mLangFile;
UTF8 *mCurrentFilename;
U32 mCurrentLine;
U32 mNumErrors;
U32 mNumWarn;
Vector<LFileWriter *> mFileWriters;
SimpleHash<U32> mTransLookup;
void Free(void);
void Error(const UTF8 *msg, ...);
void Warn(const UTF8 *msg, ...);
LString * ParseLine(UTF8 *line);
void WriteString(UTF8 *idstr, U32 idnum, UTF8 *str);
void WriteFileHeader();
void WriteFileFooter();
UTF8 *processSlashes(const UTF8 *string, UTF8 *buffer);
UTF8 *quoteString(const UTF8 *string, UTF8 *buffer);
bool AddFileWriter(LFileWriter *lfw, const UTF8 *basename);
public:
LangComp(U32 options = 0L);
virtual ~LangComp();
bool Compile(const UTF8 *filename, const UTF8 *outbasename, const UTF8 *englishTable = NULL);
bool LoadLangForTranslation(const UTF8 *filename);
};
// Option Flags
#define LCO_DONTSTRIPSPACES (1L) // Don't strip leading spaces in strings
#define LCO_STRIPTRAILINGSPACE (1L << 1) // Strip trailing spaces in strings
#define LCO_DONTWARNINVALIDCHAR (1L << 2) // Don't warn for invalid characters
#define LCO_WARNNOSTRING (1L << 3) // Warn for empty identifiers
#define LCO_NOWARNINGS (1L << 4) // Don't warn at all
#define LCO_WRITEHEADER (1L << 5) // Write C++ Header file
#define LCO_WRITESCRIPT (1L << 6) // Write Script IDs file
#define LCO_WRITECDEFAULTS (1L << 7) // Write C++ defaults file
#define LCO_WRITECSDEFAULTS (1L << 8) // Write Script defaults file
#define LCO_WRITELANGTABLE (1L << 9) // Write Lang Table
#define LCO_WRITETRANSLATION (1L << 10) // Write an empty language file for translation
#define LCO_COMPILETRANSLATION (1L << 11) // Compile a translation file
#endif // _LANGCOMP_H_

140
tools/langc/tSimpleHash.h Executable file
View File

@ -0,0 +1,140 @@
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
// [tom, 4/26/2005] This was based on SparseArray ...
//
// SparseArray deletes the objects contained within it, and so doesnt really
// work for our purposes. Although SparseArray is not used anywhere in the
// engine, I figured it would be best to copy it and hack the copy rather
// then risk anyone's code breaking for no real reason.
#ifndef _TSIMPLEHASH_H_
#define _TSIMPLEHASH_H_
//Includes
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _PLATFORMASSERT_H_
#include "platform/platformAssert.h"
#endif
template <class T>
class SimpleHash
{
protected:
struct Node {
T* pObject;
U32 key;
Node* next;
};
protected:
U32 mModulus;
Node* mSentryTables;
void clearTables(); // Note: _deletes_ the objects!
public:
SimpleHash(const U32 modulusSize = 64);
~SimpleHash();
void insert(T* pObject, U32 key);
T* remove(U32 key);
T* retreive(U32 key);
};
template <class T>
inline SimpleHash<T>::SimpleHash(const U32 modulusSize)
{
AssertFatal(modulusSize > 0, "Error, modulus must be > 0");
mModulus = modulusSize;
mSentryTables = new Node[mModulus];
for (U32 i = 0; i < mModulus; i++)
mSentryTables[i].next = NULL;
}
template <class T>
inline SimpleHash<T>::~SimpleHash()
{
clearTables();
}
template <class T>
inline void SimpleHash<T>::clearTables()
{
for (U32 i = 0; i < mModulus; i++) {
Node* pProbe = mSentryTables[i].next;
while (pProbe != NULL) {
Node* pNext = pProbe->next;
//delete pProbe->pObject;
delete pProbe;
pProbe = pNext;
}
}
delete [] mSentryTables;
mSentryTables = NULL;
mModulus = 0;
}
template <class T>
inline void SimpleHash<T>::insert(T* pObject, U32 key)
{
U32 insert = key % mModulus;
Node* pNew = new Node;
pNew->pObject = pObject;
pNew->key = key;
pNew->next = mSentryTables[insert].next;
mSentryTables[insert].next = pNew;
#ifdef TORQUE_DEBUG
Node* probe = pNew->next;
while (probe != NULL) {
AssertFatal(probe->key != key, "error, duplicate keys in sparse array!");
probe = probe->next;
}
#endif
}
template <class T>
inline T* SimpleHash<T>::remove(U32 key)
{
U32 remove = key % mModulus;
Node* probe = mSentryTables[remove];
while (probe->next != NULL) {
if (probe->next->key == key) {
Node* remove = probe->next;
T* pReturn = remove->pObject;
probe->next = remove->next;
delete remove;
return pReturn;
}
probe = probe->next;
}
AssertFatal(false, "Key didn't exist in the array!");
return NULL;
}
template <class T>
inline T* SimpleHash<T>::retreive(U32 key)
{
U32 retrieve = key % mModulus;
Node* probe = &mSentryTables[retrieve];
while (probe->next != NULL) {
if (probe->next->key == key) {
return probe->next->pObject;
}
probe = probe->next;
}
AssertFatal(false, "Key didn't exist in the array!");
return NULL;
}
#endif //_TSIMPLEHASH_H_