mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-26 22:31:17 +00:00
2dc2d3adb5
In idDeclManagerLocal::CreateNewDecl() decl->self->DefaultDefinition(); crashed because self was uninitialized. Now it gets set to NULL in the constructor and initialized to something sensible in decl->AllocateSelf(); this is part of #378
2245 lines
63 KiB
C++
2245 lines
63 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 GPL Source Code
|
|
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
|
|
|
|
Doom 3 Source Code 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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "sys/platform.h"
|
|
#include "idlib/containers/List.h"
|
|
#include "idlib/containers/HashIndex.h"
|
|
#include "idlib/hashing/MD5.h"
|
|
#include "idlib/BitMsg.h"
|
|
#include "framework/FileSystem.h"
|
|
#include "framework/CVarSystem.h"
|
|
#include "framework/DeclAF.h"
|
|
#include "framework/DeclEntityDef.h"
|
|
#include "framework/DeclFX.h"
|
|
#include "framework/DeclPDA.h"
|
|
#include "framework/DeclParticle.h"
|
|
#include "framework/DeclSkin.h"
|
|
#include "framework/DeclTable.h"
|
|
#include "renderer/Material.h"
|
|
#include "sound/sound.h"
|
|
|
|
#include "framework/DeclManager.h"
|
|
|
|
/*
|
|
|
|
GUIs and script remain separately parsed
|
|
|
|
Following a parse, all referenced media (and other decls) will have been touched.
|
|
|
|
sinTable and cosTable are required for the rotate material keyword to function
|
|
|
|
A new FindType on a purged decl will cause it to be reloaded, but a stale pointer to a purged
|
|
decl will look like a defaulted decl.
|
|
|
|
Moving a decl from one file to another will not be handled correctly by a reload, the material
|
|
will be defaulted.
|
|
|
|
NULL or empty decl names will always return NULL
|
|
Should probably make a default decl for this
|
|
|
|
Decls are initially created without a textSource
|
|
A parse without textSource set should always just call MakeDefault()
|
|
A parse that has an error should internally call MakeDefault()
|
|
A purge does nothing to a defaulted decl
|
|
|
|
Should we have a "purged" media state separate from the "defaulted" media state?
|
|
|
|
reloading over a decl name that was defaulted
|
|
|
|
reloading over a decl name that was valid
|
|
|
|
missing reload over a previously explicit definition
|
|
|
|
*/
|
|
|
|
#define USE_COMPRESSED_DECLS
|
|
//#define GET_HUFFMAN_FREQUENCIES
|
|
|
|
class idDeclType {
|
|
public:
|
|
idStr typeName;
|
|
declType_t type;
|
|
idDecl * (*allocator)( void );
|
|
};
|
|
|
|
class idDeclFolder {
|
|
public:
|
|
idStr folder;
|
|
idStr extension;
|
|
declType_t defaultType;
|
|
};
|
|
|
|
class idDeclFile;
|
|
|
|
class idDeclLocal : public idDeclBase {
|
|
friend class idDeclFile;
|
|
friend class idDeclManagerLocal;
|
|
|
|
public:
|
|
idDeclLocal();
|
|
virtual ~idDeclLocal() {};
|
|
virtual const char * GetName( void ) const;
|
|
virtual declType_t GetType( void ) const;
|
|
virtual declState_t GetState( void ) const;
|
|
virtual bool IsImplicit( void ) const;
|
|
virtual bool IsValid( void ) const;
|
|
virtual void Invalidate( void );
|
|
virtual void Reload( void );
|
|
virtual void EnsureNotPurged( void );
|
|
virtual int Index( void ) const;
|
|
virtual int GetLineNum( void ) const;
|
|
virtual const char * GetFileName( void ) const;
|
|
virtual size_t Size( void ) const;
|
|
virtual void GetText( char *text ) const;
|
|
virtual int GetTextLength( void ) const;
|
|
virtual void SetText( const char *text );
|
|
virtual bool ReplaceSourceFileText( void );
|
|
virtual bool SourceFileChanged( void ) const;
|
|
virtual void MakeDefault( void );
|
|
virtual bool EverReferenced( void ) const;
|
|
|
|
protected:
|
|
virtual bool SetDefaultText( void );
|
|
virtual const char * DefaultDefinition( void ) const;
|
|
virtual bool Parse( const char *text, const int textLength );
|
|
virtual void FreeData( void );
|
|
virtual void List( void ) const;
|
|
virtual void Print( void ) const;
|
|
|
|
protected:
|
|
void AllocateSelf( void );
|
|
|
|
// Parses the decl definition.
|
|
// After calling parse, a decl will be guaranteed usable.
|
|
void ParseLocal( void );
|
|
|
|
// Does a MakeDefualt, but flags the decl so that it
|
|
// will Parse() the next time the decl is found.
|
|
void Purge( void );
|
|
|
|
// Set textSource possible with compression.
|
|
void SetTextLocal( const char *text, const int length );
|
|
|
|
private:
|
|
idDecl * self;
|
|
|
|
idStr name; // name of the decl
|
|
char * textSource; // decl text definition
|
|
int textLength; // length of textSource
|
|
int compressedLength; // compressed length
|
|
idDeclFile * sourceFile; // source file in which the decl was defined
|
|
int sourceTextOffset; // offset in source file to decl text
|
|
int sourceTextLength; // length of decl text in source file
|
|
int sourceLine; // this is where the actual declaration token starts
|
|
int checksum; // checksum of the decl text
|
|
declType_t type; // decl type
|
|
declState_t declState; // decl state
|
|
int index; // index in the per-type list
|
|
|
|
bool parsedOutsideLevelLoad; // these decls will never be purged
|
|
bool everReferenced; // set to true if the decl was ever used
|
|
bool referencedThisLevel; // set to true when the decl is used for the current level
|
|
bool redefinedInReload; // used during file reloading to make sure a decl that has
|
|
// its source removed will be defaulted
|
|
idDeclLocal * nextInFile; // next decl in the decl file
|
|
};
|
|
|
|
class idDeclFile {
|
|
public:
|
|
idDeclFile();
|
|
idDeclFile( const char *fileName, declType_t defaultType );
|
|
|
|
void Reload( bool force );
|
|
int LoadAndParse();
|
|
|
|
public:
|
|
idStr fileName;
|
|
declType_t defaultType;
|
|
|
|
ID_TIME_T timestamp;
|
|
int checksum;
|
|
int fileSize;
|
|
int numLines;
|
|
|
|
idDeclLocal * decls;
|
|
};
|
|
|
|
class idDeclManagerLocal : public idDeclManager {
|
|
friend class idDeclLocal;
|
|
|
|
public:
|
|
virtual void Init( void );
|
|
virtual void Shutdown( void );
|
|
virtual void Reload( bool force );
|
|
virtual void BeginLevelLoad();
|
|
virtual void EndLevelLoad();
|
|
virtual void RegisterDeclType( const char *typeName, declType_t type, idDecl *(*allocator)( void ) );
|
|
virtual void RegisterDeclFolder( const char *folder, const char *extension, declType_t defaultType );
|
|
virtual int GetChecksum( void ) const;
|
|
virtual int GetNumDeclTypes( void ) const;
|
|
virtual int GetNumDecls( declType_t type );
|
|
virtual const char * GetDeclNameFromType( declType_t type ) const;
|
|
virtual declType_t GetDeclTypeFromName( const char *typeName ) const;
|
|
virtual const idDecl * FindType( declType_t type, const char *name, bool makeDefault = true );
|
|
virtual const idDecl * DeclByIndex( declType_t type, int index, bool forceParse = true );
|
|
|
|
virtual const idDecl* FindDeclWithoutParsing( declType_t type, const char *name, bool makeDefault = true );
|
|
virtual void ReloadFile( const char* filename, bool force );
|
|
|
|
virtual void ListType( const idCmdArgs &args, declType_t type );
|
|
virtual void PrintType( const idCmdArgs &args, declType_t type );
|
|
|
|
virtual idDecl * CreateNewDecl( declType_t type, const char *name, const char *fileName );
|
|
|
|
//BSM Added for the material editors rename capabilities
|
|
virtual bool RenameDecl( declType_t type, const char* oldName, const char* newName );
|
|
|
|
virtual void MediaPrint( const char *fmt, ... ) id_attribute((format(printf,2,3)));
|
|
virtual void WritePrecacheCommands( idFile *f );
|
|
|
|
virtual const idMaterial * FindMaterial( const char *name, bool makeDefault = true );
|
|
virtual const idDeclSkin * FindSkin( const char *name, bool makeDefault = true );
|
|
virtual const idSoundShader * FindSound( const char *name, bool makeDefault = true );
|
|
|
|
virtual const idMaterial * MaterialByIndex( int index, bool forceParse = true );
|
|
virtual const idDeclSkin * SkinByIndex( int index, bool forceParse = true );
|
|
virtual const idSoundShader * SoundByIndex( int index, bool forceParse = true );
|
|
|
|
public:
|
|
static void MakeNameCanonical( const char *name, char *result, int maxLength );
|
|
idDeclLocal * FindTypeWithoutParsing( declType_t type, const char *name, bool makeDefault = true );
|
|
|
|
idDeclType * GetDeclType( int type ) const { return declTypes[type]; }
|
|
const idDeclFile * GetImplicitDeclFile( void ) const { return &implicitDecls; }
|
|
|
|
private:
|
|
idList<idDeclType *> declTypes;
|
|
idList<idDeclFolder *> declFolders;
|
|
|
|
idList<idDeclFile *> loadedFiles;
|
|
idHashIndex hashTables[DECL_MAX_TYPES];
|
|
idList<idDeclLocal *> linearLists[DECL_MAX_TYPES];
|
|
idDeclFile implicitDecls; // this holds all the decls that were created because explicit
|
|
// text definitions were not found. Decls that became default
|
|
// because of a parse error are not in this list.
|
|
int checksum; // checksum of all loaded decl text
|
|
int indent; // for MediaPrint
|
|
bool insideLevelLoad;
|
|
|
|
static idCVar decl_show;
|
|
|
|
private:
|
|
static void ListDecls_f( const idCmdArgs &args );
|
|
static void ReloadDecls_f( const idCmdArgs &args );
|
|
static void TouchDecl_f( const idCmdArgs &args );
|
|
};
|
|
|
|
idCVar idDeclManagerLocal::decl_show( "decl_show", "0", CVAR_SYSTEM, "set to 1 to print parses, 2 to also print references", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
|
|
|
|
idDeclManagerLocal declManagerLocal;
|
|
idDeclManager * declManager = &declManagerLocal;
|
|
|
|
/*
|
|
====================================================================================
|
|
|
|
decl text huffman compression
|
|
|
|
====================================================================================
|
|
*/
|
|
|
|
const int MAX_HUFFMAN_SYMBOLS = 256;
|
|
|
|
typedef struct huffmanNode_s {
|
|
int symbol;
|
|
int frequency;
|
|
struct huffmanNode_s * next;
|
|
struct huffmanNode_s * children[2];
|
|
} huffmanNode_t;
|
|
|
|
typedef struct huffmanCode_s {
|
|
unsigned long bits[8];
|
|
int numBits;
|
|
} huffmanCode_t;
|
|
|
|
// compression ratio = 64%
|
|
static int huffmanFrequencies[] = {
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00078fb6, 0x000352a7, 0x00000002, 0x00000001, 0x0002795e, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00049600, 0x000000dd, 0x00018732, 0x0000005a, 0x00000007, 0x00000092, 0x0000000a, 0x00000919,
|
|
0x00002dcf, 0x00002dda, 0x00004dfc, 0x0000039a, 0x000058be, 0x00002d13, 0x00014d8c, 0x00023c60,
|
|
0x0002ddb0, 0x0000d1fc, 0x000078c4, 0x00003ec7, 0x00003113, 0x00006b59, 0x00002499, 0x0000184a,
|
|
0x0000250b, 0x00004e38, 0x000001ca, 0x00000011, 0x00000020, 0x000023da, 0x00000012, 0x00000091,
|
|
0x0000000b, 0x00000b14, 0x0000035d, 0x0000137e, 0x000020c9, 0x00000e11, 0x000004b4, 0x00000737,
|
|
0x000006b8, 0x00001110, 0x000006b3, 0x000000fe, 0x00000f02, 0x00000d73, 0x000005f6, 0x00000be4,
|
|
0x00000d86, 0x0000014d, 0x00000d89, 0x0000129b, 0x00000db3, 0x0000015a, 0x00000167, 0x00000375,
|
|
0x00000028, 0x00000112, 0x00000018, 0x00000678, 0x0000081a, 0x00000677, 0x00000003, 0x00018112,
|
|
0x00000001, 0x000441ee, 0x000124b0, 0x0001fa3f, 0x00026125, 0x0005a411, 0x0000e50f, 0x00011820,
|
|
0x00010f13, 0x0002e723, 0x00003518, 0x00005738, 0x0002cc26, 0x0002a9b7, 0x0002db81, 0x0003b5fa,
|
|
0x000185d2, 0x00001299, 0x00030773, 0x0003920d, 0x000411cd, 0x00018751, 0x00005fbd, 0x000099b0,
|
|
0x00009242, 0x00007cf2, 0x00002809, 0x00005a1d, 0x00000001, 0x00005a1d, 0x00000001, 0x00000001,
|
|
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
|
|
};
|
|
|
|
static huffmanCode_t huffmanCodes[MAX_HUFFMAN_SYMBOLS];
|
|
static huffmanNode_t *huffmanTree = NULL;
|
|
static int totalUncompressedLength = 0;
|
|
static int totalCompressedLength = 0;
|
|
static int maxHuffmanBits = 0;
|
|
|
|
|
|
/*
|
|
================
|
|
ClearHuffmanFrequencies
|
|
================
|
|
*/
|
|
void ClearHuffmanFrequencies( void ) {
|
|
int i;
|
|
|
|
for( i = 0; i < MAX_HUFFMAN_SYMBOLS; i++ ) {
|
|
huffmanFrequencies[i] = 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
InsertHuffmanNode
|
|
================
|
|
*/
|
|
huffmanNode_t *InsertHuffmanNode( huffmanNode_t *firstNode, huffmanNode_t *node ) {
|
|
huffmanNode_t *n, *lastNode;
|
|
|
|
lastNode = NULL;
|
|
for ( n = firstNode; n; n = n->next ) {
|
|
if ( node->frequency <= n->frequency ) {
|
|
break;
|
|
}
|
|
lastNode = n;
|
|
}
|
|
if ( lastNode ) {
|
|
node->next = lastNode->next;
|
|
lastNode->next = node;
|
|
} else {
|
|
node->next = firstNode;
|
|
firstNode = node;
|
|
}
|
|
return firstNode;
|
|
}
|
|
|
|
/*
|
|
================
|
|
BuildHuffmanCode_r
|
|
================
|
|
*/
|
|
void BuildHuffmanCode_r( huffmanNode_t *node, huffmanCode_t code, huffmanCode_t codes[MAX_HUFFMAN_SYMBOLS] ) {
|
|
if ( node->symbol == -1 ) {
|
|
huffmanCode_t newCode = code;
|
|
assert( code.numBits < sizeof( codes[0].bits ) * 8 );
|
|
newCode.numBits++;
|
|
if ( code.numBits > maxHuffmanBits ) {
|
|
maxHuffmanBits = newCode.numBits;
|
|
}
|
|
BuildHuffmanCode_r( node->children[0], newCode, codes );
|
|
newCode.bits[code.numBits >> 5] |= 1 << ( code.numBits & 31 );
|
|
BuildHuffmanCode_r( node->children[1], newCode, codes );
|
|
} else {
|
|
assert( code.numBits <= sizeof( codes[0].bits ) * 8 );
|
|
codes[node->symbol] = code;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
FreeHuffmanTree_r
|
|
================
|
|
*/
|
|
void FreeHuffmanTree_r( huffmanNode_t *node ) {
|
|
if ( node->symbol == -1 ) {
|
|
FreeHuffmanTree_r( node->children[0] );
|
|
FreeHuffmanTree_r( node->children[1] );
|
|
}
|
|
delete node;
|
|
}
|
|
|
|
/*
|
|
================
|
|
HuffmanHeight_r
|
|
================
|
|
*/
|
|
int HuffmanHeight_r( huffmanNode_t *node ) {
|
|
if ( node == NULL ) {
|
|
return -1;
|
|
}
|
|
int left = HuffmanHeight_r( node->children[0] );
|
|
int right = HuffmanHeight_r( node->children[1] );
|
|
if ( left > right ) {
|
|
return left + 1;
|
|
}
|
|
return right + 1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
SetupHuffman
|
|
================
|
|
*/
|
|
void SetupHuffman( void ) {
|
|
int i, height id_attribute((unused));
|
|
huffmanNode_t *firstNode, *node;
|
|
huffmanCode_t code;
|
|
|
|
firstNode = NULL;
|
|
for( i = 0; i < MAX_HUFFMAN_SYMBOLS; i++ ) {
|
|
node = new huffmanNode_t;
|
|
node->symbol = i;
|
|
node->frequency = huffmanFrequencies[i];
|
|
node->next = NULL;
|
|
node->children[0] = NULL;
|
|
node->children[1] = NULL;
|
|
firstNode = InsertHuffmanNode( firstNode, node );
|
|
}
|
|
|
|
for( i = 1; i < MAX_HUFFMAN_SYMBOLS; i++ ) {
|
|
node = new huffmanNode_t;
|
|
node->symbol = -1;
|
|
node->frequency = firstNode->frequency + firstNode->next->frequency;
|
|
node->next = NULL;
|
|
node->children[0] = firstNode;
|
|
node->children[1] = firstNode->next;
|
|
firstNode = InsertHuffmanNode( firstNode->next->next, node );
|
|
}
|
|
|
|
maxHuffmanBits = 0;
|
|
memset( &code, 0, sizeof( code ) );
|
|
BuildHuffmanCode_r( firstNode, code, huffmanCodes );
|
|
|
|
huffmanTree = firstNode;
|
|
|
|
height = HuffmanHeight_r( firstNode );
|
|
assert( maxHuffmanBits == height );
|
|
}
|
|
|
|
/*
|
|
================
|
|
ShutdownHuffman
|
|
================
|
|
*/
|
|
void ShutdownHuffman( void ) {
|
|
if ( huffmanTree ) {
|
|
FreeHuffmanTree_r( huffmanTree );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
HuffmanCompressText
|
|
================
|
|
*/
|
|
int HuffmanCompressText( const char *text, int textLength, byte *compressed, int maxCompressedSize ) {
|
|
int i, j;
|
|
idBitMsg msg;
|
|
|
|
totalUncompressedLength += textLength;
|
|
|
|
msg.Init( compressed, maxCompressedSize );
|
|
msg.BeginWriting();
|
|
for ( i = 0; i < textLength; i++ ) {
|
|
const huffmanCode_t &code = huffmanCodes[(unsigned char)text[i]];
|
|
for ( j = 0; j < ( code.numBits >> 5 ); j++ ) {
|
|
msg.WriteBits( code.bits[j], 32 );
|
|
}
|
|
if ( code.numBits & 31 ) {
|
|
msg.WriteBits( code.bits[j], code.numBits & 31 );
|
|
}
|
|
}
|
|
|
|
totalCompressedLength += msg.GetSize();
|
|
|
|
return msg.GetSize();
|
|
}
|
|
|
|
/*
|
|
================
|
|
HuffmanDecompressText
|
|
================
|
|
*/
|
|
int HuffmanDecompressText( char *text, int textLength, const byte *compressed, int compressedSize ) {
|
|
int i, bit;
|
|
idBitMsg msg;
|
|
huffmanNode_t *node;
|
|
|
|
msg.Init( compressed, compressedSize );
|
|
msg.SetSize( compressedSize );
|
|
msg.BeginReading();
|
|
for ( i = 0; i < textLength; i++ ) {
|
|
node = huffmanTree;
|
|
do {
|
|
bit = msg.ReadBits( 1 );
|
|
node = node->children[bit];
|
|
} while( node->symbol == -1 );
|
|
text[i] = node->symbol;
|
|
}
|
|
text[i] = '\0';
|
|
return msg.GetReadCount();
|
|
}
|
|
|
|
/*
|
|
================
|
|
ListHuffmanFrequencies_f
|
|
================
|
|
*/
|
|
void ListHuffmanFrequencies_f( const idCmdArgs &args ) {
|
|
int i;
|
|
float compression;
|
|
compression = !totalUncompressedLength ? 100 : 100 * totalCompressedLength / totalUncompressedLength;
|
|
common->Printf( "// compression ratio = %d%%\n", (int)compression );
|
|
common->Printf( "static int huffmanFrequencies[] = {\n" );
|
|
for( i = 0; i < MAX_HUFFMAN_SYMBOLS; i += 8 ) {
|
|
common->Printf( "\t0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x,\n",
|
|
huffmanFrequencies[i+0], huffmanFrequencies[i+1],
|
|
huffmanFrequencies[i+2], huffmanFrequencies[i+3],
|
|
huffmanFrequencies[i+4], huffmanFrequencies[i+5],
|
|
huffmanFrequencies[i+6], huffmanFrequencies[i+7]);
|
|
}
|
|
common->Printf( "}\n" );
|
|
}
|
|
|
|
/*
|
|
====================================================================================
|
|
|
|
idDeclFile
|
|
|
|
====================================================================================
|
|
*/
|
|
|
|
/*
|
|
================
|
|
idDeclFile::idDeclFile
|
|
================
|
|
*/
|
|
idDeclFile::idDeclFile( const char *fileName, declType_t defaultType ) {
|
|
this->fileName = fileName;
|
|
this->defaultType = defaultType;
|
|
this->timestamp = 0;
|
|
this->checksum = 0;
|
|
this->fileSize = 0;
|
|
this->numLines = 0;
|
|
this->decls = NULL;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclFile::idDeclFile
|
|
================
|
|
*/
|
|
idDeclFile::idDeclFile() {
|
|
this->fileName = "<implicit file>";
|
|
this->defaultType = DECL_MAX_TYPES;
|
|
this->timestamp = 0;
|
|
this->checksum = 0;
|
|
this->fileSize = 0;
|
|
this->numLines = 0;
|
|
this->decls = NULL;
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclFile::Reload
|
|
|
|
ForceReload will cause it to reload even if the timestamp hasn't changed
|
|
================
|
|
*/
|
|
void idDeclFile::Reload( bool force ) {
|
|
// check for an unchanged timestamp
|
|
if ( !force && timestamp != 0 ) {
|
|
ID_TIME_T testTimeStamp;
|
|
fileSystem->ReadFile( fileName, NULL, &testTimeStamp );
|
|
|
|
if ( testTimeStamp == timestamp ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// parse the text
|
|
LoadAndParse();
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclFile::LoadAndParse
|
|
|
|
This is used during both the initial load, and any reloads
|
|
================
|
|
*/
|
|
int c_savedMemory = 0;
|
|
|
|
int idDeclFile::LoadAndParse() {
|
|
int i, numTypes;
|
|
idLexer src;
|
|
idToken token;
|
|
int startMarker;
|
|
char * buffer;
|
|
int length, size;
|
|
int sourceLine;
|
|
idStr name;
|
|
idDeclLocal *newDecl;
|
|
bool reparse;
|
|
|
|
// load the text
|
|
common->DPrintf( "...loading '%s'\n", fileName.c_str() );
|
|
length = fileSystem->ReadFile( fileName, (void **)&buffer, ×tamp );
|
|
if ( length == -1 ) {
|
|
common->FatalError( "couldn't load %s", fileName.c_str() );
|
|
return 0;
|
|
}
|
|
|
|
if ( !src.LoadMemory( buffer, length, fileName ) ) {
|
|
common->Error( "Couldn't parse %s", fileName.c_str() );
|
|
Mem_Free( buffer );
|
|
return 0;
|
|
}
|
|
|
|
// mark all the defs that were from the last reload of this file
|
|
for ( idDeclLocal *decl = decls; decl; decl = decl->nextInFile ) {
|
|
decl->redefinedInReload = false;
|
|
}
|
|
|
|
src.SetFlags( DECL_LEXER_FLAGS );
|
|
|
|
checksum = MD5_BlockChecksum( buffer, length );
|
|
|
|
fileSize = length;
|
|
|
|
// scan through, identifying each individual declaration
|
|
while( 1 ) {
|
|
|
|
startMarker = src.GetFileOffset();
|
|
sourceLine = src.GetLineNum();
|
|
|
|
// parse the decl type name
|
|
if ( !src.ReadToken( &token ) ) {
|
|
break;
|
|
}
|
|
|
|
declType_t identifiedType = DECL_MAX_TYPES;
|
|
|
|
// get the decl type from the type name
|
|
numTypes = declManagerLocal.GetNumDeclTypes();
|
|
for ( i = 0; i < numTypes; i++ ) {
|
|
idDeclType *typeInfo = declManagerLocal.GetDeclType( i );
|
|
if ( typeInfo && typeInfo->typeName.Icmp( token ) == 0 ) {
|
|
identifiedType = (declType_t) typeInfo->type;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( i >= numTypes ) {
|
|
|
|
if ( token.Icmp( "{" ) == 0 ) {
|
|
|
|
// if we ever see an open brace, we somehow missed the [type] <name> prefix
|
|
src.Warning( "Missing decl name" );
|
|
src.SkipBracedSection( false );
|
|
continue;
|
|
|
|
} else {
|
|
|
|
if ( defaultType == DECL_MAX_TYPES ) {
|
|
src.Warning( "No type" );
|
|
continue;
|
|
}
|
|
src.UnreadToken( &token );
|
|
// use the default type
|
|
identifiedType = defaultType;
|
|
}
|
|
}
|
|
|
|
// now parse the name
|
|
if ( !src.ReadToken( &token ) ) {
|
|
src.Warning( "Type without definition at end of file" );
|
|
break;
|
|
}
|
|
|
|
if ( !token.Icmp( "{" ) ) {
|
|
// if we ever see an open brace, we somehow missed the [type] <name> prefix
|
|
src.Warning( "Missing decl name" );
|
|
src.SkipBracedSection( false );
|
|
continue;
|
|
}
|
|
|
|
// FIXME: export decls are only used by the model exporter, they are skipped here for now
|
|
if ( identifiedType == DECL_MODELEXPORT ) {
|
|
src.SkipBracedSection();
|
|
continue;
|
|
}
|
|
|
|
name = token;
|
|
|
|
// make sure there's a '{'
|
|
if ( !src.ReadToken( &token ) ) {
|
|
src.Warning( "Type without definition at end of file" );
|
|
break;
|
|
}
|
|
if ( token != "{" ) {
|
|
src.Warning( "Expecting '{' but found '%s'", token.c_str() );
|
|
continue;
|
|
}
|
|
src.UnreadToken( &token );
|
|
|
|
// now take everything until a matched closing brace
|
|
src.SkipBracedSection();
|
|
size = src.GetFileOffset() - startMarker;
|
|
|
|
// look it up, possibly getting a newly created default decl
|
|
reparse = false;
|
|
newDecl = declManagerLocal.FindTypeWithoutParsing( identifiedType, name, false );
|
|
if ( newDecl ) {
|
|
// update the existing copy
|
|
if ( newDecl->sourceFile != this || newDecl->redefinedInReload ) {
|
|
src.Warning( "%s '%s' previously defined at %s:%i", declManagerLocal.GetDeclNameFromType( identifiedType ),
|
|
name.c_str(), newDecl->sourceFile->fileName.c_str(), newDecl->sourceLine );
|
|
continue;
|
|
}
|
|
if ( newDecl->declState != DS_UNPARSED ) {
|
|
reparse = true;
|
|
}
|
|
} else {
|
|
// allow it to be created as a default, then add it to the per-file list
|
|
newDecl = declManagerLocal.FindTypeWithoutParsing( identifiedType, name, true );
|
|
newDecl->nextInFile = this->decls;
|
|
this->decls = newDecl;
|
|
}
|
|
|
|
newDecl->redefinedInReload = true;
|
|
|
|
if ( newDecl->textSource ) {
|
|
Mem_Free( newDecl->textSource );
|
|
newDecl->textSource = NULL;
|
|
}
|
|
|
|
newDecl->SetTextLocal( buffer + startMarker, size );
|
|
newDecl->sourceFile = this;
|
|
newDecl->sourceTextOffset = startMarker;
|
|
newDecl->sourceTextLength = size;
|
|
newDecl->sourceLine = sourceLine;
|
|
newDecl->declState = DS_UNPARSED;
|
|
|
|
// if it is currently in use, reparse it immedaitely
|
|
if ( reparse ) {
|
|
newDecl->ParseLocal();
|
|
}
|
|
}
|
|
|
|
numLines = src.GetLineNum();
|
|
|
|
Mem_Free( buffer );
|
|
|
|
// any defs that weren't redefinedInReload should now be defaulted
|
|
for ( idDeclLocal *decl = decls ; decl ; decl = decl->nextInFile ) {
|
|
if ( decl->redefinedInReload == false ) {
|
|
decl->MakeDefault();
|
|
decl->sourceTextOffset = decl->sourceFile->fileSize;
|
|
decl->sourceTextLength = 0;
|
|
decl->sourceLine = decl->sourceFile->numLines;
|
|
}
|
|
}
|
|
|
|
return checksum;
|
|
}
|
|
|
|
/*
|
|
====================================================================================
|
|
|
|
idDeclManagerLocal
|
|
|
|
====================================================================================
|
|
*/
|
|
|
|
const char *listDeclStrings[] = { "current", "all", "ever", NULL };
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::Init
|
|
===================
|
|
*/
|
|
void idDeclManagerLocal::Init( void ) {
|
|
|
|
common->Printf( "----- Initializing Decls -----\n" );
|
|
|
|
checksum = 0;
|
|
|
|
#ifdef USE_COMPRESSED_DECLS
|
|
SetupHuffman();
|
|
#endif
|
|
|
|
#ifdef GET_HUFFMAN_FREQUENCIES
|
|
ClearHuffmanFrequencies();
|
|
#endif
|
|
|
|
// decls used throughout the engine
|
|
RegisterDeclType( "table", DECL_TABLE, idDeclAllocator<idDeclTable> );
|
|
RegisterDeclType( "material", DECL_MATERIAL, idDeclAllocator<idMaterial> );
|
|
RegisterDeclType( "skin", DECL_SKIN, idDeclAllocator<idDeclSkin> );
|
|
RegisterDeclType( "sound", DECL_SOUND, idDeclAllocator<idSoundShader> );
|
|
|
|
RegisterDeclType( "entityDef", DECL_ENTITYDEF, idDeclAllocator<idDeclEntityDef> );
|
|
RegisterDeclType( "mapDef", DECL_MAPDEF, idDeclAllocator<idDeclEntityDef> );
|
|
RegisterDeclType( "fx", DECL_FX, idDeclAllocator<idDeclFX> );
|
|
RegisterDeclType( "particle", DECL_PARTICLE, idDeclAllocator<idDeclParticle> );
|
|
RegisterDeclType( "articulatedFigure", DECL_AF, idDeclAllocator<idDeclAF> );
|
|
RegisterDeclType( "pda", DECL_PDA, idDeclAllocator<idDeclPDA> );
|
|
RegisterDeclType( "email", DECL_EMAIL, idDeclAllocator<idDeclEmail> );
|
|
RegisterDeclType( "video", DECL_VIDEO, idDeclAllocator<idDeclVideo> );
|
|
RegisterDeclType( "audio", DECL_AUDIO, idDeclAllocator<idDeclAudio> );
|
|
|
|
RegisterDeclFolder( "materials", ".mtr", DECL_MATERIAL );
|
|
RegisterDeclFolder( "skins", ".skin", DECL_SKIN );
|
|
RegisterDeclFolder( "sound", ".sndshd", DECL_SOUND );
|
|
|
|
// add console commands
|
|
cmdSystem->AddCommand( "listDecls", ListDecls_f, CMD_FL_SYSTEM, "lists all decls" );
|
|
|
|
cmdSystem->AddCommand( "reloadDecls", ReloadDecls_f, CMD_FL_SYSTEM, "reloads decls" );
|
|
cmdSystem->AddCommand( "touch", TouchDecl_f, CMD_FL_SYSTEM, "touches a decl" );
|
|
|
|
cmdSystem->AddCommand( "listTables", idListDecls_f<DECL_TABLE>, CMD_FL_SYSTEM, "lists tables", idCmdSystem::ArgCompletion_String<listDeclStrings> );
|
|
cmdSystem->AddCommand( "listMaterials", idListDecls_f<DECL_MATERIAL>, CMD_FL_SYSTEM, "lists materials", idCmdSystem::ArgCompletion_String<listDeclStrings> );
|
|
cmdSystem->AddCommand( "listSkins", idListDecls_f<DECL_SKIN>, CMD_FL_SYSTEM, "lists skins", idCmdSystem::ArgCompletion_String<listDeclStrings> );
|
|
cmdSystem->AddCommand( "listSoundShaders", idListDecls_f<DECL_SOUND>, CMD_FL_SYSTEM, "lists sound shaders", idCmdSystem::ArgCompletion_String<listDeclStrings> );
|
|
|
|
cmdSystem->AddCommand( "listEntityDefs", idListDecls_f<DECL_ENTITYDEF>, CMD_FL_SYSTEM, "lists entity defs", idCmdSystem::ArgCompletion_String<listDeclStrings> );
|
|
cmdSystem->AddCommand( "listFX", idListDecls_f<DECL_FX>, CMD_FL_SYSTEM, "lists FX systems", idCmdSystem::ArgCompletion_String<listDeclStrings> );
|
|
cmdSystem->AddCommand( "listParticles", idListDecls_f<DECL_PARTICLE>, CMD_FL_SYSTEM, "lists particle systems", idCmdSystem::ArgCompletion_String<listDeclStrings> );
|
|
cmdSystem->AddCommand( "listAF", idListDecls_f<DECL_AF>, CMD_FL_SYSTEM, "lists articulated figures", idCmdSystem::ArgCompletion_String<listDeclStrings>);
|
|
|
|
cmdSystem->AddCommand( "listPDAs", idListDecls_f<DECL_PDA>, CMD_FL_SYSTEM, "lists PDAs", idCmdSystem::ArgCompletion_String<listDeclStrings> );
|
|
cmdSystem->AddCommand( "listEmails", idListDecls_f<DECL_EMAIL>, CMD_FL_SYSTEM, "lists Emails", idCmdSystem::ArgCompletion_String<listDeclStrings> );
|
|
cmdSystem->AddCommand( "listVideos", idListDecls_f<DECL_VIDEO>, CMD_FL_SYSTEM, "lists Videos", idCmdSystem::ArgCompletion_String<listDeclStrings> );
|
|
cmdSystem->AddCommand( "listAudios", idListDecls_f<DECL_AUDIO>, CMD_FL_SYSTEM, "lists Audios", idCmdSystem::ArgCompletion_String<listDeclStrings> );
|
|
|
|
cmdSystem->AddCommand( "printTable", idPrintDecls_f<DECL_TABLE>, CMD_FL_SYSTEM, "prints a table", idCmdSystem::ArgCompletion_Decl<DECL_TABLE> );
|
|
cmdSystem->AddCommand( "printMaterial", idPrintDecls_f<DECL_MATERIAL>, CMD_FL_SYSTEM, "prints a material", idCmdSystem::ArgCompletion_Decl<DECL_MATERIAL> );
|
|
cmdSystem->AddCommand( "printSkin", idPrintDecls_f<DECL_SKIN>, CMD_FL_SYSTEM, "prints a skin", idCmdSystem::ArgCompletion_Decl<DECL_SKIN> );
|
|
cmdSystem->AddCommand( "printSoundShader", idPrintDecls_f<DECL_SOUND>, CMD_FL_SYSTEM, "prints a sound shader", idCmdSystem::ArgCompletion_Decl<DECL_SOUND> );
|
|
|
|
cmdSystem->AddCommand( "printEntityDef", idPrintDecls_f<DECL_ENTITYDEF>, CMD_FL_SYSTEM, "prints an entity def", idCmdSystem::ArgCompletion_Decl<DECL_ENTITYDEF> );
|
|
cmdSystem->AddCommand( "printFX", idPrintDecls_f<DECL_FX>, CMD_FL_SYSTEM, "prints an FX system", idCmdSystem::ArgCompletion_Decl<DECL_FX> );
|
|
cmdSystem->AddCommand( "printParticle", idPrintDecls_f<DECL_PARTICLE>, CMD_FL_SYSTEM, "prints a particle system", idCmdSystem::ArgCompletion_Decl<DECL_PARTICLE> );
|
|
cmdSystem->AddCommand( "printAF", idPrintDecls_f<DECL_AF>, CMD_FL_SYSTEM, "prints an articulated figure", idCmdSystem::ArgCompletion_Decl<DECL_AF> );
|
|
|
|
cmdSystem->AddCommand( "printPDA", idPrintDecls_f<DECL_PDA>, CMD_FL_SYSTEM, "prints an PDA", idCmdSystem::ArgCompletion_Decl<DECL_PDA> );
|
|
cmdSystem->AddCommand( "printEmail", idPrintDecls_f<DECL_EMAIL>, CMD_FL_SYSTEM, "prints an Email", idCmdSystem::ArgCompletion_Decl<DECL_EMAIL> );
|
|
cmdSystem->AddCommand( "printVideo", idPrintDecls_f<DECL_VIDEO>, CMD_FL_SYSTEM, "prints a Audio", idCmdSystem::ArgCompletion_Decl<DECL_VIDEO> );
|
|
cmdSystem->AddCommand( "printAudio", idPrintDecls_f<DECL_AUDIO>, CMD_FL_SYSTEM, "prints an Video", idCmdSystem::ArgCompletion_Decl<DECL_AUDIO> );
|
|
|
|
cmdSystem->AddCommand( "listHuffmanFrequencies", ListHuffmanFrequencies_f, CMD_FL_SYSTEM, "lists decl text character frequencies" );
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::Shutdown
|
|
===================
|
|
*/
|
|
void idDeclManagerLocal::Shutdown( void ) {
|
|
int i, j;
|
|
idDeclLocal *decl;
|
|
|
|
// free decls
|
|
for ( i = 0; i < DECL_MAX_TYPES; i++ ) {
|
|
for ( j = 0; j < linearLists[i].Num(); j++ ) {
|
|
decl = linearLists[i][j];
|
|
if ( decl->self != NULL ) {
|
|
decl->self->FreeData();
|
|
delete decl->self;
|
|
}
|
|
if ( decl->textSource ) {
|
|
Mem_Free( decl->textSource );
|
|
decl->textSource = NULL;
|
|
}
|
|
delete decl;
|
|
}
|
|
linearLists[i].Clear();
|
|
hashTables[i].Free();
|
|
}
|
|
|
|
// free decl files
|
|
loadedFiles.DeleteContents( true );
|
|
|
|
// free the decl types and folders
|
|
declTypes.DeleteContents( true );
|
|
declFolders.DeleteContents( true );
|
|
|
|
#ifdef USE_COMPRESSED_DECLS
|
|
ShutdownHuffman();
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::Reload
|
|
===================
|
|
*/
|
|
void idDeclManagerLocal::Reload( bool force ) {
|
|
for ( int i = 0; i < loadedFiles.Num(); i++ ) {
|
|
loadedFiles[i]->Reload( force );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::BeginLevelLoad
|
|
===================
|
|
*/
|
|
void idDeclManagerLocal::BeginLevelLoad() {
|
|
insideLevelLoad = true;
|
|
|
|
// clear all the referencedThisLevel flags and purge all the data
|
|
// so the next reference will cause a reparse
|
|
for ( int i = 0; i < DECL_MAX_TYPES; i++ ) {
|
|
int num = linearLists[i].Num();
|
|
for ( int j = 0 ; j < num ; j++ ) {
|
|
idDeclLocal *decl = linearLists[i][j];
|
|
decl->Purge();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::EndLevelLoad
|
|
===================
|
|
*/
|
|
void idDeclManagerLocal::EndLevelLoad() {
|
|
insideLevelLoad = false;
|
|
|
|
// we don't need to do anything here, but the image manager, model manager,
|
|
// and sound sample manager will need to free media that was not referenced
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::RegisterDeclType
|
|
===================
|
|
*/
|
|
void idDeclManagerLocal::RegisterDeclType( const char *typeName, declType_t type, idDecl *(*allocator)( void ) ) {
|
|
idDeclType *declType;
|
|
|
|
if ( type < declTypes.Num() && declTypes[(int)type] ) {
|
|
common->Warning( "idDeclManager::RegisterDeclType: type '%s' already exists", typeName );
|
|
return;
|
|
}
|
|
|
|
declType = new idDeclType;
|
|
declType->typeName = typeName;
|
|
declType->type = type;
|
|
declType->allocator = allocator;
|
|
|
|
if ( (int)type + 1 > declTypes.Num() ) {
|
|
declTypes.AssureSize( (int)type + 1, NULL );
|
|
}
|
|
declTypes[type] = declType;
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::RegisterDeclFolder
|
|
===================
|
|
*/
|
|
void idDeclManagerLocal::RegisterDeclFolder( const char *folder, const char *extension, declType_t defaultType ) {
|
|
int i, j;
|
|
idStr fileName;
|
|
idDeclFolder *declFolder;
|
|
idFileList *fileList;
|
|
idDeclFile *df;
|
|
|
|
// check whether this folder / extension combination already exists
|
|
for ( i = 0; i < declFolders.Num(); i++ ) {
|
|
if ( declFolders[i]->folder.Icmp( folder ) == 0 && declFolders[i]->extension.Icmp( extension ) == 0 ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( i < declFolders.Num() ) {
|
|
declFolder = declFolders[i];
|
|
} else {
|
|
declFolder = new idDeclFolder;
|
|
declFolder->folder = folder;
|
|
declFolder->extension = extension;
|
|
declFolder->defaultType = defaultType;
|
|
declFolders.Append( declFolder );
|
|
}
|
|
|
|
// scan for decl files
|
|
fileList = fileSystem->ListFiles( declFolder->folder, declFolder->extension, true );
|
|
|
|
// load and parse decl files
|
|
for ( i = 0; i < fileList->GetNumFiles(); i++ ) {
|
|
fileName = declFolder->folder + "/" + fileList->GetFile( i );
|
|
|
|
// check whether this file has already been loaded
|
|
for ( j = 0; j < loadedFiles.Num(); j++ ) {
|
|
if ( fileName.Icmp( loadedFiles[j]->fileName ) == 0 ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( j < loadedFiles.Num() ) {
|
|
df = loadedFiles[j];
|
|
} else {
|
|
df = new idDeclFile( fileName, defaultType );
|
|
loadedFiles.Append( df );
|
|
}
|
|
df->LoadAndParse();
|
|
}
|
|
|
|
fileSystem->FreeFileList( fileList );
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::GetChecksum
|
|
===================
|
|
*/
|
|
int idDeclManagerLocal::GetChecksum( void ) const {
|
|
int i, j, total, num;
|
|
int *checksumData;
|
|
|
|
// get the total number of decls
|
|
total = 0;
|
|
for ( i = 0; i < DECL_MAX_TYPES; i++ ) {
|
|
total += linearLists[i].Num();
|
|
}
|
|
|
|
checksumData = (int *) _alloca16( total * 2 * sizeof( int ) );
|
|
|
|
total = 0;
|
|
for ( i = 0; i < DECL_MAX_TYPES; i++ ) {
|
|
declType_t type = (declType_t) i;
|
|
|
|
// FIXME: not particularly pretty but PDAs and associated decls are localized and should not be checksummed
|
|
if ( type == DECL_PDA || type == DECL_VIDEO || type == DECL_AUDIO || type == DECL_EMAIL ) {
|
|
continue;
|
|
}
|
|
|
|
num = linearLists[i].Num();
|
|
for ( j = 0; j < num; j++ ) {
|
|
idDeclLocal *decl = linearLists[i][j];
|
|
|
|
if ( decl->sourceFile == &implicitDecls ) {
|
|
continue;
|
|
}
|
|
|
|
checksumData[total*2+0] = total;
|
|
checksumData[total*2+1] = decl->checksum;
|
|
total++;
|
|
}
|
|
}
|
|
|
|
LittleRevBytes( checksumData, sizeof(int), total * 2 );
|
|
return MD5_BlockChecksum( checksumData, total * 2 * sizeof( int ) );
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::GetNumDeclTypes
|
|
===================
|
|
*/
|
|
int idDeclManagerLocal::GetNumDeclTypes( void ) const {
|
|
return declTypes.Num();
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::GetDeclNameFromType
|
|
===================
|
|
*/
|
|
const char * idDeclManagerLocal::GetDeclNameFromType( declType_t type ) const {
|
|
int typeIndex = (int)type;
|
|
|
|
if ( typeIndex < 0 || typeIndex >= declTypes.Num() || declTypes[typeIndex] == NULL ) {
|
|
common->FatalError( "idDeclManager::GetDeclNameFromType: bad type: %i", typeIndex );
|
|
}
|
|
return declTypes[typeIndex]->typeName;
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::GetDeclTypeFromName
|
|
===================
|
|
*/
|
|
declType_t idDeclManagerLocal::GetDeclTypeFromName( const char *typeName ) const {
|
|
int i;
|
|
|
|
for ( i = 0; i < declTypes.Num(); i++ ) {
|
|
if ( declTypes[i] && declTypes[i]->typeName.Icmp( typeName ) == 0 ) {
|
|
return (declType_t)declTypes[i]->type;
|
|
}
|
|
}
|
|
return DECL_MAX_TYPES;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclManagerLocal::FindType
|
|
|
|
External users will always cause the decl to be parsed before returning
|
|
=================
|
|
*/
|
|
const idDecl *idDeclManagerLocal::FindType( declType_t type, const char *name, bool makeDefault ) {
|
|
idDeclLocal *decl;
|
|
|
|
if ( !name || !name[0] ) {
|
|
name = "_emptyName";
|
|
//common->Warning( "idDeclManager::FindType: empty %s name", GetDeclType( (int)type )->typeName.c_str() );
|
|
}
|
|
|
|
decl = FindTypeWithoutParsing( type, name, makeDefault );
|
|
if ( !decl ) {
|
|
return NULL;
|
|
}
|
|
|
|
decl->AllocateSelf();
|
|
|
|
// if it hasn't been parsed yet, parse it now
|
|
if ( decl->declState == DS_UNPARSED ) {
|
|
decl->ParseLocal();
|
|
}
|
|
|
|
// mark it as referenced
|
|
decl->referencedThisLevel = true;
|
|
decl->everReferenced = true;
|
|
if ( insideLevelLoad ) {
|
|
decl->parsedOutsideLevelLoad = false;
|
|
}
|
|
|
|
return decl->self;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idDeclManagerLocal::FindDeclWithoutParsing
|
|
===============
|
|
*/
|
|
const idDecl* idDeclManagerLocal::FindDeclWithoutParsing( declType_t type, const char *name, bool makeDefault) {
|
|
idDeclLocal* decl;
|
|
decl = FindTypeWithoutParsing(type, name, makeDefault);
|
|
if(decl) {
|
|
return decl->self;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idDeclManagerLocal::ReloadFile
|
|
===============
|
|
*/
|
|
void idDeclManagerLocal::ReloadFile( const char* filename, bool force ) {
|
|
for ( int i = 0; i < loadedFiles.Num(); i++ ) {
|
|
if(!loadedFiles[i]->fileName.Icmp(filename)) {
|
|
checksum ^= loadedFiles[i]->checksum;
|
|
loadedFiles[i]->Reload( force );
|
|
checksum ^= loadedFiles[i]->checksum;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::GetNumDecls
|
|
===================
|
|
*/
|
|
int idDeclManagerLocal::GetNumDecls( declType_t type ) {
|
|
int typeIndex = (int)type;
|
|
|
|
if ( typeIndex < 0 || typeIndex >= declTypes.Num() || declTypes[typeIndex] == NULL ) {
|
|
common->FatalError( "idDeclManager::GetNumDecls: bad type: %i", typeIndex );
|
|
}
|
|
return linearLists[ typeIndex ].Num();
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::DeclByIndex
|
|
===================
|
|
*/
|
|
const idDecl *idDeclManagerLocal::DeclByIndex( declType_t type, int index, bool forceParse ) {
|
|
int typeIndex = (int)type;
|
|
|
|
if ( typeIndex < 0 || typeIndex >= declTypes.Num() || declTypes[typeIndex] == NULL ) {
|
|
common->FatalError( "idDeclManager::DeclByIndex: bad type: %i", typeIndex );
|
|
}
|
|
if ( index < 0 || index >= linearLists[ typeIndex ].Num() ) {
|
|
common->Error( "idDeclManager::DeclByIndex: out of range" );
|
|
}
|
|
idDeclLocal *decl = linearLists[ typeIndex ][ index ];
|
|
|
|
decl->AllocateSelf();
|
|
|
|
if ( forceParse && decl->declState == DS_UNPARSED ) {
|
|
decl->ParseLocal();
|
|
}
|
|
|
|
return decl->self;
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::ListType
|
|
|
|
list*
|
|
Lists decls currently referenced
|
|
|
|
list* ever
|
|
Lists decls that have been referenced at least once since app launched
|
|
|
|
list* all
|
|
Lists every decl declared, even if it hasn't been referenced or parsed
|
|
|
|
FIXME: alphabetized, wildcards?
|
|
===================
|
|
*/
|
|
void idDeclManagerLocal::ListType( const idCmdArgs &args, declType_t type ) {
|
|
bool all, ever;
|
|
|
|
if ( !idStr::Icmp( args.Argv( 1 ), "all" ) ) {
|
|
all = true;
|
|
} else {
|
|
all = false;
|
|
}
|
|
if ( !idStr::Icmp( args.Argv( 1 ), "ever" ) ) {
|
|
ever = true;
|
|
} else {
|
|
ever = false;
|
|
}
|
|
|
|
common->Printf( "--------------------\n" );
|
|
int printed = 0;
|
|
int count = linearLists[ (int)type ].Num();
|
|
for ( int i = 0 ; i < count ; i++ ) {
|
|
idDeclLocal *decl = linearLists[ (int)type ][ i ];
|
|
|
|
if ( !all && decl->declState == DS_UNPARSED ) {
|
|
continue;
|
|
}
|
|
|
|
if ( !all && !ever && !decl->referencedThisLevel ) {
|
|
continue;
|
|
}
|
|
|
|
if ( decl->referencedThisLevel ) {
|
|
common->Printf( "*" );
|
|
} else if ( decl->everReferenced ) {
|
|
common->Printf( "." );
|
|
} else {
|
|
common->Printf( " " );
|
|
}
|
|
if ( decl->declState == DS_DEFAULTED ) {
|
|
common->Printf( "D" );
|
|
} else {
|
|
common->Printf( " " );
|
|
}
|
|
common->Printf( "%4i: ", decl->index );
|
|
printed++;
|
|
if ( decl->declState == DS_UNPARSED ) {
|
|
// doesn't have any type specific data yet
|
|
common->Printf( "%s\n", decl->GetName() );
|
|
} else {
|
|
decl->self->List();
|
|
}
|
|
}
|
|
|
|
common->Printf( "--------------------\n" );
|
|
common->Printf( "%i of %i %s\n", printed, count, declTypes[type]->typeName.c_str() );
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::PrintType
|
|
===================
|
|
*/
|
|
void idDeclManagerLocal::PrintType( const idCmdArgs &args, declType_t type ) {
|
|
// individual decl types may use additional command parameters
|
|
if ( args.Argc() < 2 ) {
|
|
common->Printf( "USAGE: Print<decl type> <decl name> [type specific parms]\n" );
|
|
return;
|
|
}
|
|
|
|
// look it up, skipping the public path so it won't parse or reference
|
|
idDeclLocal *decl = FindTypeWithoutParsing( type, args.Argv( 1 ), false );
|
|
if ( !decl ) {
|
|
common->Printf( "%s '%s' not found.\n", declTypes[ type ]->typeName.c_str(), args.Argv( 1 ) );
|
|
return;
|
|
}
|
|
|
|
// print information common to all decls
|
|
common->Printf( "%s %s:\n", declTypes[ type ]->typeName.c_str(), decl->name.c_str() );
|
|
common->Printf( "source: %s:%i\n", decl->sourceFile->fileName.c_str(), decl->sourceLine );
|
|
common->Printf( "----------\n" );
|
|
if ( decl->textSource != NULL ) {
|
|
char *declText = (char *)_alloca( decl->textLength + 1 );
|
|
decl->GetText( declText );
|
|
common->Printf( "%s\n", declText );
|
|
} else {
|
|
common->Printf( "NO SOURCE\n" );
|
|
}
|
|
common->Printf( "----------\n" );
|
|
switch( decl->declState ) {
|
|
case DS_UNPARSED:
|
|
common->Printf( "Unparsed.\n" );
|
|
break;
|
|
case DS_DEFAULTED:
|
|
common->Printf( "<DEFAULTED>\n" );
|
|
break;
|
|
case DS_PARSED:
|
|
common->Printf( "Parsed.\n" );
|
|
break;
|
|
}
|
|
|
|
if ( decl->referencedThisLevel ) {
|
|
common->Printf( "Currently referenced this level.\n" );
|
|
} else if ( decl->everReferenced ) {
|
|
common->Printf( "Referenced in a previous level.\n" );
|
|
} else {
|
|
common->Printf( "Never referenced.\n" );
|
|
}
|
|
|
|
// allow type-specific data to be printed
|
|
if ( decl->self != NULL ) {
|
|
decl->self->Print();
|
|
}
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::CreateNewDecl
|
|
===================
|
|
*/
|
|
idDecl *idDeclManagerLocal::CreateNewDecl( declType_t type, const char *name, const char *_fileName ) {
|
|
int typeIndex = (int)type;
|
|
int i, hash;
|
|
|
|
if ( typeIndex < 0 || typeIndex >= declTypes.Num() || declTypes[typeIndex] == NULL ) {
|
|
common->FatalError( "idDeclManager::CreateNewDecl: bad type: %i", typeIndex );
|
|
}
|
|
|
|
char canonicalName[MAX_STRING_CHARS];
|
|
|
|
MakeNameCanonical( name, canonicalName, sizeof( canonicalName ) );
|
|
|
|
idStr fileName = _fileName;
|
|
fileName.BackSlashesToSlashes();
|
|
|
|
// see if it already exists
|
|
hash = hashTables[typeIndex].GenerateKey( canonicalName, false );
|
|
for ( i = hashTables[typeIndex].First( hash ); i >= 0; i = hashTables[typeIndex].Next( i ) ) {
|
|
if ( linearLists[typeIndex][i]->name.Icmp( canonicalName ) == 0 ) {
|
|
linearLists[typeIndex][i]->AllocateSelf();
|
|
return linearLists[typeIndex][i]->self;
|
|
}
|
|
}
|
|
|
|
idDeclFile *sourceFile;
|
|
|
|
// find existing source file or create a new one
|
|
for ( i = 0; i < loadedFiles.Num(); i++ ) {
|
|
if ( loadedFiles[i]->fileName.Icmp( fileName ) == 0 ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( i < loadedFiles.Num() ) {
|
|
sourceFile = loadedFiles[i];
|
|
} else {
|
|
sourceFile = new idDeclFile( fileName, type );
|
|
loadedFiles.Append( sourceFile );
|
|
}
|
|
|
|
idDeclLocal *decl = new idDeclLocal;
|
|
decl->name = canonicalName;
|
|
decl->type = type;
|
|
decl->declState = DS_UNPARSED;
|
|
decl->AllocateSelf();
|
|
idStr header = declTypes[typeIndex]->typeName;
|
|
idStr defaultText = decl->self->DefaultDefinition();
|
|
|
|
|
|
int size = header.Length() + 1 + idStr::Length( canonicalName ) + 1 + defaultText.Length();
|
|
char *declText = ( char * ) _alloca( size + 1 );
|
|
|
|
memcpy( declText, header, header.Length() );
|
|
declText[header.Length()] = ' ';
|
|
memcpy( declText + header.Length() + 1, canonicalName, idStr::Length( canonicalName ) );
|
|
declText[header.Length() + 1 + idStr::Length( canonicalName )] = ' ';
|
|
memcpy( declText + header.Length() + 1 + idStr::Length( canonicalName ) + 1, defaultText, defaultText.Length() + 1 );
|
|
|
|
decl->SetTextLocal( declText, size );
|
|
decl->sourceFile = sourceFile;
|
|
decl->sourceTextOffset = sourceFile->fileSize;
|
|
decl->sourceTextLength = 0;
|
|
decl->sourceLine = sourceFile->numLines;
|
|
|
|
decl->ParseLocal();
|
|
|
|
// add this decl to the source file list
|
|
decl->nextInFile = sourceFile->decls;
|
|
sourceFile->decls = decl;
|
|
|
|
// add it to the hash table and linear list
|
|
decl->index = linearLists[typeIndex].Num();
|
|
hashTables[typeIndex].Add( hash, linearLists[typeIndex].Append( decl ) );
|
|
|
|
return decl->self;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
idDeclManagerLocal::RenameDecl
|
|
===============
|
|
*/
|
|
bool idDeclManagerLocal::RenameDecl( declType_t type, const char* oldName, const char* newName ) {
|
|
|
|
char canonicalOldName[MAX_STRING_CHARS];
|
|
MakeNameCanonical( oldName, canonicalOldName, sizeof( canonicalOldName ));
|
|
|
|
char canonicalNewName[MAX_STRING_CHARS];
|
|
MakeNameCanonical( newName, canonicalNewName, sizeof( canonicalNewName ) );
|
|
|
|
idDeclLocal *decl = NULL;
|
|
|
|
// make sure it already exists
|
|
int typeIndex = (int)type;
|
|
int i, hash;
|
|
hash = hashTables[typeIndex].GenerateKey( canonicalOldName, false );
|
|
for ( i = hashTables[typeIndex].First( hash ); i >= 0; i = hashTables[typeIndex].Next( i ) ) {
|
|
if ( linearLists[typeIndex][i]->name.Icmp( canonicalOldName ) == 0 ) {
|
|
decl = linearLists[typeIndex][i];
|
|
break;
|
|
}
|
|
}
|
|
if(!decl)
|
|
return false;
|
|
|
|
//if ( !hashTables[(int)type].Get( canonicalOldName, &declPtr ) )
|
|
// return false;
|
|
|
|
//decl = *declPtr;
|
|
|
|
//Change the name
|
|
decl->name = canonicalNewName;
|
|
|
|
|
|
// add it to the hash table
|
|
//hashTables[(int)decl->type].Set( decl->name, decl );
|
|
int newhash = hashTables[typeIndex].GenerateKey( canonicalNewName, false );
|
|
hashTables[typeIndex].Add( newhash, decl->index );
|
|
|
|
//Remove the old hash item
|
|
hashTables[typeIndex].Remove(hash, decl->index);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::MediaPrint
|
|
|
|
This is just used to nicely indent media caching prints
|
|
===================
|
|
*/
|
|
void idDeclManagerLocal::MediaPrint( const char *fmt, ... ) {
|
|
if ( !decl_show.GetInteger() ) {
|
|
return;
|
|
}
|
|
for ( int i = 0 ; i < indent ; i++ ) {
|
|
common->Printf( " " );
|
|
}
|
|
va_list argptr;
|
|
char buffer[1024];
|
|
va_start (argptr,fmt);
|
|
idStr::vsnPrintf( buffer, sizeof(buffer), fmt, argptr );
|
|
va_end (argptr);
|
|
buffer[sizeof(buffer)-1] = '\0';
|
|
|
|
common->Printf( "%s", buffer );
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::WritePrecacheCommands
|
|
===================
|
|
*/
|
|
void idDeclManagerLocal::WritePrecacheCommands( idFile *f ) {
|
|
for ( int i = 0; i < declTypes.Num(); i++ ) {
|
|
int num;
|
|
|
|
if ( declTypes[i] == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
num = linearLists[i].Num();
|
|
|
|
for ( int j = 0 ; j < num ; j++ ) {
|
|
idDeclLocal *decl = linearLists[i][j];
|
|
|
|
if ( !decl->referencedThisLevel ) {
|
|
continue;
|
|
}
|
|
|
|
char str[1024];
|
|
sprintf( str, "touch %s %s\n", declTypes[i]->typeName.c_str(), decl->GetName() );
|
|
common->Printf( "%s", str );
|
|
f->Printf( "%s", str );
|
|
}
|
|
}
|
|
}
|
|
|
|
/********************************************************************/
|
|
|
|
const idMaterial *idDeclManagerLocal::FindMaterial( const char *name, bool makeDefault ) {
|
|
return static_cast<const idMaterial *>( FindType( DECL_MATERIAL, name, makeDefault ) );
|
|
}
|
|
|
|
const idMaterial *idDeclManagerLocal::MaterialByIndex( int index, bool forceParse ) {
|
|
return static_cast<const idMaterial *>( DeclByIndex( DECL_MATERIAL, index, forceParse ) );
|
|
}
|
|
|
|
/********************************************************************/
|
|
|
|
const idDeclSkin *idDeclManagerLocal::FindSkin( const char *name, bool makeDefault ) {
|
|
return static_cast<const idDeclSkin *>( FindType( DECL_SKIN, name, makeDefault ) );
|
|
}
|
|
|
|
const idDeclSkin *idDeclManagerLocal::SkinByIndex( int index, bool forceParse ) {
|
|
return static_cast<const idDeclSkin *>( DeclByIndex( DECL_SKIN, index, forceParse ) );
|
|
}
|
|
|
|
/********************************************************************/
|
|
|
|
const idSoundShader *idDeclManagerLocal::FindSound( const char *name, bool makeDefault ) {
|
|
return static_cast<const idSoundShader *>( FindType( DECL_SOUND, name, makeDefault ) );
|
|
}
|
|
|
|
const idSoundShader *idDeclManagerLocal::SoundByIndex( int index, bool forceParse ) {
|
|
return static_cast<const idSoundShader *>( DeclByIndex( DECL_SOUND, index, forceParse ) );
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::MakeNameCanonical
|
|
===================
|
|
*/
|
|
void idDeclManagerLocal::MakeNameCanonical( const char *name, char *result, int maxLength ) {
|
|
int i, lastDot;
|
|
|
|
lastDot = -1;
|
|
for ( i = 0; i < maxLength && name[i] != '\0'; i++ ) {
|
|
int c = name[i];
|
|
if ( c == '\\' ) {
|
|
result[i] = '/';
|
|
} else if ( c == '.' ) {
|
|
lastDot = i;
|
|
result[i] = c;
|
|
} else {
|
|
result[i] = idStr::ToLower( c );
|
|
}
|
|
}
|
|
if ( lastDot != -1 ) {
|
|
result[lastDot] = '\0';
|
|
} else {
|
|
result[i] = '\0';
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
idDeclManagerLocal::ListDecls_f
|
|
================
|
|
*/
|
|
void idDeclManagerLocal::ListDecls_f( const idCmdArgs &args ) {
|
|
int i, j;
|
|
int totalDecls = 0;
|
|
int totalText = 0;
|
|
int totalStructs = 0;
|
|
|
|
for ( i = 0; i < declManagerLocal.declTypes.Num(); i++ ) {
|
|
int size, num;
|
|
|
|
if ( declManagerLocal.declTypes[i] == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
num = declManagerLocal.linearLists[i].Num();
|
|
totalDecls += num;
|
|
|
|
size = 0;
|
|
for ( j = 0; j < num; j++ ) {
|
|
size += declManagerLocal.linearLists[i][j]->Size();
|
|
if ( declManagerLocal.linearLists[i][j]->self != NULL ) {
|
|
size += declManagerLocal.linearLists[i][j]->self->Size();
|
|
}
|
|
}
|
|
totalStructs += size;
|
|
|
|
common->Printf( "%4ik %4i %s\n", size >> 10, num, declManagerLocal.declTypes[i]->typeName.c_str() );
|
|
}
|
|
|
|
for ( i = 0 ; i < declManagerLocal.loadedFiles.Num() ; i++ ) {
|
|
idDeclFile *df = declManagerLocal.loadedFiles[i];
|
|
totalText += df->fileSize;
|
|
}
|
|
|
|
common->Printf( "%i total decls is %i decl files\n", totalDecls, declManagerLocal.loadedFiles.Num() );
|
|
common->Printf( "%iKB in text, %iKB in structures\n", totalText >> 10, totalStructs >> 10 );
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::ReloadDecls_f
|
|
|
|
Reload will not find any new files created in the directories, it
|
|
will only reload existing files.
|
|
|
|
A reload will never cause anything to be purged.
|
|
===================
|
|
*/
|
|
void idDeclManagerLocal::ReloadDecls_f( const idCmdArgs &args ) {
|
|
bool force;
|
|
|
|
if ( !idStr::Icmp( args.Argv( 1 ), "all" ) ) {
|
|
force = true;
|
|
common->Printf( "reloading all decl files:\n" );
|
|
} else {
|
|
force = false;
|
|
common->Printf( "reloading changed decl files:\n" );
|
|
}
|
|
|
|
soundSystem->SetMute( true );
|
|
|
|
declManagerLocal.Reload( force );
|
|
|
|
soundSystem->SetMute( false );
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::TouchDecl_f
|
|
===================
|
|
*/
|
|
void idDeclManagerLocal::TouchDecl_f( const idCmdArgs &args ) {
|
|
int i;
|
|
|
|
if ( args.Argc() != 3 ) {
|
|
common->Printf( "usage: touch <type> <name>\n" );
|
|
common->Printf( "valid types: " );
|
|
for ( int i = 0 ; i < declManagerLocal.declTypes.Num() ; i++ ) {
|
|
if ( declManagerLocal.declTypes[i] ) {
|
|
common->Printf( "%s ", declManagerLocal.declTypes[i]->typeName.c_str() );
|
|
}
|
|
}
|
|
common->Printf( "\n" );
|
|
return;
|
|
}
|
|
|
|
for ( i = 0; i < declManagerLocal.declTypes.Num(); i++ ) {
|
|
if ( declManagerLocal.declTypes[i] && declManagerLocal.declTypes[i]->typeName.Icmp( args.Argv( 1 ) ) == 0 ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( i >= declManagerLocal.declTypes.Num() ) {
|
|
common->Printf( "unknown decl type '%s'\n", args.Argv( 1 ) );
|
|
return;
|
|
}
|
|
|
|
const idDecl *decl = declManagerLocal.FindType( (declType_t)i, args.Argv( 2 ), false );
|
|
if ( !decl ) {
|
|
common->Printf( "%s '%s' not found\n", declManagerLocal.declTypes[i]->typeName.c_str(), args.Argv( 2 ) );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===================
|
|
idDeclManagerLocal::FindTypeWithoutParsing
|
|
|
|
This finds or creats the decl, but does not cause a parse. This is only used internally.
|
|
===================
|
|
*/
|
|
idDeclLocal *idDeclManagerLocal::FindTypeWithoutParsing( declType_t type, const char *name, bool makeDefault ) {
|
|
int typeIndex = (int)type;
|
|
int i, hash;
|
|
|
|
if ( typeIndex < 0 || typeIndex >= declTypes.Num() || declTypes[typeIndex] == NULL ) {
|
|
common->FatalError( "idDeclManager::FindTypeWithoutParsing: bad type: %i", typeIndex );
|
|
}
|
|
|
|
char canonicalName[MAX_STRING_CHARS];
|
|
|
|
MakeNameCanonical( name, canonicalName, sizeof( canonicalName ) );
|
|
|
|
// see if it already exists
|
|
hash = hashTables[typeIndex].GenerateKey( canonicalName, false );
|
|
for ( i = hashTables[typeIndex].First( hash ); i >= 0; i = hashTables[typeIndex].Next( i ) ) {
|
|
if ( linearLists[typeIndex][i]->name.Icmp( canonicalName ) == 0 ) {
|
|
// only print these when decl_show is set to 2, because it can be a lot of clutter
|
|
if ( decl_show.GetInteger() > 1 ) {
|
|
MediaPrint( "referencing %s %s\n", declTypes[ type ]->typeName.c_str(), name );
|
|
}
|
|
return linearLists[typeIndex][i];
|
|
}
|
|
}
|
|
|
|
if ( !makeDefault ) {
|
|
return NULL;
|
|
}
|
|
|
|
idDeclLocal *decl = new idDeclLocal;
|
|
decl->self = NULL;
|
|
decl->name = canonicalName;
|
|
decl->type = type;
|
|
decl->declState = DS_UNPARSED;
|
|
decl->textSource = NULL;
|
|
decl->textLength = 0;
|
|
decl->sourceFile = &implicitDecls;
|
|
decl->referencedThisLevel = false;
|
|
decl->everReferenced = false;
|
|
decl->parsedOutsideLevelLoad = !insideLevelLoad;
|
|
|
|
// add it to the linear list and hash table
|
|
decl->index = linearLists[typeIndex].Num();
|
|
hashTables[typeIndex].Add( hash, linearLists[typeIndex].Append( decl ) );
|
|
|
|
return decl;
|
|
}
|
|
|
|
|
|
/*
|
|
====================================================================================
|
|
|
|
idDeclLocal
|
|
|
|
====================================================================================
|
|
*/
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::idDeclLocal
|
|
=================
|
|
*/
|
|
idDeclLocal::idDeclLocal( void ) {
|
|
name = "unnamed";
|
|
textSource = NULL;
|
|
textLength = 0;
|
|
compressedLength = 0;
|
|
sourceFile = NULL;
|
|
sourceTextOffset = 0;
|
|
sourceTextLength = 0;
|
|
sourceLine = 0;
|
|
checksum = 0;
|
|
type = DECL_ENTITYDEF;
|
|
index = 0;
|
|
declState = DS_UNPARSED;
|
|
parsedOutsideLevelLoad = false;
|
|
referencedThisLevel = false;
|
|
everReferenced = false;
|
|
redefinedInReload = false;
|
|
nextInFile = NULL;
|
|
self = NULL;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::GetName
|
|
=================
|
|
*/
|
|
const char *idDeclLocal::GetName( void ) const {
|
|
return name.c_str();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::GetType
|
|
=================
|
|
*/
|
|
declType_t idDeclLocal::GetType( void ) const {
|
|
return type;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::GetState
|
|
=================
|
|
*/
|
|
declState_t idDeclLocal::GetState( void ) const {
|
|
return declState;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::IsImplicit
|
|
=================
|
|
*/
|
|
bool idDeclLocal::IsImplicit( void ) const {
|
|
return ( sourceFile == declManagerLocal.GetImplicitDeclFile() );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::IsValid
|
|
=================
|
|
*/
|
|
bool idDeclLocal::IsValid( void ) const {
|
|
return ( declState != DS_UNPARSED );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::Invalidate
|
|
=================
|
|
*/
|
|
void idDeclLocal::Invalidate( void ) {
|
|
declState = DS_UNPARSED;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::EnsureNotPurged
|
|
=================
|
|
*/
|
|
void idDeclLocal::EnsureNotPurged( void ) {
|
|
if ( declState == DS_UNPARSED ) {
|
|
ParseLocal();
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::Index
|
|
=================
|
|
*/
|
|
int idDeclLocal::Index( void ) const {
|
|
return index;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::GetLineNum
|
|
=================
|
|
*/
|
|
int idDeclLocal::GetLineNum( void ) const {
|
|
return sourceLine;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::GetFileName
|
|
=================
|
|
*/
|
|
const char *idDeclLocal::GetFileName( void ) const {
|
|
return ( sourceFile ) ? sourceFile->fileName.c_str() : "*invalid*";
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::Size
|
|
=================
|
|
*/
|
|
size_t idDeclLocal::Size( void ) const {
|
|
return sizeof( idDecl ) + name.Allocated();
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::GetText
|
|
=================
|
|
*/
|
|
void idDeclLocal::GetText( char *text ) const {
|
|
#ifdef USE_COMPRESSED_DECLS
|
|
HuffmanDecompressText( text, textLength, (byte *)textSource, compressedLength );
|
|
#else
|
|
memcpy( text, textSource, textLength+1 );
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::GetTextLength
|
|
=================
|
|
*/
|
|
int idDeclLocal::GetTextLength( void ) const {
|
|
return textLength;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::SetText
|
|
=================
|
|
*/
|
|
void idDeclLocal::SetText( const char *text ) {
|
|
SetTextLocal( text, idStr::Length( text ) );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::SetTextLocal
|
|
=================
|
|
*/
|
|
void idDeclLocal::SetTextLocal( const char *text, const int length ) {
|
|
|
|
Mem_Free( textSource );
|
|
|
|
checksum = MD5_BlockChecksum( text, length );
|
|
|
|
#ifdef GET_HUFFMAN_FREQUENCIES
|
|
for( int i = 0; i < length; i++ ) {
|
|
huffmanFrequencies[((const unsigned char *)text)[i]]++;
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_COMPRESSED_DECLS
|
|
int maxBytesPerCode = ( maxHuffmanBits + 7 ) >> 3;
|
|
byte *compressed = (byte *)_alloca( length * maxBytesPerCode );
|
|
compressedLength = HuffmanCompressText( text, length, compressed, length * maxBytesPerCode );
|
|
textSource = (char *)Mem_Alloc( compressedLength );
|
|
memcpy( textSource, compressed, compressedLength );
|
|
#else
|
|
compressedLength = length;
|
|
textSource = (char *) Mem_Alloc( length + 1 );
|
|
memcpy( textSource, text, length );
|
|
textSource[length] = '\0';
|
|
#endif
|
|
textLength = length;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::ReplaceSourceFileText
|
|
=================
|
|
*/
|
|
bool idDeclLocal::ReplaceSourceFileText( void ) {
|
|
int oldFileLength, newFileLength;
|
|
char *buffer;
|
|
idFile *file;
|
|
|
|
common->Printf( "Writing \'%s\' to \'%s\'...\n", GetName(), GetFileName() );
|
|
|
|
if ( sourceFile == &declManagerLocal.implicitDecls ) {
|
|
common->Warning( "Can't save implicit declaration %s.", GetName() );
|
|
return false;
|
|
}
|
|
|
|
// get length and allocate buffer to hold the file
|
|
oldFileLength = sourceFile->fileSize;
|
|
newFileLength = oldFileLength - sourceTextLength + textLength;
|
|
buffer = (char *) Mem_Alloc( Max( newFileLength, oldFileLength ) );
|
|
|
|
// read original file
|
|
if ( sourceFile->fileSize ) {
|
|
|
|
file = fileSystem->OpenFileRead( GetFileName() );
|
|
if ( !file ) {
|
|
Mem_Free( buffer );
|
|
common->Warning( "Couldn't open %s for reading.", GetFileName() );
|
|
return false;
|
|
}
|
|
|
|
if ( file->Length() != sourceFile->fileSize || file->Timestamp() != sourceFile->timestamp ) {
|
|
Mem_Free( buffer );
|
|
common->Warning( "The file %s has been modified outside of the engine.", GetFileName() );
|
|
return false;
|
|
}
|
|
|
|
file->Read( buffer, oldFileLength );
|
|
fileSystem->CloseFile( file );
|
|
|
|
if ( MD5_BlockChecksum( buffer, oldFileLength ) != sourceFile->checksum ) {
|
|
Mem_Free( buffer );
|
|
common->Warning( "The file %s has been modified outside of the engine.", GetFileName() );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// insert new text
|
|
char *declText = (char *) _alloca( textLength + 1 );
|
|
GetText( declText );
|
|
memmove( buffer + sourceTextOffset + textLength, buffer + sourceTextOffset + sourceTextLength, oldFileLength - sourceTextOffset - sourceTextLength );
|
|
memcpy( buffer + sourceTextOffset, declText, textLength );
|
|
|
|
// write out new file
|
|
file = fileSystem->OpenFileWrite( GetFileName(), "fs_devpath" );
|
|
if ( !file ) {
|
|
Mem_Free( buffer );
|
|
common->Warning( "Couldn't open %s for writing.", GetFileName() );
|
|
return false;
|
|
}
|
|
file->Write( buffer, newFileLength );
|
|
fileSystem->CloseFile( file );
|
|
|
|
// set new file size, checksum and timestamp
|
|
sourceFile->fileSize = newFileLength;
|
|
sourceFile->checksum = MD5_BlockChecksum( buffer, newFileLength );
|
|
fileSystem->ReadFile( GetFileName(), NULL, &sourceFile->timestamp );
|
|
|
|
// free buffer
|
|
Mem_Free( buffer );
|
|
|
|
// move all decls in the same file
|
|
for ( idDeclLocal *decl = sourceFile->decls; decl; decl = decl->nextInFile ) {
|
|
if (decl->sourceTextOffset > sourceTextOffset) {
|
|
decl->sourceTextOffset += textLength - sourceTextLength;
|
|
}
|
|
}
|
|
|
|
// set new size of text in source file
|
|
sourceTextLength = textLength;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::SourceFileChanged
|
|
=================
|
|
*/
|
|
bool idDeclLocal::SourceFileChanged( void ) const {
|
|
int newLength;
|
|
ID_TIME_T newTimestamp;
|
|
|
|
if ( sourceFile->fileSize <= 0 ) {
|
|
return false;
|
|
}
|
|
|
|
newLength = fileSystem->ReadFile( GetFileName(), NULL, &newTimestamp );
|
|
|
|
if ( newLength != sourceFile->fileSize || newTimestamp != sourceFile->timestamp ) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::MakeDefault
|
|
=================
|
|
*/
|
|
void idDeclLocal::MakeDefault() {
|
|
static int recursionLevel;
|
|
const char *defaultText;
|
|
|
|
declManagerLocal.MediaPrint( "DEFAULTED\n" );
|
|
declState = DS_DEFAULTED;
|
|
|
|
AllocateSelf();
|
|
|
|
defaultText = self->DefaultDefinition();
|
|
|
|
// a parse error inside a DefaultDefinition() string could
|
|
// cause an infinite loop, but normal default definitions could
|
|
// still reference other default definitions, so we can't
|
|
// just dump out on the first recursion
|
|
if ( ++recursionLevel > 100 ) {
|
|
common->FatalError( "idDecl::MakeDefault: bad DefaultDefinition(): %s", defaultText );
|
|
}
|
|
|
|
// always free data before parsing
|
|
self->FreeData();
|
|
|
|
// parse
|
|
self->Parse( defaultText, strlen( defaultText ) );
|
|
|
|
// we could still eventually hit the recursion if we have enough Error() calls inside Parse...
|
|
--recursionLevel;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::SetDefaultText
|
|
=================
|
|
*/
|
|
bool idDeclLocal::SetDefaultText( void ) {
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::DefaultDefinition
|
|
=================
|
|
*/
|
|
const char *idDeclLocal::DefaultDefinition() const {
|
|
return "{ }";
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::Parse
|
|
=================
|
|
*/
|
|
bool idDeclLocal::Parse( const char *text, const int textLength ) {
|
|
idLexer src;
|
|
|
|
src.LoadMemory( text, textLength, GetFileName(), GetLineNum() );
|
|
src.SetFlags( DECL_LEXER_FLAGS );
|
|
src.SkipUntilString( "{" );
|
|
src.SkipBracedSection( false );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::FreeData
|
|
=================
|
|
*/
|
|
void idDeclLocal::FreeData() {
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::List
|
|
=================
|
|
*/
|
|
void idDeclLocal::List() const {
|
|
common->Printf( "%s\n", GetName() );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::Print
|
|
=================
|
|
*/
|
|
void idDeclLocal::Print() const {
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::Reload
|
|
=================
|
|
*/
|
|
void idDeclLocal::Reload( void ) {
|
|
this->sourceFile->Reload( false );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::AllocateSelf
|
|
=================
|
|
*/
|
|
void idDeclLocal::AllocateSelf( void ) {
|
|
if ( self == NULL ) {
|
|
self = declManagerLocal.GetDeclType( (int)type )->allocator();
|
|
self->base = this;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::ParseLocal
|
|
=================
|
|
*/
|
|
void idDeclLocal::ParseLocal( void ) {
|
|
bool generatedDefaultText = false;
|
|
|
|
AllocateSelf();
|
|
|
|
// always free data before parsing
|
|
self->FreeData();
|
|
|
|
declManagerLocal.MediaPrint( "parsing %s %s\n", declManagerLocal.declTypes[type]->typeName.c_str(), name.c_str() );
|
|
|
|
// if no text source try to generate default text
|
|
if ( textSource == NULL ) {
|
|
generatedDefaultText = self->SetDefaultText();
|
|
}
|
|
|
|
// indent for DEFAULTED or media file references
|
|
declManagerLocal.indent++;
|
|
|
|
// no text immediately causes a MakeDefault()
|
|
if ( textSource == NULL ) {
|
|
MakeDefault();
|
|
declManagerLocal.indent--;
|
|
return;
|
|
}
|
|
|
|
declState = DS_PARSED;
|
|
|
|
// parse
|
|
char *declText = (char *) _alloca( ( GetTextLength() + 1 ) * sizeof( char ) );
|
|
GetText( declText );
|
|
self->Parse( declText, GetTextLength() );
|
|
|
|
// free generated text
|
|
if ( generatedDefaultText ) {
|
|
Mem_Free( textSource );
|
|
textSource = 0;
|
|
textLength = 0;
|
|
}
|
|
|
|
declManagerLocal.indent--;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::Purge
|
|
=================
|
|
*/
|
|
void idDeclLocal::Purge( void ) {
|
|
// never purge things that were referenced outside level load,
|
|
// like the console and menu graphics
|
|
if ( parsedOutsideLevelLoad ) {
|
|
return;
|
|
}
|
|
|
|
referencedThisLevel = false;
|
|
MakeDefault();
|
|
|
|
// the next Find() for this will re-parse the real data
|
|
declState = DS_UNPARSED;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
idDeclLocal::EverReferenced
|
|
=================
|
|
*/
|
|
bool idDeclLocal::EverReferenced( void ) const {
|
|
return everReferenced;
|
|
}
|