tweak C1-era CARR
[openc2e.git] / pray.cpp
blob5310223ac7cc8774ce9cfffb8e3a869eb30f1f95
1 /*
2 * pray.cpp
3 * openc2e
5 * Created by Alyssa Milburn on Mon Jan 16 2006.
6 * Copyright (c) 2006 Alyssa Milburn. All rights reserved.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
20 #include "pray.h"
21 #include "exceptions.h"
22 #include "endianlove.h"
23 #include "zlib.h"
25 prayFile::prayFile(fs::path filepath) {
26 path = filepath;
27 file.open(path.native_directory_string().c_str(), std::ios::binary);
28 if (!file.is_open())
29 throw creaturesException(std::string("couldn't open PRAY file \"") + path.native_directory_string() + "\"");
31 char majic[4];
32 file.read(majic, 4);
33 if (strncmp(majic, "PRAY", 4) != 0)
34 throw creaturesException(std::string("bad magic of PRAY file \"") + path.native_directory_string() + "\"");
36 while (!file.eof()) {
37 // TODO: catch exceptions, and free all blocks before passing it up the stack
38 prayBlock *b = new prayBlock(this);
39 blocks.push_back(b);
41 file.peek(); // make sure eof() gets set
45 prayFile::~prayFile() {
46 for (std::vector<prayBlock *>::iterator i = blocks.begin(); i != blocks.end(); i++) {
47 delete *i;
51 prayBlock::prayBlock(prayFile *p) {
52 std::istream &file = p->getStream();
54 char stringid[5]; stringid[4] = 0;
55 file.read(stringid, 4);
56 type = stringid;
58 char nameid[129]; nameid[128] = 0;
59 file.read(nameid, 128);
60 name = nameid;
62 file.read((char *)&compressedsize, 4); compressedsize = swapEndianLong(compressedsize);
63 file.read((char *)&size, 4); size = swapEndianLong(size);
64 unsigned int flags;
65 file.read((char *)&flags, 4); flags = swapEndianLong(flags);
66 compressed = ((flags & 1) == 1);
67 if (!compressed && size != compressedsize)
68 throw creaturesException("Size doesn't match compressed size for uncompressed block.");
70 // Skip the data for this block.
71 offset = file.tellg();
72 file.seekg(compressedsize, std::ios::cur);
74 loaded = false;
75 tagsloaded = false;
76 buffer = 0;
77 parent = p;
80 prayBlock::~prayBlock() {
81 if (loaded)
82 delete[] buffer;
85 void prayBlock::load() {
86 std::istream &file = parent->getStream();
88 file.clear();
89 file.seekg(offset);
90 if (!file.good())
91 throw creaturesException("Failed to seek to block offset.");
93 buffer = new unsigned char[size];
94 if (compressed) {
95 // TODO: check pray_uncompress_sanity_check
96 char *src = new char[compressedsize];
97 file.read(src, compressedsize);
98 if (!file.good()) {
99 delete[] buffer;
100 throw creaturesException("Failed to read all of compressed block.");
102 uLongf usize = size;
103 int r = uncompress((Bytef *)buffer, (uLongf *)&usize, (Bytef *)src, compressedsize);
104 if (r != Z_OK) {
105 delete[] buffer; delete[] src;
106 std::string o = "Unknown error";
107 switch (r) {
108 case Z_MEM_ERROR: o = "Out of memory"; break;
109 case Z_BUF_ERROR: o = "Out of buffer space"; break;
110 case Z_DATA_ERROR: o = "Corrupt data"; break;
112 o = o + " while decompressing PRAY block \"" + name + "\"";
113 throw creaturesException(o);
115 delete[] src;
116 if (usize != size) {
117 delete[] buffer;
118 throw creaturesException("Decompressed data is not the correct size.");
120 } else {
121 file.read((char *)buffer, size);
122 if (!file.good()) {
123 delete[] buffer;
124 throw creaturesException("Failed to read all of uncompressed block.");
127 loaded = true;
130 std::string tagStringRead(unsigned char *&ptr) {
131 unsigned int len = *(unsigned int *)ptr;
132 len = swapEndianLong(len);
133 ptr += 4;
135 unsigned char *data = ptr;
136 ptr += len;
138 return std::string((char *)data, len);
141 void prayBlock::parseTags() {
142 if (tagsloaded) return;
144 if (!loaded)
145 load();
147 tagsloaded = true;
149 unsigned char *ptr = buffer;
151 unsigned int nointvalues = swapEndianLong(*(unsigned int *)ptr); ptr += 4;
153 for (unsigned int i = 0; i < nointvalues; i++) {
154 std::string n = tagStringRead(ptr);
155 unsigned int v = swapEndianLong(*(unsigned int *)ptr); ptr += 4;
157 if (integerValues.find(n) != integerValues.end())
158 throw creaturesException(std::string("Duplicate tag \"") + n + "\"");
159 integerValues[n] = v;
162 unsigned int nostrvalues = swapEndianLong(*(unsigned int *)ptr); ptr += 4;
164 for (unsigned int i = 0; i < nostrvalues; i++) {
165 std::string n = tagStringRead(ptr);
166 std::string v = tagStringRead(ptr);
167 if (stringValues.find(n) != stringValues.end()) // TODO: check integers too?
168 throw creaturesException(std::string("Duplicate tag \"") + n + "\"");
169 stringValues[n] = v;
173 /* vim: set noet: */