File.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. //
  2. // Copyright (c) 2008-2014 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "Precompiled.h"
  23. #include "File.h"
  24. #include "FileSystem.h"
  25. #include "Log.h"
  26. #include "MemoryBuffer.h"
  27. #include "PackageFile.h"
  28. #include "Profiler.h"
  29. #include <cstdio>
  30. #include <lz4.h>
  31. #include "DebugNew.h"
  32. namespace Urho3D
  33. {
  34. #ifdef WIN32
  35. static const wchar_t* openMode[] =
  36. {
  37. L"rb",
  38. L"wb",
  39. L"r+b",
  40. L"w+b"
  41. };
  42. #else
  43. static const char* openMode[] =
  44. {
  45. "rb",
  46. "wb",
  47. "r+b",
  48. "w+b"
  49. };
  50. #endif
  51. static const unsigned READ_BUFFER_SIZE = 32768;
  52. static const unsigned SKIP_BUFFER_SIZE = 1024;
  53. File::File(Context* context) :
  54. Object(context),
  55. mode_(FILE_READ),
  56. handle_(0),
  57. #ifdef ANDROID
  58. assetHandle_(0),
  59. #endif
  60. readBufferOffset_(0),
  61. readBufferSize_(0),
  62. offset_(0),
  63. checksum_(0),
  64. compressed_(false),
  65. readSyncNeeded_(false),
  66. writeSyncNeeded_(false)
  67. {
  68. }
  69. File::File(Context* context, const String& fileName, FileMode mode) :
  70. Object(context),
  71. mode_(FILE_READ),
  72. handle_(0),
  73. #ifdef ANDROID
  74. assetHandle_(0),
  75. #endif
  76. readBufferOffset_(0),
  77. readBufferSize_(0),
  78. offset_(0),
  79. checksum_(0),
  80. compressed_(false),
  81. readSyncNeeded_(false),
  82. writeSyncNeeded_(false)
  83. {
  84. Open(fileName, mode);
  85. }
  86. File::File(Context* context, PackageFile* package, const String& fileName) :
  87. Object(context),
  88. mode_(FILE_READ),
  89. handle_(0),
  90. #ifdef ANDROID
  91. assetHandle_(0),
  92. #endif
  93. readBufferOffset_(0),
  94. readBufferSize_(0),
  95. offset_(0),
  96. checksum_(0),
  97. compressed_(false),
  98. readSyncNeeded_(false),
  99. writeSyncNeeded_(false)
  100. {
  101. Open(package, fileName);
  102. }
  103. File::~File()
  104. {
  105. Close();
  106. }
  107. bool File::Open(const String& fileName, FileMode mode)
  108. {
  109. Close();
  110. FileSystem* fileSystem = GetSubsystem<FileSystem>();
  111. if (fileSystem && !fileSystem->CheckAccess(GetPath(fileName)))
  112. {
  113. LOGERROR("Access denied to " + fileName);
  114. return false;
  115. }
  116. #ifdef ANDROID
  117. if (fileName.StartsWith("/apk/"))
  118. {
  119. if (mode != FILE_READ)
  120. {
  121. LOGERROR("Only read mode is supported for asset files");
  122. return false;
  123. }
  124. assetHandle_ = SDL_RWFromFile(fileName.Substring(5).CString(), "rb");
  125. if (!assetHandle_)
  126. {
  127. LOGERROR("Could not open asset file " + fileName);
  128. return false;
  129. }
  130. else
  131. {
  132. fileName_ = fileName;
  133. mode_ = mode;
  134. position_ = 0;
  135. offset_ = 0;
  136. checksum_ = 0;
  137. size_ = assetHandle_->hidden.androidio.size;
  138. readBuffer_ = new unsigned char[READ_BUFFER_SIZE];
  139. readBufferOffset_ = 0;
  140. readBufferSize_ = 0;
  141. return true;
  142. }
  143. }
  144. #endif
  145. if (fileName.Empty())
  146. {
  147. LOGERROR("Could not open file with empty name");
  148. return false;
  149. }
  150. #ifdef WIN32
  151. handle_ = _wfopen(GetWideNativePath(fileName).CString(), openMode[mode]);
  152. #else
  153. handle_ = fopen(GetNativePath(fileName).CString(), openMode[mode]);
  154. #endif
  155. // If file did not exist in readwrite mode, retry with write-update mode
  156. if (mode == FILE_READWRITE && !handle_)
  157. {
  158. #ifdef WIN32
  159. handle_ = _wfopen(GetWideNativePath(fileName).CString(), openMode[mode + 1]);
  160. #else
  161. handle_ = fopen(GetNativePath(fileName).CString(), openMode[mode + 1]);
  162. #endif
  163. }
  164. if (!handle_)
  165. {
  166. LOGERROR("Could not open file " + fileName);
  167. return false;
  168. }
  169. fileName_ = fileName;
  170. mode_ = mode;
  171. position_ = 0;
  172. offset_ = 0;
  173. checksum_ = 0;
  174. compressed_ = false;
  175. readSyncNeeded_ = false;
  176. writeSyncNeeded_ = false;
  177. fseek((FILE*)handle_, 0, SEEK_END);
  178. size_ = ftell((FILE*)handle_);
  179. fseek((FILE*)handle_, 0, SEEK_SET);
  180. return true;
  181. }
  182. bool File::Open(PackageFile* package, const String& fileName)
  183. {
  184. Close();
  185. if (!package)
  186. return false;
  187. const PackageEntry* entry = package->GetEntry(fileName);
  188. if (!entry)
  189. return false;
  190. #ifdef WIN32
  191. handle_ = _wfopen(GetWideNativePath(package->GetName()).CString(), L"rb");
  192. #else
  193. handle_ = fopen(GetNativePath(package->GetName()).CString(), "rb");
  194. #endif
  195. if (!handle_)
  196. {
  197. LOGERROR("Could not open package file " + fileName);
  198. return false;
  199. }
  200. fileName_ = fileName;
  201. mode_ = FILE_READ;
  202. offset_ = entry->offset_;
  203. checksum_ = entry->checksum_;
  204. position_ = 0;
  205. size_ = entry->size_;
  206. compressed_ = package->IsCompressed();
  207. readSyncNeeded_ = false;
  208. writeSyncNeeded_ = false;
  209. fseek((FILE*)handle_, offset_, SEEK_SET);
  210. return true;
  211. }
  212. unsigned File::Read(void* dest, unsigned size)
  213. {
  214. #ifdef ANDROID
  215. if (!handle_ && !assetHandle_)
  216. #else
  217. if (!handle_)
  218. #endif
  219. {
  220. // Do not log the error further here to prevent spamming the stderr stream
  221. return 0;
  222. }
  223. if (mode_ == FILE_WRITE)
  224. {
  225. LOGERROR("File not opened for reading");
  226. return 0;
  227. }
  228. if (size + position_ > size_)
  229. size = size_ - position_;
  230. if (!size)
  231. return 0;
  232. #ifdef ANDROID
  233. if (assetHandle_)
  234. {
  235. unsigned sizeLeft = size;
  236. unsigned char* destPtr = (unsigned char*)dest;
  237. while (sizeLeft)
  238. {
  239. if (readBufferOffset_ >= readBufferSize_)
  240. {
  241. readBufferSize_ = Min((int)size_ - position_, (int)READ_BUFFER_SIZE);
  242. readBufferOffset_ = 0;
  243. SDL_RWread(assetHandle_, readBuffer_.Get(), readBufferSize_, 1);
  244. }
  245. unsigned copySize = Min((int)(readBufferSize_ - readBufferOffset_), (int)sizeLeft);
  246. memcpy(destPtr, readBuffer_.Get() + readBufferOffset_, copySize);
  247. destPtr += copySize;
  248. sizeLeft -= copySize;
  249. readBufferOffset_ += copySize;
  250. position_ += copySize;
  251. }
  252. return size;
  253. }
  254. #endif
  255. if (compressed_)
  256. {
  257. unsigned sizeLeft = size;
  258. unsigned char* destPtr = (unsigned char*)dest;
  259. while (sizeLeft)
  260. {
  261. if (!readBuffer_ || readBufferOffset_ >= readBufferSize_)
  262. {
  263. unsigned char blockHeaderBytes[4];
  264. fread(blockHeaderBytes, sizeof blockHeaderBytes, 1, (FILE*)handle_);
  265. MemoryBuffer blockHeader(&blockHeaderBytes[0], sizeof blockHeaderBytes);
  266. unsigned unpackedSize = blockHeader.ReadUShort();
  267. unsigned packedSize = blockHeader.ReadUShort();
  268. if (!readBuffer_)
  269. {
  270. readBuffer_ = new unsigned char[unpackedSize];
  271. inputBuffer_ = new unsigned char[LZ4_compressBound(unpackedSize)];
  272. }
  273. /// \todo Handle errors
  274. fread(inputBuffer_.Get(), packedSize, 1, (FILE*)handle_);
  275. LZ4_decompress_fast((const char*)inputBuffer_.Get(), (char *)readBuffer_.Get(), unpackedSize);
  276. readBufferSize_ = unpackedSize;
  277. readBufferOffset_ = 0;
  278. }
  279. unsigned copySize = Min((int)(readBufferSize_ - readBufferOffset_), (int)sizeLeft);
  280. memcpy(destPtr, readBuffer_.Get() + readBufferOffset_, copySize);
  281. destPtr += copySize;
  282. sizeLeft -= copySize;
  283. readBufferOffset_ += copySize;
  284. position_ += copySize;
  285. }
  286. return size;
  287. }
  288. // Need to reassign the position due to internal buffering when transitioning from writing to reading
  289. if (readSyncNeeded_)
  290. {
  291. fseek((FILE*)handle_, position_ + offset_, SEEK_SET);
  292. readSyncNeeded_ = false;
  293. }
  294. size_t ret = fread(dest, size, 1, (FILE*)handle_);
  295. if (ret != 1)
  296. {
  297. // Return to the position where the read began
  298. fseek((FILE*)handle_, position_ + offset_, SEEK_SET);
  299. LOGERROR("Error while reading from file " + GetName());
  300. return 0;
  301. }
  302. writeSyncNeeded_ = true;
  303. position_ += size;
  304. return size;
  305. }
  306. unsigned File::Seek(unsigned position)
  307. {
  308. #ifdef ANDROID
  309. if (!handle_ && !assetHandle_)
  310. #else
  311. if (!handle_)
  312. #endif
  313. {
  314. // Do not log the error further here to prevent spamming the stderr stream
  315. return 0;
  316. }
  317. // Allow sparse seeks if writing
  318. if (mode_ == FILE_READ && position > size_)
  319. position = size_;
  320. #ifdef ANDROID
  321. if (assetHandle_)
  322. {
  323. SDL_RWseek(assetHandle_, position, SEEK_SET);
  324. position_ = position;
  325. readBufferOffset_ = 0;
  326. readBufferSize_ = 0;
  327. return position_;
  328. }
  329. #endif
  330. if (compressed_)
  331. {
  332. // Start over from the beginning
  333. if (position == 0)
  334. {
  335. position_ = 0;
  336. readBufferOffset_ = 0;
  337. readBufferSize_ = 0;
  338. fseek((FILE*)handle_, offset_, SEEK_SET);
  339. }
  340. // Skip bytes
  341. else if (position >= position_)
  342. {
  343. unsigned char skipBuffer[SKIP_BUFFER_SIZE];
  344. while (position > position_)
  345. Read(skipBuffer, Min((int)position - position_, (int)SKIP_BUFFER_SIZE));
  346. }
  347. else
  348. LOGERROR("Seeking backward in a compressed file is not supported");
  349. return position_;
  350. }
  351. fseek((FILE*)handle_, position + offset_, SEEK_SET);
  352. position_ = position;
  353. readSyncNeeded_ = false;
  354. writeSyncNeeded_ = false;
  355. return position_;
  356. }
  357. unsigned File::Write(const void* data, unsigned size)
  358. {
  359. if (!handle_)
  360. {
  361. // Do not log the error further here to prevent spamming the stderr stream
  362. return 0;
  363. }
  364. if (mode_ == FILE_READ)
  365. {
  366. LOGERROR("File not opened for writing");
  367. return 0;
  368. }
  369. if (!size)
  370. return 0;
  371. // Need to reassign the position due to internal buffering when transitioning from reading to writing
  372. if (writeSyncNeeded_)
  373. {
  374. fseek((FILE*)handle_, position_ + offset_, SEEK_SET);
  375. writeSyncNeeded_ = false;
  376. }
  377. if (fwrite(data, size, 1, (FILE*)handle_) != 1)
  378. {
  379. // Return to the position where the write began
  380. fseek((FILE*)handle_, position_ + offset_, SEEK_SET);
  381. LOGERROR("Error while writing to file " + GetName());
  382. return 0;
  383. }
  384. readSyncNeeded_ = true;
  385. position_ += size;
  386. if (position_ > size_)
  387. size_ = position_;
  388. return size;
  389. }
  390. unsigned File::GetChecksum()
  391. {
  392. if (offset_ || checksum_)
  393. return checksum_;
  394. if (!handle_ || mode_ == FILE_WRITE)
  395. return 0;
  396. PROFILE(CalculateFileChecksum);
  397. unsigned oldPos = position_;
  398. checksum_ = 0;
  399. Seek(0);
  400. while (!IsEof())
  401. {
  402. unsigned char block[1024];
  403. unsigned readBytes = Read(block, 1024);
  404. for (unsigned i = 0; i < readBytes; ++i)
  405. checksum_ = SDBMHash(checksum_, block[i]);
  406. }
  407. Seek(oldPos);
  408. return checksum_;
  409. }
  410. void File::Close()
  411. {
  412. #ifdef ANDROID
  413. if (assetHandle_)
  414. {
  415. SDL_RWclose(assetHandle_);
  416. assetHandle_ = 0;
  417. }
  418. #endif
  419. readBuffer_.Reset();
  420. inputBuffer_.Reset();
  421. if (handle_)
  422. {
  423. fclose((FILE*)handle_);
  424. handle_ = 0;
  425. position_ = 0;
  426. size_ = 0;
  427. offset_ = 0;
  428. checksum_ = 0;
  429. }
  430. }
  431. void File::Flush()
  432. {
  433. if (handle_)
  434. fflush((FILE*)handle_);
  435. }
  436. void File::SetName(const String& name)
  437. {
  438. fileName_ = name;
  439. }
  440. bool File::IsOpen() const
  441. {
  442. #ifdef ANDROID
  443. return handle_ != 0 || assetHandle_ != 0;
  444. #else
  445. return handle_ != 0;
  446. #endif
  447. }
  448. }