1 #ifndef _FILE_HANDLER_
2 #define _FILE_HANDLER_
3 /*
4 
5 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
6 	and the "Aleph One" developers.
7 
8 	This program is free software; you can redistribute it and/or modify
9 	it under the terms of the GNU General Public License as published by
10 	the Free Software Foundation; either version 3 of the License, or
11 	(at your option) any later version.
12 
13 	This program 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
16 	GNU General Public License for more details.
17 
18 	This license is contained in the file "COPYING",
19 	which is included with this source code; it is available online at
20 	http://www.gnu.org/licenses/gpl.html
21 
22 	File-handler classes
23 	by Loren Petrich,
24 	August 11, 2000
25 
26 	These are designed to provide some abstract interfaces to file and directory objects.
27 
28 	Most of these routines return whether they had succeeded;
29 	more detailed error codes are API-specific.
30 	Attempted to support stdio I/O directly, but on my Macintosh, at least,
31 	the performance was much poorer. This is possibly due to "fseek" having to
32 	actually read the file or something.
33 
34 	Merged all the Macintosh-specific code into these base classes, so that
35 	it will be selected with a preprocessor statement when more than one file-I/O
36 	API is supported.
37 
38 Dec 7, 2000 (Loren Petrich):
39 	Added a MacOS-specific file-creation function that allows direct specification
40 	of type and creator codes
41 
42 Jan 25, 2002 (Br'fin (Jeremy Parsons)):
43 	Added TARGET_API_MAC_CARBON for Carbon.h
44 	Rearranged initializers in DirectorySpecifier constructor to appease compiler warnings
45 
46 March 18, 2002 (Br'fin (Jeremy Parsons)):
47 	Added FileSpecifier::SetParentToResources for Carbon
48 */
49 
50 // For the filetypes
51 #include "tags.h"
52 
53 #include <stddef.h>	// For size_t
54 #include <time.h>	// For time_t
55 #include <vector>
56 #include <SDL.h>
57 
58 #include <errno.h>
59 #include <string>
60 #ifndef NO_STD_NAMESPACE
61 using std::string;
62 using std::vector;
63 #endif
64 
65 #ifdef __WIN32__
66 #include <windows.h>
67 #ifdef GetFreeSpace
68 #undef GetFreeSpace
69 #endif
70 #ifdef CreateDirectory
71 #undef CreateDirectory
72 #endif
73 #endif
74 
75 #include <boost/iostreams/categories.hpp>
76 #include <boost/iostreams/positioning.hpp>
77 
78 /*
79 	Abstraction for opened files; it does reading, writing, and closing of such files,
80 	without doing anything to the files' specifications
81 */
82 class OpenedFile
83 {
84 	// This class will need to set the refnum and error value appropriately
85 	friend class FileSpecifier;
86 	friend class opened_file_device;
87 
88 public:
89 	bool IsOpen();
90 	bool Close();
91 
92 	bool GetPosition(int32& Position);
93 	bool SetPosition(int32 Position);
94 
95 	bool GetLength(int32& Length);
96 	bool SetLength(int32 Length);
97 
98 	bool Read(int32 Count, void *Buffer);
99 	bool Write(int32 Count, void *Buffer);
100 
101 	OpenedFile();
~OpenedFile()102 	~OpenedFile() {Close();}	// Auto-close when destroying
103 
GetError()104 	int GetError() {return err;}
GetRWops()105 	SDL_RWops *GetRWops() {return f;}
106 	SDL_RWops *TakeRWops();		// Hand over SDL_RWops
107 
108 private:
109 	SDL_RWops *f;	// File handle
110 	int err;		// Error code
111 	bool is_forked;
112 	int32 fork_offset, fork_length;
113 };
114 
115 class opened_file_device {
116 public:
117 	typedef char char_type;
118 	typedef boost::iostreams::seekable_device_tag category;
119 	std::streamsize read(char* s, std::streamsize n);
120 	std::streamsize write(const char* s, std::streamsize n);
121 	std::streampos seek(boost::iostreams::stream_offset off, std::ios_base::seekdir way);
122 
123 	opened_file_device(OpenedFile& f);
124 
125 private:
126 	OpenedFile& f;
127 };
128 
129 /*
130 	Abstraction for loaded resources;
131 	this object will release that resource when it finishes.
132 	MacOS resource handles will be assumed to be locked.
133 */
134 class LoadedResource
135 {
136 	// This class grabs a resource to be loaded into here
137 	friend class OpenedResourceFile;
138 
139 public:
140 	// Resource loaded?
141 	bool IsLoaded();
142 
143 	// Unloads the resource
144 	void Unload();
145 
146 	// Get size of loaded resource
147 	size_t GetLength();
148 
149 	// Get pointer (always present)
150 	void *GetPointer(bool DoDetach = false);
151 
152 	// Make resource from raw resource data; the caller gives up ownership
153 	// of the pointed to memory block
154 	void SetData(void *data, size_t length);
155 
156 	LoadedResource();
~LoadedResource()157 	~LoadedResource() {Unload();}	// Auto-unload when destroying
158 
159 private:
160 	// Detaches an allocated resource from this object
161 	// (keep private to avoid memory leaks)
162 	void Detach();
163 
164 public:
165 	void *p;		// Pointer to resource data (malloc()ed)
166 	size_t size;	// Size of data
167 };
168 
169 
170 /*
171 	Abstraction for opened resource files:
172 	it does opening, setting, and closing of such files;
173 	also getting "LoadedResource" objects that return pointers
174 */
175 class OpenedResourceFile
176 {
177 	// This class will need to set the refnum and error value appropriately
178 	friend class FileSpecifier;
179 
180 public:
181 
182 	// Pushing and popping the current file -- necessary in the MacOS version,
183 	// since resource forks are globally open with one of them the current top one.
184 	// Push() saves the earlier top one makes the current one the top one,
185 	// while Pop() restores the earlier top one.
186 	// Will leave SetResLoad in the state of true.
187 	bool Push();
188 	bool Pop();
189 
190 	// Pushing and popping are unnecessary for the MacOS versions of Get() and Check()
191 	// Check simply checks if a resource is present; returns whether it is or not
192 	// Get loads a resource; returns whether or not one had been successfully loaded
193 	// CB: added functions that take 4 characters instead of uint32, which is more portable
194 	bool Check(uint32 Type, int16 ID);
Check(uint8 t1,uint8 t2,uint8 t3,uint8 t4,int16 ID)195 	bool Check(uint8 t1, uint8 t2, uint8 t3, uint8 t4, int16 ID) {return Check(FOUR_CHARS_TO_INT(t1, t2, t3, t4), ID);}
196 	bool Get(uint32 Type, int16 ID, LoadedResource& Rsrc);
Get(uint8 t1,uint8 t2,uint8 t3,uint8 t4,int16 ID,LoadedResource & Rsrc)197 	bool Get(uint8 t1, uint8 t2, uint8 t3, uint8 t4, int16 ID, LoadedResource& Rsrc) {return Get(FOUR_CHARS_TO_INT(t1, t2, t3, t4), ID, Rsrc);}
198 
199 	bool IsOpen();
200 	bool Close();
201 
202 	OpenedResourceFile();
~OpenedResourceFile()203 	~OpenedResourceFile() {Close();}	// Auto-close when destroying
204 
GetError()205 	int GetError() {return err;}
206 
207 private:
208 	int err;		// Error code
209 	SDL_RWops *f, *saved_f;
210 };
211 
212 
213 // Directories are treated like files
214 #define DirectorySpecifier FileSpecifier
215 
216 // Directory entry, returned by FileSpecifier::ReadDirectory()
217 struct dir_entry {
dir_entrydir_entry218 	dir_entry() : size(0), is_directory(false), is_volume(false) {}
219 	dir_entry(const string &n, int32 s, bool is_dir, bool is_vol = false, TimeType d = 0)
namedir_entry220 		: name(n), size(s), is_directory(is_dir), is_volume(is_vol), date(d) {}
~dir_entrydir_entry221 	~dir_entry() {}
222 
223 	bool operator<(const dir_entry &other) const
224 	{
225 		if (is_directory == other.is_directory)
226 			return name < other.name;
227 		else	// Sort directories before files
228 			return is_directory > other.is_directory;
229 	}
230 
231 	bool operator==(const dir_entry& other) const {
232 		return is_directory == other.is_directory && name == other.name;
233 	}
234 
235 	string name;		// Entry name
236 	int32 size;			// File size (only valid if !is_directory)
237 	bool is_directory;	// Entry is a directory (plain file otherwise)
238 	bool is_volume;		// Entry is a volume (for platforms that have volumes, is_directory must also be set)
239 	TimeType date;          // modification date
240 };
241 
242 
243 /*
244 	Abstraction for file specifications;
245 	designed to encapsulate both directly-specified paths
246 	and MacOS FSSpecs
247 */
248 class FileSpecifier
249 {
250 public:
251 	// The typecodes here are the symbolic constants defined in tags.h (_typecode_creator, etc.)
252 
253 	// Get the name (final path element) as a C string:
254 	// assumes enough space to hold it if getting (max. 256 bytes)
255 	void GetName(char *Name) const;
256 
257 	//   Looks in all directories in the current data search
258 	//   path for a file with the relative path "NameWithPath" and
259 	//   sets the file specifier to the full path of the first file
260 	//   found.
261 	// "NameWithPath" follows Unix-like syntax: <dirname>/<dirname>/<dirname>/filename
262 	// A ":" will be translated into a "/" in the MacOS.
263 	// Returns whether or not the setting was successful
264 	bool SetNameWithPath(const char *NameWithPath);
265 	bool SetNameWithPath(const char* NameWithPath, const DirectorySpecifier& Directory);
266 
267 	void SetTempName(const FileSpecifier& other);
268 
269 	// Move the directory specification
270 	void ToDirectory(DirectorySpecifier& Dir);
271 	void FromDirectory(DirectorySpecifier& Dir);
272 
273 	// These functions take an appropriate one of the typecodes used earlier;
274 	// this is to try to cover the cases of both typecode attributes
275 	// and typecode suffixes.
276 	bool Create(Typecode Type);
277 
278 	// Opens a file:
279 	bool Open(OpenedFile& OFile, bool Writable=false);
280 
281 	// Opens either a MacOS resource fork or some imitation of it:
282 	bool Open(OpenedResourceFile& OFile, bool Writable=false);
283 
284 	// These calls are for creating dialog boxes to set the filespec
285 	// A null pointer means an empty string
286 	bool ReadDialog(Typecode Type, const char *Prompt=NULL);
287 	bool WriteDialog(Typecode Type, const char *Prompt=NULL, const char *DefaultName=NULL);
288 
289 	// Write dialog box for savegames (must be asynchronous, allowing the sound
290 	// to continue in the background)
291 	bool WriteDialogAsync(Typecode Type, char *Prompt=NULL, char *DefaultName=NULL);
292 
293 	// Check on whether a file exists, and its type
294 	bool Exists();
295 	bool IsDir();
296 
297 	// Gets the modification date
298 	TimeType GetDate();
299 
300 	// Returns _typecode_unknown if the type could not be identified;
301 	// the types returned are the _typecode_stuff in tags.h
302 	Typecode GetType();
303 
304 	// How many bytes are free in the disk that the file lives in?
305 	bool GetFreeSpace(uint32& FreeSpace);
306 
307 	// Copy file contents
308 	bool CopyContents(FileSpecifier& File);
309 
310 	// Delete file
311 	bool Delete();
312 
313 	// Rename file
314 	bool Rename(const FileSpecifier& Destination);
315 
316 	// Copy file specification
317 	const FileSpecifier &operator=(const FileSpecifier &other);
318 
319 	// hide extensions known to Aleph One
320 	static std::string HideExtension(const std::string& filename);
321 
GetPath(void)322 	const char *GetPath(void) const {return name.c_str();}
323 
324 	FileSpecifier();
FileSpecifier(const string & s)325 	FileSpecifier(const string &s) : name(s), err(0) {canonicalize_path();}
FileSpecifier(const char * s)326 	FileSpecifier(const char *s) : name(s), err(0) {canonicalize_path();}
FileSpecifier(const FileSpecifier & other)327 	FileSpecifier(const FileSpecifier &other) : name(other.name), err(other.err) {}
328 
329 	bool operator==(const FileSpecifier &other) const {return name == other.name;}
330 	bool operator!=(const FileSpecifier &other) const {return name != other.name;}
331 
332 	void SetToLocalDataDir();		// Per-user directory (for temporary files)
333 	void SetToPreferencesDir();		// Directory for preferences (per-user)
334 	void SetToSavedGamesDir();		// Directory for saved games (per-user)
335 	void SetToQuickSavesDir();		// Directory for auto-named saved games (per-user)
336 	void SetToImageCacheDir();		// Directory for image cache (per-user)
337 	void SetToRecordingsDir();		// Directory for recordings (per-user)
338 
339 	void AddPart(const string &part);
340 	FileSpecifier &operator+=(const FileSpecifier &other) {AddPart(other.name); return *this;}
341 	FileSpecifier &operator+=(const string &part) {AddPart(part); return *this;}
342 	FileSpecifier &operator+=(const char *part) {AddPart(string(part)); return *this;}
343 	FileSpecifier operator+(const FileSpecifier &other) const {FileSpecifier a(name); a.AddPart(other.name); return a;}
344 	FileSpecifier operator+(const string &part) const {FileSpecifier a(name); a.AddPart(part); return a;}
345 	FileSpecifier operator+(const char *part) const {FileSpecifier a(name); a.AddPart(string(part)); return a;}
346 
347 	void SplitPath(string &base, string &part) const;
SplitPath(DirectorySpecifier & base,string & part)348 	void SplitPath(DirectorySpecifier &base, string &part) const {string b; SplitPath(b, part); base = b;}
349 
350 	bool CreateDirectory();
351 	bool ReadDirectory(vector<dir_entry> &vec);
352 
GetError()353 	int GetError() const {return err;}
354 
355 private:
356 	void canonicalize_path(void);
357 
358 	string name;	// Path name
359 	int err;
360 };
361 
362 // inserts dir before the search path, then restores the original path
363 // when going out of scope
364 class ScopedSearchPath
365 {
366 public:
367 	ScopedSearchPath(const DirectorySpecifier& dir);
368 	~ScopedSearchPath();
369 };
370 
371 #endif
372 
373