diff --git a/cocos/kr2/Resources/res/locale/en_us.xml b/cocos/kr2/Resources/res/locale/en_us.xml index 8f503357..655f23e0 100644 --- a/cocos/kr2/Resources/res/locale/en_us.xml +++ b/cocos/kr2/Resources/res/locale/en_us.xml @@ -50,9 +50,12 @@ + + + @@ -67,9 +70,10 @@ - - + + + @@ -78,6 +82,7 @@ + @@ -104,5 +109,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/cocos/kr2/Resources/res/locale/ja_jp.xml b/cocos/kr2/Resources/res/locale/ja_jp.xml index d6abfbd8..b0b2468c 100644 --- a/cocos/kr2/Resources/res/locale/ja_jp.xml +++ b/cocos/kr2/Resources/res/locale/ja_jp.xml @@ -50,9 +50,12 @@ + + + @@ -65,9 +68,10 @@ - - + + + @@ -75,6 +79,7 @@ + @@ -100,6 +105,15 @@ - + + + + + + + + + + \ No newline at end of file diff --git a/cocos/kr2/Resources/res/locale/zh_cn.xml b/cocos/kr2/Resources/res/locale/zh_cn.xml index c04ae8f8..200819d7 100644 --- a/cocos/kr2/Resources/res/locale/zh_cn.xml +++ b/cocos/kr2/Resources/res/locale/zh_cn.xml @@ -50,9 +50,12 @@ + + + @@ -69,6 +72,7 @@ + @@ -78,6 +82,7 @@ + @@ -104,5 +109,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/cocos/kr2/Resources/res/locale/zh_tw.xml b/cocos/kr2/Resources/res/locale/zh_tw.xml index fe2ea9ee..d2a1f07c 100644 --- a/cocos/kr2/Resources/res/locale/zh_tw.xml +++ b/cocos/kr2/Resources/res/locale/zh_tw.xml @@ -50,9 +50,12 @@ + + + @@ -69,7 +72,8 @@ - + + @@ -78,6 +82,7 @@ + @@ -104,5 +109,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/cocos/kr2/cocosstudio/ui/CheckListDialog.csd b/cocos/kr2/cocosstudio/ui/CheckListDialog.csd new file mode 100644 index 00000000..ea324f2d --- /dev/null +++ b/cocos/kr2/cocosstudio/ui/CheckListDialog.csd @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cocos/kr2/cocosstudio/ui/FileManageMenu.csd b/cocos/kr2/cocosstudio/ui/FileManageMenu.csd new file mode 100644 index 00000000..d9d98bcf --- /dev/null +++ b/cocos/kr2/cocosstudio/ui/FileManageMenu.csd @@ -0,0 +1,1193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cocos/kr2/cocosstudio/ui/MenuList.csd b/cocos/kr2/cocosstudio/ui/MenuList.csd index 3d5c678b..80ec9eaf 100644 --- a/cocos/kr2/cocosstudio/ui/MenuList.csd +++ b/cocos/kr2/cocosstudio/ui/MenuList.csd @@ -73,7 +73,7 @@ - + @@ -115,17 +115,17 @@ - + - - + + - + @@ -179,17 +179,17 @@ - + - - + + - + @@ -243,17 +243,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + @@ -298,14 +407,14 @@ - - + + - + @@ -350,14 +459,14 @@ - - + + - + @@ -403,7 +512,7 @@ - + diff --git a/cocos/kr2/cocosstudio/ui/MessageBox.csd b/cocos/kr2/cocosstudio/ui/MessageBox.csd index b14f7f13..879a5047 100644 --- a/cocos/kr2/cocosstudio/ui/MessageBox.csd +++ b/cocos/kr2/cocosstudio/ui/MessageBox.csd @@ -1,5 +1,5 @@ - + diff --git a/cocos/kr2/cocosstudio/ui/ProgressBox.csd b/cocos/kr2/cocosstudio/ui/ProgressBox.csd new file mode 100644 index 00000000..9b864b14 --- /dev/null +++ b/cocos/kr2/cocosstudio/ui/ProgressBox.csd @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cocos/kr2/cocosstudio/ui/comctrl/SliderIconItem.csd b/cocos/kr2/cocosstudio/ui/comctrl/SliderIconItem.csd index 9d7d9374..13e30eda 100644 --- a/cocos/kr2/cocosstudio/ui/comctrl/SliderIconItem.csd +++ b/cocos/kr2/cocosstudio/ui/comctrl/SliderIconItem.csd @@ -4,33 +4,33 @@ - + - - + + - + - + - - + + - + - - + + @@ -44,29 +44,29 @@ - + - + - + - + - + - - + + diff --git a/cocos/kr2/kr2.ccs b/cocos/kr2/kr2.ccs index 44b5fa31..9b379e60 100644 --- a/cocos/kr2/kr2.ccs +++ b/cocos/kr2/kr2.ccs @@ -57,6 +57,7 @@ + @@ -70,6 +71,7 @@ + diff --git a/project/android/AndroidManifest.xml b/project/android/AndroidManifest.xml index 30c815b2..e3dc35ae 100644 --- a/project/android/AndroidManifest.xml +++ b/project/android/AndroidManifest.xml @@ -4,8 +4,8 @@ --> @@ -22,6 +22,8 @@ + =Build.VERSION_CODES.LOLLIPOP) { for(String path : getExtSdCardPaths(this)) { if (!isWritableNormalOrSaf(path)) { @@ -304,7 +304,7 @@ public void onCreate(Bundle savedInstanceState) { } } } - + */ initDump(this.getFilesDir().getAbsolutePath() + "/dump"); } @@ -314,6 +314,11 @@ public void onDestroy() { System.exit(0); } + @Override + public void onLowMemory() { + nativeOnLowMemory(); + } + static class DialogMessage { public String Title; @@ -494,6 +499,7 @@ public void run() { static public native void onNativeInit(); static public native void onBannerSizeChanged(int w, int h); static private native void initDump(String path); + static private native void nativeOnLowMemory(); static public void MessageController(int what, int arg1, int arg2) { Message msg = msgHandler.obtainMessage(); @@ -749,10 +755,18 @@ public Cocos2dxGLSurfaceView onCreateView() { public int get_res_sd_operate_step() { return -1; } - void guideDialogForLEXA(String path) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - ImageView image = new ImageView(this); - image.setImageResource(get_res_sd_operate_step()); + static void requireLEXA(final String path) { + msgHandler.post(new Runnable() { + @Override + public void run() { + guideDialogForLEXA(path); + } + }); + } + static void guideDialogForLEXA(final String path) { + AlertDialog.Builder builder = new AlertDialog.Builder(sInstance); + ImageView image = new ImageView(sInstance); + image.setImageResource(sInstance.get_res_sd_operate_step()); builder .setView(image) .setTitle(path) diff --git a/src/core/Android.mk b/src/core/Android.mk index 107f84fc..f1191296 100644 --- a/src/core/Android.mk +++ b/src/core/Android.mk @@ -4,15 +4,13 @@ include $(CLEAR_VARS) LOCAL_MODULE := krkr2 -ifeq ($(TARGET_ARCH_ABI),arm64-v8a) -ThreadPool11FILE := -else -ThreadPool11FILE := utils/threadpool11/pool.cpp utils/threadpool11/worker.cpp -endif - LOCAL_SRC_FILES := \ $(filter-out $(LOCAL_PATH)/visual/Resampler.cpp, $(wildcard $(LOCAL_PATH)/visual/*.cpp)) \ $(wildcard $(LOCAL_PATH)/base/7zip/*.c) \ +$(wildcard $(LOCAL_PATH)/base/7zip/C/*.c) \ +$(wildcard $(LOCAL_PATH)/base/7zip/CPP/*/*.cpp) \ +$(wildcard $(LOCAL_PATH)/base/7zip/CPP/*/*/*.cpp) \ +$(wildcard $(LOCAL_PATH)/base/7zip/CPP/*/*/*/*.cpp) \ $(wildcard $(LOCAL_PATH)/base/*.cpp) \ $(filter-out $(LOCAL_PATH)/base/win32/FuncStubs.cpp $(LOCAL_PATH)/base/win32/SusieArchive.cpp, $(wildcard $(LOCAL_PATH)/base/win32/*.cpp)) \ $(filter-out $(LOCAL_PATH)/environ/MainFormUnit.cpp, $(wildcard $(LOCAL_PATH)/environ/*.cpp)) \ @@ -37,7 +35,6 @@ $(wildcard $(LOCAL_PATH)/utils/encoding/*.c) \ $(wildcard $(LOCAL_PATH)/utils/minizip/*.c) \ $(wildcard $(LOCAL_PATH)/utils/minizip/*.cpp) \ $(wildcard $(LOCAL_PATH)/utils/win32/*.cpp) \ -$(ThreadPool11FILE) \ $(wildcard $(LOCAL_PATH)/visual/gl/*.cpp) \ $(wildcard $(LOCAL_PATH)/visual/ogl/*.cpp) \ $(filter-out $(LOCAL_PATH)/visual/win32/GDIFontRasterizer.cpp $(LOCAL_PATH)/visual/win32/NativeFreeTypeFace.cpp \ @@ -80,6 +77,7 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH)/base \ $(LOCAL_PATH)/../../vendor/threadpool11/ext/include \ $(LOCAL_PATH)/../../libs/android/bpg/include \ $(LOCAL_PATH)/../../libs/android/ffmpeg/include \ + $(LOCAL_PATH)/../../libs/android/libarchive/include \ $(LOCAL_PATH)/visual/RenderScript/rs \ $(LOCAL_PATH)/../../vendor/cocos2d-x/current/cocos \ $(LOCAL_PATH)/../../vendor/cocos2d-x/current/cocos/platform \ @@ -89,10 +87,10 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH)/base \ $(LOCAL_PATH)/../../vendor/cocos2d-x/current/external \ LOCAL_CPPFLAGS += -DTJS_TEXT_OUT_CRLF -D__STDC_CONSTANT_MACROS -LOCAL_CFLAGS += -DTJS_TEXT_OUT_CRLF +LOCAL_CFLAGS += -DTJS_TEXT_OUT_CRLF -D_7ZIP_ST LOCAL_STATIC_LIBRARIES := ffmpeg libopencv_imgproc libopencv_core libopencv_hal libtbb gdiplus_static cpufeatures \ opusfile_static opus_static onig_static libbpg_static vorbis_static cairo_static pixman_static expat_static \ - breakpad_client openal_static jxrlib_static + breakpad_client openal_static jxrlib_static libarchive_static # libRScpp_static include $(BUILD_STATIC_LIBRARY) @@ -112,4 +110,5 @@ $(call import-module, pixman) $(call import-module, gdiplus) $(call import-module, expat) $(call import-module, google_breakpad) -#$(call import-module, libRScpp) \ No newline at end of file +#$(call import-module, libRScpp) +$(call import-module, libarchive) \ No newline at end of file diff --git a/src/core/base/7zArchive.cpp b/src/core/base/7zArchive.cpp index b49d7da1..345e97f0 100644 --- a/src/core/base/7zArchive.cpp +++ b/src/core/base/7zArchive.cpp @@ -7,22 +7,36 @@ extern "C" { #include "7zip/C/7zFile.h" #include "7zip/C/7zCrc.h" } +#include "StorageImpl.h" static ISzAlloc allocImp = { [](void *p, size_t size) -> void *{ return malloc(size); }, [](void *p, void *addr) { free(addr); } }; -class SevenZipArchive : public tTVPArchive { +class SevenZipStreamWrap { +public: CSzArEx db; tTJSBinaryStream *_stream; + CLookToRead lookStream; struct CSeekInStream : public ISeekInStream { - SevenZipArchive *Host; + SevenZipStreamWrap *Host; } archiveStream; - CLookToRead lookStream; - - std::vector > filelist; +public: + SevenZipStreamWrap(tTJSBinaryStream * st) : _stream(st) { + archiveStream.Host = this; + archiveStream.Read = [](void *p, void *buf, size_t *size)->SRes {return ((CSeekInStream*)p)->Host->StreamRead(buf, size); }; + archiveStream.Seek = [](void *p, Int64 *pos, ESzSeek origin)->SRes {return ((CSeekInStream*)p)->Host->StreamSeek(pos, origin); }; + LookToRead_CreateVTable(&lookStream, false); + lookStream.realStream = &archiveStream; + SzArEx_Init(&db); + if (!g_CrcTable[1]) CrcGenerateTable(); + } + ~SevenZipStreamWrap() { + SzArEx_Free(&db, &allocImp); + delete _stream; + } SRes StreamRead(void *buf, size_t *size) { *size = _stream->Read(buf, *size); return SZ_OK; @@ -39,22 +53,16 @@ class SevenZipArchive : public tTVPArchive { *pos = _stream->Seek(*pos, whence); return SZ_OK; } +}; + +class SevenZipArchive : public tTVPArchive, public SevenZipStreamWrap { + std::vector > filelist; public: - SevenZipArchive(const ttstr & name, tTJSBinaryStream *st) : tTVPArchive(name), _stream(st) { - archiveStream.Host = this; - archiveStream.Read = [](void *p, void *buf, size_t *size)->SRes{return ((CSeekInStream*)p)->Host->StreamRead(buf, size); }; - archiveStream.Seek = [](void *p, Int64 *pos, ESzSeek origin)->SRes{return ((CSeekInStream*)p)->Host->StreamSeek(pos, origin); }; - LookToRead_CreateVTable(&lookStream, false); - lookStream.realStream = &archiveStream; - SzArEx_Init(&db); - if(!g_CrcTable[1]) CrcGenerateTable(); + SevenZipArchive(const ttstr & name, tTJSBinaryStream *st) : tTVPArchive(name), SevenZipStreamWrap(st) { } - virtual ~SevenZipArchive() { - SzArEx_Free(&db, &allocImp); - delete _stream; - } + virtual ~SevenZipArchive() { } virtual tjs_uint GetCount() { return filelist.size(); } virtual ttstr GetName(tjs_uint idx) { return filelist[idx].first; } @@ -103,9 +111,12 @@ class SevenZipArchive : public tTVPArchive { return mem; } - bool Open() { + bool Open(bool normalizeFileName) { SRes res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocImp); - if (res != SZ_OK) return false; + if (res != SZ_OK) { + _stream = nullptr; + return false; + } for (int i = 0; i < db.NumFiles; i++) { size_t offset = 0; size_t outSizeProcessed = 0; @@ -115,25 +126,102 @@ class SevenZipArchive : public tTVPArchive { ttstr filename; SzArEx_GetFileNameUtf16(&db, i, (UInt16*)filename.AllocBuffer(len)); filename.FixLen(); - NormalizeInArchiveStorageName(filename); + if (normalizeFileName) + NormalizeInArchiveStorageName(filename); filelist.emplace_back(filename, i); } - std::sort(filelist.begin(), filelist.end(), [](const std::pair& a, const std::pair& b){ - return a.first < b.first; - }); + if (normalizeFileName) { + std::sort(filelist.begin(), filelist.end(), [](const std::pair& a, const std::pair& b) { + return a.first < b.first; + }); + } return true; } }; -tTVPArchive * TVPOpen7ZArchive(const ttstr & name, tTJSBinaryStream *st) { +tTVPArchive * TVPOpen7ZArchive(const ttstr & name, tTJSBinaryStream *st, bool normalizeFileName) { tjs_uint64 pos = st->GetPosition(); bool checkZIP = st->ReadI16LE() == 0x7A37; // '7z' st->SetPosition(pos); if (!checkZIP) return nullptr; SevenZipArchive *arc = new SevenZipArchive(name, st); - if (!arc->Open()) { + if (!arc->Open(normalizeFileName)) { delete arc; return nullptr; } return arc; -} \ No newline at end of file +} + +#if 0 +void TVPUnpack7ZArchive(tTJSBinaryStream *st, ttstr outpath) { + tjs_uint64 origpos = st->GetPosition(); + SevenZipStreamWrap szsw(st); + CSzArEx &db = szsw.db; + SRes res = SzArEx_Open(&db, &szsw.lookStream.s, &allocImp, &allocImp); + if (res != SZ_OK) return; + outpath += TJS_W("/"); + for (int i = 0; i < db.db.NumFolders; ++i) { + ; + } + for (int i = 0; i < db.NumFiles; i++) { + size_t offset = 0; + size_t outSizeProcessed = 0; + size_t len = SzArEx_GetFileNameUtf16(&db, i, NULL); + ttstr filename; + SzArEx_GetFileNameUtf16(&db, i, (UInt16*)filename.AllocBuffer(len)); + filename.FixLen(); + bool isDir = SzArEx_IsDir(&db, i); + ttstr fullpath = outpath + filename; + if (isDir) { + if (!TVPCheckExistentLocalFolder(fullpath)) + TVPCreateFolders(fullpath); + } else { + tjs_uint fileIndex = i; + UInt64 fileSize = SzArEx_GetFileSize(&db, fileIndex); + if (fileSize <= 0) { + FILE *fp = fopen(fullpath.AsStdString().c_str(), "wb"); + fclose(fp); + } + + UInt32 folderIndex = db.FileToFolder[fileIndex]; + if (folderIndex == (UInt32)-1) continue; + + const CSzAr *p = &db.db; + CSzFolder folder; + CSzData sd; + const Byte *data = p->CodersData + p->FoCodersOffsets[folderIndex]; + sd.Data = data; + sd.Size = p->FoCodersOffsets[folderIndex + 1] - p->FoCodersOffsets[folderIndex]; + + if (SzGetNextFolderItem(&folder, &sd) != SZ_OK) continue; + if (folder.NumCoders == 1) { + UInt64 startPos = db.dataPos; + const UInt64 *packPositions = p->PackPositions + p->FoStartPackStreamIndex[folderIndex]; + UInt64 offset = packPositions[0]; + UInt64 inSize = packPositions[1] - offset; + if (folder.Coders[0].MethodID == k_Copy && inSize == fileSize) { + CopyStreamToFile(st, origpos + startPos + offset, inSize, fullpath); + continue; + } + } + + UInt32 blockIndex; + Byte *outBuffer = nullptr; + size_t outBufferSize; + size_t offset, outSizeProcessed; + SRes res = SzArEx_Extract(&db, &szsw.lookStream.s, fileIndex, &blockIndex, &outBuffer, &outBufferSize, + &offset, &outSizeProcessed, &allocImp, &allocImp); + tTVPMemoryStream *mem; + if (offset == 0 && fileSize <= outBufferSize) { + mem = new tTVPMemoryStream(outBuffer, outBufferSize); + } else { + Byte *buf = new Byte[fileSize]; + memcpy(buf, outBuffer, fileSize); + mem = new tTVPMemoryStream(buf, fileSize); + delete outBuffer; + } + return mem; + } + } +} +#endif \ No newline at end of file diff --git a/src/core/base/StorageIntf.cpp b/src/core/base/StorageIntf.cpp index 60637c87..9fd96fa7 100644 --- a/src/core/base/StorageIntf.cpp +++ b/src/core/base/StorageIntf.cpp @@ -775,7 +775,7 @@ class tTVPArchiveCache } // not exist in the cache - tTVPArchive *arc = TVPOpenArchive(name); + tTVPArchive *arc = TVPOpenArchive(name, true); tHolder holder(arc); ArchiveCache.AddWithHash(name, hash, holder); return arc; diff --git a/src/core/base/StorageIntf.h b/src/core/base/StorageIntf.h index 329d8948..a1679881 100644 --- a/src/core/base/StorageIntf.h +++ b/src/core/base/StorageIntf.h @@ -63,6 +63,7 @@ class tTVPArchive tTJSHashTable, 1024> Hash; bool Init; + public: ttstr ArchiveName; @@ -151,7 +152,7 @@ class iTVPStorageMedia //--------------------------------------------------------------------------- // must be implemented in each platform //--------------------------------------------------------------------------- -extern tTVPArchive * TVPOpenArchive(const ttstr & name); +extern tTVPArchive * TVPOpenArchive(const ttstr & name, bool normalizeFileName); // open archive and return tTVPArchive instance. TJS_EXP_FUNC_DEF(ttstr, TVPGetTemporaryName, ()); @@ -341,12 +342,12 @@ class TArchiveStream : public tTJSBinaryStream { break; } if (CurrentPos < 0) CurrentPos = 0; - else if (CurrentPos > DataLength) CurrentPos = DataLength; + else if (CurrentPos > (tjs_int64)DataLength) CurrentPos = DataLength; _instr->SetPosition(CurrentPos + StartPos); return CurrentPos; } virtual tjs_uint TJS_INTF_METHOD Read(void *buffer, tjs_uint read_size) { - if (CurrentPos + read_size >= DataLength) { + if (CurrentPos + read_size >= (tjs_int64)DataLength) { read_size = (tjs_uint)(DataLength - CurrentPos); } diff --git a/src/core/base/TARArchive.cpp b/src/core/base/TARArchive.cpp index da61acb3..c3e2cc20 100644 --- a/src/core/base/TARArchive.cpp +++ b/src/core/base/TARArchive.cpp @@ -51,7 +51,7 @@ class TARArchive : public tTVPArchive { ~TARArchive() { TVPFreeArchiveHandlePoolByPointer(this); } - bool init(tTJSBinaryStream * _instr) { + bool init(tTJSBinaryStream * _instr, bool normalizeFileName) { if (_instr) { tjs_uint64 archiveSize = _instr->GetSize(); TAR_HEADER tar_header; @@ -86,16 +86,19 @@ class TARArchive : public tTVPArchive { } EntryInfo item; storeFilename(item.filename, &filename[0], ArchiveName); - NormalizeInArchiveStorageName(item.filename); + if (normalizeFileName) + NormalizeInArchiveStorageName(item.filename); item.size = original_size; item.offset = _instr->GetPosition(); filelist.emplace_back(item); tjs_uint64 readsize = (original_size + (TBLOCK - 1)) & ~(TBLOCK - 1); _instr->SetPosition(item.offset + readsize); } - std::sort(filelist.begin(), filelist.end(), [](const EntryInfo& a, const EntryInfo& b){ - return a.filename < b.filename; - }); + if (normalizeFileName) { + std::sort(filelist.begin(), filelist.end(), [](const EntryInfo& a, const EntryInfo& b) { + return a.filename < b.filename; + }); + } TVPReleaseCachedArchiveHandle(this, _instr); return true; } @@ -111,9 +114,9 @@ tTJSBinaryStream * TARArchive::CreateStreamByIndex(tjs_uint idx) { return new TArchiveStream(this, info.offset, info.size); } -tTVPArchive * TVPOpenTARArchive(const ttstr & name, tTJSBinaryStream * st) { +tTVPArchive * TVPOpenTARArchive(const ttstr & name, tTJSBinaryStream * st, bool normalizeFileName) { TARArchive *arc = new TARArchive(name); - if (!arc->init(st)) { + if (!arc->init(st, normalizeFileName)) { delete arc; return nullptr; } diff --git a/src/core/base/UtilStreams.cpp b/src/core/base/UtilStreams.cpp index cac3ac7d..d98b134f 100644 --- a/src/core/base/UtilStreams.cpp +++ b/src/core/base/UtilStreams.cpp @@ -15,9 +15,10 @@ #include "DebugIntf.h" #include "EventIntf.h" #include "StorageIntf.h" - - - +#include +#include +#include +#include "Platform.h" @@ -368,3 +369,350 @@ tjs_uint64 TJS_INTF_METHOD tTVPPartialStream::GetSize() //--------------------------------------------------------------------------- + +extern "C" { +#include "libarchive/archive.h" +#include "libarchive/archive_entry.h" +} +#if 0 +class LibArchive_Archive : public tTVPArchive { + struct archive *_arc; + tTJSBinaryStream *_stream; + static const int BUFFER_SIZE = 16 * 1024; + tjs_uint8 *_buffer = new tjs_uint8[BUFFER_SIZE]; + std::vector > _filelist; + + void Clear() { + if (_arc) { + archive_read_free(_arc); + _arc = nullptr; + } + for (std::pair &it : _filelist) { + archive_entry_free(it.second); + } + _filelist.clear(); + } + +public: + LibArchive_Archive(const ttstr & name, tTJSBinaryStream *st) : tTVPArchive(name), _stream(st) { + _arc = archive_read_new(); + } + ~LibArchive_Archive() { + Clear(); + if (_stream) + delete _stream; + if (_buffer) + delete[] _buffer; + } + + virtual tjs_uint GetCount() { return _filelist.size(); } + virtual ttstr GetName(tjs_uint idx) { return _filelist[idx].first; } + virtual tTJSBinaryStream * CreateStreamByIndex(tjs_uint idx) { + struct archive_entry * entry = _filelist[idx].second; + tjs_uint64 fileSize = archive_entry_size(entry); + if (fileSize <= 0) return new tTVPMemoryStream(); + return nullptr; + } + + bool Open(bool normalizeFileName) { + archive_read_support_filter_all(_arc); + archive_read_support_format_all(_arc); + archive_read_set_seek_callback(_arc, seek_callback); + archive_read_open2(_arc, this, nullptr, read_callback, nullptr, nullptr); + + struct archive_entry *entry = archive_entry_new2(_arc); + while (archive_read_next_header2(_arc, entry) == ARCHIVE_OK) { + ttstr filename(archive_entry_pathname_utf8(entry)); + if (normalizeFileName) + NormalizeInArchiveStorageName(filename); + _filelist.emplace_back(filename, entry); + entry = archive_entry_new2(_arc); + } + archive_entry_free(entry); + if (normalizeFileName) { + std::sort(_filelist.begin(), _filelist.end(), [](const std::pair& a, const std::pair& b) { + return a.first < b.first; + }); + } + } + + void Detach() { + Clear(); + _stream = nullptr; + } + + static la_ssize_t read_callback(struct archive *, void *_client_data, const void **_buffer) { + LibArchive_Archive *_this = (LibArchive_Archive *)_client_data; + *_buffer = _this->_buffer; + return _this->_stream->Read(_this->_buffer, BUFFER_SIZE); + } + + static la_int64_t seek_callback(struct archive *, void *_client_data, la_int64_t offset, int whence) { + LibArchive_Archive *_this = (LibArchive_Archive *)_client_data; + return _this->_stream->Seek(offset, whence); + } + + static la_int64_t skip_callback(struct archive *, void *_client_data, la_int64_t request) { + LibArchive_Archive *_this = (LibArchive_Archive *)_client_data; + return _this->_stream->Seek(request, TJS_BS_SEEK_CUR); + } +}; + +tTVPArchive *TVPOpenLibArchive(const ttstr & name, tTJSBinaryStream *st, bool normalizeFileName) { + LibArchive_Archive *arc = new LibArchive_Archive(name, st); + if (arc->Open(normalizeFileName)) { + return arc; + } + arc->Detach(); + delete arc; + return nullptr; +} +#endif + +static FILE *_fileopen(ttstr path) { + std::string strpath = path.AsStdString(); + FILE *fp = fopen(strpath.c_str(), "wb"); + if (!fp) { // make dirs + path = TVPExtractStoragePath(path); + TVPCreateFolders(path); + fp = fopen(strpath.c_str(), "wb"); + } + return fp; +} + +class tTVPUnpackArchiveImpl { + std::thread *ThreadObj; + std::mutex Mutex; + std::condition_variable Cond; + tTVPUnpackArchive *Owner; + + void Entry() { + { + std::unique_lock lk(Mutex); + Cond.wait(lk); + } + Owner->Process(); + } + +public: + tTVPUnpackArchiveImpl(tTVPUnpackArchive *owner) : Owner(owner) { + ThreadObj = new std::thread(&tTVPUnpackArchiveImpl::Entry, this); + } + + ~tTVPUnpackArchiveImpl() { + if (ThreadObj->joinable()) { + ThreadObj->join(); + } + delete ThreadObj; + } + + void Start() { + Cond.notify_all(); + } +}; + +int tTVPUnpackArchive::Prepare(const std::string &path, const std::string &_outpath, tjs_uint64 *totalSize) { + FpIn = fopen(path.c_str(), "rb"); + if (!FpIn) return -1; + OutPath = _outpath + "/"; + int file_count = 0; + tjs_uint64 size_count = 0; + ArcObj = archive_read_new(); + archive_read_support_filter_all(ArcObj); + archive_read_support_format_all(ArcObj); + int r = archive_read_open_FILE(ArcObj, FpIn); + if (r < ARCHIVE_OK) { + fclose(FpIn); + FpIn = nullptr; + archive_read_free(ArcObj); + ArcObj = nullptr; + // try TVPArchive + pTVPArc = TVPOpenArchive(path, false); + if (pTVPArc) { + file_count = pTVPArc->GetCount(); + *totalSize = 0; + for (int i = 0; i < file_count; ++i) { + tTJSBinaryStream *str = pTVPArc->CreateStreamByIndex(i); + if (str) { + *totalSize += str->GetSize(); + delete str; + } + } + if (file_count) { + Impl = new tTVPUnpackArchiveImpl(this); + } + return file_count; + } + return -1; + } + r = 0; + while (true) { + struct archive_entry *entry; + int r = archive_read_next_header(ArcObj, &entry); + if (r == ARCHIVE_EOF) { + r = ARCHIVE_OK; + break; + } + if (r < ARCHIVE_OK) + OnError(r, archive_error_string(ArcObj)); + if (r < ARCHIVE_WARN) + break; + ++file_count; + size_count += archive_entry_size(entry); + } + + if (totalSize) *totalSize = size_count; + archive_read_close(ArcObj); + archive_read_free(ArcObj); + ArcObj = nullptr; + if (file_count) { + Impl = new tTVPUnpackArchiveImpl(this); + } + return file_count; +} + +void tTVPUnpackArchive::Start() +{ + StopRequired = false; + Impl->Start(); +} + +void tTVPUnpackArchive::Stop() +{ + StopRequired = true; + Impl->Start(); +} + +void tTVPUnpackArchive::Process() +{ + if (StopRequired) + return; + + tjs_uint64 total_size = 0; + if (pTVPArc) { + int file_count = pTVPArc->GetCount(); + std::vector buffer; buffer.resize(4 * 1024 * 1024); + for (int index = 0; index < file_count && !StopRequired; ++index) { + tjs_uint64 file_size = 0; + std::string filename = pTVPArc->GetName(index).AsStdString(); + if (filename.size() > 600) continue; + std::string fullpath = OutPath + filename; + FILE *fp = _fileopen(fullpath); + if (!fp) { + OnError(ARCHIVE_FAILED, "Cannot open output file"); + break; + } + tTJSBinaryStream *str = pTVPArc->CreateStreamByIndex(index); + if (!str) { + OnError(ARCHIVE_FAILED, "Cannot open archive stream"); + fclose(fp); + break; + } + OnNewFile(index, filename.c_str(), str->GetSize()); + while (!StopRequired) { + tjs_uint readed = str->Read(&buffer.front(), buffer.size()); + if (readed == 0) break; + if (readed != fwrite(&buffer.front(), 1, readed, fp)) { + OnError(ARCHIVE_FAILED, "Fail to write file.\nPlease check the disk space."); + break; + } + file_size += readed; + total_size += readed; + OnProgress(total_size, file_size); + if (readed < buffer.size()) + break; + } + delete str; + fclose(fp); + } + pTVPArc->Release(); + pTVPArc = nullptr; + OnEnded(); + return; + } + + fseek(FpIn, 0, SEEK_SET); + ArcObj = archive_read_new(); + archive_read_support_filter_all(ArcObj); + archive_read_support_format_all(ArcObj); + int r = archive_read_open_FILE(ArcObj, FpIn); + if (r < ARCHIVE_OK) { + OnError(r, archive_error_string(ArcObj)); + OnEnded(); + return; + } + for (int index = 0; !StopRequired; ++index) { + struct archive_entry *entry; + int r = archive_read_next_header(ArcObj, &entry); + if (r == ARCHIVE_EOF) { + r = ARCHIVE_OK; + break; + } + if (r < ARCHIVE_OK) + OnError(r, archive_error_string(ArcObj)); + if (r < ARCHIVE_WARN) + break; + const char *filename = archive_entry_pathname_utf8(entry); + std::string fullpath = OutPath + filename; + FILE *fp = _fileopen(fullpath); + if (!fp) { + OnError(ARCHIVE_FAILED, "Cannot open output file"); + break; + } + OnNewFile(index, filename, archive_entry_size(entry)); + + const void *buff; + size_t size; + la_int64_t offset; + tjs_uint64 file_size = 0; + const char *errmsg; + while (!StopRequired) { + r = archive_read_data_block(ArcObj, &buff, &size, &offset); + if (r == ARCHIVE_EOF) { + r = ARCHIVE_OK; + break; + } + if (r < ARCHIVE_OK) { + errmsg = archive_error_string(ArcObj); + break; + } + if (size != fwrite(buff, 1, size, fp)) { + r = ARCHIVE_FAILED; + errmsg = "Fail to write file.\nPlease check the disk space."; + break; + } + file_size += size; + total_size += size; + OnProgress(total_size, file_size); + } + fclose(fp); + if (r < ARCHIVE_OK) + OnError(r, errmsg); + if (r < ARCHIVE_WARN) + break; + if (archive_entry_mtime_is_set(entry)) { + TVP_utime(fullpath.c_str(), archive_entry_mtime(entry)); + } + } + archive_read_close(ArcObj); + OnEnded(); +} + +tTVPUnpackArchive::tTVPUnpackArchive() +{ +} + +tTVPUnpackArchive::~tTVPUnpackArchive() +{ + if (Impl) delete Impl; + if (FpIn) { + fclose(FpIn); + FpIn = nullptr; + } + if (ArcObj) { + archive_read_close(ArcObj); + archive_free(ArcObj); + } + if (pTVPArc) + pTVPArc->Release(); +} diff --git a/src/core/base/UtilStreams.h b/src/core/base/UtilStreams.h index 1598703f..2f6b2ed7 100644 --- a/src/core/base/UtilStreams.h +++ b/src/core/base/UtilStreams.h @@ -13,6 +13,7 @@ #include "StorageIntf.h" +#include @@ -174,7 +175,59 @@ class tTVPPartialStream : public tTJSBinaryStream }; //--------------------------------------------------------------------------- +class tTVPUnpackArchiveImpl; +class tTVPUnpackArchive { +public: + tTVPUnpackArchive(); + virtual ~tTVPUnpackArchive(); // must ve deconstructed from main thread + int Prepare(const std::string &path, const std::string &_outpath, tjs_uint64 *totalSize); + void Start(); + void Stop(); + + void SetCallback( + const std::function &funcOnEnded, + const std::function &funcOnError, + const std::function &funcOnProgress, + const std::function &funcOnNewFile) { + FuncOnEnded = funcOnEnded; + FuncOnError = funcOnError; + FuncOnProgress = funcOnProgress; + FuncOnNewFile = funcOnNewFile; + } +protected: + // these callbacks are not in main thread ! + virtual void OnEnded() { + if (FuncOnEnded) + FuncOnEnded(); + } + virtual void OnError(int err, const char *msg) { + if (FuncOnError) + FuncOnError(err, msg); + } + virtual void OnProgress(tjs_uint64 total_size, tjs_uint64 file_size) { + if (FuncOnProgress) + FuncOnProgress(total_size, file_size); + } + virtual void OnNewFile(int idx, const char * utf8name, tjs_uint64 file_size) { + if (FuncOnNewFile) + FuncOnNewFile(idx, utf8name, file_size); + } +private: + void Process(); + FILE *FpIn = nullptr; + struct archive *ArcObj = nullptr; + std::string OutPath; + friend class tTVPUnpackArchiveImpl; + tTVPUnpackArchiveImpl *Impl = nullptr; + tTVPArchive *pTVPArc = nullptr; + bool StopRequired = false; + + std::function FuncOnEnded; + std::function FuncOnError; + std::function FuncOnProgress; + std::function FuncOnNewFile; +}; #endif diff --git a/src/core/base/XP3Archive.cpp b/src/core/base/XP3Archive.cpp index 96c17621..5b9e7773 100644 --- a/src/core/base/XP3Archive.cpp +++ b/src/core/base/XP3Archive.cpp @@ -526,7 +526,8 @@ void tTVPXP3Archive::Init(tTJSBinaryStream *st, tjs_int64 off, bool normalizeNam } // sort item vector by its name (required for tTVPArchive specification) - std::stable_sort(ItemVector.begin(), ItemVector.end()); + if(normalizeName) + std::stable_sort(ItemVector.begin(), ItemVector.end()); } catch(...) { @@ -542,10 +543,10 @@ void tTVPXP3Archive::Init(tTJSBinaryStream *st, tjs_int64 off, bool normalizeNam } //--------------------------------------------------------------------------- -tTVPXP3Archive::tTVPXP3Archive(const ttstr & name, tTJSBinaryStream *st, tjs_int64 offset) : tTVPArchive(name) +tTVPXP3Archive::tTVPXP3Archive(const ttstr & name, tTJSBinaryStream *st, tjs_int64 offset, bool normalizeFileName) : tTVPArchive(name) { if (!st) st = TVPCreateStream(name); - Init(st, offset); + Init(st, offset, normalizeFileName); } //--------------------------------------------------------------------------- tTVPXP3Archive::~tTVPXP3Archive() @@ -553,7 +554,7 @@ tTVPXP3Archive::~tTVPXP3Archive() TVPFreeArchiveHandlePoolByPointer(this); } -tTVPArchive * tTVPXP3Archive::Create(const ttstr & name, tTJSBinaryStream *st) +tTVPArchive * tTVPXP3Archive::Create(const ttstr & name, tTJSBinaryStream *st, bool normalizeFileName) { bool refStream = st; if (!st) { @@ -564,7 +565,7 @@ tTVPArchive * tTVPXP3Archive::Create(const ttstr & name, tTJSBinaryStream *st) if (!refStream) delete st; return nullptr; } - return new tTVPXP3Archive(name, st, offset); + return new tTVPXP3Archive(name, st, offset, normalizeFileName); } //--------------------------------------------------------------------------- diff --git a/src/core/base/XP3Archive.h b/src/core/base/XP3Archive.h index 49beade4..cdae7099 100644 --- a/src/core/base/XP3Archive.h +++ b/src/core/base/XP3Archive.h @@ -115,10 +115,10 @@ class tTVPXP3Archive : public tTVPArchive public: tTVPXP3Archive(const ttstr & name, int) : tTVPArchive(name) {} - tTVPXP3Archive(const ttstr & name, tTJSBinaryStream *st = nullptr, tjs_int64 offset = -1); + tTVPXP3Archive(const ttstr & name, tTJSBinaryStream *st = nullptr, tjs_int64 offset = -1, bool normalizeFileName = true); ~tTVPXP3Archive(); - static tTVPArchive *Create(const ttstr & name, tTJSBinaryStream *st = nullptr); + static tTVPArchive *Create(const ttstr & name, tTJSBinaryStream *st = nullptr, bool normalizeFileName = true); tjs_uint GetCount() { return Count; } const ttstr & GetName(tjs_uint idx) const { return ItemVector[idx].Name; } diff --git a/src/core/base/ZIPArchive.cpp b/src/core/base/ZIPArchive.cpp index 75ed7078..68836f2f 100644 --- a/src/core/base/ZIPArchive.cpp +++ b/src/core/base/ZIPArchive.cpp @@ -196,25 +196,13 @@ static voidpf zip_open64file(voidpf opaque, const void * filename, int mode) { return nullptr; } -static uLong zip_readfile(voidpf, voidpf s, void *buf, uLong size) { - return ((tTJSBinaryStream*)s)->Read(buf, size); -} - -static uLong zip_writefile(voidpf, voidpf s, const void *buf, uLong size) { - return ((tTJSBinaryStream*)s)->Write(buf, size); -} - -static ZPOS64_T zip_tell64file(voidpf, voidpf s) { - return ((tTJSBinaryStream*)s)->GetPosition(); -} - -static long zip_seek64file(voidpf, voidpf s, ZPOS64_T offset, int origin) { - ((tTJSBinaryStream*)s)->Seek(offset, origin); - return 0; -} +static uLong zip_readfile(voidpf, voidpf s, void *buf, uLong size); +static uLong zip_writefile(voidpf, voidpf s, const void *buf, uLong size); +static ZPOS64_T zip_tell64file(voidpf, voidpf s); +static long zip_seek64file(voidpf, voidpf s, ZPOS64_T offset, int origin); static int zip_closefile(voidpf, voidpf s) { - delete ((tTJSBinaryStream*)s); +// delete ((tTJSBinaryStream*)s); return 0; } @@ -2060,21 +2048,41 @@ class ZipArchive : public tTVPArchive { public: ~ZipArchive(); - ZipArchive(const ttstr & name, tTJSBinaryStream *st); + ZipArchive(const ttstr & name, tTJSBinaryStream *st, bool normalizeFileName); bool isValid() { return uf != nullptr; } virtual tjs_uint GetCount() { return filelist.size(); } virtual ttstr GetName(tjs_uint idx) { return filelist[idx].first; } virtual tTJSBinaryStream * CreateStreamByIndex(tjs_uint idx); + + tTJSBinaryStream *_st = nullptr; }; -tTVPArchive * TVPOpenZIPArchive(const ttstr & name, tTJSBinaryStream *st) { +static uLong zip_readfile(voidpf, voidpf s, void *buf, uLong size) { + return ((ZipArchive*)s)->_st->Read(buf, size); +} + +static uLong zip_writefile(voidpf, voidpf s, const void *buf, uLong size) { + return ((ZipArchive*)s)->_st->Write(buf, size); +} + +static ZPOS64_T zip_tell64file(voidpf, voidpf s) { + return ((ZipArchive*)s)->_st->GetPosition(); +} + +static long zip_seek64file(voidpf, voidpf s, ZPOS64_T offset, int origin) { + ((ZipArchive*)s)->_st->Seek(offset, origin); + return 0; +} + +tTVPArchive * TVPOpenZIPArchive(const ttstr & name, tTJSBinaryStream *st, bool normalizeFileName) { tjs_uint64 pos = st->GetPosition(); bool checkZIP = st->ReadI16LE() == 0x4B50; // 'PK' st->SetPosition(pos); if (!checkZIP) return nullptr; - ZipArchive *arc = new ZipArchive(name, st); + ZipArchive *arc = new ZipArchive(name, st, normalizeFileName); if (!arc->isValid()) { + arc->_st = nullptr; delete arc; return nullptr; } @@ -2108,12 +2116,17 @@ ZipArchive::~ZipArchive() { unzClose(uf); uf = NULL; } + if (_st) { + delete _st; + _st = nullptr; + } } void storeFilename(ttstr &name, const char *narrowName, const ttstr &filename); -ZipArchive::ZipArchive(const ttstr & name, tTJSBinaryStream *st) : tTVPArchive(name) { +ZipArchive::ZipArchive(const ttstr & name, tTJSBinaryStream *st, bool normalizeFileName) : tTVPArchive(name) { if (!st) st = TVPCreateStream(name); - if ((uf = unzOpenInternal(st, &zipfunc, 1)) != NULL) { + _st = st; + if ((uf = unzOpenInternal(this, &zipfunc, 1)) != NULL) { //unzGoToFirstFile64(uf, NULL, NULL, 0); unz_file_info file_info; do { @@ -2123,14 +2136,17 @@ ZipArchive::ZipArchive(const ttstr & name, tTJSBinaryStream *st) : tTVPArchive(n char filename_inzip[1024]; if (unzGetCurrentFileInfo(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0) == UNZ_OK) { storeFilename(filename, filename_inzip, name); - NormalizeInArchiveStorageName(filename); + if (normalizeFileName) + NormalizeInArchiveStorageName(filename); filelist.emplace_back(filename, entry); } } } while (unzGoToNextFile64(uf, NULL, NULL, 0) == UNZ_OK); - std::sort(filelist.begin(), filelist.end(), [](const FileEntry& a, const FileEntry& b){ - return a.first < b.first; - }); + if (normalizeFileName) { + std::sort(filelist.begin(), filelist.end(), [](const FileEntry& a, const FileEntry& b) { + return a.first < b.first; + }); + } } } diff --git a/src/core/base/win32/StorageImpl.cpp b/src/core/base/win32/StorageImpl.cpp index c63424bf..0f9fba5e 100644 --- a/src/core/base/win32/StorageImpl.cpp +++ b/src/core/base/win32/StorageImpl.cpp @@ -153,6 +153,10 @@ void TVPGetLocalFileListAt(const ttstr &name, const std::functiond_name); + if (file.length() <= 2) { + if (file == TJS_W(".") || file == TJS_W("..")) + continue; + } tjs_char *p = file.Independ(); while (*p) { @@ -581,10 +585,10 @@ bool TVPCheckExistentLocalFolder(const ttstr &name) -tTVPArchive * TVPOpenZIPArchive(const ttstr & name, tTJSBinaryStream *st); -tTVPArchive * TVPOpen7ZArchive(const ttstr & name, tTJSBinaryStream *st); -tTVPArchive * TVPOpenTARArchive(const ttstr & name, tTJSBinaryStream *st); -static tTVPArchive*(*ArchiveCreators[])(const ttstr & name, tTJSBinaryStream *st) = { +tTVPArchive * TVPOpenZIPArchive(const ttstr & name, tTJSBinaryStream *st, bool normalizeFileName); +tTVPArchive * TVPOpen7ZArchive(const ttstr & name, tTJSBinaryStream *st, bool normalizeFileName); +tTVPArchive * TVPOpenTARArchive(const ttstr & name, tTJSBinaryStream *st, bool normalizeFileName); +static tTVPArchive*(*ArchiveCreators[])(const ttstr & name, tTJSBinaryStream *st, bool normalizeFileName) = { TVPOpenZIPArchive, TVPOpen7ZArchive, TVPOpenTARArchive, @@ -594,7 +598,7 @@ static tTVPArchive*(*ArchiveCreators[])(const ttstr & name, tTJSBinaryStream *st //--------------------------------------------------------------------------- // TVPOpenArchive //--------------------------------------------------------------------------- -tTVPArchive * TVPOpenArchive(const ttstr & name) +tTVPArchive * TVPOpenArchive(const ttstr & name, bool normalizeFileName) { #if 0 tTVPArchive * archive = TVPOpenSusieArchive(name); // in SusieArchive.h @@ -603,8 +607,8 @@ tTVPArchive * TVPOpenArchive(const ttstr & name) tTJSBinaryStream *st = TVPCreateStream(name); if (!st) return nullptr; for (int i = 0; i < sizeof(ArchiveCreators) / sizeof(ArchiveCreators[0]); ++i) { - tTVPArchive*(*creator)(const ttstr &, tTJSBinaryStream*) = ArchiveCreators[i]; - tTVPArchive * archive = creator(name, st); + tTVPArchive*(*creator)(const ttstr &, tTJSBinaryStream*, bool) = ArchiveCreators[i]; + tTVPArchive * archive = creator(name, st, normalizeFileName); if (archive) return archive; st->SetPosition(0); } @@ -617,7 +621,7 @@ int TVPCheckArchive(const ttstr &localname) tTVPArchive *arc = nullptr; int validArchive = 2; // archive but no startup.tjs try { - arc = TVPOpenArchive(TVPNormalizeStorageName(localname)); + arc = TVPOpenArchive(TVPNormalizeStorageName(localname), false); if (arc) { tjs_uint count = arc->GetCount(); ttstr str_startup_tjs = TJS_W("startup.tjs"); @@ -1504,3 +1508,30 @@ TJS_END_NATIVE_STATIC_METHOD_DECL_OUTER(/*object to register*/cls, } //--------------------------------------------------------------------------- +static FILE *_fileopen(ttstr path) { + std::string strpath = path.AsStdString(); + FILE *fp = fopen(strpath.c_str(), "wb"); + if (!fp) { // make dirs + path = TVPExtractStoragePath(path); + TVPCreateFolders(path); + fp = fopen(strpath.c_str(), "wb"); + } + return fp; +} + +bool TVPSaveStreamToFile(tTJSBinaryStream *st, tjs_uint64 offset, tjs_uint64 size, ttstr outpath) { + FILE *fp = _fileopen(outpath); + if (!fp) return false; + tjs_uint64 origpos = st->GetPosition(); + st->SetPosition(offset); + std::vector buffer; buffer.resize(2 * 1024 * 1024); + while (size > 0) { + unsigned int readsize = size > buffer.size() ? buffer.size() : size; + readsize = st->Read(&buffer.front(), readsize); + fwrite(&buffer.front(), 1, readsize, fp); + size -= readsize; + } + fclose(fp); + st->SetPosition(origpos); + return true; +} diff --git a/src/core/base/win32/StorageImpl.h b/src/core/base/win32/StorageImpl.h index 31c7cf0b..280ef117 100644 --- a/src/core/base/win32/StorageImpl.h +++ b/src/core/base/win32/StorageImpl.h @@ -199,4 +199,5 @@ class tTVPPluginHolder //--------------------------------------------------------------------------- void TVPListDir(const std::string &folder, std::function cb); +bool TVPSaveStreamToFile(tTJSBinaryStream *st, tjs_uint64 offset, tjs_uint64 size, ttstr outpath); #endif diff --git a/src/core/environ/ConfigManager/LocaleConfigManager.cpp b/src/core/environ/ConfigManager/LocaleConfigManager.cpp index 1d3019cf..b6329ba6 100644 --- a/src/core/environ/ConfigManager/LocaleConfigManager.cpp +++ b/src/core/environ/ConfigManager/LocaleConfigManager.cpp @@ -61,6 +61,12 @@ bool LocaleConfigManager::initText(cocos2d::ui::Text *ctrl) { return initText(ctrl, ctrl->getString()); } +bool LocaleConfigManager::initText(cocos2d::ui::Button *ctrl) +{ + if (!ctrl) return false; + return initText(ctrl, ctrl->getTitleText()); +} + bool LocaleConfigManager::initText(cocos2d::ui::Text *ctrl, const std::string &tid) { if (!ctrl) return false; diff --git a/src/core/environ/ConfigManager/LocaleConfigManager.h b/src/core/environ/ConfigManager/LocaleConfigManager.h index a01045f1..629ce5af 100644 --- a/src/core/environ/ConfigManager/LocaleConfigManager.h +++ b/src/core/environ/ConfigManager/LocaleConfigManager.h @@ -29,6 +29,7 @@ class LocaleConfigManager { const std::string &GetText(const std::string &tid); // in utf8 bool initText(cocos2d::ui::Text *ctrl); + bool initText(cocos2d::ui::Button *ctrl); bool initText(cocos2d::ui::Text *ctrl, const std::string &tid); bool initText(cocos2d::ui::Button *ctrl, const std::string &tid); diff --git a/src/core/environ/DetectCPU.cpp b/src/core/environ/DetectCPU.cpp index c24b5dc8..63bea201 100644 --- a/src/core/environ/DetectCPU.cpp +++ b/src/core/environ/DetectCPU.cpp @@ -148,6 +148,7 @@ static ttstr TVPDumpCPUInfo(tjs_int cpu_num) //--------------------------------------------------------------------------- static void TVPDisableCPU(tjs_uint32 featurebit, const tjs_char *name) { +#if 0 tTJSVariant val; ttstr str; if(TVPGetCommandLine(name, &val)) @@ -158,6 +159,7 @@ static void TVPDisableCPU(tjs_uint32 featurebit, const tjs_char *name) else if(str == TJS_W("force")) TVPCPUType |= featurebit; } +#endif } #if defined(WIN32) || defined(__ANDROID__) diff --git a/src/core/environ/Platform.h b/src/core/environ/Platform.h index 37cb72d9..48fcf283 100644 --- a/src/core/environ/Platform.h +++ b/src/core/environ/Platform.h @@ -54,4 +54,4 @@ struct tTVP_stat { bool TVP_stat(const tjs_char *name, tTVP_stat &s); bool TVP_stat(const char *name, tTVP_stat &s); - +void TVP_utime(const char *name, time_t modtime); diff --git a/src/core/environ/XP3ArchiveRepack.cpp b/src/core/environ/XP3ArchiveRepack.cpp index 0280ff9e..b0c7faee 100644 --- a/src/core/environ/XP3ArchiveRepack.cpp +++ b/src/core/environ/XP3ArchiveRepack.cpp @@ -16,19 +16,30 @@ extern "C" { #include "win32io.h" #include "GraphicsLoaderIntf.h" #include "ogl/etcpak.h" +#include +#include "LayerIntf.h" +#include "MsgIntf.h" +#include "ncbind/ncbind.hpp" +#include "visual/tvpgl_asm_init.h" +#include "DetectCPU.h" +#include using namespace TJS; using namespace NArchive::N7z; #define COMPRESS_THRESHOLD 4 * 1024 * 1024 +#define S_INTERRUPT ((HRESULT)0x114705) void TVPSetXP3FilterScript(ttstr content); +void TVPSavePVRv3(void* formatdata, tTJSBinaryStream* dst, const iTVPBaseBitmap* image, const ttstr & mode, iTJSDispatch2* meta); class tTVPXP3ArchiveEx : public tTVPXP3Archive { public: tTVPXP3ArchiveEx(const ttstr & name, tTJSBinaryStream *st) : tTVPXP3Archive(name, 0) { Init(st, -1, false); } + + uint64_t TotalSize = 0; }; class OutStreamFor7z : public IOutStream { @@ -37,7 +48,7 @@ class OutStreamFor7z : public IOutStream { public: OutStreamFor7z(const std::string &path) { - open(path.c_str(), O_RDWR | O_CREAT, 0666); + fp = open(path.c_str(), O_RDWR | O_CREAT, 0666); } ~OutStreamFor7z() { close(fp); @@ -144,19 +155,142 @@ class SequentialInStreamFor7z : public ISequentialInStream { } }; -XP3ArchiveRepackAsync::XP3ArchiveRepackAsync(const std::string &xp3filter) +class XP3ArchiveRepackAsyncImpl + : ICompressProgressInfo +{ +public: + XP3ArchiveRepackAsyncImpl(); + ~XP3ArchiveRepackAsyncImpl(); + void Clear(); + void Start(); + void Stop() { bStopRequired = true; } + void DoConv(); + uint64_t AddTask(const std::string &src); + void SetXP3Filter(const std::string &xp3filter); + void SetCallback( + const std::function &onNewFile, + const std::function &onNewArchive, + const std::function &onProgress, + const std::function &onError, + const std::function &onEnded) { + OnNewFile = onNewFile; + OnNewArchive = onNewArchive; + OnProgress = onProgress; + OnError = onError; + OnEnded = onEnded; + } + void SetOption(const std::string &name, bool v); + +public: + bool OptionUsingETC2 = false; + bool OptionMergeMaskImg = true; + +private: + HRESULT AddTo7zArchive(tTJSBinaryStream* s, const ttstr &_naem); + + // ICompressProgressInfo + virtual HRESULT SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) override; + // IUnknown + virtual HRESULT QueryInterface(REFIID riid, void **ppvObject) override { return E_NOTIMPL; } + virtual ULONG AddRef(void) override { return 0; } + virtual ULONG Release(void) override { return 0; } + + std::vector > ConvFileList; + std::thread* Thread = nullptr; + CCreatedCoder CoderCompress, CoderCopy; + static const int nCodecMethod = 0x30101, // LZMA + nCodecMethodCopy = 0; + CArchiveDatabaseOut newDatabase; + OutStreamFor7z *strout; + CByteBuffer props; + bool bStopRequired = false; + uint64_t nTotalSize = 0, nArcSize = 0; + + std::function OnNewFile; + std::function OnNewArchive; + std::function OnProgress; + std::function OnError; + std::function OnEnded; +}; + +XP3ArchiveRepackAsync::XP3ArchiveRepackAsync() + : _impl(new XP3ArchiveRepackAsyncImpl()) +{ + TVPDetectCPU(); + TVPGL_ASM_Init(); +} + +XP3ArchiveRepackAsync::~XP3ArchiveRepackAsync() { + delete _impl; +} + +uint64_t XP3ArchiveRepackAsync::AddTask(const std::string &src) { + return _impl->AddTask(src); +} + +void XP3ArchiveRepackAsync::Start() +{ + _impl->Start(); +} + +void XP3ArchiveRepackAsync::Stop() +{ + _impl->Stop(); +} + +void XP3ArchiveRepackAsync::SetXP3Filter(const std::string &xp3filter) +{ + _impl->SetXP3Filter(xp3filter); +} + +void XP3ArchiveRepackAsync::SetCallback( + const std::function &onNewFile, + const std::function &onNewArchive, + const std::function &onProgress, + const std::function &onError, + const std::function &onEnded) +{ + _impl->SetCallback(onNewFile, onNewArchive, onProgress, onError, onEnded); +} + +void XP3ArchiveRepackAsync::SetOption(const std::string &name, bool v) +{ + _impl->SetOption(name, v); +} + +XP3ArchiveRepackAsyncImpl::XP3ArchiveRepackAsyncImpl() { if (!g_CrcTable[1]) CrcGenerateTable(); - ttstr xp3filterScript; - iTJSTextReadStream * stream = TVPCreateTextStreamForRead(xp3filter, ""); - stream->Read(xp3filterScript, 0); - delete stream; - TVPSetXP3FilterScript(xp3filterScript); + CreateCoder(nCodecMethod, true, CoderCompress); + CreateCoder(nCodecMethodCopy, true, CoderCopy); + + CMyComPtr writeCoderProperties; + CMyComPtr setCoderProperties; + CoderCompress.Coder->QueryInterface(IID_ICompressWriteCoderProperties, (void **)&writeCoderProperties); + CoderCompress.Coder->QueryInterface(IID_ICompressSetCoderProperties, (void **)&setCoderProperties); + + if (setCoderProperties) { + const int nProp = 1; + PROPVARIANT propvars[nProp]; + PROPID props[nProp] = { + NCoderPropID::kDictionarySize, + }; + propvars[0].vt = VT_UI4; + propvars[0].ulVal = 4 * 1024 * 1024; + setCoderProperties->SetCoderProperties(props, propvars, nProp); + } + if (writeCoderProperties) { + CDynBufSeqOutStream *outStreamSpec = new CDynBufSeqOutStream; + CMyComPtr dynOutStream(outStreamSpec); + outStreamSpec->Init(); + writeCoderProperties->WriteCoderProperties(dynOutStream); + outStreamSpec->CopyToBuffer(props); + } } -XP3ArchiveRepackAsync::~XP3ArchiveRepackAsync() +XP3ArchiveRepackAsyncImpl::~XP3ArchiveRepackAsyncImpl() { - std::thread* t = (std::thread*)Thread; + std::thread* t = Thread; if (t && t->joinable()) { t->join(); delete t; @@ -164,273 +298,461 @@ XP3ArchiveRepackAsync::~XP3ArchiveRepackAsync() Clear(); } -uint64_t XP3ArchiveRepackAsync::AddTask(const std::string &src/*, const std::string &dst*/) +uint64_t XP3ArchiveRepackAsyncImpl::AddTask(const std::string &src/*, const std::string &dst*/) { - uint64_t totalSize = 0; ttstr path(src); tTVPLocalFileStream *str = new tTVPLocalFileStream(path, path, TJS_BS_READ); tTVPXP3ArchiveEx *xp3arc = new tTVPXP3ArchiveEx(path, str); for (const tTVPXP3Archive::tArchiveItem &item : xp3arc->ItemVector) { - totalSize += item.OrgSize; + xp3arc->TotalSize += item.OrgSize; } ConvFileList.emplace_back(xp3arc, src); - return totalSize; + return xp3arc->TotalSize; +} + +void XP3ArchiveRepackAsyncImpl::SetXP3Filter(const std::string &xp3filter) +{ + ttstr xp3filterScript; + iTJSTextReadStream * stream = TVPCreateTextStreamForRead(xp3filter, ""); + stream->Read(xp3filterScript, 0); + delete stream; + TVPSetXP3FilterScript(xp3filterScript); } -void XP3ArchiveRepackAsync::Clear() +void XP3ArchiveRepackAsyncImpl::SetOption(const std::string &name, bool v) +{ + if (name == "merge_mask_img") { + OptionMergeMaskImg = v; + } else if (name == "conv_etc2") { + OptionUsingETC2 = v; + } +} + +void XP3ArchiveRepackAsyncImpl::Clear() { for (auto & it : ConvFileList) { - delete it.first; + if (it.first) + delete it.first; } ConvFileList.clear(); TVPSetXP3FilterScript(TJS_W("")); } -void XP3ArchiveRepackAsync::Start() +void XP3ArchiveRepackAsyncImpl::Start() { if (!Thread) { - Thread = new std::thread(std::bind(&XP3ArchiveRepackAsync::DoConv, this)); + Thread = new std::thread(std::bind(&XP3ArchiveRepackAsyncImpl::DoConv, this)); + } +} + +static bool CheckIsImage(tTJSBinaryStream *s) { + // convert image > 8k + tjs_uint8 header[16]; + tjs_uint readed = s->Read(header, 16); + s->SetPosition(0); + if (readed != 16) return false; + if (!memcmp(header, "BM", 2)) { + return true; + } else if (!memcmp(header, "\x89PNG", 4)) { + return true; + } else if (!memcmp(header, "\xFF\xD8\xFF", 3)) { // JPEG + return true; + } else if (!memcmp(header, "TLG", 3)) { + return true; + } else if (!memcmp(header, "\x49\x49\xbc\x01", 4)) { // JXR + return true; + } else if (!memcmp(header, "BPG", 3)) { + return true; + } else if (!memcmp(header, "RIFF", 4)) { + if (!memcmp(header + 8, "WEBPVP8", 7)) { + return true; + } } + return false; } -void XP3ArchiveRepackAsync::DoConv() +HRESULT XP3ArchiveRepackAsyncImpl::AddTo7zArchive(tTJSBinaryStream* s, const ttstr &_name) { + CFileItem file; + CFileItem2 file2; + SequentialInStreamFor7z str(s); + tjs_uint64 origSize = s->GetSize(); + file.Size = origSize; + file2.Init(); + UString name(_name.c_str()); + UInt64 inSizeForReduce = origSize; + UInt64 newSize = origSize; + bool compressable = origSize > 64 && origSize < 4 * 1024 * 1024; + if (compressable) { + // compressable quick check + tjs_uint8 header[16]; + s->Read(header, 16); + if (!memcmp(header, "\x89PNG", 4)) { + compressable = false; + } else if (!memcmp(header, "OggS\x00", 5)) { + compressable = false; + } else if (!memcmp(header, "\xFF\xD8\xFF", 3)) { // JPEG + compressable = false; + } else if (!memcmp(header, "TLG", 3)) { + compressable = false; + } else if (!memcmp(header, "0&\xB2\x75\x8E\x66\xCF\x11", 8)) { // wmv/wma + compressable = false; + } else if (!memcmp(header, "AJPM", 4)) { // AMV + compressable = false; + } else if (!memcmp(header, "\x49\x49\xbc\x01", 4)) { // JXR + compressable = false; + } else if (!memcmp(header, "BPG", 3)) { + compressable = false; + } else if (!memcmp(header, "RIFF", 4)) { + if (!memcmp(header + 8, "WEBPVP8", 7)) { + compressable = false; + } + } + s->SetPosition(0); + } + HRESULT ret; + if (compressable) { + UInt64 expectedSize = origSize * 4 / 5; + OutStreamMemory tmp; + if ((ret = CoderCompress.Coder->Code(&str, &tmp, &inSizeForReduce, &newSize, this)) != S_OK) + return ret; + if (tmp.GetSize() < expectedSize) { + UInt32 writed; + if ((ret = strout->Write(tmp.GetInternalBuffer(), tmp.GetSize(), &writed)) != S_OK) { + return ret; + } + if (writed != tmp.GetSize()) + return E_FAIL; + newDatabase.PackSizes.Add(tmp.GetSize()); + newDatabase.CoderUnpackSizes.Add(origSize); + newDatabase.NumUnpackStreamsVector.Add(1); + CFolder &folder = newDatabase.Folders.AddNew(); + folder.Bonds.SetSize(0); + folder.Coders.SetSize(1); + CCoderInfo &cod = folder.Coders[0]; + cod.MethodID = nCodecMethod; + cod.NumStreams = 1; + cod.Props = props; + folder.PackStreams.SetSize(1); + folder.PackStreams[0] = 0; // only 1 stream + newDatabase.AddFile(file, file2, name); + return S_OK; + } + s->SetPosition(0); + } + // direct copy + newSize = origSize; + if ((ret = CoderCopy.Coder->Code(&str, strout, &inSizeForReduce, &newSize, this)) != S_OK) { + return ret; + } + newDatabase.PackSizes.Add(s->GetSize()); + newDatabase.CoderUnpackSizes.Add(origSize); + newDatabase.NumUnpackStreamsVector.Add(1); + CFolder &folder = newDatabase.Folders.AddNew(); + folder.Bonds.SetSize(0); + folder.Coders.SetSize(1); + CCoderInfo &cod = folder.Coders[0]; + cod.MethodID = nCodecMethodCopy; + cod.NumStreams = 1; + folder.PackStreams.SetSize(1); + folder.PackStreams[0] = 0; // only 1 stream + newDatabase.AddFile(file, file2, name); + return S_OK; +} + +HRESULT XP3ArchiveRepackAsyncImpl::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) { - for (auto &it : ConvFileList) { + if (OnProgress) { + OnProgress(nTotalSize + *inSize, nArcSize + *inSize, *inSize); + } + return bStopRequired ? S_INTERRUPT : S_OK; +} + +void XP3ArchiveRepackAsyncImpl::DoConv() +{ + nTotalSize = 0; + for (unsigned int arcidx = 0; arcidx < ConvFileList.size(); ++arcidx) { + auto &it = ConvFileList[arcidx]; + if (bStopRequired) break; + if (OnNewArchive) + OnNewArchive(arcidx, it.first->TotalSize, it.second); + tTVPXP3ArchiveEx *xp3arc = it.first; tjs_uint totalFile = xp3arc->GetCount(); std::string tmpname = it.second + ".7z"; - OutStreamFor7z *strout = new OutStreamFor7z(tmpname); - - const int nCodecMethod = 0x30101, // LZMA - nCodecMethodCopy = 0; - CCreatedCoder coder, coderCopy; - CreateCoder(nCodecMethod, true, coder); - CreateCoder(nCodecMethodCopy, true, coderCopy); - CMyComPtr writeCoderProperties; - CMyComPtr setCoderProperties; - coder.Coder->QueryInterface(IID_ICompressWriteCoderProperties, (void **)&writeCoderProperties); - coder.Coder->QueryInterface(IID_ICompressSetCoderProperties, (void **)&setCoderProperties); - CByteBuffer props; - - if (setCoderProperties) { - const int nProp = 1; - PROPVARIANT propvars[nProp]; - PROPID props[nProp] = { - NCoderPropID::kDictionarySize, - }; - propvars[0].vt = VT_UI4; - propvars[0].ulVal = 4 * 1024 * 1024; - setCoderProperties->SetCoderProperties(props, propvars, nProp); - } - if (writeCoderProperties) { - CDynBufSeqOutStream *outStreamSpec = new CDynBufSeqOutStream; - CMyComPtr dynOutStream(outStreamSpec); - outStreamSpec->Init(); - writeCoderProperties->WriteCoderProperties(dynOutStream); - outStreamSpec->CopyToBuffer(props); - } + strout = new OutStreamFor7z(tmpname); COutArchive archive; - archive.Create(strout, false); + if (archive.Create(strout, false) != S_OK) { + if (OnError) + OnError(-1, "Fail to create output archive."); + break; + } archive.SkipPrefixArchiveHeader(); - CArchiveDatabaseOut newDatabase; - - for (const tTVPXP3Archive::tArchiveItem &item : xp3arc->ItemVector) { - if (!item.OrgSize) { - CFileItem file; - CFileItem2 file2; - file2.Init(); - UString name(item.Name.c_str()); - file.Size = item.OrgSize; - file.HasStream = false; - newDatabase.AddFile(file, file2, name); + newDatabase.Clear(); + + nArcSize = 0; + + // filter image files , img_mask = -1 means no mask available or normal file + std::map imglist; + std::vector filelist; // normal files + { + std::unordered_multimap allFileName; + std::vector allfilelist; + std::set masklist; + for (tjs_uint idx = 0; idx < xp3arc->ItemVector.size(); ++idx) { + const tTVPXP3Archive::tArchiveItem &item = xp3arc->ItemVector[idx]; + if (!item.OrgSize) { + // add empty files first + CFileItem file; + CFileItem2 file2; + file2.Init(); + UString name(item.Name.c_str()); + file.Size = item.OrgSize; + file.HasStream = false; + newDatabase.AddFile(file, file2, name); + continue; + } + if (item.Name.length() > 256 && item.Name.StartsWith(TJS_W("$$$"))) + continue; + ttstr name = TVPChopStorageExt(item.Name); + allFileName.emplace(name, &item); + allfilelist.emplace_back(idx); } - } - for (const tTVPXP3Archive::tArchiveItem &item : xp3arc->ItemVector) { - if (item.OrgSize) { - CFileItem file; - CFileItem2 file2; - file2.Init(); - UString name(item.Name.c_str()); - file.Size = item.OrgSize; - std::auto_ptr s(xp3arc->CreateStreamByIndex(&item - &xp3arc->ItemVector.front())); - if (UsingETC2 && item.OrgSize > 8192) { - // convert image > 8k - tjs_uint8 header[16]; - s->Read(header, 16); - bool isImage = false, isOpaque = false; - if (!memcmp(header, "BM", 2)) { - isImage = true; - } else if (!memcmp(header, "\x89PNG", 4)) { - isImage = true; - } else if (!memcmp(header, "\xFF\xD8\xFF", 3)) { // JPEG - isImage = true; - } else if (!memcmp(header, "TLG", 3)) { - isImage = true; - } else if (!memcmp(header, "\x49\x49\xbc\x01", 4)) { // JXR - isImage = true; - } else if (!memcmp(header, "BPG", 3)) { - isImage = true; - } else if (!memcmp(header, "RIFF", 4)) { - if (!memcmp(header + 8, "WEBPVP8", 7)) { - isImage = true; - } + if(OptionMergeMaskImg) + for (tjs_uint idx : allfilelist) { + const tTVPXP3Archive::tArchiveItem &item = xp3arc->ItemVector[idx]; + ttstr name = TVPChopStorageExt(item.Name); + tjs_int pos = name.length() - 2; + if (pos > 0 && name.SubString(pos, 2) == TJS_W("_m")) { + ttstr prefix = name.SubString(0, pos); + int count = 0; + auto itpair = allFileName.equal_range(prefix); + for (auto it = itpair.first; it != itpair.second; ++it) { + ++count; } - // actual TVPLoadGraphicRouter - static tTVPGraphicHandlerType *handler = TVPGetGraphicLoadHandler(TJS_W(".png")); - if (isImage) { - // skip 8-bit image - s->SetPosition(0); - iTJSDispatch2 *dic = nullptr; - handler->Header(s.get(), &dic); - if (dic) { - dic->Release(); - tTJSVariant val; - dic->PropGet(0, TJS_W("bpp"), 0, &val, dic); - int bpp = val.AsInteger(); - isOpaque = bpp == 24; - isImage = isOpaque || bpp == 32; - } else { - isImage = false; // unknown error, skip - } + if (count == 1) { // assume that one mask associate to one image + const tTVPXP3Archive::tArchiveItem* imgItem = itpair.first->second; + allFileName.erase(itpair.first); + allFileName.erase(name); + imglist.emplace(imgItem - &xp3arc->ItemVector.front(), idx); + masklist.emplace(idx); + continue; } - if (isImage) { - // TODO merge mask file - s->SetPosition(0); - typedef std::tuple > PicInfo; - PicInfo picinfo; - handler->Load(nullptr, &picinfo, [](void *callbackdata, tjs_uint w, tjs_uint h, tTVPGraphicPixelFormat fmt)->int { - PicInfo &picinfo = *(PicInfo *)callbackdata; - std::get<0>(picinfo) = w; - std::get<1>(picinfo) = h; - int pitch = w * 4; - std::get<2>(picinfo) = pitch; - std::get<3>(picinfo).resize(pitch * h); - return w * 4; - }, [](void *callbackdata, int y)->void* { - PicInfo &picinfo = *(PicInfo *)callbackdata; - int pitch = std::get<2>(picinfo); - return &std::get<3>(picinfo)[pitch * y]; - }, [](void *callbackdata, const ttstr & name, const ttstr & value) { - }, s.get(), 0, glmNormal); - - int w = std::get<0>(picinfo); - int h = std::get<1>(picinfo); - int pitch = std::get<2>(picinfo); - uint8_t *imgdata = nullptr; - size_t datalen; - uint64_t pvr_pixfmt; - if (isOpaque) { - imgdata = (uint8_t *)ETCPacker::convert(&std::get<3>(picinfo).front(), w, h, pitch, true, datalen); - pvr_pixfmt = 22; // ETC2_RGB - } else { - imgdata = (uint8_t *)ETCPacker::convertWithAlpha(&std::get<3>(picinfo).front(), w, h, pitch, datalen); - pvr_pixfmt = 23; // ETC2_RGBA - } + } + } + for (tjs_uint idx : allfilelist) { + if (imglist.find(idx) != imglist.end() || masklist.find(idx) != masklist.end()) + continue; + filelist.push_back(idx); + } + } - tTVPMemoryStream * memstr = new tTVPMemoryStream; - // in pvr v3 container - memstr->WriteBuffer("PVR\3", 4); - memstr->WriteBuffer("\0\0\0\0", 4); // no premultiply alpha - memstr->WriteBuffer(&pvr_pixfmt, sizeof(pvr_pixfmt)); - memstr->WriteBuffer("\0\0\0\0", 4); // colorSpace - memstr->WriteBuffer("\0\0\0\0", 4); // channelType - memstr->WriteBuffer(&h, sizeof(h)); - memstr->WriteBuffer(&w, sizeof(w)); - memstr->WriteBuffer("\1\0\0\0", 4); // depth - memstr->WriteBuffer("\1\0\0\0", 4); // numberOfSurfaces - memstr->WriteBuffer("\1\0\0\0", 4); // numberOfFaces - memstr->WriteBuffer("\1\0\0\0", 4); // numberOfMipmaps - memstr->WriteBuffer("\0\0\0\0", 4); // metadataLength - memstr->WriteBuffer(imgdata, datalen); - delete[] imgdata; - memstr->SetPosition(0); - s.reset(memstr); - } + // merge image with mask + // actual TVPLoadGraphicRouter + static tTVPGraphicHandlerType *handler = TVPGetGraphicLoadHandler(TJS_W(".png")); + for (const auto &it : imglist) { + if (bStopRequired) break; + const tTVPXP3Archive::tArchiveItem &imgItem = xp3arc->ItemVector[it.first]; + const tTVPXP3Archive::tArchiveItem &maskItem = xp3arc->ItemVector[it.second]; + std::auto_ptr strImg(xp3arc->CreateStreamByIndex(&imgItem - &xp3arc->ItemVector.front())); + std::auto_ptr strMask(xp3arc->CreateStreamByIndex(&maskItem - &xp3arc->ItemVector.front())); + bool isImage = CheckIsImage(strImg.get()) && CheckIsImage(strMask.get()); + if (isImage) { + // skip 8-bit image + iTJSDispatch2 *dic = nullptr; + handler->Header(strImg.get(), &dic); + strImg->SetPosition(0); + if (dic) { + tTJSVariant val; + dic->PropGet(0, TJS_W("bpp"), 0, &val, dic); + dic->Release(); + int bpp = val.AsInteger(); + isImage = bpp == 24 || bpp == 32; + } else { + isImage = false; // unknown error, skip + } + } + if (!isImage) { + filelist.push_back(it.first); + filelist.push_back(it.second); + continue; + } + struct BmpInfoWithMask { + tTVPBitmap* bmp = nullptr; + tTVPBitmap* bmpForMask = nullptr; + std::unordered_map metainfo; + ~BmpInfoWithMask() { + if (bmp) delete bmp; + if (bmpForMask) delete bmpForMask; + } + } data; + + if (OnNewFile) + OnNewFile(it.first, imgItem.OrgSize, imgItem.Name.AsStdString()); + + // main part + handler->Load(handler->FormatData, &data, + [](void *callbackdata, tjs_uint w, tjs_uint h, tTVPGraphicPixelFormat fmt)->int{ + BmpInfoWithMask * data = (BmpInfoWithMask *)callbackdata; + if (!data->bmp) { + data->bmp = new tTVPBitmap(w, h, 32); + } + return data->bmp->GetPitch(); + }, [](void *callbackdata, tjs_int y)->void* { + BmpInfoWithMask * data = (BmpInfoWithMask *)callbackdata; + if (y >= 0) { + return data->bmp->GetScanLine(y); + } + return nullptr; + }, [](void *callbackdata, const ttstr & name, const ttstr & value) { + BmpInfoWithMask * data = (BmpInfoWithMask *)callbackdata; + data->metainfo.emplace(name, value); + }, strImg.get(), TVP_clNone, glmNormal); + + // mask part + handler->Load(handler->FormatData, &data, + [](void *callbackdata, tjs_uint w, tjs_uint h, tTVPGraphicPixelFormat fmt)->int { + BmpInfoWithMask * data = (BmpInfoWithMask *)callbackdata; + if (data->bmp->GetWidth() != w || data->bmp->GetHeight() != h) + TVPThrowExceptionMessage(TVPMaskSizeMismatch); + if (!data->bmpForMask) { + data->bmpForMask = new tTVPBitmap(w, h, 8); + } + return data->bmpForMask->GetPitch(); + }, [](void *callbackdata, tjs_int y)->void* { + BmpInfoWithMask * data = (BmpInfoWithMask *)callbackdata; + if (y >= 0) { + return data->bmpForMask->GetScanLine(y); + } + return nullptr; + }, [](void *callbackdata, const ttstr & name, const ttstr & value) { + // no metainfo for mask + }, strMask.get(), TVP_clNone, glmGrayscale); + + for (tjs_uint y = 0; y < data.bmp->GetHeight(); ++y) { + TVPBindMaskToMain((tjs_uint32*)data.bmp->GetScanLine(y), (tjs_uint8*)data.bmpForMask->GetScanLine(y), data.bmp->GetWidth()); + } + delete data.bmpForMask; + data.bmpForMask = nullptr; + ncbDictionaryAccessor meta; + for (const auto &it : data.metainfo) { + meta.SetValue(it.first.c_str(), it.second); + } + tTVPMemoryStream memstr; + tTVPBaseBitmap *bmp = new tTVPBaseBitmap(data.bmp->GetWidth(), data.bmp->GetHeight()); + bmp->AssignBitmap(data.bmp); + + if (OptionUsingETC2) { + TVPSavePVRv3(nullptr, &memstr, bmp, TJS_W("ETC2_RGBA"), meta.GetDispatch()); + } else { + // convert to tlg5 for better performance + TVPSaveAsTLG(nullptr, &memstr, bmp, TJS_W("tlg5"), meta.GetDispatch()); + } + delete bmp; + memstr.SetPosition(0); + HRESULT ret = AddTo7zArchive(&memstr, imgItem.Name); + if (OnProgress) { + uint64_t size = imgItem.OrgSize + maskItem.OrgSize; + nArcSize += size; + nTotalSize += size; + OnProgress(nTotalSize, nArcSize, size); + } + if (ret != S_OK) { + if (ret != S_INTERRUPT && OnError) { + OnError(ret, "Write to file fail."); } + bStopRequired = true; + break; + } + } + for (tjs_uint idx : filelist) { + const tTVPXP3Archive::tArchiveItem& item = xp3arc->ItemVector[idx]; + + if (bStopRequired) break; + std::auto_ptr s(xp3arc->CreateStreamByIndex(idx)); + + if (OnNewFile) + OnNewFile(&item - &xp3arc->ItemVector.front(), item.OrgSize, item.Name.AsStdString()); - SequentialInStreamFor7z str(s.get()); - tjs_uint64 origSize = s->GetSize(); - UInt64 inSizeForReduce = origSize; - UInt64 newSize = origSize; - bool compressable = origSize > 64 && origSize < 4 * 1024 * 1024; - if (compressable) { - // compressable quick check - tjs_uint8 header[16]; - s->Read(header, 16); - if (!memcmp(header, "\x89PNG", 4)) { - compressable = false; - } else if (!memcmp(header, "OggS\x00", 5)) { - compressable = false; - } else if (!memcmp(header, "\xFF\xD8\xFF", 3)) { // JPEG - compressable = false; - } else if (!memcmp(header, "TLG", 3)) { - compressable = false; - } else if (!memcmp(header, "0&\xB2\x75\x8E\x66\xCF\x11", 8)) { // wmv/wma - compressable = false; - } else if (!memcmp(header, "AJPM", 4)) { // AMV - compressable = false; - } else if (!memcmp(header, "\x49\x49\xbc\x01", 4)) { // JXR - compressable = false; - } else if (!memcmp(header, "BPG", 3)) { - compressable = false; - } else if (!memcmp(header, "RIFF", 4)) { - if (!memcmp(header + 8, "WEBPVP8", 7)) { - compressable = false; + if (OptionUsingETC2 && item.OrgSize > 8192) { + // convert image > 8k + bool isImage = CheckIsImage(s.get()); + int width = 0, height = 0; + struct BmpInfo { + tTVPBitmap* bmp = nullptr; + std::unordered_map metainfo; + tTVPGraphicPixelFormat fmt = gpfLuminance; + ~BmpInfo() { + if (bmp) delete bmp; + } + } data; + + if (isImage) { + // main part + handler->Load(handler->FormatData, &data, + [](void *callbackdata, tjs_uint w, tjs_uint h, tTVPGraphicPixelFormat fmt)->int { + BmpInfo * data = (BmpInfo *)callbackdata; + if (!data->bmp) { + data->bmp = new tTVPBitmap(w, h, 32); + data->fmt = fmt; + } + return data->bmp->GetPitch(); + }, [](void *callbackdata, tjs_int y)->void* { + BmpInfo * data = (BmpInfo *)callbackdata; + if (y >= 0) { + return data->bmp->GetScanLine(y); } + return nullptr; + }, [](void *callbackdata, const ttstr & name, const ttstr & value) { + BmpInfo * data = (BmpInfo *)callbackdata; + data->metainfo.emplace(name, value); + }, s.get(), TVP_clNone, glmNormal); + + // skip 8-bit image + if (data.fmt != gpfRGB || data.fmt != gpfRGBA) { + isImage = false; } - s->SetPosition(0); } - if (compressable) { - UInt64 expectedSize = origSize * 4 / 5; - OutStreamMemory tmp; - coder.Coder->Code(&str, &tmp, &inSizeForReduce, &newSize, nullptr); - if (tmp.GetSize() < expectedSize) { - UInt32 writed; - strout->Write(tmp.GetInternalBuffer(), tmp.GetSize(), &writed); - newDatabase.PackSizes.Add(tmp.GetSize()); - newDatabase.CoderUnpackSizes.Add(origSize); - newDatabase.NumUnpackStreamsVector.Add(1); - CFolder &folder = newDatabase.Folders.AddNew(); - folder.Bonds.SetSize(0); - folder.Coders.SetSize(1); - CCoderInfo &cod = folder.Coders[0]; - cod.MethodID = nCodecMethod; - cod.NumStreams = 1; - cod.Props = props; - folder.PackStreams.SetSize(1); - folder.PackStreams[0] = 0; // only 1 stream - newDatabase.AddFile(file, file2, name); - continue; + if (isImage) { + tTVPBaseBitmap *bmp = new tTVPBaseBitmap(data.bmp->GetWidth(), data.bmp->GetHeight()); + bmp->AssignBitmap(data.bmp); + s.reset(new tTVPMemoryStream); + ncbDictionaryAccessor meta; + for (const auto &it : data.metainfo) { + meta.SetValue(it.first.c_str(), it.second); } + TVPSavePVRv3(nullptr, s.get(), bmp, + data.fmt == gpfRGB ? TJS_W("ETC2_RGB") : TJS_W("ETC2_RGBA"), + meta.GetDispatch()); + delete bmp; s->SetPosition(0); } - // direct copy - newSize = origSize; - coderCopy.Coder->Code(&str, strout, &inSizeForReduce, &newSize, nullptr); - newDatabase.PackSizes.Add(s->GetSize()); - newDatabase.CoderUnpackSizes.Add(origSize); - newDatabase.NumUnpackStreamsVector.Add(1); - CFolder &folder = newDatabase.Folders.AddNew(); - folder.Bonds.SetSize(0); - folder.Coders.SetSize(1); - CCoderInfo &cod = folder.Coders[0]; - cod.MethodID = nCodecMethodCopy; - cod.NumStreams = 1; - folder.PackStreams.SetSize(1); - folder.PackStreams[0] = 0; // only 1 stream - newDatabase.AddFile(file, file2, name); + } + HRESULT ret = AddTo7zArchive(s.get(), item.Name); + nArcSize += item.OrgSize; + nTotalSize += item.OrgSize; + if (ret != S_OK) { + if (ret != S_INTERRUPT && OnError) { + OnError(ret, "Write to file fail."); + } + bStopRequired = true; + break; } } + if (bStopRequired) + break; CCompressionMethodMode mode; CHeaderOptions hdropt; hdropt.CompressMainHeader = true; archive.WriteDatabase(newDatabase, &mode, hdropt); archive.Close(); + strout = nullptr; delete xp3arc; + it.first = nullptr; remove(it.second.c_str()); rename(tmpname.c_str(), it.second.c_str()); } - ConvFileList.clear(); + Clear(); + if (OnEnded) OnEnded(); } diff --git a/src/core/environ/XP3ArchiveRepack.h b/src/core/environ/XP3ArchiveRepack.h index e0f76f53..49b50724 100644 --- a/src/core/environ/XP3ArchiveRepack.h +++ b/src/core/environ/XP3ArchiveRepack.h @@ -1,20 +1,27 @@ #pragma once #include #include +#include -class tTVPXP3ArchiveEx; +class XP3ArchiveRepackAsyncImpl; class XP3ArchiveRepackAsync { - std::vector > ConvFileList; - void * /*std::thread*/ Thread = nullptr; public: - XP3ArchiveRepackAsync(const std::string &xp3filter); + XP3ArchiveRepackAsync(); ~XP3ArchiveRepackAsync(); - uint64_t AddTask(const std::string &src/*, const std::string &dst*/); - void Clear(); + uint64_t AddTask(const std::string &src); void Start(); - void DoConv(); - -public: - bool UsingETC2 = false; + void Stop(); + void SetXP3Filter(const std::string &xp3filter); + void SetCallback( + const std::function &onNewFile, + const std::function &onNewArchive, + const std::function &onProgress, + const std::function &onError, + const std::function &onEnded); + + void SetOption(const std::string &name, bool v); + +private: + XP3ArchiveRepackAsyncImpl *_impl; }; diff --git a/src/core/environ/android/AndroidUtils.cpp b/src/core/environ/android/AndroidUtils.cpp index 2c494bfc..db4faa96 100644 --- a/src/core/environ/android/AndroidUtils.cpp +++ b/src/core/environ/android/AndroidUtils.cpp @@ -696,28 +696,64 @@ bool TVPCreateFolders(const ttstr &folder) return false; } -bool TVPWriteDataToFile(const ttstr &filepath, const void *data, unsigned int size) { - std::string filename = filepath.AsStdString(); -// int Handle = open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666); -// if (Handle >= 0) { -// bool ret = write(Handle, data, size) == size; -// close(Handle); -// return ret; -// } +static bool TVPWriteDataToFileJava(const std::string &filename, const void* data, unsigned int size) { JniMethodInfo methodInfo; if (JniHelper::getStaticMethodInfo(methodInfo, "org/tvp/kirikiri2/KR2Activity", "WriteFile", "(Ljava/lang/String;[B)Z")) { - jstring jstr = methodInfo.env->NewStringUTF(filename.c_str()); - jbyteArray arr = methodInfo.env->NewByteArray(size); - methodInfo.env->SetByteArrayRegion(arr, 0, size, (jbyte*)data); - bool ret = methodInfo.env->CallStaticBooleanMethod(methodInfo.classID, methodInfo.methodID, jstr, arr); - methodInfo.env->DeleteLocalRef(arr); - methodInfo.env->DeleteLocalRef(jstr); - methodInfo.env->DeleteLocalRef(methodInfo.classID); + cocos2d::FileUtils *fileutil = cocos2d::FileUtils::getInstance(); + bool ret = false; + do { // write files until file not exist + jstring jstr = methodInfo.env->NewStringUTF(filename.c_str()); + jbyteArray arr = methodInfo.env->NewByteArray(size); + methodInfo.env->SetByteArrayRegion(arr, 0, size, (jbyte*)data); + bool ret = methodInfo.env->CallStaticBooleanMethod(methodInfo.classID, methodInfo.methodID, jstr, arr); + methodInfo.env->DeleteLocalRef(arr); + methodInfo.env->DeleteLocalRef(jstr); + methodInfo.env->DeleteLocalRef(methodInfo.classID); + } while (!fileutil->isFileExist(filename)); return ret; } return false; } +bool TVPWriteDataToFile(const ttstr &filepath, const void *data, unsigned int size) { + cocos2d::FileUtils *fileutil = cocos2d::FileUtils::getInstance(); + std::string filename = filepath.AsStdString(); + if (fileutil->isFileExist(filename)) { + // for number filename suffix issue + time_t t = time(nullptr); + std::vector buffer; + buffer.resize(filename.size() + 32); + sprintf(&buffer.front(), "%s.%d.bak", filename.c_str(), (int)t); + std::string tempname = &buffer.front(); + if (rename(filename.c_str(), tempname.c_str()) == 0) { + // file api is OK + FILE *fp = fopen(filename.c_str(), "wb"); + if (fp) { + bool ret = fwrite(data, 1, size, fp) == size; + fclose(fp); + ret = (remove(tempname.c_str()) == 0) && ret; + return ret; + } + } + while (fileutil->isFileExist(filename)) { + if (!TVPRenameFile(filename, tempname)) { + return false; + } + } + bool ret = TVPWriteDataToFileJava(filename, data, size); + ret = TVPDeleteFile(tempname) && ret; + return ret; + } + FILE *fp = fopen(filename.c_str(), "wb"); + if (fp) { + // file api is OK + int writed = fwrite(data, 1, size, fp); + fclose(fp); + return writed == size; + } + return TVPWriteDataToFileJava(filename, data, size); +} + std::string TVPGetCurrentLanguage() { JniMethodInfo t; std::string ret(""); diff --git a/src/core/environ/cocos2d/AppDelegate.cpp b/src/core/environ/cocos2d/AppDelegate.cpp index b6638a43..eb9dd55f 100644 --- a/src/core/environ/cocos2d/AppDelegate.cpp +++ b/src/core/environ/cocos2d/AppDelegate.cpp @@ -9,13 +9,14 @@ #include "Platform.h" #include "ui/MessageBox.h" #include "ui/GlobalPreferenceForm.h" +#include "CustomFileUtils.h" USING_NS_CC; static Size designResolutionSize(960, 640); bool TVPCheckStartupArg(); std::string TVPGetCurrentLanguage(); -cocos2d::FileUtils *TVPCreateCustomFileUtils(); +cocos2d::CustomFileUtils *TVPCreateCustomFileUtils(); void TVPAppDelegate::applicationWillEnterForeground() { ::Application->OnActivate(); @@ -30,7 +31,8 @@ void TVPAppDelegate::applicationDidEnterBackground() { bool TVPAppDelegate::applicationDidFinishLaunching() { cocos2d::log("applicationDidFinishLaunching"); // initialize director - FileUtils::setDelegate(TVPCreateCustomFileUtils()); + cocos2d::CustomFileUtils *fileutil = TVPCreateCustomFileUtils(); + FileUtils::setDelegate(fileutil); auto director = Director::getInstance(); auto glview = director->getOpenGLView(); if (!glview) { @@ -56,6 +58,15 @@ bool TVPAppDelegate::applicationDidFinishLaunching() { // this can make sure that the resource's height could fit for the height of design resolution. searchPath.emplace_back("res"); + std::string skinpath = GlobalConfigManager::GetInstance()->GetValue("skin_path", ""); + if (!skinpath.empty()) { + if (!fileutil->isFileExist(skinpath)) { + GlobalConfigManager::GetInstance()->SetValue("skin_path", ""); + } else { + fileutil->addAutoSearchArchive(skinpath); + } + } + // set searching path FileUtils::getInstance()->setSearchPaths(searchPath); diff --git a/src/core/environ/cocos2d/CustomFileUtils.cpp b/src/core/environ/cocos2d/CustomFileUtils.cpp index f97e15cb..22cfca6c 100644 --- a/src/core/environ/cocos2d/CustomFileUtils.cpp +++ b/src/core/environ/cocos2d/CustomFileUtils.cpp @@ -1,52 +1,7 @@ -#include "cocos2d.h" -#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 -#include "platform/win32/CCFileUtils-win32.h" -#elif CC_TARGET_PLATFORM == CC_PLATFORM_IOS -#import -#import "platform/apple/CCFileUtils-apple.h" -#elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID -#include "platform/android/CCFileUtils-android.h" -#endif -#ifdef MINIZIP_FROM_SYSTEM -#include -#else // from our embedded sources -#include "external/unzip/unzip.h" -#endif +#include "CustomFileUtils.h" NS_CC_BEGIN -typedef -#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 -FileUtilsWin32 -#elif CC_TARGET_PLATFORM == CC_PLATFORM_IOS -FileUtilsApple -#elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID -FileUtilsAndroid -#endif -FileUtilsInherit; -class CustomFileUtils : public FileUtilsInherit -{ -public: - CustomFileUtils(); - - void addAutoSearchArchive(const std::string& path); - virtual std::string fullPathForFilename(const std::string &filename) const override; - virtual std::string getStringFromFile(const std::string& filename) override; - virtual Data getDataFromFile(const std::string& filename) override; - virtual unsigned char* getFileData(const std::string& filename, const char* mode, ssize_t *size) override; - virtual bool isFileExistInternal(const std::string& strFilePath) const override; - virtual bool isDirectoryExistInternal(const std::string& dirPath) const override; - virtual bool init() override { - return FileUtilsInherit::init(); - } - -private: - unsigned char* getFileDataFromArchive(const std::string& filename, ssize_t *size); - - std::unordered_map > _autoSearchArchive; - std::mutex _lock; -}; - CustomFileUtils::CustomFileUtils() { } @@ -146,7 +101,7 @@ std::string CustomFileUtils::getStringFromFile(const std::string& filename) NS_CC_END -cocos2d::FileUtils *TVPCreateCustomFileUtils() { +cocos2d::CustomFileUtils *TVPCreateCustomFileUtils() { cocos2d::CustomFileUtils *ret = new cocos2d::CustomFileUtils; ret->init(); return ret; diff --git a/src/core/environ/cocos2d/CustomFileUtils.h b/src/core/environ/cocos2d/CustomFileUtils.h new file mode 100644 index 00000000..f1046ca4 --- /dev/null +++ b/src/core/environ/cocos2d/CustomFileUtils.h @@ -0,0 +1,52 @@ +#pragma once +#include "cocos2d.h" +#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 +#include "platform/win32/CCFileUtils-win32.h" +#elif CC_TARGET_PLATFORM == CC_PLATFORM_IOS +#import +#import "platform/apple/CCFileUtils-apple.h" +#elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID +#include "platform/android/CCFileUtils-android.h" +#endif +#ifdef MINIZIP_FROM_SYSTEM +#include +#else // from our embedded sources +#include "external/unzip/unzip.h" +#endif + +NS_CC_BEGIN + +typedef +#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 +FileUtilsWin32 +#elif CC_TARGET_PLATFORM == CC_PLATFORM_IOS +FileUtilsApple +#elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID +FileUtilsAndroid +#endif +FileUtilsInherit; + +class CustomFileUtils : public FileUtilsInherit +{ +public: + CustomFileUtils(); + + void addAutoSearchArchive(const std::string& path); + virtual std::string fullPathForFilename(const std::string &filename) const override; + virtual std::string getStringFromFile(const std::string& filename) override; + virtual Data getDataFromFile(const std::string& filename) override; + virtual unsigned char* getFileData(const std::string& filename, const char* mode, ssize_t *size) override; + virtual bool isFileExistInternal(const std::string& strFilePath) const override; + virtual bool isDirectoryExistInternal(const std::string& dirPath) const override; + virtual bool init() override { + return FileUtilsInherit::init(); + } + +private: + unsigned char* getFileDataFromArchive(const std::string& filename, ssize_t *size); + + std::unordered_map > _autoSearchArchive; + std::mutex _lock; +}; + +NS_CC_END \ No newline at end of file diff --git a/src/core/environ/linux/Platform.cpp b/src/core/environ/linux/Platform.cpp index b49ca628..381fc0a0 100644 --- a/src/core/environ/linux/Platform.cpp +++ b/src/core/environ/linux/Platform.cpp @@ -70,4 +70,13 @@ void TVPGetMemoryInfo(TVPMemoryInfo &m) #include void TVPRelinquishCPU(){ sched_yield(); -} \ No newline at end of file +} + +void TVP_utime(const char *name, time_t modtime) { + timeval mt[2]; + mt[0].tv_sec = modtime; + mt[0].tv_usec = 0; + mt[1].tv_sec = modtime; + mt[1].tv_usec = 0; + utimes(name, mt); +} diff --git a/src/core/environ/ui/BaseForm.cpp b/src/core/environ/ui/BaseForm.cpp index 1eca06fb..63ee8fbd 100644 --- a/src/core/environ/ui/BaseForm.cpp +++ b/src/core/environ/ui/BaseForm.cpp @@ -238,7 +238,7 @@ void iTVPFloatForm::rearrangeLayout() RootNode->setContentSize(sceneSize); ui::Helper::doLayout(RootNode); RootNode->setScale(scale); - RootNode->setAnchorPoint(sceneSize / 2); + RootNode->setAnchorPoint(Vec2(0.5f, 0.5f)); RootNode->setPosition(center); } } diff --git a/src/core/environ/ui/FileSelectorForm.cpp b/src/core/environ/ui/FileSelectorForm.cpp index d2122946..ee92721b 100644 --- a/src/core/environ/ui/FileSelectorForm.cpp +++ b/src/core/environ/ui/FileSelectorForm.cpp @@ -13,6 +13,8 @@ #include "base/CCDirector.h" #include "MessageBox.h" #include "platform/CCDevice.h" +#include "base/CCScheduler.h" +#include "utils/TickCount.h" using namespace cocos2d; using namespace cocos2d::extension; @@ -219,7 +221,6 @@ void TVPBaseFileSelectorForm::onCellLongPress(int idx) LocaleConfigManager::GetInstance()->initText(reader.findController("titleCut")); LocaleConfigManager::GetInstance()->initText(reader.findController("titlePaste")); LocaleConfigManager::GetInstance()->initText(reader.findController("titleUnpack")); - LocaleConfigManager::GetInstance()->initText(reader.findController("titleRepack", false)); LocaleConfigManager::GetInstance()->initText(reader.findController("titleDelete")); LocaleConfigManager::GetInstance()->initText(reader.findController("titleSendTo")); _fileOperateMenulist = reader.findController("list"); @@ -229,7 +230,6 @@ void TVPBaseFileSelectorForm::onCellLongPress(int idx) _fileOperateCell_cut = reader.findWidget("cut"); _fileOperateCell_paste = reader.findWidget("paste"); _fileOperateCell_delete = reader.findWidget("delete"); - _fileOperateCell_repack = reader.findWidget("repack", false); _fileOperateCell_unpack = reader.findWidget("unpack", false); _fileOperateCell_sendto = reader.findWidget("sendto"); Widget *btn;; @@ -251,9 +251,6 @@ void TVPBaseFileSelectorForm::onCellLongPress(int idx) if ((btn = reader.findWidget("btnUnpack", false))) { btn->addClickEventListener(std::bind(&TVPBaseFileSelectorForm::onUnpackClicked, this, std::placeholders::_1)); } - if ((btn = reader.findWidget("btnRepack", false))) { - btn->addClickEventListener(std::bind(&TVPBaseFileSelectorForm::onRepackClicked, this, std::placeholders::_1)); - } if ((btn = reader.findWidget("btnDelete"))) { btn->addClickEventListener(std::bind(&TVPBaseFileSelectorForm::onDeleteClicked, this, std::placeholders::_1)); } @@ -485,14 +482,143 @@ void TVPBaseFileSelectorForm::onPasteClicked(cocos2d::Ref *owner) void TVPBaseFileSelectorForm::onUnpackClicked(cocos2d::Ref *owner) { - + if (_selectedFileIndex.size() != 1) { + return; + } + FileInfo &info = CurrentDirList[*_selectedFileIndex.begin()]; + _selectedFileIndex.clear(); clearFileMenu(); -} + ttstr outpath = TVPChopStorageExt(info.FullPath); -void TVPBaseFileSelectorForm::onRepackClicked(cocos2d::Ref *owner) -{ + class UnpackArchive { + const int UpdateMS = 100; // update rate 10 fps + tTVPUnpackArchive ArcUnpacker; - clearFileMenu(); + public: + bool Init(const std::string &path, const std::string &outpath) { + if (ArcUnpacker.Prepare(path, outpath, &TotalSize) <= 0) { + return false; + } + if (TotalSize > 1024 * 1024) { + ProgressForm = TVPSimpleProgressForm::create(); + TVPMainScene::GetInstance()->pushUIForm(ProgressForm, TVPMainScene::eEnterAniNone); + std::vector > > vecButtons; + vecButtons.emplace_back("Stop", [this](Ref*) { + ArcUnpacker.Stop(); + }); + ProgressForm->initButtons(vecButtons); + ProgressForm->setTitle("Unpacking..."); + ProgressForm->setPercentOnly(0); + ProgressForm->setPercentOnly2(0); + ProgressForm->setPercentText(""); + ProgressForm->setPercentText2(""); + ProgressForm->setContent(""); + } + ArcUnpacker.SetCallback( + std::bind(&UnpackArchive::OnEnded, this), + std::bind(&UnpackArchive::OnError, this, std::placeholders::_1, std::placeholders::_2), + std::bind(&UnpackArchive::OnProgress, this, std::placeholders::_1, std::placeholders::_2), + std::bind(&UnpackArchive::OnNewFile, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) + ); + return true; + } + virtual ~UnpackArchive() { + if (ProgressForm) { + TVPMainScene::GetInstance()->popUIForm(ProgressForm, TVPMainScene::eLeaveAniNone); + ProgressForm = nullptr; + } + } + void Start(const std::function &onEnded) { + FuncOnEnded = onEnded; + ArcUnpacker.Start(); + } + + private: + void OnEnded() { + Director::getInstance()->getScheduler()->performFunctionInCocosThread([this] { + if (FuncOnEnded) + FuncOnEnded(); + delete this; + }); + } + void OnError(int err, const char *msg) { + Director::getInstance()->getScheduler()->performFunctionInCocosThread([this, err, msg] { + char buf[64]; + sprintf(buf, "Error %d\n", err); + ttstr strmsg(buf); + strmsg += msg; + TVPShowSimpleMessageBox(strmsg, TJS_W("Fail to unpack archive")); + }); + } + void OnProgress(tjs_uint64 total_size, tjs_uint64 file_size) { + if (!ProgressForm) return; + tjs_uint32 tick = TVPGetRoughTickCount32(); + if ((int)(tick - LastUpdate) < UpdateMS) { + return; + } + LastUpdate = tick; + Director::getInstance()->getScheduler()->performFunctionInCocosThread([this, total_size, file_size] { + ProgressForm->setPercentOnly((float)total_size / TotalSize); + ProgressForm->setPercentOnly2((float)file_size / CurrentFileSize); + char buf[64]; + int sizeMB = static_cast(total_size / (1024 * 1024)), + totalMB = static_cast(TotalSize / (1024 * 1024)); + sprintf(buf, "%d / %dMB", sizeMB, totalMB); + ProgressForm->setPercentText(buf); + sizeMB = static_cast(file_size / (1024 * 1024)); + totalMB = static_cast(CurrentFileSize / (1024 * 1024)); + sprintf(buf, "%d / %dMB", sizeMB, totalMB); + ProgressForm->setPercentText2(buf); + }); + } + void OnNewFile(int idx, const char * utf8name, tjs_uint64 file_size) { + if (!ProgressForm) return; + tjs_uint32 tick = TVPGetRoughTickCount32(); + if ((int)(tick - LastUpdate) < UpdateMS && file_size < 1024 * 1024) { + return; + } + LastUpdate = tick; + CurrentFileSize = file_size; + std::string filename(utf8name); + Director::getInstance()->getScheduler()->performFunctionInCocosThread([this, filename] { + ProgressForm->setContent(filename); + }); + } + + TVPSimpleProgressForm *ProgressForm = nullptr; + tjs_uint32 LastUpdate = 0; + tjs_uint64 TotalSize, CurrentFileSize; + std::function FuncOnEnded; + }; + UnpackArchive *unpacker = new UnpackArchive; + // using libarchive to unpack archive + if (unpacker->Init(info.FullPath, outpath.AsStdString())) { + unpacker->Start([this]() { + ListDir(CurrentPath); + }); + return; + } + delete unpacker; + + // use internal archive module + tTVPArchive *arc = TVPOpenArchive(info.FullPath, false); + if (!arc) { + LocaleConfigManager *localeMgr = LocaleConfigManager::GetInstance(); + TVPShowSimpleMessageBox(info.FullPath, localeMgr->GetText("fail_to_open_archive")); + return; + } + + tjs_uint count = arc->GetCount(); + for (tjs_uint i = 0; i < count; ++i) { + ttstr name = arc->GetName(i); + ttstr fullpath = outpath + name; + tTJSBinaryStream *st = arc->CreateStreamByIndex(i); + if (!st) continue; + TVPSaveStreamToFile(st, 0, st->GetSize(), fullpath); + delete st; + } + + delete arc; } void TVPBaseFileSelectorForm::onDeleteClicked(cocos2d::Ref *owner) @@ -540,8 +666,7 @@ void TVPBaseFileSelectorForm::updateFileMenu() } if (_selectedFileIndex.size() == 1) { if (_fileOperateCell_view) _fileOperateMenulist->pushBackCustomItem(_fileOperateCell_view.get()); - // _fileOperateMenulist->pushBackCustomItem(_fileOperateCell_unpack.get()); - // _fileOperateMenulist->pushBackCustomItem(_fileOperateCell_repack.get()); + _fileOperateMenulist->pushBackCustomItem(_fileOperateCell_unpack.get()); // _fileOperateMenulist->pushBackCustomItem(_fileOperateCell_sendto.get()); } if (!_selectedFileIndex.empty()) { diff --git a/src/core/environ/ui/FileSelectorForm.h b/src/core/environ/ui/FileSelectorForm.h index b7b8c796..e069fca2 100644 --- a/src/core/environ/ui/FileSelectorForm.h +++ b/src/core/environ/ui/FileSelectorForm.h @@ -73,7 +73,6 @@ class TVPBaseFileSelectorForm : public iTVPBaseForm, public cocos2d::extension:: void onCutClicked(cocos2d::Ref *owner); void onPasteClicked(cocos2d::Ref *owner); void onUnpackClicked(cocos2d::Ref *owner); - void onRepackClicked(cocos2d::Ref *owner); void onDeleteClicked(cocos2d::Ref *owner); void onSendToClicked(cocos2d::Ref *owner); void updateFileMenu(); diff --git a/src/core/environ/ui/GlobalPreferenceForm.cpp b/src/core/environ/ui/GlobalPreferenceForm.cpp index 85aa690a..55863b07 100644 --- a/src/core/environ/ui/GlobalPreferenceForm.cpp +++ b/src/core/environ/ui/GlobalPreferenceForm.cpp @@ -13,7 +13,24 @@ using namespace cocos2d::ui; const char * const FileName_NaviBar = "ui/NaviBar.csb"; const char * const FileName_Body = "ui/ListView.csb"; - +static bool PreferenceGetValueBool(const std::string &name, bool defval) { + return GlobalConfigManager::GetInstance()->GetValue(name, defval); +} +static void PreferenceSetValueBool(const std::string &name, bool v) { + GlobalConfigManager::GetInstance()->SetValueInt(name, v); +} +static std::string PreferenceGetValueString(const std::string &name, const std::string& defval) { + return GlobalConfigManager::GetInstance()->GetValue(name, defval); +} +static void PreferenceSetValueString(const std::string &name, const std::string& v) { + GlobalConfigManager::GetInstance()->SetValue(name, v); +} +static float PreferenceGetValueFloat(const std::string &name, float defval) { + return GlobalConfigManager::GetInstance()->GetValue(name, defval); +} +static void PreferenceSetValueFloat(const std::string &name, float v) { + GlobalConfigManager::GetInstance()->SetValueFloat(name, v); +} #include "PreferenceConfig.h" TVPGlobalPreferenceForm * TVPGlobalPreferenceForm::create(const tPreferenceScreen *config) { diff --git a/src/core/environ/ui/IndividualPreferenceForm.cpp b/src/core/environ/ui/IndividualPreferenceForm.cpp index 0daee1a4..129d2515 100644 --- a/src/core/environ/ui/IndividualPreferenceForm.cpp +++ b/src/core/environ/ui/IndividualPreferenceForm.cpp @@ -13,13 +13,29 @@ using namespace cocos2d::ui; const char * const FileName_NaviBar = "ui/NaviBar.csb"; const char * const FileName_Body = "ui/ListView.csb"; -#define GlobalConfigManager IndividualConfigManager #define TVPGlobalPreferenceForm IndividualPreferenceForm +static bool PreferenceGetValueBool(const std::string &name, bool defval) { + return IndividualConfigManager::GetInstance()->GetValue(name, defval); +} +static void PreferenceSetValueBool(const std::string &name, bool v) { + IndividualConfigManager::GetInstance()->SetValueInt(name, v); +} +static std::string PreferenceGetValueString(const std::string &name, const std::string& defval) { + return IndividualConfigManager::GetInstance()->GetValue(name, defval); +} +static void PreferenceSetValueString(const std::string &name, const std::string& v) { + IndividualConfigManager::GetInstance()->SetValue(name, v); +} +static float PreferenceGetValueFloat(const std::string &name, float defval) { + return IndividualConfigManager::GetInstance()->GetValue(name, defval); +} +static void PreferenceSetValueFloat(const std::string &name, float v) { + IndividualConfigManager::GetInstance()->SetValueFloat(name, v); +} #include "PreferenceConfig.h" #undef TVPGlobalPreferenceForm -#undef GlobalConfigManager static void initInividualConfig() { if (!RootPreference.Preferences.empty()) return; diff --git a/src/core/environ/ui/MainFileSelectorForm.cpp b/src/core/environ/ui/MainFileSelectorForm.cpp index f4d8e073..b0057001 100644 --- a/src/core/environ/ui/MainFileSelectorForm.cpp +++ b/src/core/environ/ui/MainFileSelectorForm.cpp @@ -19,6 +19,7 @@ #include "tinyxml2/tinyxml2.h" #include "StorageImpl.h" #include "TipsHelpForm.h" +#include "XP3RepackForm.h" using namespace cocos2d; using namespace cocos2d::ui; @@ -108,7 +109,7 @@ static bool _CheckGameFolder(const std::string &path) { return c - ('A' - 'a'); return c; }); - int pos = lowername.rfind('.'); + size_t pos = lowername.rfind('.'); if (pos == lowername.npos) return; if (lowername.substr(pos) == ".xp3") { isValidGameFolder = true; @@ -220,7 +221,8 @@ TVPMainFileSelectorForm * TVPMainFileSelectorForm::create() { void TVPMainFileSelectorForm::initFromFile() { _LoadHistory(); - if (!_HistoryPath.empty()) { +// if (!_HistoryPath.empty()) + { CSBReader reader; Node *root = reader.Load("ui/MainFileSelector.csb"); _fileList = reader.findController("fileList"); @@ -302,13 +304,14 @@ void TVPMainFileSelectorForm::showMenu(Ref*) { // captions LocaleConfigManager *localeMgr = LocaleConfigManager::GetInstance(); - localeMgr->initText(dynamic_cast(reader.findController("titleRotate"))); - localeMgr->initText(dynamic_cast(reader.findController("titleGlobalPref"))); - localeMgr->initText(dynamic_cast(reader.findController("titleNewLocalPref"))); - localeMgr->initText(dynamic_cast(reader.findController("titleLocalPref"))); - localeMgr->initText(dynamic_cast(reader.findController("titleHelp"))); - localeMgr->initText(dynamic_cast(reader.findController("titleAbout"))); - localeMgr->initText(dynamic_cast(reader.findController("titleExit"))); + localeMgr->initText(reader.findController("titleRotate")); + localeMgr->initText(reader.findController("titleGlobalPref")); + localeMgr->initText(reader.findController("titleNewLocalPref")); + localeMgr->initText(reader.findController("titleLocalPref")); + localeMgr->initText(reader.findController("titleHelp")); + localeMgr->initText(reader.findController("titleAbout")); + localeMgr->initText(reader.findController("titleExit")); + localeMgr->initText(reader.findController("titleRepack")); // button events reader.findWidget("btnRotate")->addClickEventListener([](Ref*) { @@ -380,6 +383,10 @@ void TVPMainFileSelectorForm::showMenu(Ref*) { _AskExit(); }); } + reader.findWidget("btnRepack")->addClickEventListener([this](Ref*) { + TVPProcessXP3Repack(CurrentPath); + hideMenu(nullptr); + }); } const Size &uiSize = getContentSize(); diff --git a/src/core/environ/ui/MessageBox.cpp b/src/core/environ/ui/MessageBox.cpp index 972c34f6..1781b7dc 100644 --- a/src/core/environ/ui/MessageBox.cpp +++ b/src/core/environ/ui/MessageBox.cpp @@ -4,6 +4,7 @@ #include "ui/UIText.h" #include "ui/UIScrollView.h" #include "ui/UIHelper.h" +#include "ui/UILoadingBar.h" #include "2d/CCLabel.h" #include "ConfigManager/LocaleConfigManager.h" @@ -119,3 +120,102 @@ void TVPMessageBoxForm::onKeyPressed(cocos2d::EventKeyboard::KeyCode keyCode, co } } +TVPSimpleProgressForm* TVPSimpleProgressForm::create() +{ + TVPSimpleProgressForm* form = new TVPSimpleProgressForm; + form->autorelease(); + form->initFromFile("ui/ProgressBox.csb"); + return form; +} + +void TVPSimpleProgressForm::initButtons(const std::vector > > &vec) +{ + Size btnSize = _btnCell->getContentSize(); + float totalWidth = 0; + float containerWidth = _btnContainer->getContentSize().width; + float edge = _btnCell->getPosition().x; + containerWidth -= edge * 2; + std::vector buttons; + float btnEdge = btnSize.width - _btnButton->getTitleRenderer()->getContentSize().width; + for (const auto &it : vec) { + _btnButton->setTitleText(it.first); + _btnButton->addClickEventListener(it.second); + btnSize.width = _btnButton->getTitleRenderer()->getContentSize().width + btnEdge; + _btnCell->setContentSize(btnSize); + ui::Helper::doLayout(_btnCell); + totalWidth += btnSize.width; + Widget *btn = _btnCell->clone(); + buttons.push_back(btn); + _btnContainer->addChild(btn); + } + float x = edge; + float gap = (containerWidth - totalWidth) / buttons.size(); + for (Node *btn : buttons) { + btn->setPositionX(x); + x += btn->getContentSize().width; + } +} + +void TVPSimpleProgressForm::setTitle(const std::string &text) +{ + _textTitle->setString(text); +} +void TVPSimpleProgressForm::setContent(const std::string &text) +{ + _textContent->setString(text); +} + +void TVPSimpleProgressForm::setPercentWithText(float percent) +{ + _progressBar[0]->setPercent(percent); + char tmp[16]; + sprintf(tmp, "%2.2f%%", percent); + _textProgress[0]->setString(tmp); +} +void TVPSimpleProgressForm::setPercentWithText2(float percent) +{ + _progressBar[1]->setPercent(percent); + char tmp[16]; + sprintf(tmp, "%2.2f%%", percent); + _textProgress[1]->setString(tmp); +} + +void TVPSimpleProgressForm::setPercentOnly(float percent) +{ + _progressBar[0]->setPercent(percent * 100); +} + +void TVPSimpleProgressForm::setPercentOnly2(float percent) +{ + _progressBar[1]->setPercent(percent * 100); +} + +void TVPSimpleProgressForm::setPercentText(const std::string &text) +{ + _textProgress[0]->setString(text); +} + +void TVPSimpleProgressForm::setPercentText2(const std::string &text) +{ + _textProgress[1]->setString(text); +} + +void TVPSimpleProgressForm::setProgress2Visible(bool visible) +{ + // TODO +} + +void TVPSimpleProgressForm::bindBodyController(const NodeMap &allNodes) +{ + LocaleConfigManager *localeMgr = LocaleConfigManager::GetInstance(); + _progressBar[0] = allNodes.findController("progrss_1"); + _progressBar[1] = allNodes.findController("progrss_2"); + _textProgress[0] = allNodes.findController("progress_text_1"); + _textProgress[1] = allNodes.findController("progress_text_2"); + _textContent = allNodes.findController("text"); + _textTitle = allNodes.findController("title"); + _btnContainer = allNodes.findController("btnList"); + _btnCell = allNodes.findWidget("btnCell"); + _btnButton = allNodes.findController