Horizon Official Technical Documentation
GRF Class Reference

#include <GRF.hpp>

Public Member Functions

 GRF ()
 
grf_load_result_type load ()
 
void extractAllFiles ()
 
void extractFile (std::string file_name, std::string output_path, clock_t begin_time)
 
std::pair< grf_read_error_type, uint8_t * > read (const char *in_name, int *size)
 
void decode (unsigned char *buf, size_t len, char entry_type, int entry_len)
 Decodes grf data. More...
 
void decodeFull (unsigned char *buf, size_t len, int cycle)
 
void decodeHeader (unsigned char *buf, size_t len)
 
void decodeShuffledBytes (BIT64 *src)
 Substitutes some specific values for others, leaves rest intact. More...
 
uint8_t substituteObfuscatedByte (uint8_t in)
 Substitutes some specific values for others, leaves rest intact. More...
 
uint8_t get_id ()
 
void set_id (uint8_t id)
 
const boost::filesystem::path & getGRFPath () const
 
void setGRFPath (std::string const &path)
 
std::size_t getGRFSize () const
 
void setGRFSize (std::size_t const &size)
 
int getGRFVersion () const
 
void setGRFVersion (int version)
 
int getTotalFiles () const
 
void setTotalFiles (int total)
 
FileMapTypegetFileMap ()
 
FileErrorMapTypegetFileErrorMap ()
 

Private Types

typedef std::unordered_map< std::string, std::shared_ptr< DataFile > > FileMapType
 
typedef std::unordered_map< std::string, grf_file_error_typeFileErrorMapType
 

Private Attributes

int _id
 
boost::filesystem::path _path
 
std::size_t _grf_size
 
int _grf_version
 
int _total_files
 
FileMapType _file_map
 
FileErrorMapType _file_error_map
 

Member Typedef Documentation

◆ FileErrorMapType

typedef std::unordered_map<std::string, grf_file_error_type> GRF::FileErrorMapType
private

◆ FileMapType

typedef std::unordered_map<std::string, std::shared_ptr<DataFile> > GRF::FileMapType
private

Constructor & Destructor Documentation

◆ GRF()

GRF::GRF ( )
49{
50 //
51}

Member Function Documentation

◆ decode()

void GRF::decode ( unsigned char *  buf,
size_t  len,
char  entry_type,
int  entry_len 
)

Decodes grf data.

Parameters
bufdata to decode (in-place)
lenlength of the data
entry_typeflags associated with the data
entry_lentrue (unaligned) length of the data
163{
164 if (entry_type & DATAFILE_TYPE_DES_MIXED) { // fully encrypted
165 int digits;
166 int cycle;
167 int i;
168
169 // compute number of digits of the entry length
170 digits = 1;
171 for( i = 10; i <= entry_len; i *= 10 )
172 ++digits;
173
174 // choose size of gap between two encrypted blocks
175 // digits: 0 1 2 3 4 5 6 7 8 9 ...
176 // cycle: 1 1 1 4 5 14 15 22 23 24 ...
177 cycle = ( digits < 3 ) ? 1
178 : ( digits < 5 ) ? digits + 1
179 : ( digits < 7 ) ? digits + 9
180 : digits + 15;
181
182 decodeFull(buf, len, cycle);
183 } else if (entry_type & DATAFILE_TYPE_DES_HEADER) { // header encrypted
184 decodeHeader(buf, len);
185 }
186}
@ DATAFILE_TYPE_DES_MIXED
Definition: GRF.hpp:42
@ DATAFILE_TYPE_DES_HEADER
Definition: GRF.hpp:43
void decodeFull(unsigned char *buf, size_t len, int cycle)
Definition: GRF.cpp:120
void decodeHeader(unsigned char *buf, size_t len)
Definition: GRF.cpp:105

References DATAFILE_TYPE_DES_HEADER, DATAFILE_TYPE_DES_MIXED, decodeFull(), and decodeHeader().

Referenced by read().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ decodeFull()

void GRF::decodeFull ( unsigned char *  buf,
size_t  len,
int  cycle 
)
121{
122 BIT64 *p = (BIT64 *)buf;
123 size_t nblocks = len / sizeof(BIT64);
124 int dcycle, scycle;
125 size_t i, j;
126
127 DES des;
128
129 // first 20 blocks are all des-encrypted
130 for (i = 0; i < 20 && i < nblocks; ++i)
131 des.decryptBlock(&p[i]);
132
133 // after that only one of every 'dcycle' blocks is des-encrypted
134 dcycle = cycle;
135
136 // and one of every 'scycle' plaintext blocks is shuffled (starting from the 0th but skipping the 0th)
137 scycle = 7;
138
139 // so decrypt/de-shuffle periodically
140 j = -1; // 0, adjusted to fit the ++j step
141 for (i = 20; i < nblocks; ++i) {
142 if (i % dcycle == 0) { // decrypt block
143 des.decryptBlock(&p[i]);
144 continue;
145 }
146
147 ++j;
148
149 if (j % scycle == 0 && j != 0) { // de-shuffle block
150 decodeShuffledBytes(&p[i]);
151 continue;
152 }
153 // plaintext, do nothing.
154 }
155}
struct BIT64 BIT64
One 64-bit block.
Definition: DES.hpp:46
void decryptBlock(BIT64 *block)
Definition: DES.cpp:208
void decodeShuffledBytes(BIT64 *src)
Substitutes some specific values for others, leaves rest intact.
Definition: GRF.cpp:89
One 64-bit block.
Definition: DES.hpp:42

References decodeShuffledBytes(), and DES::decryptBlock().

Referenced by decode().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ decodeHeader()

void GRF::decodeHeader ( unsigned char *  buf,
size_t  len 
)
106{
107 BIT64* p = (BIT64 *) buf;
108 size_t nblocks = len / sizeof(BIT64);
109 size_t i;
110
111 DES *des = new DES();
112
113 // first 20 blocks are all des-encrypted
114 for (i = 0; i < 20 && i < nblocks; ++i)
115 des->decryptBlock(&p[i]);
116
117 // the rest is plaintext, done.
118}

References DES::decryptBlock().

Referenced by decode().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ decodeShuffledBytes()

void GRF::decodeShuffledBytes ( BIT64 src)

Substitutes some specific values for others, leaves rest intact.

Obfuscation. NOTE: Operation is symmetric (calling it twice gives back the original input).

Parameters
[in|out]src source to be decoded.
90{
91 BIT64 out;
92
93 out.b[0] = src->b[3];
94 out.b[1] = src->b[4];
95 out.b[2] = src->b[6];
96 out.b[3] = src->b[0];
97 out.b[4] = src->b[1];
98 out.b[5] = src->b[2];
99 out.b[6] = src->b[5];
100 out.b[7] = substituteObfuscatedByte(src->b[7]);
101
102 *src = out;
103}
uint8_t substituteObfuscatedByte(uint8_t in)
Substitutes some specific values for others, leaves rest intact.
Definition: GRF.cpp:59
uint8_t b[8]
Definition: DES.hpp:42

References BIT64::b, and substituteObfuscatedByte().

Referenced by decodeFull().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ extractAllFiles()

void GRF::extractAllFiles ( )
275{
276 const clock_t begin_time = clock();
277
278 int i = 0;
279 for (auto &it : getFileMap()) {
280 extractFile(it.second->file_name, ".", begin_time);
281 i++;
282 }
283}
FileMapType & getFileMap()
Definition: GRF.hpp:117
void extractFile(std::string file_name, std::string output_path, clock_t begin_time)
Definition: GRF.cpp:286

References extractFile(), and getFileMap().

+ Here is the call graph for this function:

◆ extractFile()

void GRF::extractFile ( std::string  file_name,
std::string  output_path,
clock_t  begin_time 
)
287{
288 auto elem = getFileMap().find(file_name);
289
290 if (elem == getFileMap().end()) {
291 return;
292 }
293
294 //std::cout << "File '" << elem->first << "' found. Size: " << elem->second->original_size << std::endl;
295
296 std::string utf8_file_path = boost::locale::conv::to_utf<char>(file_name, "Latin1");
297
298 std::vector<std::string> dirs = StrUtils::explode(utf8_file_path, '\\');
299
300 std::string extracted_file_name = (dirs.end() - 1)->c_str();
301
302 utf8_file_path = utf8_file_path.substr(0, std::strstr(utf8_file_path.c_str(), extracted_file_name.c_str()) - utf8_file_path.c_str());
303
304 std::replace(utf8_file_path.begin(), utf8_file_path.end(), '\\', '/');
305
306 try {
307 boost::filesystem::path path = utf8_file_path;
308 if (!boost::filesystem::is_directory(path))
309 boost::filesystem::create_directories(path);
310 } catch (boost::filesystem::filesystem_error &e) {
311 std::cerr << e.what() << std::endl;
312 return;
313 }
314
315 FILE *buf = std::fopen(utf8_file_path.append(extracted_file_name).c_str(), "w");
316
317 if (buf == nullptr) {
318 std::cout << "Extraction failed." << std::endl;
319 return;
320 }
321
322 std::pair<grf_read_error_type, uint8_t*> grf_file = read(elem->second->file_name, nullptr);
323
324 if (grf_file.second == nullptr) {
325 std::cerr << "Failed to extract '" << elem->second->file_name << "'" <<std::endl;
326 return;
327 }
328
329 std::fwrite(grf_file.second, elem->second->original_size, 1, buf);
330
331 delete[] grf_file.second;
332
333 std::fclose(buf);
334
335 file_count++;
336
337 std::cout << "Extracted (" << file_count << "/" << getFileMap().size() << ") '" << file_name << "' "
338 <<"(" << elem->second->compressed_size << " -> " << elem->second->original_size <<") "
339 << float( clock() - begin_time ) / CLOCKS_PER_SEC << "s." << std::endl;
340}
static int file_count
Definition: GRF.cpp:285
std::pair< grf_read_error_type, uint8_t * > read(const char *in_name, int *size)
Definition: GRF.cpp:345
static std::vector< std::string > explode(std::string const &s, char delim)
Definition: StrUtils.hpp:38

References StrUtils::explode(), file_count, getFileMap(), and read().

Referenced by extractAllFiles().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ get_id()

uint8_t GRF::get_id ( )
inline
101{ return _id; }
int _id
Definition: GRF.hpp:121

References _id.

◆ getFileErrorMap()

FileErrorMapType & GRF::getFileErrorMap ( )
inline
118{ return _file_error_map; }
FileErrorMapType _file_error_map
Definition: GRF.hpp:127

References _file_error_map.

Referenced by load().

+ Here is the caller graph for this function:

◆ getFileMap()

FileMapType & GRF::getFileMap ( )
inline
117{ return _file_map; }
FileMapType _file_map
Definition: GRF.hpp:126

References _file_map.

Referenced by extractAllFiles(), extractFile(), load(), and read().

+ Here is the caller graph for this function:

◆ getGRFPath()

const boost::filesystem::path & GRF::getGRFPath ( ) const
inline
105{ return _path; }
boost::filesystem::path _path
Definition: GRF.hpp:122

References _path.

Referenced by load(), Horizon::Tools::MapCache::ParseGRFLoadResult(), Horizon::Libraries::MapCache::ParseGRFReadResult(), and read().

+ Here is the caller graph for this function:

◆ getGRFSize()

std::size_t GRF::getGRFSize ( ) const
inline
108{ return _grf_size; }
std::size_t _grf_size
Definition: GRF.hpp:123

References _grf_size.

Referenced by load().

+ Here is the caller graph for this function:

◆ getGRFVersion()

int GRF::getGRFVersion ( ) const
inline
111{ return _grf_version; }
int _grf_version
Definition: GRF.hpp:124

References _grf_version.

Referenced by load(), and Horizon::Tools::MapCache::ParseGRFLoadResult().

+ Here is the caller graph for this function:

◆ getTotalFiles()

int GRF::getTotalFiles ( ) const
inline
114{ return _total_files; }
int _total_files
Definition: GRF.hpp:125

References _total_files.

Referenced by load().

+ Here is the caller graph for this function:

◆ load()

grf_load_result_type GRF::load ( )

Set the file pointer.

189{
190 unsigned char grf_header[0x2e];
191 std::ifstream grf_ifs(getGRFPath().c_str(), std::ios::in | std::ios::binary);
192
196 if (!grf_ifs.good())
197 return GRF_LOAD_PATH_ERROR;
198
199 // Devise the number of bytes.
200 grf_ifs.seekg(0, grf_ifs.end);
201 _grf_size = grf_ifs.tellg();
202 grf_ifs.seekg(0, grf_ifs.beg);
203
204 if (!grf_ifs.read((char *) grf_header, 0x2e))
206
207 if (std::strcmp((const char *) grf_header, "Master of Magic") != 0)
209
210 if (grf_ifs.seekg(GetLongLong(grf_header + 0x1e), std::ios::cur).fail())
212
213 // Set GRF Version.
214 setGRFVersion((GetLong(grf_header + 0x2a) >> 8));
215
216 if (getGRFVersion() != 0x02)
218
219 unsigned char eheader[8];
220
221 if (!grf_ifs.read((char *) eheader, 8))
223
224 unsigned long compressed_size = GetULong(eheader); // Read Size
225 unsigned long decompressed_size = GetULong(eheader + 4); // Extend Size
226
227 if (compressed_size > (unsigned long) (getGRFSize() - grf_ifs.tellg()))
229
230 uint8_t *compressed_buf = new uint8_t[compressed_size]; // Get a Read Size
231 uint8_t *decompressed_buf = new uint8_t[decompressed_size]; // Get a Extend Size
232
233 if (!grf_ifs.read((char *) compressed_buf, compressed_size)) {
234 delete[] decompressed_buf;
235 delete[] compressed_buf;
236 return GRF_LOAD_READ_ERROR;
237 }
238
239 uncompress(decompressed_buf, &decompressed_size, compressed_buf, compressed_size);
240
241 delete[] compressed_buf;
242
243 setTotalFiles((GetLong(grf_header + 0x26) - 7));
244
245 for (int entry = 0, ofs = 0; entry < getTotalFiles(); ++entry) {
246 std::shared_ptr<DataFile> data_file = std::make_shared<DataFile>();
247 char *fname = (char *) (decompressed_buf + ofs);
248 int ofs2 = ofs + (int) strlen(fname) + 1;
249 int type = decompressed_buf[ofs2 + 12];
250
251 ofs = ofs2 + 17;
252
253 if (strlen(fname) > sizeof(data_file->file_name) - 1) {
254 getFileErrorMap().insert(std::make_pair(fname, GRF_FILE_ERROR_NAME_TOO_LONG));
255 continue;
256 }
257
258 if (type & DATAFILE_TYPE_FILE) {// file
259 data_file->compressed_size = GetLong(decompressed_buf + ofs2 + 0);
260 data_file->compressed_aligned_size = GetLong(decompressed_buf + ofs2 + 4);
261 data_file->original_size = GetLong(decompressed_buf + ofs2 + 8);
262 data_file->entry_position = GetLong(decompressed_buf + ofs2 + 13) + 0x2E;
263 data_file->type = type;
264 std::memcpy(&data_file->file_name, fname, sizeof(data_file->file_name));
265 getFileMap().insert(std::pair<std::string, std::shared_ptr<DataFile>>(data_file->file_name, data_file));
266 }
267 }
268
269 delete[] decompressed_buf;
270
271 return GRF_LOAD_OK;
272}
@ GRF_FILE_ERROR_NAME_TOO_LONG
Definition: GRF.hpp:62
@ DATAFILE_TYPE_FILE
Definition: GRF.hpp:41
@ GRF_LOAD_INCOMPLETE_HEADER
Definition: GRF.hpp:50
@ GRF_LOAD_ILLEGAL_DATA_FORMAT
Definition: GRF.hpp:55
@ GRF_LOAD_OK
Definition: GRF.hpp:48
@ GRF_LOAD_MAGIC_ERROR
Definition: GRF.hpp:51
@ GRF_LOAD_FORMAT_ERROR
Definition: GRF.hpp:52
@ GRF_LOAD_READ_ERROR
Definition: GRF.hpp:56
@ GRF_LOAD_PATH_ERROR
Definition: GRF.hpp:49
@ GRF_LOAD_INVALID_VERSION
Definition: GRF.hpp:53
@ GRF_LOAD_HEADER_READ_ERROR
Definition: GRF.hpp:54
std::atomic< bool > fail
Definition: LockedLookupTableTest.cpp:49
int64_t GetLongLong(const unsigned char *buf)
Definition: Utility.cpp:234
unsigned int GetULong(unsigned char *p)
Definition: Utility.cpp:169
int32_t GetLong(const unsigned char *buf)
Definition: Utility.cpp:229
const boost::filesystem::path & getGRFPath() const
Definition: GRF.hpp:105
FileErrorMapType & getFileErrorMap()
Definition: GRF.hpp:118
int getTotalFiles() const
Definition: GRF.hpp:114
void setTotalFiles(int total)
Definition: GRF.hpp:115
std::size_t getGRFSize() const
Definition: GRF.hpp:108
int getGRFVersion() const
Definition: GRF.hpp:111
void setGRFVersion(int version)
Definition: GRF.hpp:112

References _grf_size, DATAFILE_TYPE_FILE, fail, getFileErrorMap(), getFileMap(), getGRFPath(), getGRFSize(), getGRFVersion(), GetLong(), GetLongLong(), getTotalFiles(), GetULong(), GRF_FILE_ERROR_NAME_TOO_LONG, GRF_LOAD_FORMAT_ERROR, GRF_LOAD_HEADER_READ_ERROR, GRF_LOAD_ILLEGAL_DATA_FORMAT, GRF_LOAD_INCOMPLETE_HEADER, GRF_LOAD_INVALID_VERSION, GRF_LOAD_MAGIC_ERROR, GRF_LOAD_OK, GRF_LOAD_PATH_ERROR, GRF_LOAD_READ_ERROR, setGRFVersion(), and setTotalFiles().

+ Here is the call graph for this function:

◆ read()

std::pair< grf_read_error_type, uint8_t * > GRF::read ( const char *  in_name,
int *  size 
)
346{
347 std::string file_name = in_name;
348 std::shared_ptr<DataFile> entry;
349 uint8_t *file_buf = nullptr;
350
351 auto it = getFileMap().find(file_name);
352
353 if (it == getFileMap().end())
354 return std::make_pair(GRE_NOT_FOUND, nullptr);
355
356 entry = it->second;
357
358 // Archive[GRF] File Read
359 if (entry != nullptr) {
360 std::ifstream grf_ifs(getGRFPath().c_str(), std::ios::in | std::ios::binary);
361 uint8_t *grf_compressed_buf = new uint8_t[entry->compressed_aligned_size];
362
363 grf_ifs.seekg(entry->entry_position, grf_ifs.beg);
364
365 if (!grf_ifs.read((char *) grf_compressed_buf, entry->compressed_aligned_size)) {
366 delete[] grf_compressed_buf;
367 return std::make_pair(GRE_READ_ERROR, nullptr);
368 }
369
370 file_buf = new uint8_t[entry->original_size + 1]; // +1 for resnametable zero-termination
371
372 if (entry->type & DATAFILE_TYPE_FILE) { // file
373 uLongf len;
374 decode(grf_compressed_buf, entry->compressed_aligned_size, entry->type, entry->compressed_size);
375 len = entry->original_size;
376 uncompress(file_buf, &len, grf_compressed_buf, entry->compressed_size);
377 if (len != (uLong) entry->original_size) {
378 delete[] grf_compressed_buf;
379 delete[] file_buf;
380 return std::make_pair(GRE_DECOMPRESS_SIZE_MISMATCH, nullptr);
381 }
382 } else { // directory?
383 std::memcpy(file_buf, grf_compressed_buf, entry->original_size);
384 }
385
386 if (size)
387 *size = entry->original_size;
388
389 delete[] grf_compressed_buf;
390 }
391
392 return std::make_pair(GRE_OK, file_buf);
393}
@ GRE_READ_ERROR
Definition: GRF.hpp:69
@ GRE_NOT_FOUND
Definition: GRF.hpp:68
@ GRE_DECOMPRESS_SIZE_MISMATCH
Definition: GRF.hpp:70
@ GRE_OK
Definition: GRF.hpp:67
void decode(unsigned char *buf, size_t len, char entry_type, int entry_len)
Decodes grf data.
Definition: GRF.cpp:162

References DATAFILE_TYPE_FILE, decode(), getFileMap(), getGRFPath(), GRE_DECOMPRESS_SIZE_MISMATCH, GRE_NOT_FOUND, GRE_OK, and GRE_READ_ERROR.

Referenced by extractFile(), and Horizon::Libraries::MapCache::GetMapFromGRF().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ set_id()

void GRF::set_id ( uint8_t  id)
inline
102{ _id = id; }

References _id.

Referenced by Horizon::Libraries::MapCache::ReadGRFListConfig().

+ Here is the caller graph for this function:

◆ setGRFPath()

void GRF::setGRFPath ( std::string const &  path)
inline
106{ _path = path; }

References _path.

Referenced by Horizon::Libraries::MapCache::ReadGRFListConfig().

+ Here is the caller graph for this function:

◆ setGRFSize()

void GRF::setGRFSize ( std::size_t const &  size)
inline
109{ _grf_size = size; }

References _grf_size.

◆ setGRFVersion()

void GRF::setGRFVersion ( int  version)
inline
112{ _grf_version = version; }

References _grf_version.

Referenced by load().

+ Here is the caller graph for this function:

◆ setTotalFiles()

void GRF::setTotalFiles ( int  total)
inline
115{ _total_files = total; }

References _total_files.

Referenced by load().

+ Here is the caller graph for this function:

◆ substituteObfuscatedByte()

uint8_t GRF::substituteObfuscatedByte ( uint8_t  in)

Substitutes some specific values for others, leaves rest intact.

Obfuscation. NOTE: Operation is symmetric (calling it twice gives back the original input).

Parameters
[in]inthe byte needed to be swapped.
Returns
the substitute or itself if none.
60{
61 uint8_t out;
62
63 switch (in) {
64 case 0x00: out = 0x2B; break;
65 case 0x2B: out = 0x00; break;
66 case 0x6C: out = 0x80; break;
67 case 0x01: out = 0x68; break;
68 case 0x68: out = 0x01; break;
69 case 0x48: out = 0x77; break;
70 case 0x60: out = 0xFF; break;
71 case 0x77: out = 0x48; break;
72 case 0xB9: out = 0xC0; break;
73 case 0xC0: out = 0xB9; break;
74 case 0xFE: out = 0xEB; break;
75 case 0xEB: out = 0xFE; break;
76 case 0x80: out = 0x6C; break;
77 case 0xFF: out = 0x60; break;
78 default: out = in; break;
79 }
80
81 return out;
82}

Referenced by decodeShuffledBytes().

+ Here is the caller graph for this function:

Member Data Documentation

◆ _file_error_map

FileErrorMapType GRF::_file_error_map
private

Referenced by getFileErrorMap().

◆ _file_map

FileMapType GRF::_file_map
private

Referenced by getFileMap().

◆ _grf_size

std::size_t GRF::_grf_size
private

Referenced by getGRFSize(), load(), and setGRFSize().

◆ _grf_version

int GRF::_grf_version
private

Referenced by getGRFVersion(), and setGRFVersion().

◆ _id

int GRF::_id
private

Referenced by get_id(), and set_id().

◆ _path

boost::filesystem::path GRF::_path
private

Referenced by getGRFPath(), and setGRFPath().

◆ _total_files

int GRF::_total_files
private

Referenced by getTotalFiles(), and setTotalFiles().


The documentation for this class was generated from the following files: