File.cpp 13 KB

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