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