1 /*
2  * Copyright 2011-2013 Arx Libertatis Team (see the AUTHORS file)
3  *
4  * This file is part of Arx Libertatis.
5  *
6  * Arx Libertatis 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 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Arx Libertatis 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 Arx Libertatis.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 /* Based on:
20 ===========================================================================
21 ARX FATALIS GPL Source Code
22 Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
23 
24 This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
25 
26 Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
27 License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
28 
29 Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
30 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
31 
32 You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code.  If not, see
33 <http://www.gnu.org/licenses/>.
34 
35 In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
36 additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
37 Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
38 
39 If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
40 ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
41 ===========================================================================
42 */
43 // Initial Code: Cyril Meynier
44 //
45 // Copyright (c) 1999-2001 ARKANE Studios SA. All rights reserved
46 
47 /*!
48  * ARX FTL file loading and saving
49  * FTL files contains Optimised/Pre-computed versions of objects for faster loads
50  */
51 
52 #include "graphics/data/FTL.h"
53 
54 #include <cstdlib>
55 #include <cstring>
56 
57 #include <boost/algorithm/string/case_conv.hpp>
58 #include <boost/static_assert.hpp>
59 
60 #include "graphics/data/FTLFormat.h"
61 #include "graphics/data/TextureContainer.h"
62 
63 #include "io/fs/FilePath.h"
64 #include "io/fs/FileStream.h"
65 #include "io/fs/Filesystem.h"
66 #include "io/resource/ResourcePath.h"
67 #include "io/resource/PakReader.h"
68 #include "io/Blast.h"
69 #include "io/Implode.h"
70 #include "io/IO.h"
71 #include "io/log/Logger.h"
72 
73 #include "scene/Object.h"
74 
75 #include "util/String.h"
76 
77 using std::string;
78 using std::vector;
79 
80 #ifdef BUILD_EDIT_LOADSAVE
81 
ARX_FTL_Save(const fs::path & file,const EERIE_3DOBJ * obj)82 bool ARX_FTL_Save(const fs::path & file, const EERIE_3DOBJ * obj) {
83 
84 	LogWarning << "ARX_FTL_Save " << file;
85 
86 	if(!obj) {
87 		return false;
88 	}
89 
90 	// Generate File name/path and create it
91 	fs::path gamefic = "game" / file;
92 	gamefic.set_ext("ftl");
93 
94 	if(!fs::create_directories(gamefic.parent())) {
95 		return false;
96 	}
97 
98 	// Compute allocsize...
99 	size_t allocsize = sizeof(ARX_FTL_PRIMARY_HEADER)
100 	                   + 512 //checksum
101 	                   + sizeof(ARX_FTL_SECONDARY_HEADER)
102 	                   + sizeof(ARX_FTL_3D_DATA_HEADER)
103 	                   + sizeof(EERIE_OLD_VERTEX) * obj->vertexlist.size()
104 	                   + sizeof(EERIE_FACE_FTL) * obj->facelist.size()
105 	                   + sizeof(Texture_Container_FTL) * obj->texturecontainer.size()
106 	                   + sizeof(EERIE_ACTIONLIST_FTL) * obj->actionlist.size()
107 	                   + 128000; // TODO just in case...
108 
109 	if(obj->nbgroups > 0) {
110 		allocsize += sizeof(EERIE_GROUPLIST_FTL) * obj->nbgroups;
111 		for(long i = 0; i < obj->nbgroups; i++) {
112 			 allocsize += sizeof(long) * obj->grouplist[i].indexes.size();
113 		}
114 	}
115 
116 	if(!obj->selections.empty()) {
117 		allocsize += sizeof(EERIE_SELECTIONS_FTL) * obj->selections.size();
118 		for(size_t i = 0; i < obj->selections.size(); i++) {
119 			allocsize += sizeof(long) * obj->selections[i].selected.size();
120 		}
121 	}
122 
123 	if(obj->sdata && !obj->sdata->spheres.empty()) {
124 		allocsize += sizeof(ARX_FTL_COLLISION_SPHERES_DATA_HEADER);
125 		allocsize += sizeof(COLLISION_SPHERE_FTL) * obj->sdata->spheres.size();
126 	}
127 
128 	if(obj->cdata) {
129 		allocsize += sizeof(ARX_FTL_CLOTHES_DATA_HEADER);
130 		allocsize += sizeof(CLOTHESVERTEX_FTL) * obj->cdata->nb_cvert;
131 		allocsize += sizeof(EERIE_SPRINGS_FTL) * obj->cdata->springs.size();
132 	}
133 
134 	// Finished computing allocsize Now allocate it...
135 	char * dat = new char[allocsize];
136 	size_t pos = 0;
137 
138 	memset(dat, 0, allocsize);
139 
140 	// Primary Header
141 	{
142 		ARX_FTL_PRIMARY_HEADER afph;
143 
144 		afph.ident[0] = 'F';
145 		afph.ident[1] = 'T';
146 		afph.ident[2] = 'L';
147 		afph.ident[3] = '0';
148 		afph.version = CURRENT_FTL_VERSION;
149 
150 		memcpy(dat + pos, &afph, sizeof(ARX_FTL_PRIMARY_HEADER));
151 		pos += sizeof(ARX_FTL_PRIMARY_HEADER);
152 	}
153 
154 	// Identification
155 	char check[512];
156 	HERMES_CreateFileCheck(file, check, 512, CURRENT_FTL_VERSION);
157 	memcpy(dat + pos, check, 512);
158 	pos += 512;
159 
160 	// Secondary Header
161 	ARX_FTL_SECONDARY_HEADER*afsh = (ARX_FTL_SECONDARY_HEADER *)(dat + pos);
162 	pos += sizeof(ARX_FTL_SECONDARY_HEADER);
163 
164 	if (pos > allocsize) LogError << ("Invalid Allocsize in ARX_FTL_Save");
165 
166 	// 3D Data
167 	afsh->offset_3Ddata = pos; //-1;
168 	ARX_FTL_3D_DATA_HEADER * af3Ddh = (ARX_FTL_3D_DATA_HEADER *)(dat + afsh->offset_3Ddata);
169 	pos += sizeof(ARX_FTL_3D_DATA_HEADER);
170 
171 	if (pos > allocsize) LogError << ("Invalid Allocsize in ARX_FTL_Save");
172 
173 	af3Ddh->nb_vertex = obj->vertexlist.size();
174 	af3Ddh->nb_faces = obj->facelist.size();
175 	af3Ddh->nb_maps = obj->texturecontainer.size();
176 	af3Ddh->nb_groups = obj->nbgroups;
177 	af3Ddh->nb_action = obj->actionlist.size();
178 	af3Ddh->nb_selections = obj->selections.size();
179 	af3Ddh->origin = obj->origin;
180 
181 	// vertexes
182 	if (af3Ddh->nb_vertex > 0) {
183 		std::copy(obj->vertexlist.begin(), obj->vertexlist.end(), (EERIE_OLD_VERTEX*)(dat + pos));
184 		pos += sizeof(EERIE_OLD_VERTEX) * obj->vertexlist.size();
185 
186 		if (pos > allocsize) LogError << ("Invalid Allocsize in ARX_FTL_Save");
187 	}
188 
189 	// faces
190 	if (af3Ddh->nb_faces > 0)
191 	{
192 		for (long ii = 0; ii < af3Ddh->nb_faces; ii++)
193 		{
194 			EERIE_FACE_FTL * eff = (EERIE_FACE_FTL *)(dat + pos);
195 			eff->facetype = obj->facelist[ii].facetype;
196 			eff->texid = obj->facelist[ii].texid;
197 			eff->transval = obj->facelist[ii].transval;
198 			eff->temp = obj->facelist[ii].temp;
199 			eff->norm = obj->facelist[ii].norm;
200 
201 			for (long kk = 0; kk < IOPOLYVERT; kk++)
202 			{
203 				eff->nrmls[kk] = obj->facelist[ii].nrmls[kk];
204 				eff->vid[kk] = obj->facelist[ii].vid[kk];
205 				eff->u[kk] = obj->facelist[ii].u[kk];
206 				eff->v[kk] = obj->facelist[ii].v[kk];
207 				eff->ou[kk] = obj->facelist[ii].ou[kk];
208 				eff->ov[kk] = obj->facelist[ii].ov[kk];
209 				eff->rgb[kk] = 0;
210 			}
211 
212 			pos += sizeof(EERIE_FACE_FTL);
213 			if (pos > allocsize) LogError << ("Invalid Allocsize in ARX_FTL_Save");
214 		}
215 	}
216 
217 	// textures
218 	for (long i = 0; i < af3Ddh->nb_maps; i++)
219 	{
220 		char ficc[256];
221 		memset(ficc, 0, 256);
222 
223 		if (obj->texturecontainer[i])
224 			strcpy(ficc, obj->texturecontainer[i]->m_texName.string().c_str());
225 
226 		memcpy(dat + pos, ficc, 256);
227 		pos += 256;
228 
229 		if (pos > allocsize) LogError << ("Invalid Allocsize in ARX_FTL_Save");
230 	}
231 
232 	// groups
233 	if (af3Ddh->nb_groups > 0)
234 	{
235 		std::copy(obj->grouplist, obj->grouplist + obj->nbgroups, (EERIE_GROUPLIST_FTL*)(dat + pos));
236 		pos += sizeof(EERIE_GROUPLIST_FTL) * af3Ddh->nb_groups;
237 
238 		if (pos > allocsize) LogError << ("Invalid Allocsize in ARX_FTL_Save");
239 
240 		for(int i = 0; i < af3Ddh->nb_groups; i++) {
241 			if(!obj->grouplist[i].indexes.empty()) {
242 				std::copy(obj->grouplist[i].indexes.begin(), obj->grouplist[i].indexes.end(), (s32*)(dat + pos));
243 				pos += sizeof(s32) * obj->grouplist[i].indexes.size();
244 			}
245 		}
246 	}
247 
248 	// actionpoints
249 	if(af3Ddh->nb_action > 0) {
250 		std::copy(obj->actionlist.begin(), obj->actionlist.end(), (EERIE_ACTIONLIST_FTL*)(dat + pos));
251 		pos += sizeof(EERIE_ACTIONLIST_FTL) * af3Ddh->nb_action;
252 
253 		if (pos > allocsize) LogError << ("Invalid Allocsize in ARX_FTL_Save");
254 	}
255 
256 	// selections
257 	if (af3Ddh->nb_selections > 0)
258 	{
259 		std::copy(obj->selections.begin(), obj->selections.end(), (EERIE_SELECTIONS_FTL*)(dat + pos));
260 		pos += sizeof(EERIE_SELECTIONS_FTL) * af3Ddh->nb_selections;
261 
262 		if (pos > allocsize) LogError << ("Invalid Allocsize in ARX_FTL_Save");
263 
264 		for (int i = 0; i < af3Ddh->nb_selections; i++) {
265 			std::copy(obj->selections[i].selected.begin(), obj->selections[i].selected.end(), (s32*)(dat + pos));
266 			pos += sizeof(s32) * obj->selections[i].selected.size();
267 
268 			if (pos > allocsize) LogError << ("Invalid Allocsize in ARX_FTL_Save");
269 		}
270 
271 		strncpy(af3Ddh->name, obj->file.string().c_str(), sizeof(af3Ddh->name));
272 	}
273 
274 	// Progressive DATA
275 	afsh->offset_progressive_data = -1;
276 
277 
278 	// Collision Spheres Data
279 	if (obj->sdata && !obj->sdata->spheres.empty())
280 	{
281 		afsh->offset_collision_spheres = pos; //-1;
282 		ARX_FTL_COLLISION_SPHERES_DATA_HEADER 	*		afcsdh = (ARX_FTL_COLLISION_SPHERES_DATA_HEADER *)(dat + afsh->offset_collision_spheres);
283 		pos += sizeof(ARX_FTL_COLLISION_SPHERES_DATA_HEADER);
284 
285 		if (pos > allocsize) LogError << ("Invalid Allocsize in ARX_FTL_Save");
286 
287 		afcsdh->nb_spheres = obj->sdata->spheres.size();
288 
289 		std::copy(obj->sdata->spheres.begin(), obj->sdata->spheres.end(), (COLLISION_SPHERE_FTL*)(dat + pos));
290 		pos += sizeof(COLLISION_SPHERE_FTL) * obj->sdata->spheres.size();
291 
292 		if (pos > allocsize) LogError << ("Invalid Allocsize in ARX_FTL_Save");
293 	}
294 	else afsh->offset_collision_spheres = -1;
295 
296 
297 	// Clothes DATA
298 	if (obj->cdata == NULL)
299 	{
300 		afsh->offset_clothes_data = -1;
301 	}
302 	else
303 	{
304 		afsh->offset_clothes_data = pos;
305 		ARX_FTL_CLOTHES_DATA_HEADER * afcdh = (ARX_FTL_CLOTHES_DATA_HEADER *)(dat + afsh->offset_clothes_data);
306 
307 		afcdh->nb_cvert = obj->cdata->nb_cvert;
308 		afcdh->nb_springs = obj->cdata->springs.size();
309 		pos += sizeof(ARX_FTL_CLOTHES_DATA_HEADER);
310 
311 		if (pos > allocsize) LogError << ("Invalid Allocsize in ARX_FTL_Save");
312 
313 		// now save cvert
314 		std::copy(obj->cdata->cvert, obj->cdata->cvert + obj->cdata->nb_cvert, (CLOTHESVERTEX_FTL*)(dat + pos));
315 		pos += sizeof(CLOTHESVERTEX_FTL) * obj->cdata->nb_cvert;
316 
317 		if (pos > allocsize) LogError << ("Invalid Allocsize in ARX_FTL_Save");
318 
319 		// now saves springs
320 		std::copy(obj->cdata->springs.begin(), obj->cdata->springs.end(), (EERIE_SPRINGS_FTL*)(dat + pos));
321 		pos += sizeof(EERIE_SPRINGS_FTL) * obj->cdata->springs.size();
322 
323 		if (pos > allocsize) LogError << ("Invalid Allocsize in ARX_FTL_Save");
324 	}
325 
326 	afsh->offset_physics_box = -1;
327 
328 	afsh->offset_cylinder = -1;
329 
330 	// Now we can flush our cool FTL file to the hard drive
331 
332 	if(pos > allocsize) {
333 		LogError << "Badly Allocated SaveBuffer... " << gamefic;
334 		delete[] dat;
335 		return false;
336 	}
337 
338 	size_t cpr_pos = 0;
339 	char * compressed = implodeAlloc(dat, pos, cpr_pos);
340 	delete[] dat;
341 
342 	// Now Saving Whole Buffer
343 	fs::ofstream ofs(gamefic, fs::fstream::out | fs::fstream::binary | fs::fstream::trunc);
344 	if(!ofs.is_open()) {
345 		LogError << "Unable to Open " << gamefic << " for Write...";
346 		return false;
347 	}
348 
349 	ofs.write(compressed, cpr_pos);
350 	delete[] compressed;
351 
352 
353 	if(ofs.fail()) {
354 		LogError <<  "Unable to Write to " << gamefic;
355 		return false;
356 	}
357 
358 	return true;
359 }
360 
361 #endif // BUILD_EDIT_LOADSAVE
362 
363 // MESH cache structure definition & Globals
364 struct MCACHE_DATA {
365 	res::path name;
366 	char * data;
367 	size_t size;
368 };
369 static vector<MCACHE_DATA> meshCache;
370 
371 // Checks for Mesh file existence in cache
MCache_Get(const res::path & file)372 static long MCache_Get(const res::path & file) {
373 
374 	for(size_t i = 0; i < meshCache.size(); i++) {
375 		if(meshCache[i].name == file) {
376 			return i;
377 		}
378 	}
379 
380 	return -1;
381 }
382 
383 // Pushes a Mesh In Mesh Cache
MCache_Push(const res::path & file,char * data,size_t size)384 static bool MCache_Push(const res::path & file, char * data, size_t size) {
385 
386 	if(MCache_Get(file) != -1) {
387 		return false; // already cached
388 	}
389 
390 	LogDebug(file << " #" << meshCache.size());
391 
392 	MCACHE_DATA newMesh;
393 	newMesh.size = size;
394 	newMesh.data = data;
395 	newMesh.name = file;
396 	meshCache.push_back(newMesh);
397 
398 	return true;
399 }
400 
MCache_ClearAll()401 void MCache_ClearAll(){
402 	for(vector<MCACHE_DATA>::iterator it = meshCache.begin(); it != meshCache.end(); ++it) {
403 		free(it->data);
404 	}
405 
406 	meshCache.clear();
407 }
408 
409 // Retreives a Mesh File pointer from cache...
MCache_Pop(const res::path & file,size_t & size)410 static char * MCache_Pop(const res::path & file, size_t & size) {
411 
412 	long num = MCache_Get(file);
413 	if(num == -1) {
414 		return NULL;
415 	}
416 
417 	size = meshCache[num].size;
418 	return meshCache[num].data;
419 }
420 
ARX_FTL_Load(const res::path & file)421 EERIE_3DOBJ * ARX_FTL_Load(const res::path & file) {
422 
423 	// Creates FTL file name
424 	res::path filename = (res::path("game") / file).set_ext("ftl");
425 
426 	// Checks for FTL file existence
427 	PakFile * pf = resources->getFile(filename);
428 	if(!pf) {
429 		return NULL;
430 	}
431 
432 	size_t compressedSize = 0;
433 	char * compressedData = MCache_Pop(filename, compressedSize);
434 	LogDebug("File name check " << filename);
435 
436 	bool NOrelease = true;
437 	if(!compressedData) {
438 		compressedData = pf->readAlloc();
439 		compressedSize = pf->size();
440 		NOrelease = MCache_Push(filename, compressedData, compressedSize) ? 1 : 0;
441 	}
442 
443 	if(!compressedData) {
444 		LogError << "ARX_FTL_Load: error loading from PAK/cache " << filename;
445 		return NULL;
446 	}
447 
448 	size_t allocsize; // The size of the data TODO size ignored
449 	char * dat = blastMemAlloc(compressedData, compressedSize, allocsize);
450 	if(!dat) {
451 		LogError << "ARX_FTL_Load: error decompressing " << filename;
452 		return NULL;
453 	}
454 
455 	if(!NOrelease) {
456 		free(compressedData);
457 	}
458 
459 	size_t pos = 0; // The position within the data
460 
461 	// Pointer to Primary Header
462 	const ARX_FTL_PRIMARY_HEADER * afph = reinterpret_cast<const ARX_FTL_PRIMARY_HEADER *>(dat + pos);
463 	pos += sizeof(ARX_FTL_PRIMARY_HEADER);
464 
465 	// Verify FTL file Signature
466 	if(afph->ident[0] != 'F' || afph->ident[1] != 'T' || afph->ident[2] != 'L') {
467 		LogError << "ARX_FTL_Load: wrong magic number in " << filename;
468 		free(dat);
469 		return NULL;
470 	}
471 
472 	// Verify FTL file version
473 	if(afph->version != CURRENT_FTL_VERSION) {
474 		LogError << "ARX_FTL_Load: wring version " << afph->version << ", expected "
475 		         << CURRENT_FTL_VERSION << " in " << filename;
476 		free(dat);
477 		return NULL;
478 	}
479 
480 	// Increases offset by checksum size
481 	pos += 512;
482 
483 	// Pointer to Secondary Header
484 	const ARX_FTL_SECONDARY_HEADER * afsh;
485 	afsh = reinterpret_cast<const ARX_FTL_SECONDARY_HEADER *>(dat + pos);
486 	if(afsh->offset_3Ddata == -1) {
487 		LogError << "ARX_FTL_Load: error loading data from " << filename;
488 		free(dat);
489 		return NULL;
490 	}
491 	pos = afsh->offset_3Ddata;
492 
493 	// Available from here in whole function
494 	EERIE_3DOBJ * obj = new EERIE_3DOBJ();
495 
496 	const ARX_FTL_3D_DATA_HEADER * af3Ddh;
497 	af3Ddh = reinterpret_cast<const ARX_FTL_3D_DATA_HEADER *>(dat + pos);
498 	pos += sizeof(ARX_FTL_3D_DATA_HEADER);
499 
500 	obj->vertexlist.resize(af3Ddh->nb_vertex);
501 	obj->facelist.resize(af3Ddh->nb_faces);
502 	obj->texturecontainer.resize(af3Ddh->nb_maps);
503 	obj->nbgroups = af3Ddh->nb_groups;
504 	obj->actionlist.resize(af3Ddh->nb_action);
505 	obj->selections.resize(af3Ddh->nb_selections);
506 	obj->origin = af3Ddh->origin;
507 	obj->file = res::path::load(util::loadString(af3Ddh->name));
508 
509 	// Alloc'n'Copy vertices
510 	if(!obj->vertexlist.empty()) {
511 
512 		// Copy the vertex data in
513 		for(size_t ii = 0; ii < obj->vertexlist.size(); ii++) {
514 
515 			// Vertices stored as EERIE_OLD_VERTEX, copy in to new one
516 			obj->vertexlist[ii] = *reinterpret_cast<const EERIE_OLD_VERTEX *>(dat + pos);
517 			pos += sizeof(EERIE_OLD_VERTEX);
518 
519 			obj->vertexlist[ii].vert.color = 0xFF000000;
520 		}
521 
522 		// Set the origin point of the mesh
523 		obj->point0 = obj->vertexlist[obj->origin].v;
524 
525 		obj->vertexlist3 = obj->vertexlist;
526 	}
527 
528 	// Alloc'n'Copy faces
529 	if(!obj->facelist.empty()) {
530 
531 		// Copy the face data in
532 		for(long ii = 0; ii < af3Ddh->nb_faces; ii++) {
533 
534 			const EERIE_FACE_FTL * eff = reinterpret_cast<const EERIE_FACE_FTL*>(dat + pos);
535 			pos += sizeof(EERIE_FACE_FTL);
536 
537 			obj->facelist[ii].facetype = PolyType::load(eff->facetype);
538 			obj->facelist[ii].texid = eff->texid;
539 			obj->facelist[ii].transval = eff->transval;
540 			obj->facelist[ii].temp = eff->temp;
541 			obj->facelist[ii].norm = eff->norm;
542 
543 			// Copy in all the texture and normals data
544 			BOOST_STATIC_ASSERT(IOPOLYVERT_FTL == IOPOLYVERT);
545 			for(size_t kk = 0; kk < IOPOLYVERT_FTL; kk++) {
546 				obj->facelist[ii].nrmls[kk] = eff->nrmls[kk];
547 				obj->facelist[ii].vid[kk] = eff->vid[kk];
548 				obj->facelist[ii].u[kk] = eff->u[kk];
549 				obj->facelist[ii].v[kk] = eff->v[kk];
550 				obj->facelist[ii].ou[kk] = eff->ou[kk];
551 				obj->facelist[ii].ov[kk] = eff->ov[kk];
552 			}
553 
554 		}
555 	}
556 
557 	// Alloc'n'Copy textures
558 	if(af3Ddh->nb_maps > 0) {
559 
560 		// Copy in the texture containers
561 		for(long i = 0; i < af3Ddh->nb_maps; i++) {
562 
563 			const Texture_Container_FTL * tex;
564 			tex = reinterpret_cast<const Texture_Container_FTL *>(dat + pos);
565 			pos += sizeof(Texture_Container_FTL);
566 
567 			if(tex->name[0] == '\0') {
568 				// Some object files contain textures with empty names
569 				// Don't bother trying to load them as that will just generate an error message
570 				obj->texturecontainer[i] = NULL;
571 			} else {
572 				// Create the texture and put it in the container list
573 				res::path name = res::path::load(util::loadString(tex->name)).remove_ext();
574 				obj->texturecontainer[i] = TextureContainer::Load(name, TextureContainer::Level);
575 			}
576 		}
577 	}
578 
579 	// Alloc'n'Copy groups
580 	if(obj->nbgroups > 0) {
581 
582 		// Alloc the grouplists
583 		obj->grouplist = new EERIE_GROUPLIST[obj->nbgroups];
584 
585 		// Copy in the grouplist data
586 		for(long i = 0 ; i < obj->nbgroups ; i++) {
587 
588 			const EERIE_GROUPLIST_FTL* group = reinterpret_cast<const EERIE_GROUPLIST_FTL *>(dat + pos);
589 			pos += sizeof(EERIE_GROUPLIST_FTL);
590 
591 			obj->grouplist[i].name = boost::to_lower_copy(util::loadString(group->name));
592 			obj->grouplist[i].origin = group->origin;
593 			obj->grouplist[i].indexes.resize(group->nb_index);
594 			obj->grouplist[i].siz = group->siz;
595 
596 		}
597 
598 		// Copy in the group index data
599 		for(long i = 0; i < obj->nbgroups; i++) {
600 			if(!obj->grouplist[i].indexes.empty()) {
601 				size_t oldpos = pos;
602 				pos += sizeof(s32) * obj->grouplist[i].indexes.size(); // Advance to the next index block
603 				std::copy((const s32 *)(dat+oldpos), (const s32 *)(dat + pos), obj->grouplist[i].indexes.begin());
604 			}
605 		}
606 	}
607 
608 	// Copy in the action points data
609 	for(size_t i = 0 ; i < obj->actionlist.size(); i++) {
610 		obj->actionlist[i] = *reinterpret_cast<const EERIE_ACTIONLIST_FTL *>(dat + pos);
611 		pos += sizeof(EERIE_ACTIONLIST_FTL);
612 	}
613 
614 	// Copy in the selections data
615 	for(size_t i = 0 ; i < obj->selections.size(); i++) {
616 
617 		const EERIE_SELECTIONS_FTL * selection = reinterpret_cast<const EERIE_SELECTIONS_FTL *>(dat + pos);
618 		pos += sizeof(EERIE_SELECTIONS_FTL);
619 
620 		obj->selections[i].name = boost::to_lower_copy(util::loadString(selection->name));
621 		obj->selections[i].selected.resize(selection->nb_selected);
622 	}
623 
624 	// Copy in the selections selected data
625 	for(long i = 0; i < af3Ddh->nb_selections; i++) {
626 		std::copy((const s32 *)(dat + pos), (const s32 *)(dat + pos) + obj->selections[i].selected.size(), obj->selections[i].selected.begin() );
627 		pos += sizeof(s32) * obj->selections[i].selected.size(); // Advance to the next selection data block
628 	}
629 
630 	obj->pbox = NULL; // Reset physics
631 
632 	// Alloc'n'Copy Collision Spheres Data
633 	if(afsh->offset_collision_spheres != -1) {
634 
635 		// Cast to header
636 		pos = afsh->offset_collision_spheres;
637 		const ARX_FTL_COLLISION_SPHERES_DATA_HEADER * afcsdh;
638 		afcsdh = reinterpret_cast<const ARX_FTL_COLLISION_SPHERES_DATA_HEADER*>(dat + pos);
639 		pos += sizeof(ARX_FTL_COLLISION_SPHERES_DATA_HEADER);
640 
641 		// Alloc the collision sphere data object
642 		obj->sdata = new COLLISION_SPHERES_DATA();
643 		obj->sdata->spheres.resize(afcsdh->nb_spheres);
644 
645 		// Alloc the collision speheres
646 		const COLLISION_SPHERE_FTL * begin = reinterpret_cast<const COLLISION_SPHERE_FTL *>(dat + pos);
647 		pos += sizeof(COLLISION_SPHERE_FTL) * obj->sdata->spheres.size();
648 		const COLLISION_SPHERE_FTL * end = reinterpret_cast<const COLLISION_SPHERE_FTL *>(dat + pos);
649 		std::copy(begin, end, obj->sdata->spheres.begin());
650 	}
651 
652 	// Alloc'n'Copy Progressive DATA
653 	if(afsh->offset_progressive_data != -1) {
654 		// Progressive data ignored.
655 	}
656 
657 	// Alloc'n'Copy Clothes DATA
658 	if(afsh->offset_clothes_data != -1) {
659 
660 		obj->cdata = new CLOTHES_DATA();
661 
662 		const ARX_FTL_CLOTHES_DATA_HEADER * afcdh;
663 		afcdh = reinterpret_cast<const ARX_FTL_CLOTHES_DATA_HEADER*>(dat + afsh->offset_clothes_data);
664 		obj->cdata->nb_cvert = (short)afcdh->nb_cvert;
665 		obj->cdata->springs.resize(afcdh->nb_springs);
666 		size_t pos = afsh->offset_clothes_data;
667 		pos += sizeof(ARX_FTL_CLOTHES_DATA_HEADER);
668 
669 		// now load cvert
670 		obj->cdata->cvert = new CLOTHESVERTEX[obj->cdata->nb_cvert];
671 		obj->cdata->backup = new CLOTHESVERTEX[obj->cdata->nb_cvert];
672 		std::copy(reinterpret_cast<const CLOTHESVERTEX_FTL *>(dat + pos), reinterpret_cast<const CLOTHESVERTEX_FTL *>(dat + pos) + obj->cdata->nb_cvert, obj->cdata->cvert);
673 		memcpy(obj->cdata->backup, obj->cdata->cvert, sizeof(CLOTHESVERTEX)*obj->cdata->nb_cvert);
674 		pos += sizeof(CLOTHESVERTEX_FTL) * obj->cdata->nb_cvert;
675 
676 		// now load springs
677 		const EERIE_SPRINGS_FTL * begin = reinterpret_cast<const EERIE_SPRINGS_FTL *>(dat + pos);
678 		pos += sizeof(EERIE_SPRINGS_FTL) * obj->cdata->springs.size();
679 		const EERIE_SPRINGS_FTL * end = reinterpret_cast<const EERIE_SPRINGS_FTL *>(dat + pos);
680 		std::copy(begin, end, obj->cdata->springs.begin());
681 	}
682 
683 	// Free the loaded file memory
684 	free(dat);
685 
686 	EERIE_OBJECT_CenterObjectCoordinates(obj);
687 	EERIE_CreateCedricData(obj);
688 	// Now we can release our cool FTL file
689 	EERIE_Object_Precompute_Fast_Access(obj);
690 
691 	LogDebug("ARX_FTL_Load: loaded object " << filename);
692 
693 	return obj;
694 }
695