gcsx_file.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. /* GCSx
  2. ** FILE.CPP
  3. **
  4. ** Basic file access with exceptions
  5. */
  6. /*****************************************************************************
  7. ** Copyright (C) 2003-2006 Janson
  8. **
  9. ** This program is free software; you can redistribute it and/or modify
  10. ** it under the terms of the GNU General Public License as published by
  11. ** the Free Software Foundation; either version 2 of the License, or
  12. ** (at your option) any later version.
  13. **
  14. ** This program is distributed in the hope that it will be useful,
  15. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ** GNU General Public License for more details.
  18. **
  19. ** You should have received a copy of the GNU General Public License
  20. ** along with this program; if not, write to the Free Software
  21. ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
  22. *****************************************************************************/
  23. #include "all.h"
  24. string* mainDir = NULL;
  25. string* resourceDir = NULL;
  26. File::File(const char* fFilename, int fMode) throw_File { start_func
  27. filename = fFilename;
  28. mode = fMode;
  29. if (fMode == FILE_MODE_OVERWRITE) {
  30. filePtr = fopen(filename, "w+b");
  31. mode = FILE_MODE_READWRITE;
  32. }
  33. else {
  34. filePtr = fopen(filename, fMode == FILE_MODE_READ ? "rb" : "r+b");
  35. }
  36. if (!filePtr) {
  37. throw FileException("Unable to load file %s: %s", filename, strerror(errno));
  38. }
  39. lastOperation = OPER_SEEK;
  40. offset = ftell(filePtr);
  41. }
  42. File::~File() { start_func
  43. if (filePtr) fclose(filePtr);
  44. }
  45. char File::copyBuffer[COPY_BUFFER_SIZE + 1];
  46. void File::reseek() { start_func
  47. assert(filePtr);
  48. fseek(filePtr, 0, SEEK_CUR);
  49. }
  50. /*
  51. int File::readStr(string& str) throw_File { start_func
  52. assert(filePtr);
  53. // (sets read mode)
  54. int size = readInt16();
  55. int origSize = size;
  56. int first = 1;
  57. if (size) {
  58. while (size) {
  59. int amount = min(size, COPY_BUFFER_SIZE);
  60. read(copyBuffer, amount);
  61. copyBuffer[amount] = 0;
  62. if (first) str = copyBuffer;
  63. else str += copyBuffer;
  64. size -= amount;
  65. }
  66. }
  67. else {
  68. str = blankString;
  69. }
  70. return origSize + 2;
  71. }
  72. */
  73. void File::read(void* ptr, int size) throw_File { start_func
  74. assert(size >= 0);
  75. assert(filePtr);
  76. assert(ptr);
  77. if (size) {
  78. if (lastOperation == OPER_WRITE) reseek();
  79. lastOperation = OPER_READ;
  80. if (fread(ptr, size, 1, filePtr) != 1) {
  81. offset = ftell(filePtr);
  82. if (ferror(filePtr)) {
  83. throw FileException("File read error: %s", strerror(errno));
  84. }
  85. else {
  86. throw FileException("Unexpected end-of-file");
  87. }
  88. }
  89. offset += size;
  90. }
  91. }
  92. Uint32 File::readInt() throw_File { start_func
  93. #if SDL_BYTEORDER == SDL_LIL_ENDIAN
  94. Uint32 result;
  95. read(&result, 4);
  96. return result;
  97. #else
  98. Uint8 bytes[4];
  99. read(bytes, 4);
  100. return (Uint32)bytes[0] | ((Uint32)bytes[1] << 8) | ((Uint32)bytes[2] << 16) | ((Uint32)bytes[1] << 24);
  101. #endif
  102. }
  103. /*
  104. Uint16 File::readInt16() throw_File { start_func
  105. #if SDL_BYTEORDER == SDL_LIL_ENDIAN
  106. Uint16 result;
  107. read(&result, 2);
  108. return result;
  109. #else
  110. Uint8 bytes[2];
  111. read(bytes, 2);
  112. return (Uint16)bytes[0] | ((Uint16)bytes[1] << 8);
  113. #endif
  114. }
  115. */
  116. /*
  117. int File::writeStr(const string& str) throw_File { start_func
  118. assert(filePtr);
  119. // (sets write mode)
  120. writeInt16(str.size());
  121. write(str.c_str(), str.size());
  122. return str.size() + 2;
  123. }
  124. */
  125. void File::write(const void* ptr, int size) throw_File { start_func
  126. assert(size >= 0);
  127. assert(filePtr);
  128. assert(ptr);
  129. if (size) {
  130. if (lastOperation == OPER_READ) reseek();
  131. lastOperation = OPER_WRITE;
  132. if (fwrite(ptr, size, 1, filePtr) == 0) {
  133. offset = ftell(filePtr);
  134. throw FileException("File write error: %s", strerror(errno));
  135. }
  136. offset += size;
  137. }
  138. }
  139. void File::writeInt(Uint32 value) throw_File { start_func
  140. #if SDL_BYTEORDER == SDL_LIL_ENDIAN
  141. write(&value, 4);
  142. #else
  143. Uint8 bytes[4];
  144. bytes[0] = value & 0xFF;
  145. bytes[1] = (value >> 8) & 0xFF;
  146. bytes[2] = (value >> 16) & 0xFF;
  147. bytes[3] = (value >> 24) & 0xFF;
  148. write(bytes, 4);
  149. #endif
  150. }
  151. /*
  152. void File::writeInt16(Uint16 value) throw_File { start_func
  153. #if SDL_BYTEORDER == SDL_LIL_ENDIAN
  154. write(&value, 2);
  155. #else
  156. Uint8 bytes[2];
  157. bytes[0] = value & 0xFF;
  158. bytes[1] = (value >> 8) & 0xFF;
  159. write(bytes, 2);
  160. #endif
  161. }
  162. */
  163. void File::writeBlanks(int size) throw_File { start_func
  164. assert(size >= 0);
  165. assert(filePtr);
  166. if (size) {
  167. if (lastOperation == OPER_READ) reseek();
  168. lastOperation = OPER_WRITE;
  169. while (size--) {
  170. if (fputc(0, filePtr) == EOF) {
  171. throw FileException("File write error: %s", strerror(errno));
  172. }
  173. ++offset;
  174. }
  175. }
  176. }
  177. void File::copy(File* copyFrom, int size) throw_File { start_func
  178. assert(size >= 0);
  179. assert(filePtr);
  180. assert(copyFrom);
  181. if (size) {
  182. if (lastOperation == OPER_READ) reseek();
  183. lastOperation = OPER_WRITE;
  184. while (size > COPY_BUFFER_SIZE) {
  185. copyFrom->read(copyBuffer, COPY_BUFFER_SIZE);
  186. write(copyBuffer, COPY_BUFFER_SIZE);
  187. size -= COPY_BUFFER_SIZE;
  188. }
  189. if (size) {
  190. copyFrom->read(copyBuffer, size);
  191. write(copyBuffer, size);
  192. }
  193. }
  194. }
  195. void File::flush() throw_File { start_func
  196. if (fflush(filePtr)) {
  197. throw FileException("File flush error: %s", strerror(errno));
  198. }
  199. }
  200. void File::skip(int size) throw_File { start_func
  201. assert(filePtr);
  202. if (fseek(filePtr, size, SEEK_CUR)) {
  203. offset = ftell(filePtr);
  204. throw FileException("File seek error: %s", strerror(errno));
  205. }
  206. lastOperation = OPER_SEEK;
  207. offset += size;
  208. }
  209. void File::seek(int position) throw_File { start_func
  210. assert(filePtr);
  211. if (offset != position) {
  212. if (fseek(filePtr, position, SEEK_SET)) {
  213. offset = ftell(filePtr);
  214. throw FileException("File seek error: %s", strerror(errno));
  215. }
  216. lastOperation = OPER_SEEK;
  217. offset = position;
  218. }
  219. }
  220. int File::tell() const { start_func
  221. assert(filePtr);
  222. return offset;
  223. }
  224. const char* topDir() { start_func
  225. return FILESYSTEM_ABSOLUTEPREFIX;
  226. }
  227. int findFiles(const char* rootdir, const char* match, vector<string>& result) { start_func
  228. assert(rootdir);
  229. string searchterm;
  230. if ((FILESYSTEM_DRIVE_SEP) && (!myStricmp(rootdir, FILESYSTEM_ABSOLUTEPREFIX))) {
  231. #ifdef WIN32
  232. // Return all drives, but only on WIN32/similar
  233. if (match) return 0;
  234. Uint32 drives = GetLogicalDrives();
  235. char driveName[3] = "A?";
  236. driveName[1] = FILESYSTEM_DRIVE_SEP;
  237. while (drives) {
  238. if (drives & 1) result.push_back(driveName);
  239. ++driveName[0];
  240. drives = drives >> 1;
  241. }
  242. return 1;
  243. #endif
  244. // Any other OS that supports drives?
  245. return 0;
  246. }
  247. int matches = 0;
  248. DIR* dirPtr;
  249. // Drives must be opened in a special way
  250. if ((FILESYSTEM_DRIVE_SEP) && (rootdir[0] != 0) && (rootdir[1] == FILESYSTEM_DRIVE_SEP) && (rootdir[2] == 0)) {
  251. char drivedir[strlen(rootdir) + strlen(FILESYSTEM_PREFERREDSEP)];
  252. strcpy(drivedir, rootdir);
  253. strcat(drivedir, FILESYSTEM_PREFERREDSEP);
  254. dirPtr = opendir(drivedir);
  255. }
  256. else {
  257. dirPtr = opendir(rootdir);
  258. }
  259. if (!dirPtr) return 0;
  260. struct dirent* entry;
  261. struct stat inode;
  262. int matchlen;
  263. if (match) matchlen = strlen(match);
  264. // (assignment intentional)
  265. while ( (entry = readdir(dirPtr)) ) {
  266. // Skip entries beginning with .
  267. if (entry->d_name[0] == '.') continue;
  268. string fullpath;
  269. createFilename(rootdir, entry->d_name, fullpath);
  270. // Skip entries we can't stat
  271. if (stat(fullpath.c_str(), &inode) < 0) continue;
  272. if (match) {
  273. // @TODO: We could also support links- if (S_ISLNK (inode.st_mode))
  274. if (S_ISREG (inode.st_mode)) {
  275. // Match extension to end of string; always case insensitive
  276. int len = strlen(entry->d_name);
  277. if (len > matchlen) {
  278. if (!myStricmp(entry->d_name + (len - matchlen), match)) {
  279. ++matches;
  280. result.push_back(entry->d_name);
  281. }
  282. }
  283. }
  284. }
  285. else {
  286. if (S_ISDIR (inode.st_mode)) {
  287. ++matches;
  288. result.push_back(entry->d_name);
  289. }
  290. }
  291. }
  292. closedir(dirPtr);
  293. return matches;
  294. }
  295. int existFiles(const char* rootdir, const char* match) { start_func
  296. assert(rootdir);
  297. string searchterm;
  298. // For drives, always return "yes" to whether subdirectories exist
  299. // to prevent needless drive accesses
  300. if (FILESYSTEM_DRIVE_SEP) {
  301. // Also, if we have drives, topdir has drives in it
  302. if (!myStricmp(rootdir, FILESYSTEM_ABSOLUTEPREFIX)) {
  303. if (match) return 0;
  304. return 1;
  305. }
  306. if ((rootdir[0] != 0) && (rootdir[1] == FILESYSTEM_DRIVE_SEP) && (rootdir[2] == 0) && (match == NULL)) return 1;
  307. }
  308. DIR* dirPtr = opendir(rootdir);
  309. if (!dirPtr) return 0;
  310. struct dirent* entry;
  311. struct stat inode;
  312. int matchlen;
  313. if (match) matchlen = strlen(match);
  314. // @TODO: update GUI/show delay in some way (stat'ing a large network directory, etc.)
  315. // (assignment intentional)
  316. while ( (entry = readdir(dirPtr)) ) {
  317. // Skip entries beginning with .
  318. if (entry->d_name[0] == '.') continue;
  319. string fullpath;
  320. createFilename(rootdir, entry->d_name, fullpath);
  321. // Skip entries we can't stat
  322. if (stat(fullpath.c_str(), &inode) < 0) continue;
  323. if (match) {
  324. // #TODO: We could also support links- if (S_ISLNK (inode.st_mode))
  325. if (S_ISREG (inode.st_mode)) {
  326. // Match extension to end of string; always case insensitive
  327. int len = strlen(entry->d_name);
  328. if (len > matchlen) {
  329. if (!myStricmp(entry->d_name + (len - matchlen), match)) {
  330. closedir(dirPtr);
  331. return 1;
  332. }
  333. }
  334. }
  335. }
  336. else {
  337. if (S_ISDIR (inode.st_mode)) {
  338. closedir(dirPtr);
  339. return 1;
  340. }
  341. }
  342. }
  343. closedir(dirPtr);
  344. return 0;
  345. }
  346. void createDirname(const char* rootdir, const char* subdir, string& newRootdir) { start_func
  347. assert(rootdir);
  348. assert(subdir);
  349. newRootdir = rootdir;
  350. // Only add separator if not absolute root directory
  351. if (myStricmp(rootdir, FILESYSTEM_ABSOLUTEPREFIX)) newRootdir += FILESYSTEM_PREFERREDSEP;
  352. newRootdir += subdir;
  353. }
  354. void createFilename(const char* rootdir, const char* filename, string& fullFilename) { start_func
  355. assert(rootdir);
  356. assert(filename);
  357. // Absolute path if starts with any separator
  358. if ((strchr(FILESYSTEM_SEPARATORS, filename[0])) ||
  359. // Also an absolute path if begins with a drive
  360. ((FILESYSTEM_DRIVE_SEP) && (filename[0] != 0) && (filename[1] == FILESYSTEM_DRIVE_SEP))
  361. ) {
  362. fullFilename = filename;
  363. }
  364. // Relative path- add to rootdir
  365. else {
  366. createDirname(rootdir, filename, fullFilename);
  367. }
  368. }
  369. void splitFilename(const char* filename, vector<string>& result) { start_func
  370. assert(filename);
  371. string file = filename;
  372. string::size_type prev = 0;
  373. string::size_type found = 0;
  374. while (1) {
  375. found = file.find_first_of(FILESYSTEM_SEPARATORS, found);
  376. if (found >= string::npos) {
  377. result.push_back(file.substr(prev));
  378. break;
  379. }
  380. if (found > prev) {
  381. result.push_back(file.substr(prev, found - prev));
  382. }
  383. ++found;
  384. prev = found;
  385. }
  386. }
  387. void getPathname(const char* filename, string& result) { start_func
  388. assert(filename);
  389. string file = filename;
  390. string::size_type found = file.find_last_of(FILESYSTEM_SEPARATORS);
  391. if (found >= string::npos) result = FILESYSTEM_ABSOLUTEPREFIX;
  392. else result = file.substr(0, found);
  393. }