File.cpp 13 KB

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