diablo2-web/tools/d2moo-oracle/src/stubs.cpp

603 lines
20 KiB
C++

// Runtime environment for D2MOO's D2Common DRLG code inside the oracle.
//
// Policy: anything the DRLG generation path genuinely needs is implemented with real data
// (compiled 1.13c .bin tables, DS1 files). Anything it should never reach FAILS FAST, so a
// stub can never silently change the reference output again (the previous ad-hoc runner
// returned dummy records, empty buffers for missing files and a constant 0 direction).
#include "oracle.h"
#include "Windows.h"
#include "D2DataTbls.h"
#include "Drlg/D2DrlgDrlg.h"
#include "Drlg/D2DrlgPreset.h"
#include "D2Dungeon.h"
#include "D2Seed.h"
#include <D2Unicode.h>
#include <D2CMP.h>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <malloc.h>
#include <map>
#include <string>
#include <vector>
#ifndef D2ORACLE_TRACED_SEED
#error "The oracle must be compiled against tools/d2moo-oracle/include/D2Seed.h"
#endif
static_assert(sizeof(void*) == 4, "Build the oracle with -m32: D2MOO structs must match the 1.13c 32-bit .bin layout");
std::string gsD2OracleDataDir;
static D2DataTablesStrc gDataTables = {};
D2DataTablesStrc* sgptDataTables = &gDataTables;
void D2ORACLE_Fail(const char* szFormat, ...)
{
va_list args;
va_start(args, szFormat);
fprintf(stderr, "[d2moo-oracle] FATAL: ");
vfprintf(stderr, szFormat, args);
fprintf(stderr, "\n");
va_end(args);
exit(2);
}
static std::string Lower(std::string s)
{
for (char& c : s)
{
c = (char)std::tolower((unsigned char)c);
}
return s;
}
static std::vector<uint8_t> ReadWholeFile(const std::string& path)
{
FILE* f = fopen(path.c_str(), "rb");
if (!f)
{
D2ORACLE_Fail("missing data file %s", path.c_str());
}
fseek(f, 0, SEEK_END);
const long len = ftell(f);
fseek(f, 0, SEEK_SET);
std::vector<uint8_t> bytes((size_t)len);
if (len > 0 && fread(bytes.data(), 1, (size_t)len, f) != (size_t)len)
{
D2ORACLE_Fail("short read on %s", path.c_str());
}
fclose(f);
return bytes;
}
// Loads <data>/tables/<name>.bin: int32 record count followed by packed 32-bit records.
static void* LoadBinTable(const char* szName, int* pRecordCount, size_t dwSize)
{
const std::string path = gsD2OracleDataDir + "/tables/" + Lower(szName) + ".bin";
std::vector<uint8_t> bytes = ReadWholeFile(path);
if (bytes.size() < 4)
{
D2ORACLE_Fail("%s is truncated", path.c_str());
}
int32_t nCount = 0;
memcpy(&nCount, bytes.data(), 4);
const size_t nBody = bytes.size() - 4;
if (nCount <= 0 || nBody != (size_t)nCount * dwSize)
{
D2ORACLE_Fail("%s: %d records x %zu bytes != %zu body bytes (struct layout mismatch)", path.c_str(), nCount, dwSize, nBody);
}
void* pRecords = calloc((size_t)nCount, dwSize);
memcpy(pRecords, bytes.data() + 4, nBody);
if (pRecordCount)
{
*pRecordCount = nCount;
}
return pRecords;
}
// ---- Unicode / D2Lang (level names only) ----
Unicode::Unicode(unsigned short v) : ch(v) {}
Unicode& Unicode::operator=(const Unicode& other) { ch = other.ch; return *this; }
Unicode* Unicode::strncpy(Unicode* dst, const Unicode* src, int count)
{
if (!dst) return nullptr;
for (int i = 0; i < count; ++i)
{
dst[i].ch = src ? src[i].ch : 0;
if (!src || src[i].ch == 0) break;
}
return dst;
}
static Unicode gEmptyUnicode[64] = {};
const Unicode* __fastcall D2LANG_GetStringByReferenceString(char*)
{
return gEmptyUnicode;
}
// ---- Fog ----
void* __fastcall FOG_AllocPool(void*, int nSize, const char*, int, int)
{
return calloc(1, nSize > 0 ? nSize : 1);
}
void __fastcall FOG_FreePool(void*, void* pFree, const char*, int, int)
{
free(pFree);
}
// D2_REALLOC_POOL leaves the grown bytes uninitialised in the game. The only caller on the DRLG path,
// DRLGROOMTILE_ReallocRoofTileGrid, never initialises unk0x10 / nIntensity / unk0x2C of the appended
// roof tiles, so the oracle zero-fills everything past the old block (the TypeScript port appends
// zeroed elements). FOG_AllocPool is calloc, which clears the whole usable size, so by induction every
// byte past the requested size is zero.
void* __fastcall FOG_ReallocPool(void*, void* pMem, int nSize, const char*, int, int)
{
const size_t nOldUsable = pMem ? malloc_usable_size(pMem) : 0;
void* pNew = realloc(pMem, nSize > 0 ? nSize : 1);
if (!pNew)
{
D2ORACLE_Fail("FOG_ReallocPool(%d) failed", nSize);
}
const size_t nNewUsable = malloc_usable_size(pNew);
if (nNewUsable > nOldUsable)
{
memset((char*)pNew + nOldUsable, 0, nNewUsable - nOldUsable);
}
return pNew;
}
void* __fastcall FOG_Alloc(int nSize, const char*, int, int)
{
return calloc(1, nSize > 0 ? nSize : 1);
}
void __fastcall FOG_Free(void* pFree, const char*, int, int)
{
free(pFree);
}
void __fastcall FOG_10050_EnterCriticalSection(CRITICAL_SECTION*, int) {}
void FOG_DisplayAssert(const char* msg, const char* file, int line)
{
D2ORACLE_Fail("D2MOO assertion failed: %s (%s:%d)", msg, file, line);
}
void FOG_DisplayHalt(const char* msg, const char* file, int line)
{
D2ORACLE_Fail("D2MOO halt: %s (%s:%d)", msg, file, line);
}
void FOG_DisplayWarning(const char* msg, const char* file, int line)
{
D2ORACLE_Fail("D2MOO verify failed: %s (%s:%d)", msg, file, line);
}
void FOG_Trace(const char* fmt, ...)
{
(void)fmt;
}
const char* FOG_csprintf(char* dst, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
vsprintf(dst, fmt, args);
va_end(args);
return dst;
}
BOOL __stdcall FOG_IsExpansion()
{
return TRUE;
}
// ---- Archive: DS1 files from <data>/mpq ----
void* __fastcall ARCHIVE_AllocateBufferAndReadFile(HD2ARCHIVE, const char* szFile, size_t* pSize, const char*, int)
{
std::string rel = szFile ? szFile : "";
for (char& c : rel)
{
c = (c == '\\') ? '/' : (char)std::tolower((unsigned char)c);
}
std::vector<uint8_t> bytes = ReadWholeFile(gsD2OracleDataDir + "/mpq/" + rel);
void* pBuffer = calloc(1, bytes.size() + 16);
memcpy(pBuffer, bytes.data(), bytes.size());
if (pSize)
{
*pSize = bytes.size();
}
return pBuffer;
}
// ---- Data tables ----
void* __stdcall DATATBLS_CompileTxt(HD2ARCHIVE, const char* szName, D2BinFieldStrc*, int* pRecordCount, size_t dwSize)
{
void* pRecords = LoadBinTable(szName, pRecordCount, dwSize);
if (Lower(szName) == "lvlsub")
{
// DATATBLS_CompileTxt builds LvlSub records from text, so the runtime-only members start
// zeroed; DRLGTILESUB_InitializeDrlgFile relies on pDrlgFile == NULL to load the DS1.
D2LvlSubTxt* pLvlSub = (D2LvlSubTxt*)pRecords;
for (int i = 0; i < *pRecordCount; ++i)
{
pLvlSub[i].pDrlgFile = nullptr;
memset(pLvlSub[i].pTileTypeGrid, 0, sizeof(pLvlSub[i].pTileTypeGrid));
memset(pLvlSub[i].pWallGrid, 0, sizeof(pLvlSub[i].pWallGrid));
memset(&pLvlSub[i].pFloorGrid, 0, sizeof(pLvlSub[i].pFloorGrid));
memset(&pLvlSub[i].pShadowGrid, 0, sizeof(pLvlSub[i].pShadowGrid));
}
}
return pRecords;
}
void __stdcall DATATBLS_UnloadBin(void* pBin)
{
free(pBin);
}
static D2ObjectsTxt* gpObjectsTxt = nullptr;
static int gnObjectsTxtRecordCount = 0;
static int gnSuperUniquesTxtRecordCount = 0;
// Loads the monster tables DRLGPRESET_LoadDrlgFile needs. Only the SuperUniques/MonStats record
// counts are consumed (monster preset id translation, DrlgPreset.cpp:250-275).
static void D2ORACLE_LoadMonsterTables()
{
int nCount = 0;
free(LoadBinTable("monstats", &nCount, sizeof(D2MonStatsTxt)));
sgptDataTables->nMonStatsTxtRecordCount = nCount;
free(LoadBinTable("superuniques", &gnSuperUniquesTxtRecordCount, sizeof(D2SuperUniquesTxt)));
// Same sectioning as DATATBLS_LoadMonPresetTxt (MonsterTbls.cpp:2524-2557).
int nRecordCount = 0;
D2MonPresetTxt* pMonPresetTxt = (D2MonPresetTxt*)LoadBinTable("monpreset", &nRecordCount, sizeof(D2MonPresetTxt));
sgptDataTables->pMonPresetTxt = pMonPresetTxt;
sgptDataTables->pMonPresetTxtActSections[0] = pMonPresetTxt;
int nActRecords = 0;
int nAct = 0;
for (int i = 0; i < nRecordCount; ++i)
{
for (int j = nAct + 1; j < pMonPresetTxt[i].nAct; ++j)
{
sgptDataTables->nMonPresetTxtActRecordCounts[nAct] = nActRecords;
++nAct;
nActRecords = 0;
sgptDataTables->pMonPresetTxtActSections[nAct] = &pMonPresetTxt[i];
}
++nActRecords;
}
sgptDataTables->nMonPresetTxtActRecordCounts[nAct] = nActRecords;
}
// Same relative order as DATATBLS_LoadAllTxts (DataTbls.cpp:856-870): the monster tables are
// loaded by DATATBLS_LoadSomeMonsterTxts (MonsterTbls.cpp:2741-2744) BEFORE the level tables,
// because DATATBLS_LoadLvlSubTxt loads the LvlSub DS1s and translates their monster preset units
// through MonPreset. Objects.txt is loaded after LvlSub.
void D2ORACLE_LoadDataTables()
{
D2ORACLE_LoadMonsterTables();
DATATBLS_LoadLevelsTxt(nullptr);
DATATBLS_LoadLevelDefsBin(nullptr);
DATATBLS_LoadLevelTypesTxt(nullptr);
DATATBLS_LoadLvlPrestTxt(nullptr, 1);
DATATBLS_LoadLvlWarpTxt(nullptr);
DATATBLS_LoadLvlMazeTxt(nullptr);
DATATBLS_LoadLvlSubTxt(nullptr, 1, 0);
gpObjectsTxt = (D2ObjectsTxt*)LoadBinTable("objects", &gnObjectsTxtRecordCount, sizeof(D2ObjectsTxt));
}
D2ObjectsTxt* __stdcall DATATBLS_GetObjectsTxtRecord(int nObjectId)
{
if (nObjectId < 0 || nObjectId >= gnObjectsTxtRecordCount)
{
D2ORACLE_Fail("DATATBLS_GetObjectsTxtRecord(%d) out of range", nObjectId);
}
return &gpObjectsTxt[nObjectId];
}
//D2Common.0x6FD6EF30 (#11256), same body as MonsterTbls.cpp:2560
D2MonPresetTxt* __fastcall DATATBLS_GetMonPresetTxtActSection(int nAct, int* pRecordCount)
{
if (pRecordCount)
{
if (nAct >= 0 && nAct < 5 && sgptDataTables->pMonPresetTxt)
{
*pRecordCount = sgptDataTables->nMonPresetTxtActRecordCounts[nAct];
return sgptDataTables->pMonPresetTxtActSections[nAct];
}
*pRecordCount = 0;
}
return NULL;
}
int __fastcall DATATBLS_GetSuperUniquesTxtRecordCount()
{
return gnSuperUniquesTxtRecordCount;
}
D2ItemsTxt* __stdcall DATATBLS_GetItemRecordFromItemCode(uint32_t, int*)
{
D2ORACLE_Fail("unexpected DATATBLS_GetItemRecordFromItemCode (item preset unit in a DS1)");
}
uint32_t __fastcall DATATBLS_StringToCode(char*)
{
D2ORACLE_Fail("unexpected DATATBLS_StringToCode");
}
D2FieldStrc* __stdcall DATATBLS_AllocField()
{
D2ORACLE_Fail("unexpected DATATBLS_AllocField");
}
void __stdcall DATATBLS_FreeField(D2FieldStrc*)
{
D2ORACLE_Fail("unexpected DATATBLS_FreeField");
}
void __stdcall DATATBLS_SetFieldCoordinates(D2FieldStrc*, int, int)
{
D2ORACLE_Fail("unexpected DATATBLS_SetFieldCoordinates");
}
BOOL __stdcall D2Common_11099(D2FieldStrc*, D2ActiveRoomStrc*, int, int, uint16_t)
{
D2ORACLE_Fail("unexpected D2Common_11099");
}
BOOL __fastcall MONSTERS_ValidateMonsterId(int nMonsterId)
{
return nMonsterId >= 0 && nMonsterId < sgptDataTables->nMonStatsTxtRecordCount ? nMonsterId : -1;
}
// ---- D2CMP: DT1 tile libraries ----
// D2CMP.dll is not reimplemented by D2MOO (its D2CMP sources are placeholders importing the DLL), so
// the tile library functions are implemented here from the DT1 files. They are NOT optional:
// DRLGROOMTILE_GetTileCache (DrlgRoomTile.cpp:45-92) rolls the room seed iff the rarity sum of the
// tiles returned by D2CMP_10088_GetTiles is > 0, and it runs inside the LvlSub stamping of
// InitOutdoorRoomGrids (sub_6FD8ACE0 -> DRLGROOMTILE_InitTileShadow), so the tile data advances the
// substitution RNG stream. Since schema 3 the chosen entry itself is dumped (D2DrlgTileDataStrc::pTile
// as [library path, tile index in that DT1]), so the ORDER of the returned entries (slot order, then DT1
// file order) decides dumped output: which variant DRLGROOMTILE_GetTileCache picks for a given roll.
// That order is NOT verified against the real D2CMP.dll (open question; the returned count and rarity
// sum do not depend on it).
namespace
{
constexpr size_t kDt1HeaderSize = 0x114;
constexpr size_t kDt1TileHeaderSize = 96;
constexpr int kTileSlots = 32; // D2DrlgRoomStrc::pTiles / D2DrlgStrc::pTiles
struct OracleTileLibrary;
// POD: allocated with new + memset(0). pLibrary / nIndex are the oracle's identity of the entry
// (not D2CMP fields), used only by D2ORACLE_DescribeTile.
struct OracleTile
{
D2TileLibraryEntryStrc tEntry;
uint8_t nSubtileFlags[25];
const OracleTileLibrary* pLibrary;
int nIndex;
};
struct OracleTileLibrary
{
D2TileLibraryHashStrc tHash; // identity stored in the pTiles slots
std::string szPath;
std::vector<OracleTile*> tiles;
};
std::map<std::string, OracleTileLibrary*> gTileLibrariesByPath;
std::map<const D2TileLibraryHashStrc*, OracleTileLibrary*> gTileLibrariesByHash;
std::map<const D2TileLibraryEntryStrc*, OracleTile*> gTilesByEntry;
template <typename T>
T ReadLE(const std::vector<uint8_t>& bytes, size_t nOffset)
{
T value;
memcpy(&value, bytes.data() + nOffset, sizeof(T));
return value;
}
OracleTileLibrary* LoadTileLibrary(const char* szFileName)
{
std::string rel = szFileName ? szFileName : "";
for (char& c : rel)
{
c = (c == '\\') ? '/' : (char)std::tolower((unsigned char)c);
}
auto it = gTileLibrariesByPath.find(rel);
if (it != gTileLibrariesByPath.end())
{
return it->second;
}
const std::string path = gsD2OracleDataDir + "/mpq/" + rel;
const std::vector<uint8_t> bytes = ReadWholeFile(path);
if (bytes.size() < kDt1HeaderSize)
{
D2ORACLE_Fail("%s: truncated DT1 header", path.c_str());
}
const int32_t nVersion1 = ReadLE<int32_t>(bytes, 0x00);
const int32_t nVersion2 = ReadLE<int32_t>(bytes, 0x04);
const int32_t nTiles = ReadLE<int32_t>(bytes, 0x10C);
const int32_t nTileStart = ReadLE<int32_t>(bytes, 0x110);
if (nVersion1 != 7 || nVersion2 != 6)
{
D2ORACLE_Fail("%s: unsupported DT1 version %d.%d", path.c_str(), nVersion1, nVersion2);
}
if (nTiles < 0 || nTileStart < 0 || (size_t)nTileStart + (size_t)nTiles * kDt1TileHeaderSize > bytes.size())
{
D2ORACLE_Fail("%s: %d tile headers at 0x%X exceed the file", path.c_str(), nTiles, nTileStart);
}
OracleTileLibrary* pLibrary = new OracleTileLibrary();
memset(&pLibrary->tHash, 0, sizeof(pLibrary->tHash));
pLibrary->szPath = rel;
for (int i = 0; i < nTiles; ++i)
{
const size_t nBase = (size_t)nTileStart + (size_t)i * kDt1TileHeaderSize;
OracleTile* pTile = new OracleTile();
memset(pTile, 0, sizeof(*pTile));
pTile->tEntry.nLightDirection = ReadLE<int32_t>(bytes, nBase + 0x00);
pTile->tEntry.nRoofHeight = ReadLE<uint16_t>(bytes, nBase + 0x04);
pTile->tEntry.nFlags = ReadLE<uint16_t>(bytes, nBase + 0x06);
pTile->tEntry.nTotalHeight = ReadLE<int32_t>(bytes, nBase + 0x08);
pTile->tEntry.nWidth = ReadLE<int32_t>(bytes, nBase + 0x0C);
pTile->tEntry.nHeightToBottom = ReadLE<int32_t>(bytes, nBase + 0x10);
pTile->tEntry.nType = ReadLE<int32_t>(bytes, nBase + 0x14);
pTile->tEntry.nStyle = ReadLE<int32_t>(bytes, nBase + 0x18);
pTile->tEntry.nSequence = ReadLE<int32_t>(bytes, nBase + 0x1C);
pTile->tEntry.nRarity_Frame = ReadLE<int32_t>(bytes, nBase + 0x20);
memcpy(pTile->nSubtileFlags, bytes.data() + nBase + 0x28, sizeof(pTile->nSubtileFlags));
pTile->pLibrary = pLibrary;
pTile->nIndex = i;
pLibrary->tiles.push_back(pTile);
gTilesByEntry[&pTile->tEntry] = pTile;
}
gTileLibrariesByPath[rel] = pLibrary;
gTileLibrariesByHash[&pLibrary->tHash] = pLibrary;
return pLibrary;
}
OracleTile* TileOf(const D2TileLibraryEntryStrc* pEntry)
{
auto it = gTilesByEntry.find(pEntry);
if (it == gTilesByEntry.end())
{
D2ORACLE_Fail("D2CMP accessor called with a tile the oracle did not load");
}
return it->second;
}
} // namespace
void D2ORACLE_DescribeTile(const D2TileLibraryEntryStrc* pTileLibraryEntry, std::string* pszLibrary, int* pnIndex)
{
const OracleTile* pTile = TileOf(pTileLibraryEntry);
*pszLibrary = pTile->pLibrary->szPath;
*pnIndex = pTile->nIndex;
}
//D2Cmp.#10087 (1.13c D2CMP.dll 0x6fe25480): stores the library in the first free slot of the 32-slot
// array without deduplicating (e.g. LvlTypes.txt row 21 Act 3 Jungle lists Bridge.dt1 in slots 0 and 8).
void __stdcall D2CMP_10087_LoadTileLibrarySlot(D2TileLibraryHashStrc** ppTileLibraryHash, const char* szFileName)
{
OracleTileLibrary* pLibrary = LoadTileLibrary(szFileName);
for (int i = 0; i < kTileSlots; ++i)
{
if (!ppTileLibraryHash[i])
{
ppTileLibraryHash[i] = &pLibrary->tHash;
return;
}
}
D2ORACLE_Fail("no free tile library slot for %s", pLibrary->szPath.c_str());
}
//D2Cmp.#10088
int __stdcall D2CMP_10088_GetTiles(D2TileLibraryHashStrc** ppTileLibraryHash, int nType, int nStyle, int nSequence, D2TileLibraryEntryStrc** pTileList, int nTileListSize)
{
int nCount = 0;
int nRaritySum = 0;
for (int i = 0; i < kTileSlots && ppTileLibraryHash[i]; ++i)
{
auto it = gTileLibrariesByHash.find(ppTileLibraryHash[i]);
if (it == gTileLibrariesByHash.end())
{
D2ORACLE_Fail("D2CMP_10088_GetTiles: slot %d holds a library the oracle did not load", i);
}
for (OracleTile* pTile : it->second->tiles)
{
if (pTile->tEntry.nType != nType || pTile->tEntry.nStyle != nStyle || pTile->tEntry.nSequence != nSequence)
{
continue;
}
if (nCount >= nTileListSize)
{
// Which entries D2CMP keeps when truncating depends on its internal order.
D2ORACLE_Fail("D2CMP_10088_GetTiles(%d,%d,%d): more than %d matching tiles", nType, nStyle, nSequence, nTileListSize);
}
pTileList[nCount++] = &pTile->tEntry;
nRaritySum += pTile->tEntry.nRarity_Frame;
}
}
D2ORACLE_TraceLine("T %d %d %d %d %d", nType, nStyle, nSequence, nCount, nRaritySum);
return nCount;
}
//D2Cmp.#10078
int __stdcall D2CMP_10078_GetTileStyle(D2TileLibraryEntryStrc* pTileLibraryEntry)
{
return TileOf(pTileLibraryEntry)->tEntry.nStyle;
}
//D2Cmp.#10079
int __stdcall D2CMP_10079_GetTileFlags(D2TileLibraryEntryStrc* pTileLibraryEntry)
{
return TileOf(pTileLibraryEntry)->tEntry.nFlags;
}
//D2Cmp.#10081
int __stdcall D2CMP_10081_GetTileRarity(D2TileLibraryEntryStrc* pTileLibraryEntry)
{
return TileOf(pTileLibraryEntry)->tEntry.nRarity_Frame;
}
//D2Cmp.#10082
int __stdcall D2CMP_10082_GetTileSequence(D2TileLibraryEntryStrc* pTileLibraryEntry)
{
return TileOf(pTileLibraryEntry)->tEntry.nSequence;
}
//D2Cmp.#10085: the 25 sub-tile collision flags.
uint8_t* __stdcall D2CMP_10085_GetTileFlagArray(D2TileLibraryEntryStrc* pTileLibraryEntry)
{
return TileOf(pTileLibraryEntry)->nSubtileFlags;
}
// ---- D2Dungeon ----
//D2Common.0x6FD8D6F0, same body as D2Dungeon.cpp:1323
void __stdcall DUNGEON_GameTileToSubtileCoords(int* pX, int* pY)
{
*pX *= 5;
*pY *= 5;
}
//Same body as D2Dungeon.cpp:1293
void __stdcall DUNGEON_GameTileToClientCoords(int* pX, int* pY)
{
int nX = *pX;
int nY = *pY;
*pX = 80 * (nX - nY);
*pY = (80 * (nX + nY)) / 2;
}
D2ActiveRoomStrc* __fastcall DUNGEON_AllocRoom(D2DrlgActStrc*, D2DrlgRoomStrc*, D2DrlgCoordsStrc*, D2DrlgRoomTilesStrc*, int, uint32_t)
{
D2ORACLE_Fail("unexpected DUNGEON_AllocRoom");
}
BOOL __stdcall DUNGEON_AreSubtileCoordinatesInsideRoom(D2DrlgCoordsStrc*, int, int)
{
D2ORACLE_Fail("unexpected DUNGEON_AreSubtileCoordinatesInsideRoom");
}
void __stdcall DUNGEON_GetAdjacentRoomsListFromRoom(D2ActiveRoomStrc*, D2ActiveRoomStrc***, int*)
{
D2ORACLE_Fail("unexpected DUNGEON_GetAdjacentRoomsListFromRoom");
}
D2RoomCollisionGridStrc* __stdcall DUNGEON_GetCollisionGridFromRoom(D2ActiveRoomStrc*)
{
D2ORACLE_Fail("unexpected DUNGEON_GetCollisionGridFromRoom");
}
void __stdcall DUNGEON_SetCollisionGridInRoom(D2ActiveRoomStrc*, D2RoomCollisionGridStrc*)
{
D2ORACLE_Fail("unexpected DUNGEON_SetCollisionGridInRoom");
}
D2DrlgTileDataStrc* __stdcall DUNGEON_GetFloorTilesFromRoom(D2ActiveRoomStrc*, int*)
{
D2ORACLE_Fail("unexpected DUNGEON_GetFloorTilesFromRoom");
}
D2DrlgTileDataStrc* __stdcall DUNGEON_GetWallTilesFromRoom(D2ActiveRoomStrc*, int*)
{
D2ORACLE_Fail("unexpected DUNGEON_GetWallTilesFromRoom");
}
D2DrlgTileDataStrc* __stdcall DUNGEON_GetRoofTilesFromRoom(D2ActiveRoomStrc*, int*)
{
D2ORACLE_Fail("unexpected DUNGEON_GetRoofTilesFromRoom");
}
void __stdcall DUNGEON_GetRoomCoordinates(D2ActiveRoomStrc*, D2DrlgCoordsStrc*)
{
D2ORACLE_Fail("unexpected DUNGEON_GetRoomCoordinates");
}
D2DrlgRoomStrc* __stdcall DUNGEON_GetRoomExFromRoom(D2ActiveRoomStrc*)
{
D2ORACLE_Fail("unexpected DUNGEON_GetRoomExFromRoom");
}
void __fastcall DUNGEON_RemoveRoomFromAct(D2DrlgActStrc*, D2ActiveRoomStrc*)
{
D2ORACLE_Fail("unexpected DUNGEON_RemoveRoomFromAct");
}