123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986 |
- // Filename:- stringed_ingame.cpp
- //
- // This file is designed to be pasted into each game project that uses the StringEd package's files.
- // You can alter the way it does things by (eg) replacing STL with RATL, BUT LEAVE THE OVERALL
- // FUNCTIONALITY THE SAME, or if I ever make any funadamental changes to the way the package works
- // then you're going to be SOOL (shit out of luck ;-)...
- //
- //////////////////////////////////////////////////
- //
- // stuff common to all qcommon files...
- #include "../server/server.h"
- #include "../game/q_shared.h"
- #include "qcommon.h"
- #include "../qcommon/fixedmap.h"
- #include "../zlib/zlib.h"
- //
- //////////////////////////////////////////////////
- #pragma warning ( disable : 4511 ) // copy constructor could not be generated
- #pragma warning ( disable : 4512 ) // assignment operator could not be generated
- #pragma warning ( disable : 4663 ) // C++ language change: blah blah template crap blah blah
- #include "stringed_ingame.h"
- #include "stringed_interface.h"
- // Needed for DWORD and XC_LANGUAGE defines:
- #include <xtl.h>
- ///////////////////////////////////////////////
- // some STL stuff...
- #include <string>
- using namespace std;
- ///////////////////////////////////////////////
- cvar_t *se_language = NULL;
- // Yeah, it's hardcoded. I don't give a shit.
- #define MAX_STRING_ENTRIES 4096
- typedef struct SE_Entry_s
- {
- string m_strString;
- } SE_Entry_t;
- //typedef map <string, SE_Entry_t> mapStringEntries_t;
- class CStringEdPackage
- {
- private:
- SE_BOOL m_bEndMarkerFound_ParseOnly;
- string m_strCurrentEntryRef_ParseOnly;
- string m_strCurrentEntryEnglish_ParseOnly;
- string m_strCurrentFileRef_ParseOnly;
- string m_strLoadingLanguage_ParseOnly; // eg "german"
- SE_BOOL m_bLoadingEnglish_ParseOnly;
- public:
- CStringEdPackage()
- {
- Z_PushNewDeleteTag( TAG_STRINGED );
- m_Strings = new VVFixedMap< char *, unsigned long >( MAX_STRING_ENTRIES );
- Z_PopNewDeleteTag();
- Clear( SE_FALSE );
- }
- ~CStringEdPackage()
- {
- Clear( SE_FALSE );
- }
- // Text entries, indexed by crc32 of reference:
- VVFixedMap< char *, unsigned long > *m_Strings;
- // mapStringEntries_t m_StringEntries; // needs to be in public space now
- void Clear( SE_BOOL bChangingLanguages );
- void SetupNewFileParse( LPCSTR psFileName );
- SE_BOOL ReadLine( LPCSTR &psParsePos, char *psDest );
- LPCSTR ParseLine( LPCSTR psLine );
- LPCSTR ExtractLanguageFromPath( LPCSTR psFileName );
- SE_BOOL EndMarkerFoundDuringParse( void )
- {
- return m_bEndMarkerFound_ParseOnly;
- }
- private:
- void AddEntry( LPCSTR psLocalReference );
- int GetNumStrings(void);
- void SetString( LPCSTR psLocalReference, LPCSTR psNewString, SE_BOOL bEnglishDebug );
- SE_BOOL SetReference( int iIndex, LPCSTR psNewString );
- LPCSTR GetCurrentFileName(void);
- LPCSTR GetCurrentReference_ParseOnly( void );
- SE_BOOL CheckLineForKeyword( LPCSTR psKeyword, LPCSTR &psLine);
- LPCSTR InsideQuotes( LPCSTR psLine );
- LPCSTR ConvertCRLiterals_Read( LPCSTR psString );
- void REMKill( char *psBuffer );
- char *Filename_PathOnly( LPCSTR psFilename );
- char *Filename_WithoutPath(LPCSTR psFilename);
- char *Filename_WithoutExt(LPCSTR psFilename);
- };
- CStringEdPackage TheStringPackage;
- void CStringEdPackage::Clear( SE_BOOL bChangingLanguages )
- {
- // m_StringEntries.clear();
- m_bEndMarkerFound_ParseOnly = SE_FALSE;
- m_strCurrentEntryRef_ParseOnly = "";
- m_strCurrentEntryEnglish_ParseOnly = "";
- //
- // the other vars are cleared in SetupNewFileParse(), and are ok to not do here.
- //
- }
- // loses anything after the path (if any), (eg) "dir/name.bmp" becomes "dir"
- // (copes with either slash-scheme for names)
- //
- // (normally I'd call another function for this, but this is supposed to be engine-independant,
- // so a certain amount of re-invention of the wheel is to be expected...)
- //
- char *CStringEdPackage::Filename_PathOnly(LPCSTR psFilename)
- {
- static char sString[ iSE_MAX_FILENAME_LENGTH ];
- strcpy(sString,psFilename);
-
- char *p1= strrchr(sString,'\\');
- char *p2= strrchr(sString,'/');
- char *p = (p1>p2)?p1:p2;
- if (p)
- *p=0;
- return sString;
- }
- // returns (eg) "dir/name" for "dir/name.bmp"
- // (copes with either slash-scheme for names)
- //
- // (normally I'd call another function for this, but this is supposed to be engine-independant,
- // so a certain amount of re-invention of the wheel is to be expected...)
- //
- char *CStringEdPackage::Filename_WithoutExt(LPCSTR psFilename)
- {
- static char sString[ iSE_MAX_FILENAME_LENGTH ];
- strcpy(sString,psFilename);
- char *p = strrchr(sString,'.');
- char *p2= strrchr(sString,'\\');
- char *p3= strrchr(sString,'/');
- // special check, make sure the first suffix we found from the end wasn't just a directory suffix (eg on a path'd filename with no extension anyway)
- //
- if (p &&
- (p2==0 || (p2 && p>p2)) &&
- (p3==0 || (p3 && p>p3))
- )
- *p=0;
- return sString;
- }
- // returns actual filename only, no path
- // (copes with either slash-scheme for names)
- //
- // (normally I'd call another function for this, but this is supposed to be engine-independant,
- // so a certain amount of re-invention of the wheel is to be expected...)
- //
- char *CStringEdPackage::Filename_WithoutPath(LPCSTR psFilename)
- {
- static char sString[ iSE_MAX_FILENAME_LENGTH ];
- LPCSTR psCopyPos = psFilename;
-
- while (*psFilename)
- {
- if (*psFilename == '/' || *psFilename == '\\')
- psCopyPos = psFilename+1;
- psFilename++;
- }
- strcpy(sString,psCopyPos);
- return sString;
- }
- LPCSTR CStringEdPackage::ExtractLanguageFromPath( LPCSTR psFileName )
- {
- return Filename_WithoutPath( Filename_PathOnly( psFileName ) );
- }
- void CStringEdPackage::SetupNewFileParse( LPCSTR psFileName )
- {
- char sString[ iSE_MAX_FILENAME_LENGTH ];
- strcpy(sString, Filename_WithoutPath( Filename_WithoutExt( psFileName ) ));
- Q_strupr(sString);
- m_strCurrentFileRef_ParseOnly = sString; // eg "OBJECTIVES"
- m_strLoadingLanguage_ParseOnly = ExtractLanguageFromPath( psFileName );
- m_bLoadingEnglish_ParseOnly = (!stricmp( m_strLoadingLanguage_ParseOnly.c_str(), "english" )) ? SE_TRUE : SE_FALSE;
- }
- // returns SE_TRUE if supplied keyword found at line start (and advances supplied ptr past any whitespace to next arg (or line end if none),
- //
- // else returns SE_FALSE...
- //
- SE_BOOL CStringEdPackage::CheckLineForKeyword( LPCSTR psKeyword, LPCSTR &psLine)
- {
- if (!Q_stricmpn(psKeyword, psLine, strlen(psKeyword)) )
- {
- psLine += strlen(psKeyword);
- // skip whitespace to arrive at next item...
- //
- while ( *psLine == '\t' || *psLine == ' ' )
- {
- psLine++;
- }
- return SE_TRUE;
- }
- return SE_FALSE;
- }
- // change "\n" to '\n' (i.e. 2-byte char-string to 1-byte ctrl-code)...
- // (or "\r\n" in editor)
- //
- LPCSTR CStringEdPackage::ConvertCRLiterals_Read( LPCSTR psString )
- {
- static string str;
- str = psString;
- int iLoc;
- while ( (iLoc = str.find("\\n")) != -1 )
- {
- str[iLoc ] = '\n';
- str.erase( iLoc+1,1 );
- }
- return str.c_str();
- }
- // kill off any "//" onwards part in the line, but NOT if it's inside a quoted string...
- //
- void CStringEdPackage::REMKill( char *psBuffer )
- {
- char *psScanPos = psBuffer;
- char *p;
- int iDoubleQuotesSoFar = 0;
- // scan forwards in case there are more than one (and the first is inside quotes)...
- //
- while ( (p=strstr(psScanPos,"//")) != NULL)
- {
- // count the number of double quotes before this point, if odd number, then we're inside quotes...
- //
- int iDoubleQuoteCount = iDoubleQuotesSoFar;
- for (int i=0; i<p-psScanPos; i++)
- {
- if (psScanPos[i] == '"')
- {
- iDoubleQuoteCount++;
- }
- }
- if (!(iDoubleQuoteCount&1))
- {
- // not inside quotes, so kill line here...
- //
- *p='\0';
- //
- // and remove any trailing whitespace...
- //
- if (psScanPos[0]) // any strlen? (else access violation with -1 below)
- {
- int iWhiteSpaceScanPos = strlen(psScanPos)-1;
- while (iWhiteSpaceScanPos>=0 && isspace(psScanPos[iWhiteSpaceScanPos]))
- {
- psScanPos[iWhiteSpaceScanPos--] = '\0';
- }
- }
- return;
- }
- else
- {
- // inside quotes (blast), oh well, skip past and keep scanning...
- //
- psScanPos = p+1;
- iDoubleQuotesSoFar = iDoubleQuoteCount;
- }
- }
- }
- // returns true while new lines available to be read...
- //
- SE_BOOL CStringEdPackage::ReadLine( LPCSTR &psParsePos, char *psDest )
- {
- if (psParsePos[0])
- {
- LPCSTR psLineEnd = strchr(psParsePos, '\n');
- if (psLineEnd)
- {
- int iCharsToCopy = (psLineEnd - psParsePos);
- strncpy(psDest, psParsePos, iCharsToCopy);
- psDest[iCharsToCopy] = '\0';
- psParsePos += iCharsToCopy;
- while (*psParsePos && strchr("\r\n",*psParsePos))
- {
- psParsePos++; // skip over CR or CR/LF pairs
- }
- }
- else
- {
- // last line...
- //
- strcpy(psDest, psParsePos);
- psParsePos += strlen(psParsePos);
- }
- // clean up the line...
- //
- if (psDest[0])
- {
- int iWhiteSpaceScanPos = strlen(psDest)-1;
- while (iWhiteSpaceScanPos>=0 && isspace(psDest[iWhiteSpaceScanPos]))
- {
- psDest[iWhiteSpaceScanPos--] = '\0';
- }
- REMKill( psDest );
- }
- return SE_TRUE;
- }
- return SE_FALSE;
- }
- // remove any outside quotes from this supplied line, plus any leading or trailing whitespace...
- //
- LPCSTR CStringEdPackage::InsideQuotes( LPCSTR psLine )
- {
- // I *could* replace this string object with a declared array, but wasn't sure how big to leave it, and it'd have to
- // be static as well, hence permanent. (problem on consoles?)
- //
- static string str;
- str = ""; // do NOT join to above line
- // skip any leading whitespace...
- //
- while (*psLine == ' ' || *psLine == '\t')
- {
- psLine++;
- }
- // skip any leading quote...
- //
- if (*psLine == '"')
- {
- psLine++;
- }
- // assign it...
- //
- str = psLine;
- if (psLine[0])
- {
- // lose any trailing whitespace...
- //
- while ( str.c_str()[ strlen(str.c_str()) -1 ] == ' ' ||
- str.c_str()[ strlen(str.c_str()) -1 ] == '\t'
- )
- {
- str.erase( strlen(str.c_str()) -1, 1);
- }
- // lose any trailing quote...
- //
- if (str.c_str()[ strlen(str.c_str()) -1 ] == '"')
- {
- str.erase( strlen(str.c_str()) -1, 1);
- }
- }
- // and return it...
- //
- return str.c_str();
- }
- // this copes with both foreigners using hi-char values (eg the french using 0x92 instead of 0x27
- // for a "'" char), as well as the fact that our buggy fontgen program writes out zeroed glyph info for
- // some fonts anyway (though not all, just as a gotcha).
- //
- // New bit, instead of static buffer (since XBox guys are desperately short of mem) I return a malloc'd buffer now,
- // so remember to free it!
- //
- static char *CopeWithDumbStringData( LPCSTR psSentence, LPCSTR psThisLanguage )
- {
- const int iBufferSize = strlen(psSentence)*3; // *3 to allow for expansion of anything even stupid string consisting entirely of elipsis chars
- char *psNewString = (char *) Z_Malloc(iBufferSize, TAG_TEMP_WORKSPACE, qfalse);
- Q_strncpyz(psNewString, psSentence, iBufferSize);
- // this is annoying, I have to just guess at which languages to do it for (ie NOT ASIAN/MBCS!!!) since the
- // string system was deliberately (and correctly) designed to not know or care whether it was doing SBCS
- // or MBCS languages, because it was never envisioned that I'd have to clean up other people's mess.
- //
- // Ok, bollocks to it, this will have to do. Any other languages that come later and have bugs in their text can
- // get fixed by them typing it in properly in the first place...
- //
- if (!stricmp(psThisLanguage,"ENGLISH") ||
- !stricmp(psThisLanguage,"FRENCH") ||
- !stricmp(psThisLanguage,"GERMAN") ||
- !stricmp(psThisLanguage,"ITALIAN") ||
- !stricmp(psThisLanguage,"SPANISH") ||
- !stricmp(psThisLanguage,"POLISH") ||
- !stricmp(psThisLanguage,"RUSSIAN")
- )
- {
- char *p;
- // strXLS_Speech.Replace(va("%c",0x92),va("%c",0x27)); // "'"
- while ((p=strchr(psNewString,0x92))!=NULL) // "rich" (and illegal) apostrophe
- {
- *p = 0x27;
- }
- // strXLS_Speech.Replace(va("%c",0x93),"\""); // smart quotes -> '"'
- while ((p=strchr(psNewString,0x93))!=NULL)
- {
- *p = '"';
- }
- // strXLS_Speech.Replace(va("%c",0x94),"\""); // smart quotes -> '"'
- while ((p=strchr(psNewString,0x94))!=NULL)
- {
- *p = '"';
- }
- // strXLS_Speech.Replace(va("%c",0x0B),"."); // full stop
- while ((p=strchr(psNewString,0x0B))!=NULL)
- {
- *p = '.';
- }
- // strXLS_Speech.Replace(va("%c",0x85),"..."); // "..."-char -> 3-char "..."
- while ((p=strchr(psNewString,0x85))!=NULL) // "rich" (and illegal) apostrophe
- {
- memmove(p+2,p,strlen(p));
- *p++ = '.';
- *p++ = '.';
- *p = '.';
- }
- // strXLS_Speech.Replace(va("%c",0x91),va("%c",0x27)); // "'"
- while ((p=strchr(psNewString,0x91))!=NULL)
- {
- *p = 0x27;
- }
- // strXLS_Speech.Replace(va("%c",0x96),va("%c",0x2D)); // "-"
- while ((p=strchr(psNewString,0x96))!=NULL)
- {
- *p = 0x2D;
- }
- // strXLS_Speech.Replace(va("%c",0x97),va("%c",0x2D)); // "-"
- while ((p=strchr(psNewString,0x97))!=NULL)
- {
- *p = 0x2D;
- }
- // bug fix for picky grammatical errors, replace "?." with "? "
- //
- while ((p=strstr(psNewString,"?."))!=NULL)
- {
- p[1] = ' ';
- }
- // StripEd and our print code don't support tabs...
- //
- while ((p=strchr(psNewString,0x09))!=NULL)
- {
- *p = ' ';
- }
- }
- return psNewString;
- }
- // return is either NULL for good else error message to display...
- //
- LPCSTR CStringEdPackage::ParseLine( LPCSTR psLine )
- {
- LPCSTR psErrorMessage = NULL;
- if (psLine)
- {
- if (CheckLineForKeyword( sSE_KEYWORD_VERSION, psLine ))
- {
- // VERSION "1"
- //
- LPCSTR psVersionNumber = InsideQuotes( psLine );
- int iVersionNumber = atoi( psVersionNumber );
-
- if (iVersionNumber != iSE_VERSION)
- {
- psErrorMessage = va("Unexpected version number %d, expecting %d!\n", iVersionNumber, iSE_VERSION);
- }
- }
- else
- if ( CheckLineForKeyword(sSE_KEYWORD_CONFIG, psLine)
- || CheckLineForKeyword(sSE_KEYWORD_FILENOTES, psLine)
- || CheckLineForKeyword(sSE_KEYWORD_NOTES, psLine)
- )
- {
- // not used ingame, but need to absorb the token
- }
- else
- if (CheckLineForKeyword(sSE_KEYWORD_REFERENCE, psLine))
- {
- // REFERENCE GUARD_GOOD_TO_SEE_YOU
- //
- LPCSTR psLocalReference = InsideQuotes( psLine );
- AddEntry( psLocalReference );
- }
- else
- if (CheckLineForKeyword(sSE_KEYWORD_ENDMARKER, psLine))
- {
- // ENDMARKER
- //
- m_bEndMarkerFound_ParseOnly = SE_TRUE; // the only major error checking I bother to do (for file truncation)
- }
- else
- if (!Q_stricmpn(sSE_KEYWORD_LANG, psLine, strlen(sSE_KEYWORD_LANG)))
- {
- // LANG_ENGLISH "GUARD: Good to see you, sir. Taylor is waiting for you in the clean tent. We need to get you suited up. "
- //
- LPCSTR psReference = GetCurrentReference_ParseOnly();
- if ( psReference[0] )
- {
- psLine += strlen(sSE_KEYWORD_LANG);
- // what language is this?...
- //
- LPCSTR psWordEnd = psLine;
- while (*psWordEnd && *psWordEnd != ' ' && *psWordEnd != '\t')
- {
- psWordEnd++;
- }
- char sThisLanguage[1024]={0};
- int iCharsToCopy = psWordEnd - psLine;
- if (iCharsToCopy > sizeof(sThisLanguage)-1)
- {
- iCharsToCopy = sizeof(sThisLanguage)-1;
- }
- strncpy(sThisLanguage, psLine, iCharsToCopy); // already declared as {0} so no need to zero-cap dest buffer
- psLine += strlen(sThisLanguage);
- LPCSTR _psSentence = ConvertCRLiterals_Read( InsideQuotes( psLine ) );
- // Dammit, I hate having to do crap like this just because other people mess up and put
- // stupid data in their text, so I have to cope with it.
- //
- // note hackery with _psSentence and psSentence because of const-ness. bleurgh. Just don't ask.
- //
- char *psSentence = CopeWithDumbStringData( _psSentence, sThisLanguage );
- if ( m_bLoadingEnglish_ParseOnly )
- {
- // if loading just "english", then go ahead and store it...
- //
- SetString( psReference, psSentence, SE_FALSE );
- }
- else
- {
- // if loading a foreign language...
- //
- SE_BOOL bSentenceIsEnglish = (!stricmp(sThisLanguage,"english")) ? SE_TRUE: SE_FALSE; // see whether this is the english master or not
- // this check can be omitted, I'm just being extra careful here...
- //
- if ( !bSentenceIsEnglish )
- {
- // basically this is just checking that an .STE file override is the same language as the .STR...
- //
- if (stricmp( m_strLoadingLanguage_ParseOnly.c_str(), sThisLanguage ))
- {
- psErrorMessage = va("Language \"%s\" found when expecting \"%s\"!\n", sThisLanguage, m_strLoadingLanguage_ParseOnly.c_str());
- }
- }
- if (!psErrorMessage)
- {
- SetString( psReference, psSentence, bSentenceIsEnglish );
- }
- }
- Z_Free( psSentence );
- }
- else
- {
- psErrorMessage = "Error parsing file: Unexpected \"" sSE_KEYWORD_LANG "\"\n";
- }
- }
- else
- {
- psErrorMessage = va("Unknown keyword at linestart: \"%s\"\n", psLine);
- }
- }
- return psErrorMessage;
- }
-
- // returns reference of string being parsed, else "" for none.
- //
- LPCSTR CStringEdPackage::GetCurrentReference_ParseOnly( void )
- {
- return m_strCurrentEntryRef_ParseOnly.c_str();
- }
-
- // add new string entry (during parse)
- //
- void CStringEdPackage::AddEntry( LPCSTR psLocalReference )
- {
- // the reason I don't just assign it anyway is because the optional .STE override files don't contain flags,
- // and therefore would wipe out the parsed flags of the .STR file...
- //
- /*
- mapStringEntries_t::iterator itEntry = m_StringEntries.find( va("%s_%s",m_strCurrentFileRef_ParseOnly.c_str(), psLocalReference) );
- if (itEntry == m_StringEntries.end())
- {
- SE_Entry_t SE_Entry;
- m_StringEntries[ va("%s_%s", m_strCurrentFileRef_ParseOnly.c_str(), psLocalReference) ] = SE_Entry;
- }
- */
- m_strCurrentEntryRef_ParseOnly = psLocalReference;
- }
- void CStringEdPackage::SetString( LPCSTR psLocalReference, LPCSTR psNewString, SE_BOOL bEnglishDebug )
- {
- const char *ref = va("%s_%s", m_strCurrentFileRef_ParseOnly.c_str(), psLocalReference);
- unsigned long refCrc = crc32( 0, (const Bytef *)ref, strlen(ref) );
- if ( bEnglishDebug )
- {
- // This is the leading english text of a foreign sentence pair (so it's the debug-key text):
- // don't store, just make a note in-case #same shows up:
- m_strCurrentEntryEnglish_ParseOnly = psNewString;
- }
- else if ( m_bLoadingEnglish_ParseOnly )
- {
- // It's the english text, and we're loading english. Add it!
- int len = strlen( psNewString );
- char *strData = (char *) Z_Malloc( len + 1, TAG_STRINGED, qfalse );
- strcpy( strData, psNewString );
- m_Strings->Insert( strData, refCrc );
- }
- else
- {
- // It's foreign text, we're going to add it, but we need to check for #same
- if (!stricmp(psNewString, sSE_EXPORT_SAME))
- {
- // If it's #same, then copy the stored english version:
- int len = m_strCurrentEntryEnglish_ParseOnly.length();
- char *strData = (char *) Z_Malloc( len + 1, TAG_STRINGED, qfalse );
- strcpy( strData, m_strCurrentEntryEnglish_ParseOnly.c_str() );
- m_Strings->Insert( strData, refCrc );
- }
- else
- {
- // Explicit foreign text. Add it!
- int len = strlen( psNewString );
- char *strData = (char *) Z_Malloc( len + 1, TAG_STRINGED, qfalse );
- strcpy( strData, psNewString );
- m_Strings->Insert( strData, refCrc );
- }
- }
- }
- // filename is local here, eg: "strings/german/obj.str"
- //
- // return is either NULL for good else error message to display...
- //
- static LPCSTR SE_Load_Actual( LPCSTR psFileName, SE_BOOL bSpeculativeLoad )
- {
- LPCSTR psErrorMessage = NULL;
-
- unsigned char *psLoadedData = SE_LoadFileData( psFileName );
- if ( psLoadedData )
- {
- // now parse the data...
- //
- char *psParsePos = (char *) psLoadedData;
- TheStringPackage.SetupNewFileParse( psFileName );
- char sLineBuffer[16384]; // should be enough for one line of text (some of them can be BIG though)
- while ( !psErrorMessage && TheStringPackage.ReadLine((LPCSTR &) psParsePos, sLineBuffer ) )
- {
- if (strlen(sLineBuffer))
- {
- psErrorMessage = TheStringPackage.ParseLine( sLineBuffer );
- }
- }
- SE_FreeFileDataAfterLoad( psLoadedData);
-
- if (!psErrorMessage && !TheStringPackage.EndMarkerFoundDuringParse())
- {
- psErrorMessage = va("Truncated file, failed to find \"%s\" at file end!", sSE_KEYWORD_ENDMARKER);
- }
- }
- else
- {
- if ( bSpeculativeLoad )
- {
- // then it's ok to not find the file, so do nothing...
- }
- else
- {
- psErrorMessage = va("Unable to load \"%s\"!", psFileName);
- }
- }
- return psErrorMessage;
- }
- static LPCSTR SE_GetFoundFile( string &strResult )
- {
- static char sTemp[1024/*MAX_PATH*/];
- if (!strlen(strResult.c_str()))
- return NULL;
-
- strncpy(sTemp,strResult.c_str(),sizeof(sTemp)-1);
- sTemp[sizeof(sTemp)-1]='\0';
- char *psSemiColon = strchr(sTemp,';');
- if ( psSemiColon)
- {
- *psSemiColon = '\0';
- strResult.erase(0,(psSemiColon-sTemp)+1);
- }
- else
- {
- // no semicolon found, probably last entry? (though i think even those have them on, oh well)
- //
- strResult.erase();
- }
- // strlwr(sTemp); // just for consistancy and set<> -> set<> erasure checking etc
- return sTemp;
- }
- //////////// API entry points from rest of game.... //////////////////////////////
- // filename is local here, eg: "strings/german/obj.str"
- //
- // return is either NULL for good else error message to display...
- //
- LPCSTR SE_Load( LPCSTR psFileName, SE_BOOL bFailIsCritical = SE_TRUE )
- {
- ////////////////////////////////////////////////////
- //
- // ingame here tends to pass in names without paths, but I expect them when doing a language load, so...
- //
- char sTemp[1000]={0};
- if (!strchr(psFileName,'/'))
- {
- strcpy(sTemp,sSE_STRINGS_DIR);
- strcat(sTemp,"/");
- if (se_language)
- {
- strcat(sTemp,se_language->string);
- strcat(sTemp,"/");
- }
- }
- strcat(sTemp,psFileName);
- COM_DefaultExtension( sTemp, sizeof(sTemp), sSE_INGAME_FILE_EXTENSION);
- psFileName = &sTemp[0];
- //
- ////////////////////////////////////////////////////
- LPCSTR psErrorMessage = SE_Load_Actual( psFileName, SE_FALSE );
- // check for any corresponding / overriding .STE files and load them afterwards...
- //
- if ( !psErrorMessage )
- {
- char sFileName[ iSE_MAX_FILENAME_LENGTH ];
- strncpy( sFileName, psFileName, sizeof(sFileName)-1 );
- sFileName[ sizeof(sFileName)-1 ] = '\0';
- char *p = strrchr( sFileName, '.' );
- if (p && strlen(p) == strlen(sSE_EXPORT_FILE_EXTENSION))
- {
- strcpy( p, sSE_EXPORT_FILE_EXTENSION );
-
- psErrorMessage = SE_Load_Actual( sFileName, SE_TRUE );
- }
- }
- if (psErrorMessage)
- {
- if (bFailIsCritical)
- {
- // TheStringPackage.Clear(TRUE); // Will we want to do this? Any errors that arise should be fixed immediately
- Com_Error( ERR_DROP, "SE_Load(): Couldn't load \"%s\"!\n\nError: \"%s\"\n", psFileName, psErrorMessage );
- }
- else
- {
- Com_DPrintf(S_COLOR_YELLOW "SE_Load(): Couldn't load \"%s\"!\n", psFileName );
- }
- }
- return psErrorMessage;
- }
- // convenience-function for the main GetString call...
- //
- LPCSTR SE_GetString( LPCSTR psPackageReference, LPCSTR psStringReference)
- {
- char sReference[256]; // will always be enough, I've never seen one more than about 30 chars long
- sprintf(sReference,"%s_%s", psPackageReference, psStringReference);
- return SE_GetString( Q_strupr(sReference) );
- }
- LPCSTR SE_GetString( LPCSTR psPackageAndStringReference )
- {
- int len = strlen( psPackageAndStringReference );
- char sReference[256]; // will always be enough, I've never seen one more than about 30 chars long
- assert( len < sizeof(sReference) );
- Q_strncpyz( sReference, psPackageAndStringReference, sizeof(sReference) );
- Q_strupr( sReference );
- unsigned long refCrc = crc32( 0, (const Bytef *)sReference, len );
- char **strData = TheStringPackage.m_Strings->Find( refCrc );
- if( !strData )
- return "";
- else
- return *strData;
- }
- void SE_NewLanguage(void)
- {
- TheStringPackage.Clear( SE_TRUE );
- }
- // these two functions aren't needed other than to make Quake-type games happy and/or stop memory managers
- // complaining about leaks if they report them before the global StringEd package object calls it's own dtor.
- //
- // but here they are for completeness's sake I guess...
- //
- void SE_Init(void)
- {
- Z_PushNewDeleteTag( TAG_STRINGED );
- TheStringPackage.Clear( SE_FALSE );
- // se_language = Cvar_Get("se_language", "english", CVAR_ARCHIVE | CVAR_NORESTART);
- extern DWORD g_dwLanguage;
- switch( g_dwLanguage )
- {
- case XC_LANGUAGE_FRENCH:
- se_language = Cvar_Get("se_language", "french", CVAR_NORESTART);
- break;
- case XC_LANGUAGE_GERMAN:
- se_language = Cvar_Get("se_language", "german", CVAR_NORESTART);
- break;
- case XC_LANGUAGE_ENGLISH:
- default:
- se_language = Cvar_Get("se_language", "english", CVAR_NORESTART);
- break;
- }
- // Rather than calling SE_LoadLanguage directly, do this. Otherwise,
- // se_langauge->modified doesn't get cleared, and we parse the string files
- // twice. Gah.
- SE_CheckForLanguageUpdates();
- Z_PopNewDeleteTag();
- }
- // returns error message else NULL for ok.
- //
- // Any errors that result from this should probably be treated as game-fatal, since an asset file is fuxored.
- //
- LPCSTR SE_LoadLanguage( LPCSTR psLanguage )
- {
- LPCSTR psErrorMessage = NULL;
- if (psLanguage && psLanguage[0])
- {
- SE_NewLanguage();
- string strResults;
- /*int iFilesFound = */SE_BuildFileList(
- #ifdef _STRINGED
- va("C:\\Source\\Tools\\StringEd\\test_data\\%s",sSE_STRINGS_DIR)
- #else
- sSE_STRINGS_DIR
- #endif
- , strResults
- );
- LPCSTR p;
- while ( (p=SE_GetFoundFile (strResults)) != NULL && !psErrorMessage )
- {
- LPCSTR psThisLang = TheStringPackage.ExtractLanguageFromPath( p );
- if ( !stricmp( psLanguage, psThisLang ) )
- {
- psErrorMessage = SE_Load( p );
- }
- }
- }
- else
- {
- assert( 0 && "SE_LoadLanguage(): Bad language name!" );
- }
- return psErrorMessage;
- }
- // called in Com_Frame, so don't take up any time! (can also be called during dedicated)
- //
- // instead of re-loading just the files we've already loaded I'm going to load the whole language (simpler)
- //
- void SE_CheckForLanguageUpdates(void)
- {
- if (se_language && se_language->modified)
- {
- LPCSTR psErrorMessage = SE_LoadLanguage( se_language->string );
- if ( psErrorMessage )
- {
- Com_Error( ERR_DROP, psErrorMessage );
- }
- TheStringPackage.m_Strings->Sort();
- se_language->modified = SE_FALSE;
- }
- }
- ///////////////////////// eof //////////////////////////
|