file.cpp 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673
  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 RWS Inc, All Rights Reserved
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of version 2 of the GNU General Public License as published by
  7. // the Free Software Foundation
  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 along
  15. // with this program; if not, write to the Free Software Foundation, Inc.,
  16. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. //
  18. //////////////////////////////////////////////////////////////////////////////
  19. //
  20. // File.CPP
  21. //
  22. // History:
  23. // 07/29/95 JMI Started.
  24. //
  25. // 08/14/95 JMI Changed name from CFile to CNFile.
  26. //
  27. // 08/17/95 JMI Added memory file ability.
  28. //
  29. // 08/28/95 JMI Added use of m_sMemError to allow Error() function to
  30. // flag errors.
  31. //
  32. // 09/19/95 JMI Altered ASSERT in Close to work with memory files.
  33. //
  34. // 09/22/95 JMI Added Open and Close hooks.
  35. //
  36. // 10/04/95 JMI Added automagic reopen ability.
  37. //
  38. // 10/14/96 MJR Removed <SMRTHEAP.HPP> since it will be taken care of
  39. // by system.h, which is included via blue.h
  40. //
  41. // 10/15/96 JMI Added Read/Write((float*)...) and Read/Write((double*)...).
  42. //
  43. // 10/18/96 MJR Fixed memory leaks on Write() functions.
  44. //
  45. // 10/21/96 JMI Changed Write() functions to utilize a shared static buf-
  46. // fer and write in sizeof(ms_u8SwapBuf) byte chunks. Also,
  47. // fixed possible problem introduced by the #if 1 blocks
  48. // added to Write(U32/U16) that would have caused twice the
  49. // info to have been written.
  50. //
  51. // 10/21/96 JMI Added ability to read & write in new "ASCII mode".
  52. //
  53. // 10/23/96 JMI Open() for memory was initializing m_sFlags to 0 instead
  54. // of NFILE_BINARY.
  55. //
  56. // 10/23/96 JMI Byte swapping was always doing at least a buffers worth
  57. // and was overrunning the swap buffer. The fix to this
  58. // was merely implementing the slight performance improvement
  59. // that was listed in comment before each swap algorithm.
  60. //
  61. // 10/25/96 JMI Now reads and writes PIXEL24 which is a 24 bit type
  62. // defined in <Sys>System.h. It can vary in order on big
  63. // and little endian systems, but it may, in some cases,
  64. // not vary. I'm just not sure. For now, this module
  65. // byte swaps if the file's endianness varies from the
  66. // systems, but this may not always be the case for 24 bit
  67. // pixel data.
  68. //
  69. // 10/28/96 JMI Disabled warning on constant conditional expression so
  70. // this file would compile nicely on warning level 4.
  71. //
  72. // 10/30/96 JMI Changed:
  73. // Old label: New label:
  74. // ========= =========
  75. // CNFile RFile
  76. // *_CNFILE_* *_RFILE_*
  77. // CList RList
  78. // ENDIAN_BIG RFile::BigEndian
  79. // ENDIAN_LITTLE RFile::LittleEndian
  80. // NFILE_BINARY RFile::Binary
  81. // NFILE_ASCII RFile::Ascii
  82. // PIXEL24 RPixel24
  83. // CNFILEOPENHOOK RFile::OpenHook
  84. // CNFILECLOSEHOOK RFile::CloseHook
  85. //
  86. // 12/11/96 JMI Now calls ms_criticall with the number of bytes that are
  87. // about to be read or written when reading/writing a disk
  88. // file.
  89. //
  90. // 01/22/97 JMI New Open() call allows one to make a file that grows
  91. // when Write()s exceed its size.
  92. //
  93. // 01/28/97 JMI Added yet another Open() overload that allows one to open
  94. // an existing FILE* stream with an RFile.
  95. // And yet another Open() which opens from another RFile
  96. // that is already attached to disk or memory. This is use-
  97. // ful for descended classes such as RIff (so they can sub-
  98. // class an existing RFile).
  99. //
  100. // 05/30/97 JMI Added FILE_VERBOSE. Which, when defined,
  101. // causes the resmgr to output TRACEs when a file
  102. // is not found. Although we all love this feature
  103. // for debugging purposes, it gets bad when we're
  104. // loading a file only if it exists.
  105. //
  106. // 07/17/97 MJR Added code to set default buffer size for RFile's to
  107. // 16k, but commented it out pending further testing to
  108. // determine whether it's a good idea.
  109. //
  110. // 08/23/97 JMI Now will call the criticallback for disk files every
  111. // MAX_CALLBACK_GRANULARITY_BYTES bytes at a maximum to
  112. // ensure a certain amount of callback granularity.
  113. //
  114. // 08/23/97 JMI Fixed bug with not incrementing dest pointers in Read()
  115. // and Write().
  116. //
  117. //////////////////////////////////////////////////////////////////////////////
  118. //
  119. // This module handles file stuff, providing services such as Open, Close,
  120. // Read, and Write. It also can transparently convert data as read in based
  121. // on the endian nature of the current platform and the endian macro supplied
  122. // by the app to the Open() or SetEndian() (BigEndian or LittleEndian).
  123. //
  124. // There are two modes in which this module can operate (Binary or
  125. // Ascii (Ascii does not work for memory files)). In order to
  126. // hide ASCII operation, the ASCII versions of these functions return values
  127. // that would, hopefully, be acceptable to a binary reader/writer using
  128. // CNFile. The big suck is that you cannot make a placeholder for places you
  129. // want to seek back to with intent of updating a field since the field's size,
  130. // in ASCII mode, is dependent on the value.
  131. //
  132. //////////////////////////////////////////////////////////////////////////////
  133. //////////////////////////////////////////////////////////////////////////////
  134. // Headers.
  135. //////////////////////////////////////////////////////////////////////////////
  136. #include <stdlib.h>
  137. #include <memory.h>
  138. #include <string.h>
  139. #include <limits.h>
  140. #include <float.h> // For float and double limits.
  141. #if PLATFORM_UNIX
  142. #include <unistd.h>
  143. #include <dirent.h>
  144. #include <sys/param.h>
  145. #include <sys/types.h>
  146. #include <sys/stat.h>
  147. #include <ctype.h>
  148. #endif
  149. #ifdef WIN32
  150. #define WIN32_LEAN_AND_MEAN 1
  151. #include <windows.h>
  152. #include <io.h>
  153. #include <direct.h>
  154. #include <malloc.h>
  155. #define PATH_MAX MAX_PATH
  156. #ifndef F_OK
  157. #define F_OK 00
  158. #endif
  159. #ifndef R_OK
  160. #define R_OK 04
  161. #endif
  162. typedef HRESULT (WINAPI *fnSHGetFolderPathW)(HWND hwnd, int nFolder, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath);
  163. #endif
  164. #include "Blue.h"
  165. #ifdef PATHS_IN_INCLUDES
  166. #include "ORANGE/File/file.h"
  167. #else
  168. #include "file.h"
  169. #endif // PATHS_IN_INCLUDES
  170. //////////////////////////////////////////////////////////////////////////////
  171. // Module specific macros.
  172. //////////////////////////////////////////////////////////////////////////////
  173. #ifdef SYS_ENDIAN_BIG
  174. #ifdef SYS_ENDIAN_LITTLE
  175. #error SYS_ENDIAN_LITTLE & SYS_ENDIAN_BIG cannot both be defined!
  176. #else
  177. #define ENDIAN_CONSISTENT (m_endian != LittleEndian)
  178. #endif // SYS_ENDIAN_LITTLE
  179. #else
  180. #ifdef SYS_ENDIAN_LITTLE
  181. #define ENDIAN_CONSISTENT (m_endian != BigEndian)
  182. #else
  183. #error SYS_ENDIAN_LITTLE or SYS_ENDIAN_BIG must be defined!
  184. #endif // SYS_ENDIAN_LITTLE
  185. #endif // SYS_ENDIAN_BIG
  186. // Defines what is whitespace to this module.
  187. #define WHITE_SPACE "\t \n"
  188. // Granularity of callbacks in bytes.
  189. #define MAX_CALLBACK_GRANULARITY_BYTES 1024
  190. //////////////////////////////////////////////////////////////////////////////
  191. // Module specific pragmas.
  192. //////////////////////////////////////////////////////////////////////////////
  193. #if defined(WIN32)
  194. #pragma warning (disable: 4127) // conditional expression is constant.
  195. #endif
  196. //////////////////////////////////////////////////////////////////////////////
  197. // Module specific typedefs.
  198. //////////////////////////////////////////////////////////////////////////////
  199. //////////////////////////////////////////////////////////////////////////////
  200. // Module specific (static) variables.
  201. //////////////////////////////////////////////////////////////////////////////
  202. // Called on every read and write with
  203. // the amount that is about to be
  204. // processed.
  205. RFile::CritiCall RFile::ms_criticall = NULL;
  206. // For hooking Open(char*, ...) calls.
  207. RFile::OpenHook RFile::ms_hOpen = NULL;
  208. long RFile::ms_lOpenUser = 0L;
  209. RFile::CloseHook RFile::ms_hClose = NULL;
  210. long RFile::ms_lCloseUser = 0L;
  211. // Used to byte swap by Write().
  212. U8 RFile::ms_au8SwapBuf[RFILE_SWAP_SIZE];
  213. // String description of endian (index by Endian enum).
  214. static char* ms_apszEndian[] =
  215. {
  216. "BigEndian",
  217. "NeutralEndian",
  218. "LittleEndian",
  219. };
  220. #ifdef ALLOW_RFILE_REOPEN
  221. RList <RFile> RFile::ms_listOpen; // List of open RFiles connected to
  222. // disk.
  223. #endif // ALLOW_RFILE_REOPEN
  224. //////////////////////////////////////////////////////////////////////////////
  225. // Construction/Destruction.
  226. //////////////////////////////////////////////////////////////////////////////
  227. //////////////////////////////////////////////////////////////////////////////
  228. //
  229. // Default constructor.
  230. //
  231. //////////////////////////////////////////////////////////////////////////////
  232. RFile::RFile(void)
  233. {
  234. m_fs = NULL;
  235. m_endian = BigEndian;
  236. m_flags = NoFlags;
  237. m_pucFile = NULL;
  238. m_sOwnMem = FALSE;
  239. m_pucCur = NULL;
  240. m_lSize = 0L;
  241. m_lGrowSize = 0L;
  242. m_sMemError = 0;
  243. m_sOpenSem = 0;
  244. m_sCloseSem = 0;
  245. m_lUser = 0;
  246. m_pfileSynch = NULL;
  247. }
  248. //////////////////////////////////////////////////////////////////////////////
  249. //
  250. // Destructor.
  251. //
  252. //////////////////////////////////////////////////////////////////////////////
  253. RFile::~RFile(void)
  254. {
  255. if (m_fs != NULL || m_pucFile != NULL)
  256. {
  257. Close();
  258. TRACE("~RFile(): Closed the file that you forgot to, hoser!\n");
  259. }
  260. }
  261. //////////////////////////////////////////////////////////////////////////////
  262. // Methods.
  263. //////////////////////////////////////////////////////////////////////////////
  264. #if PLATFORM_UNIX
  265. // this is from PhysicsFS originally ( http://icculus.org/physfs/ )
  266. // (also zlib-licensed.)
  267. static int locateOneElement(char *buf)
  268. {
  269. char *ptr = NULL;
  270. DIR *dirp = NULL;
  271. struct dirent *dent = NULL;
  272. if (access(buf, F_OK) == 0)
  273. return 1; /* quick rejection: exists in current case. */
  274. ptr = strrchr(buf, '/'); /* find entry at end of path. */
  275. if (ptr == NULL)
  276. {
  277. dirp = opendir(".");
  278. ptr = buf;
  279. }
  280. else
  281. {
  282. *ptr = '\0';
  283. dirp = opendir(buf);
  284. *ptr = '/';
  285. ptr++; /* point past dirsep to entry itself. */
  286. }
  287. while ((dent = readdir(dirp)) != NULL)
  288. {
  289. if (strcasecmp(dent->d_name, ptr) == 0)
  290. {
  291. strcpy(ptr, dent->d_name); /* found a match. Overwrite with this case. */
  292. closedir(dirp);
  293. return 1;
  294. }
  295. }
  296. /* no match at all... */
  297. closedir(dirp);
  298. return 0;
  299. }
  300. #endif
  301. static void locateCorrectCase(char *buf)
  302. {
  303. #if PLATFORM_UNIX
  304. char *ptr = buf;
  305. char *prevptr = buf;
  306. while (ptr = strchr(ptr + 1, '/'))
  307. {
  308. *ptr = '\0'; /* block this path section off */
  309. if (!locateOneElement(buf))
  310. {
  311. *ptr = '/'; /* restore path separator */
  312. return; /* missing element in path. */
  313. }
  314. *ptr = '/'; /* restore path separator */
  315. }
  316. /* check final element... */
  317. locateOneElement(buf);
  318. #endif
  319. }
  320. extern const char *FindCorrectFile(const char *_pszName, const char *pszMode)
  321. {
  322. char *pszName = (char *) alloca(strlen(_pszName) + 1);
  323. strcpy(pszName, _pszName);
  324. static bool initialized = false;
  325. static bool nohomedir = false;
  326. static char prefpath[PATH_MAX];
  327. if (!initialized)
  328. {
  329. TRACE("FindCorrectFile initializing...\n");
  330. if (rspCommandLine("nohomedir"))
  331. {
  332. TRACE("--nohomedir is on the command line.\n");
  333. nohomedir = true;
  334. }
  335. else
  336. {
  337. #ifdef WIN32
  338. /*
  339. * Vista and later has a new API for this, but SHGetFolderPath works there,
  340. * and apparently just wraps the new API. This is the new way to do it:
  341. *
  342. * SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE,
  343. * NULL, &wszPath);
  344. */
  345. strcpy(prefpath, ".\\"); // a default for failure case.
  346. HMODULE lib = LoadLibraryA("Shell32.dll");
  347. if (lib != NULL)
  348. {
  349. fnSHGetFolderPathW pSHGetFolderPathW = (fnSHGetFolderPathW) GetProcAddress(lib, "SHGetFolderPathW");
  350. if (pSHGetFolderPathW != NULL)
  351. {
  352. WCHAR path[MAX_PATH];
  353. if (SUCCEEDED(pSHGetFolderPathW(NULL, 0x001a/*CSIDL_APPDATA*/ | 0x8000/*CSIDL_FLAG_CREATE*/, NULL, 0, path)))
  354. {
  355. // !!! FIXME: screwed if there's a unicode path for now.
  356. snprintf(prefpath, sizeof (prefpath), "%S\\RunningWithScissors", (const wchar_t *) path);
  357. mkdir(prefpath);
  358. snprintf(prefpath, sizeof (prefpath), "%S\\RunningWithScissors\\Postal Plus", (const wchar_t *) path);
  359. mkdir(prefpath);
  360. snprintf(prefpath, sizeof (prefpath), "%S\\RunningWithScissors\\Postal Plus\\", (const wchar_t *) path);
  361. }
  362. }
  363. FreeLibrary(lib);
  364. }
  365. #elif defined(PLATFORM_MACOSX)
  366. const char *homedir = getenv("HOME");
  367. if ( (!homedir) || ((strlen(homedir) + 32) >= sizeof (prefpath)) )
  368. homedir = "./"; // oh well.
  369. strcpy(prefpath, homedir);
  370. if (prefpath[strlen(prefpath)-1] != '/') strcat(prefpath, "/");
  371. strcat(prefpath, "Library/Application Support/Postal Plus/");
  372. #else
  373. const char *homedir = getenv("HOME");
  374. const char *xdghomedir = getenv("XDG_DATA_HOME");
  375. const char *append = "";
  376. if (xdghomedir == NULL)
  377. {
  378. if (homedir == NULL)
  379. xdghomedir = "."; // oh well.
  380. else
  381. {
  382. xdghomedir = homedir;
  383. append = "/.local/share";
  384. }
  385. }
  386. snprintf(prefpath, sizeof (prefpath), "%s%s/PostalPlus/", xdghomedir, append);
  387. if (homedir != NULL)
  388. {
  389. char oldpath[PATH_MAX];
  390. snprintf(oldpath, sizeof (oldpath), "%s/.postal1", homedir);
  391. if (access(oldpath, F_OK) == 0)
  392. {
  393. TRACE("using oldschool prefpath at \"%s\"\n", oldpath);
  394. snprintf(prefpath, sizeof (prefpath), "%s/", oldpath);
  395. }
  396. }
  397. // try to make sure the dirs exist...
  398. for (char *i = prefpath; *i; i++)
  399. {
  400. if (*i == '/')
  401. {
  402. *i = '\0';
  403. mkdir(prefpath, 0700);
  404. *i = '/';
  405. }
  406. }
  407. mkdir(prefpath, 0700);
  408. #endif
  409. TRACE("prefpath is \"%s\"\n", prefpath);
  410. }
  411. initialized = true;
  412. }
  413. static char finalname[PATH_MAX];
  414. static bool bail_early = true;
  415. if (nohomedir)
  416. strcpy(finalname, pszName);
  417. else if ((strlen(pszName) + strlen(prefpath)) > sizeof (finalname))
  418. strcpy(finalname, pszName); // oh well.
  419. else
  420. {
  421. bail_early = false;
  422. sprintf(finalname, "%s%s", prefpath, pszName);
  423. }
  424. locateCorrectCase(finalname);
  425. if (bail_early) // don't choose between prefpath and basedir?
  426. return(finalname);
  427. // writing? Always use prefpath.
  428. if (strcspn(pszMode, "aAwW+") < strlen(pszMode))
  429. {
  430. // build directories...
  431. for (char *ptr = finalname; *ptr; ptr++)
  432. {
  433. if (((*ptr == '/') || (*ptr == '\\')) && (ptr != finalname))
  434. {
  435. *ptr = '\0';
  436. if (access(finalname, F_OK) == -1)
  437. {
  438. TRACE("Making directory \"%s\"\n", finalname);
  439. #ifdef WIN32
  440. mkdir(finalname);
  441. #else
  442. mkdir(finalname, S_IRWXU);
  443. #endif
  444. }
  445. *ptr = '/';
  446. }
  447. }
  448. // read AND write. :/ Copy the file if it's not there.
  449. if ((strchr(pszMode, '+')) && (access(finalname, F_OK) == -1))
  450. {
  451. FILE *in = fopen(pszName, "rb");
  452. FILE *out = fopen(finalname, "wb");
  453. if (in && out)
  454. {
  455. int ch = 0;
  456. while (1) // !!! FIXME: this is really lame.
  457. {
  458. ch = fgetc(in);
  459. if (ch == EOF) break;
  460. fputc(ch, out);
  461. }
  462. }
  463. if (in) fclose(in);
  464. if (out) fclose(out);
  465. }
  466. return finalname;
  467. }
  468. else // reading.
  469. {
  470. if (access(finalname, R_OK) == -1) // favor prefpath?
  471. {
  472. strcpy(finalname, pszName); // nope, use original name.
  473. locateCorrectCase(finalname);
  474. }
  475. }
  476. return finalname;
  477. }
  478. //////////////////////////////////////////////////////////////////////////////
  479. //
  480. // Open file pszFileName with fopen flags pszFlags and endian format endian
  481. // { BigEndian, LittleEndian }.
  482. // Returns 0 on success.
  483. //
  484. //////////////////////////////////////////////////////////////////////////////
  485. short RFile::Open( // Returns 0 on success.
  486. const char* pszFileName, // Filename to open.
  487. const char* pszFlags, // fopen flags to use for opening.
  488. Endian endian, // { BigEndian | LittleEndian | NeutralEndian }.
  489. Flags flags) // See comments in Typedefs & Enums section in .h.
  490. {
  491. short sRes = 0; // Assume success.
  492. // If not already open . . .
  493. if (m_fs == NULL && m_pucFile == NULL)
  494. {
  495. short sOpen = TRUE;
  496. // Store flags for this file.
  497. // Make sure Ascii and Binary are not both specified.
  498. ASSERT( (flags & (Ascii | Binary)) != (Ascii | Binary) );
  499. // Make sure either Ascii or Binary is specified.
  500. ASSERT( (flags & (Ascii | Binary)) != 0);
  501. m_flags = flags;
  502. // If hook defined . . .
  503. if (ms_hOpen != NULL)
  504. {
  505. // If not re-entered . . .
  506. if (m_sOpenSem == 0)
  507. {
  508. m_sOpenSem++;
  509. sOpen = (*ms_hOpen)(this, pszFileName, pszFlags, endian, ms_lOpenUser);
  510. m_sOpenSem--;
  511. }
  512. }
  513. // If no hook or hook told use to continue as normal . . .
  514. if (sOpen == TRUE)
  515. {
  516. // Set endian type.
  517. SetEndian(endian);
  518. // Attempt to open file.
  519. m_fs = fopen(FindCorrectFile(pszFileName, pszFlags), pszFlags);
  520. // If successful . . .
  521. if (m_fs != NULL)
  522. {
  523. // Attempt to set a better buffer size
  524. int setres = 0;
  525. #if 0
  526. size_t bufsize = BUFSIZ;
  527. if (bufsize < 16384)
  528. bufsize = (16384 / BUFSIZ) * BUFSIZ;
  529. setres = setvbuf(m_fs, NULL, _IOFBF, bufsize);
  530. #endif
  531. if (setres == 0)
  532. {
  533. // Success.
  534. #ifdef ALLOW_RFILE_REOPEN
  535. // Store file flags.
  536. ASSERT(strlen(pszFlags) < sizeof(m_szFlags));
  537. strcpy(m_szFlags, pszFlags);
  538. // Store file name.
  539. ASSERT(strlen(pszFileName) < sizeof(m_szFileName));
  540. strcpy(m_szFileName, pszFileName);
  541. // Update access.
  542. m_lLastAccess = Blu_GetTime();
  543. // Connected.
  544. m_sDisconnected = FALSE;
  545. // Add to open list.
  546. if (ms_listOpen.Add(this) == 0)
  547. {
  548. }
  549. else
  550. {
  551. TRACE("Open(\"%s\", \"%s\", %s): Unable to add to open list.\n",
  552. pszFileName, pszFlags,
  553. ms_apszEndian[endian]);
  554. sRes = -3;
  555. }
  556. #endif // ALLOW_RFILE_REOPEN
  557. }
  558. else
  559. {
  560. TRACE("Open(\"%s\", \"%s\", %s): Error returned by setvbuf()!\n",
  561. pszFileName, pszFlags,
  562. ms_apszEndian[endian]);
  563. sRes = -5; // Is there any REAL sense to these error numbers???
  564. }
  565. // If an error occurrs after fopen . . .
  566. if (sRes != 0)
  567. {
  568. fclose(m_fs);
  569. m_fs = NULL;
  570. }
  571. }
  572. else
  573. {
  574. #ifdef FILE_VERBOSE
  575. TRACE("Open(\"%s\", \"%s\", %s): Unable to open file.\n",
  576. pszFileName, pszFlags,
  577. ms_apszEndian[endian]);
  578. #endif // FILE_VERBOSE
  579. sRes = -1;
  580. }
  581. }
  582. }
  583. else
  584. {
  585. TRACE("Open(\"%s\", \"%s\", %s): File already open.\n",
  586. pszFileName, pszFlags,
  587. ms_apszEndian[endian]);
  588. sRes = -2;
  589. }
  590. return sRes;
  591. }
  592. //////////////////////////////////////////////////////////////////////////////
  593. //
  594. // Open memory pFile of size lSize and endian format endian
  595. // { BigEndian, LittleEndian }.
  596. // Returns 0 on success.
  597. //
  598. //////////////////////////////////////////////////////////////////////////////
  599. short RFile::Open( // Returns 0 on success.
  600. void* pFile, // Pointer to memory to open.
  601. long lSize, // Size of *pFile in bytes.
  602. Endian endian) // { BigEndian | LittleEndian | NeutralEndian }.
  603. {
  604. short sRes = 0; // Assume success.
  605. // If not already open . . .
  606. if (m_fs == NULL && m_pucFile == NULL)
  607. {
  608. // Store flags for this file.
  609. m_flags = Binary;
  610. // Set endian type.
  611. SetEndian(endian);
  612. // Open memory.
  613. m_pucFile = m_pucCur = (UCHAR*)pFile;
  614. // Do not own buffer.
  615. m_sOwnMem = FALSE;
  616. // Set size of file.
  617. m_lSize = lSize;
  618. // Clear error flag.
  619. m_sMemError = 0;
  620. }
  621. else
  622. {
  623. TRACE("Open(%08lX, %ld, %s): File already open.\n",
  624. pFile, lSize,
  625. ms_apszEndian[endian]);
  626. sRes = -2;
  627. }
  628. return sRes;
  629. }
  630. //////////////////////////////////////////////////////////////////////////////
  631. //
  632. // Open memory pFile of size lSize and endian format endian.
  633. // RFile may size and or relocate the memory in order to expand the memory file.
  634. // Deallocates on Close().
  635. // { RFile::Big, RFile::Little }.
  636. // Returns 0 on success.
  637. //
  638. //////////////////////////////////////////////////////////////////////////////
  639. short RFile::Open( // Returns 0 on success.
  640. long lSize, // Size in bytes to begin with.
  641. long lGrowSize, // Min amount in bytes to grow memory file when written passed end.
  642. // Note: The larger of lGrowSize and the amount overwritten will
  643. // be allocated in the case of an overrun.
  644. Endian endian) // { BigEndian | LittleEndian | NeutralEndian }.
  645. {
  646. short sRes = 0; // Assume success.
  647. // If not already open . . .
  648. if (m_fs == NULL && m_pucFile == NULL)
  649. {
  650. // Store flags for this file.
  651. m_flags = Binary;
  652. // Set endian type.
  653. SetEndian(endian);
  654. // Open memory.
  655. m_pucFile = m_pucCur = (UCHAR*)malloc(lSize);
  656. if (m_pucFile != NULL)
  657. {
  658. // Do own buffer.
  659. m_sOwnMem = TRUE;
  660. // Set size of file.
  661. m_lSize = lSize;
  662. // Set minimum grow size for overwrites.
  663. m_lGrowSize = lGrowSize;
  664. // Clear error flag.
  665. m_sMemError = 0;
  666. }
  667. else
  668. {
  669. TRACE("Open(%ld, %ld, %s): File too large for free memory.\n",
  670. lSize, lGrowSize,
  671. ms_apszEndian[endian]);
  672. sRes = -3;
  673. }
  674. }
  675. else
  676. {
  677. TRACE("Open(%ld, %ld, %s): File already open.\n",
  678. lSize, lGrowSize,
  679. ms_apszEndian[endian]);
  680. sRes = -2;
  681. }
  682. return sRes;
  683. }
  684. //////////////////////////////////////////////////////////////////////////////
  685. //
  686. // Open an existing FILE* stream.
  687. // Once a FILE* is opened, you can use this class's Close() instead of fclose(),
  688. // if that is more convenient.
  689. //
  690. //////////////////////////////////////////////////////////////////////////////
  691. short RFile::Open( // Returns 0 on success.
  692. FILE* fs, // FILE* stream to open.
  693. Endian endian, // { RFile::BigEndian | RFile::LittleEndian | RFile::NeutralEndian }.
  694. Flags flags /*= // See comments in Typedefs & Enums section
  695. Binary*/) // above.
  696. {
  697. short sRes = 0; // Assume success.
  698. if (fs != NULL)
  699. {
  700. // Store flags for this file.
  701. // Make sure Ascii and Binary are not both specified.
  702. ASSERT( (flags & (Ascii | Binary)) != (Ascii | Binary) );
  703. // Make sure either Ascii or Binary is specified.
  704. ASSERT( (flags & (Ascii | Binary)) != 0);
  705. m_flags = flags;
  706. // Set endian.
  707. SetEndian(endian);
  708. // Store FILE* stream.
  709. m_fs = fs;
  710. }
  711. else
  712. {
  713. TRACE("Open(0x%08lX, %s): Invalid FILE*.\n",
  714. fs,
  715. ms_apszEndian[endian]);
  716. sRes = -1;
  717. }
  718. return sRes;
  719. }
  720. //////////////////////////////////////////////////////////////////////////////
  721. //
  722. // Open an existing RFile.
  723. // "Ack!" you say ... and I agree.
  724. // This basically begins what I like to think of as a synchronization between two RFiles.
  725. // This RFile snags the current state (basically copies the members) from the specified
  726. // RFile. Then, one can use the new RFile to access the file/memory/whatever that the
  727. // original RFile is attached to. When Close() is called, the synchronization is finsished
  728. // by updating the original RFile with the state from this.
  729. // Danger: Do not access the original RFile between Open(RFile*)/Close() pairs!
  730. //
  731. //////////////////////////////////////////////////////////////////////////////
  732. short RFile::Open( // Returns 0 on success.
  733. RFile* pfile) // RFile to open.
  734. {
  735. short sRes = 0;
  736. // Synchronize.
  737. m_fs = pfile->m_fs;
  738. m_pucFile = pfile->m_pucFile;
  739. m_sOwnMem = pfile->m_sOwnMem;
  740. m_pucCur = pfile->m_pucCur;
  741. m_lSize = pfile->m_lSize;
  742. m_lGrowSize = pfile->m_lGrowSize;
  743. m_sMemError = pfile->m_sMemError;
  744. m_endian = pfile->m_endian;
  745. m_flags = pfile->m_flags;
  746. // Remember who to Unsynch with.
  747. m_pfileSynch = pfile;
  748. return sRes;
  749. }
  750. //////////////////////////////////////////////////////////////////////////////
  751. //
  752. // Change the endian format used to read/write the file.
  753. // Returns nothing.
  754. //
  755. //////////////////////////////////////////////////////////////////////////////
  756. void RFile::SetEndian(Endian endian)
  757. {
  758. ASSERT(endian == BigEndian || endian == NeutralEndian || endian == LittleEndian);
  759. m_endian = endian;
  760. }
  761. //////////////////////////////////////////////////////////////////////////////
  762. //
  763. // Close a file successfully opened with Open().
  764. // Returns 0 on success.
  765. //
  766. //////////////////////////////////////////////////////////////////////////////
  767. short RFile::Close(void)
  768. {
  769. short sRes = 0; // Assume success.
  770. ASSERT(m_fs != NULL || m_pucFile != NULL);
  771. // If we are synchronizing with another RFile . . .
  772. if (m_pfileSynch != NULL)
  773. {
  774. // De/Un/Resynch.
  775. m_pfileSynch->m_fs = m_fs;
  776. m_pfileSynch->m_pucFile = m_pucFile;
  777. m_pfileSynch->m_sOwnMem = m_sOwnMem;
  778. m_pfileSynch->m_pucCur = m_pucCur;
  779. m_pfileSynch->m_lSize = m_lSize;
  780. m_pfileSynch->m_lGrowSize = m_lGrowSize;
  781. m_pfileSynch->m_sMemError = m_sMemError;
  782. m_pfileSynch->m_endian = m_endian;
  783. m_pfileSynch->m_flags = m_flags;
  784. // Clear ("Close").
  785. m_fs = NULL;
  786. m_pucFile = NULL;
  787. m_sOwnMem = FALSE;
  788. m_pucCur = NULL;
  789. m_sMemError = FALSE;
  790. m_pfileSynch = NULL;
  791. }
  792. else
  793. {
  794. short sClose = TRUE;
  795. // If hook defined . . .
  796. if (ms_hClose != NULL)
  797. {
  798. // If not re-entered . . .
  799. if (m_sCloseSem == 0)
  800. {
  801. m_sCloseSem++;
  802. sClose = (*ms_hClose)(this, ms_lCloseUser);
  803. m_sCloseSem--;
  804. }
  805. }
  806. // If no hook or hook told us to perform business as usual . . .
  807. if (sClose == TRUE)
  808. {
  809. if (IsFile() == TRUE)
  810. {
  811. KEEPCONNECTEDANDUPDATELASTACCESS;
  812. if (fclose(m_fs) == 0)
  813. {
  814. // Success.
  815. m_fs = NULL;
  816. #ifdef ALLOW_RFILE_REOPEN
  817. // Remove from open list.
  818. if (ms_listOpen.Remove(this) == 0)
  819. {
  820. }
  821. else
  822. {
  823. TRACE("Close(): Unable to remove this from open list.\n");
  824. sRes = -3;
  825. }
  826. #endif // ALLLOW_RFILE_REOPEN
  827. }
  828. else
  829. {
  830. TRACE("Close(): Unable to close file.\n");
  831. sRes = -2;
  832. }
  833. }
  834. else
  835. {
  836. if (IsMemory() == TRUE)
  837. {
  838. // If we own the buffer . . .
  839. if (m_sOwnMem != FALSE)
  840. {
  841. // Make sure we still have a buffer (a resize could have failed).
  842. if (m_pucFile != NULL)
  843. {
  844. // Be gone.
  845. free(m_pucFile);
  846. }
  847. }
  848. m_pucFile = NULL;
  849. m_sOwnMem = FALSE;
  850. m_pucCur = NULL;
  851. m_lSize = 0L;
  852. m_lGrowSize = 0L;
  853. m_sMemError = 0;
  854. }
  855. else
  856. {
  857. TRACE("Close(): Unable to close unopened file.\n");
  858. sRes = -1;
  859. }
  860. }
  861. }
  862. }
  863. return sRes;
  864. }
  865. //////////////////////////////////////////////////////////////////////////////
  866. //
  867. // Reads sNum bytes from currently open file.
  868. // Returns number of bytes successfully read.
  869. //
  870. //////////////////////////////////////////////////////////////////////////////
  871. long RFile::Read(void* pData, long lNum)
  872. {
  873. long lRes = 0; // Assume success.
  874. if (IsFile() == TRUE)
  875. {
  876. long lToRead;
  877. long lDidRead = 1;
  878. long lMaxRead = ms_criticall ? MAX_CALLBACK_GRANULARITY_BYTES : lNum;
  879. do
  880. {
  881. lToRead = MIN(lMaxRead, lNum);
  882. // If there is a CritiCall . . .
  883. if (ms_criticall != NULL)
  884. {
  885. // Call it.
  886. (*ms_criticall)(lToRead);
  887. }
  888. KEEPCONNECTEDANDUPDATELASTACCESS;
  889. lDidRead = fread(pData, 1, lToRead, m_fs);
  890. if (lDidRead > 0)
  891. {
  892. lRes += lDidRead;
  893. lNum -= lDidRead;
  894. pData = (U8*)pData + lDidRead;
  895. }
  896. } while (lNum > 0 && lDidRead > 0);
  897. }
  898. else
  899. {
  900. if (IsMemory() == TRUE)
  901. {
  902. if (m_pucCur + lNum <= m_pucFile + m_lSize)
  903. {
  904. lRes = lNum;
  905. }
  906. else
  907. {
  908. lRes = (m_pucFile + m_lSize) - m_pucCur;
  909. // TRACE("Read(): Attempt to read passed end of memory file.\n");
  910. m_sMemError = 1;
  911. }
  912. // "Read" data.
  913. memcpy(pData, m_pucCur, lRes);
  914. // Move file ptr.
  915. m_pucCur += lRes;
  916. }
  917. else
  918. {
  919. TRACE("Read(): File not open.\n");
  920. lRes = -1L;
  921. }
  922. }
  923. return lRes;
  924. }
  925. //////////////////////////////////////////////////////////////////////////////
  926. //
  927. // Attempts to read lNum TYPE numbers from pfsIn or pu8In.
  928. //
  929. //////////////////////////////////////////////////////////////////////////////
  930. template <
  931. class TYPE> // Type for storing and overflow checking.
  932. inline // Speed.
  933. long ReadASCII( // Returns number of complete TYPE items successfully read
  934. // and stored.
  935. TYPE* ptData, // Out: Pointer to array of TYPE items for read data.
  936. long lNum, // In: Number of TYPE items to read.
  937. FILE* pfsIn, // In: File stream to use for input.
  938. double dMax) // In: Maximum value for this type.
  939. {
  940. long lRes = 0; // Assume success.
  941. // Temp var to read into.
  942. double dTemp;
  943. while (lNum--)
  944. {
  945. if (fscanf(pfsIn, " %le", &dTemp) == 1)
  946. {
  947. // Successfully read an item.
  948. // Check size . . .
  949. if (ABS(dTemp) < dMax)
  950. {
  951. // Within range. Store.
  952. *ptData++ = (TYPE)dTemp;
  953. // Increment number stored.
  954. lRes++;
  955. }
  956. else
  957. {
  958. TRACE("ReadASCII(): Read %le which does not fit into item of size %u bits.\n",
  959. dTemp, sizeof(TYPE) * 8);
  960. break;
  961. }
  962. }
  963. else
  964. {
  965. TRACE("ReadASCII(): Failed to read numeric of size %u bits.\n",
  966. sizeof(TYPE) * 8);
  967. break;
  968. }
  969. }
  970. return lRes;
  971. }
  972. //////////////////////////////////////////////////////////////////////////////
  973. //
  974. // Reads lNum U8 values from currently open file.
  975. // Returns number of U8 values successfully written.
  976. //
  977. //////////////////////////////////////////////////////////////////////////////
  978. long RFile::Read(U8* pu8Data, long lNum /*= 1L*/)
  979. {
  980. long lRes = 0L; // Assume success.
  981. if ((m_flags & Ascii) != 0)
  982. {
  983. // Read ASCII data.
  984. lRes = ReadASCII(pu8Data, lNum, m_fs, (double)UCHAR_MAX);
  985. }
  986. else
  987. {
  988. // Read Binary data.
  989. lRes = Read8(pu8Data, lNum);
  990. }
  991. return lRes;
  992. }
  993. //////////////////////////////////////////////////////////////////////////////
  994. //
  995. // Reads lNum S8 values from currently open file.
  996. // Returns number of S8 values successfully written.
  997. //
  998. //////////////////////////////////////////////////////////////////////////////
  999. long RFile::Read(S8* ps8Data, long lNum /*= 1L*/)
  1000. {
  1001. long lRes = 0L; // Assume success.
  1002. if ((m_flags & Ascii) != 0)
  1003. {
  1004. // Read ASCII data.
  1005. lRes = ReadASCII(ps8Data, lNum, m_fs, (double)CHAR_MAX);
  1006. }
  1007. else
  1008. {
  1009. // Read Binary data.
  1010. lRes = Read8((U8*)ps8Data, lNum);
  1011. }
  1012. return lRes;
  1013. }
  1014. //////////////////////////////////////////////////////////////////////////////
  1015. //
  1016. // Reads sNum U16 values from currently open file.
  1017. // Returns number of U16 values successfully read.
  1018. //
  1019. //////////////////////////////////////////////////////////////////////////////
  1020. long RFile::Read(U16* pu16Data, long lNum /*= 1L*/)
  1021. {
  1022. long lRes = 0L; // Assume success.
  1023. if ((m_flags & Ascii) != 0)
  1024. {
  1025. // Read ASCII data.
  1026. lRes = ReadASCII(pu16Data, lNum, m_fs, (double)USHRT_MAX);
  1027. }
  1028. else
  1029. {
  1030. // Read Binary data.
  1031. lRes = Read16(pu16Data, lNum);
  1032. }
  1033. return lRes;
  1034. }
  1035. //////////////////////////////////////////////////////////////////////////////
  1036. //
  1037. // Reads lNum S16 values from currently open file.
  1038. // Returns number of S16 values successfully read.
  1039. //
  1040. //////////////////////////////////////////////////////////////////////////////
  1041. long RFile::Read(S16* ps16Data, long lNum /*= 1L*/)
  1042. {
  1043. long lRes = 0L; // Assume success.
  1044. if ((m_flags & Ascii) != 0)
  1045. {
  1046. // Read ASCII data.
  1047. lRes = ReadASCII(ps16Data, lNum, m_fs, (double)SHRT_MAX);
  1048. }
  1049. else
  1050. {
  1051. // Read Binary data.
  1052. lRes = Read16((U16*)ps16Data, lNum);
  1053. }
  1054. return lRes;
  1055. }
  1056. //////////////////////////////////////////////////////////////////////////////
  1057. //
  1058. // Reads lNum RPixel24 values from currently open file.
  1059. // Returns number of RPixel24 values successfully read.
  1060. //
  1061. //////////////////////////////////////////////////////////////////////////////
  1062. long RFile::Read(RPixel24* ppix24, long lNum /*= 1L*/)
  1063. {
  1064. long lRes = 0L; // Assume success.
  1065. if ((m_flags & Ascii) != 0)
  1066. {
  1067. ASSERT(0 && "**Cannot read 24 bit types in ASCII**");
  1068. // Read ASCII data.
  1069. // lRes = ReadASCII(ppix24, lNum, m_fs, (double)(1 << 24));
  1070. }
  1071. else
  1072. {
  1073. // Read Binary data.
  1074. lRes = Read24(ppix24, lNum);
  1075. }
  1076. return lRes;
  1077. }
  1078. //////////////////////////////////////////////////////////////////////////////
  1079. //
  1080. // Reads sNum U32 values from currently open file.
  1081. // Returns number of U32 values successfully read.
  1082. //
  1083. //////////////////////////////////////////////////////////////////////////////
  1084. long RFile::Read(U32* pu32Data, long lNum /*= 1L*/)
  1085. {
  1086. long lRes = 0L; // Assume success.
  1087. if ((m_flags & Ascii) != 0)
  1088. {
  1089. // Read ASCII data.
  1090. lRes = ReadASCII(pu32Data, lNum, m_fs, (double)ULONG_MAX);
  1091. }
  1092. else
  1093. {
  1094. // Read Binary data.
  1095. lRes = Read32(pu32Data, lNum);
  1096. }
  1097. return lRes;
  1098. }
  1099. //////////////////////////////////////////////////////////////////////////////
  1100. //
  1101. // Reads lNum S32 values from currently open file.
  1102. // Returns number of S32 values successfully read.
  1103. //
  1104. //////////////////////////////////////////////////////////////////////////////
  1105. long RFile::Read(S32* ps32Data, long lNum /*= 1L*/)
  1106. {
  1107. long lRes = 0L; // Assume success.
  1108. if ((m_flags & Ascii) != 0)
  1109. {
  1110. // Read ASCII data.
  1111. lRes = ReadASCII(ps32Data, lNum, m_fs, (double)LONG_MAX);
  1112. }
  1113. else
  1114. {
  1115. // Read Binary data.
  1116. lRes = Read32((U32*)ps32Data, lNum);
  1117. }
  1118. return lRes;
  1119. }
  1120. //////////////////////////////////////////////////////////////////////////////
  1121. //
  1122. // Reads lNum U64 values from currently open file.
  1123. // Returns number of U64 values successfully read.
  1124. //
  1125. //////////////////////////////////////////////////////////////////////////////
  1126. long RFile::Read(U64* pu64Data, long lNum /*= 1L*/)
  1127. {
  1128. long lRes = 0L; // Assume success.
  1129. if ((m_flags & Ascii) != 0)
  1130. {
  1131. ASSERT(0 && "**Cannot read 64 bit types other than double in ASCII**");
  1132. }
  1133. else
  1134. {
  1135. // Read Binary data.
  1136. lRes = Read64(pu64Data, lNum);
  1137. }
  1138. return lRes;
  1139. }
  1140. //////////////////////////////////////////////////////////////////////////////
  1141. //
  1142. // Reads lNum S64 values from currently open file.
  1143. // Returns number of S64 values successfully read.
  1144. //
  1145. //////////////////////////////////////////////////////////////////////////////
  1146. long RFile::Read(S64* ps64Data, long lNum /*= 1L*/)
  1147. {
  1148. long lRes = 0L; // Assume success.
  1149. if ((m_flags & Ascii) != 0)
  1150. {
  1151. ASSERT(0 && "**Cannot read 64 bit types other than double in ASCII**");
  1152. }
  1153. else
  1154. {
  1155. // Read Binary data.
  1156. lRes = Read64((U64*)ps64Data, lNum);
  1157. }
  1158. return lRes;
  1159. }
  1160. //////////////////////////////////////////////////////////////////////////////
  1161. //
  1162. // Reads lNum float values from currently open file.
  1163. // Returns number of float values successfully read.
  1164. //
  1165. //////////////////////////////////////////////////////////////////////////////
  1166. long RFile::Read(float* pfData, long lNum /*= 1L*/)
  1167. {
  1168. long lRes = 0L; // Assume success.
  1169. if ((m_flags & Ascii) != 0)
  1170. {
  1171. // Read ASCII data.
  1172. lRes = ReadASCII(pfData, lNum, m_fs, (double)FLT_MAX);
  1173. }
  1174. else
  1175. {
  1176. // Read Binary data.
  1177. lRes = Read32((U32*)pfData, lNum);
  1178. }
  1179. return lRes;
  1180. }
  1181. //////////////////////////////////////////////////////////////////////////////
  1182. //
  1183. // Reads lNum double values from currently open file.
  1184. // Returns number of double values successfully read.
  1185. //
  1186. //////////////////////////////////////////////////////////////////////////////
  1187. long RFile::Read(double* pdData, long lNum /*= 1L*/)
  1188. {
  1189. long lRes = 0L; // Assume success.
  1190. if ((m_flags & Ascii) != 0)
  1191. {
  1192. // Read ASCII data.
  1193. lRes = ReadASCII(pdData, lNum, m_fs, DBL_MAX);
  1194. }
  1195. else
  1196. {
  1197. // Read Binary data.
  1198. lRes = Read64((U64*)pdData, lNum);
  1199. }
  1200. return lRes;
  1201. }
  1202. //////////////////////////////////////////////////////////////////////////////
  1203. // NOTE that this function has very different functionality in each of the
  1204. // two Open() modes (Ascii & Binary).
  1205. //////////////////////////////////////////////////////////////////////////////
  1206. //
  1207. // ASCII MODE:
  1208. // Reads a quotes or white-space delimited string. Quotes preceded by back-
  1209. // slash are ignored and are stored as just quotes (without the backslash).
  1210. // Strings are either whitespace or quotes delimited (e.g., a string beginning
  1211. // with quotes can only be ended by quotes, and a string beginning without
  1212. // quotes can only be ended by whitespace).
  1213. // Returns number of characters successfully stored (which may be less than
  1214. // the amount read), NOT including the NULL terminator (like strlen()).
  1215. //
  1216. // BINARY MODE:
  1217. // Reads a NULL terminated string.
  1218. // Returns number of characters successfully read,
  1219. // including the NULL terminator (UNlike strlen()).
  1220. //
  1221. //
  1222. // pszString must point to a memory block sufficiently large
  1223. // enough to hold the string.
  1224. //
  1225. //////////////////////////////////////////////////////////////////////////////
  1226. long RFile::Read(char* pszString)
  1227. {
  1228. long lRes = 0; // Assume nothing.
  1229. if (m_flags & Binary)
  1230. {
  1231. do
  1232. {
  1233. // Read one character.
  1234. if (Read(pszString, 1) == 1)
  1235. {
  1236. }
  1237. else
  1238. {
  1239. TRACE("Read(): Failed to read to NULL terminator of string.\n");
  1240. *pszString = '\0';
  1241. }
  1242. lRes++;
  1243. // Do this until we hit the NULL or an error occurs.
  1244. } while (*pszString++ != '\0');
  1245. }
  1246. else
  1247. {
  1248. // Skip whitespace.
  1249. do
  1250. {
  1251. if (Read(pszString, 1) == 1)
  1252. {
  1253. }
  1254. else
  1255. {
  1256. break;
  1257. }
  1258. } while (strchr(WHITE_SPACE, *pszString++) == NULL);
  1259. // Note whether started by qutoes.
  1260. short sInQuotes = FALSE;
  1261. if (*pszString == '"')
  1262. {
  1263. sInQuotes = TRUE;
  1264. }
  1265. // Store string.
  1266. short sLastCharWasBackSlash = FALSE;
  1267. while (*pszString != '\0')
  1268. {
  1269. if (Read(pszString, 1) == 1)
  1270. {
  1271. // Increment count.
  1272. lRes++;
  1273. // If previous was backslash . . .
  1274. if (sLastCharWasBackSlash != FALSE)
  1275. {
  1276. // If current is quotes . . .
  1277. if (*pszString == '"')
  1278. {
  1279. // Overwrite backslash.
  1280. *pszString-- = '"';
  1281. // Deduct from count.
  1282. lRes--;
  1283. }
  1284. }
  1285. switch (*pszString++)
  1286. {
  1287. case '\\':
  1288. sLastCharWasBackSlash = TRUE;
  1289. break;
  1290. case '"':
  1291. // If this is quotes delimited . . .
  1292. if (sInQuotes != FALSE && sLastCharWasBackSlash == FALSE)
  1293. {
  1294. // Done.
  1295. *pszString = '\0';
  1296. }
  1297. // Intentional fall through to default case.
  1298. default:
  1299. // Clear last was backslash flag.
  1300. sLastCharWasBackSlash = FALSE;
  1301. // If this is whitespace delimited . . .
  1302. if (sInQuotes == FALSE)
  1303. {
  1304. // If this is whitespace . . .
  1305. if (strchr(WHITE_SPACE, *pszString) != NULL)
  1306. {
  1307. // Done.
  1308. *pszString = '\0';
  1309. }
  1310. }
  1311. break;
  1312. }
  1313. }
  1314. else
  1315. {
  1316. // Done.
  1317. *pszString = '\0';
  1318. }
  1319. }
  1320. }
  1321. return lRes;
  1322. }
  1323. //////////////////////////////////////////////////////////////////////////////
  1324. //
  1325. // Writes lNum bytes from currently open file.
  1326. // Returns number of bytes successfully written.
  1327. //
  1328. //////////////////////////////////////////////////////////////////////////////
  1329. long RFile::Write(const void* pData, long lNum)
  1330. {
  1331. long lRes = 0; // Assume success.
  1332. if (IsFile() == TRUE)
  1333. {
  1334. long lToWrite;
  1335. long lDidWrite = 1;
  1336. long lMaxWrite = ms_criticall ? MAX_CALLBACK_GRANULARITY_BYTES : lNum;
  1337. do
  1338. {
  1339. lToWrite = MIN(lMaxWrite, lNum);
  1340. // If there is a CritiCall . . .
  1341. if (ms_criticall != NULL)
  1342. {
  1343. // Call it.
  1344. (*ms_criticall)(lNum);
  1345. }
  1346. KEEPCONNECTEDANDUPDATELASTACCESS;
  1347. lDidWrite = fwrite(pData, 1, lToWrite, m_fs);
  1348. if (lDidWrite > 0)
  1349. {
  1350. lRes += lDidWrite;
  1351. lNum -= lDidWrite;
  1352. pData = (U8*)pData + lDidWrite;
  1353. }
  1354. } while (lDidWrite > 0 && lNum > 0);
  1355. }
  1356. else
  1357. {
  1358. if (IsMemory() == TRUE)
  1359. {
  1360. // If still within buffer . . .
  1361. if (m_pucCur + lNum <= m_pucFile + m_lSize)
  1362. {
  1363. lRes = lNum;
  1364. }
  1365. else
  1366. {
  1367. // If we don't own the bufer . . .
  1368. if (m_sOwnMem == FALSE)
  1369. {
  1370. // Unlike normal files, our file can't get bigger.
  1371. // Our space is limited by the original value of m_lSize.
  1372. lRes = (m_pucFile + m_lSize) - m_pucCur;
  1373. TRACE("Write(): Attempt to write passed end of memory file.\n");
  1374. m_sMemError = 1;
  1375. }
  1376. else
  1377. {
  1378. // Attempt to enlarge the buffer by the max of m_lGrowSize or the
  1379. // overrun amount.
  1380. long lCurPos = m_pucCur - m_pucFile;
  1381. long lDistanceToEOF = m_lSize - lCurPos;
  1382. long lNewSize = m_lSize + MAX(m_lGrowSize, (lNum - lDistanceToEOF) );
  1383. // Enlarge . . .
  1384. UCHAR* pucNewFile = (UCHAR*)realloc(m_pucFile, lNewSize);
  1385. // If successful . . .
  1386. if (pucNewFile != NULL)
  1387. {
  1388. // Set new buffer pointer.
  1389. m_pucFile = pucNewFile;
  1390. // Set current position.
  1391. m_pucCur = pucNewFile + lCurPos;
  1392. // Set new buffer size.
  1393. m_lSize = lNewSize;
  1394. // Write amount requested.
  1395. lRes = lNum;
  1396. }
  1397. else
  1398. {
  1399. // Only write what we can. Keep same buffer.
  1400. lRes = lDistanceToEOF;
  1401. }
  1402. }
  1403. }
  1404. // "Write" data.
  1405. memcpy(m_pucCur, pData, lRes);
  1406. // Move file ptr.
  1407. m_pucCur += lRes;
  1408. }
  1409. else
  1410. {
  1411. TRACE("Write(): File not open.\n");
  1412. lRes = -1L;
  1413. }
  1414. }
  1415. return lRes;
  1416. }
  1417. //////////////////////////////////////////////////////////////////////////////
  1418. //
  1419. // Attempts to write lNum TYPE numbers from pfsIn or pu8In.
  1420. //
  1421. //////////////////////////////////////////////////////////////////////////////
  1422. template <
  1423. class TYPE> // Type for storing and overflow checking.
  1424. inline // Speed.
  1425. long WriteASCII( // Returns number of complete TYPE items successfully
  1426. // written.
  1427. TYPE* ptData, // In: Pointer to array of TYPE items for write data.
  1428. long lNum, // In: Number of TYPE items to read.
  1429. FILE* pfsOut, // In: File stream to use for output.
  1430. char* pszFrmt) // In: Output printf style format specifier.
  1431. {
  1432. long lRes = 0; // Assume success.
  1433. while (lNum--)
  1434. {
  1435. if (fprintf(pfsOut, pszFrmt, *ptData++) > 0)
  1436. {
  1437. // Successfully wrote an item.
  1438. // Increment number written.
  1439. lRes++;
  1440. }
  1441. else
  1442. {
  1443. TRACE("WriteASCII(): Failed to write numeric of size %u bits.\n",
  1444. sizeof(TYPE) * 8);
  1445. break;
  1446. }
  1447. }
  1448. return lRes;
  1449. }
  1450. //////////////////////////////////////////////////////////////////////////////
  1451. //
  1452. // Writes lNum U8 values to currently open file.
  1453. // Returns number of U8 values successfully written.
  1454. //
  1455. //////////////////////////////////////////////////////////////////////////////
  1456. long RFile::Write(const U8* pu8Data, long lNum /*= 1L*/)
  1457. {
  1458. long lRes = 0L; // Assume success.
  1459. if ((m_flags & Ascii) != 0)
  1460. {
  1461. // Write ASCII data.
  1462. lRes = WriteASCII(pu8Data, lNum, m_fs, "%u ");
  1463. }
  1464. else
  1465. {
  1466. // Write Binary data.
  1467. lRes = Write8(pu8Data, lNum);
  1468. }
  1469. return lRes;
  1470. }
  1471. //////////////////////////////////////////////////////////////////////////////
  1472. //
  1473. // Writes lNum S8 values to currently open file.
  1474. // Returns number of S8 values successfully written.
  1475. //
  1476. //////////////////////////////////////////////////////////////////////////////
  1477. long RFile::Write(const S8* ps8Data, long lNum /*= 1L*/)
  1478. {
  1479. long lRes = 0L; // Assume success.
  1480. if ((m_flags & Ascii) != 0)
  1481. {
  1482. // Write ASCII data.
  1483. lRes = WriteASCII(ps8Data, lNum, m_fs, "%d ");
  1484. }
  1485. else
  1486. {
  1487. // Write Binary data.
  1488. lRes = Write8((U8*)ps8Data, lNum);
  1489. }
  1490. return lRes;
  1491. }
  1492. //////////////////////////////////////////////////////////////////////////////
  1493. //
  1494. // Writes sNum U16 values from currently open file.
  1495. // Returns number of U16 values successfully written.
  1496. //
  1497. //////////////////////////////////////////////////////////////////////////////
  1498. long RFile::Write(const U16* pu16Data, long lNum /*= 1L*/)
  1499. {
  1500. long lRes = 0L; // Assume success.
  1501. if ((m_flags & Ascii) != 0)
  1502. {
  1503. // Write ASCII data.
  1504. lRes = WriteASCII(pu16Data, lNum, m_fs, "%u ");
  1505. }
  1506. else
  1507. {
  1508. // Write Binary data.
  1509. lRes = Write16(pu16Data, lNum);
  1510. }
  1511. return lRes;
  1512. }
  1513. //////////////////////////////////////////////////////////////////////////////
  1514. //
  1515. // Writes lNum S16 values to currently open file.
  1516. // Returns number of S16 values successfully written.
  1517. //
  1518. //////////////////////////////////////////////////////////////////////////////
  1519. long RFile::Write(const S16* ps16Data, long lNum /*= 1L*/)
  1520. {
  1521. long lRes = 0L; // Assume success.
  1522. if ((m_flags & Ascii) != 0)
  1523. {
  1524. // Write ASCII data.
  1525. lRes = WriteASCII(ps16Data, lNum, m_fs, "%d ");
  1526. }
  1527. else
  1528. {
  1529. // Write Binary data.
  1530. lRes = Write16((U16*)ps16Data, lNum);
  1531. }
  1532. return lRes;
  1533. }
  1534. //////////////////////////////////////////////////////////////////////////////
  1535. //
  1536. // Writes lNum RPixel24 values to currently open file.
  1537. // Returns number of RPixel24 values successfully written.
  1538. //
  1539. //////////////////////////////////////////////////////////////////////////////
  1540. long RFile::Write(const RPixel24* ppix24, long lNum /*= 1L*/)
  1541. {
  1542. long lRes = 0; // Assume success.
  1543. if ((m_flags & Ascii) != 0)
  1544. {
  1545. ASSERT(0 && "**Cannot write 24 bit types in ASCII**");
  1546. // Write ASCII data.
  1547. // lRes = WriteASCII(ppix24, lNum, m_fs, "%06lX ");
  1548. }
  1549. else
  1550. {
  1551. // Write Binary data.
  1552. lRes = Write24(ppix24, lNum);
  1553. }
  1554. return lRes;
  1555. }
  1556. //////////////////////////////////////////////////////////////////////////////
  1557. //
  1558. // Writes sNum U32 values from currently open file.
  1559. // Returns number of U32 values successfully written.
  1560. //
  1561. //////////////////////////////////////////////////////////////////////////////
  1562. long RFile::Write(const U32* pu32Data, long lNum /*= 1L*/)
  1563. {
  1564. long lRes = 0; // Assume success.
  1565. if ((m_flags & Ascii) != 0)
  1566. {
  1567. // Write ASCII data.
  1568. lRes = WriteASCII(pu32Data, lNum, m_fs, "%lu ");
  1569. }
  1570. else
  1571. {
  1572. // Write Binary data.
  1573. lRes = Write32(pu32Data, lNum);
  1574. }
  1575. return lRes;
  1576. }
  1577. //////////////////////////////////////////////////////////////////////////////
  1578. //
  1579. // Writes lNum S32 values to currently open file.
  1580. // Returns number of S32 values successfully written.
  1581. //
  1582. //////////////////////////////////////////////////////////////////////////////
  1583. long RFile::Write(const S32* ps32Data, long lNum /*= 1L*/)
  1584. {
  1585. long lRes = 0; // Assume success.
  1586. if ((m_flags & Ascii) != 0)
  1587. {
  1588. // Write ASCII data.
  1589. lRes = WriteASCII(ps32Data, lNum, m_fs, "%ld ");
  1590. }
  1591. else
  1592. {
  1593. // Write Binary data.
  1594. lRes = Write32((U32*)ps32Data, lNum);
  1595. }
  1596. return lRes;
  1597. }
  1598. //////////////////////////////////////////////////////////////////////////////
  1599. //
  1600. // Writes lNum float values to the currently open file.
  1601. // Returns number of float values successfully written.
  1602. //
  1603. //////////////////////////////////////////////////////////////////////////////
  1604. long RFile::Write(const float* pfData, long lNum /*= 1L*/)
  1605. {
  1606. long lRes = 0; // Assume success.
  1607. if ((m_flags & Ascii) != 0)
  1608. {
  1609. // Write ASCII data.
  1610. lRes = WriteASCII(pfData, lNum, m_fs, "%e ");
  1611. }
  1612. else
  1613. {
  1614. // Write Binary data.
  1615. lRes = Write32((U32*)pfData, lNum);
  1616. }
  1617. return lRes;
  1618. }
  1619. //////////////////////////////////////////////////////////////////////////////
  1620. //
  1621. // Writes lNum double values to the currently open file.
  1622. // Returns number of double values successfully written.
  1623. //
  1624. //////////////////////////////////////////////////////////////////////////////
  1625. long RFile::Write(const double* pdData, long lNum /*= 1L*/)
  1626. {
  1627. long lRes = 0; // Assume success.
  1628. if ((m_flags & Ascii) != 0)
  1629. {
  1630. // Write ASCII data.
  1631. lRes = WriteASCII(pdData, lNum, m_fs, "%le ");
  1632. }
  1633. else
  1634. {
  1635. // Write Binary data.
  1636. lRes = Write64((U64*)pdData, lNum);
  1637. }
  1638. return lRes;
  1639. }
  1640. //////////////////////////////////////////////////////////////////////////////
  1641. //
  1642. // Writes lNum U64 values to currently open file.
  1643. // Returns number of U64 values successfully written.
  1644. //
  1645. //////////////////////////////////////////////////////////////////////////////
  1646. long RFile::Write(const U64* pu64Data, long lNum /*= 1L*/)
  1647. {
  1648. long lRes = 0; // Assume success.
  1649. if ((m_flags & Ascii) != 0)
  1650. {
  1651. ASSERT(0 && "**Cannot write 64 bit types other than double in ASCII**");
  1652. }
  1653. else
  1654. {
  1655. // Write Binary data.
  1656. lRes = Write64(pu64Data, lNum);
  1657. }
  1658. return lRes;
  1659. }
  1660. //////////////////////////////////////////////////////////////////////////////
  1661. //
  1662. // Writes lNum S64 values to currently open file.
  1663. // Returns number of S64 values successfully written.
  1664. //
  1665. //////////////////////////////////////////////////////////////////////////////
  1666. long RFile::Write(const S64* ps64Data, long lNum /*= 1L*/)
  1667. {
  1668. long lRes = 0; // Assume success.
  1669. if ((m_flags & Ascii) != 0)
  1670. {
  1671. ASSERT(0 && "**Cannot write 64 bit types other than double in ASCII**");
  1672. }
  1673. else
  1674. {
  1675. // Write Binary data.
  1676. lRes = Write64((U64*)ps64Data, lNum);
  1677. }
  1678. return lRes;
  1679. }
  1680. //////////////////////////////////////////////////////////////////////////////
  1681. // NOTE that this function has very different functionality in each of the
  1682. // two Open() modes (Ascii & Binary).
  1683. //////////////////////////////////////////////////////////////////////////////
  1684. //
  1685. // ASCII MODE:
  1686. // Writes a NULL terminated string. The string is surrounded by quotes in
  1687. // the file. Quotes in your string will be converted to \".
  1688. // Returns number of characters from your string successfully written (which
  1689. // is less than the total amount written) (this value should be the same
  1690. // as that returned from strlen() on success).
  1691. //
  1692. // BINARY MODE:
  1693. // Writes a NULL terminated string.
  1694. // Returns number of characters successfully written,
  1695. // including the NULL terminator (UNlike strlen()).
  1696. //
  1697. //////////////////////////////////////////////////////////////////////////////
  1698. long RFile::Write(const char* pszString)
  1699. {
  1700. long lRes = 0; // Assume nothing.
  1701. if (m_flags & Binary)
  1702. {
  1703. lRes = Write(pszString, strlen(pszString) + 1);
  1704. }
  1705. else
  1706. {
  1707. // Write leading quotes.
  1708. if (Write("\"", 1) == 1)
  1709. {
  1710. while (*pszString != '\0')
  1711. {
  1712. switch (*pszString)
  1713. {
  1714. case '"':
  1715. if (Write("\\", 1) == 1)
  1716. {
  1717. }
  1718. break;
  1719. }
  1720. if (Write(pszString, 1) == 1)
  1721. {
  1722. lRes++;
  1723. }
  1724. else
  1725. {
  1726. break;
  1727. }
  1728. }
  1729. // Write trailing quotes.
  1730. Write("\"", 1);
  1731. }
  1732. }
  1733. return lRes;
  1734. }
  1735. //////////////////////////////////////////////////////////////////////////////
  1736. //
  1737. // Seeks within the file based on the supplied position argument
  1738. // { SEEK_SET, SEEK_CUR, SEEK_END }.
  1739. // Returns 0 on success.
  1740. //
  1741. //////////////////////////////////////////////////////////////////////////////
  1742. short RFile::Seek(long lPos, long lOrigin)
  1743. {
  1744. short sRes = 0; // Assume success.
  1745. if (IsFile() == TRUE)
  1746. {
  1747. KEEPCONNECTEDANDUPDATELASTACCESS;
  1748. sRes = fseek(m_fs, lPos, lOrigin);
  1749. }
  1750. else
  1751. {
  1752. if (IsMemory() == TRUE)
  1753. {
  1754. switch (lOrigin)
  1755. {
  1756. case SEEK_SET:
  1757. // If w/i range . . .
  1758. if (lPos <= m_lSize && lPos >= 0)
  1759. {
  1760. m_pucCur = m_pucFile + lPos;
  1761. }
  1762. else
  1763. {
  1764. m_sMemError = 1;
  1765. TRACE("Seek(): Attempt to seek passed end or before beginning of mem file.\n");
  1766. sRes = -3;
  1767. }
  1768. break;
  1769. case SEEK_CUR:
  1770. // If w/i range . . .
  1771. if (m_pucCur + lPos <= m_pucFile + m_lSize && m_pucCur + lPos >= m_pucFile)
  1772. {
  1773. m_pucCur += lPos;
  1774. }
  1775. else
  1776. {
  1777. m_sMemError = 1;
  1778. TRACE("Seek(): Attempt to seek passed end or before beginning of mem file.\n");
  1779. sRes = -3;
  1780. }
  1781. break;
  1782. case SEEK_END:
  1783. if (lPos <= 0 && -lPos <= m_lSize)
  1784. {
  1785. m_pucCur = m_pucFile + m_lSize + lPos;
  1786. }
  1787. else
  1788. {
  1789. m_sMemError = 1;
  1790. TRACE("Seek(): Attempt to seek passed end or before beginning of mem file.\n");
  1791. sRes = -3;
  1792. }
  1793. break;
  1794. default:
  1795. TRACE("Seek(): Invalid origin flag provided.\n");
  1796. sRes = -2;
  1797. break;
  1798. }
  1799. }
  1800. else
  1801. {
  1802. TRACE("Seek(): File not open.\n");
  1803. sRes = -1;
  1804. }
  1805. }
  1806. return sRes;
  1807. }
  1808. //////////////////////////////////////////////////////////////////////////////
  1809. //
  1810. // Returns the current file position or -1 on error.
  1811. // Returns 0 on success.
  1812. //
  1813. //////////////////////////////////////////////////////////////////////////////
  1814. long RFile::Tell(void)
  1815. {
  1816. long lRes = -1L; // Assume error.
  1817. if (IsFile() == TRUE)
  1818. {
  1819. KEEPCONNECTEDANDUPDATELASTACCESS;
  1820. lRes = ftell(m_fs);
  1821. }
  1822. else
  1823. {
  1824. if (IsMemory() == TRUE)
  1825. {
  1826. lRes = m_pucCur - m_pucFile;
  1827. }
  1828. else
  1829. {
  1830. TRACE("Tell(): File not open.\n");
  1831. }
  1832. }
  1833. return lRes;
  1834. }
  1835. //////////////////////////////////////////////////////////////////////////////
  1836. //
  1837. // Returns the size of the file on success. Negative on error.
  1838. //
  1839. //////////////////////////////////////////////////////////////////////////////
  1840. long RFile::GetSize(void)
  1841. {
  1842. long lRes;
  1843. // Remember the current position.
  1844. long lPos = Tell();
  1845. if (lPos >= 0L)
  1846. {
  1847. // Attempt to seek to the end . . .
  1848. if (Seek(0L, SEEK_END) == 0)
  1849. {
  1850. // Current pos is size of file.
  1851. lRes = Tell();
  1852. // Seek back.
  1853. if (Seek(lPos, SEEK_SET) == 0)
  1854. {
  1855. // Success.
  1856. }
  1857. else
  1858. {
  1859. TRACE("GetSize(): Unable to seek back to start point.\n");
  1860. lRes = -3;
  1861. }
  1862. }
  1863. else
  1864. {
  1865. TRACE("GetSize(): Unable to seek to end of file.\n");
  1866. lRes = -2;
  1867. }
  1868. }
  1869. else
  1870. {
  1871. TRACE("GetSize(): Unable to tell position of file.\n");
  1872. lRes = -1;
  1873. }
  1874. return lRes;
  1875. }
  1876. //////////////////////////////////////////////////////////////////////////////
  1877. // Internal Methods.
  1878. //////////////////////////////////////////////////////////////////////////////
  1879. //////////////////////////////////////////////////////////////////////////////
  1880. //
  1881. // Reads in 8 bit data, swapped if necessary (BWAH HA).
  1882. //
  1883. //////////////////////////////////////////////////////////////////////////////
  1884. long RFile::Read8( // Returns number of 8 bit items read.
  1885. U8* pu8, // In: 8 bit data to read (swapping, if necessary).
  1886. long lNum) // In: Number of 8 bit items to read.
  1887. {
  1888. long lRes = 0;
  1889. // Read data.
  1890. lRes = Read((void*)pu8, lNum);
  1891. return lRes;
  1892. }
  1893. //////////////////////////////////////////////////////////////////////////////
  1894. //
  1895. // Reads in 16 bit data, swapped if necessary.
  1896. //
  1897. //////////////////////////////////////////////////////////////////////////////
  1898. long RFile::Read16( // Returns number of 16 bit items read.
  1899. U16* pu16, // In: 16 bit data to read (swapping, if necessary).
  1900. long lNum) // In: Number of 16 bit items to read.
  1901. {
  1902. long lRes = 0;
  1903. // Read data.
  1904. lRes = Read((void*)pu16, lNum * sizeof(U16)) / sizeof(U16);
  1905. // If m_endian is consistent with this platform . . .
  1906. if (ENDIAN_CONSISTENT)
  1907. {
  1908. // Done.
  1909. }
  1910. else
  1911. {
  1912. // Convert.
  1913. U8 u8_0;
  1914. U8* pu8 = (U8*)pu16;
  1915. for (long l = 0L; l < lRes; l++, pu8 += sizeof(U16))
  1916. {
  1917. // Store end.
  1918. u8_0 = *(pu8 + 1);
  1919. // Put beginning in end.
  1920. *(pu8 + 1) = *(pu8 + 0);
  1921. // Put end in beginning.
  1922. *(pu8 + 0) = u8_0;
  1923. }
  1924. }
  1925. return lRes;
  1926. }
  1927. //////////////////////////////////////////////////////////////////////////////
  1928. //
  1929. // Reads in 24 bit data, swapped if necessary.
  1930. //
  1931. //////////////////////////////////////////////////////////////////////////////
  1932. long RFile::Read24( // Returns number of 24 bit items read.
  1933. RPixel24* ppix24, // In: 24 bit data to read (swapping, if necessary).
  1934. long lNum) // In: Number of 24 bit items to read.
  1935. {
  1936. long lRes = 0;
  1937. // Read data.
  1938. lRes = Read((void*)ppix24, lNum * sizeof(RPixel24)) / sizeof(RPixel24);
  1939. // If m_endian is consistent with this platform . . .
  1940. if (ENDIAN_CONSISTENT)
  1941. {
  1942. // Done.
  1943. }
  1944. else
  1945. {
  1946. // Convert.
  1947. U8 u8_0;
  1948. U8* pu8 = (U8*)ppix24;
  1949. for (long l = 0L; l < lRes; l++, pu8 += sizeof(RPixel24))
  1950. {
  1951. // Store end.
  1952. u8_0 = *(pu8 + 2);
  1953. // Put beginning in end.
  1954. *(pu8 + 2) = *(pu8 + 0);
  1955. // Put end in beginning.
  1956. *(pu8 + 0) = u8_0;
  1957. // Middle does not need to be swapped in odd sized types.
  1958. }
  1959. }
  1960. return lRes;
  1961. }
  1962. //////////////////////////////////////////////////////////////////////////////
  1963. //
  1964. // Reads in 32 bit data, swapped if necessary.
  1965. //
  1966. //////////////////////////////////////////////////////////////////////////////
  1967. long RFile::Read32( // Returns number of 32 bit items read.
  1968. U32* pu32, // In: 32 bit data to read (swapping, if necessary).
  1969. long lNum) // In: Number of 32 bit items to read.
  1970. {
  1971. long lRes = 0;
  1972. // Read data.
  1973. lRes = Read((void*)pu32, lNum * sizeof(U32)) / sizeof(U32);
  1974. // If m_endian is consistent with this platform . . .
  1975. if (ENDIAN_CONSISTENT)
  1976. {
  1977. // Done.
  1978. }
  1979. else
  1980. {
  1981. // Convert.
  1982. U8 u8_0;
  1983. U8 u8_1;
  1984. U8* pu8 = (U8*)pu32;
  1985. for (long l = 0L; l < lRes; l++, pu8 += sizeof(U32))
  1986. {
  1987. // Store end.
  1988. u8_0 = *(pu8 + 3);
  1989. // Store second to end.
  1990. u8_1 = *(pu8 + 2);
  1991. // Put beginning into end.
  1992. *(pu8 + 3) = *(pu8 + 0);
  1993. // Put second to beginning into second to end.
  1994. *(pu8 + 2) = *(pu8 + 1);
  1995. // Put end in beginning.
  1996. *(pu8 + 0) = u8_0;
  1997. // Put second to end into second to beginning.
  1998. *(pu8 + 1) = u8_1;
  1999. }
  2000. }
  2001. return lRes;
  2002. }
  2003. //////////////////////////////////////////////////////////////////////////////
  2004. //
  2005. // Reads in 64 bit data, swapped if necessary.
  2006. //
  2007. //////////////////////////////////////////////////////////////////////////////
  2008. long RFile::Read64( // Returns number of 64 bit items read.
  2009. U64* pu64, // In: 64 bit data to read (swapping, if necessary).
  2010. long lNum) // In: Number of 64 bit items to read.
  2011. {
  2012. long lRes = 0;
  2013. // Read data.
  2014. lRes = Read((void*)pu64, lNum * sizeof(U64)) / sizeof(U64);
  2015. // If m_endian is consistent with this platform . . .
  2016. if (ENDIAN_CONSISTENT)
  2017. {
  2018. // Done.
  2019. }
  2020. else
  2021. {
  2022. // Convert.
  2023. U8 u8Tmp;
  2024. U8* pu8 = (U8*)pu64;
  2025. for (long l = 0L; l < lRes; l++, pu8 += sizeof(U64))
  2026. {
  2027. u8Tmp = *(pu8 + 0);
  2028. *(pu8 + 0) = *(pu8 + 7);
  2029. *(pu8 + 7) = u8Tmp;
  2030. u8Tmp = *(pu8 + 1);
  2031. *(pu8 + 1) = *(pu8 + 6);
  2032. *(pu8 + 6) = u8Tmp;
  2033. u8Tmp = *(pu8 + 2);
  2034. *(pu8 + 2) = *(pu8 + 5);
  2035. *(pu8 + 5) = u8Tmp;
  2036. u8Tmp = *(pu8 + 3);
  2037. *(pu8 + 3) = *(pu8 + 4);
  2038. *(pu8 + 4) = u8Tmp;
  2039. }
  2040. }
  2041. return lRes;
  2042. }
  2043. //////////////////////////////////////////////////////////////////////////////
  2044. //
  2045. // Writes out 8 bit data, swapped if necessary (BWAH HA).
  2046. //
  2047. //////////////////////////////////////////////////////////////////////////////
  2048. long RFile::Write8( // Returns number of 8 bit items written.
  2049. const U8* pu8, // In: 8 bit data to write (swapping, if necessary).
  2050. long lNum) // In: Number of 8 bit items to write.
  2051. {
  2052. long lRes = 0;
  2053. lRes = Write((void*)pu8, lNum);
  2054. return lRes;
  2055. }
  2056. //////////////////////////////////////////////////////////////////////////////
  2057. //
  2058. // Writes out 16 bit data, swapped if necessary.
  2059. //
  2060. //////////////////////////////////////////////////////////////////////////////
  2061. long RFile::Write16( // Returns number of 16 bit items written.
  2062. const U16* pu16, // In: 16 bit data to write (swapping, if necessary).
  2063. long lNum) // In: Number of 16 bit items to write.
  2064. {
  2065. long lRes = 0;
  2066. // If m_endian is consistent with this platform . . .
  2067. if (ENDIAN_CONSISTENT)
  2068. {
  2069. // Write data.
  2070. lRes = Write((void*)pu16, lNum * sizeof(U16)) / sizeof(U16);
  2071. }
  2072. else
  2073. {
  2074. // Convert.
  2075. // Note that this could be sped up a bit more by using a variable to store
  2076. // the current lSrc position before entering the inner loop and then subtract
  2077. // that from lSrc after exiting the inner loop to determine the amount of
  2078. // items to write.
  2079. // No template is used b/c it would be necessary to loop on sizeof(U32)
  2080. // which would negate much of the effort of this algorithm.
  2081. U8* pu8Src = (U8*)pu16; // Source data.
  2082. U8* pu8Dst; // Temp destination to be written after
  2083. // swapped to.
  2084. long lSrc; // Number of source items swapped/written.
  2085. long lDst; // Number of items swapped on current
  2086. // iteration.
  2087. long lWritten = 0; // Number of items written on current
  2088. // iteration.
  2089. for (lSrc = 0L; lSrc < lNum && lWritten >= 0L; lRes += lWritten / sizeof(U16))
  2090. {
  2091. pu8Dst = ms_au8SwapBuf;
  2092. for (
  2093. lDst = 0L;
  2094. lSrc < lNum && lDst < sizeof(ms_au8SwapBuf);
  2095. lDst += sizeof(U16), lSrc++, pu8Src += sizeof(U16), pu8Dst += sizeof(U16) )
  2096. {
  2097. *(pu8Dst + 1) = *(pu8Src + 0);
  2098. *(pu8Dst + 0) = *(pu8Src + 1);
  2099. }
  2100. // Write data subportion.
  2101. lWritten = Write((void*)ms_au8SwapBuf, lDst);
  2102. }
  2103. }
  2104. return lRes;
  2105. }
  2106. //////////////////////////////////////////////////////////////////////////////
  2107. //
  2108. // Writes out 24 bit data, swapped if necessary.
  2109. //
  2110. //////////////////////////////////////////////////////////////////////////////
  2111. long RFile::Write24( // Returns number of 24 bit items written.
  2112. const RPixel24* ppix24, // In: 24 bit data to write (swapping, if necessary).
  2113. long lNum) // In: Number of 24 bit items to write.
  2114. {
  2115. long lRes = 0;
  2116. // If m_endian is consistent with this platform . . .
  2117. if (ENDIAN_CONSISTENT)
  2118. {
  2119. // Write data.
  2120. lRes = Write((void*)ppix24, lNum * sizeof(RPixel24)) / sizeof(RPixel24);
  2121. }
  2122. else
  2123. {
  2124. // Convert.
  2125. // Note that this could be sped up a bit more by using a variable to store
  2126. // the current lSrc position before entering the inner loop and then subtract
  2127. // that from lSrc after exiting the inner loop to determine the amount of
  2128. // items to write.
  2129. // No template is used b/c it would be necessary to loop on sizeof(U32)
  2130. // which would negate much of the effort of this algorithm.
  2131. U8* pu8Src = (U8*)ppix24; // Source data.
  2132. U8* pu8Dst; // Temp destination to be written after
  2133. // swapped to.
  2134. long lSrc; // Number of source items swapped/written.
  2135. long lDst; // Number of items swapped on current
  2136. // iteration.
  2137. long lWritten = 0; // Number of items written on current
  2138. // iteration.
  2139. for (lSrc = 0L; lSrc < lNum && lWritten >= 0L; lRes += lWritten / sizeof(RPixel24))
  2140. {
  2141. pu8Dst = ms_au8SwapBuf;
  2142. for (
  2143. lDst = 0L;
  2144. lSrc < lNum && lDst < sizeof(ms_au8SwapBuf);
  2145. lDst += sizeof(RPixel24), lSrc++, pu8Src += sizeof(RPixel24), pu8Dst += sizeof(RPixel24) )
  2146. {
  2147. *(pu8Dst + 2) = *(pu8Src + 0);
  2148. *(pu8Dst + 1) = *(pu8Src + 1);
  2149. *(pu8Dst + 0) = *(pu8Src + 2);
  2150. }
  2151. // Write data subportion.
  2152. lWritten = Write((void*)ms_au8SwapBuf, lDst);
  2153. }
  2154. }
  2155. return lRes;
  2156. }
  2157. //////////////////////////////////////////////////////////////////////////////
  2158. //
  2159. // Writes out 32 bit data, swapped if necessary.
  2160. //
  2161. //////////////////////////////////////////////////////////////////////////////
  2162. long RFile::Write32( // Returns number of 32 bit items written.
  2163. const U32* pu32, // In: 32 bit data to write (swapping, if necessary).
  2164. long lNum) // In: Number of 32 bit items to write.
  2165. {
  2166. long lRes = 0;
  2167. // If m_endian is consistent with this platform . . .
  2168. if (ENDIAN_CONSISTENT)
  2169. {
  2170. // Write data.
  2171. lRes = Write((void*)pu32, lNum * sizeof(U32)) / sizeof(U32);
  2172. }
  2173. else
  2174. {
  2175. // Convert.
  2176. // Note that this could be sped up a bit more by using a variable to store
  2177. // the current lSrc position before entering the inner loop and then subtract
  2178. // that from lSrc after exiting the inner loop to determine the amount of
  2179. // items to write.
  2180. // No template is used b/c it would be necessary to loop on sizeof(U32)
  2181. // which would negate much of the effort of this algorithm.
  2182. U8* pu8Src = (U8*)pu32; // Source data.
  2183. U8* pu8Dst; // Temp destination to be written after
  2184. // swapped to.
  2185. long lSrc; // Number of source items swapped/written.
  2186. long lDst; // Number of items swapped on current
  2187. // iteration.
  2188. long lWritten = 0; // Number of items written on current
  2189. // iteration.
  2190. for (lSrc = 0L; lSrc < lNum && lWritten >= 0L; lRes += lWritten / sizeof(U32))
  2191. {
  2192. pu8Dst = ms_au8SwapBuf;
  2193. for (
  2194. lDst = 0L;
  2195. lSrc < lNum && lDst < sizeof(ms_au8SwapBuf);
  2196. lDst += sizeof(U32), lSrc++, pu8Src += sizeof(U32), pu8Dst += sizeof(U32) )
  2197. {
  2198. *(pu8Dst + 3) = *(pu8Src + 0);
  2199. *(pu8Dst + 2) = *(pu8Src + 1);
  2200. *(pu8Dst + 1) = *(pu8Src + 2);
  2201. *(pu8Dst + 0) = *(pu8Src + 3);
  2202. }
  2203. // Write data subportion.
  2204. lWritten = Write((void*)ms_au8SwapBuf, lDst);
  2205. }
  2206. }
  2207. return lRes;
  2208. }
  2209. //////////////////////////////////////////////////////////////////////////////
  2210. //
  2211. // Writes out 64 bit data, swapped if necessary.
  2212. //
  2213. //////////////////////////////////////////////////////////////////////////////
  2214. long RFile::Write64( // Returns number of 64 bit items written.
  2215. const U64* pu64, // In: 64 bit data to write (swapping, if necessary).
  2216. long lNum) // In: Number of 64 bit items to write.
  2217. {
  2218. long lRes = 0;
  2219. // If m_endian is consistent with this platform . . .
  2220. if (ENDIAN_CONSISTENT)
  2221. {
  2222. // Done.
  2223. lRes = Write((void*)pu64, lNum * sizeof(U64)) / sizeof(U64);
  2224. }
  2225. else
  2226. {
  2227. // Note that this could be sped up a bit more by using a variable to store
  2228. // the current lSrc position before entering the inner loop and then subtract
  2229. // that from lSrc after exiting the inner loop to determine the amount of
  2230. // items to write.
  2231. // No template is used b/c it would be necessary to loop on sizeof(U32)
  2232. // which would negate much of the effort of this algorithm.
  2233. U8* pu8Src = (U8*)pu64; // Source data.
  2234. U8* pu8Dst; // Temp destination to be written after
  2235. // swapped to.
  2236. long lSrc; // Number of source items swapped/written.
  2237. long lDst; // Number of items swapped on current
  2238. // iteration.
  2239. long lWritten = 0; // Number of items written on current
  2240. // iteration.
  2241. for (lSrc = 0L; lSrc < lNum && lWritten >= 0L; lRes += lWritten / sizeof(U64))
  2242. {
  2243. pu8Dst = ms_au8SwapBuf;
  2244. for (
  2245. lDst = 0L;
  2246. lSrc < lNum && lDst < sizeof(ms_au8SwapBuf);
  2247. lDst += sizeof(U64), lSrc++, pu8Src += sizeof(U64), pu8Dst += sizeof(U64) )
  2248. {
  2249. *(pu8Dst + 0) = *(pu8Src + 7);
  2250. *(pu8Dst + 1) = *(pu8Src + 6);
  2251. *(pu8Dst + 2) = *(pu8Src + 5);
  2252. *(pu8Dst + 3) = *(pu8Src + 4);
  2253. *(pu8Dst + 4) = *(pu8Src + 3);
  2254. *(pu8Dst + 5) = *(pu8Src + 2);
  2255. *(pu8Dst + 6) = *(pu8Src + 1);
  2256. *(pu8Dst + 7) = *(pu8Src + 0);
  2257. }
  2258. // Write data subportion.
  2259. lWritten = Write((void*)ms_au8SwapBuf, lDst);
  2260. }
  2261. }
  2262. return lRes;
  2263. }
  2264. #ifdef ALLOW_RFILE_REOPEN
  2265. //////////////////////////////////////////////////////////////////////////////
  2266. //
  2267. // Disconnects a disk file from the disk temporarily so that another
  2268. // can use the FILE* that is made available. Returns 0 on success.
  2269. //
  2270. //////////////////////////////////////////////////////////////////////////////
  2271. short RFile::Disconnect(void)
  2272. {
  2273. short sRes = 0; // Assume success.
  2274. // Must be disk file and must be connected.
  2275. ASSERT(IsFile() == TRUE);
  2276. ASSERT(m_sDisconnected == FALSE);
  2277. // Close this file. BUT DON'T CLEAR m_fs!!!! Needed for calls to IsFile(),
  2278. // etc.
  2279. if (fclose(m_fs) == 0)
  2280. {
  2281. // Successfully disconnected.
  2282. }
  2283. else
  2284. {
  2285. TRACE("Disconnect(): Unable to close file for disconnection from "
  2286. "disk.\n");
  2287. sRes = -2;
  2288. }
  2289. return sRes;
  2290. }
  2291. //////////////////////////////////////////////////////////////////////////////
  2292. //
  2293. // Reconnects a disk file that has been previously disconnected.
  2294. // Does nothing if connected (i.e., if m_sDisconnected == FALSE).
  2295. // Returns 0 on success.
  2296. //
  2297. //////////////////////////////////////////////////////////////////////////////
  2298. short RFile::Reconnect(void)
  2299. {
  2300. short sRes = 0; // Assume success.
  2301. // Must be disk file.
  2302. ASSERT(IsFile() == TRUE);
  2303. // If disconnected . . .
  2304. if (m_sDisconnected == TRUE)
  2305. {
  2306. // Re open.
  2307. m_fs = fopen(m_szFileName, m_szFlags);
  2308. if (m_fs != NULL)
  2309. {
  2310. // Reconnected. Add to open list.
  2311. if (ms_listOpen.Add(this) == 0)
  2312. {
  2313. // Success.
  2314. }
  2315. else
  2316. {
  2317. TRACE("Reconnect(): Unable to add file to open files list!\n");
  2318. sRes = -2;
  2319. }
  2320. }
  2321. else
  2322. {
  2323. TRACE("Reconnect(): UNABLE TO RECONNECT FILE! A FILE MAY HAVE BEEN "
  2324. "LOCKED BY ANOTHER USER/APP DURING DURATION OF DISCONNECT!\n");
  2325. sRes = -1;
  2326. }
  2327. }
  2328. return sRes;
  2329. }
  2330. //////////////////////////////////////////////////////////////////////////////
  2331. //
  2332. // Disconnect the RFile attached to disk file that was accessed
  2333. // longest ago.
  2334. // Returns 0 on success.
  2335. // (static)
  2336. //
  2337. //////////////////////////////////////////////////////////////////////////////
  2338. short RFile::MakeStreamAvailable(void)
  2339. {
  2340. short sRes = 0; // Assume success.
  2341. // Find the open RFile attached to disk that was accessed longest ago.
  2342. // All RFiles in the ms_listOpen list are attached to disk files.
  2343. RFile* pfileOld = ms_listOpen.GetHead();
  2344. RFile* pfile = ms_listOpen.GetNext();
  2345. while (pfile != NULL)
  2346. {
  2347. // If current was accessed longer ago than pfileOld . . .
  2348. if (pfile->m_lLastAccess < pfileOld->m_lLastAccess)
  2349. {
  2350. pfileOld = pfile;
  2351. }
  2352. pfile = ms_listOpen.GetNext();
  2353. }
  2354. // If we got one . . .
  2355. if (pfileOld != NULL)
  2356. {
  2357. // Attempt to temporarily disconnect . . .
  2358. if (pfileOld->Disconnect() == 0)
  2359. {
  2360. // Remove RFile from list.
  2361. if (ms_listOpen.Remove(pfileOld) == 0)
  2362. {
  2363. }
  2364. else
  2365. {
  2366. TRACE("MakeStreamAvailable(): Unable to remove RFile from list! "
  2367. "This could cause major problems!\n");
  2368. // No error return value since we actually made on available.
  2369. }
  2370. }
  2371. else
  2372. {
  2373. TRACE("MakeStreamAvailable(): Unable to disconnect RFile.\n");
  2374. sRes = -2;
  2375. }
  2376. }
  2377. else
  2378. {
  2379. TRACE("MakeStreamAvailable(): No open RFiles attached to disk.\n");
  2380. sRes = -1;
  2381. }
  2382. return sRes;
  2383. }
  2384. #endif // ALLOW_RFILE_REOPEN
  2385. //////////////////////////////////////////////////////////////////////////////
  2386. // Querries.
  2387. //////////////////////////////////////////////////////////////////////////////
  2388. //////////////////////////////////////////////////////////////////////////////
  2389. // EOF
  2390. //////////////////////////////////////////////////////////////////////////////