1 /*
2     Copyright (C) 2005-2007 Tom Beaumont
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 
19 #include <stdint.h>
20 #include <SDL_endian.h>
21 
22 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
23 #define SWAP16(X)    (X)
24 #define SWAP32(X)    (X)
25 #else
26 #define SWAP16(X)    SDL_Swap16(X)
27 #define SWAP32(X)    SDL_Swap32(X)
28 #endif
29 
30 struct PackFile1
31 {
32   /* Is it *NOT* save to interpret a byte stream as list of Entries!
33    * The alignment could increase the Entry size on some systems without attribute.
34    *
35    * It works on: i386, amd64, mips o32 ABI, powerPC
36    * Maybe it's also compiler dependent ...
37    *
38    * See also http://c-faq.com/struct/padding.html,
39    * http://c-faq.com/strangeprob/ptralign.html and Debian bug #442854
40    * (Need to refer to a C FAQ in a (so called) C++ program, argh ...)
41    * */
42 	class Entry {
43 		int32_t len;
44 	public:
45 		// an array of size 1 (no char* pointer!) is saved after len,
46 		// accessing name[0] should (but doesn't always) fit the first byte after len
47 		// See e.g. http://c-faq.com/aryptr/index.html
48 		char name[1];
49 
GetNextPackFile150 		Entry* GetNext()
51 		{
52 			return (Entry*)((char*)name + len);
53 		}
54 
DataPackFile155 		void* Data()
56 		{
57 			char* pos = name;
58 			while (*pos!=0)
59 				pos++;
60 			return pos+1;
61 		}
62 
DataLenPackFile163 		int DataLen()
64 		{
65 			return len - strlen(name) - 1;
66 		}
67 	}
68 #ifdef __GNUC__
69    __attribute__ ((__packed__));
70 	 int static_assert1[sizeof(Entry)==5 ? 0 : -1];
71 #else
72   int static_assert1[sizeof(Entry)<=8 ? 0 : -1];
73 #endif
74 
75 	int numfiles;
76 	Entry** e;
77 	void* data;
78 
PackFile1PackFile179 	PackFile1() : numfiles(0), e(0), data(0)
80 	{}
81 
FindPackFile182 	Entry* Find(const char* name)
83 	{
84 		if (numfiles==0) return 0;
85 		int a=0, b=numfiles;
86 		while (b>a)
87 		{
88 			const int mid = (a+b)>>1;
89 			int diff = strcmp(name, e[mid]->name);
90 			if (diff==0)
91 				return e[mid];
92 			else if (diff < 0)
93 				b=mid;
94 			else
95 				a=mid+1;
96 		}
97 		return 0;
98 	}
99 
ReadPackFile1100 	void Read(FILE* f)
101 	{
102 		if (numfiles || e || data)
103 			FATAL("Calling Packfile1::Read when already initialised.");
104 
105 		int32_t size;
106 		fseek(f, -(int)sizeof(size), SEEK_END);
107 		int end_offset = ftell(f);
108 		fread(&size, sizeof(size), 1, f);
109 		size = SWAP32(size);
110 		fseek(f, end_offset - size, SEEK_SET);
111 
112 		data = malloc(size);
113 		char* data_end = (char*)data + size;
114 		fread(data, 1, size, f);
115 
116 		numfiles=0;
117 		Entry* i = (Entry*)data;
118 		while ((void*)i < data_end)
119 		{
120 			numfiles++;
121 			int32_t *data_length = (int32_t*)i;
122 			*data_length = SWAP32(*data_length);
123 			i = i->GetNext();
124 		}
125 
126 		e = new Entry* [numfiles]; // CHECKME: where to delete?
127 
128 		i = (Entry*)data;
129 		for (int j=0; j<numfiles; j++, i = i->GetNext())
130 			e[j] = i;
131 	}
132 
~PackFile1PackFile1133 	~PackFile1()
134 	{
135 		free(data);
136 	}
137 
138 };
139