diff --git a/GNUmakefile b/GNUmakefile index ace1416c8..e1727537b 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -778,6 +778,7 @@ blood_game_objs := \ aizomba.cpp \ aizombf.cpp \ asound.cpp \ + barf.cpp \ callback.cpp \ choke.cpp \ common.cpp \ diff --git a/platform/Windows/nblood.vcxproj b/platform/Windows/nblood.vcxproj index ae97ef0e5..93b802e91 100644 --- a/platform/Windows/nblood.vcxproj +++ b/platform/Windows/nblood.vcxproj @@ -195,6 +195,7 @@ + @@ -299,6 +300,7 @@ + diff --git a/platform/Windows/nblood.vcxproj.filters b/platform/Windows/nblood.vcxproj.filters index 2fba2f06c..1e38b67a6 100644 --- a/platform/Windows/nblood.vcxproj.filters +++ b/platform/Windows/nblood.vcxproj.filters @@ -228,6 +228,9 @@ Source Files + + Source Files + @@ -457,5 +460,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/source/blood/src/barf.cpp b/source/blood/src/barf.cpp new file mode 100644 index 000000000..f548dafc9 --- /dev/null +++ b/source/blood/src/barf.cpp @@ -0,0 +1,952 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 2010-2019 EDuke32 developers and contributors +Copyright (C) 2019 sirlemonhead, Nuke.YKT + +This file is part of NBlood. + +NBlood is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +//------------------------------------------------------------------------- + +#include "compat.h" +#ifdef WITHKPLIB +#include "kplib.h" +#endif +#include "common_game.h" +#include "resource.h" +#include "misc.h" +#include "globals.h" +#include "sound.h" + +static Resource gBarfRes; + +#define kMaxCmdLineDefines 5 +#define kMaxDefines 1000 +#define kMaxParseLevels 5 +static int nCmdDefines = 0; +static int nDefines = 0; + +static int gParseLevel = 0; +int dword_44CE0[kMaxParseLevels] = { 0, 0, 0, 0, 0 }; + +// FIXME +unsigned int nBytes = 0; +char buffer[1024]; +int scriptValue = 0; + +char scriptBuffer[256]; + +struct define_t +{ + char *_text; + int _value; +}; + +define_t gCmdDefines[kMaxCmdLineDefines]; + +void sub_11DF0(char *pzScriptDir, char *fileName, char flags, int ID); +void sub_11C10(char *pzScriptDir, char *fileName, char flags, int ID); + +struct tag_t { + const char *_value; + uint8_t _index; +}; + +enum eTags +{ + kTag0, + kTagEnd, + kTagString, + kTagConstant, + kTag4, // string constant? + kTagComma, + kTagSemiColon, + kTagColon, + kTagEquals, + kTagHash, + kTagComment, + kTagInclude, + kTagResource, + kTagAs, + kTagPreload, + kTagPrelock, + kTagData, + kTagLoad, + kTagEmit, + kTagIfDef, + kTagEndif, + kTagElse +}; + +tag_t tags[] = +{ + { ",", kTagComma }, + { ";", kTagSemiColon }, + { ":", kTagColon }, + { "=", kTagEquals }, + { "#", kTagHash }, + { "//", kTagComment }, + { "INCLUDE", kTagInclude }, + { "RESOURCE", kTagResource }, + { "AS", kTagAs }, + { "PRELOAD", kTagPreload }, + { "PRELOCK", kTagPrelock }, + { "DATA", kTagData }, + { "LOAD", kTagLoad }, + { "EMIT", kTagEmit }, + { "%ifdef", kTagIfDef }, + { "%endif", kTagEndif }, + { "%else", kTagElse } +}; + +const int kTagCount = sizeof(tags) / sizeof(tag_t); + +int qsort_compar(const void *a, const void *b) +{ + return stricmp((const char*)a, (const char*)b); +} + +void SortTags() +{ + qsort(tags, kTagCount, sizeof(tag_t), qsort_compar); +} + +void AddCmdDefine(char *text, int value) +{ + dassert(nCmdDefines < kMaxCmdLineDefines); + + gCmdDefines[nCmdDefines]._text = (char*)Resource::Alloc(strlen(text) + 1); + + strcpy(gCmdDefines[nCmdDefines]._text, text); + gCmdDefines[nCmdDefines]._value = value; + + nCmdDefines++; +} + +// 174 bytes +struct RFS +{ +private: + char *_ptr; // [0] + char _curChar; // [4] + char *_pUnknown2; // [5] - some sort of pointer into _ptr? + char *_pStartLine; // [9] + char *_pEnd; // [13] + char *_pMark; // [17] + char _unknown6; // [21] + int _unknown7; // [22] + int _curLine; // [26] + char _fileName[BMAX_PATH]; // [30] + +public: + void Open(const char *fileName); + void Close(); + void Increment(); + void SkipBeyondValue(char value); + uint8_t GetNextTag(); + void ScriptError(const char *message); + void SetMark(); + void UnsetMark(); +}; + +void RFS::Open(const char *fileName) +{ + strcpy(_fileName, fileName); + + int hFile = open(_fileName, O_BINARY); + if (hFile == -1) { + ThrowError("Error opening file %s", _fileName); + } + + int fileSize = filelength(hFile); + _ptr = (char*)Resource::Alloc(fileSize); + if (_ptr == NULL) { + ThrowError("Not enough memory to read %s", _fileName); + } + + read(hFile, _ptr, fileSize); + close(hFile); + + _curLine = 0; + _pUnknown2 = _ptr; + _curChar = '\n'; + _pEnd = &_ptr[fileSize - 1]; +} + +void RFS::Close() +{ + if (_ptr) { +/* BUG - the original code called nfree but this should be a Resource::Free() + _nfree(_ptr); +*/ + Resource::Free(_ptr); + } +} + +void RFS::Increment() +{ + if (_curChar == '\n') { + _curLine++; + _pStartLine = _pUnknown2; + } + + if (_pUnknown2 >= _pEnd) { + _curChar = 0; + } + else { + _curChar = *_pUnknown2; // grabs the next char + _pUnknown2++; // increment pointer into char data + } +} + +void RFS::SkipBeyondValue(char nVal) +{ + while (_curChar && _curChar != nVal) { + Increment(); + } +} + +void RFS::SetMark() +{ + _pMark = _pUnknown2; + _unknown6 = _curChar; + _unknown7 = _curLine; +} + +// inverse of the above function +void RFS::UnsetMark() +{ + _pUnknown2 = _pMark; + _curChar = _unknown6; + _curLine = _unknown7; +} + +void RFS::ScriptError(const char *message) +{ + // TODO + + char *p = _pStartLine; + while (*p != '\n') + { + if (isprint(*p)) + putchar(*p); + else + putchar(' '); + p++; + } + + putchar('\n'); + + p = _pStartLine; + + while (p < _pMark) + { + putchar(' '); + p++; + } + + puts("^"); + + initprintf("Error in %s line %d: %s\n\n", _fileName, _curLine, message); +} + +uint8_t RFS::GetNextTag() +{ + // skip any space characters + do { + Increment(); + } while (isspace(_curChar)); + + if (_curChar == '\0') { + return kTagEnd; + } + + SetMark(); + + // Path A + if (_curChar == '"') + { + Increment(); + + int i = 0; + + // section 1 + while (1) { + + if (_curChar == '\0' || _curChar == '"') { + scriptBuffer[i] = '\0'; + return kTagString; + } + + if (i == 256) { + ScriptError("String exceeds maximum string length"); + break; + } + + scriptBuffer[i] = _curChar; + i++; + Increment(); + } + + // section 2 + while (1) + { + if (_curChar == '\0' || _curChar == '"') { + return kTag0; + } + + Increment(); + } + } + else + { + scriptValue = 0; + BOOL isNegative = FALSE; // or 'isSigned' ? + + // is it a negative number? + if (_curChar == '-') + { + Increment(); + + isNegative = TRUE; + + if (!isdigit(_curChar)) { + UnsetMark(); + } + } + + if (isdigit(_curChar)) + { + // left path + if (_curChar == '0') + { + Increment(); + + // handle a hex value + if (toupper(_curChar) == 'X') + { + // orange loop + while (1) + { + Increment(); + if (!isxdigit(_curChar)) { // isxdigit() checks for a hex value + break; + } + + // hex version of atoi? + scriptValue *= 16; + if (!isdigit(_curChar)) { + scriptValue += toupper(_curChar) - 55; + } + else { + scriptValue += _curChar - '0'; + } + + SetMark(); + } + + UnsetMark(); + if (isNegative) { + scriptValue = -scriptValue; + } + + return kTagConstant; + } + + UnsetMark(); + } + + // the loop + while (isdigit(_curChar)) + { + // atoi implementation + scriptValue = scriptValue * 10 + _curChar - '0'; + SetMark(); + Increment(); + } + + UnsetMark(); + if (isNegative) { + scriptValue = -scriptValue; + } + + return kTagConstant; + } + else + { + // BLUEISH PATH + int ebp = 0; // v11 + int i = 0; + + // blue loop #1 + while (1) + { + scriptBuffer[ebp] = _curChar; + ebp++; + int eax = -1; + + // blue loop #2 + for (i = 0; i < kTagCount; i++) + { + //if (eax >= 0) { + if (eax == 0) { + break; + } + + // eax = strnicmp(tags[i]._value, scriptBuffer, ebp); + eax = strnicmp(scriptBuffer, tags[i]._value, ebp); + + //if (eax >= 0) { + if (eax == 0) { + break; + } + } + + if (eax > 0 || i == kTagCount) { + break; + } + + if (eax == 0 && strlen(tags[i]._value) == ebp) + { + scriptBuffer[ebp] = 0; + return tags[i]._index; + } + + Increment(); + } + + UnsetMark(); + + i = 0; + + while (isalnum(_curChar)) + { + scriptBuffer[i] = _curChar; + SetMark(); + i++; + Increment(); + } + + UnsetMark(); + scriptBuffer[i] = 0; + return kTag4; + } + } + + // qAssert(1==0); // TODO - what to return here +} + +void ParseScript(char *scriptFileName) +{ + char text[256]; + char char256_1[256]; + char char256_2[256]; + char fileName[BMAX_PATH]; + char inp[BMAX_PATH]; + char zScriptDirectory[BMAX_PATH], zTemp1[BMAX_PATH], zTemp2[BMAX_PATH]; + + SplitPath(scriptFileName, zScriptDirectory, zTemp1, zTemp2); + + RFS rfs; + + // AddExtension(name, ".RFS"); + rfs.Open(scriptFileName); + + gParseLevel = 0; + dword_44CE0[0] = 0; + + BOOL parsing = TRUE; + + while (parsing) + { + // START LOOP. to be fixed later + START: + + uint8_t tag = rfs.GetNextTag(); + + switch (tag) + { + default: + { + break; + } + case kTagEnd: + { + parsing = FALSE; + break; + } + case kTagComment: + { + // skip to next line + rfs.SkipBeyondValue('\n'); + break; + } + case kTagEmit: // minty/light green colour + { + tag = rfs.GetNextTag(); + if (tag != kTag4) + { + rfs.ScriptError("Symbol name expected"); + rfs.SkipBeyondValue(';'); + break; + } + else + { + strcpy(char256_2, scriptBuffer); + tag = rfs.GetNextTag(); + if (tag != kTagEquals) + { + rfs.ScriptError("Missing '='"); + rfs.SkipBeyondValue(';'); + break; + } + + tag = rfs.GetNextTag(); + if (tag != kTagConstant) + { + rfs.ScriptError("Constant expected"); + rfs.SkipBeyondValue(';'); + break; + } + else + { + //AddDefine(char256_2, scriptValue); + rfs.SkipBeyondValue('\n'); + } + } + } + case kTagResource: // really light blue.. + { + if (kTagString != rfs.GetNextTag()) { + rfs.ScriptError("String constant exected"); + rfs.SkipBeyondValue('\n'); + break; + } + + strcpy(inp, scriptBuffer); + char nFlags = 0; + int ID = 0; + BOOL isDefine = FALSE; + + tag = rfs.GetNextTag(); + if (tag == kTagAs) + { + tag = rfs.GetNextTag(); + if (tag == kTag4) + { + strcpy(text, scriptBuffer); + + if (rfs.GetNextTag() != kTagEquals) + { + rfs.ScriptError("Missing '='"); + rfs.SkipBeyondValue(';'); + break; + } + + isDefine = TRUE; + tag = rfs.GetNextTag(); + } + + if (tag != kTagConstant) + { + rfs.ScriptError("Constant expected"); + rfs.SkipBeyondValue(';'); + break; + } + + if (isDefine) { + //AddDefine(text, scriptValue); + } + + ID = scriptValue; + nFlags |= DICT_ID; + tag = rfs.GetNextTag(); + } + + //if (!bNoEncrypt) { + // nFlags |= kResFlagIsEncrypted; + //} + + while (tag == kTagComma) + { + tag = rfs.GetNextTag(); + + if (tag == kTagPreload) { + nFlags |= DICT_LOAD; + } + else if (tag == kTagPrelock) { + nFlags |= DICT_LOCK; + } + else { + rfs.ScriptError("Unrecognized flag"); + rfs.SkipBeyondValue(';'); + goto START; // FIXME + } + + tag = rfs.GetNextTag(); + } + + if (tag != kTagSemiColon) + { + rfs.ScriptError("';' expected"); + rfs.SkipBeyondValue(';'); + break; + } + + if (dword_44CE0[gParseLevel] == 0) { + sub_11C10(zScriptDirectory, inp, nFlags, ID); + } + + break; + } + case kTagIfDef: // purplish colour + { + tag = rfs.GetNextTag(); + if (tag != kTag4) + { + rfs.ScriptError("Parameter error in ifdef"); + rfs.SkipBeyondValue('\n'); + break; + } + else + { + rfs.SetMark(); + strcpy(char256_1, scriptBuffer); + + BOOL bGotDefine = FALSE; + + // check if this was defined via command prompt arguments + for (int i = 0; i < nCmdDefines; i++) + { + if (stricmp(gCmdDefines[i]._text, char256_1) == 0) { // string is equivalent + bGotDefine = TRUE; + break; + } + } + + // loc_11FC3: + gParseLevel++; + dassert(gParseLevel < kMaxParseLevels); + + if (bGotDefine) { + dword_44CE0[gParseLevel] = dword_44CE0[gParseLevel - 1]; + } + else { + dword_44CE0[gParseLevel] = 1; + } + } + break; + } + case kTagElse: // pinky colour + { + if (gParseLevel) + { + // loc_12066: + if (dword_44CE0[gParseLevel - 1] == 0) { + if (dword_44CE0[gParseLevel] == 0) { + dword_44CE0[gParseLevel] = 1; + } + + rfs.SkipBeyondValue('\n'); + break; + } + } + else { + rfs.ScriptError("Unexpected else"); + rfs.SkipBeyondValue('\n'); + break; + } + break; + } + case kTagEndif: // poo coloured + { + if (gParseLevel) { + gParseLevel--; + rfs.SkipBeyondValue('\n'); + break; + } + else + { + rfs.ScriptError("Unexpected Endif"); + rfs.SkipBeyondValue('\n'); + break; + } + } + case kTagHash: // gold colour + { + tag = rfs.GetNextTag(); + if (tag == kTagInclude) + { + tag = rfs.GetNextTag(); + if (tag != kTagString) + { + rfs.ScriptError("String constant exected"); + // why no SkipBeyondValue? + break; + } + else + { + ParseScript(scriptBuffer); + } + } + break; + } + case kTagData: // eg: data "AMB1.SFX" as 1: 80, 0x10000, 0x0, 1, -1, "amb1"; + { + // green coloured section + if (rfs.GetNextTag() != kTagString) { + rfs.ScriptError("String constant expected"); + rfs.SkipBeyondValue(';'); + break; + } + + // eg strcpy(fileName, "AMB1.SFX"); + strcpy(fileName, scriptBuffer); + + char nFlags = 0; + int ID = 0; + + BOOL isDefine = FALSE; + + tag = rfs.GetNextTag(); + + // process an ID section + if (tag == kTagAs) + { + tag = rfs.GetNextTag(); + if (tag == kTag4) + { + strcpy(fileName, scriptBuffer); + + tag = rfs.GetNextTag(); + if (tag != kTagEquals) { + rfs.ScriptError("Missing '='"); + rfs.SkipBeyondValue(';'); + break; + } + + isDefine = TRUE; + tag = rfs.GetNextTag(); + } + + if (tag != kTagConstant) + { + rfs.ScriptError("Constant Expected"); + rfs.SkipBeyondValue(';'); + break; + } + else { + //if (isDefine) { + // AddDefine(fileName, scriptValue); + //} + + nFlags |= DICT_ID; + ID = scriptValue; + tag = rfs.GetNextTag(); + } + } + + if (tag == kTagComma) + { + // process all sections on this line that are comma separated + while (1) + { + tag = rfs.GetNextTag(); + if (tag == kTagPreload) + { + nFlags |= DICT_LOAD; + tag = rfs.GetNextTag(); + + if (tag == kTagComma) { + continue; + } + } + else if (tag == kTagPrelock) + { + nFlags |= DICT_LOCK; + tag = rfs.GetNextTag(); + + if (tag == kTagComma) { + continue; + } + } + else + { + rfs.ScriptError("Unrecognized flag"); + rfs.SkipBeyondValue(';'); + goto START; // FIXME + } + } + } + + // loc_12471: + if (tag != kTagColon) // marked orange in IDA + { + while (1) + { + if (tag == kTagPreload) + { + nFlags |= DICT_LOAD; + tag = rfs.GetNextTag(); + + if (tag == kTagColon) { + break; + } + } + else if (tag == kTagPrelock) + { + nFlags |= DICT_LOCK; + tag = rfs.GetNextTag(); + + if (tag == kTagColon) { + break; + } + } + else { + rfs.ScriptError("':' expected"); + rfs.SkipBeyondValue(';'); + goto START; // FIXME + } + } + } + + nBytes = 0; + + // yellow loop + while (1) + { + tag = rfs.GetNextTag(); + + switch (tag) + { + case kTagString: + { + memcpy(&buffer[nBytes], scriptBuffer, strlen(scriptBuffer) + 1); + nBytes += strlen(scriptBuffer) + 1; + break; + } + case kTagConstant: + { + memcpy(&buffer[nBytes], &scriptValue, sizeof(scriptValue)); + nBytes += sizeof(scriptValue); + break; + } + default: + { + rfs.ScriptError("Constant expected"); + rfs.SkipBeyondValue(';'); + goto START; // FIXME + } + } + + tag = rfs.GetNextTag(); + if (tag != kTagComma) { + break; + } + } + + if (tag != kTagSemiColon) { + rfs.ScriptError("Semicolon expected"); + rfs.SkipBeyondValue(';'); + break; + } + else + { + if (dword_44CE0[gParseLevel] == 0) { + sub_11DF0(zScriptDirectory, fileName, nFlags, ID); + } + } + break; + } + } + } + + //CreateHeader(); + rfs.Close(); +} + +void sub_11C10(char *pzScriptDir, char *fileName, char flags, int ID) +{ + char zDirectory[BMAX_PATH]; + char zFilename[BMAX_PATH]; + char zType[BMAX_PATH]; + BDIR* dirr; + struct Bdirent* dirent; + dirr = Bopendir("./"); + if (dirr) + { + while (dirent = Breaddir(dirr)) + { + if (!Bwildmatch(dirent->name, fileName)) + continue; + + SplitPath(dirent->name, zDirectory, zFilename, zType); + + if (!Bstrcasecmp(zType, "RAW") || !Bstrcasecmp(zType, "SFX") || !Bstrcasecmp(zType, "MID") || !Bstrcasecmp(zType, "TMB")) + gSoundRes.AddExternalResource(zFilename, zType, ID, flags, dirent->name); + else + gSysRes.AddExternalResource(zFilename, zType, ID, flags, dirent->name); + } + Bclosedir(dirr); + } + dirr = Bopendir(pzScriptDir); + if (dirr) + { + while (dirent = Breaddir(dirr)) + { + if (!Bwildmatch(dirent->name, fileName)) + continue; + + SplitPath(dirent->name, zDirectory, zFilename, zType); + + if (!Bstrcasecmp(zType, "RAW") || !Bstrcasecmp(zType, "SFX") || !Bstrcasecmp(zType, "MID") || !Bstrcasecmp(zType, "TMB")) + gSoundRes.AddExternalResource(zFilename, zType, ID, flags, dirent->name); + else + gSysRes.AddExternalResource(zFilename, zType, ID, flags, dirent->name); + } + Bclosedir(dirr); + } +} + +void sub_11DF0(char* pzScriptDir, char *filePath, char flags, int ID) +{ + char zDirectory[BMAX_PATH]; + char zFilename[BMAX_PATH]; + char zType[BMAX_PATH]; + char zFilePath[BMAX_PATH]; + buildvfs_fd handle; + + ConcatPath(pzScriptDir, filePath, zFilePath); + + handle = kopen4loadfrommod(zFilePath, 0); + if (handle == buildvfs_fd_invalid) + { + Bstrcpy(zFilePath, filePath); + handle = kopen4loadfrommod(zFilePath, 0); + if (handle == buildvfs_fd_invalid) + return; + } + + kclose(handle); + + SplitPath(zFilePath, zDirectory, zFilename, zType); + + if (!Bstrcasecmp(zType, "RAW") || !Bstrcasecmp(zType, "SFX") || !Bstrcasecmp(zType, "MID") || !Bstrcasecmp(zType, "TMB")) + gSoundRes.AddExternalResource(zFilename, zType, ID, flags, zFilePath); + else + gSysRes.AddExternalResource(zFilename, zType, ID, flags, zFilePath); +} + diff --git a/source/blood/src/barf.h b/source/blood/src/barf.h new file mode 100644 index 000000000..e93bcc7d3 --- /dev/null +++ b/source/blood/src/barf.h @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 2010-2019 EDuke32 developers and contributors +Copyright (C) 2019 sirlemonhead, Nuke.YKT + +This file is part of NBlood. + +NBlood is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +//------------------------------------------------------------------------- +#pragma once + diff --git a/source/blood/src/misc.cpp b/source/blood/src/misc.cpp index cc3e62503..cce0ce438 100644 --- a/source/blood/src/misc.cpp +++ b/source/blood/src/misc.cpp @@ -131,11 +131,80 @@ void ChangeExtension(char *pzFile, const char *pzExt) _splitpath(pzFile, drive, dir, filename, NULL); _makepath(pzFile, drive, dir, filename, pzExt); #else - char *pDot = strrchr(pzFile, '.'); - if (!pDot) - pDot = pzFile + strlen(pzFile); - else - *pDot = 0; - strcat(pDot, pzExt); + int const nLength = Bstrlen(pzFile); + char * pDot = pzFile+nLength; + for (int i = nLength-1; i >= 0; i--) + { + if (pzFile[i] == '/' || pzFile[i] == '\\') + break; + if (pzFile[i] == '.') + { + pDot = pzFile+i; + break; + } + } + *pDot = '\0'; + Bstrcat(pDot, pzExt); #endif } + +void SplitPath(const char *pzPath, char *pzDirectory, char *pzFile, char *pzType) +{ + int const nLength = Bstrlen(pzFile); + const char *pDirectory = pzFile+nLength; + const char *pDot = NULL; + for (int i = nLength-1; i >= 0; i--) + { + if (pzFile[i] == '/' || pzFile[i] == '\\') + { + Bstrncpy(pzDirectory, pzPath, i); + pzDirectory[i] = 0; + if (!pDot) + { + Bstrcpy(pzFile, pzPath+i+1); + Bstrcpy(pzType, ""); + } + else + { + Bstrncpy(pzFile, pzPath+i+1, pDot-(pzPath+i+1)); + Bstrcpy(pzType, pDot+1); + } + + return; + } + else if (pzFile[i] == '.') + { + pDot = pzFile+i; + } + } + Bstrcpy(pzDirectory, "/"); + if (!pDot) + { + Bstrcpy(pzFile, pzPath); + Bstrcpy(pzType, ""); + } + else + { + Bstrncpy(pzFile, pzPath, pDot-pzPath); + Bstrcpy(pzType, pDot+1); + } +} + +void ConcatPath(const char *pzPath1, const char *pzPath2, char *pzConcatPath) +{ + int n1 = Bstrlen(pzPath1), n2 = Bstrlen(pzPath2); + int i = n1, j = 0; + while (i > 0 && (pzPath1[i-1] == '/' || pzPath1[i-1] == '\\')) + { + i--; + } + while (j < n2 && (pzPath2[j] == '/' || pzPath1[j] == '\\')) + { + j++; + } + Bstrncpy(pzConcatPath, pzPath1, i); + pzConcatPath[i] = 0; + Bstrcpy(pzConcatPath, pzPath2+j); +} + + diff --git a/source/blood/src/misc.h b/source/blood/src/misc.h index ea6cd367b..8e83e82ac 100644 --- a/source/blood/src/misc.h +++ b/source/blood/src/misc.h @@ -30,3 +30,5 @@ unsigned int qrand(void); int wrand(void); void wsrand(int); void ChangeExtension(char *pzFile, const char *pzExt); +void SplitPath(const char *pzPath, char *pzDirectory, char *pzFile, char *pzType); +void ConcatPath(const char* pzPath1, const char* pzPath2, char* pzConcatPath); diff --git a/source/blood/src/resource.cpp b/source/blood/src/resource.cpp index 3c386e6f7..71ae279f4 100644 --- a/source/blood/src/resource.cpp +++ b/source/blood/src/resource.cpp @@ -68,6 +68,8 @@ Resource::~Resource(void) Free(dict[i].type); if (dict[i].name) Free(dict[i].name); + if (dict[i].path) + Free(dict[i].path); } Free(dict); dict = NULL; @@ -149,6 +151,7 @@ void Resource::Init(const char *filename) dict[i].name = (char*)Alloc(nNameLength+1); strncpy(dict[i].type, tdict[i].type, min(3, nTypeLength)); strncpy(dict[i].name, tdict[i].name, min(8, nNameLength)); + dict[i].path = NULL; dict[i].type[nTypeLength] = 0; dict[i].name[nNameLength] = 0; dict[i].id = B_LITTLE32(tdict[i].id); @@ -362,14 +365,20 @@ void Resource::Grow(void) Reindex(); } -void Resource::AddExternalResource(const char *name, const char *type, int id) +void Resource::AddExternalResource(const char *name, const char *type, int id, int flags, const char *pzDirectory) { - char name2[BMAX_PATH], type2[BMAX_PATH], filename[BMAX_PATH*2]; - //if (strlen(name) > 8 || strlen(type) > 3) return; + char name2[BMAX_PATH], type2[BMAX_PATH], filename[BMAX_PATH], path[BMAX_PATH]; + if (Bstrlen(type) > 0) - Bsprintf(filename, "%s.%s", name, type); + Bsnprintf(filename, BMAX_PATH-1, "%s.%s", name, type); else - Bsprintf(filename, "%s", name); + Bsnprintf(filename, BMAX_PATH-1, "%s", name); + + if (pzDirectory) + Bsnprintf(path, BMAX_PATH-1, "%s/%s", pzDirectory, filename); + else + Bstrncpy(path, filename, BMAX_PATH-1); + int fhandle = kopen4loadfrommod(filename, 0); if (fhandle == -1) return; @@ -404,17 +413,25 @@ void Resource::AddExternalResource(const char *name, const char *type, int id) Free(node->name); node->name = NULL; } + if (node->path) + { + Free(node->path); + node->path = NULL; + } int nTypeLength = strlen(type2); int nNameLength = strlen(name2); + int nPathLength = strlen(path); node->type = (char*)Alloc(nTypeLength+1); node->name = (char*)Alloc(nNameLength+1); + node->path = (char*)Alloc(nPathLength+1); strcpy(node->type, type2); strcpy(node->name, name2); + strcpy(node->path, path); } node->size = size; - node->flags |= DICT_EXTERNAL; + node->flags |= DICT_EXTERNAL | flags; Flush(node); - if (id != -1) + if (id >= 0) { index = Probe(id, type2); dassert(index != NULL); @@ -439,15 +456,23 @@ void Resource::AddExternalResource(const char *name, const char *type, int id) Free(node->name); node->name = NULL; } + if (node->path) + { + Free(node->path); + node->path = NULL; + } int nTypeLength = strlen(type2); int nNameLength = strlen(name2); + int nPathLength = strlen(path); node->type = (char*)Alloc(nTypeLength+1); node->name = (char*)Alloc(nNameLength+1); + node->path = (char*)Alloc(nPathLength+1); strcpy(node->type, type2); strcpy(node->name, name2); + strcpy(node->path, path); node->id = id; node->size = size; - node->flags |= DICT_EXTERNAL; + node->flags |= DICT_EXTERNAL | flags; Flush(node); } } @@ -550,7 +575,10 @@ void Resource::Read(DICTNODE *n, void *p) dassert(n != NULL); if (n->flags & DICT_EXTERNAL) { - sprintf(filename, "%s.%s", n->name, n->type); + if (n->path) + Bstrncpy(filename, n->path, BMAX_PATH-1); + else + Bsnprintf(filename, MAX_PATH-1, "%s.%s", n->name, n->type); int fhandle = kopen4loadfrommod(filename, 0); if (fhandle == -1 || (uint32_t)kread(fhandle, p, n->size) != n->size) { @@ -814,6 +842,11 @@ void Resource::RemoveNode(DICTNODE* pNode) Free(pNode->type); pNode->type = NULL; } + if (pNode->path) + { + Free(pNode->path); + pNode->path = NULL; + } *pNode = dict[--count]; Bmemset(&dict[count], 0, sizeof(DICTNODE)); if (pNode->ptr && !pNode->lockCount) diff --git a/source/blood/src/resource.h b/source/blood/src/resource.h index 18359291e..d5d5dcdcc 100644 --- a/source/blood/src/resource.h +++ b/source/blood/src/resource.h @@ -76,6 +76,7 @@ struct DICTNODE : CACHENODE //char name[8]; char *type; char *name; + char *path; unsigned int id; }; @@ -92,7 +93,7 @@ public: DICTNODE **Probe(unsigned int id, const char *type); void Reindex(void); void Grow(void); - void AddExternalResource(const char *name, const char *type, int id = -1); + void AddExternalResource(const char *name, const char *type, int id = 0, int flags = 0, const char* pzDirectory = NULL); static void *Alloc(int nSize); static void Free(void *p); DICTNODE *Lookup(const char *name, const char *type);