2
0

FileService.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875
  1. #include "FileService.h"
  2. #include "DataFile.h"
  3. #include "File.h"
  4. #include "IniFile.h"
  5. #include "Finder.h"
  6. #include "LoadBuffer.h"
  7. #include "PackFile.h"
  8. #include "MirrorPath.h"
  9. CREATE_SERVICE(FileService, 0)
  10. FileService * FileService::object = null;
  11. FileService::FileService() : dataFiles(_FL_),
  12. iniFiles(_FL_),
  13. finders(_FL_),
  14. buffers(_FL_),
  15. packFiles(_FL_),
  16. mirrorPaths(_FL_)
  17. #ifndef _XBOX
  18. , rwFiles(_FL_)
  19. #endif
  20. {
  21. fullPath.Reserve(MAX_PATH*4);
  22. object = this;
  23. dcmprs = NEW XStreamDecompressor();
  24. isUseDcmprs = false;
  25. #ifndef _XBOX
  26. dword size = MAX_PATH + 4096;
  27. char * buffer = NEW char[size];
  28. memset(buffer, 0, size);
  29. HMODULE module = GetModuleHandle(null);
  30. GetModuleFileName(module, buffer, size);
  31. buffer[size - 1] = 0;
  32. currentPath = buffer;
  33. currentPath.GetFilePath(string(buffer));
  34. delete buffer;
  35. if(currentPath.Len() > 0)
  36. {
  37. currentPath.CheckPath();
  38. if(currentPath.Last() != '\\')
  39. {
  40. currentPath += '\\';
  41. }
  42. }
  43. #else
  44. currentPath = "game:\\";
  45. #endif
  46. iniPacks = null;
  47. systemIni = null;
  48. stopErrors = false;
  49. isLockFileAccess = false;
  50. }
  51. FileService::~FileService()
  52. {
  53. if(systemIni)
  54. {
  55. ((IniFile *)systemIni)->LockRelease(false);
  56. systemIni->Release();
  57. systemIni = null;
  58. }
  59. for(long i = 0; i < dataFiles; i++)
  60. {
  61. dataFiles[i]->ErrorRelease();
  62. }
  63. #ifndef _XBOX
  64. for(long i = 0; i < rwFiles; i++)
  65. {
  66. rwFiles[i]->ErrorRelease();
  67. }
  68. #endif
  69. for(long i = 0; i < iniFiles; i++)
  70. {
  71. iniFiles[i]->ErrorRelease();
  72. }
  73. for(long i = 0; i < finders; i++)
  74. {
  75. finders[i]->ErrorRelease();
  76. }
  77. for(long i = 0; i < buffers; i++)
  78. {
  79. buffers[i]->ErrorRelease();
  80. }
  81. if(iniPacks)
  82. {
  83. iniPacks->Release();
  84. }
  85. for(long i = 0; i < packFiles; i++)
  86. {
  87. packFiles[i]->ErrorRelease();
  88. }
  89. for(long i = 0; i < mirrorPaths; i++)
  90. {
  91. mirrorPaths[i]->ErrorRelease();
  92. }
  93. delete dcmprs;
  94. }
  95. bool FileService::Init()
  96. {
  97. #ifndef _XBOX
  98. iniPacks = LoadPack("resource\\ini.pkx", _FL_);
  99. #else
  100. iniPacks = LoadPack("ini.pkx", _FL_);
  101. #endif
  102. IIniFile * ini = SystemIni();
  103. if(ini)
  104. {
  105. //drainPath = systemIni->GetString("fileservice", "drainpath", "");
  106. stopErrors = systemIni->GetLong("fileservice", "trace", 1) == 0;
  107. }
  108. return true;
  109. }
  110. //Удаление записи об ресурсе
  111. void FileService::DeleteDataFile(DataFileBase * ptr)
  112. {
  113. dataFiles.Del(ptr);
  114. }
  115. #ifndef _XBOX
  116. void FileService::DeleteFile(File * ptr)
  117. {
  118. rwFiles.Del(ptr);
  119. }
  120. #endif
  121. void FileService::DeleteIniFile(IniFile * ptr)
  122. {
  123. iniFiles.Del(ptr);
  124. }
  125. void FileService::DeleteFinder(Finder * ptr)
  126. {
  127. finders.Del(ptr);
  128. }
  129. void FileService::DeleteLoadBuffer(LoadBuffer * ptr)
  130. {
  131. buffers.Del(ptr);
  132. }
  133. void FileService::DeletePackFile(PackFile * ptr)
  134. {
  135. packFiles.Del(ptr);
  136. }
  137. void FileService::DeleteMirrorPath(MirrorPath * ptr)
  138. {
  139. filesTree.DelMirrorPath(ptr);
  140. mirrorPaths.Del(ptr);
  141. }
  142. //Открыть файл данных (файл может быть архивирован)
  143. IDataFile * FileService::OpenDataFile(const char * fileName, IFileService_DFOpenMode mode, const char * cppFileName, long cppFileLine)
  144. {
  145. return OpenDataFile(fileName, mode, true, cppFileName, cppFileLine);
  146. }
  147. //Открыть файл данных (файл может быть архивирован)
  148. IDataFile * FileService::OpenDataFile(const char * fileName, IFileService_DFOpenMode mode, bool isOutputError, const char * cppFileName, long cppFileLine)
  149. {
  150. if(isLockFileAccess)
  151. {
  152. return null;
  153. }
  154. SingleClassThread
  155. //Режим открытия файла
  156. if(mode == file_open_default)
  157. {
  158. #ifndef _XBOX
  159. #ifndef STOP_DEBUG
  160. mode = file_open_any;
  161. #else
  162. mode = file_open_frompack;
  163. #endif
  164. #else
  165. //XBOX
  166. mode = file_open_frompack;
  167. #endif
  168. }
  169. Assert((mode & 3) != 0);
  170. //Смотрим файл в загруженных паках
  171. if(packFiles.Size() > 0 && (mode & file_open_frompack))
  172. {
  173. const char * name = string::GetFileName(fileName);
  174. dword len = 0;
  175. dword hash = string::HashNoCase(name, len);
  176. dword packedSize = 0;
  177. dword fileSize = 0;
  178. for(dword i = 0; i < packFiles.Size(); i++)
  179. {
  180. const byte * ptr = packFiles[i]->Find(name, hash, len, packedSize, fileSize);
  181. if(ptr)
  182. {
  183. //Открываем из пак файла
  184. DataFileBase * mfile = null;
  185. if(packedSize < fileSize)
  186. {
  187. mfile = NEW DataFileMemPack(cppFileName, cppFileLine, ptr, packedSize, fileSize);
  188. }else{
  189. mfile = NEW DataFileMemUnpack(cppFileName, cppFileLine, ptr, fileSize);
  190. }
  191. dataFiles.Add(mfile);
  192. fullPath = name;
  193. return mfile;
  194. }
  195. }
  196. }
  197. //Получаем полный путь
  198. const char * path = null;
  199. if((mode & file_open_fromdisk) == 0 || (path = BuildPath_noSafe(fileName)) == null)
  200. {
  201. if(!isOutputError)
  202. {
  203. Error("FileService error: can't open file \"%s\" (file: %s, line: %i -> OpenDataFile)", fileName, cppFileName, cppFileLine);
  204. }
  205. return null;
  206. }
  207. //Пытаемся найти файл в зеркальных паках
  208. dword count = filesTree.FindPaths(path);
  209. //Открываем с диска файл
  210. DataFile * file = NEW DataFile(cppFileName, cppFileLine);
  211. //Пытаемся найти файл в зеркальных папках
  212. for(dword i = 0; i < count; i++)
  213. {
  214. const char * p = filesTree.GetPath(i);
  215. if(file->Open(p))
  216. {
  217. fullPath = p;
  218. dataFiles.Add(file);
  219. DrainFile(fullPath.c_str());
  220. return file;
  221. }
  222. }
  223. //Пробуем открыть по прямому пути
  224. if(file->Open(path))
  225. {
  226. DrainFile(path);
  227. dataFiles.Add(file);
  228. return file;
  229. }
  230. delete file;
  231. if(!isOutputError)
  232. {
  233. Error("FileService error: can't open file \"%s\" (file: %s, line: %i -> OpenDataFile)", fileName, cppFileName, cppFileLine);
  234. }
  235. return null;
  236. }
  237. #ifndef _XBOX
  238. //Открыть файл
  239. IFile * FileService::OpenFile(const char * fileName, IFileService_OpenMode mode, const char * cppFileName, long cppFileLine)
  240. {
  241. return OpenFile(fileName, mode, true, cppFileName, cppFileLine);
  242. }
  243. //Открыть файл
  244. IFile * FileService::OpenFile(const char * fileName, IFileService_OpenMode mode, bool isOutputError, const char * cppFileName, long cppFileLine)
  245. {
  246. if(isLockFileAccess)
  247. {
  248. return null;
  249. }
  250. //Получаем полный путь
  251. const char * path = BuildPath_noSafe(fileName);
  252. if(path)
  253. {
  254. File * file = NEW File(cppFileName, cppFileLine);
  255. if(file->Open(path, mode))
  256. {
  257. SingleClassThread
  258. rwFiles.Add(file);
  259. return file;
  260. }
  261. delete file;
  262. }
  263. if(!isOutputError)
  264. {
  265. Error("FileService error: can't open file \"%s\" (file: %s, line: %i -> OpenFile)", fileName, cppFileName, cppFileLine);
  266. }
  267. return null;
  268. }
  269. //Открыть ini файл для редактирования
  270. IEditableIniFile * FileService::OpenEditableIniFile(const char * fileName, IFileService_OpenMode mode, const char * cppFileName, long cppFileLine)
  271. {
  272. if(isLockFileAccess)
  273. {
  274. return null;
  275. }
  276. IniFile * ini = NEW IniFile(fileName, false, cppFileName, cppFileLine);
  277. if(!ini->Open(mode))
  278. {
  279. delete ini;
  280. return null;
  281. }
  282. SingleClassThread
  283. iniFiles.Add(ini);
  284. return ini;
  285. }
  286. #endif
  287. //Открыть ini файл
  288. IIniFile * FileService::OpenIniFile(const char * fileName, const char * cppFileName, long cppFileLine)
  289. {
  290. return OpenIniFile(fileName, file_open_default, cppFileName, cppFileLine);
  291. }
  292. //Открыть ini файл
  293. IIniFile * FileService::OpenIniFile(const char * fileName, IFileService_DFOpenMode mode, const char * cppFileName, long cppFileLine)
  294. {
  295. if(isLockFileAccess)
  296. {
  297. return null;
  298. }
  299. IniFile * ini = NEW IniFile(fileName, true, cppFileName, cppFileLine);
  300. if(!ini->Open(mode))
  301. {
  302. delete ini;
  303. return null;
  304. }
  305. SingleClassThread
  306. iniFiles.Add(ini);
  307. return ini;
  308. }
  309. //Итератор поиска файлов
  310. IFinder * FileService::CreateFinder(const char * findPath, const char * findMask, dword flags, const char * cppFileName, long cppFileLine)
  311. {
  312. if(isLockFileAccess)
  313. {
  314. return null;
  315. }
  316. SingleClassThread
  317. //Получаем полный путь
  318. const char * path = BuildPath_noSafe(findPath);
  319. if(!path)
  320. {
  321. path = currentPath;
  322. }
  323. Finder * finder = NEW Finder(path, findMask, flags, cppFileName, cppFileLine);
  324. finders.Add(finder);
  325. return finder;
  326. }
  327. //Загрузить пак-файл в память
  328. IPackFile * FileService::LoadPack(const char * fileName, const char * cppFileName, long cppFileLine)
  329. {
  330. if(isLockFileAccess)
  331. {
  332. return null;
  333. }
  334. const char * name = string::GetFileName(fileName);
  335. ClassThreadLock
  336. //Просматриваем открытые
  337. for(long i = 0; i < packFiles; i++)
  338. {
  339. if(packFiles[i]->IsThis(fullPath.c_str()))
  340. {
  341. packFiles[i]->AddRefCount();
  342. ClassThreadUnlock
  343. return packFiles[i];
  344. }
  345. }
  346. ClassThreadUnlock
  347. //Открываем как файл данных
  348. IDataFile * df = OpenDataFile(fileName, file_open_any, _FL_);
  349. if(!df)
  350. {
  351. Error("FileService error: can't open pack file \"%s\" (file: %s, line: %i -> LoadPak)", fileName, cppFileName, cppFileLine);
  352. return false;
  353. }
  354. PackFile * pf = NEW PackFile(name, cppFileName, cppFileLine);
  355. if(pf->Load(df))
  356. {
  357. ClassThreadLock
  358. packFiles.Add(pf);
  359. ClassThreadUnlock
  360. df->Release();
  361. return pf;
  362. }
  363. df->Release();
  364. delete pf;
  365. return null;
  366. }
  367. //Отразить один путь на другой
  368. IMirrorPath * FileService::CreateMirrorPath(const char * from, const char * on, const char * cppFileName, long cppFileLine)
  369. {
  370. if(isLockFileAccess)
  371. {
  372. return null;
  373. }
  374. SingleClassThread
  375. //Получаем полные нормализованые пути
  376. const char * path = BuildPath_noSafe(from);
  377. if(!path)
  378. {
  379. path = currentPath;
  380. }
  381. string path_from = path;
  382. if(path_from.Len() > 0)
  383. {
  384. if(path_from.Last() != '\\')
  385. {
  386. path_from += '\\';
  387. }
  388. }
  389. path = BuildPath_noSafe(on);
  390. if(!path)
  391. {
  392. path = currentPath;
  393. }
  394. string path_on = path;
  395. if(path_on.Last() != '\\')
  396. {
  397. path_on += '\\';
  398. }
  399. if(path_from == path_on)
  400. {
  401. return null;
  402. }
  403. //Ищем среди созданых
  404. for(long i = 0; i < mirrorPaths; i++)
  405. {
  406. if(mirrorPaths[i]->IsThis(path_from, path_on))
  407. {
  408. mirrorPaths[i]->AddRefCount();
  409. return mirrorPaths[i];
  410. }
  411. }
  412. //Создаём новый объект и регистрируем его
  413. MirrorPath * mpath = NEW MirrorPath(from, on, path_from, path_on, cppFileName, cppFileLine);
  414. mirrorPaths.Add(mpath);
  415. filesTree.AddMirrorPath(mpath, path_on);
  416. return mpath;
  417. }
  418. //Прочитать файл в выделеную память с помощью OpenDataFile
  419. ILoadBuffer * FileService::LoadData(const char * fileName, const char * cppFileName, long cppFileLine)
  420. {
  421. IDataFile * file = OpenDataFile(fileName, file_open_default, _FL_);
  422. if(!file) return null;
  423. dword size = file->Size();
  424. byte * buffer = new(cppFileName, cppFileLine) byte[size];
  425. if(file->Read(buffer, size) != size)
  426. {
  427. Error("FileService error: read error from file \"%s\" (file: %s, line: %i -> LoadData)", fileName, cppFileName, cppFileLine);
  428. delete buffer;
  429. file->Release();
  430. return null;
  431. }
  432. file->Release();
  433. file = null;
  434. LoadBuffer * buf = NEW LoadBuffer(buffer, size, cppFileName, cppFileLine);
  435. SingleClassThread
  436. buffers.Add(buf);
  437. return buf;
  438. }
  439. //Заблокировать создание файлов
  440. void FileService::LockFileAccess(bool isLock)
  441. {
  442. isLockFileAccess = isLock;
  443. }
  444. #ifndef _XBOX
  445. //Сохранить данные в файл с помощью OpenFile, перезаписав его
  446. bool FileService::SaveData(const char * fileName, const void * data, dword size)
  447. {
  448. AssertCoreThread
  449. IFile * file = OpenFile(fileName, file_create_always, _FL_);
  450. if(!file)
  451. {
  452. return false;
  453. }
  454. if(file->Write(data, size) != size)
  455. {
  456. Error("FileService error: write error to file \"%s\" (file: %s)", fileName);
  457. return false;
  458. }
  459. file->Release();
  460. return true;
  461. }
  462. //Проверить существует ли файл
  463. bool FileService::IsExist(const char * fileName, bool checkAsDataFile)
  464. {
  465. if(checkAsDataFile)
  466. {
  467. IDataFile * file = OpenDataFile(fileName, file_open_default, false, _FL_);
  468. if(file)
  469. {
  470. file->Release();
  471. return true;
  472. }
  473. }else{
  474. //Получаем полный путь
  475. SingleClassThread
  476. const char * path = BuildPath_noSafe(fileName);
  477. if(GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES)
  478. {
  479. return true;
  480. }
  481. }
  482. return false;
  483. }
  484. #endif
  485. //Получить указатель на системный ini-файл
  486. IIniFile * FileService::SystemIni()
  487. {
  488. //Если файл уже открыт, то возвращаем указатель
  489. if(systemIni)
  490. {
  491. return systemIni;
  492. }
  493. //Необходимо загрузить ini файл
  494. systemIni = OpenIniFile(api->Storage().GetString("system.ini"), file_open_any, _FL_);
  495. if(systemIni)
  496. {
  497. ((IniFile *)systemIni)->LockRelease(true);
  498. }
  499. return systemIni;
  500. }
  501. #ifndef _XBOX
  502. //Создать папку (возможно указать иерархический путь)
  503. bool FileService::CreateFolder(const char * path)
  504. {
  505. if(isLockFileAccess)
  506. {
  507. return false;
  508. }
  509. AssertCoreThread
  510. if(!path)
  511. {
  512. return false;
  513. }
  514. string tmp;
  515. while(*path)
  516. {
  517. char c = *path++;
  518. if(c == '/') c = '\\';
  519. if(c == '\\')
  520. {
  521. if(tmp.Len() && tmp[tmp.Len() - 1] != '\\')
  522. {
  523. if(tmp[tmp.Len() - 1] != ':')
  524. {
  525. if(::CreateDirectory(tmp.c_str(), null) == 0)
  526. {
  527. if(::GetLastError() != ERROR_ALREADY_EXISTS)
  528. {
  529. return false;
  530. }
  531. }
  532. }
  533. tmp += '\\';
  534. }
  535. }else{
  536. tmp += c;
  537. }
  538. }
  539. if(::CreateDirectory(tmp.c_str(), null) == 0)
  540. {
  541. if(::GetLastError() != ERROR_ALREADY_EXISTS)
  542. {
  543. return false;
  544. }
  545. }
  546. return true;
  547. }
  548. //Скопировать файл
  549. bool FileService::Copy(const char * from, const char * to)
  550. {
  551. AssertCoreThread
  552. return ::CopyFile(from, to, false) != 0;
  553. }
  554. //Переместить файл
  555. bool FileService::Move(const char * from, const char * to)
  556. {
  557. AssertCoreThread
  558. return ::MoveFile(from, to) != 0;
  559. }
  560. //Переименовать файл
  561. bool FileService::Rename(const char * from, const char * to)
  562. {
  563. AssertCoreThread
  564. return ::MoveFile(from, to) != 0;
  565. }
  566. //Удалить файл
  567. bool FileService::Delete(const char * fileName)
  568. {
  569. AssertCoreThread
  570. dword attributes = ::GetFileAttributes(fileName);
  571. if(attributes != INVALID_FILE_ATTRIBUTES)
  572. {
  573. if(attributes & FILE_ATTRIBUTE_READONLY)
  574. {
  575. attributes &= ~FILE_ATTRIBUTE_READONLY;
  576. ::SetFileAttributes(fileName, attributes);
  577. }
  578. if(attributes & FILE_ATTRIBUTE_DIRECTORY)
  579. {
  580. return ::RemoveDirectory(fileName) != 0;
  581. }
  582. }
  583. return ::DeleteFile(fileName) != 0;
  584. }
  585. //Установить путь для сохранения открываемых файлов данных на чтение
  586. void FileService::SetDrainPath(const char * path)
  587. {
  588. AssertCoreThread
  589. drainPath = path;
  590. }
  591. //Создать пак-файл из файлов находящихся в заданной папке
  592. bool FileService::BuildPack(const char * fileName, const char * folderPath, const char * filesMask, IFileService_PackCompressMethod method)
  593. {
  594. AssertCoreThread
  595. //Перебираем все реальные файлы, загружая в память
  596. IFinder * finder = CreateFinder(folderPath, filesMask, find_all_files_no_mirrors, _FL_);
  597. if(!finder) return false;
  598. string filePath;
  599. PackArchivator compressor;
  600. for(dword i = 0; i < finder->Count(); i++)
  601. {
  602. //Открываем файл
  603. filePath = finder->FilePath(i);
  604. IFile * file = OpenFile(filePath, file_open_existing_for_read, _FL_);
  605. if(!file)
  606. {
  607. finder->Release();
  608. return false;
  609. }
  610. //Проверяем размер файла
  611. dword size = file->Size();
  612. if (size == 0)
  613. {
  614. api->Trace("File '%s' incorrect file size : %d bytes - delete from pack \"%s\"", filePath.c_str(), size, fileName);
  615. file->Release();
  616. file = NULL;
  617. continue;
  618. }
  619. //Загружаем файл в память
  620. void * data = NEW byte[size];
  621. if(file->Read(data, size) != size)
  622. {
  623. api->Trace("IO error, can't load data from file \"%s\". Stop building pack \"%s\"", filePath.c_str(), fileName);
  624. file->Release();
  625. finder->Release();
  626. return false;
  627. }
  628. file->Release();
  629. compressor.AddFile(filePath, data, size);
  630. }
  631. finder->Release();
  632. finder = null;
  633. PackArchivator::Method pam = PackArchivator::m_archive;
  634. switch(method)
  635. {
  636. case pack_cmpr_method_archive:
  637. pam = PackArchivator::m_archive;
  638. break;
  639. case pack_cmpr_method_store:
  640. pam = PackArchivator::m_store;
  641. break;
  642. default:
  643. Assert(false);
  644. }
  645. compressor.Process(pam);
  646. //Сохраняем полученные данные в файл
  647. if(!PackFile::SaveToPack(fileName, compressor))
  648. {
  649. api->Trace("IO error, can't save pack file \"%s\"", fileName);
  650. return false;
  651. }
  652. return true;
  653. }
  654. #endif
  655. //Получить полный нормализованный путь до файла, возвращает ссылку на result
  656. string & FileService::BuildPath(const char * path, string & result)
  657. {
  658. SingleClassThread
  659. path = BuildPath_noSafe(path);
  660. if(path)
  661. {
  662. result = path;
  663. }else{
  664. result = currentPath;
  665. }
  666. return result;
  667. }
  668. //Получить полный нормализованный путь до файла
  669. const char * FileService::BuildPath_noSafe(const char * path)
  670. {
  671. if(!path || !path[0])
  672. {
  673. return null;
  674. }
  675. //Смотрим по наличию диска относительный или полный путь
  676. for(long i = 0; path[i]; i++)
  677. {
  678. if(path[i] == '\\' || path[i] == '/')
  679. {
  680. if(i > 0 && path[i - 1] == ':')
  681. {
  682. //Первым задано имя диска, значит путь полный
  683. break;
  684. }
  685. }
  686. }
  687. //Получаем полный ненармализованый путь
  688. if(path[i])
  689. {
  690. fullPath = path;
  691. }else{
  692. fullPath = currentPath;
  693. fullPath += path;
  694. }
  695. //Убираем лишние слеши, выправляем кривые, убираем точки
  696. fullPath.Reserve(fullPath.Len() + MAX_PATH);
  697. char * src = fullPath.GetDataBuffer();
  698. char * dst = src;
  699. char * start = src;
  700. while(*src)
  701. {
  702. if(*src == '\\' || *src == '/')
  703. {
  704. //Пропускаем последующие слэши
  705. for(src++; *src == '\\' || *src == '/'; src++);
  706. //Смотрим что за слешом
  707. char * bookmark = src;
  708. for(long up = 0; *src == '.'; up++, src++);
  709. if(up)
  710. {
  711. if(*src == '\\' || *src == '/')
  712. {
  713. //Надо поднятся на up уровней вверх
  714. for(; dst >= start; dst--)
  715. {
  716. if(*dst == '\\')
  717. {
  718. //Уровни закончились, заканчиваем
  719. if(--up == 0) break;
  720. if(dst > start && dst[-1] == ':')
  721. {
  722. //Дошли до диска, дальше не поднимаемся
  723. break;
  724. }
  725. }
  726. }
  727. continue;
  728. }
  729. }
  730. *dst++ = '\\';
  731. src = bookmark;
  732. if(!*src) break;
  733. }
  734. char c = *src++;
  735. //Приведём символ к одному регистру
  736. if(c >= 'A' && c <= 'Z')
  737. {
  738. c += 'a' - 'A';
  739. }
  740. //Сохраняем символ
  741. *dst++ = c;
  742. }
  743. *dst = 0;
  744. return fullPath.c_str();
  745. }
  746. //Скопировать файл
  747. __forceinline void FileService::DrainFile(const char * path)
  748. {
  749. #ifndef _XBOX
  750. if(drainPath.Len() > 0)
  751. {
  752. string relPath = path;
  753. relPath.GetRelativePath(currentPath);
  754. string drain = drainPath;
  755. if(drain.Last() != '\\' || drain.Last() != '/')
  756. {
  757. drain += '\\';
  758. }
  759. drain += relPath;
  760. //Error("From: %s\nTo: %s\n", path, drain.c_str());
  761. string filePath;
  762. filePath.GetFilePath(drain);
  763. CreateFolder(filePath.c_str());
  764. if(!Copy(path, drain.c_str()))
  765. {
  766. if(!IsExist(drain.c_str(), false))
  767. {
  768. Error("Can't drain file %s to %s", path, drain.c_str());
  769. }
  770. }
  771. }
  772. #endif
  773. }
  774. //Вывести в лог сообщение об ошибке
  775. void _cdecl FileService::Error(const char * error, ...)
  776. {
  777. if(!stopErrors)
  778. {
  779. SingleClassThread
  780. api->TraceData(error, &error + 1);
  781. }
  782. }
  783. //Собрать все имена файлов с паков
  784. void FileService::CollectFilesFromPacks(array<const char *> & names)
  785. {
  786. names.Empty();
  787. for(dword i = 0; i < packFiles.Size(); i++)
  788. {
  789. packFiles[i]->CollectFiles(names);
  790. }
  791. }
  792. //Выделить декомпрессор для файла
  793. XStreamDecompressor * FileService::GetStreamDecompressor()
  794. {
  795. AssertCoreThread
  796. if(isUseDcmprs)
  797. {
  798. return NEW XStreamDecompressor();
  799. }
  800. isUseDcmprs = true;
  801. return dcmprs;
  802. }
  803. //Удалить декомпрессор для файла
  804. void FileService::ReleaseStreamDecompressor(XStreamDecompressor * d)
  805. {
  806. AssertCoreThread
  807. if(d == dcmprs)
  808. {
  809. isUseDcmprs = false;
  810. }else{
  811. delete d;
  812. }
  813. }