1 /*
2 * Copyright (C) 2000-2013 The Exult Team
3 *
4 * Original file by Dancer A.L Vesperman
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #define DEBUGFLEX 0
26
27 #include "Flex.h"
28
29 #include <fstream>
30 #if DEBUGFLEX
31 # include <iostream>
32 #endif
33 #include "exceptions.h"
34 #include "utils.h"
35 #include "databuf.h"
36
37 using std::size_t;
38 using std::string;
39
read(IDataSource * in)40 bool Flex_header::read(IDataSource* in) {
41 in->read(title, FLEX_TITLE_LEN);
42 magic1 = in->read4();
43 count = in->read4();
44 magic2 = in->read4();
45 for (uint32& elem : padding) {
46 elem = in->read4();
47 }
48 return magic1 == FLEX_MAGIC1;
49 }
50
51 /**
52 * Write out a FLEX header. Note that this is a STATIC method.
53 * @param out DataSource to which the data is written.
54 * @param title Flex file title.
55 * @param count Number of entries in the flex.
56 * @param vers Version of the flex file.
57 */
58
write(ODataSource * out,const char * title,size_t count,Flex_header::Flex_vers vers)59 void Flex_header::write(
60 ODataSource *out, // File to write to.
61 const char *title,
62 size_t count, // # entries.
63 Flex_header::Flex_vers vers
64 ) {
65 string titlebuf(title); // Use savename for title.
66 titlebuf.resize(FLEX_TITLE_LEN, '\0');
67 out->write(titlebuf);
68 out->write4(FLEX_MAGIC1); // Magic number.
69 out->write4(static_cast<uint32>(count));
70 if (vers == orig) { // 2nd magic #: use for version.
71 out->write4(FLEX_MAGIC2);
72 } else {
73 out->write4(EXULT_FLEX_MAGIC2 + static_cast<uint32>(vers));
74 }
75 for (size_t ii = 0; ii < FLEX_HEADER_PADDING + 2 * count; ii++) {
76 out->write4(0);
77 }
78 }
79
80 /**
81 * Verify if a file is a FLEX. Note that this is a STATIC method.
82 * @param in DataSource to verify.
83 * @return Whether or not the DataSource is a FLEX file.
84 */
is_flex(IDataSource * in)85 bool Flex_header::is_flex(IDataSource *in) {
86 const size_t pos = in->getPos(); // Fill to data (past table at 0x80).
87 const size_t len = in->getSize(); // Check length.
88 uint32 magic = 0;
89 if (len >= FLEX_HEADER_LEN) { // Has to be at least this long.
90 in->seek(FLEX_TITLE_LEN);
91 magic = in->read4();
92 }
93 in->seek(pos);
94
95 // Is a flex?
96 return magic == Flex_header::FLEX_MAGIC1;
97 }
98
99 /**
100 * Reads the header from a flex and builds an object index.
101 */
index_file()102 void Flex::index_file() {
103 if (!data) {
104 throw file_read_exception(identifier.name);
105 }
106 start_pos = data->getPos();
107 if (!hdr.read(data.get())) {
108 // Not a flex file.
109 throw wrong_file_type_exception(identifier.name, "FLEX");
110 }
111 #if DEBUGFLEX
112 cout << "Title: " << hdr.title << endl;
113 cout << "Count: " << hdr.count << endl;
114 #endif
115
116 // We should already be there.
117 data->seek(start_pos + Flex_header::FLEX_HEADER_LEN);
118 for (uint32 c = 0; c < hdr.count; c++) {
119 Reference f;
120 f.offset = data->read4() + start_pos;
121 f.size = data->read4();
122 #if DEBUGFLEX
123 cout << "Item " << c << ": " << f.size << " bytes @ " << f.offset << endl;
124 #endif
125 object_list.push_back(f);
126 }
127 }
128
129 /**
130 * Obtains information about an object from the flex.
131 * @param objnum Number of object.
132 * @param len Receives the length of the object, or zero in any failure.
133 * @return Offset of object from the beginning of the file.
134 */
get_entry_info(uint32 objnum,size_t & len)135 size_t Flex::get_entry_info(uint32 objnum, size_t &len) {
136 if (!data || objnum >= object_list.size()) {
137 len = 0;
138 return 0;
139 }
140 len = object_list[objnum].size;
141 return object_list[objnum].offset;
142 }
143
144 /**
145 * Verify if a file is a FLEX. Note that this is a STATIC method.
146 * @param fname Name of file to verify.
147 * @return Whether or not the file is a FLEX file. Returns false if
148 * the file does not exist.
149 */
is_flex(const std::string & fname)150 bool Flex::is_flex(const std::string& fname) {
151 IFileDataSource ds(fname.c_str());
152 return ds.good() && is_flex(&ds);
153 }
154
155 /**
156 * Start writing out a new Flex file.
157 */
Flex_writer(OStreamDataSource & o,const char * title,size_t cnt,Flex_header::Flex_vers vers)158 Flex_writer::Flex_writer(
159 OStreamDataSource& o, ///< Where to write.
160 const char *title, ///< Flex title.
161 size_t cnt, ///< Number of entries we'll write.
162 Flex_header::Flex_vers vers ///< Version of flex file.
163 ) : dout(o), count(cnt), start_pos(dout.getPos()) {
164 // Write out header.
165 Flex_header::write(&dout, title, count, vers);
166 // Create table.
167 table = std::make_unique<uint8[]>(2 * count * 4);
168 tptr = table.get();
169 cur_start = dout.getPos(); // Store start of 1st entry.
170 }
171
172 /**
173 * Clean up.
174 */
175
~Flex_writer()176 Flex_writer::~Flex_writer(
177 ) {
178 flush();
179 }
180
flush()181 void Flex_writer::flush(
182 ) {
183 if (table) {
184 dout.seek(Flex_header::FLEX_HEADER_LEN); // Write table.
185 dout.write(table.get(), 2 * count * 4);
186 dout.flush();
187 table.reset();
188 }
189 }
190
191 /**
192 * Call this when done writing out a section.
193 */
194
finish_object()195 void Flex_writer::finish_object(
196 ) {
197 // Location past end of section.
198 size_t pos = dout.getPos();
199 Write4(tptr, static_cast<uint32>(cur_start - start_pos)); // Store start of section.
200 Write4(tptr, static_cast<uint32>(pos - cur_start)); // Store length.
201 cur_start = pos;
202 }
203
204