mirror of
https://github.com/thunderbrewhq/thunderbrew
synced 2025-10-27 22:36:05 +03:00
627 lines
17 KiB
C++
627 lines
17 KiB
C++
#include "ui/FrameXML.hpp"
|
|
#include "ui/CSimpleButton.hpp"
|
|
#include "ui/CSimpleCheckbox.hpp"
|
|
#include "ui/CSimpleEditBox.hpp"
|
|
#include "ui/CSimpleFont.hpp"
|
|
#include "ui/CSimpleFrame.hpp"
|
|
#include "ui/CSimpleHTML.hpp"
|
|
#include "ui/CSimpleModel.hpp"
|
|
#include "ui/CSimpleMovieFrame.hpp"
|
|
#include "ui/CSimpleScrollFrame.hpp"
|
|
#include "ui/CSimpleMessageScrollFrame.hpp"
|
|
#include "ui/CSimpleSlider.hpp"
|
|
#include "ui/CSimpleStatusBar.hpp"
|
|
#include "util/CStatus.hpp"
|
|
#include "util/SFile.hpp"
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <common/XML.hpp>
|
|
#include <storm/String.hpp>
|
|
|
|
int32_t FrameXML::s_debugLevel;
|
|
TSHashTable<FrameFactoryNode, HASHKEY_STRI> FrameXML::s_factoryHash;
|
|
TSHashTable<HashedNode, HASHKEY_STRI> FrameXML::s_nodeHash;
|
|
|
|
CSimpleFrame* Create_SimpleButton(CSimpleFrame* parent) {
|
|
// TODO
|
|
// auto m = CDataAllocator::GetData(CSimpleFrame::s_simpleButtonHeap, 0, __FILE__, __LINE__);
|
|
|
|
auto m = SMemAlloc(sizeof(CSimpleButton), __FILE__, __LINE__, 0x0);
|
|
return new (m) CSimpleButton(parent);
|
|
}
|
|
|
|
CSimpleFrame* Create_SimpleCheckButton(CSimpleFrame* parent) {
|
|
// TODO
|
|
// auto m = CDataAllocator::GetData(CSimpleCheckbox::s_simpleCheckboxHeap, 0, __FILE__, __LINE__);
|
|
|
|
auto m = SMemAlloc(sizeof(CSimpleCheckbox), __FILE__, __LINE__, 0x0);
|
|
return new (m) CSimpleCheckbox(parent);
|
|
}
|
|
|
|
CSimpleFrame* Create_SimpleEditBox(CSimpleFrame* parent) {
|
|
// TODO
|
|
// auto m = CDataAllocator::GetData(CSimpleEditBox::s_simpleEditBoxHeap, 0, __FILE__, __LINE__);
|
|
|
|
auto m = SMemAlloc(sizeof(CSimpleEditBox), __FILE__, __LINE__, 0x0);
|
|
return new (m) CSimpleEditBox(parent);
|
|
}
|
|
|
|
CSimpleFrame* Create_SimpleFrame(CSimpleFrame* parent) {
|
|
// TODO
|
|
// auto m = CDataAllocator::GetData(CSimpleFrame::s_simpleFrameHeap, 0, __FILE__, __LINE__);
|
|
|
|
auto m = SMemAlloc(sizeof(CSimpleFrame), __FILE__, __LINE__, 0x0);
|
|
return new (m) CSimpleFrame(parent);
|
|
}
|
|
|
|
CSimpleFrame* Create_SimpleMessageFrame(CSimpleFrame* parent) {
|
|
// TODO
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
CSimpleFrame* Create_SimpleModel(CSimpleFrame* parent) {
|
|
// TODO
|
|
// auto m = CDataAllocator::GetData(CSimpleFrame::s_simpleModelHeap, 0, __FILE__, __LINE__);
|
|
|
|
auto m = SMemAlloc(sizeof(CSimpleModel), __FILE__, __LINE__, 0x0);
|
|
return new (m) CSimpleModel(parent);
|
|
}
|
|
|
|
CSimpleFrame* Create_SimpleScrollFrame(CSimpleFrame* parent) {
|
|
// TODO
|
|
// auto m = CDataAllocator::GetData(CSimpleScrollFrame::s_simpleScrollHeap, 0, __FILE__, __LINE__);
|
|
|
|
auto m = SMemAlloc(sizeof(CSimpleScrollFrame), __FILE__, __LINE__, 0x0);
|
|
return new (m) CSimpleScrollFrame(parent);
|
|
}
|
|
|
|
CSimpleFrame* Create_SimpleScrollingMessageFrame(CSimpleFrame* parent) {
|
|
// TODO
|
|
|
|
auto m = SMemAlloc(sizeof(CSimpleMessageScrollFrame), __FILE__, __LINE__, 0x0);
|
|
return new (m) CSimpleMessageScrollFrame(parent);
|
|
}
|
|
|
|
CSimpleFrame* Create_SimpleSlider(CSimpleFrame* parent) {
|
|
// TODO
|
|
// auto m = CDataAllocator::GetData(CSimpleSlider::s_simpleSliderHeap, 0, __FILE__, __LINE__);
|
|
|
|
auto m = SMemAlloc(sizeof(CSimpleSlider), __FILE__, __LINE__, 0x0);
|
|
return new (m) CSimpleSlider(parent);
|
|
}
|
|
|
|
CSimpleFrame* Create_SimpleHTML(CSimpleFrame* parent) {
|
|
// TODO
|
|
// auto m = CDataAllocator::GetData(CSimpleHTML::s_simpleHTMLHeap, 0, __FILE__, __LINE__);
|
|
|
|
auto m = SMemAlloc(sizeof(CSimpleHTML), __FILE__, __LINE__, 0x0);
|
|
return new (m) CSimpleHTML(parent);
|
|
}
|
|
|
|
CSimpleFrame* Create_SimpleStatusBar(CSimpleFrame* parent) {
|
|
// TODO
|
|
|
|
auto m = SMemAlloc(sizeof(CSimpleStatusBar), __FILE__, __LINE__, 0x0);
|
|
return new (m) CSimpleStatusBar(parent);
|
|
}
|
|
|
|
CSimpleFrame* Create_SimpleColorSelect(CSimpleFrame* parent) {
|
|
// TODO
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
CSimpleFrame* Create_SimpleMovieFrame(CSimpleFrame* parent) {
|
|
// TODO
|
|
// auto m = CDataAllocator::GetData(CSimpleMovie::s_simpleMovieHeap, 0, __FILE__, __LINE__);
|
|
|
|
auto m = SMemAlloc(sizeof(CSimpleMovieFrame), __FILE__, __LINE__, 0x0);
|
|
return new (m) CSimpleMovieFrame(parent);
|
|
}
|
|
|
|
XMLNode* FrameXML_AcquireHashNode(const char* name, const char*& tainted, bool& locked) {
|
|
HashedNode* hashedNode = FrameXML::s_nodeHash.Ptr(name);
|
|
|
|
if (!hashedNode) {
|
|
return nullptr;
|
|
}
|
|
|
|
locked = hashedNode->locked;
|
|
tainted = hashedNode->tainted;
|
|
hashedNode->locked = true;
|
|
|
|
return hashedNode->node;
|
|
}
|
|
|
|
int32_t FrameXML_CheckSignature(const char* tocPath, const char* a2, const unsigned char* key, unsigned char* digest) {
|
|
// TODO
|
|
|
|
return 3;
|
|
}
|
|
|
|
/**
|
|
* Find and return the appropriate parent name.
|
|
*
|
|
* Parent priorities:
|
|
* 1. parent attribute on local node
|
|
* 2. parent attribute on last inherited node with parent attribute present
|
|
*/
|
|
const char* FindParent(XMLNode* node) {
|
|
const char* parentName = node->GetAttributeByName("parent");
|
|
|
|
if (parentName && *parentName) {
|
|
return parentName;
|
|
}
|
|
|
|
const char* inheritNames = node->GetAttributeByName("inherits");
|
|
|
|
if (!inheritNames || !*inheritNames) {
|
|
return nullptr;
|
|
}
|
|
|
|
char inheritName[1024];
|
|
|
|
do {
|
|
SStrTokenize(&inheritNames, inheritName, 0x400u, " ,", 0);
|
|
|
|
if (!*inheritName) {
|
|
break;
|
|
}
|
|
|
|
auto hashedNode = FrameXML::s_nodeHash.Ptr(inheritName);
|
|
|
|
if (hashedNode) {
|
|
auto inheritNode = hashedNode->node;
|
|
int32_t v10 = hashedNode->locked;
|
|
hashedNode->locked = true;
|
|
|
|
if (inheritNode && !v10) {
|
|
parentName = inheritNode->GetAttributeByName("parent");
|
|
FrameXML::s_nodeHash.Ptr(inheritName)->locked = false;
|
|
}
|
|
}
|
|
} while (*inheritName);
|
|
|
|
return parentName;
|
|
}
|
|
|
|
CSimpleFrame* FrameXML_CreateFrame(XMLNode* node, CSimpleFrame* parent, CStatus* status) {
|
|
if (FrameXML::s_debugLevel > 0) {
|
|
const char* name = node->GetAttributeByName("name");
|
|
|
|
if (name && *name) {
|
|
status->Add(STATUS_INFO, "-- Creating %s named %s", node->m_name.m_str, name);
|
|
} else {
|
|
status->Add(STATUS_INFO, "-- Creating unnamed %s", node->m_name.m_str);
|
|
}
|
|
}
|
|
|
|
auto factoryNode = FrameXML::s_factoryHash.Ptr(node->m_name.m_str);
|
|
|
|
if (!factoryNode) {
|
|
status->Add(STATUS_WARNING, "Unknown frame type: %s", node->m_name.m_str);
|
|
return nullptr;
|
|
}
|
|
|
|
const char* parentName = FindParent(node);
|
|
|
|
if (parentName && *parentName) {
|
|
int32_t parentType = CSimpleFrame::GetObjectType();
|
|
parent = static_cast<CSimpleFrame*>(CScriptObject::GetScriptObjectByName(parentName, parentType));
|
|
|
|
if (!parent) {
|
|
status->Add(STATUS_WARNING, "Couldn't find frame parent: %s", parentName);
|
|
}
|
|
}
|
|
|
|
CSimpleFrame* frame = factoryNode->m_factory(parent);
|
|
|
|
if (frame) {
|
|
if (factoryNode->m_unique) {
|
|
// TODO
|
|
// Delete the factory node
|
|
// sub_565030(factoryNode);
|
|
// FrameXML::s_factoryHash.InternalDelete(factoryNode);
|
|
}
|
|
|
|
frame->PreLoadXML(node, status);
|
|
frame->LoadXML(node, status);
|
|
frame->PostLoadXML(node, status);
|
|
|
|
return frame;
|
|
} else {
|
|
status->Add(STATUS_WARNING, "Unable to create frame type: %s", node->m_name.m_str);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
int32_t FrameXML_CreateFrames(const char* tocPath, const char* a2, MD5_CTX* md5, CStatus* status) {
|
|
if (!status) {
|
|
status = new CStatus;
|
|
}
|
|
|
|
char v15[260];
|
|
v15[0] = 0;
|
|
memset(v15 + 1, 0, 259);
|
|
|
|
const char* v5 = tocPath;
|
|
|
|
const char* v6 = SStrChrR(tocPath, '\\');
|
|
|
|
if (v6) {
|
|
uintptr_t v7 = v6 - tocPath + 1;
|
|
uintptr_t v8 = v7;
|
|
|
|
if (v7 < 259) {
|
|
SStrCopy(v15, tocPath, v7 + 1);
|
|
v15[v8] = 0;
|
|
}
|
|
}
|
|
|
|
void* tocBuffer;
|
|
size_t tocBytes;
|
|
|
|
if (!SFile::Load(nullptr, tocPath, &tocBuffer, &tocBytes, 1, 1, nullptr)) {
|
|
status->Add(STATUS_ERROR, "Couldn't open %s", a2);
|
|
return 0;
|
|
}
|
|
|
|
MD5Update(md5, static_cast<unsigned char*>(tocBuffer), tocBytes);
|
|
|
|
const char* tocData = static_cast<char*>(tocBuffer);
|
|
|
|
// Advance past UTF-8 BOM if present
|
|
if (*tocData == static_cast<char>(0xEF) && *(tocData + 1) == static_cast<char>(0xBB) && *(tocData + 2) == static_cast<char>(0xBF)) {
|
|
tocData = tocData + 3;
|
|
}
|
|
|
|
CStatus frameStatus;
|
|
char tocLine[1024];
|
|
char tocEntryPath[260];
|
|
int32_t v25 = 0;
|
|
|
|
do {
|
|
SStrTokenize(&tocData, tocLine, 1024, "\r\n", 0);
|
|
|
|
if (!*tocLine) {
|
|
break;
|
|
}
|
|
|
|
if (*tocLine == '#') {
|
|
continue;
|
|
}
|
|
|
|
SStrCopy(tocEntryPath, v15, 260);
|
|
SStrPack(tocEntryPath, tocLine, 260);
|
|
|
|
size_t v10 = SStrLen(tocEntryPath);
|
|
|
|
bool v11 = tocEntryPath[v10 - 1] == ' ';
|
|
char* v12 = &tocEntryPath[v10];
|
|
|
|
if (v11) {
|
|
do {
|
|
if (v12 <= tocEntryPath) {
|
|
break;
|
|
}
|
|
|
|
--v12;
|
|
} while (*(v12 - 1) == ' ');
|
|
}
|
|
|
|
*v12 = 0;
|
|
|
|
FrameXML_ProcessFile(tocEntryPath, a2, md5, &frameStatus);
|
|
|
|
// TODO
|
|
// if (s_progressCallback && s_progressFiles < s_progressTotal) {
|
|
// ++s_progressFiles;
|
|
// v20 = s_progressTotal;
|
|
// v13 = (double)(unsigned int)s_progressFiles / (double)(unsigned int)s_progressTotal;
|
|
// s_progressCallback(LODWORD(v13), s_progressParam);
|
|
// v5 = a2;
|
|
// }
|
|
} while (*tocLine);
|
|
|
|
// TODO
|
|
// XMLTree_Cleanup();
|
|
|
|
SFile::Unload(tocBuffer);
|
|
|
|
if (FrameXML::s_debugLevel > 0 || v25 > 0) {
|
|
frameStatus.Prepend(STATUS_INFO, "** Loading table of contents %s", v5);
|
|
}
|
|
|
|
status->Add(frameStatus);
|
|
|
|
return 1;
|
|
}
|
|
|
|
void FrameXML_FreeHashNodes() {
|
|
FrameXML::s_nodeHash.Clear();
|
|
}
|
|
|
|
XMLTree* FrameXML_LoadXML(const char* filePath, MD5_CTX* md5, CStatus* status) {
|
|
void* xmlBuffer;
|
|
size_t xmlBytes;
|
|
|
|
if (SFile::Load(nullptr, filePath, &xmlBuffer, &xmlBytes, 0, 1, nullptr)) {
|
|
MD5Update(md5, static_cast<unsigned char*>(xmlBuffer), xmlBytes);
|
|
|
|
XMLTree* tree = XMLTree_Load(static_cast<char*>(xmlBuffer), xmlBytes);
|
|
|
|
if (!tree) {
|
|
status->Add(STATUS_ERROR, "Couldn't parse XML in %s", filePath);
|
|
}
|
|
|
|
SFile::Unload(xmlBuffer);
|
|
|
|
return tree;
|
|
} else {
|
|
status->Add(STATUS_ERROR, "Couldn't open %s", filePath);
|
|
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
int32_t FrameXML_ProcessFile(const char* filePath, const char* a2, MD5_CTX* md5, CStatus* status) {
|
|
const char* v5 = filePath;
|
|
|
|
char v25[260];
|
|
char* i;
|
|
char* j;
|
|
|
|
// Deal with relative file paths
|
|
if (SStrStr(filePath, "..")) {
|
|
SStrCopy(v25, filePath, 260);
|
|
|
|
for (i = (char*)SStrStr(v25, ".."); i; i = (char*)SStrStr(v25, "..")) {
|
|
char v7 = *(i - 1);
|
|
|
|
if (v7 != '\\' && v7 != '/') {
|
|
break;
|
|
}
|
|
|
|
char v8 = i[2];
|
|
|
|
if (v8 != '\\' && v8 != '/') {
|
|
break;
|
|
}
|
|
|
|
for (j = i - 2; j >= v25; --j) {
|
|
if (*j == '\\') {
|
|
break;
|
|
}
|
|
|
|
if (*j == '/') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
SStrCopy(j + 1, i + 3, 0x7FFFFFFF);
|
|
}
|
|
|
|
filePath = v25;
|
|
v5 = v25;
|
|
}
|
|
|
|
const char* v10 = SStrChrR(v5, '.');
|
|
|
|
// If file ends in .lua, execute it and return
|
|
if (v10 && !SStrCmpI(v10, ".lua", 0x7FFFFFFFu)) {
|
|
return FrameScript_ExecuteFile(v5, a2, md5, status);
|
|
}
|
|
|
|
// Assume all other files are xml
|
|
XMLTree* tree = FrameXML_LoadXML(v5, md5, status);
|
|
|
|
if (!tree) {
|
|
return 0;
|
|
}
|
|
|
|
CStatus fileStatus;
|
|
|
|
XMLNode* node = XMLTree_GetRoot(tree)->m_child;
|
|
|
|
char v26[271];
|
|
char v27[264];
|
|
|
|
// TODO
|
|
// Should come from some kind of Lua headers
|
|
const char* lua_tainted = nullptr;
|
|
|
|
int32_t v34 = 0;
|
|
|
|
while (node) {
|
|
// <Include>
|
|
if (!SStrCmpI(node->GetName(), "Include", 0x7FFFFFFFu)) {
|
|
const char* v14 = node->GetAttributeByName("file");
|
|
|
|
if (v14) {
|
|
const char* v15 = SStrChrR(v5, 92);
|
|
|
|
if (v15) {
|
|
uint32_t v13 = v15 - v5 + 1;
|
|
|
|
if (v13 < 260 ) {
|
|
SStrCopy(v27, v5, 260);
|
|
v27[v13] = 0;
|
|
SStrPack(v27, v14, 260);
|
|
}
|
|
} else {
|
|
SStrCopy(v27, v14, 260);
|
|
}
|
|
|
|
FrameXML_ProcessFile(v27, a2, md5, &fileStatus);
|
|
} else {
|
|
fileStatus.Add(STATUS_ERROR, "Element 'Include' without file attribute");
|
|
}
|
|
// <Script>
|
|
} else if (!SStrCmpI(node->GetName(), "Script", 0x7FFFFFFFu)) {
|
|
const char* v16 = node->GetAttributeByName("file");
|
|
|
|
if (v16) {
|
|
const char* v17 = SStrChrR(v5, 92);
|
|
|
|
if (v17) {
|
|
uint32_t v18 = v17 - v5 + 1;
|
|
|
|
if (v18 < 260) {
|
|
SStrCopy(v27, filePath, 260);
|
|
v27[v18] = 0;
|
|
SStrPack(v27, v16, 260);
|
|
}
|
|
|
|
v5 = filePath;
|
|
} else {
|
|
SStrCopy(v27, v16, 260);
|
|
}
|
|
|
|
FrameScript_ExecuteFile(v27, a2, md5, &fileStatus);
|
|
}
|
|
|
|
char* v19 = node->m_body;
|
|
|
|
if (v19 && *v19) {
|
|
SStrPrintf(v26, 271, "%s:<Scripts>", v5);
|
|
FrameScript_Execute(v19, v26, lua_tainted);
|
|
}
|
|
// <Font>
|
|
} else if (!SStrCmpI(node->GetName(), "Font", 0x7FFFFFFFu)) {
|
|
const char* fontName = node->GetAttributeByName("name");
|
|
|
|
if (fontName && *fontName) {
|
|
CSimpleFont* font = CSimpleFont::GetFont(fontName, 1);
|
|
font->LoadXML(node, status);
|
|
} else {
|
|
fileStatus.Add(STATUS_WARNING, "Unnamed font node at top level");
|
|
}
|
|
// Everything else (frame nodes)
|
|
} else {
|
|
const char* v22 = node->GetAttributeByName("virtual");
|
|
|
|
if (!v22 || SStrCmpI(v22, "true", 0x7FFFFFFFu)) {
|
|
FrameXML_CreateFrame(node, nullptr, &fileStatus);
|
|
CLayoutFrame::ResizePending();
|
|
} else {
|
|
const char* v23 = node->GetAttributeByName("name");
|
|
|
|
if (v23 && *v23) {
|
|
FrameXML_StoreHashNode(node, v23, lua_tainted, &fileStatus);
|
|
} else {
|
|
fileStatus.Add(STATUS_WARNING, "Unnamed virtual node at top level");
|
|
}
|
|
}
|
|
}
|
|
|
|
node = node->m_next;
|
|
}
|
|
|
|
XMLTree_Free(tree);
|
|
|
|
if (FrameXML::s_debugLevel > 0 || v34 > 0) {
|
|
fileStatus.Prepend(STATUS_INFO, "++ Loading file %s", v5);
|
|
}
|
|
|
|
status->Add(fileStatus);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int32_t FrameXML_RegisterFactory(const char* type, CSimpleFrame* (*factory)(CSimpleFrame*), bool a3) {
|
|
if (FrameXML::s_factoryHash.Ptr(type)) {
|
|
// Already registered
|
|
return 0;
|
|
}
|
|
|
|
FrameFactoryNode* node = FrameXML::s_factoryHash.New(type, 0, 0);
|
|
|
|
node->m_factory = factory;
|
|
node->m_unique = a3;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void FrameXML_RegisterDefault() {
|
|
FrameXML_RegisterFactory("Button", &Create_SimpleButton, 0);
|
|
FrameXML_RegisterFactory("CheckButton", &Create_SimpleCheckButton, 0);
|
|
FrameXML_RegisterFactory("EditBox", &Create_SimpleEditBox, 0);
|
|
FrameXML_RegisterFactory("Frame", &Create_SimpleFrame, 0);
|
|
FrameXML_RegisterFactory("MessageFrame", &Create_SimpleMessageFrame, 0);
|
|
FrameXML_RegisterFactory("Model", &Create_SimpleModel, 0);
|
|
FrameXML_RegisterFactory("ScrollFrame", &Create_SimpleScrollFrame, 0);
|
|
FrameXML_RegisterFactory("ScrollingMessageFrame", &Create_SimpleScrollingMessageFrame, 0);
|
|
FrameXML_RegisterFactory("Slider", &Create_SimpleSlider, 0);
|
|
FrameXML_RegisterFactory("SimpleHTML", &Create_SimpleHTML, 0);
|
|
FrameXML_RegisterFactory("StatusBar", &Create_SimpleStatusBar, 0);
|
|
FrameXML_RegisterFactory("ColorSelect", &Create_SimpleColorSelect, 0);
|
|
FrameXML_RegisterFactory("MovieFrame", &Create_SimpleMovieFrame, 0);
|
|
}
|
|
|
|
void FrameXML_ReleaseHashNode(const char* name) {
|
|
HashedNode* hashedNode = FrameXML::s_nodeHash.Ptr(name);
|
|
hashedNode->locked = false;
|
|
}
|
|
|
|
void FrameXML_StoreHashNode(XMLNode* node, const char* name, const char* a3, CStatus* status) {
|
|
HashedNode* hashed = FrameXML::s_nodeHash.Ptr(name);
|
|
|
|
if (hashed) {
|
|
if (!hashed->tainted || a3) {
|
|
status->Add(STATUS_WARNING, "Virtual object named %s already exists", name);
|
|
return;
|
|
}
|
|
|
|
XMLNode *v6 = hashed->node;
|
|
|
|
if (v6) {
|
|
delete v6;
|
|
|
|
// TODO
|
|
// CDataAllocator::PutData((int)XMLNode::s_XMLNodeHeap, v6, 0, 0);
|
|
}
|
|
} else {
|
|
HashedNode* hashed = FrameXML::s_nodeHash.New(name, 0, 0);
|
|
|
|
hashed->node = node;
|
|
hashed->tainted = a3;
|
|
}
|
|
|
|
// TODO
|
|
// sub_814650(a1);
|
|
|
|
if (FrameXML::s_debugLevel > 0) {
|
|
status->Add(STATUS_INFO, "-- Added virtual frame %s", name);
|
|
}
|
|
}
|
|
|
|
int32_t FrameXML_GuessNumFiles(const char* data) {
|
|
int32_t result = 0;
|
|
while (*data) {
|
|
while (*data == '\r' || *data == '\n') {
|
|
++data;
|
|
}
|
|
if (!*data) {
|
|
break;
|
|
}
|
|
char v3 = *data;
|
|
if (v3 != '#')
|
|
++result;
|
|
while (v3 != '\r' && v3 != '\n') {
|
|
v3 = *++data;
|
|
if (!v3)
|
|
return result;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int32_t FrameXML_GetDebugLevel() {
|
|
return FrameXML::s_debugLevel;
|
|
}
|
|
|
|
void FrameXML_SetDebugLevel(int32_t level) {
|
|
FrameXML::s_debugLevel = level;
|
|
}
|