Tons of save-related fixes

This commit is contained in:
Jack Andersen 2017-02-07 20:48:43 -10:00
parent 356788fb9e
commit b8613d61f8
17 changed files with 219 additions and 95 deletions

View File

@ -884,6 +884,7 @@ void ProjectResourceFactoryBase::EnumerateNamedResources(
std::this_thread::sleep_for(std::chrono::milliseconds(2));
lk.lock();
}
lk.unlock();
for (const auto& pair : m_catalogNameToTag)
{
if (!lambda(pair.first, pair.second))

View File

@ -63,8 +63,8 @@ public:
ResId TranslateOriginalToNew(ResId id) const
{
auto search = std::lower_bound(m_origToNew.cbegin(), m_origToNew.cend(),
std::make_pair(id, ResId(0)));
auto search = std::lower_bound(m_origToNew.cbegin(), m_origToNew.cend(), id,
[](const auto& id, ResId test) -> bool { return id.first < test; });
if (search == m_origToNew.cend() || search->first != id)
return -1;
return search->second;
@ -72,8 +72,8 @@ public:
ResId TranslateNewToOriginal(ResId id) const
{
auto search = std::lower_bound(m_newToOrig.cbegin(), m_newToOrig.cend(),
std::make_pair(id, ResId(0)));
auto search = std::lower_bound(m_newToOrig.cbegin(), m_newToOrig.cend(), id,
[](const auto& id, ResId test) -> bool { return id.first < test; });
if (search == m_newToOrig.cend() || search->first != id)
return -1;
return search->second;

View File

@ -5,43 +5,115 @@
namespace urde
{
CMapWorldInfo::CMapWorldInfo(CBitStreamReader& reader, const CSaveWorld& saveWorld, ResId mlvlId)
CMapWorldInfo::CMapWorldInfo(CBitStreamReader& reader, const CSaveWorld& savw, ResId mlvlId)
{
/* TODO: implement */
const CSaveWorldMemory& worldMem = g_MemoryCardSys->GetSaveWorldMemory(mlvlId);
const auto& memWorlds = g_MemoryCardSys->GetMemoryWorlds();
auto saveWorldMem = std::find_if(memWorlds.cbegin(), memWorlds.cend(),
[&](const auto& test) -> bool { return test.first == mlvlId; });
if (saveWorldMem != memWorlds.cend())
x4_visitedAreas.reserve((worldMem.GetAreaCount() + 31) / 32);
for (int i=0 ; i<worldMem.GetAreaCount() ; ++i)
{
bool visited = reader.ReadEncoded(1);
SetAreaVisited(i, visited);
}
x18_mappedAreas.reserve((worldMem.GetAreaCount() + 31) / 32);
for (int i=0 ; i<worldMem.GetAreaCount() ; ++i)
{
bool mapped = reader.ReadEncoded(1);
SetIsMapped(i, mapped);
}
for (TEditorId doorId : savw.GetDoors())
SetDoorVisited(doorId, reader.ReadEncoded(1));
x38_worldVisited = reader.ReadEncoded(1);
}
void CMapWorldInfo::PutTo(CBitStreamWriter& writer, const CSaveWorld& savw, ResId mlvlId) const
{
const CSaveWorldMemory& worldMem = g_MemoryCardSys->GetSaveWorldMemory(mlvlId);
for (int i=0 ; i<worldMem.GetAreaCount() ; ++i)
{
if (i < x0_visitedAreasAllocated)
writer.WriteEncoded(const_cast<CMapWorldInfo&>(*this).IsAreaVisted(i), 1);
else
writer.WriteEncoded(0, 1);
}
for (int i=0 ; i<worldMem.GetAreaCount() ; ++i)
{
if (i < x14_mappedAreasAllocated)
writer.WriteEncoded(const_cast<CMapWorldInfo&>(*this).IsMapped(i), 1);
else
writer.WriteEncoded(0, 1);
}
for (TEditorId doorId : savw.GetDoors())
writer.WriteEncoded(IsDoorVisited(doorId), 1);
writer.WriteEncoded(x38_worldVisited, 1);
}
void CMapWorldInfo::SetDoorVisited(TEditorId eid, bool visited)
{
x14_[eid] = visited;
x28_visitedDoors[eid] = visited;
}
bool CMapWorldInfo::IsDoorVisited(TEditorId eid) const
{
return x28_visitedDoors.find(eid) != x28_visitedDoors.end();
}
bool CMapWorldInfo::IsAreaVisted(TAreaId aid)
{
if (aid + 1 > x0_visitedAreasAllocated)
{
x4_visitedAreas.resize((aid + 31) / 32);
x0_visitedAreasAllocated = aid + 1;
}
return (x4_visitedAreas[aid / 32] >> (aid % 32)) & 0x1;
}
void CMapWorldInfo::SetAreaVisited(TAreaId aid, bool visited)
{
if (aid + 1 > x0_visitedAreasAllocated)
{
x4_visitedAreas.resize((aid + 31) / 32);
x0_visitedAreasAllocated = aid + 1;
}
if (visited)
x4_visitedAreas[aid / 32] |= 1 << (aid % 32);
else
x4_visitedAreas[aid / 32] &= ~(1 << (aid % 32));
}
bool CMapWorldInfo::IsMapped(TAreaId aid)
{
if (aid + 1 > x14_mappedAreasAllocated)
{
x18_mappedAreas.resize((aid + 31) / 32);
x14_mappedAreasAllocated = aid + 1;
}
return (x18_mappedAreas[aid / 32] >> (aid % 32)) & 0x1;
}
void CMapWorldInfo::SetIsMapped(TAreaId aid, bool mapped)
{
if (aid + 1 > x14_mappedAreasAllocated)
{
x18_mappedAreas.resize((aid + 31) / 32);
x14_mappedAreasAllocated = aid + 1;
}
if (mapped)
x18_mappedAreas[aid / 32] |= 1 << (aid % 32);
else
x18_mappedAreas[aid / 32] &= ~(1 << (aid % 32));
}
bool CMapWorldInfo::IsWorldVisible(TAreaId aid)
{
return x38_worldVisited || IsMapped(aid);
}
}

View File

@ -9,19 +9,23 @@ class CSaveWorld;
class CMapWorldInfo
{
u32 x0_visitedAreasAllocated = 0;
std::vector<u32> x4_visitedAreas;
std::map<TEditorId, bool> x14_;
std::vector<u32> x18_visitedAreas;
u32 x14_mappedAreasAllocated = 0;
std::vector<u32> x18_mappedAreas;
std::map<TEditorId, bool> x28_visitedDoors;
bool x38_worldVisited = false;
public:
CMapWorldInfo()=default;
CMapWorldInfo(CBitStreamReader&, const CSaveWorld& saveWorld, ResId mlvlId);
void PutTo(CBitStreamWriter& writer, const CSaveWorld& savw, ResId mlvlId) const;
bool IsMapped() const;
void SetIsMapped(bool) const;
bool IsMapped(TAreaId);
void SetIsMapped(TAreaId, bool);
void SetDoorVisited(TEditorId eid, bool val);
bool IsDoorVisited() const;
bool IsDoorVisited(TEditorId eid) const;
bool IsAreaVisted(TAreaId);
void SetAreaVisited(TAreaId, bool);
bool IsWorldVisible(TAreaId);
};
}

View File

@ -161,14 +161,14 @@ void CPersistentOptions::SetCinematicState(ResId mlvlId, TEditorId cineId, bool
CGameOptions::CGameOptions(CBitStreamReader& stream)
{
for (int b=0 ; b<64 ; ++b)
x0_[b] = stream.ReadEncoded(1);
x0_[b] = stream.ReadEncoded(8);
x44_soundMode = CAudioSys::ESurroundModes(stream.ReadEncoded(2));
x48_screenBrightness = stream.ReadEncoded(4);
x4c_screenXOffset = stream.ReadEncoded(6);
x50_screenYOffset = stream.ReadEncoded(6);
x54_screenStretch = stream.ReadEncoded(5);
x4c_screenXOffset = stream.ReadEncoded(6) - 30;
x50_screenYOffset = stream.ReadEncoded(6) - 30;
x54_screenStretch = stream.ReadEncoded(5) - 10;
x58_sfxVol = stream.ReadEncoded(7);
x5c_musicVol = stream.ReadEncoded(7);
x60_hudAlpha = stream.ReadEncoded(8);
@ -204,14 +204,14 @@ void CGameOptions::ResetToDefaults()
void CGameOptions::PutTo(CBitStreamWriter& writer) const
{
for (int b=0 ; b<64 ; ++b)
writer.WriteEncoded(x0_[b], 1);
writer.WriteEncoded(x0_[b], 8);
writer.WriteEncoded(u32(x44_soundMode), 2);
writer.WriteEncoded(x48_screenBrightness, 4);
writer.WriteEncoded(x4c_screenXOffset, 6);
writer.WriteEncoded(x50_screenYOffset, 6);
writer.WriteEncoded(x54_screenStretch, 5);
writer.WriteEncoded(x4c_screenXOffset + 30, 6);
writer.WriteEncoded(x50_screenYOffset + 30, 6);
writer.WriteEncoded(x54_screenStretch + 10, 5);
writer.WriteEncoded(x58_sfxVol, 7);
writer.WriteEncoded(x5c_musicVol, 7);
writer.WriteEncoded(x60_hudAlpha, 8);

View File

@ -104,7 +104,7 @@ public:
/** Options tracked per game session */
class CGameOptions
{
bool x0_[64] = {};
u8 x0_[64] = {};
CAudioSys::ESurroundModes x44_soundMode = CAudioSys::ESurroundModes::Stereo;
u32 x48_screenBrightness = 4;
s32 x4c_screenXOffset = 0;

View File

@ -83,18 +83,29 @@ CMemoryCardSys::CMemoryCardSys()
xc_memoryWorlds.reserve(16);
x1c_worldInter.emplace();
x1c_worldInter->reserve(16);
std::vector<std::pair<ResId, ResId>> orderedMLVLs;
orderedMLVLs.reserve(16);
g_ResFactory->EnumerateNamedResources([&](const std::string& name, const SObjectTag& tag) -> bool
{
if (tag.type == FOURCC('MLVL'))
{
if (!HasSaveWorldMemory(tag.id))
{
xc_memoryWorlds.emplace_back(tag.id, CSaveWorldMemory{});
x1c_worldInter->emplace_back(tag.id, -1);
}
ResId origId = g_ResFactory->TranslateNewToOriginal(tag.id);
orderedMLVLs.emplace_back(origId, tag.id);
}
return true;
});
std::sort(orderedMLVLs.begin(), orderedMLVLs.end(),
[](const auto& a, const auto& b) -> bool { return a.first < b.first; });
for (const auto& mlvl : orderedMLVLs)
{
if (!HasSaveWorldMemory(mlvl.second))
{
xc_memoryWorlds.emplace_back(mlvl.second, CSaveWorldMemory{});
x1c_worldInter->emplace_back(mlvl.second, -1);
}
}
}
bool CMemoryCardSys::InitializePump()

View File

@ -0,0 +1,42 @@
#include "CPakFile.hpp"
namespace urde
{
CPakFile::CPakFile(const std::string& filename, bool samusPak, bool worldPak)
: CDvdFile(filename.c_str())
{
x28_24_samusPak = samusPak;
x28_26_worldPak = worldPak;
}
const SObjectTag* CPakFile::GetResIdByName(const char* name) const
{
for (const std::pair<std::string, SObjectTag>& p : x4c_nameList)
if (!CStringExtras::CompareCaseInsensitive(p.first.c_str(), name))
return &p.second;
return nullptr;
}
void CPakFile::AsyncIdle()
{
if (x2c_asyncLoadPhase == EAsyncPhase::Loaded)
return;
if (x34_dvdReq && x34_dvdReq->IsComplete())
return;
switch (x2c_asyncLoadPhase)
{
case EAsyncPhase::Warmup:
Warmup();
break;
case EAsyncPhase::InitialHeader:
InitialHeaderLoad();
break;
case EAsyncPhase::DataLoad:
DataLoad();
break;
default: break;
}
}
}

View File

@ -22,7 +22,17 @@ public:
bool xb_compressed;
};
private:
bool x28_b24_ctFlag;
union
{
struct
{
bool x28_24_samusPak;
bool x28_25_aramFile;
bool x28_26_worldPak;
bool x28_27_worldPakInitialized;
};
u32 _dummy = 0;
};
enum class EAsyncPhase
{
Warmup = 0,
@ -35,41 +45,16 @@ private:
std::vector<u32> x5c_depList;
std::vector<std::pair<u32, SResInfo>> x6c_resList;
public:
CPakFile(const std::string& filename, bool flag) : CDvdFile(filename.c_str()) {}
const std::vector<u32>& GetDepList() const {return x5c_depList;}
const SObjectTag* GetResIdByName(const char* name) const
{
for (const std::pair<std::string, SObjectTag>& p : x4c_nameList)
if (!CStringExtras::CompareCaseInsensitive(p.first.c_str(), name))
return &p.second;
return nullptr;
}
const SResInfo* GetResInfoForLoad(u32 id) {return nullptr;}
const SResInfo* GetResInfo(u32 id) const {return nullptr;}
u32 GetFakeStaticSize() const {return 0;}
CPakFile(const std::string& filename, bool samusPak, bool worldPak);
const std::vector<u32>& GetDepList() const { return x5c_depList; }
const SObjectTag* GetResIdByName(const char* name) const;
const SResInfo* GetResInfoForLoad(u32 id) { return nullptr; }
const SResInfo* GetResInfo(u32 id) const { return nullptr; }
u32 GetFakeStaticSize() const { return 0; }
void DataLoad() {}
void InitialHeaderLoad() {}
void Warmup() {}
void AsyncIdle()
{
if (x2c_asyncLoadPhase == EAsyncPhase::Loaded)
return;
if (x34_dvdReq && x34_dvdReq->IsComplete())
return;
switch (x2c_asyncLoadPhase)
{
case EAsyncPhase::Warmup:
Warmup();
break;
case EAsyncPhase::InitialHeader:
InitialHeaderLoad();
break;
case EAsyncPhase::DataLoad:
DataLoad();
break;
default: break;
}
}
void AsyncIdle();
};
}

View File

@ -101,9 +101,9 @@ void CPlayerState::PutTo(CBitStreamWriter &stream)
{
stream.WriteEncoded(x4_, 32);
float hp = xc_health.GetHP();
stream.WriteEncoded(*reinterpret_cast<int*>(&hp), 32);
stream.WriteEncoded((u32)x8_currentBeam, CBitStreamWriter::GetBitCount(5));
stream.WriteEncoded((u32)x20_currentSuit, CBitStreamWriter::GetBitCount(4));
stream.WriteEncoded(*reinterpret_cast<u32*>(&hp), 32);
stream.WriteEncoded(u32(x8_currentBeam), CBitStreamWriter::GetBitCount(5));
stream.WriteEncoded(u32(x20_currentSuit), CBitStreamWriter::GetBitCount(4));
for (u32 i = 0; i < x24_powerups.size(); ++i)
{
const CPowerUp& pup = x24_powerups[i];
@ -211,7 +211,9 @@ bool CPlayerState::CanVisorSeeFog(const CStateManager& stateMgr) const
CPlayerState::EPlayerVisor CPlayerState::GetActiveVisor(const CStateManager& stateMgr) const
{
const CFirstPersonCamera* cam = static_cast<const CFirstPersonCamera*>(stateMgr.GetCameraManager()->GetCurrentCamera(stateMgr));
const CFirstPersonCamera* cam =
static_cast<const CFirstPersonCamera*>(
stateMgr.GetCameraManager()->GetCurrentCamera(stateMgr));
return (cam ? x14_currentVisor : EPlayerVisor::Combat);
}

View File

@ -7,25 +7,25 @@ namespace urde
const std::vector<u32>* CResLoader::GetTagListForFile(const std::string& name) const
{
std::string namePak = name + ".pak";
for (const std::unique_ptr<CPakFile>& pak : x1c_pakLoadedList)
for (const std::unique_ptr<CPakFile>& pak : x18_pakLoadedList)
if (!CStringExtras::CompareCaseInsensitive(namePak, pak->x18_path))
return &pak->GetDepList();
return nullptr;
}
void CResLoader::AddPakFileAsync(const std::string& name, bool flag)
void CResLoader::AddPakFileAsync(const std::string& name, bool samusPak, bool worldPak)
{
std::string namePak = name + ".pak";
if (CDvdFile::FileExists(namePak.c_str()))
{
x34_pakLoadingList.emplace_back(new CPakFile(namePak, flag));
x30_pakLoadingList.emplace_back(new CPakFile(namePak, samusPak, worldPak));
++x44_pakLoadingCount;
}
}
void CResLoader::AddPakFile(const std::string& name, bool flag)
void CResLoader::AddPakFile(const std::string& name, bool samusPak, bool worldPak)
{
AddPakFileAsync(name, flag);
AddPakFileAsync(name, samusPak, worldPak);
while (x44_pakLoadingCount)
AsyncIdlePakLoading();
}
@ -119,7 +119,7 @@ FourCC CResLoader::GetResourceTypeById(u32 id) const
const SObjectTag* CResLoader::GetResourceIdByName(const char* name) const
{
for (const std::unique_ptr<CPakFile>& file : x1c_pakLoadedList)
for (const std::unique_ptr<CPakFile>& file : x18_pakLoadedList)
{
const SObjectTag* id = file->GetResIdByName(name);
if (id)
@ -135,15 +135,15 @@ bool CResLoader::AreAllPaksLoaded() const
void CResLoader::AsyncIdlePakLoading()
{
for (auto it=x34_pakLoadingList.begin();
it != x34_pakLoadingList.end();
for (auto it=x30_pakLoadingList.begin();
it != x30_pakLoadingList.end();
++it)
{
(*it)->AsyncIdle();
if ((*it)->x2c_asyncLoadPhase == CPakFile::EAsyncPhase::Loaded)
{
MoveToCorrectLoadedList(std::move(*it));
it = x34_pakLoadingList.erase(it);
it = x30_pakLoadingList.erase(it);
--x44_pakLoadingCount;
}
}
@ -151,7 +151,7 @@ void CResLoader::AsyncIdlePakLoading()
bool CResLoader::FindResource(u32 id) const
{
for (const std::unique_ptr<CPakFile>& file : x1c_pakLoadedList)
for (const std::unique_ptr<CPakFile>& file : x18_pakLoadedList)
if (const_cast<CResLoader*>(this)->CacheFromPak(*file, id))
return true;
return false;
@ -159,7 +159,7 @@ bool CResLoader::FindResource(u32 id) const
CPakFile* CResLoader::FindResourceForLoad(u32 id)
{
for (std::unique_ptr<CPakFile>& file : x1c_pakLoadedList)
for (std::unique_ptr<CPakFile>& file : x18_pakLoadedList)
if (CacheFromPakForLoad(*file, id))
return file.get();
return nullptr;
@ -196,7 +196,7 @@ bool CResLoader::CacheFromPak(const CPakFile& file, u32 id)
void CResLoader::MoveToCorrectLoadedList(std::unique_ptr<CPakFile>&& file)
{
x1c_pakLoadedList.push_back(std::move(file));
x18_pakLoadedList.push_back(std::move(file));
}
}

View File

@ -15,16 +15,16 @@ class IDvdRequest;
class CResLoader
{
std::string m_loaderPath;
//std::list<std::unique_ptr<CPakFile>> x4_unusedList;
std::list<std::unique_ptr<CPakFile>> x1c_pakLoadedList;
std::list<std::unique_ptr<CPakFile>> x34_pakLoadingList;
//std::list<std::unique_ptr<CPakFile>> x0_aramList;
std::list<std::unique_ptr<CPakFile>> x18_pakLoadedList;
std::list<std::unique_ptr<CPakFile>> x30_pakLoadingList;
u32 x44_pakLoadingCount = 0;
u32 x4c_cachedResId = -1;
const CPakFile::SResInfo* x50_cachedResInfo = nullptr;
public:
const std::vector<u32>* GetTagListForFile(const std::string& name) const;
void AddPakFileAsync(const std::string& name, bool flag);
void AddPakFile(const std::string& name, bool flag);
void AddPakFileAsync(const std::string& name, bool samusPak, bool worldPak);
void AddPakFile(const std::string& name, bool samusPak, bool worldPak);
CInputStream* LoadNewResourcePartSync(const SObjectTag& tag, int offset, int length, void* extBuf);
void LoadMemResourceSync(const SObjectTag& tag, void** bufOut, int* sizeOut);
CInputStream* LoadResourceFromMemorySync(const SObjectTag& tag, const void* buf);

View File

@ -65,7 +65,7 @@ s32 CSaveWorld::GetCinematicIndex(const TEditorId &id) const
auto it = std::find(x4_cinematics.begin(), x4_cinematics.end(), id);
if (it == x4_cinematics.end())
return -1;
return x4_cinematics.begin() - it;
return it - x4_cinematics.begin();
}
u32 CSaveWorld::GetRelayCount() const
@ -78,7 +78,7 @@ s32 CSaveWorld::GetRelayIndex(const TEditorId &id) const
auto it = std::find(x14_relays.begin(), x14_relays.end(), id);
if (it == x14_relays.end())
return -1;
return x14_relays.begin() - it;
return it - x14_relays.begin();
}
TEditorId CSaveWorld::GetRelayEditorId(u32 idx) const
@ -96,7 +96,7 @@ s32 CSaveWorld::GetDoorIndex(const TEditorId &id) const
auto it = std::find(x34_doors.begin(), x34_doors.end(), id);
if (it == x34_doors.end())
return -1;
return x34_doors.begin() - it;
return it - x34_doors.begin();
}
CFactoryFnReturn FSaveWorldFactory(const SObjectTag& tag, CInputStream& in, const CVParamTransfer& param,

View File

@ -37,6 +37,7 @@ public:
u32 GetCinematicCount() const;
s32 GetCinematicIndex(const TEditorId& id) const;
const std::vector<TEditorId>& GetCinematics() const { return x4_cinematics; }
const std::vector<TEditorId>& GetDoors() const { return x34_doors; }
const std::vector<SScanState>& GetScans() const { return x44_scans; }
u32 GetRelayCount() const;
s32 GetRelayIndex(const TEditorId& id) const;

View File

@ -22,6 +22,7 @@ struct SObjectTag
operator bool() const { return (id & 0xffffffff) != 0xffffffff; }
bool operator!=(const SObjectTag& other) const { return id != other.id; }
bool operator==(const SObjectTag& other) const { return id == other.id; }
bool operator<(const SObjectTag& other) const { return id < other.id; }
SObjectTag() = default;
SObjectTag(FourCC tp, ResId rid) : type(tp), id(rid) {}
SObjectTag(CInputStream& in)

View File

@ -6,6 +6,7 @@
#include "CInGameTweakManagerBase.hpp"
#include "Audio/CAudioGroupSet.hpp"
#include "Editor/ProjectResourceFactoryBase.hpp"
#include "CGameState.hpp"
namespace urde
{
@ -67,8 +68,11 @@ std::vector<CWorld::CRelay> CWorld::CRelay::ReadMemoryRelays(athena::io::MemoryR
return ret;
}
CWorldLayers CWorldLayers::ReadWorldLayers(athena::io::MemoryReader& r)
void CWorldLayers::ReadWorldLayers(athena::io::MemoryReader& r, int version, ResId mlvlId)
{
if (version <= 14)
return;
CWorldLayers ret;
u32 areaCount = r.readUint32Big();
@ -89,7 +93,8 @@ CWorldLayers CWorldLayers::ReadWorldLayers(athena::io::MemoryReader& r)
for (u32 i = 0; i < areaCount; ++i)
ret.m_areas[i].m_startNameIdx = r.readUint32Big();
return ret;
CWorldState& wldState = g_GameState->StateForWorld(mlvlId);
wldState.GetLayerState()->InitializeWorldLayers(ret.m_areas);
}
bool CDummyWorld::ICheckWorldComplete()
@ -139,7 +144,7 @@ bool CDummyWorld::ICheckWorldComplete()
if (version > 12)
r.readString();
CWorldLayers::ReadWorldLayers(r);
CWorldLayers::ReadWorldLayers(r, version, xc_mlvlId);
if (x4_loadMap)
x8_phase = Phase::LoadingMap;
@ -321,7 +326,7 @@ bool CWorld::CheckWorldComplete(CStateManager* mgr, TAreaId id, ResId mreaId)
x84_defAudioTrack = g_TweakManager->GetTweakValue(trackKey)->GetAudio().GetFileName();
}
CWorldLayers::ReadWorldLayers(r);
CWorldLayers::ReadWorldLayers(r, version, x8_mlvlId);
x4_phase = Phase::LoadingMap;
}

View File

@ -194,7 +194,7 @@ struct CWorldLayers
};
std::vector<Area> m_areas;
std::vector<std::string> m_names;
static CWorldLayers ReadWorldLayers(athena::io::MemoryReader& r);
static void ReadWorldLayers(athena::io::MemoryReader& r, int version, ResId mlvlId);
};
}