/*
===========================================================================
IceTech GPL Source Code
Copyright (C) 2026 Justin Marshall
This file is part of the IceTech GPL Source Code (?IceTech Source Code?).
IceTech 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.
IceTech 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 IceTech Source Code. If not, see .
In addition, the IceTech 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 IceTech 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 Justin Marshall, justinmarshall20@gmail.com
===========================================================================
*/
#include "precompiled.h"
#pragma hdrstop
/*
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;
};
#ifdef QUAKE4
// jmarshall: Quake 4 guide/template support
struct rvGuideTemplate {
idStr name;
idList parms;
idStr body;
bool inlineGuide;
};
#endif
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);
#ifdef QUAKE4
// Quake 4 guide/template support
virtual void ParseGuides(void);
virtual void ShutdownGuides(void);
virtual bool EvaluateGuide(idStr& name, idLexer* src, idStr& definition);
virtual bool EvaluateInlineGuide(idStr& name, idStr& definition);
#endif
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);
void RegisterDeclSubFolder(const char* folder, const char* extension, idList& fileList);
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);
virtual const class idDeclTable* FindTable(const char* name, bool makeDefault = true);
#ifdef QUAKE4
virtual const class rvDeclMatType* FindMaterialType(const char* name, bool makeDefault = true);
virtual const class rvDeclLipSync* FindLipSync(const char* name, bool makeDefault = true);
virtual const class rvDeclPlayback* FindPlayback(const char* name, bool makeDefault = true);
virtual const class rvDeclEffect* FindEffect(const char* name, bool makeDefault = true);
virtual const class rvDeclMatType* MaterialTypeByIndex(int index, bool forceParse = true);
virtual const class rvDeclLipSync* LipSyncByIndex(int index, bool forceParse = true);
virtual const class rvDeclPlayback* PlaybackByIndex(int index, bool forceParse = true);
virtual const class rvDeclEffect* EffectByIndex(int index, bool forceParse = true);
#endif
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; }
#ifdef QUAKE4
idList guides;
#endif
private:
idList declTypes;
idList declFolders;
idList loadedFiles;
idHashIndex hashTables[DECL_MAX_TYPES];
idList 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;
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 = "";
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;
}
#ifdef QUAKE4
bool guide = false;
if (token.Icmp("guide") == 0) {
guide = true;
if (!src.ReadToken(&token)) {
src.Warning("Type without definition at end of file");
break;
}
}
#endif
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] 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] 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;
idStr declDefinition;
bool useExpandedDefinition = false;
#ifdef QUAKE4
if (guide) {
if (!declManagerLocal.EvaluateGuide(name, &src, declDefinition)) {
continue;
}
declManagerLocal.EvaluateInlineGuide(name, declDefinition);
useExpandedDefinition = true;
size = declDefinition.Length();
}
else
#endif
{
// 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;
#ifdef QUAKE4
declDefinition = idStr(buffer).Mid(startMarker, size);
if (declManagerLocal.EvaluateInlineGuide(name, declDefinition)) {
useExpandedDefinition = true;
size = declDefinition.Length();
}
#endif
}
// 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;
}
#ifdef QUAKE4
if (useExpandedDefinition) {
newDecl->SetTextLocal(declDefinition.c_str(), size);
}
else
#endif
{
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
#ifdef QUAKE4
ParseGuides();
#endif
// decls used throughout the engine
RegisterDeclType("table", DECL_TABLE, idDeclAllocator);
RegisterDeclType("material", DECL_MATERIAL, idDeclAllocator);
RegisterDeclType("skin", DECL_SKIN, idDeclAllocator);
RegisterDeclType("sound", DECL_SOUND, idDeclAllocator);
RegisterDeclType("entityDef", DECL_ENTITYDEF, idDeclAllocator);
RegisterDeclType("mapDef", DECL_MAPDEF, idDeclAllocator);
RegisterDeclType("fx", DECL_FX, idDeclAllocator);
RegisterDeclType("particle", DECL_PARTICLE, idDeclAllocator);
RegisterDeclType("articulatedFigure", DECL_AF, idDeclAllocator);
RegisterDeclType("pda", DECL_PDA, idDeclAllocator);
RegisterDeclType("email", DECL_EMAIL, idDeclAllocator);
RegisterDeclType("video", DECL_VIDEO, idDeclAllocator);
RegisterDeclType("audio", DECL_AUDIO, idDeclAllocator);
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, CMD_FL_SYSTEM, "lists tables", idCmdSystem::ArgCompletion_String);
cmdSystem->AddCommand("listMaterials", idListDecls_f, CMD_FL_SYSTEM, "lists materials", idCmdSystem::ArgCompletion_String);
cmdSystem->AddCommand("listSkins", idListDecls_f, CMD_FL_SYSTEM, "lists skins", idCmdSystem::ArgCompletion_String);
cmdSystem->AddCommand("listSoundShaders", idListDecls_f, CMD_FL_SYSTEM, "lists sound shaders", idCmdSystem::ArgCompletion_String);
cmdSystem->AddCommand("listEntityDefs", idListDecls_f, CMD_FL_SYSTEM, "lists entity defs", idCmdSystem::ArgCompletion_String);
cmdSystem->AddCommand("listFX", idListDecls_f, CMD_FL_SYSTEM, "lists FX systems", idCmdSystem::ArgCompletion_String);
cmdSystem->AddCommand("listParticles", idListDecls_f, CMD_FL_SYSTEM, "lists particle systems", idCmdSystem::ArgCompletion_String);
cmdSystem->AddCommand("listAF", idListDecls_f, CMD_FL_SYSTEM, "lists articulated figures", idCmdSystem::ArgCompletion_String);
cmdSystem->AddCommand("listPDAs", idListDecls_f, CMD_FL_SYSTEM, "lists PDAs", idCmdSystem::ArgCompletion_String);
cmdSystem->AddCommand("listEmails", idListDecls_f, CMD_FL_SYSTEM, "lists Emails", idCmdSystem::ArgCompletion_String);
cmdSystem->AddCommand("listVideos", idListDecls_f, CMD_FL_SYSTEM, "lists Videos", idCmdSystem::ArgCompletion_String);
cmdSystem->AddCommand("listAudios", idListDecls_f, CMD_FL_SYSTEM, "lists Audios", idCmdSystem::ArgCompletion_String);
cmdSystem->AddCommand("printTable", idPrintDecls_f, CMD_FL_SYSTEM, "prints a table", idCmdSystem::ArgCompletion_Decl);
cmdSystem->AddCommand("printMaterial", idPrintDecls_f, CMD_FL_SYSTEM, "prints a material", idCmdSystem::ArgCompletion_Decl);
cmdSystem->AddCommand("printSkin", idPrintDecls_f, CMD_FL_SYSTEM, "prints a skin", idCmdSystem::ArgCompletion_Decl);
cmdSystem->AddCommand("printSoundShader", idPrintDecls_f, CMD_FL_SYSTEM, "prints a sound shader", idCmdSystem::ArgCompletion_Decl);
cmdSystem->AddCommand("printEntityDef", idPrintDecls_f, CMD_FL_SYSTEM, "prints an entity def", idCmdSystem::ArgCompletion_Decl);
cmdSystem->AddCommand("printFX", idPrintDecls_f, CMD_FL_SYSTEM, "prints an FX system", idCmdSystem::ArgCompletion_Decl);
cmdSystem->AddCommand("printParticle", idPrintDecls_f, CMD_FL_SYSTEM, "prints a particle system", idCmdSystem::ArgCompletion_Decl);
cmdSystem->AddCommand("printAF", idPrintDecls_f, CMD_FL_SYSTEM, "prints an articulated figure", idCmdSystem::ArgCompletion_Decl);
cmdSystem->AddCommand("printPDA", idPrintDecls_f, CMD_FL_SYSTEM, "prints an PDA", idCmdSystem::ArgCompletion_Decl);
cmdSystem->AddCommand("printEmail", idPrintDecls_f, CMD_FL_SYSTEM, "prints an Email", idCmdSystem::ArgCompletion_Decl);
cmdSystem->AddCommand("printVideo", idPrintDecls_f, CMD_FL_SYSTEM, "prints a Audio", idCmdSystem::ArgCompletion_Decl);
cmdSystem->AddCommand("printAudio", idPrintDecls_f, CMD_FL_SYSTEM, "prints an Video", idCmdSystem::ArgCompletion_Decl);
cmdSystem->AddCommand("listHuffmanFrequencies", ListHuffmanFrequencies_f, CMD_FL_SYSTEM, "lists decl text character frequencies");
common->Printf("------------------------------\n");
}
/*
===================
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 QUAKE4
ShutdownGuides();
#endif
#ifdef USE_COMPRESSED_DECLS
ShutdownHuffman();
#endif
}
#ifdef QUAKE4
// jmarshall: Quake 4 Guide Support
/*
=========================
idDeclManagerLocal::ParseGuides
=========================
*/
void idDeclManagerLocal::ParseGuides(void) {
idFileList* fileList = fileSystem->ListFiles("guides", ".guide");
guides.Clear();
common->Printf("Parsing Guides...\n");
for (int i = 0; i < fileList->GetNumFiles(); i++) {
idLexer src;
idToken token;
idStr fileName = fileList->GetList()[i];
if (!src.LoadFile(va("guides/%s", fileName.c_str()))) {
continue;
}
src.SetFlags(DECL_LEXER_FLAGS);
while (!src.EndOfFile()) {
if (!src.ReadToken(&token) || token.Length() <= 0) {
break;
}
if (token == "guide" || token == "inlineGuide") {
rvGuideTemplate guide;
guide.inlineGuide = (token == "inlineGuide");
if (!src.ReadToken(&token)) {
src.Warning("Guide without name in %s", fileName.c_str());
break;
}
guide.name = token;
src.ExpectTokenString("(");
while (!src.EndOfFile()) {
if (!src.ReadToken(&token)) {
break;
}
if (token == ")") {
break;
}
if (token == ",") {
continue;
}
guide.parms.Append(token);
}
src.ParseBracedSection(guide.body);
guides.Append(guide);
}
else {
src.Error("Unexpected token in guide %s\n", token.c_str());
}
}
}
common->Printf("Found %d guides...\n", guides.Num());
fileSystem->FreeFileList(fileList);
}
/*
=========================
idDeclManagerLocal::ShutdownGuides
=========================
*/
void idDeclManagerLocal::ShutdownGuides(void) {
guides.Clear();
}
static rvGuideTemplate* FindGuideTemplate(idList& guides, const char* name, bool inlineOnly) {
for (int i = 0; i < guides.Num(); i++) {
if (guides[i].name.Icmp(name) == 0) {
if (!inlineOnly || guides[i].inlineGuide) {
return &guides[i];
}
}
}
return NULL;
}
static void StripGuideBraces(idStr& definition) {
int first = definition.Find('{');
if (first == -1) {
return;
}
int last = -1;
for (int i = definition.Length() - 1; i > first; i--) {
if (definition[i] == '}') {
last = i;
break;
}
}
if (last <= first) {
return;
}
definition = definition.Mid(first + 1, last - first - 1);
}
static bool ExpandGuideTemplate(rvGuideTemplate* guide, idLexer* src, idStr& definition, const char* declName, bool stripBraces) {
idToken token;
idList args;
if (!src->ExpectTokenString("(")) {
return false;
}
while (src->ReadToken(&token)) {
if (token == ")") {
break;
}
if (token == ",") {
continue;
}
args.Append(token);
}
if (args.Num() != guide->parms.Num()) {
src->Warning("Guide '%s' expects %d parms but got %d in '%s'", guide->name.c_str(), guide->parms.Num(), args.Num(), declName);
}
definition = guide->body;
if (stripBraces) {
StripGuideBraces(definition);
}
for (int i = 0; i < guide->parms.Num() && i < args.Num(); i++) {
definition.Replace(guide->parms[i].c_str(), args[i].c_str());
}
return true;
}
bool idDeclManagerLocal::EvaluateGuide(idStr& name, idLexer* src, idStr& definition) {
idToken guideName;
if (!src->ReadToken(&guideName)) {
return false;
}
rvGuideTemplate* guide = FindGuideTemplate(guides, guideName.c_str(), false);
if (!guide) {
src->Warning("Guide name '%s' not found in '%s'", guideName.c_str(), name.c_str());
src->SkipRestOfLine();
return false;
}
return ExpandGuideTemplate(guide, src, definition, name.c_str(), false);
}
bool idDeclManagerLocal::EvaluateInlineGuide(idStr& name, idStr& definition) {
bool replaced = false;
while (true) {
idLexer lexer;
if (!lexer.LoadMemory(definition.c_str(), definition.Length(), name.c_str())) {
return replaced;
}
lexer.SetFlags(DECL_LEXER_FLAGS);
idToken token;
int tokenStart = 0;
bool found = false;
while (!lexer.EndOfFile()) {
tokenStart = lexer.GetFileOffset();
if (!lexer.ReadToken(&token)) {
break;
}
if (token.Icmp("inlineGuide") == 0) {
found = true;
break;
}
}
if (!found) {
break;
}
idToken guideName;
if (!lexer.ReadToken(&guideName)) {
break;
}
rvGuideTemplate* guide = FindGuideTemplate(guides, guideName.c_str(), true);
if (!guide) {
lexer.Warning("Guide name '%s' not found in '%s'", guideName.c_str(), name.c_str());
return replaced;
}
idStr expanded;
if (!ExpandGuideTemplate(guide, &lexer, expanded, name.c_str(), true)) {
return replaced;
}
int tokenEnd = lexer.GetFileOffset();
idStr prefix = definition.Mid(0, tokenStart);
idStr suffix = definition.Mid(tokenEnd, definition.Length() - tokenEnd);
definition = prefix + expanded + suffix;
replaced = true;
}
return replaced;
}
#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;
}
/*
===================
RegisterDeclSubFolder
===================
*/
// jmarshall
void idDeclManagerLocal::RegisterDeclSubFolder(const char* folder, const char* extension, idList& fileList) {
// Find all
{
idFileList* list = fileSystem->ListFiles(folder, extension, true);
for (int d = 0; d < list->GetNumFiles(); d++)
{
fileList.Append(va("%s/%s", folder, list->GetFile(d)));
}
fileSystem->FreeFileList(list);
}
idFileList* dirList = fileSystem->ListFiles(folder, "/", true);
for (int i = 0; i < dirList->GetNumFiles(); i++)
{
idStr dir = va("%s/%s", folder, dirList->GetFile(i));
RegisterDeclSubFolder(dir, extension, fileList);
}
fileSystem->FreeFileList(dirList);
}
// jmarshall end
/*
===================
idDeclManagerLocal::RegisterDeclFolder
===================
*/
void idDeclManagerLocal::RegisterDeclFolder(const char* folder, const char* extension, declType_t defaultType) {
int i, j;
idStr fileName;
idDeclFolder* declFolder;
// jmarshall - decls subfolders
idList fileList;
// jmarshall end
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);
}
// jmarshall - decls subfolders
RegisterDeclSubFolder(declFolder->folder, declFolder->extension, fileList);
// jmarshall end
// load and parse decl files
for (i = 0; i < fileList.Num(); i++) {
// jmarshall
//fileName = declFolder->folder + "/" + fileList[ i ];
fileName = fileList[i];
// jmarshall end
// 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();
}
}
/*
===================
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 [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("\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(FindType(DECL_MATERIAL, name, makeDefault));
}
const idMaterial* idDeclManagerLocal::MaterialByIndex(int index, bool forceParse) {
return static_cast(DeclByIndex(DECL_MATERIAL, index, forceParse));
}
/********************************************************************/
const idDeclSkin* idDeclManagerLocal::FindSkin(const char* name, bool makeDefault) {
return static_cast(FindType(DECL_SKIN, name, makeDefault));
}
const idDeclSkin* idDeclManagerLocal::SkinByIndex(int index, bool forceParse) {
return static_cast(DeclByIndex(DECL_SKIN, index, forceParse));
}
/********************************************************************/
const idSoundShader* idDeclManagerLocal::FindSound(const char* name, bool makeDefault) {
return static_cast(FindType(DECL_SOUND, name, makeDefault));
}
const idSoundShader* idDeclManagerLocal::SoundByIndex(int index, bool forceParse) {
return static_cast(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 \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;
}
/*
=================
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;
}
const idDeclTable* idDeclManagerLocal::FindTable(const char* name, bool makeDefault) {
return (const idDeclTable*)FindType(DECL_TABLE, name, makeDefault);
}
#ifdef QUAKE4
const rvDeclMatType* idDeclManagerLocal::FindMaterialType(const char* name, bool makeDefault) {
return (const rvDeclMatType*)FindType(DECL_MATERIALTYPE, name, makeDefault);
}
const rvDeclLipSync* idDeclManagerLocal::FindLipSync(const char* name, bool makeDefault) {
return (const rvDeclLipSync*)FindType(DECL_LIPSYNC, name, makeDefault);
}
const rvDeclPlayback* idDeclManagerLocal::FindPlayback(const char* name, bool makeDefault) {
return (const rvDeclPlayback*)FindType(DECL_PLAYBACK, name, makeDefault);
}
const rvDeclEffect* idDeclManagerLocal::FindEffect(const char* name, bool makeDefault) {
return (const rvDeclEffect*)FindType(DECL_EFFECT, name, makeDefault);
}
const rvDeclMatType* idDeclManagerLocal::MaterialTypeByIndex(int index, bool forceParse) {
return (const rvDeclMatType*)DeclByIndex(DECL_MATERIALTYPE, index, forceParse);
}
const rvDeclLipSync* idDeclManagerLocal::LipSyncByIndex(int index, bool forceParse) {
return (const rvDeclLipSync*)DeclByIndex(DECL_LIPSYNC, index, forceParse);
}
const rvDeclPlayback* idDeclManagerLocal::PlaybackByIndex(int index, bool forceParse) {
return (const rvDeclPlayback*)DeclByIndex(DECL_PLAYBACK, index, forceParse);
}
const rvDeclEffect* idDeclManagerLocal::EffectByIndex(int index, bool forceParse) {
return (const rvDeclEffect*)DeclByIndex(DECL_EFFECT, index, forceParse);
}
#endif