Skip to content

Commit 1545028

Browse files
committed
Add support for Vector<DBString>
Used by Maniac Patch to save String Arrays
1 parent de7ede0 commit 1545028

9 files changed

Lines changed: 292 additions & 47 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ set(CMAKE_CXX_EXTENSIONS ON)
2828
# lcf library files
2929
set(LCF_SOURCES
3030
src/dbarray.cpp
31+
src/dbstring_struct.cpp
3132
src/encoder.cpp
3233
src/ini.cpp
3334
src/inireader.cpp

Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ liblcf_la_LDFLAGS = \
4242
-no-undefined
4343
liblcf_la_SOURCES = \
4444
src/dbarray.cpp \
45+
src/dbstring_struct.cpp \
4546
src/encoder.cpp \
4647
src/ini.cpp \
4748
src/inireader.cpp \

src/dbstring_struct.cpp

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
/*
2+
* This file is part of liblcf. Copyright (c) 2021 liblcf authors.
3+
* https://github.com/EasyRPG/liblcf - https://easyrpg.org
4+
*
5+
* liblcf is Free/Libre Open Source Software, released under the MIT License.
6+
* For the full copyright and license information, please view the COPYING
7+
* file that was distributed with this source code.
8+
*/
9+
10+
#include "lcf/dbstring.h"
11+
#include "reader_struct.h"
12+
#include <iostream>
13+
14+
namespace lcf {
15+
16+
/*
17+
DBString has the structure of a Pascal String (Length + Data).
18+
It could be a primitive type but vector<DBString> needs special handling.
19+
20+
Vector<DBString> is Maniac Patch and is sparse:
21+
When size is > 0xFFFFFFFF there is a gap of "0x800000000 - size"
22+
In other cases it is as above: Size Data ... Size Data
23+
*/
24+
25+
template <>
26+
struct RawStruct<DBString> {
27+
static void ReadLcf(DBString& ref, LcfReader& stream, uint32_t length);
28+
static void WriteLcf(const DBString& ref, LcfWriter& stream);
29+
static int LcfSize(const DBString& ref, LcfWriter& stream);
30+
static void WriteXml(const DBString& ref, XmlWriter& stream);
31+
static void BeginXml(DBString& ref, XmlReader& stream);
32+
};
33+
34+
template <>
35+
struct RawStruct<std::vector<DBString> > {
36+
static void ReadLcf(std::vector<DBString>& ref, LcfReader& stream, uint32_t length);
37+
static void WriteLcf(const std::vector<DBString>& ref, LcfWriter& stream);
38+
static int LcfSize(const std::vector<DBString>& ref, LcfWriter& stream);
39+
static void WriteXml(const std::vector<DBString>& ref, XmlWriter& stream);
40+
static void BeginXml(std::vector<DBString>& ref, XmlReader& stream);
41+
};
42+
43+
void RawStruct<DBString>::ReadLcf(DBString& ref, LcfReader& stream, uint32_t length) {
44+
stream.ReadString(ref, length);
45+
#ifdef LCF_DEBUG_TRACE
46+
printf(" %s\n", ref.c_str());
47+
#endif
48+
}
49+
50+
void RawStruct<DBString>::WriteLcf(const DBString& ref, LcfWriter& stream) {
51+
stream.Write(ref);
52+
}
53+
54+
int RawStruct<DBString>::LcfSize(const DBString& ref, LcfWriter& stream) {
55+
return stream.Decode(ref).size();
56+
}
57+
58+
void RawStruct<DBString>::WriteXml(const DBString& ref, XmlWriter& stream) {
59+
stream.Write(ref);
60+
}
61+
62+
class DbStringXmlHandler : public XmlHandler {
63+
private:
64+
DBString& ref;
65+
public:
66+
DbStringXmlHandler(DBString& ref) :
67+
ref(ref) {}
68+
void StartElement(XmlReader& stream, const char* name, const char** /* atts */) {
69+
// no-op
70+
}
71+
void EndElement(XmlReader& /* stream */, const char* /* name */) {
72+
// no-op
73+
}
74+
void CharacterData(XmlReader& /* stream */, const std::string& data) {
75+
ref = DBString(data);
76+
}
77+
};
78+
79+
void RawStruct<DBString>::BeginXml(DBString& /* ref */, XmlReader& /* stream */) {
80+
// no-op
81+
}
82+
83+
void RawStruct<std::vector<DBString>>::ReadLcf(std::vector<DBString>& ref, LcfReader& stream, uint32_t length) {
84+
int index = 0;
85+
DBString string_var;
86+
87+
uint32_t startpos = stream.Tell();
88+
uint32_t endpos = startpos + length;
89+
while (stream.Tell() < endpos) {
90+
// If size is bigger than 4 bytes, size indicates the gap size
91+
// Otherwise it indicates the size of the next string
92+
auto size = stream.ReadUInt64();
93+
if (size > std::numeric_limits<uint32_t>::max()) {
94+
index += static_cast<uint32_t>(0x800000000 - size);
95+
96+
ref.resize(index);
97+
} else {
98+
stream.ReadString(string_var, size);
99+
#ifdef LCF_DEBUG_TRACE
100+
fprintf(stderr, "t[%d]: %s\n", index + 1, string_var.c_str());
101+
#endif
102+
ref.push_back(string_var);
103+
104+
++index;
105+
}
106+
}
107+
108+
if (stream.Tell() != endpos) {
109+
#ifdef LCF_DEBUG_TRACE
110+
fprintf(stderr, "Misaligned!\n");
111+
#endif
112+
stream.Seek(endpos);
113+
}
114+
}
115+
116+
void RawStruct<std::vector<DBString>>::WriteLcf(const std::vector<DBString>& ref, LcfWriter& stream) {
117+
int gap_size = 0;
118+
119+
for (size_t i = 0; i < ref.size(); ++i) {
120+
const auto& e = ref[i];
121+
if (e.empty()) {
122+
++gap_size;
123+
continue;
124+
}
125+
126+
if (gap_size > 0) {
127+
stream.WriteUInt64(0x800000000 - static_cast<uint64_t>(gap_size));
128+
gap_size = 0;
129+
}
130+
131+
auto len = RawStruct<DBString>::LcfSize(e, stream);
132+
stream.WriteInt(len);
133+
RawStruct<DBString>::WriteLcf(e, stream);
134+
}
135+
}
136+
137+
int RawStruct<std::vector<DBString>>::LcfSize(const std::vector<DBString>& ref, LcfWriter& stream) {
138+
int result = 0;
139+
int gap_size = 0;
140+
141+
for (size_t i = 0; i < ref.size(); ++i) {
142+
const auto& e = ref[i];
143+
if (e.empty()) {
144+
++gap_size;
145+
continue;
146+
}
147+
148+
if (gap_size > 0) {
149+
result += LcfReader::UInt64Size(0x800000000 - static_cast<uint64_t>(gap_size));
150+
gap_size = 0;
151+
}
152+
153+
int size = RawStruct<DBString>::LcfSize(e, stream);
154+
result += LcfReader::IntSize(size);
155+
result += size;
156+
}
157+
158+
return result;
159+
}
160+
161+
void RawStruct<std::vector<DBString>>::WriteXml(const std::vector<DBString>& ref, XmlWriter& stream) {
162+
for (size_t i = 0; i < ref.size(); ++i) {
163+
const auto& e = ref[i];
164+
if (e.empty()) {
165+
continue;
166+
}
167+
168+
stream.BeginElement("item", i + 1);
169+
RawStruct<DBString>::WriteXml(e, stream);
170+
stream.EndElement("item");
171+
}
172+
}
173+
174+
class DbStringVectorXmlHandler : public XmlHandler {
175+
public:
176+
DbStringVectorXmlHandler(std::vector<DBString>& ref) : ref(ref) {}
177+
178+
void StartElement(XmlReader& stream, const char* name, const char** atts) {
179+
if (strcmp(name, "item") != 0) {
180+
stream.Error("Expecting %s but got %s", "item", name);
181+
return;
182+
}
183+
184+
int last_id = -1;
185+
int id = -1;
186+
for (int i = 0; atts[i] != NULL && atts[i + 1] != NULL; i += 2) {
187+
if (strcmp(atts[i], "id") == 0) {
188+
id = atoi(atts[i + 1]);
189+
break;
190+
}
191+
}
192+
193+
if (id <= last_id || id < -1) {
194+
stream.Error("Bad Id %d / %d", id, last_id);
195+
return;
196+
}
197+
198+
ref.resize(id);
199+
DBString& obj = ref.back();
200+
201+
stream.SetHandler(new DbStringXmlHandler(obj));
202+
}
203+
private:
204+
std::vector<DBString>& ref;
205+
};
206+
207+
void RawStruct<std::vector<DBString>>::BeginXml(std::vector<DBString>& obj, XmlReader& stream) {
208+
stream.SetHandler(new DbStringVectorXmlHandler(obj));
209+
}
210+
211+
} //namspace lcf

src/lcf/reader_lcf.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@ class LcfReader {
129129
*/
130130
int ReadInt();
131131

132+
/**
133+
* Reads a compressed 64bit unsigned integer from the stream.
134+
*
135+
* @return The decompressed integer.
136+
*/
137+
uint64_t ReadUInt64();
138+
132139
/**
133140
* Reads a string.
134141
*
@@ -204,6 +211,14 @@ class LcfReader {
204211
*/
205212
static int IntSize(unsigned int x);
206213

214+
/**
215+
* Calculates the size of a compressed 64bit unsigned integer
216+
*
217+
* @param x the integer.
218+
* @return the compressed size.
219+
*/
220+
static int UInt64Size(uint64_t x);
221+
207222
/** @return a buffer which can be reused for parsing */
208223
std::vector<int32_t>& IntBuffer();
209224

src/lcf/writer_lcf.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ class LcfWriter {
8282
*/
8383
void WriteInt(int val);
8484

85+
/**
86+
* Write a compressed 64bit unsigned integer to the stream.
87+
*
88+
* @return The integer.
89+
*/
90+
void WriteUInt64(uint64_t val);
91+
8592
/**
8693
* Write a vector of primitive values to the stream.
8794
*

src/reader_lcf.cpp

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <cstdarg>
1111
#include <cstdio>
1212
#include <istream>
13+
#include <limits>
1314

1415
#include "lcf/reader_lcf.h"
1516

@@ -100,6 +101,27 @@ int LcfReader::ReadInt() {
100101
return loops > 5 ? 0 : value;
101102
}
102103

104+
uint64_t LcfReader::ReadUInt64() {
105+
uint64_t value = 0;
106+
unsigned char temp = 0;
107+
int loops = 0;
108+
do {
109+
value <<= 7;
110+
if (Read0(&temp, 1, 1) == 0) {
111+
assert(value == 0);
112+
return 0;
113+
}
114+
value |= static_cast<uint64_t>(temp & 0x7F);
115+
116+
if (loops > 9) {
117+
fprintf(stderr, "Invalid compressed integer at %" PRIu32 "\n", Tell());
118+
}
119+
++loops;
120+
} while (temp & 0x80);
121+
122+
return loops > 9 ? 0 : value;
123+
}
124+
103125
template <>
104126
void LcfReader::Read<int32_t>(int32_t& ref) {
105127
ref = ReadInt();
@@ -112,7 +134,7 @@ void LcfReader::Read<double>(double& ref) {
112134
}
113135

114136
template <>
115-
void LcfReader::Read<bool>(std::vector<bool> &buffer, size_t size) {
137+
void LcfReader::Read<bool>(std::vector<bool>& buffer, size_t size) {
116138
buffer.clear();
117139

118140
for (unsigned i = 0; i < size; ++i) {
@@ -123,7 +145,7 @@ void LcfReader::Read<bool>(std::vector<bool> &buffer, size_t size) {
123145
}
124146

125147
template <>
126-
void LcfReader::Read<uint8_t>(std::vector<uint8_t> &buffer, size_t size) {
148+
void LcfReader::Read<uint8_t>(std::vector<uint8_t>& buffer, size_t size) {
127149
buffer.clear();
128150

129151
for (unsigned int i = 0; i < size; ++i) {
@@ -134,7 +156,7 @@ void LcfReader::Read<uint8_t>(std::vector<uint8_t> &buffer, size_t size) {
134156
}
135157

136158
template <>
137-
void LcfReader::Read<int16_t>(std::vector<int16_t> &buffer, size_t size) {
159+
void LcfReader::Read<int16_t>(std::vector<int16_t>& buffer, size_t size) {
138160
buffer.clear();
139161
size_t items = size / 2;
140162
for (unsigned int i = 0; i < items; ++i) {
@@ -150,7 +172,7 @@ void LcfReader::Read<int16_t>(std::vector<int16_t> &buffer, size_t size) {
150172
}
151173

152174
template <>
153-
void LcfReader::Read<int32_t>(std::vector<int32_t> &buffer, size_t size) {
175+
void LcfReader::Read<int32_t>(std::vector<int32_t>& buffer, size_t size) {
154176
buffer.clear();
155177
size_t items = size / 4;
156178
for (unsigned int i = 0; i < items; ++i) {
@@ -166,7 +188,7 @@ void LcfReader::Read<int32_t>(std::vector<int32_t> &buffer, size_t size) {
166188
}
167189

168190
template <>
169-
void LcfReader::Read<uint32_t>(std::vector<uint32_t> &buffer, size_t size) {
191+
void LcfReader::Read<uint32_t>(std::vector<uint32_t>& buffer, size_t size) {
170192
buffer.clear();
171193
size_t items = size / 4;
172194
for (unsigned int i = 0; i < items; ++i) {
@@ -181,7 +203,7 @@ void LcfReader::Read<uint32_t>(std::vector<uint32_t> &buffer, size_t size) {
181203
}
182204
}
183205

184-
void LcfReader::ReadBits(DBBitArray &buffer, size_t size) {
206+
void LcfReader::ReadBits(DBBitArray& buffer, size_t size) {
185207
buffer = DBBitArray(size);
186208
for (size_t i = 0; i < size; ++i) {
187209
uint8_t val;
@@ -202,8 +224,6 @@ void LcfReader::ReadString(DBString& ref, size_t size) {
202224
ref = DBString(tmp);
203225
}
204226

205-
206-
207227
bool LcfReader::IsOk() const {
208228
return stream.good() && encoder.IsOk();
209229
}
@@ -303,6 +323,15 @@ int LcfReader::IntSize(unsigned int x) {
303323
return result;
304324
}
305325

326+
int LcfReader::UInt64Size(uint64_t x) {
327+
int result = 0;
328+
do {
329+
x >>= 7;
330+
result++;
331+
} while (x != 0);
332+
return result;
333+
}
334+
306335
#ifdef WORDS_BIGENDIAN
307336
void LcfReader::SwapByteOrder(uint16_t& us)
308337
{

0 commit comments

Comments
 (0)