diff --git a/.gitignore b/.gitignore deleted file mode 100644 index dfcfd56..0000000 --- a/.gitignore +++ /dev/null @@ -1,350 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ -[Ll]ogs/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx -*.appxbundle -*.appxupload - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -.ionide/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..690959b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# change logs + +- 2022.8.30 创建 + + diff --git a/CHANGELOG.md.meta b/CHANGELOG.md.meta new file mode 100644 index 0000000..2ced6f3 --- /dev/null +++ b/CHANGELOG.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7b2d5d0a85ef3bf44be6cbe94ed5ed2a +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data.meta b/Data.meta new file mode 100644 index 0000000..146542d --- /dev/null +++ b/Data.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1c3bdcb0d35a8834aadbee0e5e02a093 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/ModifiedUnityAssemblies.meta b/Data/ModifiedUnityAssemblies.meta new file mode 100644 index 0000000..f79b8c5 --- /dev/null +++ b/Data/ModifiedUnityAssemblies.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 68f56e29afb52164b85f220d1033b461 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/ModifiedUnityAssemblies/2019.4.40.meta b/Data/ModifiedUnityAssemblies/2019.4.40.meta new file mode 100644 index 0000000..e7f046c --- /dev/null +++ b/Data/ModifiedUnityAssemblies/2019.4.40.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1cd43f2247d29084fa2b393c30648e64 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/ModifiedUnityAssemblies/2019.4.40/Unity.IL2CPP-Mac.dll.bytes b/Data/ModifiedUnityAssemblies/2019.4.40/Unity.IL2CPP-Mac.dll.bytes new file mode 100644 index 0000000..2cce7a7 Binary files /dev/null and b/Data/ModifiedUnityAssemblies/2019.4.40/Unity.IL2CPP-Mac.dll.bytes differ diff --git a/Data/ModifiedUnityAssemblies/2019.4.40/Unity.IL2CPP-Mac.dll.bytes.meta b/Data/ModifiedUnityAssemblies/2019.4.40/Unity.IL2CPP-Mac.dll.bytes.meta new file mode 100644 index 0000000..7f1660b --- /dev/null +++ b/Data/ModifiedUnityAssemblies/2019.4.40/Unity.IL2CPP-Mac.dll.bytes.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 22f11da50eb60c245b40028549937af6 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/ModifiedUnityAssemblies/2019.4.40/Unity.IL2CPP-Win.dll.bytes b/Data/ModifiedUnityAssemblies/2019.4.40/Unity.IL2CPP-Win.dll.bytes new file mode 100644 index 0000000..17135a1 Binary files /dev/null and b/Data/ModifiedUnityAssemblies/2019.4.40/Unity.IL2CPP-Win.dll.bytes differ diff --git a/Data/ModifiedUnityAssemblies/2019.4.40/Unity.IL2CPP-Win.dll.bytes.meta b/Data/ModifiedUnityAssemblies/2019.4.40/Unity.IL2CPP-Win.dll.bytes.meta new file mode 100644 index 0000000..7906319 --- /dev/null +++ b/Data/ModifiedUnityAssemblies/2019.4.40/Unity.IL2CPP-Win.dll.bytes.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 94322e93a1f61b340bdc1d5042dab659 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/ModifiedUnityAssemblies/2021.3.1.meta b/Data/ModifiedUnityAssemblies/2021.3.1.meta new file mode 100644 index 0000000..4efc6da --- /dev/null +++ b/Data/ModifiedUnityAssemblies/2021.3.1.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8f2a6638b09b52f4c9dc8906d090b710 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Mac.dll.bytes b/Data/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Mac.dll.bytes new file mode 100644 index 0000000..0eb3f38 Binary files /dev/null and b/Data/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Mac.dll.bytes differ diff --git a/Data/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Mac.dll.bytes.meta b/Data/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Mac.dll.bytes.meta new file mode 100644 index 0000000..625dd7a --- /dev/null +++ b/Data/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Mac.dll.bytes.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a96a2c46326d2a84984c2820bd4bfc91 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Win.dll.bytes b/Data/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Win.dll.bytes new file mode 100644 index 0000000..926857d Binary files /dev/null and b/Data/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Win.dll.bytes differ diff --git a/Data/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Win.dll.bytes.meta b/Data/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Win.dll.bytes.meta new file mode 100644 index 0000000..939b185 --- /dev/null +++ b/Data/ModifiedUnityAssemblies/2021.3.1/UnityEditor.CoreModule-Win.dll.bytes.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 33c064b00b452284f93cd6e89de8a06d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/ModifiedUnityAssemblies/2021.3.6.meta b/Data/ModifiedUnityAssemblies/2021.3.6.meta new file mode 100644 index 0000000..1200709 --- /dev/null +++ b/Data/ModifiedUnityAssemblies/2021.3.6.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d104ab5da8136154899a40116dc88731 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/ModifiedUnityAssemblies/2021.3.6/UnityEditor.CoreModule-Mac.dll.bytes b/Data/ModifiedUnityAssemblies/2021.3.6/UnityEditor.CoreModule-Mac.dll.bytes new file mode 100644 index 0000000..36075c1 Binary files /dev/null and b/Data/ModifiedUnityAssemblies/2021.3.6/UnityEditor.CoreModule-Mac.dll.bytes differ diff --git a/Data/ModifiedUnityAssemblies/2021.3.6/UnityEditor.CoreModule-Mac.dll.bytes.meta b/Data/ModifiedUnityAssemblies/2021.3.6/UnityEditor.CoreModule-Mac.dll.bytes.meta new file mode 100644 index 0000000..1a99f77 --- /dev/null +++ b/Data/ModifiedUnityAssemblies/2021.3.6/UnityEditor.CoreModule-Mac.dll.bytes.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f42c0f03663d4c14f8a49912c08a514f +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/ModifiedUnityAssemblies/2021.3.6/UnityEditor.CoreModule-Win.dll.bytes b/Data/ModifiedUnityAssemblies/2021.3.6/UnityEditor.CoreModule-Win.dll.bytes new file mode 100644 index 0000000..a8176cb Binary files /dev/null and b/Data/ModifiedUnityAssemblies/2021.3.6/UnityEditor.CoreModule-Win.dll.bytes differ diff --git a/Data/ModifiedUnityAssemblies/2021.3.6/UnityEditor.CoreModule-Win.dll.bytes.meta b/Data/ModifiedUnityAssemblies/2021.3.6/UnityEditor.CoreModule-Win.dll.bytes.meta new file mode 100644 index 0000000..6a4c423 --- /dev/null +++ b/Data/ModifiedUnityAssemblies/2021.3.6/UnityEditor.CoreModule-Win.dll.bytes.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b3b3b1f0cbc038149acb4d23bc6d5bf5 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/Templates.meta b/Data/Templates.meta new file mode 100644 index 0000000..2de038c --- /dev/null +++ b/Data/Templates.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f54201f21d7421d4c894298aa5266c73 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/Templates/MethodBridge_Arm64.cpp.txt b/Data/Templates/MethodBridge_Arm64.cpp.txt new file mode 100644 index 0000000..e5cfc75 --- /dev/null +++ b/Data/Templates/MethodBridge_Arm64.cpp.txt @@ -0,0 +1,22 @@ +#include "MethodBridge.h" + +#include +#include "vm/ClassInlines.h" +#include "vm/Object.h" +#include "vm/Class.h" + +#include "../metadata/MetadataModule.h" +#include "../metadata/MetadataUtil.h" + +#include "Interpreter.h" +#include "MemoryUtil.h" +#include "InstrinctDef.h" + +using namespace hybridclr::interpreter; +using hybridclr::GetInterpreterDirectlyCallMethodPointer; + +#if HYBRIDCLR_ABI_ARM_64 +//!!!{{INVOKE_STUB + +//!!!}}INVOKE_STUB +#endif diff --git a/Data/Templates/MethodBridge_Arm64.cpp.txt.meta b/Data/Templates/MethodBridge_Arm64.cpp.txt.meta new file mode 100644 index 0000000..7f4a637 --- /dev/null +++ b/Data/Templates/MethodBridge_Arm64.cpp.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1249fb131a848a14f8dd0938e3cc2558 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/Templates/MethodBridge_Universal32.cpp.txt b/Data/Templates/MethodBridge_Universal32.cpp.txt new file mode 100644 index 0000000..7a2ca8c --- /dev/null +++ b/Data/Templates/MethodBridge_Universal32.cpp.txt @@ -0,0 +1,22 @@ +#include "MethodBridge.h" + +#include +#include "vm/ClassInlines.h" +#include "vm/Object.h" +#include "vm/Class.h" + +#include "../metadata/MetadataModule.h" +#include "../metadata/MetadataUtil.h" + +#include "Interpreter.h" +#include "MemoryUtil.h" +#include "InstrinctDef.h" + +using namespace hybridclr::interpreter; +using hybridclr::GetInterpreterDirectlyCallMethodPointer; + +#if HYBRIDCLR_ABI_UNIVERSAL_32 +//!!!{{INVOKE_STUB + +//!!!}}INVOKE_STUB +#endif diff --git a/Data/Templates/MethodBridge_Universal32.cpp.txt.meta b/Data/Templates/MethodBridge_Universal32.cpp.txt.meta new file mode 100644 index 0000000..6d489e7 --- /dev/null +++ b/Data/Templates/MethodBridge_Universal32.cpp.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e06e14385d246aa4ab853d3fd04d6eec +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/Templates/MethodBridge_Universal64.cpp.txt b/Data/Templates/MethodBridge_Universal64.cpp.txt new file mode 100644 index 0000000..459d43d --- /dev/null +++ b/Data/Templates/MethodBridge_Universal64.cpp.txt @@ -0,0 +1,22 @@ +#include "MethodBridge.h" + +#include +#include "vm/ClassInlines.h" +#include "vm/Object.h" +#include "vm/Class.h" + +#include "../metadata/MetadataModule.h" +#include "../metadata/MetadataUtil.h" + +#include "Interpreter.h" +#include "MemoryUtil.h" +#include "InstrinctDef.h" + +using namespace hybridclr::interpreter; +using hybridclr::GetInterpreterDirectlyCallMethodPointer; + +#if HYBRIDCLR_ABI_UNIVERSAL_64 +//!!!{{INVOKE_STUB + +//!!!}}INVOKE_STUB +#endif diff --git a/Data/Templates/MethodBridge_Universal64.cpp.txt.meta b/Data/Templates/MethodBridge_Universal64.cpp.txt.meta new file mode 100644 index 0000000..914ef8c --- /dev/null +++ b/Data/Templates/MethodBridge_Universal64.cpp.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2965fa89651ee0c4eb4c4fdcf95b76b8 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/Templates/ReversePInvokeMethodStub.cpp.txt b/Data/Templates/ReversePInvokeMethodStub.cpp.txt new file mode 100644 index 0000000..1f298b9 --- /dev/null +++ b/Data/Templates/ReversePInvokeMethodStub.cpp.txt @@ -0,0 +1,36 @@ +#include "ReversePInvokeMethodStub.h" +#include "MetadataModule.h" + +namespace hybridclr +{ +namespace metadata +{ + //!!!{{REVERSE_PINVOKE_METHOD_STUB + + void CallLuaFunction(void* xState, int32_t wrapperIndex) + { + const MethodInfo* method = MetadataModule::GetMethodInfoByReversePInvokeWrapperIndex(wrapperIndex); + typedef void (*Callback)(void* xState, const MethodInfo* method); + ((Callback)GetInterpreterDirectlyCallMethodPointer(method))(xState, method); + } + + void __ReversePInvokeMethod_0(void* xState) + { + CallLuaFunction(xState, 0); + } + + void __ReversePInvokeMethod_1(void* xState) + { + CallLuaFunction(xState, 1); + } + + Il2CppMethodPointer s_ReversePInvokeMethodStub[] + { + (Il2CppMethodPointer)__ReversePInvokeMethod_0, + (Il2CppMethodPointer)__ReversePInvokeMethod_1, + nullptr, + }; + + //!!!}}REVERSE_PINVOKE_METHOD_STUB +} +} \ No newline at end of file diff --git a/Data/Templates/ReversePInvokeMethodStub.cpp.txt.meta b/Data/Templates/ReversePInvokeMethodStub.cpp.txt.meta new file mode 100644 index 0000000..42dda1d --- /dev/null +++ b/Data/Templates/ReversePInvokeMethodStub.cpp.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 6447cdc1825eeef49a3679545ac31d95 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/iOSBuild.meta b/Data/iOSBuild.meta new file mode 100644 index 0000000..ae67437 --- /dev/null +++ b/Data/iOSBuild.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c9442afcb42f28646a509d70ffe3d880 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/iOSBuild/CMakeLists.txt b/Data/iOSBuild/CMakeLists.txt new file mode 100644 index 0000000..3345fde --- /dev/null +++ b/Data/iOSBuild/CMakeLists.txt @@ -0,0 +1,88 @@ +# file: CMakeList.txt +cmake_minimum_required(VERSION 3.0) +set(CMAKE_OSX_DEPLOYMENT_TARGET iOS) + +set(TMP $ENV{HUATUO_IL2CPP_SOURCE_DIR}) +if ( NOT TMP ) + message(FATAL_ERROR "需要设置环境变量: HUATUO_IL2CPP_SOURCE_DIR") +else() + message(STATUS "unity il2cpp 路径为: ${TMP}") +endif() + +set(SDK_VERSION $ENV{IPHONESIMULATOR_VERSION}) +if ( SDK_VERSION ) + message(STATUS "使用iPhoneSimulator版本:" ${SDK_VERSION}) +else() + message(STATUS "当前使用默认版本的iPhoneSimulator,可以通过设置环境变量IPHONESIMULATOR_VERSION,指定版本") +endif() + +project(il2cpp) +execute_process(COMMAND sh gen_lump.sh ${PROJECT_BINARY_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + +add_subdirectory(external) +add_subdirectory(objective) + + +set(IL2CPP_SOURCE_DIR $ENV{HUATUO_IL2CPP_SOURCE_DIR}) + +message(STATUS "il2cpp project, binary dir: " ${CMAKE_BINARY_DIR}) +message(STATUS "il2cpp project, build dir: " ${PROJECT_BINARY_DIR}) +message(STATUS "il2cpp project, il2cpp source dir: " ${IL2CPP_SOURCE_DIR}) + + +SET_PROPERTY(GLOBAL PROPERTY USE_FOLDERS ON) +find_program(CLANG_EXECUTABLE NAMES clang clang-7 clang-8 clang-9 clang-10) +if (NOT CLANG_EXECUTABLE) + message(FATAL_ERROR "Cannot find any clang executable.") +endif() + + +set(CMAKE_OSX_SYSROOT /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator${SDK_VERSION}.sdk) + +#set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_CXX_COMPILER clang++) +set(CMAKE_C_COMPILER clang) +#add_compile_options(-x objective-c++) + +# 以下命令为复制的Xcode生成中的命令 +set(CMAKE_CXX_FLAGS "-target arm64-apple-ios11.0 -fmessage-length=0 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0 -std=gnu++1z -fmodules -fmodules-prune-interval=86400 -fmodules-prune-after=345600 -fmodules-ignore-macro=IL2CPP_TARGET_IOS=1 -fembed-bitcode -fmodules-ignore-macro=BASELIB_DYNAMICLIBRARY=1 -fmodules-ignore-macro=BASELIB_INLINE_NAMESPACE=il2cpp_baselib -fmodules-ignore-macro=AARCH64 -fmodules-ignore-macro=__aarch64__ -fmodules-ignore-macro=IL2CPP_SUPPORT_THREADS -fmodules-ignore-macro=IL2CPP_THREADS_PTHREAD -Wnon-modular-include-in-framework-module -Werror=non-modular-include-in-framework-module -Wno-trigraphs -fpascal-strings -Os -fno-common -Wno-missing-field-initializers -Wno-missing-prototypes -Werror=return-type -Wdocumentation -Wunreachable-code -Wquoted-include-in-framework-header -Werror=deprecated-objc-isa-usage -Werror=objc-root-class -Wno-non-virtual-dtor -Wno-overloaded-virtual -Wno-exit-time-destructors -Wno-missing-braces -Wparentheses -Wswitch -Wno-unused-function -Wno-unused-label -Wno-unused-parameter -Wno-unused-variable -Wunused-value -Wempty-body -Wuninitialized -Wconditional-uninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wno-float-conversion -Wnon-literal-null-conversion -Wobjc-literal-conversion -Wshorten-64-to-32 -Wno-newline-eof -Wno-c++11-extensions -DIL2CPP_TARGET_IOS=1 -isysroot ${CMAKE_OSX_SYSROOT} -fasm-blocks -fstrict-aliasing -Wdeprecated-declarations -Winvalid-offsetof -g -Wno-sign-conversion -Winfinite-recursion -Wmove -Wcomma -Wblock-capture-autoreleasing -Wstrict-prototypes -Wrange-loop-analysis -Wno-semicolon-before-method-body -Wunguarded-availability ") + + +add_definitions(-DIL2CPP_TARGET_IOS=1) +add_definitions(-DBASELIB_DYNAMICLIBRARY=1) +add_definitions(-DBASELIB_INLINE_NAMESPACE=il2cpp_baselib) +add_definitions(-DAARCH64) +add_definitions(-D__aarch64__) +add_definitions(-DIL2CPP_SUPPORT_THREADS) +add_definitions(-DIL2CPP_THREADS_PTHREAD) +#add_definitions(-DCMAKE_C_COMPILER=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang) +#add_definitions(-DCMAKE_CXX_COMPILER=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++) +#add_definitions(-DIL2CPP_TARGET_DARWIN) +add_definitions(-DIL2CPP_PLATFORM_SUPPORTS_TIMEZONEINFO) + +add_definitions(-MMD) +add_definitions(-MT dependencies) + + +include_directories(${IL2CPP_SOURCE_DIR}/libil2cpp/) +include_directories(${IL2CPP_SOURCE_DIR}/external/) +include_directories(${IL2CPP_SOURCE_DIR}/external/baselib/Include/) +include_directories(${IL2CPP_SOURCE_DIR}/external/bdwgc/include/) +include_directories(${IL2CPP_SOURCE_DIR}/external/baselib/Platforms/OSX/Include/) + +# 修改为本机对应SDK路径 +include_directories(SYSTEM /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/c++/v1) +include_directories(SYSTEM /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include) + +aux_source_directory(${PROJECT_BINARY_DIR}/lump_cpp LUMP_SOURCE_LIST) +aux_source_directory(${IL2CPP_SOURCE_DIR}/libil2cpp LIBIL2CPP_SOURCE_LIST) + +#find_library(zlib ${PROJECT_SOURCE_DIR}/external) +add_library(il2cpp_original STATIC ${LIBIL2CPP_SOURCE_LIST} ${LUMP_SOURCE_LIST}) +add_dependencies(il2cpp_original external objective) + +add_custom_command(TARGET il2cpp_original + POST_BUILD + COMMAND xcrun -r libtool -static -o libil2cpp.a libil2cpp_original.a external/libexternal.a objective/libobjective.a + COMMENT "post build this is command combine libil2cpp_original.a, libojjective.a and libzlib.a into libil2cpp" + ) diff --git a/Data/iOSBuild/CMakeLists.txt.meta b/Data/iOSBuild/CMakeLists.txt.meta new file mode 100644 index 0000000..323b20a --- /dev/null +++ b/Data/iOSBuild/CMakeLists.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3436274d6dd0cc64fb4d15c898cc1d1d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/iOSBuild/build.meta b/Data/iOSBuild/build.meta new file mode 100644 index 0000000..35d8f2a --- /dev/null +++ b/Data/iOSBuild/build.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 94c0855e86b12b64b8c1d29c51ac795d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/iOSBuild/build_libil2cpp.sh b/Data/iOSBuild/build_libil2cpp.sh new file mode 100644 index 0000000..b67a195 --- /dev/null +++ b/Data/iOSBuild/build_libil2cpp.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +export HUATUO_IL2CPP_SOURCE_DIR=$(pushd ../LocalIl2CppData-OSXEditor/il2cpp > /dev/null && pwd && popd > /dev/null) +export IPHONESIMULATOR_VERSION= + +rm -rf build + +mkdir build +cd build +cmake .. +make -j24 + +if [ -f "libil2cpp.a" ] +then + echo 'build succ' +else + echo "build fail" + exit 1 +fi diff --git a/Data/iOSBuild/build_libil2cpp.sh.meta b/Data/iOSBuild/build_libil2cpp.sh.meta new file mode 100644 index 0000000..25b4e4d --- /dev/null +++ b/Data/iOSBuild/build_libil2cpp.sh.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: cbb71cfc70bb1304f9527eb7ca5d38ee +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/iOSBuild/external.meta b/Data/iOSBuild/external.meta new file mode 100644 index 0000000..d3b9e7c --- /dev/null +++ b/Data/iOSBuild/external.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8ed3bb85d9526f9468289a2e26dadf0b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/iOSBuild/external/CMakeLists.txt b/Data/iOSBuild/external/CMakeLists.txt new file mode 100644 index 0000000..6ccb239 --- /dev/null +++ b/Data/iOSBuild/external/CMakeLists.txt @@ -0,0 +1,47 @@ +# file: external/CMakeList.txt +cmake_minimum_required(VERSION 3.0) +set(CMAKE_OSX_DEPLOYMENT_TARGET iOS) + +set(IL2CPP_SOURCE_DIR $ENV{HUATUO_IL2CPP_SOURCE_DIR}) +set(SDK_VERSION $ENV{IPHONESIMULATOR_VERSION}) + +message(STATUS "external project, external source dir: " ${IL2CPP_SOURCE_DIR}) + +SET_PROPERTY(GLOBAL PROPERTY USE_FOLDERS ON) + +find_program(CLANG_EXECUTABLE NAMES clang clang-7 clang-8 clang-9 clang-10) +if (NOT CLANG_EXECUTABLE) + message(FATAL_ERROR "Cannot find any clang executable.") +endif() + +set(CMAKE_OSX_SYSROOT /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator${SDK_VERSION}.sdk) +set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_C_COMPILER clang) + +set(CMAKE_C_FLAGS "-x c -target arm64-apple-ios11.0 -fmessage-length=0 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0 -std=gnu11 -fmodules -fmodules-prune-interval=86400 -fmodules-prune-after=345600 -fmodules-ignore-macro=IL2CPP_TARGET_IOS=1 -fembed-bitcode -fmodules-ignore-macro=BASELIB_DYNAMICLIBRARY=1 -fmodules-ignore-macro=BASELIB_INLINE_NAMESPACE=il2cpp_baselib -fmodules-ignore-macro=AARCH64 -fmodules-ignore-macro=__aarch64__ -fmodules-ignore-macro=IL2CPP_SUPPORT_THREADS -fmodules-ignore-macro=IL2CPP_THREADS_PTHREAD -Wnon-modular-include-in-framework-module -Werror=non-modular-include-in-framework-module -Wno-trigraphs -fpascal-strings -Os -fno-common -Wno-missing-field-initializers -Wno-missing-prototypes -Werror=return-type -Wdocumentation -Wunreachable-code -Wquoted-include-in-framework-header -Werror=deprecated-objc-isa-usage -Werror=objc-root-class -Wno-missing-braces -Wparentheses -Wswitch -Wunused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wconditional-uninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wno-float-conversion -Wnon-literal-null-conversion -Wobjc-literal-conversion -Wshorten-64-to-32 -Wpointer-sign -Wno-newline-eof -DIL2CPP_TARGET_IOS=1 -isysroot ${CMAKE_OSX_SYSROOT} -fstrict-aliasing -Wdeprecated-declarations -g -Wno-sign-conversion -Winfinite-recursion -Wcomma -Wblock-capture-autoreleasing -Wstrict-prototypes -Wno-semicolon-before-method-body -Wunguarded-availability") + + +add_definitions(-DIL2CPP_TARGET_IOS=1) +add_definitions(-DBASELIB_DYNAMICLIBRARY=1) +add_definitions(-DBASELIB_INLINE_NAMESPACE=il2cpp_baselib) +add_definitions(-DAARCH64) +add_definitions(-D__aarch64__) +#add_definitions(-D__arm64__) +add_definitions(-DIL2CPP_SUPPORT_THREADS) +add_definitions(-DIL2CPP_THREADS_PTHREAD) +#add_definitions(-DCMAKE_C_COMPILER=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang) +#add_definitions(-DCMAKE_CXX_COMPILER=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++) +#add_definitions(-DIL2CPP_TARGET_DARWIN) +add_definitions(-DIL2CPP_PLATFORM_SUPPORTS_TIMEZONEINFO=0) + +add_definitions(-MMD) +add_definitions(-MT dependencies) + +include_directories(${IL2CPP_SOURCE_DIR}/external/) +include_directories(${IL2CPP_SOURCE_DIR}/external/baselib/Include/) +include_directories(${IL2CPP_SOURCE_DIR}/external/bdwgc/include/) +include_directories(${IL2CPP_SOURCE_DIR}/external/baselib/Platforms/OSX/Include/) + +aux_source_directory(${IL2CPP_SOURCE_DIR}/external/zlib/ ZLIB_C_SOURCE_LIST) +add_library(external STATIC ${ZLIB_C_SOURCE_LIST}) + diff --git a/Data/iOSBuild/external/CMakeLists.txt.meta b/Data/iOSBuild/external/CMakeLists.txt.meta new file mode 100644 index 0000000..ee9bbad --- /dev/null +++ b/Data/iOSBuild/external/CMakeLists.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 03b1d6a5f25828042a1d215904917995 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/iOSBuild/gen_lump.sh b/Data/iOSBuild/gen_lump.sh new file mode 100644 index 0000000..a583813 --- /dev/null +++ b/Data/iOSBuild/gen_lump.sh @@ -0,0 +1,64 @@ +echo '=====================================================================' +echo 'gen lump' +echo '$HUATUO_IL2CPP_SOURCE_DIR='${HUATUO_IL2CPP_SOURCE_DIR} #/Applications/Unity/Unity.app/Contents/il2cpp/ + +GEN_SOURCE_DIR=$1 +BASE_DIR=${HUATUO_IL2CPP_SOURCE_DIR}/libil2cpp +echo base dir: ${BASE_DIR} +echo " " +#BASE_DIR=${HUATUO_IL2CPP_SOURCE_DIR}/libil2cpp +function SearchCppFile() +{ + for f in $(ls $1) + do + SUB_DIR=$1/$f + if [ -d ${SUB_DIR} ]; then + SearchCppFile ${SUB_DIR} + fi + done + + CPP_FILE_NUM=`ls -l $1/ | grep "\.cpp$"|wc -l` + if (( ${CPP_FILE_NUM} > 0 )) + then + for f in $1/*.cpp + do + echo "#include \""$f"\"" >> ${OUTPUT_FILE_NAME} + done + fi + + MM_FILE_NUM=`ls -l $1/ | grep "\.mm$"|wc -l` + if (( ${MM_FILE_NUM} > 0 )) + then + for f in $1/*.mm + do + echo "#include \""$f"\"" >> ${OBJECTIVE_FILE_NAME} + done + fi +} + +rm -rf ${GEN_SOURCE_DIR}/lump_cpp +rm -rf ${GEN_SOURCE_DIR}/lump_mm +mkdir ${GEN_SOURCE_DIR}/lump_cpp +mkdir ${GEN_SOURCE_DIR}/lump_mm + +OBJECTIVE_FILE_NAME=${GEN_SOURCE_DIR}/lump_mm/lump_libil2cpp_ojective.mm +echo "#include \"${BASE_DIR}/il2cpp-config.h\"" > ${OBJECTIVE_FILE_NAME} +echo gen file: ${OBJECTIVE_FILE_NAME} + +for FOLDER in hybridclr vm pch utils vm-utils codegen metadata os debugger mono gc icalls +do + OUTPUT_FILE_NAME=${GEN_SOURCE_DIR}/lump_cpp/lump_libil2cpp_${FOLDER}.cpp + echo "#include \"${BASE_DIR}/il2cpp-config.h\"" > ${OUTPUT_FILE_NAME} + if [ $FOLDER = hybridclr ] || [ $FOLDER = vm ] + then + echo "#include \"${BASE_DIR}/codegen/il2cpp-codegen.h\"" >> ${OUTPUT_FILE_NAME} + fi + SearchCppFile ${BASE_DIR}/${FOLDER} + echo gen file: ${OUTPUT_FILE_NAME} +done + +echo gen done. +echo '=====================================================================' +echo " " + + diff --git a/Data/iOSBuild/gen_lump.sh.meta b/Data/iOSBuild/gen_lump.sh.meta new file mode 100644 index 0000000..74be733 --- /dev/null +++ b/Data/iOSBuild/gen_lump.sh.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 641944d55f4cbc4449c3f8b3fcd4d7e5 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/iOSBuild/objective.meta b/Data/iOSBuild/objective.meta new file mode 100644 index 0000000..6be020b --- /dev/null +++ b/Data/iOSBuild/objective.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 01ed218875eaf574694fbe3a8e0c4a64 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Data/iOSBuild/objective/CMakeLists.txt b/Data/iOSBuild/objective/CMakeLists.txt new file mode 100644 index 0000000..9e21221 --- /dev/null +++ b/Data/iOSBuild/objective/CMakeLists.txt @@ -0,0 +1,62 @@ +# file: CMakeList.txt +cmake_minimum_required(VERSION 3.0) +set(CMAKE_OSX_DEPLOYMENT_TARGET iOS) +project(il2cpp) + +set(IL2CPP_SOURCE_DIR $ENV{HUATUO_IL2CPP_SOURCE_DIR}) +set(SDK_VERSION $ENV{IPHONESIMULATOR_VERSION}) + +message(STATUS "objective project, binary dir: " ${PROJECT_BINARY_DIR}) +message(STATUS "objective project, il2cpp source dir: " ${PROJECT_SOURCE_DIR}) + + +SET_PROPERTY(GLOBAL PROPERTY USE_FOLDERS ON) +find_program(CLANG_EXECUTABLE NAMES clang clang-7 clang-8 clang-9 clang-10) +if (NOT CLANG_EXECUTABLE) + message(FATAL_ERROR "Cannot find any clang executable.") +endif() + + +set(CMAKE_OSX_SYSROOT /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator${SDK_VERSION}.sdk) + +set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_CXX_COMPILER clang++) +set(CMAKE_C_COMPILER clang) +#add_compile_options(-x objective-c++) + +# 以下命令为复制的Xcode生成中的命令 +set(CMAKE_CXX_FLAGS "-target arm64-apple-ios11.0 -fmessage-length=0 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0 -std=gnu++1z -fmodules -fmodules-prune-interval=86400 -fmodules-prune-after=345600 -fmodules-ignore-macro=IL2CPP_TARGET_IOS=1 -fembed-bitcode -fmodules-ignore-macro=BASELIB_DYNAMICLIBRARY=1 -fmodules-ignore-macro=BASELIB_INLINE_NAMESPACE=il2cpp_baselib -fmodules-ignore-macro=AARCH64 -fmodules-ignore-macro=__aarch64__ -fmodules-ignore-macro=IL2CPP_SUPPORT_THREADS -fmodules-ignore-macro=IL2CPP_THREADS_PTHREAD -Wnon-modular-include-in-framework-module -Werror=non-modular-include-in-framework-module -Wno-trigraphs -fpascal-strings -Os -fno-common -Wno-missing-field-initializers -Wno-missing-prototypes -Werror=return-type -Wdocumentation -Wunreachable-code -Wquoted-include-in-framework-header -Werror=deprecated-objc-isa-usage -Werror=objc-root-class -Wno-non-virtual-dtor -Wno-overloaded-virtual -Wno-exit-time-destructors -Wno-missing-braces -Wparentheses -Wswitch -Wno-unused-function -Wno-unused-label -Wno-unused-parameter -Wno-unused-variable -Wunused-value -Wempty-body -Wuninitialized -Wconditional-uninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wno-float-conversion -Wnon-literal-null-conversion -Wobjc-literal-conversion -Wshorten-64-to-32 -Wno-newline-eof -Wno-c++11-extensions -DIL2CPP_TARGET_IOS=1 -isysroot ${SDK_VERSION} -fasm-blocks -fstrict-aliasing -Wdeprecated-declarations -Winvalid-offsetof -g -Wno-sign-conversion -Winfinite-recursion -Wmove -Wcomma -Wblock-capture-autoreleasing -Wstrict-prototypes -Wrange-loop-analysis -Wno-semicolon-before-method-body -Wunguarded-availability ") + + +add_definitions(-DIL2CPP_TARGET_IOS=1) +add_definitions(-DBASELIB_DYNAMICLIBRARY=1) +add_definitions(-DBASELIB_INLINE_NAMESPACE=il2cpp_baselib) +add_definitions(-DAARCH64) +add_definitions(-D__aarch64__) +add_definitions(-DIL2CPP_SUPPORT_THREADS) +add_definitions(-DIL2CPP_THREADS_PTHREAD) +#add_definitions(-DCMAKE_C_COMPILER=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang) +#add_definitions(-DCMAKE_CXX_COMPILER=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++) +#add_definitions(-DIL2CPP_TARGET_DARWIN) +add_definitions(-DIL2CPP_PLATFORM_SUPPORTS_TIMEZONEINFO) + +add_definitions(-MMD) +add_definitions(-MT dependencies) + + +include_directories(${IL2CPP_SOURCE_DIR}/libil2cpp) +include_directories(${IL2CPP_SOURCE_DIR}/external) +include_directories(${IL2CPP_SOURCE_DIR}/external/baselib/Include/) +include_directories(${IL2CPP_SOURCE_DIR}/external/bdwgc/include/) +include_directories(${IL2CPP_SOURCE_DIR}/external/baselib/Platforms/OSX/Include/) + +# 修改为本机对应SDK路径 +include_directories(SYSTEM /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/c++/v1) +include_directories(SYSTEM /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include) + +get_filename_component(PARENT_PATH ${PROJECT_BINARY_DIR} PATH) +aux_source_directory(${PARENT_PATH}/lump_mm LUMP_SOURCE_LIST) + +#find_library(zlib ${PROJECT_SOURCE_DIR}/external) +add_library(objective STATIC ${LUMP_SOURCE_LIST}) + diff --git a/Data/iOSBuild/objective/CMakeLists.txt.meta b/Data/iOSBuild/objective/CMakeLists.txt.meta new file mode 100644 index 0000000..85b8741 --- /dev/null +++ b/Data/iOSBuild/objective/CMakeLists.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 76642fe740b6fd74b9a4491ce83b897e +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor.meta b/Editor.meta new file mode 100644 index 0000000..003696a --- /dev/null +++ b/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c3fabc41cf17c444995fc01a76c5dbe6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/AOT.meta b/Editor/AOT.meta new file mode 100644 index 0000000..9b9539f --- /dev/null +++ b/Editor/AOT.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b4071bf66ac9c544487ae88b5ee9b20a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/AOT/Analyzer.cs b/Editor/AOT/Analyzer.cs new file mode 100644 index 0000000..a23cae4 --- /dev/null +++ b/Editor/AOT/Analyzer.cs @@ -0,0 +1,188 @@ +using dnlib.DotNet; +using HybridCLR.Editor.Meta; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace HybridCLR.Editor.AOT +{ + + public class Analyzer + { + public class Options + { + public AssemblyReferenceDeepCollector Collector { get; set; } + + public int MaxIterationCount { get; set; } + } + + private readonly int _maxInterationCount; + + private readonly AssemblyReferenceDeepCollector _assemblyCollector; + + private readonly HashSet _genericTypes = new HashSet(); + private readonly HashSet _genericMethods = new HashSet(); + + private List _processingMethods = new List(); + private List _newMethods = new List(); + + public IReadOnlyCollection GenericTypes => _genericTypes; + + public IReadOnlyCollection GenericMethods => _genericMethods; + + private readonly MethodReferenceAnalyzer _methodReferenceAnalyzer; + + private readonly HashSet _hotUpdateAssemblyFiles; + + public Analyzer(Options options) + { + _assemblyCollector = options.Collector; + _maxInterationCount = options.MaxIterationCount; + _methodReferenceAnalyzer = new MethodReferenceAnalyzer(this.OnNewMethod); + _hotUpdateAssemblyFiles = new HashSet(options.Collector.GetRootAssemblyNames().Select(assName => assName + ".dll")); + } + + private void TryAddAndWalkGenericType(GenericClass gc) + { + if (gc == null) + { + return; + } + gc = gc.ToGenericShare(); + if (_genericTypes.Add(gc) && NeedWalk(gc.Type)) + { + WalkType(gc); + } + } + + private bool NeedWalk(TypeDef type) + { + return _hotUpdateAssemblyFiles.Contains(type.Module.Name); + } + + private void OnNewMethod(GenericMethod method) + { + if(method == null) + { + return; + } + if (_genericMethods.Add(method) && NeedWalk(method.Method.DeclaringType)) + { + _newMethods.Add(method); + } + if (method.KlassInst != null) + { + TryAddAndWalkGenericType(new GenericClass(method.Method.DeclaringType, method.KlassInst)); + } + } + + private void TryAddMethodNotWalkType(GenericMethod method) + { + if (method == null) + { + return; + } + if (_genericMethods.Add(method) && NeedWalk(method.Method.DeclaringType)) + { + _newMethods.Add(method); + } + } + + private void WalkType(GenericClass gc) + { + //Debug.Log($"typespec:{sig} {sig.GenericType} {sig.GenericType.TypeDefOrRef.ResolveTypeDef()}"); + //Debug.Log($"== walk generic type:{new GenericInstSig(gc.Type.ToTypeSig().ToClassOrValueTypeSig(), gc.KlassInst)}"); + ITypeDefOrRef baseType = gc.Type.BaseType; + if (baseType != null && baseType.TryGetGenericInstSig() != null) + { + GenericClass parentType = GenericClass.ResolveClass((TypeSpec)baseType, new GenericArgumentContext(gc.KlassInst, null)); + TryAddAndWalkGenericType(parentType); + } + foreach (var method in gc.Type.Methods) + { + if (method.HasGenericParameters || !method.HasBody || method.Body.Instructions == null) + { + continue; + } + var gm = new GenericMethod(method, gc.KlassInst, null).ToGenericShare(); + //Debug.Log($"add method:{gm.Method} {gm.KlassInst}"); + TryAddMethodNotWalkType(gm); + } + } + + private void WalkType(TypeDef typeDef) + { + if (typeDef.HasGenericParameters) + { + return; + } + ITypeDefOrRef baseType = typeDef.BaseType; + if (baseType != null && baseType.TryGetGenericInstSig() != null) + { + GenericClass gc = GenericClass.ResolveClass((TypeSpec)baseType, null); + TryAddAndWalkGenericType(gc); + } + } + + private void Prepare() + { + // 将所有非泛型函数全部加入函数列表,同时立马walk这些method。 + // 后续迭代中将只遍历MethodSpec + foreach (var ass in _assemblyCollector.GetLoadedModulesOfRootAssemblies()) + { + foreach (TypeDef typeDef in ass.GetTypes()) + { + WalkType(typeDef); + } + + for (uint rid = 1, n = ass.Metadata.TablesStream.TypeSpecTable.Rows; rid <= n; rid++) + { + var ts = ass.ResolveTypeSpec(rid); + if (!ts.ContainsGenericParameter) + { + var cs = GenericClass.ResolveClass(ts, null)?.ToGenericShare(); + TryAddAndWalkGenericType(cs); + } + } + + for (uint rid = 1, n = ass.Metadata.TablesStream.MethodSpecTable.Rows; rid <= n; rid++) + { + var ms = ass.ResolveMethodSpec(rid); + if (ms.DeclaringType.ContainsGenericParameter || ms.GenericInstMethodSig.ContainsGenericParameter) + { + continue; + } + var gm = GenericMethod.ResolveMethod(ms, null)?.ToGenericShare(); + TryAddMethodNotWalkType(gm); + } + } + Debug.Log($"PostPrepare genericTypes:{_genericTypes.Count} genericMethods:{_genericMethods.Count} newMethods:{_newMethods.Count}"); + } + + private void RecursiveCollect() + { + for (int i = 0; i < _maxInterationCount && _newMethods.Count > 0; i++) + { + var temp = _processingMethods; + _processingMethods = _newMethods; + _newMethods = temp; + _newMethods.Clear(); + + foreach (var method in _processingMethods) + { + _methodReferenceAnalyzer.WalkMethod(method.Method, method.KlassInst, method.MethodInst); + } + Debug.Log($"iteration:[{i}] genericClass:{_genericTypes.Count} genericMethods:{_genericMethods.Count} newMethods:{_newMethods.Count}"); + } + } + + public void Run() + { + Prepare(); + RecursiveCollect(); + } + } +} diff --git a/Editor/AOT/Analyzer.cs.meta b/Editor/AOT/Analyzer.cs.meta new file mode 100644 index 0000000..2e573fc --- /dev/null +++ b/Editor/AOT/Analyzer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 30bbf4a80a6cf3a43b3f489747d9dd6a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/AOT/GenericReferenceWriter.cs b/Editor/AOT/GenericReferenceWriter.cs new file mode 100644 index 0000000..7fd30e1 --- /dev/null +++ b/Editor/AOT/GenericReferenceWriter.cs @@ -0,0 +1,46 @@ +using HybridCLR.Editor.Meta; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace HybridCLR.Editor.AOT +{ + public class GenericReferenceWriter + { + public void Write(List types, List methods, string outputFile) + { + string parentDir = Directory.GetParent(outputFile).FullName; + Directory.CreateDirectory(parentDir); + + List codes = new List(); + codes.Add("public class AOTGenericReferences : UnityEngine.MonoBehaviour"); + codes.Add("{"); + + + foreach(var type in types) + { + codes.Add($"\t//{type.Type}"); + } + + codes.Add("\tpublic void RefMethods()"); + codes.Add("\t{"); + + foreach(var method in methods) + { + codes.Add($"\t\t// {method.Method}"); + } + codes.Add("\t}"); + + codes.Add("}"); + + + var utf8WithoutBOM = new System.Text.UTF8Encoding(false); + File.WriteAllText(outputFile, string.Join("\n", codes), utf8WithoutBOM); + Debug.Log($"[GenericReferenceWriter] write {outputFile}"); + } + } +} diff --git a/Editor/AOT/GenericReferenceWriter.cs.meta b/Editor/AOT/GenericReferenceWriter.cs.meta new file mode 100644 index 0000000..9fc1497 --- /dev/null +++ b/Editor/AOT/GenericReferenceWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d1243cf04685361478972f93b5ca868a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/BuildProcessors.meta b/Editor/BuildProcessors.meta new file mode 100644 index 0000000..428a0a1 --- /dev/null +++ b/Editor/BuildProcessors.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f80d2287f01c89642a74b0a60f7a3305 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/BuildProcessors/CheckSettings.cs b/Editor/BuildProcessors/CheckSettings.cs new file mode 100644 index 0000000..2c181c2 --- /dev/null +++ b/Editor/BuildProcessors/CheckSettings.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; +using UnityEngine; + +namespace HybridCLR.Editor.BuildProcessors +{ + internal class CheckSettings : IPreprocessBuildWithReport + { + public int callbackOrder => 0; + + public void OnPreprocessBuild(BuildReport report) + { +#if !UNITY_2020_1_OR_NEWER || !UNITY_IOS + if (!SettingsUtil.Enable) + { + string oldIl2cppPath = Environment.GetEnvironmentVariable("UNITY_IL2CPP_PATH"); + if (!string.IsNullOrEmpty(oldIl2cppPath)) + { + Environment.SetEnvironmentVariable("UNITY_IL2CPP_PATH", ""); + Debug.Log($"[CheckSettings] 清除 UNITY_IL2CPP_PATH, 旧值为:'{oldIl2cppPath}'"); + } + } + else + { + string curIl2cppPath = Environment.GetEnvironmentVariable("UNITY_IL2CPP_PATH"); + if (curIl2cppPath != SettingsUtil.LocalIl2CppDir) + { + Environment.SetEnvironmentVariable("UNITY_IL2CPP_PATH", SettingsUtil.LocalIl2CppDir); + Debug.Log($"[CheckSettings] UNITY_IL2CPP_PATH 当前值为:'{curIl2cppPath}',更新为:'{SettingsUtil.LocalIl2CppDir}'"); + } + } +#endif + if (!SettingsUtil.Enable) + { + return; + } + if (UnityEditor.PlayerSettings.gcIncremental) + { + Debug.LogError($"[CheckSettings] HybridCLR不支持增量式GC,已经自动将该选项关闭"); + UnityEditor.PlayerSettings.gcIncremental = false; + } + + var installer = new Installer.InstallerController(); + if (!installer.HasInstalledHybridCLR()) + { + throw new Exception($"你没有初始化HybridCLR,请通过菜单'HybridCLR/Installer'安装"); + } + + HybridCLRGlobalSettings gs = SettingsUtil.GlobalSettings; + if (((gs.hotUpdateAssemblies?.Length + gs.hotUpdateAssemblyDefinitions?.Length) ?? 0) == 0) + { + throw new Exception($"GlobalSettings中未配置热更新dll"); + } + + } + } +} diff --git a/Editor/BuildProcessors/CheckSettings.cs.meta b/Editor/BuildProcessors/CheckSettings.cs.meta new file mode 100644 index 0000000..0cc3643 --- /dev/null +++ b/Editor/BuildProcessors/CheckSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fb4ba063068b17247b2d0233420aa5f0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/BuildProcessors/CopyStrippedAOTAssemblies.cs b/Editor/BuildProcessors/CopyStrippedAOTAssemblies.cs new file mode 100644 index 0000000..fcce70c --- /dev/null +++ b/Editor/BuildProcessors/CopyStrippedAOTAssemblies.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEditor; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; +using UnityEditor.Il2Cpp; +using UnityEditor.UnityLinker; +using UnityEngine; + +namespace HybridCLR.Editor.BuildProcessors +{ + internal class CopyStrippedAOTAssemblies : IPostprocessBuildWithReport +#if !UNITY_2021_1_OR_NEWER + , IIl2CppProcessor +#endif + { + + public int callbackOrder => 0; + +#if UNITY_2021_1_OR_NEWER + public static string GetStripAssembliesDir2021(BuildTarget target) + { + string projectDir = SettingsUtil.ProjectDir; +#if UNITY_STANDALONE_WIN + return $"{projectDir}/Library/Bee/artifacts/WinPlayerBuildProgram/ManagedStripped"; +#elif UNITY_ANDROID + return $"{projectDir}/Library/Bee/artifacts/Android/ManagedStripped"; +#elif UNITY_IOS + return $"{projectDir}/Temp/StagingArea/Data/Managed/tempStrip"; +#elif UNITY_WEBGL + return $"{projectDir}/Library/Bee/artifacts/WebGL/ManagedStripped"; +#elif UNITY_EDITOR_OSX + return $"{projectDir}/Library/Bee/artifacts/MacStandalonePlayerBuildProgram/ManagedStripped"; +#else + throw new NotSupportedException("GetOriginBuildStripAssembliesDir"); +#endif + } +#else + private string GetStripAssembliesDir2020(BuildTarget target) + { + string subPath = target == BuildTarget.Android ? + "assets/bin/Data/Managed" : + "Data/Managed/"; + return $"{SettingsUtil.ProjectDir}/Temp/StagingArea/{subPath}"; + } + + public void OnBeforeConvertRun(BuildReport report, Il2CppBuildPipelineData data) + { + // 此回调只在 2020中调用 + CopyStripDlls(GetStripAssembliesDir2020(data.target), data.target); + } +#endif + + public static void CopyStripDlls(string srcStripDllPath, BuildTarget target) + { + Debug.Log($"[CopyStrippedAOTAssemblies] CopyScripDlls. src:{srcStripDllPath} target:{target}"); + + var dstPath = SettingsUtil.GetAssembliesPostIl2CppStripDir(target); + + Directory.CreateDirectory(dstPath); + + foreach (var fileFullPath in Directory.GetFiles(srcStripDllPath, "*.dll")) + { + var file = Path.GetFileName(fileFullPath); + Debug.Log($"[CopyStrippedAOTAssemblies] copy strip dll {fileFullPath} ==> {dstPath}/{file}"); + File.Copy($"{fileFullPath}", $"{dstPath}/{file}", true); + } + } + + public void OnPostprocessBuild(BuildReport report) + { +#if UNITY_2021_1_OR_NEWER && !UNITY_IOS + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + CopyStripDlls(GetStripAssembliesDir2021(target), target); +#endif + } + } +} diff --git a/Editor/BuildProcessors/CopyStrippedAOTAssemblies.cs.meta b/Editor/BuildProcessors/CopyStrippedAOTAssemblies.cs.meta new file mode 100644 index 0000000..3ebc0e1 --- /dev/null +++ b/Editor/BuildProcessors/CopyStrippedAOTAssemblies.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f7884710ec2f8e545b3fe9aa05def5a8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/BuildProcessors/FilterHotFixAssemblies.cs b/Editor/BuildProcessors/FilterHotFixAssemblies.cs new file mode 100644 index 0000000..bd6cdc5 --- /dev/null +++ b/Editor/BuildProcessors/FilterHotFixAssemblies.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEditor; +using UnityEditor.Build; +using UnityEngine; + +namespace HybridCLR.Editor.BuildProcessors +{ + /// + /// 将热更新dll从Build过程中过滤,防止打包到主工程中 + /// + internal class FilterHotFixAssemblies : IFilterBuildAssemblies + { + public int callbackOrder => 0; + + public string[] OnFilterAssemblies(BuildOptions buildOptions, string[] assemblies) + { + if (!SettingsUtil.Enable) + { + Debug.Log($"[FilterHotFixAssemblies] disabled"); + return assemblies; + } + List allHotUpdateDllFiles = SettingsUtil.HotUpdateAssemblyFiles; + + // 检查是否重复填写 + var hotUpdateDllSet = new HashSet(); + foreach(var hotUpdateDll in allHotUpdateDllFiles) + { + if (!hotUpdateDllSet.Add(hotUpdateDll)) + { + throw new Exception($"热更新 assembly:{hotUpdateDll} 在列表中重复,请除去重复条目"); + } + } + + // 检查是否填写了正确的dll名称 + foreach (var hotUpdateDll in allHotUpdateDllFiles) + { + if (assemblies.All(ass => !ass.EndsWith(hotUpdateDll))) + { + throw new Exception($"热更新 assembly:{hotUpdateDll} 不存在,请检查拼写错误"); + } + Debug.Log($"[FilterHotFixAssemblies] 过滤热更新assembly:{hotUpdateDll}"); + } + + // 将热更dll从打包列表中移除 + return assemblies.Where(ass => allHotUpdateDllFiles.All(dll => !ass.EndsWith(dll, StringComparison.OrdinalIgnoreCase))).ToArray(); + } + } +} diff --git a/Editor/BuildProcessors/FilterHotFixAssemblies.cs.meta b/Editor/BuildProcessors/FilterHotFixAssemblies.cs.meta new file mode 100644 index 0000000..2ab4ba5 --- /dev/null +++ b/Editor/BuildProcessors/FilterHotFixAssemblies.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9dec2922e3df5464aa047b636eb19e0d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/BuildProcessors/PatchScriptingAssemblyList.cs b/Editor/BuildProcessors/PatchScriptingAssemblyList.cs new file mode 100644 index 0000000..2d2192d --- /dev/null +++ b/Editor/BuildProcessors/PatchScriptingAssemblyList.cs @@ -0,0 +1,143 @@ +using HybridCLR.Editor.UnityBinFileReader; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEditor; +using UnityEditor.Android; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; +using UnityEditor.Il2Cpp; +using UnityEditor.UnityLinker; +using UnityEngine; +using UnityFS; + +namespace HybridCLR.Editor.BuildProcessors +{ + public class PatchScriptingAssemblyList : +#if UNITY_ANDROID + IPostGenerateGradleAndroidProject, +#endif + IPostprocessBuildWithReport + { + public int callbackOrder => 0; + + + + + public void OnPostGenerateGradleAndroidProject(string path) + { + // 如果直接打包apk,没有机会在PostprocessBuild中修改ScriptingAssemblies.json。 + // 因此需要在这个时机处理 + PathScriptingAssembilesFile(path); + } + + public void OnPostprocessBuild(BuildReport report) + { + // 如果target为Android,由于已经在OnPostGenerateGradelAndroidProject中处理过, + // 这里不再重复处理 +#if !UNITY_ANDROID + + PathScriptingAssembilesFile(report.summary.outputPath); +#endif + } + + private void PathScriptingAssembilesFile(string path) + { + if (!SettingsUtil.Enable) + { + Debug.Log($"[PatchScriptingAssemblyList] disabled"); + return; + } + Debug.Log($"[PatchScriptingAssemblyList]. path:{path}"); + if (!Directory.Exists(path)) + { + path = Path.GetDirectoryName(path); + Debug.Log($"[PatchScriptingAssemblyList] get path parent:{path}"); + } +#if UNITY_2020_1_OR_NEWER + AddHotFixAssembliesToScriptingAssembliesJson(path); +#else + AddHotFixAssembliesToBinFile(path); +#endif + } + + private void AddHotFixAssembliesToScriptingAssembliesJson(string path) + { + Debug.Log($"[PatchScriptingAssemblyList]. path:{path}"); + /* + * ScriptingAssemblies.json 文件中记录了所有的dll名称,此列表在游戏启动时自动加载, + * 不在此列表中的dll在资源反序列化时无法被找到其类型 + * 因此 OnFilterAssemblies 中移除的条目需要再加回来 + */ + string[] jsonFiles = Directory.GetFiles(path, SettingsUtil.ScriptingAssembliesJsonFile, SearchOption.AllDirectories); + + if (jsonFiles.Length == 0) + { + Debug.LogError($"can not find file {SettingsUtil.ScriptingAssembliesJsonFile}"); + return; + } + + foreach (string file in jsonFiles) + { + var patcher = new ScriptingAssembliesJsonPatcher(); + patcher.Load(file); + patcher.AddScriptingAssemblies(SettingsUtil.HotUpdateAssemblyFiles); + patcher.Save(file); + } + } + + private void AddHotFixAssembliesToBinFile(string path) + { + if (AddHotFixAssembliesToGlobalgamemanagers(path)) + { + return; + } + if (AddHotFixAssembliesTodataunity3d(path)) + { + return; + } + Debug.LogError($"[PatchScriptingAssemblyList] can not find file '{SettingsUtil.GlobalgamemanagersBinFile}' or '{SettingsUtil.Dataunity3dBinFile}' in '{path}'"); + } + + private bool AddHotFixAssembliesToGlobalgamemanagers(string path) + { + string[] binFiles = Directory.GetFiles(path, SettingsUtil.GlobalgamemanagersBinFile, SearchOption.AllDirectories); + + if (binFiles.Length == 0) + { + return false; + } + + foreach (string binPath in binFiles) + { + var binFile = new UnityBinFile(); + binFile.Load(binPath); + binFile.AddScriptingAssemblies(SettingsUtil.HotUpdateAssemblyFiles); + binFile.Save(binPath); + Debug.Log($"[PatchScriptingAssemblyList] patch {binPath}"); + } + return true; + } + + private bool AddHotFixAssembliesTodataunity3d(string path) + { + string[] binFiles = Directory.GetFiles(path, SettingsUtil.Dataunity3dBinFile, SearchOption.AllDirectories); + + if (binFiles.Length == 0) + { + return false; + } + + foreach (string binPath in binFiles) + { + var patcher = new Dataunity3dPatcher(); + patcher.ApplyPatch(binPath, SettingsUtil.HotUpdateAssemblyFiles); + Debug.Log($"[PatchScriptingAssemblyList] patch {binPath}"); + } + return true; + } + } +} diff --git a/Editor/BuildProcessors/PatchScriptingAssemblyList.cs.meta b/Editor/BuildProcessors/PatchScriptingAssemblyList.cs.meta new file mode 100644 index 0000000..0affdc7 --- /dev/null +++ b/Editor/BuildProcessors/PatchScriptingAssemblyList.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9bb6e2908d8948648979c9ff6bb7937d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Commands.meta b/Editor/Commands.meta new file mode 100644 index 0000000..c61c778 --- /dev/null +++ b/Editor/Commands.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 92f51c069d2607447ae2f61de80540fb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Commands/AOTReferenceGeneratorCommand.cs b/Editor/Commands/AOTReferenceGeneratorCommand.cs new file mode 100644 index 0000000..9946020 --- /dev/null +++ b/Editor/Commands/AOTReferenceGeneratorCommand.cs @@ -0,0 +1,46 @@ +using HybridCLR.Editor.AOT; +using HybridCLR.Editor.Meta; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace HybridCLR.Editor.Commands +{ + public static class AOTReferenceGeneratorCommand + { + + [MenuItem("HybridCLR/GenerateAOTGenericReference", priority = 18)] + public static void GenerateAOTGenericReference() + { + GenerateAOTGenericReference(true); + } + + public static void GenerateAOTGenericReference(bool compileDll) + { + // 此处理论会有点问题,打每个平台的时候,都得针对当前平台生成桥接函数 + // 但影响不大,先这样吧 + if (compileDll) + { + CompileDllCommand.CompileDllActiveBuildTarget(); + } + + var gs = SettingsUtil.GlobalSettings; + + var analyzer = new Analyzer(new Analyzer.Options + { + MaxIterationCount = Math.Min(20, gs.maxGenericReferenceIteration), + Collector = new AssemblyReferenceDeepCollector(MetaUtil.CreateBuildTargetAssemblyResolver(EditorUserBuildSettings.activeBuildTarget), SettingsUtil.HotUpdateAssemblyNames), + }); + + analyzer.Run(); + + var writer = new GenericReferenceWriter(); + writer.Write(analyzer.GenericTypes.ToList(), analyzer.GenericMethods.ToList(), $"{Application.dataPath}/{gs.outputAOTGenericReferenceFile}"); + AssetDatabase.Refresh(); + } + } +} diff --git a/Editor/Commands/AOTReferenceGeneratorCommand.cs.meta b/Editor/Commands/AOTReferenceGeneratorCommand.cs.meta new file mode 100644 index 0000000..39760e8 --- /dev/null +++ b/Editor/Commands/AOTReferenceGeneratorCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2b464872c07f6ba4f9a4e4a02ca9a28c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Commands/CompileDllCommand.cs b/Editor/Commands/CompileDllCommand.cs new file mode 100644 index 0000000..ec1ded6 --- /dev/null +++ b/Editor/Commands/CompileDllCommand.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEditor; +using UnityEditor.Build.Player; +using UnityEngine; + +namespace HybridCLR.Editor.Commands +{ + public class CompileDllCommand + { + public static void CompileDll(string buildDir, BuildTarget target) + { + var group = BuildPipeline.GetBuildTargetGroup(target); + + ScriptCompilationSettings scriptCompilationSettings = new ScriptCompilationSettings(); + scriptCompilationSettings.group = group; + scriptCompilationSettings.target = target; + Directory.CreateDirectory(buildDir); + ScriptCompilationResult scriptCompilationResult = PlayerBuildInterface.CompilePlayerScripts(scriptCompilationSettings, buildDir); + foreach (var ass in scriptCompilationResult.assemblies) + { + //Debug.LogFormat("compile assemblies:{1}/{0}", ass, buildDir); + } + } + + public static void CompileDll(BuildTarget target) + { + CompileDll(SettingsUtil.GetHotFixDllsOutputDirByTarget(target), target); + } + + [MenuItem("HybridCLR/CompileDll/ActiveBuildTarget")] + public static void CompileDllActiveBuildTarget() + { + CompileDll(EditorUserBuildSettings.activeBuildTarget); + } + + [MenuItem("HybridCLR/CompileDll/Win32")] + public static void CompileDllWin32() + { + CompileDll(BuildTarget.StandaloneWindows); + } + + [MenuItem("HybridCLR/CompileDll/Win64")] + public static void CompileDllWin64() + { + CompileDll(BuildTarget.StandaloneWindows64); + } + + [MenuItem("HybridCLR/CompileDll/Android")] + public static void CompileDllAndroid() + { + CompileDll(BuildTarget.Android); + } + + [MenuItem("HybridCLR/CompileDll/IOS")] + public static void CompileDllIOS() + { + CompileDll(BuildTarget.iOS); + } + } +} diff --git a/Editor/Commands/CompileDllCommand.cs.meta b/Editor/Commands/CompileDllCommand.cs.meta new file mode 100644 index 0000000..88b2e30 --- /dev/null +++ b/Editor/Commands/CompileDllCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bf11b6c8bbc5afd4cb4a11921e5bd81e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Commands/LinkGeneratorCommand.cs b/Editor/Commands/LinkGeneratorCommand.cs new file mode 100644 index 0000000..f066919 --- /dev/null +++ b/Editor/Commands/LinkGeneratorCommand.cs @@ -0,0 +1,61 @@ +using HybridCLR.Editor.LinkGenerator; +using HybridCLR.Editor.Meta; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace HybridCLR.Editor.Commands +{ + + public static class LinkGeneratorCommand + { + + [MenuItem("HybridCLR/GenerateLinkXml", priority = 10)] + public static void GenerateLinkXml() + { + GenerateLinkXml(true); + } + + public static void GenerateLinkXml(bool compileDll) + { + if (compileDll) + { + CompileDllCommand.CompileDllActiveBuildTarget(); + } + + var ls = SettingsUtil.GlobalSettings; + + var allAssByNames = new Dictionary(); + foreach (var ass in AppDomain.CurrentDomain.GetAssemblies()) + { + allAssByNames[ass.GetName().Name] = ass; + } + + var hotfixAssembles = new List(); + foreach(var assName in SettingsUtil.HotUpdateAssemblyNames) + { + if (allAssByNames.TryGetValue(assName, out var ass)) + { + hotfixAssembles.Add(ass); + } + else + { + throw new Exception($"assembly:{assName} 不存在"); + } + } + + var analyzer = new Analyzer(MetaUtil.CreateBuildTargetAssemblyResolver(EditorUserBuildSettings.activeBuildTarget)); + var refTypes = analyzer.CollectRefs(hotfixAssembles); + + Debug.Log($"[LinkGeneratorCommand] hotfix assembly count:{hotfixAssembles.Count}, ref type count:{refTypes.Count} output:{Application.dataPath}/{ls.outputLinkFile}"); + var linkXmlWriter = new LinkXmlWriter(); + linkXmlWriter.Write($"{Application.dataPath}/{ls.outputLinkFile}", refTypes); + } + } +} diff --git a/Editor/Commands/LinkGeneratorCommand.cs.meta b/Editor/Commands/LinkGeneratorCommand.cs.meta new file mode 100644 index 0000000..0eeba1f --- /dev/null +++ b/Editor/Commands/LinkGeneratorCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4f5b96abdbc4c424eb1bc3bc34b3a1a4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Commands/MethodBridgeGeneratorCommand.cs b/Editor/Commands/MethodBridgeGeneratorCommand.cs new file mode 100644 index 0000000..f7697e2 --- /dev/null +++ b/Editor/Commands/MethodBridgeGeneratorCommand.cs @@ -0,0 +1,103 @@ +using HybridCLR.Editor; +using HybridCLR.Editor.Meta; +using HybridCLR.Editor.MethodBridgeGenerator; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace HybridCLR.Editor.Commands +{ + public class MethodBridgeGeneratorCommand + { + + public static void CleanIl2CppBuildCache() + { + string il2cppBuildCachePath = SettingsUtil.Il2CppBuildCacheDir; + if (!Directory.Exists(il2cppBuildCachePath)) + { + return; + } + Debug.Log($"clean il2cpp build cache:{il2cppBuildCachePath}"); + Directory.Delete(il2cppBuildCachePath, true); + } + + private static string GetTemplateCode(PlatformABI platform) + { + string tplFile; + + switch (platform) + { + case PlatformABI.Universal32: tplFile = "Universal32"; break; + case PlatformABI.Universal64: tplFile = "Universal64"; break; + case PlatformABI.Arm64: tplFile = "Arm64"; break; + default: throw new NotSupportedException(); + }; + return AssetDatabase.LoadAssetAtPath($"{SettingsUtil.TemplatePathInPackage}/MethodBridge_{tplFile}.cpp.txt").text; + } + + private static void GenerateMethodBridgeCppFile(Analyzer analyzer, PlatformABI platform, string templateCode, string outputFile) + { + var g = new Generator(new Generator.Options() + { + CallConvention = platform, + TemplateCode = templateCode, + OutputFile = outputFile, + GenericMethods = analyzer.GenericMethods, + NotGenericMethods = analyzer.NotGenericMethods, + }); + + g.PrepareMethods(); + g.Generate(); + Debug.LogFormat("== output:{0} ==", outputFile); + CleanIl2CppBuildCache(); + } + + [MenuItem("HybridCLR/GenerateMethodBridge", priority = 15)] + public static void GenerateMethodBridge() + { + GenerateMethodBridge(true); + } + + public static void GenerateMethodBridge(bool compileDll) + { + // 此处理论会有点问题,打每个平台的时候,都得针对当前平台生成桥接函数 + // 但影响不大,先这样吧 + if (compileDll) + { + CompileDllCommand.CompileDllActiveBuildTarget(); + } + var analyzer = new Analyzer(new Analyzer.Options + { + MaxIterationCount = Math.Min(20, SettingsUtil.GlobalSettings.maxMethodBridgeGenericIteration), + Collector = new AssemblyReferenceDeepCollector(MetaUtil.CreateBuildTargetAssemblyResolver(EditorUserBuildSettings.activeBuildTarget), SettingsUtil.HotUpdateAssemblyNames), + }); + + analyzer.Run(); + + var generateJobs = new List<(PlatformABI, string)>() + { + (PlatformABI.Arm64, "MethodBridge_Arm64"), + (PlatformABI.Universal64, "MethodBridge_Universal64"), + (PlatformABI.Universal32, "MethodBridge_Universal32"), + }; + + var tasks = new List(); + foreach (var (platform, stubFile) in generateJobs) + { + string templateCode = GetTemplateCode(platform); + string outputFile = $"{SettingsUtil.MethodBridgeCppDir}/{stubFile}.cpp"; + tasks.Add(Task.Run(() => + { + GenerateMethodBridgeCppFile(analyzer, platform, templateCode, outputFile); + })); + } + Task.WaitAll(tasks.ToArray()); + } + } +} diff --git a/Editor/Commands/MethodBridgeGeneratorCommand.cs.meta b/Editor/Commands/MethodBridgeGeneratorCommand.cs.meta new file mode 100644 index 0000000..9827997 --- /dev/null +++ b/Editor/Commands/MethodBridgeGeneratorCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 46bc62d5236f5e941850776c435a9560 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Commands/PrebuildCommand.cs b/Editor/Commands/PrebuildCommand.cs new file mode 100644 index 0000000..9e0cd90 --- /dev/null +++ b/Editor/Commands/PrebuildCommand.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEditor; + +namespace HybridCLR.Editor.Commands +{ + public static class PrebuildCommand + { + /// + /// 按照必要的顺序,执行所有生成操作,适合打包前操作 + /// + [MenuItem("HybridCLR/GenerateAll", priority = 30)] + public static void GenerateAll() + { + // 顺序随意 + ReversePInvokeWrapperGeneratorCommand.GenerateReversePInvokeWrapper(); + + // AOTReferenceGeneratorCommand 涉及到代码生成,必须在MethodBridgeGeneratorCommand之前 + AOTReferenceGeneratorCommand.GenerateAOTGenericReference(); + MethodBridgeGeneratorCommand.GenerateMethodBridge(); + + // 顺序随意,只要保证 GenerateLinkXml之前有调用过CompileDll即可 + LinkGeneratorCommand.GenerateLinkXml(false); + } + } +} diff --git a/Editor/Commands/PrebuildCommand.cs.meta b/Editor/Commands/PrebuildCommand.cs.meta new file mode 100644 index 0000000..a27aea2 --- /dev/null +++ b/Editor/Commands/PrebuildCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c20f09bfbe3f32143aae872d3813d9e9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Commands/ReversePInvokeWrapperGeneratorCommand.cs b/Editor/Commands/ReversePInvokeWrapperGeneratorCommand.cs new file mode 100644 index 0000000..46f57a7 --- /dev/null +++ b/Editor/Commands/ReversePInvokeWrapperGeneratorCommand.cs @@ -0,0 +1,31 @@ +using HybridCLR.Editor.LinkGenerator; +using HybridCLR.Editor.ReversePInvokeWrap; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace HybridCLR.Editor.Commands +{ + + public static class ReversePInvokeWrapperGeneratorCommand + { + + [MenuItem("HybridCLR/GenerateReversePInvokeWrapper", priority = 20)] + public static void GenerateReversePInvokeWrapper() + { + string ReversePInvokeWrapperStubFile = $"{SettingsUtil.LocalIl2CppDir}/libil2cpp/hybridclr/metadata/ReversePInvokeMethodStub.cpp"; + string wrapperTemplateStr = AssetDatabase.LoadAssetAtPath($"{SettingsUtil.TemplatePathInPackage}/ReversePInvokeMethodStub.cpp.txt").text; + int wrapperCount = SettingsUtil.GlobalSettings.ReversePInvokeWrapperCount; + var generator = new ReversePInvokeWrapperGenerator(); + generator.Generate(wrapperTemplateStr, wrapperCount,ReversePInvokeWrapperStubFile); + Debug.Log($"GenerateReversePInvokeWrapper. wraperCount:{wrapperCount} output:{ReversePInvokeWrapperStubFile}"); + MethodBridgeGeneratorCommand.CleanIl2CppBuildCache(); + } + } +} diff --git a/Editor/Commands/ReversePInvokeWrapperGeneratorCommand.cs.meta b/Editor/Commands/ReversePInvokeWrapperGeneratorCommand.cs.meta new file mode 100644 index 0000000..79fb3d8 --- /dev/null +++ b/Editor/Commands/ReversePInvokeWrapperGeneratorCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7db18e1736f593c4089c85d764cf8620 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/EqualityUtil.cs b/Editor/EqualityUtil.cs new file mode 100644 index 0000000..a50226b --- /dev/null +++ b/Editor/EqualityUtil.cs @@ -0,0 +1,36 @@ +using dnlib.DotNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HybridCLR.Editor +{ + public static class EqualityUtil + { + public static bool EqualsTypeSigArray(List a, List b) + { + if (a == b) + { + return true; + } + if (a != null && b != null) + { + if (a.Count != b.Count) + { + return false; + } + for (int i = 0; i < a.Count; i++) + { + if (!TypeEqualityComparer.Instance.Equals(a[i], b[i])) + { + return false; + } + } + return true; + } + return false; + } + } +} diff --git a/Editor/EqualityUtil.cs.meta b/Editor/EqualityUtil.cs.meta new file mode 100644 index 0000000..5849f08 --- /dev/null +++ b/Editor/EqualityUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 103704591750908419902643015e920a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/HashUtil.cs b/Editor/HashUtil.cs new file mode 100644 index 0000000..38e83e3 --- /dev/null +++ b/Editor/HashUtil.cs @@ -0,0 +1,28 @@ +using dnlib.DotNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HybridCLR.Editor +{ + public static class HashUtil + { + public static int CombineHash(int hash1, int hash2) + { + return hash1 * 1566083941 + hash2; + } + + public static int ComputHash(List sigs) + { + int hash = 135781321; + TypeEqualityComparer tc = TypeEqualityComparer.Instance; + foreach (var sig in sigs) + { + hash = hash * 1566083941 + tc.GetHashCode(sig); + } + return hash; + } + } +} diff --git a/Editor/HashUtil.cs.meta b/Editor/HashUtil.cs.meta new file mode 100644 index 0000000..9cdce22 --- /dev/null +++ b/Editor/HashUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5d4ae4a5c0bba49469c525887d812717 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/HybridCLR.Editor.asmdef b/Editor/HybridCLR.Editor.asmdef new file mode 100644 index 0000000..ed296d0 --- /dev/null +++ b/Editor/HybridCLR.Editor.asmdef @@ -0,0 +1,18 @@ +{ + "name": "HybridCLR.Editor", + "rootNamespace": "", + "references": [ + "UnityFS" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Editor/HybridCLR.Editor.asmdef.meta b/Editor/HybridCLR.Editor.asmdef.meta new file mode 100644 index 0000000..be1417b --- /dev/null +++ b/Editor/HybridCLR.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2373f786d14518f44b0f475db77ba4de +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/HybridCLRGlobalSettings.cs b/Editor/HybridCLRGlobalSettings.cs new file mode 100644 index 0000000..675e02c --- /dev/null +++ b/Editor/HybridCLRGlobalSettings.cs @@ -0,0 +1,45 @@ +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using UnityEditorInternal; +using UnityEngine; + +[CreateAssetMenu(fileName = "HybridCLRGlobalSettings", menuName = "HybridCLR/GlobalSettings")] +public class HybridCLRGlobalSettings : ScriptableObject +{ + [Header("开启HybridCLR插件")] + public bool enable = true; + + [Header("从gitee clone插件代码")] + public bool cloneFromGitee = true; // false 则从github上拉取 + + [Header("热更新Assembly Definition Modules")] + public AssemblyDefinitionAsset[] hotUpdateAssemblyDefinitions; + + [Header("热更新dlls")] + public string[] hotUpdateAssemblies; + + [Header("自动扫描生成的link.xml路径")] + public string outputLinkFile = "HybridCLR/link.xml"; + + [Header("自动扫描生成的AOTGenericReferences.cs路径")] + public string outputAOTGenericReferenceFile = "Main/AOTGenericReferences.cs"; + + [Header("AOT泛型实例化搜索迭代次数")] + public int maxGenericReferenceIteration = 4; + + [Header("预留MonoPInvokeCallbackAttribute函数个数")] + public int ReversePInvokeWrapperCount = 10; + + [Header("MethodBridge泛型搜索迭代次数")] + public int maxMethodBridgeGenericIteration = 4; + + [Header("热更新dll输出目录(相对HybridCLRData目录)")] + public string hotUpdateDllOutputDir = "HotUpdateDlls"; + + [Header("HybridCLRData目录(相对工程目录)")] + public string hybridCLRDataDir = "HybridCLRData"; + + [Header("裁剪后的AOT assembly输出目录")] + public string strippedAssemblyDir = "AssembliesPostIl2CppStrip"; +} diff --git a/Editor/HybridCLRGlobalSettings.cs.meta b/Editor/HybridCLRGlobalSettings.cs.meta new file mode 100644 index 0000000..570bc8f --- /dev/null +++ b/Editor/HybridCLRGlobalSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e189374413a3f00468e49d51d8b27a09 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Installer.meta b/Editor/Installer.meta new file mode 100644 index 0000000..b27774d --- /dev/null +++ b/Editor/Installer.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a2c8f84b297371d4cbcd5ca655bf360d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Installer/BashUtil.cs b/Editor/Installer/BashUtil.cs new file mode 100644 index 0000000..9927ab0 --- /dev/null +++ b/Editor/Installer/BashUtil.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HybridCLR.Editor.Installer +{ + public static class BashUtil + { + public static int RunCommand(string workingDir, string program, string[] args) + { + using (Process p = new Process()) + { + p.StartInfo.WorkingDirectory = workingDir; + p.StartInfo.FileName = program; + p.StartInfo.UseShellExecute = false; + p.StartInfo.CreateNoWindow = true; + string argsStr = string.Join(" ", args.Select(arg => "\"" + arg + "\"")); + p.StartInfo.Arguments = argsStr; + UnityEngine.Debug.Log($"[BashUtil] run => {program} {argsStr}"); + p.Start(); + p.WaitForExit(); + return p.ExitCode; + } + } + + + public static (int ExitCode, string StdOut, string StdErr) RunCommand2(string workingDir, string program, string[] args) + { + using (Process p = new Process()) + { + p.StartInfo.WorkingDirectory = workingDir; + p.StartInfo.FileName = program; + p.StartInfo.UseShellExecute = false; + p.StartInfo.CreateNoWindow = true; + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.RedirectStandardError = true; + string argsStr = string.Join(" ", args.Select(arg => "\"" + arg + "\"")); + p.StartInfo.Arguments = argsStr; + UnityEngine.Debug.Log($"[BashUtil] run => {program} {argsStr}"); + p.Start(); + p.WaitForExit(); + + string stdOut = p.StandardOutput.ReadToEnd(); + string stdErr = p.StandardError.ReadToEnd(); + return (p.ExitCode, stdOut, stdErr); + } + } + + public static bool ExistProgram(string prog) + { + return RunCommand(".", "where", new string[] {prog}) == 0; + } + + + public static void RemoveDir(string dir, bool log = false) + { + if (log) + { + UnityEngine.Debug.Log($"[BashUtil] RemoveDir dir:{dir}"); + } + if (!Directory.Exists(dir)) + { + return; + } + foreach (var file in Directory.GetFiles(dir)) + { + File.SetAttributes(file, FileAttributes.Normal); + File.Delete(file); + } + foreach (var subDir in Directory.GetDirectories(dir)) + { + RemoveDir(subDir); + } + Directory.Delete(dir); + } + + public static void RecreateDir(string dir) + { + if(Directory.Exists(dir)) + { + RemoveDir(dir, true); + } + Directory.CreateDirectory(dir); + } + + public static void CopyDir(string src, string dst, bool log = false) + { + if (log) + { + UnityEngine.Debug.Log($"[BashUtil] CopyDir {src} => {dst}"); + } + RemoveDir(dst); + Directory.CreateDirectory(dst); + foreach(var file in Directory.GetFiles(src)) + { + File.Copy(file, $"{dst}/{Path.GetFileName(file)}"); + } + foreach(var subDir in Directory.GetDirectories(src)) + { + CopyDir(subDir, $"{dst}/{Path.GetFileName(subDir)}"); + } + } + } +} diff --git a/Editor/Installer/BashUtil.cs.meta b/Editor/Installer/BashUtil.cs.meta new file mode 100644 index 0000000..ef7e11d --- /dev/null +++ b/Editor/Installer/BashUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 960a0257c3a17f64b810193308ce1558 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Installer/InstallerController.cs b/Editor/Installer/InstallerController.cs new file mode 100644 index 0000000..e67793c --- /dev/null +++ b/Editor/Installer/InstallerController.cs @@ -0,0 +1,373 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Diagnostics; + +using Debug = UnityEngine.Debug; +using System.Text.RegularExpressions; + +namespace HybridCLR.Editor.Installer +{ + public enum InstallErrorCode + { + Ok, + Il2CppInstallPathNotMatchIl2CppBranch, + Il2CppInstallPathNotExists, + NotIl2CppPath, + } + + public partial class InstallerController + { + private string m_Il2CppInstallDirectory; + + public string Il2CppInstallDirectory + { + get + { + return m_Il2CppInstallDirectory; + } + set + { + m_Il2CppInstallDirectory = value?.Replace('\\', '/'); + if (!string.IsNullOrEmpty(m_Il2CppInstallDirectory)) + { + EditorPrefs.SetString("UnityInstallDirectory", m_Il2CppInstallDirectory); + } + } + } + + private string GetIl2CppPlusBranchByUnityVersion(string unityVersion) + { + if (unityVersion.Contains("2019.")) + { + return "2019.4.40"; + } + if (unityVersion.Contains("2020.")) + { + return "2020.3.33"; + } + if (unityVersion.Contains("2021.")) + { + return "2021.3.1"; + } + return "not support"; + } + + public string Il2CppBranch => GetIl2CppPlusBranchByUnityVersion(Application.unityVersion); + + public string InitLocalIl2CppBatFile => Application.dataPath + "/../HybridCLRData/init_local_il2cpp_data.bat"; + + public string InitLocalIl2CppBashFile => Application.dataPath + "/../HybridCLRData/init_local_il2cpp_data.sh"; + + public InstallerController() + { + PrepareIl2CppInstallPath(); + } + + private static readonly Regex s_unityVersionPat = new Regex(@"(\d+)\.(\d+)\.(\d+)"); + + public const int min2019_4_CompatibleMinorVersion = 40; + public const int min2020_3_CompatibleMinorVersion = 21; + public const int min2021_3_CompatibleMinorVersion = 0; + + private bool TryParseMinorVersion(string installDir, out (int Major, int Minor1, int Minor2) unityVersion) + { + var matches = s_unityVersionPat.Matches(installDir); + if (matches.Count == 0) + { + unityVersion = default; + return false; + } + // 找最后一个匹配的,有的人居然会把Unity安装目录放到其他安装版本下。无语! + Match match = matches[matches.Count - 1]; + // Debug.Log($"capture count:{match.Groups.Count} {match.Groups[1].Value} {match.Groups[2].Value}"); + int major = int.Parse(match.Groups[1].Value); + int minor1 = int.Parse(match.Groups[2].Value); + int minor2 = int.Parse(match.Groups[3].Value); + unityVersion = (major, minor1, minor2); + return true; + } + + public string GetCurVersionStr(string installDir) + { + if (TryParseMinorVersion(installDir, out var version)) + { + return $"{version.Major}.{version.Minor1}.{version.Minor2}"; + } + throw new Exception($"not support version:{installDir}"); + } + + public string GetMinCompatibleVersion(string branch) + { + switch(branch) + { + case "2019.4.40": return $"2019.4.{min2019_4_CompatibleMinorVersion}"; + case "2020.3.33": return $"2020.3.{min2020_3_CompatibleMinorVersion}"; + case "2021.3.1": return $"2021.3.{min2021_3_CompatibleMinorVersion}"; + default: throw new Exception($"not support version:{branch}"); + } + } + + private bool IsComaptibleWithIl2CppPlusBranch(string branch, string installDir) + { + if (!TryParseMinorVersion(installDir, out var unityVersion)) + { + return false; + } + switch(branch) + { + case "2019.4.40": + { + if (unityVersion.Major != 2019 || unityVersion.Minor1 != 4) + { + return false; + } + return unityVersion.Minor2 >= min2019_4_CompatibleMinorVersion; + } + case "2020.3.33": + { + if (unityVersion.Major != 2020 || unityVersion.Minor1 != 3) + { + return false; + } + return unityVersion.Minor2 >= min2020_3_CompatibleMinorVersion; + } + case "2021.3.1": + { + if (unityVersion.Major != 2021 || unityVersion.Minor1 != 3) + { + return false; + } + return unityVersion.Minor2 >= min2021_3_CompatibleMinorVersion; + } + default: throw new Exception($"not support il2cpp_plus branch:{branch}"); + } + } + + void PrepareIl2CppInstallPath() + { +#if UNITY_EDITOR_WIN + + m_Il2CppInstallDirectory = EditorPrefs.GetString("Il2CppInstallDirectory"); + if (CheckValidIl2CppInstallDirectory(Il2CppBranch, m_Il2CppInstallDirectory) == InstallErrorCode.Ok) + { + return; + } + var il2cppBranch = Il2CppBranch; + var curAppInstallPath = EditorApplication.applicationPath; + if (IsComaptibleWithIl2CppPlusBranch(il2cppBranch, curAppInstallPath)) + { + Il2CppInstallDirectory = $"{Directory.GetParent(curAppInstallPath)}/Data/il2cpp"; + return; + } + string unityHubRootDir = Directory.GetParent(curAppInstallPath).Parent.Parent.ToString(); + // Debug.Log("unity hub root dir:" + unityHubRootDir); + foreach (var unityInstallDir in Directory.GetDirectories(unityHubRootDir, "*", SearchOption.TopDirectoryOnly)) + { + // Debug.Log("Unity install dir:" + unityInstallDir); + if (IsComaptibleWithIl2CppPlusBranch(il2cppBranch, unityInstallDir)) + { + Il2CppInstallDirectory = $"{unityInstallDir}/Editor/Data/il2cpp"; + return; + } + } + + Il2CppInstallDirectory = $"{Directory.GetParent(curAppInstallPath)}/Data/il2cpp"; +#else + m_Il2CppInstallDirectory = EditorPrefs.GetString("Il2CppInstallDirectory"); + if (CheckValidIl2CppInstallDirectory(Il2CppBranch, m_Il2CppInstallDirectory) == InstallErrorCode.Ok) + { + return; + } + var il2cppBranch = Il2CppBranch; + var curAppInstallPath = EditorApplication.applicationPath; + if (IsComaptibleWithIl2CppPlusBranch(il2cppBranch, curAppInstallPath)) + { + Il2CppInstallDirectory = $"{curAppInstallPath}/Contents/il2cpp"; + return; + } + string unityHubRootDir = Directory.GetParent(curAppInstallPath).Parent.Parent.ToString(); + foreach (var unityInstallDir in Directory.GetDirectories(unityHubRootDir, "*", SearchOption.TopDirectoryOnly)) + { + Debug.Log("nity install dir:" + unityInstallDir); + if (IsComaptibleWithIl2CppPlusBranch(il2cppBranch, unityInstallDir)) + { + Il2CppInstallDirectory = $"{unityInstallDir}/Unity.app/Contents/il2cpp"; + return; + } + } + + Il2CppInstallDirectory = $"{curAppInstallPath}/Contents/il2cpp"; +#endif + } + + public void InitHybridCLR(string il2cppBranch, string il2cppInstallPath) + { + if (CheckValidIl2CppInstallDirectory(il2cppBranch, il2cppInstallPath) != InstallErrorCode.Ok) + { + Debug.LogError($"请正确设置 il2cpp 安装目录"); + return; + } + RunInitLocalIl2CppData(il2cppBranch, il2cppInstallPath); + } + + public bool HasInstalledHybridCLR() + { + return Directory.Exists($"{SettingsUtil.LocalIl2CppDir}/libil2cpp/hybridclr"); + } + + public InstallErrorCode CheckValidIl2CppInstallDirectory(string il2cppBranch, string installDir) + { + installDir = installDir.Replace('\\', '/'); + if (!Directory.Exists(installDir)) + { + return InstallErrorCode.Il2CppInstallPathNotExists; + } + + if (!IsComaptibleWithIl2CppPlusBranch(il2cppBranch, installDir)) + { + return InstallErrorCode.Il2CppInstallPathNotMatchIl2CppBranch; + } + + if (!installDir.EndsWith("/il2cpp")) + { + return InstallErrorCode.NotIl2CppPath; + } + + return InstallErrorCode.Ok; + } + + public bool IsUnity2019(string branch) + { + return branch.Contains("2019."); + } + + + private string GetUnityIl2CppDllInstallLocation() + { +#if UNITY_EDITOR_WIN + return $"{SettingsUtil.LocalIl2CppDir}/build/deploy/net471/Unity.IL2CPP.dll"; +#else + return $"{SettingsUtil.LocalIl2CppDir}/build/deploy/il2cppcore/Unity.IL2CPP.dll"; +#endif + } + + private string GetUnityIl2CppDllModifiedPath(string curVersionStr) + { +#if UNITY_EDITOR_WIN + return $"{SettingsUtil.ProjectDir}/{SettingsUtil.HybridCLRDataPathInPackage}/ModifiedUnityAssemblies/{curVersionStr}/Unity.IL2CPP-Win.dll.bytes"; +#else + return $"{SettingsUtil.ProjectDir}/{SettingsUtil.HybridCLRDataPathInPackage}/ModifiedUnityAssemblies/{curVersionStr}/Unity.IL2CPP-Mac.dll.bytes"; +#endif + } + + private static string GetRepoUrl(string repoName) + { + string repoProvider = SettingsUtil.GlobalSettings.cloneFromGitee ? "gitee" : "github"; + return $"https://{repoProvider}.com/focus-creative-games/{repoName}"; + } + + private void RunInitLocalIl2CppData(string il2cppBranch, string il2cppInstallPath) + { +#if UNITY_EDITOR_WIN + if (!BashUtil.ExistProgram("git")) + { + throw new Exception($"安装本地il2cpp需要使用git从远程拉取仓库,请先安装git"); + } +#endif + + string workDir = SettingsUtil.HybridCLRDataDir; + Directory.CreateDirectory(workDir); + //BashUtil.RecreateDir(workDir); + + string buildiOSDir = $"{workDir}/iOSBuild"; + BashUtil.RemoveDir(buildiOSDir); + BashUtil.CopyDir($"{SettingsUtil.HybridCLRDataPathInPackage}/iOSBuild", buildiOSDir, true); + + // clone hybridclr + string hybridclrRepoDir = $"{workDir}/hybridclr_repo"; + { + BashUtil.RemoveDir(hybridclrRepoDir); + var ret = BashUtil.RunCommand(workDir, "git", new string[] + { + "clone", + "--depth=1", + GetRepoUrl("hybridclr"), + hybridclrRepoDir, + }); + //if (ret != 0) + //{ + // throw new Exception($"git clone 失败"); + //} + } + + // clone il2cpp_plus + string il2cppPlusRepoDir = $"{workDir}/il2cpp_plus_repo"; + { + BashUtil.RemoveDir(il2cppPlusRepoDir); + var ret = BashUtil.RunCommand(workDir, "git", new string[] + { + "clone", + "--depth=1", + "-b", + il2cppBranch, + GetRepoUrl("il2cpp_plus"), + il2cppPlusRepoDir, + }); + //if (ret != 0) + //{ + // throw new Exception($"git clone 失败"); + //} + } + + // create LocalIl2Cpp + string localUnityDataDir = SettingsUtil.LocalUnityDataDir; + BashUtil.RecreateDir(localUnityDataDir); + + // copy MonoBleedingEdge + BashUtil.CopyDir($"{Directory.GetParent(il2cppInstallPath)}/MonoBleedingEdge", $"{localUnityDataDir}/MonoBleedingEdge", true); + + // copy il2cpp + BashUtil.CopyDir(Il2CppInstallDirectory, SettingsUtil.LocalIl2CppDir, true); + + // replace libil2cpp + string dstLibil2cppDir = $"{SettingsUtil.LocalIl2CppDir}/libil2cpp"; + BashUtil.CopyDir($"{il2cppPlusRepoDir}/libil2cpp", dstLibil2cppDir, true); + BashUtil.CopyDir($"{hybridclrRepoDir}/hybridclr", $"{dstLibil2cppDir}/hybridclr", true); + + // clean Il2cppBuildCache + BashUtil.RemoveDir($"{SettingsUtil.ProjectDir}/Library/Il2cppBuildCache", true); + + if (IsUnity2019(il2cppBranch)) + { + string curVersionStr = GetCurVersionStr(il2cppInstallPath); + string srcIl2CppDll = GetUnityIl2CppDllModifiedPath(curVersionStr); + if (File.Exists(srcIl2CppDll)) + { + string dstIl2CppDll = GetUnityIl2CppDllInstallLocation(); + File.Copy(srcIl2CppDll, dstIl2CppDll, true); + Debug.Log($"copy {srcIl2CppDll} => {dstIl2CppDll}"); + } + else + { + Debug.LogError($"未找到当前版本:{curVersionStr} 对应的改造过的 Unity.IL2CPP.dll,打包出的程序将会崩溃"); + } + } + if (HasInstalledHybridCLR()) + { + Debug.Log("安装成功!"); + } + else + { + Debug.LogError("安装失败!"); + } + } + } +} diff --git a/Editor/Installer/InstallerController.cs.meta b/Editor/Installer/InstallerController.cs.meta new file mode 100644 index 0000000..6b30944 --- /dev/null +++ b/Editor/Installer/InstallerController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 44c8627d126b30d4e9560b1f738264ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Installer/InstallerWindow.cs b/Editor/Installer/InstallerWindow.cs new file mode 100644 index 0000000..eb4112d --- /dev/null +++ b/Editor/Installer/InstallerWindow.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEditor ; +using UnityEngine; + + +namespace HybridCLR.Editor.Installer +{ + public class InstallerWindow : EditorWindow + { + private InstallerController m_Controller; + + [MenuItem("HybridCLR/Installer...", false, 0)] + private static void Open() + { + InstallerWindow window = GetWindow("HybridCLR Installer", true); + window.minSize = new Vector2(800f, 500f); + } + + private void OnEnable() + { + m_Controller = new InstallerController(); + } + + private void OnGUI() + { + string minCompatibleVersion = m_Controller.GetMinCompatibleVersion(m_Controller.Il2CppBranch); + GUI.enabled = true; + GUILayout.Space(10f); + EditorGUILayout.LabelField("=======================说明===================="); + EditorGUILayout.LabelField( + $"你所在项目的Unity版本可以与il2cpp_plus版本:{m_Controller.Il2CppBranch} 不一样。\n" + + $"如果你的Unity的版本号 >= {minCompatibleVersion}, 可以直接安装。\n" + + $"如果你的Unity的版本号 < {minCompatibleVersion}, \n" + + $"由于安装HybridCLR时需要从il2cpp_plus对应版本{m_Controller.Il2CppBranch}(而不是你项目版本)拷贝il2cpp目录,\n" + + $"你必须同时安装相应版本 {m_Controller.Il2CppBranch} 才能完成安装", EditorStyles.wordWrappedLabel); + EditorGUILayout.LabelField("=============================================="); + GUILayout.Space(10f); + + EditorGUILayout.BeginVertical("box"); + EditorGUILayout.LabelField($"安装状态:{(m_Controller.HasInstalledHybridCLR() ? "已安装" : "未安装")}", EditorStyles.boldLabel); + GUILayout.Space(5f); + EditorGUILayout.LabelField($"当前Unity版本: {Application.unityVersion},匹配的il2cpp_plus分支: {m_Controller.Il2CppBranch}"); + GUISelectUnityDirectory($"il2cpp_plus分支对应Unity版本的il2cpp路径", "Select"); + GUILayout.Space(10f); + GUIInstallButton("安装最新HybridCLR插件代码到本项目", "安装", InitHybridCLR); + EditorGUILayout.EndVertical(); + } + + private void GUIInstallButton(string content, string button, Action onClick) + { + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField(content); + GUI.enabled = m_Controller.CheckValidIl2CppInstallDirectory(m_Controller.Il2CppBranch, m_Controller.Il2CppInstallDirectory) == InstallErrorCode.Ok; + if (GUILayout.Button(button, GUILayout.Width(100))) + { + onClick?.Invoke(); + GUIUtility.ExitGUI(); + } + GUI.enabled = true; + EditorGUILayout.EndHorizontal(); + + } + + + private void GUISelectUnityDirectory(string content, string selectButton) + { + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField(content, GUILayout.MaxWidth(300)); + string il2cppInstallDirectory = m_Controller.Il2CppInstallDirectory = EditorGUILayout.TextField(m_Controller.Il2CppInstallDirectory); + if (GUILayout.Button(selectButton, GUILayout.Width(100))) + { + string temp = EditorUtility.OpenFolderPanel(content, m_Controller.Il2CppInstallDirectory, string.Empty); + if (!string.IsNullOrEmpty(temp)) + { + il2cppInstallDirectory = m_Controller.Il2CppInstallDirectory = temp; + } + } + EditorGUILayout.EndHorizontal(); + + InstallErrorCode err = m_Controller.CheckValidIl2CppInstallDirectory(m_Controller.Il2CppBranch, il2cppInstallDirectory); + switch (err) + { + case InstallErrorCode.Ok: + { + break; + } + case InstallErrorCode.Il2CppInstallPathNotExists: + { + EditorGUILayout.HelpBox("li2cpp 路径不存在", MessageType.Error); + break; + } + case InstallErrorCode.Il2CppInstallPathNotMatchIl2CppBranch: + { + EditorGUILayout.HelpBox($"il2cpp 版本不兼容,最小版本为 {m_Controller.GetMinCompatibleVersion(m_Controller.Il2CppBranch)}", MessageType.Error); + break; + } + case InstallErrorCode.NotIl2CppPath: + { + EditorGUILayout.HelpBox($"当前选择的路径不是il2cpp目录(必须类似 xxx/il2cpp)", MessageType.Error); + break; + } + default: throw new Exception($"not support {err}"); + } + } + + private void InitHybridCLR() + { + m_Controller.InitHybridCLR(m_Controller.Il2CppBranch, m_Controller.Il2CppInstallDirectory); + } + } +} diff --git a/Editor/Installer/InstallerWindow.cs.meta b/Editor/Installer/InstallerWindow.cs.meta new file mode 100644 index 0000000..f404c28 --- /dev/null +++ b/Editor/Installer/InstallerWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 959fbf0bb06629542969354505189240 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/LinkGenerator.meta b/Editor/LinkGenerator.meta new file mode 100644 index 0000000..1cad1b3 --- /dev/null +++ b/Editor/LinkGenerator.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5186a137e0258034cb3832bdf6b16a70 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/LinkGenerator/Analyzer.cs b/Editor/LinkGenerator/Analyzer.cs new file mode 100644 index 0000000..eb63f08 --- /dev/null +++ b/Editor/LinkGenerator/Analyzer.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using dnlib.DotNet; +using HybridCLR.Editor.Meta; +using IAssemblyResolver = HybridCLR.Editor.Meta.IAssemblyResolver; + +namespace HybridCLR.Editor.LinkGenerator +{ + class Analyzer + { + private readonly IAssemblyResolver _resolver; + + public Analyzer(IAssemblyResolver resolver) + { + _resolver = resolver; + } + + public HashSet CollectRefs(List rootAssemblies) + { + var assCollector = new AssemblyCache(_resolver); + var rootAssemblyName = new HashSet(); + foreach(var ass in rootAssemblies) + { + if (!rootAssemblyName.Add(ass.GetName().Name)) + { + throw new Exception($"assembly:{ass.GetName().Name} 重复"); + } + } + + var typeRefs = new HashSet(TypeEqualityComparer.Instance); + foreach (var rootAss in rootAssemblies) + { + var dnAss = assCollector.LoadModule(rootAss.GetName().Name); + foreach(var type in dnAss.GetTypeRefs()) + { + if (!rootAssemblyName.Contains(type.DefinitionAssembly.Name)) + { + typeRefs.Add(type); + } + } + } + return typeRefs; + } + } +} diff --git a/Editor/LinkGenerator/Analyzer.cs.meta b/Editor/LinkGenerator/Analyzer.cs.meta new file mode 100644 index 0000000..0c6fa90 --- /dev/null +++ b/Editor/LinkGenerator/Analyzer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fd3dd4871efd10e46947cb61c13797fd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/LinkGenerator/LinkXmlWriter.cs b/Editor/LinkGenerator/LinkXmlWriter.cs new file mode 100644 index 0000000..948641e --- /dev/null +++ b/Editor/LinkGenerator/LinkXmlWriter.cs @@ -0,0 +1,47 @@ +using dnlib.DotNet; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace HybridCLR.Editor.LinkGenerator +{ + internal class LinkXmlWriter + { + public void Write(string outputLinkXmlFile, HashSet refTypes) + { + string parentDir = Directory.GetParent(outputLinkXmlFile).FullName; + Directory.CreateDirectory(parentDir); + var writer = System.Xml.XmlWriter.Create(outputLinkXmlFile, + new System.Xml.XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true}); + + writer.WriteStartDocument(); + writer.WriteStartElement("linker"); + + var typesByAssembly = refTypes.GroupBy(t => t.DefinitionAssembly.Name.String).ToList(); + typesByAssembly.Sort((a, b) => a.Key.CompareTo(b.Key)); + + foreach(var assembly in typesByAssembly) + { + writer.WriteStartElement("assembly"); + writer.WriteAttributeString("fullname", assembly.Key); + List assTypes = assembly.ToList(); + assTypes.Sort((a, b) => a.FullName.CompareTo(b.FullName)); + foreach(var type in assTypes) + { + writer.WriteStartElement("type"); + writer.WriteAttributeString("fullname", type.FullName); + writer.WriteAttributeString("preserve", "all"); + writer.WriteEndElement(); + } + writer.WriteEndElement(); + } + writer.WriteEndElement(); + writer.WriteEndDocument(); + writer.Close(); + } + } +} diff --git a/Editor/LinkGenerator/LinkXmlWriter.cs.meta b/Editor/LinkGenerator/LinkXmlWriter.cs.meta new file mode 100644 index 0000000..4c6c61c --- /dev/null +++ b/Editor/LinkGenerator/LinkXmlWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d5cc4ae4adc319b4bb1e115567d7613e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Meta.meta b/Editor/Meta.meta new file mode 100644 index 0000000..3291c30 --- /dev/null +++ b/Editor/Meta.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3787c7d8b775c754aa4ae06bf78e96ea +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Meta/AssemblyCache.cs b/Editor/Meta/AssemblyCache.cs new file mode 100644 index 0000000..90c1267 --- /dev/null +++ b/Editor/Meta/AssemblyCache.cs @@ -0,0 +1,55 @@ +using dnlib.DotNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace HybridCLR.Editor.Meta +{ + public class AssemblyCache + { + private readonly IAssemblyResolver _assemblyPathResolver; + private readonly ModuleContext _modCtx; + private readonly AssemblyResolver _asmResolver; + + public Dictionary LoadedModules { get; } = new Dictionary(); + + public AssemblyCache(IAssemblyResolver assemblyResolver) + { + _assemblyPathResolver = assemblyResolver; + _modCtx = ModuleDef.CreateModuleContext(); + _asmResolver = (AssemblyResolver)_modCtx.AssemblyResolver; + _asmResolver.EnableTypeDefCache = true; + _asmResolver.UseGAC = false; + } + + public ModuleDefMD LoadModule(string moduleName) + { + // Debug.Log($"load module:{moduleName}"); + if (LoadedModules.TryGetValue(moduleName, out var mod)) + { + return mod; + } + mod = DoLoadModule(_assemblyPathResolver.ResolveAssembly(moduleName, true)); + LoadedModules.Add(moduleName, mod); + + foreach (var refAsm in mod.GetAssemblyRefs()) + { + LoadModule(refAsm.Name); + } + return mod; + } + + private ModuleDefMD DoLoadModule(string dllPath) + { + //Debug.Log($"do load module:{dllPath}"); + ModuleDefMD mod = ModuleDefMD.Load(dllPath, _modCtx); + _asmResolver.AddToCache(mod); + return mod; + } + + } +} diff --git a/Editor/Meta/AssemblyCache.cs.meta b/Editor/Meta/AssemblyCache.cs.meta new file mode 100644 index 0000000..a99c3b2 --- /dev/null +++ b/Editor/Meta/AssemblyCache.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fa4650e79a52228488aa85e0690ca52c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Meta/AssemblyReferenceDeepCollector.cs b/Editor/Meta/AssemblyReferenceDeepCollector.cs new file mode 100644 index 0000000..6e69657 --- /dev/null +++ b/Editor/Meta/AssemblyReferenceDeepCollector.cs @@ -0,0 +1,82 @@ +using dnlib.DotNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace HybridCLR.Editor.Meta +{ + public class AssemblyReferenceDeepCollector + { + private readonly IAssemblyResolver _assemblyPathResolver; + private readonly List _rootAssemblies; + + private readonly ModuleContext _modCtx; + private readonly AssemblyResolver _asmResolver; + + public Dictionary LoadedModules { get; } = new Dictionary(); + + public IReadOnlyList GetRootAssemblyNames() + { + return _rootAssemblies; + } + + public List GetLoadedModulesExcludeRootAssemblies() + { + return LoadedModules.Where(e => !_rootAssemblies.Contains(e.Key)).Select(e => e.Value).ToList(); + } + + public List GetLoadedModulesOfRootAssemblies() + { + return _rootAssemblies.Select(ass => LoadedModules[ass]).ToList(); + } + + public AssemblyReferenceDeepCollector(IAssemblyResolver assemblyResolver, List rootAssemblies) + { + _assemblyPathResolver = assemblyResolver; + _rootAssemblies = rootAssemblies; + _modCtx = ModuleDef.CreateModuleContext(); + _asmResolver = (AssemblyResolver)_modCtx.AssemblyResolver; + _asmResolver.EnableTypeDefCache = true; + _asmResolver.UseGAC = false; + LoadAllAssembiles(); + } + + private void LoadAllAssembiles() + { + foreach (var asm in _rootAssemblies) + { + LoadModule(asm); + } + } + + private ModuleDefMD LoadModule(string moduleName) + { + // Debug.Log($"load module:{moduleName}"); + if (LoadedModules.TryGetValue(moduleName, out var mod)) + { + return mod; + } + mod = DoLoadModule(_assemblyPathResolver.ResolveAssembly(moduleName, true)); + LoadedModules.Add(moduleName, mod); + + foreach (var refAsm in mod.GetAssemblyRefs()) + { + LoadModule(refAsm.Name); + } + return mod; + } + + private ModuleDefMD DoLoadModule(string dllPath) + { + //Debug.Log($"do load module:{dllPath}"); + ModuleDefMD mod = ModuleDefMD.Load(dllPath, _modCtx); + _asmResolver.AddToCache(mod); + return mod; + } + + } +} diff --git a/Editor/Meta/AssemblyReferenceDeepCollector.cs.meta b/Editor/Meta/AssemblyReferenceDeepCollector.cs.meta new file mode 100644 index 0000000..e93354f --- /dev/null +++ b/Editor/Meta/AssemblyReferenceDeepCollector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0342c7d8575fdea49896260c77285286 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Meta/CombinedAssemblyResolver.cs b/Editor/Meta/CombinedAssemblyResolver.cs new file mode 100644 index 0000000..c8f663a --- /dev/null +++ b/Editor/Meta/CombinedAssemblyResolver.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HybridCLR.Editor.Meta +{ + public class CombinedAssemblyResolver : IAssemblyResolver + { + private readonly IAssemblyResolver[] _resolvers; + + public CombinedAssemblyResolver(params IAssemblyResolver[] resolvers) + { + _resolvers = resolvers; + } + + public string ResolveAssembly(string assemblyName, bool throwExIfNotFind) + { + foreach(var resolver in _resolvers) + { + var assembly = resolver.ResolveAssembly(assemblyName, false); + if (assembly != null) + { + return assembly; + } + } + if (throwExIfNotFind) + { + throw new Exception($"resolve assembly:{assemblyName} fail"); + } + return null; + } + } +} diff --git a/Editor/Meta/CombinedAssemblyResolver.cs.meta b/Editor/Meta/CombinedAssemblyResolver.cs.meta new file mode 100644 index 0000000..81b443c --- /dev/null +++ b/Editor/Meta/CombinedAssemblyResolver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 89b83906438c52d4b9af4aaef055f177 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Meta/GenericArgumentContext.cs b/Editor/Meta/GenericArgumentContext.cs new file mode 100644 index 0000000..577fc97 --- /dev/null +++ b/Editor/Meta/GenericArgumentContext.cs @@ -0,0 +1,103 @@ +using dnlib.DotNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HybridCLR.Editor.Meta +{ + /// + /// Replaces generic type/method var with its generic argument + /// + public sealed class GenericArgumentContext + { + List typeArgsStack = new List(); + List methodArgsStack = new List(); + + public GenericArgumentContext(List typeArgsStack, List methodArgsStack) + { + this.typeArgsStack = typeArgsStack; + this.methodArgsStack = methodArgsStack; + } + + + + /// + /// Replaces a generic type/method var with its generic argument (if any). If + /// isn't a generic type/method var or if it can't + /// be resolved, it itself is returned. Else the resolved type is returned. + /// + /// Type signature + /// New which is never null unless + /// is null + public TypeSig Resolve(TypeSig typeSig) + { + if (!typeSig.ContainsGenericParameter) + { + return typeSig; + } + switch (typeSig.ElementType) + { + case ElementType.Ptr: return new PtrSig(Resolve(typeSig.Next)); + case ElementType.ByRef: return new PtrSig(Resolve(typeSig.Next)); + + case ElementType.SZArray: return new PtrSig(Resolve(typeSig.Next)); + case ElementType.Array: + { + var ara = (ArraySig)typeSig; + return new ArraySig(Resolve(typeSig.Next), ara.Rank, ara.Sizes, ara.LowerBounds); + } + + case ElementType.Var: + { + GenericVar genericVar = (GenericVar)typeSig; + var newSig = Resolve(typeArgsStack, genericVar.Number, true); + if (newSig == null) + { + throw new Exception(); + } + return newSig; + } + + case ElementType.MVar: + { + GenericMVar genericVar = (GenericMVar)typeSig; + var newSig = Resolve(methodArgsStack, genericVar.Number, true); + if (newSig is null) + { + throw new Exception(); + } + return newSig; + } + case ElementType.GenericInst: + { + var gia = (GenericInstSig)typeSig; + return new GenericInstSig(gia.GenericType, gia.GenericArguments.Select(ga => Resolve(ga)).ToList()); + } + + case ElementType.FnPtr: + { + throw new NotSupportedException(typeSig.ToString()); + } + + case ElementType.ValueArray: + { + var vas = (ValueArraySig)typeSig; + return new ValueArraySig(Resolve(vas.Next), vas.Size); + } + default: return typeSig; + } + } + + private TypeSig Resolve(List args, uint number, bool isTypeVar) + { + var typeSig = args[(int)number]; + var gvar = typeSig as GenericSig; + if (gvar is null || gvar.IsTypeVar != isTypeVar) + return typeSig; + return gvar; + } + } + +} diff --git a/Editor/Meta/GenericArgumentContext.cs.meta b/Editor/Meta/GenericArgumentContext.cs.meta new file mode 100644 index 0000000..acb9355 --- /dev/null +++ b/Editor/Meta/GenericArgumentContext.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 07595a9b5b2f54c44a67022ae3e077d4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Meta/GenericClass.cs b/Editor/Meta/GenericClass.cs new file mode 100644 index 0000000..c913fd1 --- /dev/null +++ b/Editor/Meta/GenericClass.cs @@ -0,0 +1,63 @@ +using dnlib.DotNet; +using System.Collections.Generic; +using System.Linq; + +namespace HybridCLR.Editor.Meta +{ + public class GenericClass + { + public TypeDef Type { get; } + + public List KlassInst { get; } + + private readonly int _hashCode; + + public GenericClass(TypeDef type, List classInst) + { + Type = type; + KlassInst = classInst; + _hashCode = ComputHashCode(); + } + + public GenericClass ToGenericShare() + { + return new GenericClass(Type, MetaUtil.ToShareTypeSigs(KlassInst)); + } + + public override bool Equals(object obj) + { + if (obj is GenericClass gc) + { + return Type == gc.Type && EqualityUtil.EqualsTypeSigArray(KlassInst, gc.KlassInst); + } + return false; + } + + public override int GetHashCode() + { + return _hashCode; + } + + private int ComputHashCode() + { + int hash = TypeEqualityComparer.Instance.GetHashCode(Type); + if (KlassInst != null) + { + hash = HashUtil.CombineHash(hash, HashUtil.ComputHash(KlassInst)); + } + return hash; + } + + public static GenericClass ResolveClass(TypeSpec type, GenericArgumentContext ctx) + { + var sig = type.TypeSig.ToGenericInstSig(); + if (sig == null) + { + return null; + } + TypeDef def = type.ResolveTypeDef(); + var klassInst = ctx != null ? sig.GenericArguments.Select(ga => MetaUtil.Inflate(ga, ctx)).ToList() : sig.GenericArguments.ToList(); + return new GenericClass(def, klassInst); + } + } +} diff --git a/Editor/Meta/GenericClass.cs.meta b/Editor/Meta/GenericClass.cs.meta new file mode 100644 index 0000000..438a1a3 --- /dev/null +++ b/Editor/Meta/GenericClass.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c95ff173013909548bd9e2008812f9ff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Meta/GenericMethod.cs b/Editor/Meta/GenericMethod.cs new file mode 100644 index 0000000..113bce9 --- /dev/null +++ b/Editor/Meta/GenericMethod.cs @@ -0,0 +1,93 @@ +using dnlib.DotNet; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace HybridCLR.Editor.Meta +{ + public class GenericMethod + { + public MethodDef Method { get; } + + public List KlassInst { get; } + + public List MethodInst { get; } + + private readonly int _hashCode; + + public GenericMethod(MethodDef method, List classInst, List methodInst) + { + Method = method; + KlassInst = classInst; + MethodInst = methodInst; + _hashCode = ComputHashCode(); + } + + public GenericMethod ToGenericShare() + { + return new GenericMethod(Method, MetaUtil.ToShareTypeSigs(KlassInst), MetaUtil.ToShareTypeSigs(MethodInst)); + } + + public override bool Equals(object obj) + { + GenericMethod o = (GenericMethod)obj; + return Method == o.Method + && EqualityUtil.EqualsTypeSigArray(KlassInst, o.KlassInst) + && EqualityUtil.EqualsTypeSigArray(MethodInst, o.MethodInst); + } + + public override int GetHashCode() + { + return _hashCode; + } + + private int ComputHashCode() + { + int hash = MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(Method); + if (KlassInst != null) + { + hash = HashUtil.CombineHash(hash, HashUtil.ComputHash(KlassInst)); + } + if (MethodInst != null) + { + hash = HashUtil.CombineHash(hash, HashUtil.ComputHash(MethodInst)); + } + return hash; + } + + public static GenericMethod ResolveMethod(IMethod method, GenericArgumentContext ctx) + { + //Debug.Log($"== resolve method:{method}"); + TypeDef typeDef = null; + List klassInst = null; + List methodInst = null; + + MethodDef methodDef = null; + + + var decalringType = method.DeclaringType; + typeDef = decalringType.ResolveTypeDef(); + if (typeDef == null) + { + throw new Exception($"{decalringType}"); + } + GenericInstSig gis = decalringType.TryGetGenericInstSig(); + if (gis != null) + { + klassInst = ctx != null ? gis.GenericArguments.Select(ga => MetaUtil.Inflate(ga, ctx)).ToList() : gis.GenericArguments.ToList(); + } + methodDef = method.ResolveMethodDef(); + if (methodDef == null) + { + return null; + } + if (method is MethodSpec methodSpec) + { + methodInst = ctx != null ? methodSpec.GenericInstMethodSig.GenericArguments.Select(ga => MetaUtil.Inflate(ga, ctx)).ToList() + : methodSpec.GenericInstMethodSig.GenericArguments.ToList(); + } + return new GenericMethod(methodDef, klassInst, methodInst); + } + + } +} diff --git a/Editor/Meta/GenericMethod.cs.meta b/Editor/Meta/GenericMethod.cs.meta new file mode 100644 index 0000000..ef15a5c --- /dev/null +++ b/Editor/Meta/GenericMethod.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 88ecf3d52ec393b4cac142518944e487 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Meta/IAssemblyResolver.cs b/Editor/Meta/IAssemblyResolver.cs new file mode 100644 index 0000000..25a6d42 --- /dev/null +++ b/Editor/Meta/IAssemblyResolver.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HybridCLR.Editor.Meta +{ + public interface IAssemblyResolver + { + string ResolveAssembly(string assemblyName, bool throwExIfNotFind); + } +} diff --git a/Editor/Meta/IAssemblyResolver.cs.meta b/Editor/Meta/IAssemblyResolver.cs.meta new file mode 100644 index 0000000..d56059d --- /dev/null +++ b/Editor/Meta/IAssemblyResolver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f962a018018dbb945a19f82d2e098686 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Meta/MethodReferenceAnalyzer.cs b/Editor/Meta/MethodReferenceAnalyzer.cs new file mode 100644 index 0000000..7fd8ccd --- /dev/null +++ b/Editor/Meta/MethodReferenceAnalyzer.cs @@ -0,0 +1,87 @@ +using dnlib.DotNet; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HybridCLR.Editor.Meta +{ + public class MethodReferenceAnalyzer + { + private readonly Action _onNewMethod; + + private readonly ConcurrentDictionary> _methodEffectInsts = new ConcurrentDictionary>(); + + public MethodReferenceAnalyzer(Action onNewMethod) + { + _onNewMethod = onNewMethod; + } + + public void WalkMethod(MethodDef method, List klassGenericInst, List methodGenericInst) + { + if (klassGenericInst != null || methodGenericInst != null) + { + //var typeSig = klassGenericInst != null ? new GenericInstSig(method.DeclaringType.ToTypeSig().ToClassOrValueTypeSig(), klassGenericInst) : method.DeclaringType?.ToTypeSig(); + //Debug.Log($"== walk generic method {typeSig}::{method.Name} {method.MethodSig}"); + } + else + { + //Debug.Log($"== walk not geneeric method:{method}"); + } + var ctx = new GenericArgumentContext(klassGenericInst, methodGenericInst); + + if (_methodEffectInsts.TryGetValue(method, out var effectInsts)) + { + foreach (var met in effectInsts) + { + var resolveMet = GenericMethod.ResolveMethod(met, ctx)?.ToGenericShare(); + _onNewMethod(resolveMet); + } + return; + } + + var body = method.Body; + if (body == null || !body.HasInstructions) + { + return; + } + + effectInsts = new List(); + foreach (var inst in body.Instructions) + { + if (inst.Operand == null) + { + continue; + } + switch (inst.Operand) + { + case IMethod met: + { + if (!met.IsMethod) + { + continue; + } + var resolveMet = GenericMethod.ResolveMethod(met, ctx)?.ToGenericShare(); + if (resolveMet == null) + { + continue; + } + effectInsts.Add(met); + _onNewMethod(resolveMet); + break; + } + case ITokenOperand token: + { + //GenericParamContext paramContext = method.HasGenericParameters || method.DeclaringType.HasGenericParameters ? + // new GenericParamContext(method.DeclaringType, method) : default; + //method.Module.ResolveToken(token.MDToken, paramContext); + break; + } + } + } + _methodEffectInsts.TryAdd(method, effectInsts); + } + } +} diff --git a/Editor/Meta/MethodReferenceAnalyzer.cs.meta b/Editor/Meta/MethodReferenceAnalyzer.cs.meta new file mode 100644 index 0000000..7554a58 --- /dev/null +++ b/Editor/Meta/MethodReferenceAnalyzer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1c644b0c018fb87498d69c3202439d21 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Meta/PathAssemblyResolver.cs b/Editor/Meta/PathAssemblyResolver.cs new file mode 100644 index 0000000..d36aefd --- /dev/null +++ b/Editor/Meta/PathAssemblyResolver.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace HybridCLR.Editor.Meta +{ + public class PathAssemblyResolver : IAssemblyResolver + { + private readonly string[] _searchPaths; + public PathAssemblyResolver(params string[] searchPaths) + { + _searchPaths = searchPaths; + } + + public string ResolveAssembly(string assemblyName, bool throwExIfNotFind) + { + foreach(var path in _searchPaths) + { + string assPath = Path.Combine(path, assemblyName + ".dll"); + if (File.Exists(assPath)) + { + Debug.Log($"resolve {assemblyName} at {assPath}"); + return assPath; + } + } + if (throwExIfNotFind) + { + throw new Exception($"resolve assembly:{assemblyName} fail"); + } + return null; + } + } +} diff --git a/Editor/Meta/PathAssemblyResolver.cs.meta b/Editor/Meta/PathAssemblyResolver.cs.meta new file mode 100644 index 0000000..af02cb8 --- /dev/null +++ b/Editor/Meta/PathAssemblyResolver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 121d574bf01969444aa6619a8f6dbb4c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Meta/UnityEditorAssemblyResolver.cs b/Editor/Meta/UnityEditorAssemblyResolver.cs new file mode 100644 index 0000000..092d925 --- /dev/null +++ b/Editor/Meta/UnityEditorAssemblyResolver.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace HybridCLR.Editor.Meta +{ + public class UnityEditorAssemblyResolver : IAssemblyResolver + { + public string ResolveAssembly(string assemblyName, bool throwExIfNotFind) + { + var refAss = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == assemblyName); + if (refAss != null) + { + return refAss.Location; + } + return Assembly.Load(assemblyName).Location; + } + } +} diff --git a/Editor/Meta/UnityEditorAssemblyResolver.cs.meta b/Editor/Meta/UnityEditorAssemblyResolver.cs.meta new file mode 100644 index 0000000..fc640e4 --- /dev/null +++ b/Editor/Meta/UnityEditorAssemblyResolver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 78d06a3ad750c0b4b80b583188b02df3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/MetaUtil.cs b/Editor/MetaUtil.cs new file mode 100644 index 0000000..b5ea5ad --- /dev/null +++ b/Editor/MetaUtil.cs @@ -0,0 +1,90 @@ +using dnlib.DotNet; +using HybridCLR.Editor.Meta; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using IAssemblyResolver = HybridCLR.Editor.Meta.IAssemblyResolver; + +namespace HybridCLR.Editor +{ + public static class MetaUtil + { + public static TypeSig Inflate(TypeSig sig, GenericArgumentContext ctx) + { + if (!sig.ContainsGenericParameter) + { + return sig; + } + return ctx.Resolve(sig); + } + + public static TypeSig ToShareTypeSig(TypeSig typeSig) + { + var corTypes = typeSig?.Module?.CorLibTypes; + if (corTypes == null) + { + return typeSig; + } + var a = typeSig.RemovePinnedAndModifiers(); + switch (typeSig.ElementType) + { + case ElementType.Void: return corTypes.Void; + case ElementType.Boolean: + case ElementType.Char: + case ElementType.I1: + case ElementType.U1:return corTypes.Byte; + case ElementType.I2: + case ElementType.U2: return corTypes.UInt16; + case ElementType.I4: + case ElementType.U4: return corTypes.UInt32; + case ElementType.I8: + case ElementType.U8: return corTypes.UInt64; + case ElementType.R4: return corTypes.Single; + case ElementType.R8: return corTypes.Double; + case ElementType.String: return corTypes.Object; + case ElementType.TypedByRef: return corTypes.TypedReference; + case ElementType.I: + case ElementType.U: return corTypes.UIntPtr; + case ElementType.Object: return corTypes.Object; + case ElementType.Sentinel: return typeSig; + case ElementType.Ptr: return corTypes.UIntPtr; + case ElementType.ByRef: return corTypes.UIntPtr; + case ElementType.SZArray: return corTypes.Object; + case ElementType.Array: return corTypes.Object; + case ElementType.ValueType: return typeSig; + case ElementType.Class: return corTypes.Object; + case ElementType.GenericInst: + { + var gia = (GenericInstSig)a; + if (gia.GenericType.IsClassSig) + { + return corTypes.Object; + } + return new GenericInstSig(gia.GenericType, gia.GenericArguments.Select(ga => ToShareTypeSig(ga)).ToList()); + } + case ElementType.FnPtr: return corTypes.UIntPtr; + case ElementType.ValueArray: return typeSig; + case ElementType.Module: return typeSig; + default: + throw new NotSupportedException(typeSig.ToString()); + } + } + + public static List ToShareTypeSigs(IList typeSigs) + { + if (typeSigs == null) + { + return null; + } + return typeSigs.Select(s => ToShareTypeSig(s)).ToList(); + } + + public static IAssemblyResolver CreateBuildTargetAssemblyResolver(UnityEditor.BuildTarget target) + { + return new CombinedAssemblyResolver(new PathAssemblyResolver( + SettingsUtil.GetHotFixDllsOutputDirByTarget(target)), new UnityEditorAssemblyResolver()); + } + } +} diff --git a/Editor/MetaUtil.cs.meta b/Editor/MetaUtil.cs.meta new file mode 100644 index 0000000..d9342a4 --- /dev/null +++ b/Editor/MetaUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f3dbfe2e8b6a92742b18e287c5d281dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/MethodBridgeGenerator.meta b/Editor/MethodBridgeGenerator.meta new file mode 100644 index 0000000..d04da9b --- /dev/null +++ b/Editor/MethodBridgeGenerator.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4aa66b41c89ed7742ad067898725f7ff +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/MethodBridgeGenerator/Analyzer.cs b/Editor/MethodBridgeGenerator/Analyzer.cs new file mode 100644 index 0000000..fa8079c --- /dev/null +++ b/Editor/MethodBridgeGenerator/Analyzer.cs @@ -0,0 +1,206 @@ +using dnlib.DotNet; +using HybridCLR.Editor.Meta; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace HybridCLR.Editor.MethodBridgeGenerator +{ + + public class Analyzer + { + public class Options + { + public AssemblyReferenceDeepCollector Collector { get; set; } + + public int MaxIterationCount { get; set; } + } + + private readonly int _maxInterationCount; + private readonly AssemblyReferenceDeepCollector _assemblyCollector; + + private readonly object _lock = new object(); + + private readonly List _typeDefs = new List(); + private readonly List _notGenericMethods = new List(); + + private readonly HashSet _genericTypes = new HashSet(); + private readonly HashSet _genericMethods = new HashSet(); + + private List _processingMethods = new List(); + private List _newMethods = new List(); + + public IReadOnlyList TypeDefs => _typeDefs; + + public IReadOnlyList NotGenericMethods => _notGenericMethods; + + public IReadOnlyCollection GenericTypes => _genericTypes; + + public IReadOnlyCollection GenericMethods => _genericMethods; + + + private readonly MethodReferenceAnalyzer _methodReferenceAnalyzer; + + public Analyzer(Options options) + { + _maxInterationCount = options.MaxIterationCount; + _assemblyCollector = options.Collector; + _methodReferenceAnalyzer = new MethodReferenceAnalyzer(this.OnNewMethod); + } + + private void TryAddAndWalkGenericType(GenericClass gc) + { + lock(_lock) + { + gc = gc.ToGenericShare(); + if (_genericTypes.Add(gc)) + { + WalkType(gc); + } + } + } + + private void OnNewMethod(GenericMethod method) + { + lock(_lock) + { + if (_genericMethods.Add(method)) + { + _newMethods.Add(method); + } + if (method.KlassInst != null) + { + TryAddAndWalkGenericType(new GenericClass(method.Method.DeclaringType, method.KlassInst)); + } + } + } + + private void WalkType(GenericClass gc) + { + //Debug.Log($"typespec:{sig} {sig.GenericType} {sig.GenericType.TypeDefOrRef.ResolveTypeDef()}"); + //Debug.Log($"== walk generic type:{new GenericInstSig(gc.Type.ToTypeSig().ToClassOrValueTypeSig(), gc.KlassInst)}"); + ITypeDefOrRef baseType = gc.Type.BaseType; + if (baseType != null && baseType.TryGetGenericInstSig() != null) + { + GenericClass parentType = GenericClass.ResolveClass((TypeSpec)baseType, new GenericArgumentContext(gc.KlassInst, null)); + TryAddAndWalkGenericType(parentType); + } + foreach (var method in gc.Type.Methods) + { + if (method.HasGenericParameters) + { + continue; + } + var gm = new GenericMethod(method, gc.KlassInst, null).ToGenericShare(); + //Debug.Log($"add method:{gm.Method} {gm.KlassInst}"); + + if (_genericMethods.Add(gm)) + { + if (method.HasBody && method.Body.Instructions != null) + { + _newMethods.Add(gm); + } + } + } + } + + private void WalkType(TypeDef typeDef) + { + _typeDefs.Add(typeDef); + if (typeDef.HasGenericParameters) + { + return; + } + ITypeDefOrRef baseType = typeDef.BaseType; + if (baseType != null && baseType.TryGetGenericInstSig() != null) + { + GenericClass gc = GenericClass.ResolveClass((TypeSpec)baseType, null); + TryAddAndWalkGenericType(gc); + } + foreach (var method in typeDef.Methods) + { + if (method.HasGenericParameters) + { + continue; + } + _notGenericMethods.Add(method); + } + } + + private void Prepare() + { + // 将所有非泛型函数全部加入函数列表,同时立马walk这些method。 + // 后续迭代中将只遍历MethodSpec + foreach (var ass in _assemblyCollector.GetLoadedModulesExcludeRootAssemblies()) + { + foreach (TypeDef typeDef in ass.GetTypes()) + { + WalkType(typeDef); + } + + for (uint rid = 1, n = ass.Metadata.TablesStream.TypeSpecTable.Rows; rid <= n; rid++) + { + var ts = ass.ResolveTypeSpec(rid); + if (!ts.ContainsGenericParameter) + { + var cs = GenericClass.ResolveClass(ts, null)?.ToGenericShare(); + if (cs != null) + { + TryAddAndWalkGenericType(cs); + } + } + } + + for (uint rid = 1, n = ass.Metadata.TablesStream.MethodSpecTable.Rows; rid <= n; rid++) + { + var ms = ass.ResolveMethodSpec(rid); + if(ms.DeclaringType.ContainsGenericParameter || ms.GenericInstMethodSig.ContainsGenericParameter) + { + continue; + } + var gm = GenericMethod.ResolveMethod(ms, null)?.ToGenericShare(); + if (gm == null) + { + continue; + } + + if (_genericMethods.Add(gm)) + { + _newMethods.Add(gm); + } + //if (gm.KlassInst != null) + //{ + // TryAddAndWalkGenericType(new GenericClass(gm.Method.DeclaringType, gm.KlassInst)); + //} + } + } + Debug.Log($"PostPrepare allMethods:{_notGenericMethods.Count} newMethods:{_newMethods.Count}"); + } + + private void RecursiveCollect() + { + for (int i = 0; i < _maxInterationCount && _newMethods.Count > 0; i++) + { + var temp = _processingMethods; + _processingMethods = _newMethods; + _newMethods = temp; + _newMethods.Clear(); + + Task.WhenAll(_processingMethods.Select(method => Task.Run(() => + { + _methodReferenceAnalyzer.WalkMethod(method.Method, method.KlassInst, method.MethodInst); + }))); + Debug.Log($"iteration:[{i}] allMethods:{_notGenericMethods.Count} genericClass:{_genericTypes.Count} genericMethods:{_genericMethods.Count} newMethods:{_newMethods.Count}"); + } + } + + public void Run() + { + Prepare(); + RecursiveCollect(); + } + } +} diff --git a/Editor/MethodBridgeGenerator/Analyzer.cs.meta b/Editor/MethodBridgeGenerator/Analyzer.cs.meta new file mode 100644 index 0000000..4fd796f --- /dev/null +++ b/Editor/MethodBridgeGenerator/Analyzer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ee1ec106190e514489c7ba32bc7bc2e7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/MethodBridgeGenerator/Generator.cs b/Editor/MethodBridgeGenerator/Generator.cs new file mode 100644 index 0000000..c32df17 --- /dev/null +++ b/Editor/MethodBridgeGenerator/Generator.cs @@ -0,0 +1,236 @@ +using dnlib.DotNet; +using HybridCLR.Editor.Meta; +using HybridCLR.Editor.Template; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace HybridCLR.Editor.MethodBridgeGenerator +{ + + + public class Generator + { + public class Options + { + public PlatformABI CallConvention { get; set; } + + public string TemplateCode { get; set; } + + public string OutputFile { get; set; } + + public IReadOnlyList NotGenericMethods { get; set; } + + public IReadOnlyCollection GenericMethods { get; set; } + } + + private readonly IReadOnlyList _notGenericMethods; + + private readonly IReadOnlyCollection _genericMethods; + + private readonly string _templateCode; + + private readonly string _outputFile; + + private readonly PlatformAdaptorBase _platformAdaptor; + + private readonly HashSet _managed2nativeMethodSet = new HashSet(); + + private List _managed2nativeMethodList; + + private readonly HashSet _native2managedMethodSet = new HashSet(); + + private List _native2managedMethodList; + + private readonly HashSet _adjustThunkMethodSet = new HashSet(); + + private List _adjustThunkMethodList; + + public Generator(Options options) + { + _notGenericMethods = options.NotGenericMethods; + _genericMethods = options.GenericMethods; + _templateCode = options.TemplateCode; + _outputFile = options.OutputFile; + _platformAdaptor = CreatePlatformAdaptor(options.CallConvention); + } + + private static PlatformAdaptorBase CreatePlatformAdaptor(PlatformABI type) + { + switch (type) + { + case PlatformABI.Universal32: return new PlatformAdaptor_Universal32(); + case PlatformABI.Universal64: return new PlatformAdaptor_Universal64(); + case PlatformABI.Arm64: return new PlatformAdaptor_Arm64(); + default: throw new NotSupportedException(); + } + } + + private MethodBridgeSig CreateMethodBridgeSig(MethodDef methodDef, bool forceRemoveThis, TypeSig returnType, List parameters) + { + var paramInfos = new List(); + if (forceRemoveThis && !methodDef.IsStatic) + { + parameters.RemoveAt(0); + } + foreach (var paramInfo in parameters) + { + paramInfos.Add(new ParamInfo() { Type = _platformAdaptor.CreateTypeInfo(paramInfo) }); + } + var mbs = new MethodBridgeSig() + { + MethodDef = methodDef, + ReturnInfo = new ReturnInfo() { Type = returnType != null ? _platformAdaptor.CreateTypeInfo(returnType) : TypeInfo.s_void }, + ParamInfos = paramInfos, + }; + _platformAdaptor.OptimizeMethod(mbs); + return mbs; + } + + private void AddManaged2NativeMethod(MethodBridgeSig method) + { + if (_managed2nativeMethodSet.Add(method)) + { + method.Init(); + } + } + + private void AddNative2ManagedMethod(MethodBridgeSig method) + { + if (_native2managedMethodSet.Add(method)) + { + method.Init(); + } + } + + private void AddAdjustThunkMethod(MethodBridgeSig method) + { + if (_adjustThunkMethodSet.Add(method)) + { + method.Init(); + } + } + + private void ProcessMethod(MethodDef method, List klassInst, List methodInst) + { + if (method.IsPrivate || (method.IsAssembly && !method.IsPublic && !method.IsFamily)) + { + return; + } + + TypeSig returnType; + List parameters; + if (klassInst == null && methodInst == null) + { + returnType = method.ReturnType; + parameters = method.Parameters.Select(p => p.Type).ToList(); + } + else + { + var gc = new GenericArgumentContext(klassInst, methodInst); + returnType = MetaUtil.Inflate(method.ReturnType, gc); + parameters = method.Parameters.Select(p => MetaUtil.Inflate(p.Type, gc)).ToList(); + } + + var m2nMethod = CreateMethodBridgeSig(method, false, returnType, parameters); + AddManaged2NativeMethod(m2nMethod); + + if (method.IsVirtual) + { + if (method.DeclaringType.IsInterface) + { + AddAdjustThunkMethod(m2nMethod); + } + //var adjustThunkMethod = CreateMethodBridgeSig(method, true, returnType, parameters); + AddNative2ManagedMethod(m2nMethod); + } + if (method.Name == "Invoke" && method.DeclaringType.IsDelegate) + { + var openMethod = CreateMethodBridgeSig(method, true, returnType, parameters); + AddNative2ManagedMethod(openMethod); + } + } + + public void PrepareMethods() + { + foreach(var method in _notGenericMethods) + { + ProcessMethod(method, null, null); + } + + foreach(var method in _genericMethods) + { + ProcessMethod(method.Method, method.KlassInst, method.MethodInst); + } + + { + var sortedMethods = new SortedDictionary(); + foreach (var method in _managed2nativeMethodSet) + { + sortedMethods.Add(method.CreateCallSigName(), method); + } + _managed2nativeMethodList = sortedMethods.Values.ToList(); + } + { + var sortedMethods = new SortedDictionary(); + foreach (var method in _native2managedMethodSet) + { + sortedMethods.Add(method.CreateCallSigName(), method); + } + _native2managedMethodList = sortedMethods.Values.ToList(); + } + { + var sortedMethods = new SortedDictionary(); + foreach (var method in _adjustThunkMethodSet) + { + sortedMethods.Add(method.CreateCallSigName(), method); + } + _adjustThunkMethodList = sortedMethods.Values.ToList(); + } + } + + public void Generate() + { + var frr = new FileRegionReplace(_templateCode); + + List lines = new List(20_0000); + + Debug.LogFormat("== managed2native:{0} native2managed:{1} adjustThunk:{2}", + _managed2nativeMethodList.Count, _native2managedMethodList.Count, _adjustThunkMethodList.Count); + + foreach(var method in _managed2nativeMethodList) + { + _platformAdaptor.GenerateManaged2NativeMethod(method, lines); + } + + _platformAdaptor.GenerateManaged2NativeStub(_managed2nativeMethodList, lines); + + foreach (var method in _native2managedMethodList) + { + _platformAdaptor.GenerateNative2ManagedMethod(method, lines); + } + + _platformAdaptor.GenerateNative2ManagedStub(_native2managedMethodList, lines); + + foreach (var method in _adjustThunkMethodList) + { + _platformAdaptor.GenerateAdjustThunkMethod(method, lines); + } + + _platformAdaptor.GenerateAdjustThunkStub(_adjustThunkMethodList, lines); + + frr.Replace("INVOKE_STUB", string.Join("\n", lines)); + + Directory.CreateDirectory(Path.GetDirectoryName(_outputFile)); + + frr.Commit(_outputFile); + } + + } +} diff --git a/Editor/MethodBridgeGenerator/Generator.cs.meta b/Editor/MethodBridgeGenerator/Generator.cs.meta new file mode 100644 index 0000000..84054d4 --- /dev/null +++ b/Editor/MethodBridgeGenerator/Generator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3171ed79d43e06c4b8c889b0a3f38969 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/MethodBridgeGenerator/MethodBridgeSig.cs b/Editor/MethodBridgeGenerator/MethodBridgeSig.cs new file mode 100644 index 0000000..c0e7329 --- /dev/null +++ b/Editor/MethodBridgeGenerator/MethodBridgeSig.cs @@ -0,0 +1,103 @@ +using dnlib.DotNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace HybridCLR.Editor.MethodBridgeGenerator +{ + public class MethodBridgeSig : IEquatable + { + public MethodDef MethodDef { get; set; } + + public ReturnInfo ReturnInfo { get; set; } + + public List ParamInfos { get; set; } + + public void Init() + { + for(int i = 0; i < ParamInfos.Count; i++) + { + ParamInfos[i].Index = i; + } + } + + public void TransfromSigTypes(Func transformer) + { + ReturnInfo.Type = transformer(ReturnInfo.Type, true); + foreach(var paramType in ParamInfos) + { + paramType.Type = transformer(paramType.Type, false); + } + } + + public string CreateCallSigName() + { + var n = new StringBuilder(); + n.Append(ReturnInfo.Type.CreateSigName()); + foreach(var param in ParamInfos) + { + n.Append(param.Type.CreateSigName()); + } + return n.ToString(); + } + + public string CreateInvokeSigName() + { + var n = new StringBuilder(); + n.Append(ReturnInfo.Type.CreateSigName()); + foreach (var param in ParamInfos) + { + n.Append(param.Type.CreateSigName()); + } + return n.ToString(); + } + + public override bool Equals(object obj) + { + return Equals((MethodBridgeSig)obj); + } + + public bool Equals(MethodBridgeSig other) + { + if (other == null) + { + return false; + } + + if (!ReturnInfo.Type.Equals(other.ReturnInfo.Type)) + { + return false; + } + if (ParamInfos.Count != other.ParamInfos.Count) + { + return false; + } + for(int i = 0; i < ParamInfos.Count; i++) + { + if (!ParamInfos[i].Type.Equals(other.ParamInfos[i].Type)) + { + return false; + } + } + return true; + } + + public override int GetHashCode() + { + int hash = 17; + + hash = hash * 23 + ReturnInfo.Type.GetHashCode(); + + foreach(var p in ParamInfos) + { + hash = hash * 23 + p.Type.GetHashCode(); + } + + return hash; + } + } +} diff --git a/Editor/MethodBridgeGenerator/MethodBridgeSig.cs.meta b/Editor/MethodBridgeGenerator/MethodBridgeSig.cs.meta new file mode 100644 index 0000000..8ae3b55 --- /dev/null +++ b/Editor/MethodBridgeGenerator/MethodBridgeSig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cbe63fb39b5bdcc448f3589b475fb5d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/MethodBridgeGenerator/ParamInfo.cs b/Editor/MethodBridgeGenerator/ParamInfo.cs new file mode 100644 index 0000000..fd60fa2 --- /dev/null +++ b/Editor/MethodBridgeGenerator/ParamInfo.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HybridCLR.Editor.MethodBridgeGenerator +{ + + public class ParamInfo + { + public TypeInfo Type { get; set; } + + public int Index { get; set; } + + //public bool IsNative2ManagedByAddress => Type.PorType >= ParamOrReturnType.STRUCT_NOT_PASS_AS_VALUE; + public bool IsPassToManagedByAddress => Type.GetParamSlotNum() > 1; + + public bool IsPassToNativeByAddress => Type.PorType == ParamOrReturnType.STRUCTURE_AS_REF_PARAM; + + public string Native2ManagedParamValue(PlatformABI canv) + { + return IsPassToManagedByAddress ? $"(uint64_t)&__arg{Index}" : $"*(uint64_t*)&__arg{Index}"; + } + + public string Managed2NativeParamValue(PlatformABI canv) + { + return IsPassToNativeByAddress ? $"(uint64_t)(localVarBase+argVarIndexs[{Index}])" : $"*({Type.GetTypeName()}*)(localVarBase+argVarIndexs[{Index}])"; + } + } + + public class ReturnInfo + { + public TypeInfo Type { get; set; } + + public bool IsVoid => Type.PorType == ParamOrReturnType.VOID; + + public int GetParamSlotNum(PlatformABI canv) + { + return Type.GetParamSlotNum(); + } + } +} diff --git a/Editor/MethodBridgeGenerator/ParamInfo.cs.meta b/Editor/MethodBridgeGenerator/ParamInfo.cs.meta new file mode 100644 index 0000000..517be64 --- /dev/null +++ b/Editor/MethodBridgeGenerator/ParamInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c04e163336b93af44b4b565d0ab195e2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/MethodBridgeGenerator/ParamOrReturnType.cs b/Editor/MethodBridgeGenerator/ParamOrReturnType.cs new file mode 100644 index 0000000..5a3814c --- /dev/null +++ b/Editor/MethodBridgeGenerator/ParamOrReturnType.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HybridCLR.Editor.MethodBridgeGenerator +{ + public enum ParamOrReturnType + { + VOID, + I1_U1, + //U1, + I2_U2, + //U2, + I4_U4, + I8_U8, + //I_U, + R4, + R8, + ARM64_HFA_FLOAT_2, + VALUE_TYPE_SIZE_LESS_EQUAL_8, + I16, // 8 < size <= 16 + STRUCT_NOT_PASS_AS_VALUE, // struct pass not as value + STRUCTURE_AS_REF_PARAM, // size > 16 + ARM64_HFA_FLOAT_3, + ARM64_HFA_FLOAT_4, + ARM64_HFA_DOUBLE_2, + ARM64_HFA_DOUBLE_3, + ARM64_HFA_DOUBLE_4, + ARM64_HVA_8, + ARM64_HVA_16, + STRUCTURE_ALIGN1, // size > 16 + STRUCTURE_ALIGN2, + STRUCTURE_ALIGN4, + STRUCTURE_ALIGN8, + } +} diff --git a/Editor/MethodBridgeGenerator/ParamOrReturnType.cs.meta b/Editor/MethodBridgeGenerator/ParamOrReturnType.cs.meta new file mode 100644 index 0000000..78fd4e0 --- /dev/null +++ b/Editor/MethodBridgeGenerator/ParamOrReturnType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7886905937fafb64999dc4e358565982 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/MethodBridgeGenerator/PlatformABI.cs b/Editor/MethodBridgeGenerator/PlatformABI.cs new file mode 100644 index 0000000..6c8b14b --- /dev/null +++ b/Editor/MethodBridgeGenerator/PlatformABI.cs @@ -0,0 +1,9 @@ +namespace HybridCLR.Editor.MethodBridgeGenerator +{ + public enum PlatformABI + { + Universal32, + Universal64, + Arm64, + } +} diff --git a/Editor/MethodBridgeGenerator/PlatformABI.cs.meta b/Editor/MethodBridgeGenerator/PlatformABI.cs.meta new file mode 100644 index 0000000..e257472 --- /dev/null +++ b/Editor/MethodBridgeGenerator/PlatformABI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: deee8d06a7f05bf4d9ab4b78f0a8a47c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/MethodBridgeGenerator/PlatformAdaptorBase.cs b/Editor/MethodBridgeGenerator/PlatformAdaptorBase.cs new file mode 100644 index 0000000..9359aac --- /dev/null +++ b/Editor/MethodBridgeGenerator/PlatformAdaptorBase.cs @@ -0,0 +1,283 @@ +using dnlib.DotNet; +using HybridCLR.Editor.Meta; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace HybridCLR.Editor.MethodBridgeGenerator +{ + public abstract class PlatformAdaptorBase + { + private static readonly ValueTypeSizeAligmentCalculator s_calculator64 = new ValueTypeSizeAligmentCalculator(false); + + private static readonly ValueTypeSizeAligmentCalculator s_calculator32 = new ValueTypeSizeAligmentCalculator(true); + + public abstract bool IsArch32 { get; } + + public virtual bool IsSupportHFA => false; + + public TypeInfo GetNativeIntTypeInfo() => IsArch32 ? TypeInfo.s_i4u4 : TypeInfo.s_i8u8; + + public abstract void GenerateManaged2NativeMethod(MethodBridgeSig method, List lines); + + public abstract void GenerateNative2ManagedMethod(MethodBridgeSig method, List lines); + + public abstract void GenerateAdjustThunkMethod(MethodBridgeSig method, List outputLines); + + protected abstract TypeInfo OptimizeSigType(TypeInfo type, bool returnType); + + public virtual void OptimizeMethod(MethodBridgeSig method) + { + method.TransfromSigTypes(OptimizeSigType); + } + + private readonly Dictionary _typeSizeCache64 = new Dictionary(TypeEqualityComparer.Instance); + + private readonly Dictionary _typeSizeCache32 = new Dictionary(TypeEqualityComparer.Instance); + + public (int Size, int Aligment) ComputeSizeAndAligmentOfArch64(TypeSig t) + { + if (_typeSizeCache64.TryGetValue(t, out var sizeAndAligment)) + { + return sizeAndAligment; + } + sizeAndAligment = s_calculator64.SizeAndAligmentOf(t); + _typeSizeCache64.Add(t, sizeAndAligment); + return sizeAndAligment; + } + + protected (int Size, int Aligment) ComputeSizeAndAligmentOfArch32(TypeSig t) + { + if (_typeSizeCache32.TryGetValue(t, out var sa)) + { + return sa; + } + // all this just to invoke one opcode with no arguments! + sa = s_calculator32.SizeAndAligmentOf(t); + _typeSizeCache32.Add(t, sa); + return sa; + } + + public TypeInfo CreateTypeInfo(TypeSig type) + { + type = type.RemovePinnedAndModifiers(); + if (type.IsByRef) + { + return GetNativeIntTypeInfo(); + } + switch(type.ElementType) + { + case ElementType.Void: return TypeInfo.s_void; + case ElementType.Boolean: + case ElementType.I1: + case ElementType.U1: return TypeInfo.s_i1u1; + case ElementType.Char: + case ElementType.I2: + case ElementType.U2: return TypeInfo.s_i2u2; + case ElementType.I4: + case ElementType.U4: return TypeInfo.s_i4u4; + case ElementType.I8: + case ElementType.U8: return TypeInfo.s_i8u8; + case ElementType.R4: return TypeInfo.s_r4; + case ElementType.R8: return TypeInfo.s_r8; + case ElementType.I: + case ElementType.U: + case ElementType.String: + case ElementType.Ptr: + case ElementType.ByRef: + case ElementType.Class: + case ElementType.Array: + case ElementType.SZArray: + case ElementType.FnPtr: + case ElementType.Object: + case ElementType.Module: return GetNativeIntTypeInfo(); + case ElementType.TypedByRef: return CreateValueType(type); + case ElementType.ValueType: + { + TypeDef typeDef = type.ToTypeDefOrRef().ResolveTypeDef(); + if (typeDef.IsEnum) + { + return CreateTypeInfo(typeDef.GetEnumUnderlyingType()); + } + return CreateValueType(type); + } + case ElementType.GenericInst: + { + GenericInstSig gis = (GenericInstSig)type; + if (!gis.GenericType.IsValueType) + { + return GetNativeIntTypeInfo(); + } + TypeDef typeDef = gis.GenericType.ToTypeDefOrRef().ResolveTypeDef(); + if (typeDef.IsEnum) + { + return CreateTypeInfo(typeDef.GetEnumUnderlyingType()); + } + return CreateValueType(type); + } + default: throw new NotSupportedException($"{type.ElementType}"); + } + } + + private static bool IsNotHFAFastCheck(int typeSize) + { + return typeSize != 8 && typeSize != 12 && typeSize != 16 && typeSize != 24 && typeSize != 32; + } + + private static bool ComputHFATypeInfo0(TypeSig type, HFATypeInfo typeInfo) + { + TypeDef typeDef = type.ToTypeDefOrRef().ResolveTypeDefThrow(); + + List klassInst = type.ToGenericInstSig()?.GenericArguments?.ToList(); + GenericArgumentContext ctx = klassInst != null ? new GenericArgumentContext(klassInst, null) : null; + + var fields = typeDef.Fields;// typeDef.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + foreach (FieldDef field in fields) + { + if (field.IsStatic) + { + continue; + } + TypeSig ftype = ctx != null ? MetaUtil.Inflate(field.FieldType, ctx) : field.FieldType; + switch(ftype.ElementType) + { + case ElementType.R4: + case ElementType.R8: + { + if (ftype == typeInfo.Type || typeInfo.Type == null) + { + typeInfo.Type = ftype; + ++typeInfo.Count; + } + else + { + return false; + } + break; + } + case ElementType.ValueType: + { + if (!ComputHFATypeInfo0(ftype, typeInfo)) + { + return false; + } + break; + } + case ElementType.GenericInst: + { + if (!ftype.IsValueType || !ComputHFATypeInfo0(ftype, typeInfo)) + { + return false; + } + break; + } + default: return false; + } + } + return typeInfo.Count <= 4; + } + + private static bool ComputHFATypeInfo(TypeSig type, int typeSize, out HFATypeInfo typeInfo) + { + typeInfo = new HFATypeInfo(); + if (IsNotHFAFastCheck(typeSize)) + { + return false; + } + bool ok = ComputHFATypeInfo0(type, typeInfo); + if (ok && typeInfo.Count >= 2 && typeInfo.Count <= 4) + { + int fieldSize = typeInfo.Type.ElementType == ElementType.R4 ? 4 : 8; + return typeSize == fieldSize * typeInfo.Count; + } + return false; + } + + protected static TypeInfo CreateGeneralValueType(TypeSig type, int size, int aligment) + { + Debug.Assert(size % aligment == 0); + switch (aligment) + { + case 1: return new TypeInfo(ParamOrReturnType.STRUCTURE_ALIGN1, size); + case 2: return new TypeInfo(ParamOrReturnType.STRUCTURE_ALIGN2, size); + case 4: return new TypeInfo(ParamOrReturnType.STRUCTURE_ALIGN4, size); + case 8: return new TypeInfo(ParamOrReturnType.STRUCTURE_ALIGN8, size); + default: throw new NotSupportedException($"type:{type} not support aligment:{aligment}"); + } + } + + protected TypeInfo CreateValueType(TypeSig type) + { + (int typeSize, int typeAligment) = IsArch32 ? ComputeSizeAndAligmentOfArch32(type) : ComputeSizeAndAligmentOfArch64(type); + if (IsSupportHFA && ComputHFATypeInfo(type, typeSize, out HFATypeInfo hfaTypeInfo)) + { + bool isFloat = hfaTypeInfo.Type.ElementType == ElementType.R4; + switch (hfaTypeInfo.Count) + { + case 2: return isFloat ? TypeInfo.s_vf2 : TypeInfo.s_vd2; + case 3: return isFloat ? TypeInfo.s_vf3 : TypeInfo.s_vd3; + case 4: return isFloat ? TypeInfo.s_vf4 : TypeInfo.s_vd4; + default: throw new NotSupportedException(); + } + } + else + { + // 64位下结构体内存对齐规则是一样的 + return CreateGeneralValueType(type, typeSize, typeAligment); + } + } + + public void GenerateManaged2NativeStub(List methods, List lines) + { + lines.Add($@" +Managed2NativeMethodInfo hybridclr::interpreter::g_managed2nativeStub[] = +{{ +"); + + foreach (var method in methods) + { + lines.Add($"\t{{\"{method.CreateInvokeSigName()}\", __M2N_{method.CreateInvokeSigName()}}},"); + } + + lines.Add($"\t{{nullptr, nullptr}},"); + lines.Add("};"); + } + + public void GenerateNative2ManagedStub(List methods, List lines) + { + lines.Add($@" +Native2ManagedMethodInfo hybridclr::interpreter::g_native2managedStub[] = +{{ +"); + + foreach (var method in methods) + { + lines.Add($"\t{{\"{method.CreateInvokeSigName()}\", (Il2CppMethodPointer)__N2M_{method.CreateInvokeSigName()}}},"); + } + + lines.Add($"\t{{nullptr, nullptr}},"); + lines.Add("};"); + } + + public void GenerateAdjustThunkStub(List methods, List lines) + { + lines.Add($@" +NativeAdjustThunkMethodInfo hybridclr::interpreter::g_adjustThunkStub[] = +{{ +"); + + foreach (var method in methods) + { + lines.Add($"\t{{\"{method.CreateInvokeSigName()}\", (Il2CppMethodPointer)__N2M_AdjustorThunk_{method.CreateCallSigName()}}},"); + } + + lines.Add($"\t{{nullptr, nullptr}},"); + lines.Add("};"); + } + } +} diff --git a/Editor/MethodBridgeGenerator/PlatformAdaptorBase.cs.meta b/Editor/MethodBridgeGenerator/PlatformAdaptorBase.cs.meta new file mode 100644 index 0000000..f18ef36 --- /dev/null +++ b/Editor/MethodBridgeGenerator/PlatformAdaptorBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ebf14107fd82b364cb7d61276d444829 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/MethodBridgeGenerator/PlatformAdaptor_Arm64.cs b/Editor/MethodBridgeGenerator/PlatformAdaptor_Arm64.cs new file mode 100644 index 0000000..92d23b6 --- /dev/null +++ b/Editor/MethodBridgeGenerator/PlatformAdaptor_Arm64.cs @@ -0,0 +1,101 @@ +using dnlib.DotNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace HybridCLR.Editor.MethodBridgeGenerator +{ + + public class HFATypeInfo + { + public TypeSig Type { get; set; } + + public int Count { get; set; } + } + + public class PlatformAdaptor_Arm64 : PlatformAdaptorBase + { + public PlatformABI CallConventionType { get; } = PlatformABI.Universal64; + + public override bool IsArch32 => false; + + public override bool IsSupportHFA => true; + + protected override TypeInfo OptimizeSigType(TypeInfo type, bool returnType) + { + if (!type.IsGeneralValueType) + { + return type; + } + int typeSize = type.Size; + if (typeSize <= 8) + { + return TypeInfo.s_i8u8; + } + if (typeSize <= 16) + { + return TypeInfo.s_i16; + } + if (returnType) + { + return type.PorType != ParamOrReturnType.STRUCTURE_ALIGN1 ? new TypeInfo(ParamOrReturnType.STRUCTURE_ALIGN1, typeSize) : type; + } + return TypeInfo.s_ref; + } + + public override void GenerateManaged2NativeMethod(MethodBridgeSig method, List lines) + { + int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType); + string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" })); + string paramNameListStr = string.Join(", ", method.ParamInfos.Select(p => p.Managed2NativeParamValue(this.CallConventionType)).Concat(new string[] { "method" })); + + lines.Add($@" +// {method.MethodDef} +static void __M2N_{method.CreateCallSigName()}(const MethodInfo* method, uint16_t* argVarIndexs, StackObject* localVarBase, void* ret) +{{ + typedef {method.ReturnInfo.Type.GetTypeName()} (*NativeMethod)({paramListStr}); + {(!method.ReturnInfo.IsVoid ? $"*({method.ReturnInfo.Type.GetTypeName()}*)ret = " : "")}((NativeMethod)(GetInterpreterDirectlyCallMethodPointer(method)))({paramNameListStr}); +}} +"); + } + + public override void GenerateNative2ManagedMethod(MethodBridgeSig method, List lines) + { + int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType); + string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" })); + + lines.Add($@" +// {method.MethodDef} +static {method.ReturnInfo.Type.GetTypeName()} __N2M_{method.CreateCallSigName()}({paramListStr}) +{{ + StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => p.Native2ManagedParamValue(this.CallConventionType)))} }}; + StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)}; + Interpreter::Execute(method, args, ret); + {(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")} +}} +"); + } + + public override void GenerateAdjustThunkMethod(MethodBridgeSig method, List lines) + { + int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType); + + string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" })); + + lines.Add($@" +// {method.MethodDef} +static {method.ReturnInfo.Type.GetTypeName()} __N2M_AdjustorThunk_{method.CreateCallSigName()}({paramListStr}) +{{ + StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => (p.Index == 0 ? $"(uint64_t)(*(uint8_t**)&__arg{p.Index} + sizeof(Il2CppObject))" : p.Native2ManagedParamValue(this.CallConventionType))))} }}; + StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)}; + Interpreter::Execute(method, args, ret); + {(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")} +}} +"); + } + } +} diff --git a/Editor/MethodBridgeGenerator/PlatformAdaptor_Arm64.cs.meta b/Editor/MethodBridgeGenerator/PlatformAdaptor_Arm64.cs.meta new file mode 100644 index 0000000..4b9a4a1 --- /dev/null +++ b/Editor/MethodBridgeGenerator/PlatformAdaptor_Arm64.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bff17a6e8ee060c4eb9ac97fcf30bf78 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/MethodBridgeGenerator/PlatformAdaptor_Universal32.cs b/Editor/MethodBridgeGenerator/PlatformAdaptor_Universal32.cs new file mode 100644 index 0000000..a044440 --- /dev/null +++ b/Editor/MethodBridgeGenerator/PlatformAdaptor_Universal32.cs @@ -0,0 +1,84 @@ +using dnlib.DotNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace HybridCLR.Editor.MethodBridgeGenerator +{ + internal class PlatformAdaptor_Universal32 : PlatformAdaptorBase + { + public PlatformABI CallConventionType { get; } = PlatformABI.Universal32; + + public override bool IsArch32 => true; + + //protected override TypeInfo CreateValueType(TypeSig type, bool returnValue) + //{ + // (int typeSize, int typeAligment) = ComputeSizeAndAligmentOfArch32(type); + // int actualAliment = typeAligment <= 4 ? 1 : 8; + // return CreateGeneralValueType(type, typeSize, actualAliment); + //} + + protected override TypeInfo OptimizeSigType(TypeInfo type, bool returnType) + { + if (type.PorType > ParamOrReturnType.STRUCTURE_ALIGN1 && type.PorType <= ParamOrReturnType.STRUCTURE_ALIGN4) + { + return new TypeInfo(ParamOrReturnType.STRUCTURE_ALIGN1, type.Size); + } + return type; + } + + public override void GenerateManaged2NativeMethod(MethodBridgeSig method, List lines) + { + string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" })); + string paramNameListStr = string.Join(", ", method.ParamInfos.Select(p => p.Managed2NativeParamValue(this.CallConventionType)).Concat(new string[] { "method" })); + + lines.Add($@" +// {method.MethodDef} +static void __M2N_{method.CreateCallSigName()}(const MethodInfo* method, uint16_t* argVarIndexs, StackObject* localVarBase, void* ret) +{{ + typedef {method.ReturnInfo.Type.GetTypeName()} (*NativeMethod)({paramListStr}); + {(!method.ReturnInfo.IsVoid ? $"*({method.ReturnInfo.Type.GetTypeName()}*)ret = " : "")}((NativeMethod)(GetInterpreterDirectlyCallMethodPointer(method)))({paramNameListStr}); +}} +"); + } + public override void GenerateNative2ManagedMethod(MethodBridgeSig method, List lines) + { + int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType); + string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" })); + + lines.Add($@" +// {method.MethodDef} +static {method.ReturnInfo.Type.GetTypeName()} __N2M_{method.CreateCallSigName()}({paramListStr}) +{{ + StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => p.Native2ManagedParamValue(this.CallConventionType)))} }}; + StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)}; + Interpreter::Execute(method, args, ret); + {(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")} +}} +"); + } + public override void GenerateAdjustThunkMethod(MethodBridgeSig method, List lines) + { + int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType); + + string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" })); + + lines.Add($@" +// {method.MethodDef} +static {method.ReturnInfo.Type.GetTypeName()} __N2M_AdjustorThunk_{method.CreateCallSigName()}({paramListStr}) +{{ + StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => (p.Index == 0 ? $"(uint64_t)(*(uint8_t**)&__arg{p.Index} + sizeof(Il2CppObject))" : p.Native2ManagedParamValue(this.CallConventionType))))} }}; + StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)}; + Interpreter::Execute(method, args, ret); + {(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")} +}} +"); + + } + } + +} diff --git a/Editor/MethodBridgeGenerator/PlatformAdaptor_Universal32.cs.meta b/Editor/MethodBridgeGenerator/PlatformAdaptor_Universal32.cs.meta new file mode 100644 index 0000000..3b2f55a --- /dev/null +++ b/Editor/MethodBridgeGenerator/PlatformAdaptor_Universal32.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fb741900113b22443a2054ddba6b131b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/MethodBridgeGenerator/PlatformAdaptor_Universal64.cs b/Editor/MethodBridgeGenerator/PlatformAdaptor_Universal64.cs new file mode 100644 index 0000000..91edbf7 --- /dev/null +++ b/Editor/MethodBridgeGenerator/PlatformAdaptor_Universal64.cs @@ -0,0 +1,78 @@ +using dnlib.DotNet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace HybridCLR.Editor.MethodBridgeGenerator +{ + public class PlatformAdaptor_Universal64 : PlatformAdaptorBase + { + public PlatformABI CallConventionType { get; } = PlatformABI.Universal64; + + public override bool IsArch32 => false; + + public override bool IsSupportHFA => true; + + protected override TypeInfo OptimizeSigType(TypeInfo type, bool returnType) + { + if (type.PorType > ParamOrReturnType.STRUCTURE_ALIGN1 && type.PorType <= ParamOrReturnType.STRUCTURE_ALIGN8) + { + return new TypeInfo(ParamOrReturnType.STRUCTURE_ALIGN1, type.Size); + } + return type; + } + + public override void GenerateManaged2NativeMethod(MethodBridgeSig method, List lines) + { + int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType); + + string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" })); + string paramTypeListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()}").Concat(new string[] { "const MethodInfo*" })); ; + string paramNameListStr = string.Join(", ", method.ParamInfos.Select(p => p.Managed2NativeParamValue(this.CallConventionType)).Concat(new string[] { "method" })); + + lines.Add($@" +// {method.MethodDef} +static void __M2N_{method.CreateCallSigName()}(const MethodInfo* method, uint16_t* argVarIndexs, StackObject* localVarBase, void* ret) +{{ + typedef {method.ReturnInfo.Type.GetTypeName()} (*NativeMethod)({paramListStr}); + {(!method.ReturnInfo.IsVoid ? $"*({method.ReturnInfo.Type.GetTypeName()}*)ret = " : "")}((NativeMethod)(GetInterpreterDirectlyCallMethodPointer(method)))({paramNameListStr}); +}} +"); + } + public override void GenerateNative2ManagedMethod(MethodBridgeSig method, List lines) + { + int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType); + string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" })); + lines.Add($@" +// {method.MethodDef} +static {method.ReturnInfo.Type.GetTypeName()} __N2M_{method.CreateCallSigName()}({paramListStr}) +{{ + StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => p.Native2ManagedParamValue(this.CallConventionType)))} }}; + StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)}; + Interpreter::Execute(method, args, ret); + {(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")} +}} +"); + } + + public override void GenerateAdjustThunkMethod(MethodBridgeSig method, List lines) + { + int totalQuadWordNum = method.ParamInfos.Count + method.ReturnInfo.GetParamSlotNum(this.CallConventionType); + string paramListStr = string.Join(", ", method.ParamInfos.Select(p => $"{p.Type.GetTypeName()} __arg{p.Index}").Concat(new string[] { "const MethodInfo* method" })); + lines.Add($@" +// {method.MethodDef} +static {method.ReturnInfo.Type.GetTypeName()} __N2M_AdjustorThunk_{method.CreateCallSigName()}({paramListStr}) +{{ + StackObject args[{Math.Max(totalQuadWordNum, 1)}] = {{{string.Join(", ", method.ParamInfos.Select(p => (p.Index == 0 ? $"(uint64_t)(*(uint8_t**)&__arg{p.Index} + sizeof(Il2CppObject))" : p.Native2ManagedParamValue(this.CallConventionType))))} }}; + StackObject* ret = {(method.ReturnInfo.IsVoid ? "nullptr" : "args + " + method.ParamInfos.Count)}; + Interpreter::Execute(method, args, ret); + {(!method.ReturnInfo.IsVoid ? $"return *({method.ReturnInfo.Type.GetTypeName()}*)ret;" : "")} +}} +"); + } + } +} diff --git a/Editor/MethodBridgeGenerator/PlatformAdaptor_Universal64.cs.meta b/Editor/MethodBridgeGenerator/PlatformAdaptor_Universal64.cs.meta new file mode 100644 index 0000000..d782aae --- /dev/null +++ b/Editor/MethodBridgeGenerator/PlatformAdaptor_Universal64.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 26324be9505c9f54996bcbb62ba49132 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/MethodBridgeGenerator/TypeInfo.cs b/Editor/MethodBridgeGenerator/TypeInfo.cs new file mode 100644 index 0000000..de3c494 --- /dev/null +++ b/Editor/MethodBridgeGenerator/TypeInfo.cs @@ -0,0 +1,142 @@ +using dnlib.DotNet; +using System; +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; + +namespace HybridCLR.Editor.MethodBridgeGenerator +{ + public class TypeInfo : IEquatable + { + + public static readonly TypeInfo s_void = new TypeInfo(ParamOrReturnType.VOID); + public static readonly TypeInfo s_i1u1 = new TypeInfo(ParamOrReturnType.I1_U1); + public static readonly TypeInfo s_i2u2 = new TypeInfo(ParamOrReturnType.I2_U2); + public static readonly TypeInfo s_i4u4 = new TypeInfo(ParamOrReturnType.I4_U4); + public static readonly TypeInfo s_i8u8 = new TypeInfo(ParamOrReturnType.I8_U8); + public static readonly TypeInfo s_r4 = new TypeInfo(ParamOrReturnType.R4); + public static readonly TypeInfo s_r8 = new TypeInfo(ParamOrReturnType.R8); + public static readonly TypeInfo s_i16 = new TypeInfo(ParamOrReturnType.I16); + public static readonly TypeInfo s_ref = new TypeInfo(ParamOrReturnType.STRUCTURE_AS_REF_PARAM); + + public static readonly TypeInfo s_vf2 = new TypeInfo(ParamOrReturnType.ARM64_HFA_FLOAT_2); + public static readonly TypeInfo s_vf3 = new TypeInfo(ParamOrReturnType.ARM64_HFA_FLOAT_3); + public static readonly TypeInfo s_vf4 = new TypeInfo(ParamOrReturnType.ARM64_HFA_FLOAT_4); + public static readonly TypeInfo s_vd2 = new TypeInfo(ParamOrReturnType.ARM64_HFA_DOUBLE_2); + public static readonly TypeInfo s_vd3 = new TypeInfo(ParamOrReturnType.ARM64_HFA_DOUBLE_3); + public static readonly TypeInfo s_vd4 = new TypeInfo(ParamOrReturnType.ARM64_HFA_DOUBLE_4); + + public TypeInfo(ParamOrReturnType portype) + { + PorType = portype; + Size = 0; + } + + public TypeInfo(ParamOrReturnType portype, int size) + { + PorType = portype; + Size = size; + } + + public ParamOrReturnType PorType { get; } + + public bool IsGeneralValueType => PorType >= ParamOrReturnType.STRUCTURE_ALIGN1 && PorType <= ParamOrReturnType.STRUCTURE_ALIGN8; + + public int Size { get; } + + public bool Equals(TypeInfo other) + { + return PorType == other.PorType && Size == other.Size; + } + + public override bool Equals(object obj) + { + return Equals((TypeInfo)obj); + } + + public override int GetHashCode() + { + return (int)PorType * 23 + Size; + } + + public string CreateSigName() + { + switch (PorType) + { + case ParamOrReturnType.VOID: return "v"; + case ParamOrReturnType.I1_U1: return "i1"; + case ParamOrReturnType.I2_U2: return "i2"; + case ParamOrReturnType.I4_U4: return "i4"; + case ParamOrReturnType.I8_U8: return "i8"; + case ParamOrReturnType.R4: return "r4"; + case ParamOrReturnType.R8: return "r8"; + case ParamOrReturnType.I16: return "i16"; + case ParamOrReturnType.STRUCTURE_AS_REF_PARAM: return "sr"; + case ParamOrReturnType.ARM64_HFA_FLOAT_2: return "vf2"; + case ParamOrReturnType.ARM64_HFA_FLOAT_3: return "vf3"; + case ParamOrReturnType.ARM64_HFA_FLOAT_4: return "vf4"; + case ParamOrReturnType.ARM64_HFA_DOUBLE_2: return "vd2"; + case ParamOrReturnType.ARM64_HFA_DOUBLE_3: return "vd3"; + case ParamOrReturnType.ARM64_HFA_DOUBLE_4: return "vd4"; + case ParamOrReturnType.STRUCTURE_ALIGN1: return "S" + Size; + case ParamOrReturnType.STRUCTURE_ALIGN2: return "A" + Size; + case ParamOrReturnType.STRUCTURE_ALIGN4: return "B" + Size; + case ParamOrReturnType.STRUCTURE_ALIGN8: return "C" + Size; + default: throw new NotSupportedException(PorType.ToString()); + }; + } + + public string GetTypeName() + { + switch (PorType) + { + case ParamOrReturnType.VOID: return "void"; + case ParamOrReturnType.I1_U1: return "int8_t"; + case ParamOrReturnType.I2_U2: return "int16_t"; + case ParamOrReturnType.I4_U4: return "int32_t"; + case ParamOrReturnType.I8_U8: return "int64_t"; + case ParamOrReturnType.R4: return "float"; + case ParamOrReturnType.R8: return "double"; + case ParamOrReturnType.I16: return "ValueTypeSize16"; + case ParamOrReturnType.STRUCTURE_AS_REF_PARAM: return "uint64_t"; + case ParamOrReturnType.ARM64_HFA_FLOAT_2: return "HtVector2f"; + case ParamOrReturnType.ARM64_HFA_FLOAT_3: return "HtVector3f"; + case ParamOrReturnType.ARM64_HFA_FLOAT_4: return "HtVector4f"; + case ParamOrReturnType.ARM64_HFA_DOUBLE_2: return "HtVector2d"; + case ParamOrReturnType.ARM64_HFA_DOUBLE_3: return "HtVector3d"; + case ParamOrReturnType.ARM64_HFA_DOUBLE_4: return "HtVector4d"; + case ParamOrReturnType.STRUCTURE_ALIGN1: return $"ValueTypeSize<{Size}>"; + case ParamOrReturnType.STRUCTURE_ALIGN2: return $"ValueTypeSizeAlign2<{Size}>"; + case ParamOrReturnType.STRUCTURE_ALIGN4: return $"ValueTypeSizeAlign4<{Size}>"; + case ParamOrReturnType.STRUCTURE_ALIGN8: return $"ValueTypeSizeAlign8<{Size}>"; + default: throw new NotImplementedException(PorType.ToString()); + }; + } + public int GetParamSlotNum() + { + switch (PorType) + { + case ParamOrReturnType.VOID: return 0; + case ParamOrReturnType.I16: return 2; + case ParamOrReturnType.STRUCTURE_AS_REF_PARAM: return 1; + case ParamOrReturnType.ARM64_HFA_FLOAT_3: return 2; + case ParamOrReturnType.ARM64_HFA_FLOAT_4: return 2; + case ParamOrReturnType.ARM64_HFA_DOUBLE_2: return 2; + case ParamOrReturnType.ARM64_HFA_DOUBLE_3: return 3; + case ParamOrReturnType.ARM64_HFA_DOUBLE_4: return 4; + case ParamOrReturnType.ARM64_HVA_8: + case ParamOrReturnType.ARM64_HVA_16: throw new NotSupportedException(); + case ParamOrReturnType.STRUCTURE_ALIGN1: + case ParamOrReturnType.STRUCTURE_ALIGN2: + case ParamOrReturnType.STRUCTURE_ALIGN4: + case ParamOrReturnType.STRUCTURE_ALIGN8: return (Size + 7) / 8; + default: + { + Debug.Assert(PorType < ParamOrReturnType.STRUCT_NOT_PASS_AS_VALUE); + Debug.Assert(Size <= 8); + return 1; + } + } + } + } +} diff --git a/Editor/MethodBridgeGenerator/TypeInfo.cs.meta b/Editor/MethodBridgeGenerator/TypeInfo.cs.meta new file mode 100644 index 0000000..1ba9358 --- /dev/null +++ b/Editor/MethodBridgeGenerator/TypeInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5afd7344483678a4abcb56158fd1442d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/MethodBridgeGenerator/ValueTypeSizeAligmentCalculator.cs b/Editor/MethodBridgeGenerator/ValueTypeSizeAligmentCalculator.cs new file mode 100644 index 0000000..c312b7d --- /dev/null +++ b/Editor/MethodBridgeGenerator/ValueTypeSizeAligmentCalculator.cs @@ -0,0 +1,177 @@ +using dnlib.DotNet; +using HybridCLR.Editor.Meta; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace HybridCLR.Editor.MethodBridgeGenerator +{ + public class ValueTypeSizeAligmentCalculator + { + public ValueTypeSizeAligmentCalculator(bool arch32) + { + _referenceSize = arch32 ? 4 : 8; + } + + private readonly int _referenceSize; + + private static bool IsIgnoreField(FieldDef field) + { + var ignoreAttr = field.CustomAttributes.Where(a => a.AttributeType.FullName == "UnityEngine.Bindings.IgnoreAttribute").FirstOrDefault(); + if (ignoreAttr == null) + { + return false; + } + CANamedArgument arg = ignoreAttr.GetProperty("DoesNotContributeToSize"); + if(arg != null && (bool)arg.Value) + { + //Debug.Log($"IgnoreField.DoesNotContributeToSize = true:{field}"); + return true; + } + return false; + } + + private (int Size, int Aligment) SizeAndAligmentOfStruct(TypeSig type) + { + int totalSize = 0; + int packAligment = 8; + int maxAligment = 1; + + TypeDef typeDef = type.ToTypeDefOrRef().ResolveTypeDefThrow(); + + List klassInst = type.ToGenericInstSig()?.GenericArguments?.ToList(); + GenericArgumentContext ctx = klassInst != null ? new GenericArgumentContext(klassInst, null) : null; + + ClassLayout sa = typeDef.ClassLayout; + if (sa != null && sa.PackingSize > 0) + { + packAligment = sa.PackingSize; + } + bool useSLSize = true; + foreach (FieldDef field in typeDef.Fields) + { + if (field.IsStatic) + { + continue; + } + TypeSig fieldType = ctx != null ? MetaUtil.Inflate(field.FieldType, ctx) : field.FieldType; + var (fs, fa) = SizeAndAligmentOf(fieldType); + fa = Math.Min(fa, packAligment); + if (fa > maxAligment) + { + maxAligment = fa; + } + if (IsIgnoreField(field)) + { + continue; + } + if (sa != null && typeDef.Layout.HasFlag(dnlib.DotNet.TypeAttributes.ExplicitLayout)) + { + int offset = (int)field.FieldOffset.Value; + totalSize = Math.Max(totalSize, offset + fs); + if (offset > sa.ClassSize) + { + useSLSize = false; + } + } + else + { + if (totalSize % fa != 0) + { + totalSize = (totalSize + fa - 1) / fa * fa; + } + totalSize += fs; + if (sa != null && typeDef.Layout.HasFlag(dnlib.DotNet.TypeAttributes.SequentialLayout) && totalSize > sa.ClassSize) + { + useSLSize = false; + } + } + } + if (totalSize == 0) + { + totalSize = maxAligment; + } + if (totalSize % maxAligment != 0) + { + totalSize = (totalSize + maxAligment - 1) / maxAligment * maxAligment; + } + if (sa != null && sa.ClassSize > 0) + { + if (/*sa.Value == LayoutKind.Explicit &&*/ useSLSize) + { + totalSize = (int)sa.ClassSize; + while(totalSize % maxAligment != 0) + { + maxAligment /= 2; + } + } + } + return (totalSize, maxAligment); + } + + public (int Size, int Aligment) SizeAndAligmentOf(TypeSig type) + { + type = type.RemovePinnedAndModifiers(); + if (type.IsByRef || !type.IsValueType || type.IsArray) + return (_referenceSize, _referenceSize); + + switch (type.ElementType) + { + case ElementType.Void: throw new NotSupportedException(type.ToString()); + case ElementType.Boolean: + case ElementType.I1: + case ElementType.U1: return (1, 1); + case ElementType.Char: + case ElementType.I2: + case ElementType.U2: return (2, 2); + case ElementType.I4: + case ElementType.U4: return (4, 4); + case ElementType.I8: + case ElementType.U8: return (8, 8); + case ElementType.R4: return (4, 4); + case ElementType.R8: return (8, 8); + case ElementType.I: + case ElementType.U: + case ElementType.String: + case ElementType.Ptr: + case ElementType.ByRef: + case ElementType.Class: + case ElementType.Array: + case ElementType.SZArray: + case ElementType.FnPtr: + case ElementType.Object: + case ElementType.Module: return (_referenceSize, _referenceSize); + case ElementType.TypedByRef: return SizeAndAligmentOfStruct(type); + case ElementType.ValueType: + { + TypeDef typeDef = type.ToTypeDefOrRef().ResolveTypeDef(); + if (typeDef.IsEnum) + { + return SizeAndAligmentOf(typeDef.GetEnumUnderlyingType()); + } + return SizeAndAligmentOfStruct(type); + } + case ElementType.GenericInst: + { + GenericInstSig gis = (GenericInstSig)type; + if (!gis.GenericType.IsValueType) + { + return (_referenceSize, _referenceSize); + } + TypeDef typeDef = gis.GenericType.ToTypeDefOrRef().ResolveTypeDef(); + if (typeDef.IsEnum) + { + return SizeAndAligmentOf(typeDef.GetEnumUnderlyingType()); + } + return SizeAndAligmentOfStruct(type); + } + default: throw new NotSupportedException(type.ToString()); + } + } + } +} diff --git a/Editor/MethodBridgeGenerator/ValueTypeSizeAligmentCalculator.cs.meta b/Editor/MethodBridgeGenerator/ValueTypeSizeAligmentCalculator.cs.meta new file mode 100644 index 0000000..97cf075 --- /dev/null +++ b/Editor/MethodBridgeGenerator/ValueTypeSizeAligmentCalculator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 49b3dbcebadb1b543a42e01afec07ed1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Resources.meta b/Editor/Resources.meta new file mode 100644 index 0000000..84009a7 --- /dev/null +++ b/Editor/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: defceb25af6d66a4c8d9b0cbf4393a01 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/ReversePInvokeWrap.meta b/Editor/ReversePInvokeWrap.meta new file mode 100644 index 0000000..234a1bc --- /dev/null +++ b/Editor/ReversePInvokeWrap.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 259c1cb7fe681f74eb435ab8f268890d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/ReversePInvokeWrap/ReversePInvokeWrapperGenerator.cs b/Editor/ReversePInvokeWrap/ReversePInvokeWrapperGenerator.cs new file mode 100644 index 0000000..100671c --- /dev/null +++ b/Editor/ReversePInvokeWrap/ReversePInvokeWrapperGenerator.cs @@ -0,0 +1,55 @@ +using HybridCLR.Editor.Template; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HybridCLR.Editor.ReversePInvokeWrap +{ + internal class ReversePInvokeWrapperGenerator + { + public void Generate(string template, int wrapperCount, string outputFile) + { + + var frr = new FileRegionReplace(template); + var codes = new List(); + + codes.Add(@" + void CallLuaFunction(void* xState, int32_t wrapperIndex) + { + const MethodInfo* method = MetadataModule::GetMethodInfoByReversePInvokeWrapperIndex(wrapperIndex); + typedef void (*Callback)(void* xState, const MethodInfo* method); + ((Callback)GetInterpreterDirectlyCallMethodPointer(method))(xState, method); + } +"); + + for(int i = 0; i < wrapperCount; i++) + { + codes.Add($@" + void __ReversePInvokeMethod_{i}(void* xState) + {{ + CallLuaFunction(xState, {i}); + }} +"); + } + + codes.Add(@" + Il2CppMethodPointer s_ReversePInvokeMethodStub[] + { +"); + for(int i = 0; i < wrapperCount; i++) + { + codes.Add($"\t\t(Il2CppMethodPointer)__ReversePInvokeMethod_{i},\n"); + } + + codes.Add(@" + nullptr, + }; +"); + + frr.Replace("REVERSE_PINVOKE_METHOD_STUB", string.Join("", codes)); + frr.Commit(outputFile); + } + } +} diff --git a/Editor/ReversePInvokeWrap/ReversePInvokeWrapperGenerator.cs.meta b/Editor/ReversePInvokeWrap/ReversePInvokeWrapperGenerator.cs.meta new file mode 100644 index 0000000..36a7c55 --- /dev/null +++ b/Editor/ReversePInvokeWrap/ReversePInvokeWrapperGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7d883f182f206fa4db31f4085ce0ecdc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/SettingsUtil.cs b/Editor/SettingsUtil.cs new file mode 100644 index 0000000..9dd47a5 --- /dev/null +++ b/Editor/SettingsUtil.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEditor; +using UnityEditorInternal; +using UnityEngine; + +namespace HybridCLR.Editor +{ + public static class SettingsUtil + { + public static bool Enable => GlobalSettings.enable; + + public static string PackageName { get; } = "com.focus-creative-games.hybridclr_unity"; + + public static string HybridCLRDataPathInPackage => $"Packages/{PackageName}/Data"; + + public static string TemplatePathInPackage => $"{HybridCLRDataPathInPackage}/Templates"; + + public static string ProjectDir { get; } = Directory.GetParent(Application.dataPath).ToString(); + + public static string ScriptingAssembliesJsonFile { get; } = "ScriptingAssemblies.json"; + + public static string GlobalgamemanagersBinFile { get; } = "globalgamemanagers"; + + public static string Dataunity3dBinFile { get; } = "data.unity3d"; + + public static string HotFixDllsOutputDir => $"{HybridCLRDataDir}/{GlobalSettings.hotUpdateDllOutputDir}"; + + public static string HybridCLRDataDir => $"{ProjectDir}/{GlobalSettings.hybridCLRDataDir}"; + + public static string AssembliesPostIl2CppStripDir => $"{HybridCLRDataDir}/{GlobalSettings.strippedAssemblyDir}"; + + public static string LocalUnityDataDir => $"{HybridCLRDataDir}/LocalIl2CppData-{Application.platform}"; + + public static string LocalIl2CppDir => $"{LocalUnityDataDir}/il2cpp"; + + public static string MethodBridgeCppDir => $"{LocalIl2CppDir}/libil2cpp/hybridclr/interpreter"; + + public static string Il2CppBuildCacheDir { get; } = $"{ProjectDir}/Library/Il2cppBuildCache"; + + public static string GetHotFixDllsOutputDirByTarget(BuildTarget target) + { + return $"{HotFixDllsOutputDir}/{target}"; + } + + public static string GetAssembliesPostIl2CppStripDir(BuildTarget target) + { + return $"{AssembliesPostIl2CppStripDir}/{target}"; + } + + class AssemblyDefinitionData + { + public string name; + } + + /// + /// 所有热更新dll列表。放到此列表中的dll在打包时OnFilterAssemblies回调中被过滤。 + /// + public static List HotUpdateAssemblyNames + { + get + { + var gs = GlobalSettings; + var hotfixAssNames = (gs.hotUpdateAssemblyDefinitions ?? Array.Empty()).Select(ad => JsonUtility.FromJson(ad.text)); + + var hotfixAssembles = new List(); + foreach (var assName in hotfixAssNames) + { + hotfixAssembles.Add(assName.name); + } + hotfixAssembles.AddRange(gs.hotUpdateAssemblies ?? Array.Empty()); + return hotfixAssembles.ToList(); + } + } + + public static List HotUpdateAssemblyFiles => HotUpdateAssemblyNames.Select(dll => dll + ".dll").ToList(); + + public static T GetSingletonAssets() where T : ScriptableObject, new() + { + string assetType = typeof(T).Name; + string[] globalAssetPaths = AssetDatabase.FindAssets($"t:{assetType}"); + if (globalAssetPaths == null || globalAssetPaths.Length == 0) + { + string defaultNewAssetPath = $"Assets/{typeof(T).Name}.asset"; + Debug.LogWarning($"没找到 {assetType} asset,自动创建创建一个:{defaultNewAssetPath}."); + + var newAsset = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(newAsset, defaultNewAssetPath); + return newAsset; + } + if (globalAssetPaths.Length > 1) + { + foreach (var assetPath in globalAssetPaths) + { + Debug.LogError($"不能有多个 {assetType}. 路径: {AssetDatabase.GUIDToAssetPath(assetPath)}"); + } + throw new Exception($"不能有多个 {assetType}"); + } + string assPath = AssetDatabase.GUIDToAssetPath(globalAssetPaths[0]); + //Debug.Log($"find asset:{assPath}"); + return AssetDatabase.LoadAssetAtPath(assPath); + } + + public static HybridCLRGlobalSettings GlobalSettings => GetSingletonAssets(); + } +} diff --git a/Editor/SettingsUtil.cs.meta b/Editor/SettingsUtil.cs.meta new file mode 100644 index 0000000..03ce589 --- /dev/null +++ b/Editor/SettingsUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bf7714fc37515834382cd5836b503a9f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Template.meta b/Editor/Template.meta new file mode 100644 index 0000000..f2430d5 --- /dev/null +++ b/Editor/Template.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9af6345cc5ab1ae4a81262ab4b537911 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Template/FileRegionReplace.cs b/Editor/Template/FileRegionReplace.cs new file mode 100644 index 0000000..a547b57 --- /dev/null +++ b/Editor/Template/FileRegionReplace.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HybridCLR.Editor.Template +{ + public class FileRegionReplace + { + private readonly string _tplCode; + + private readonly Dictionary _regionReplaceContents = new Dictionary(); + + public FileRegionReplace(string tplCode) + { + _tplCode = tplCode; + } + + public void Replace(string regionName, string regionContent) + { + _regionReplaceContents.Add(regionName, regionContent); + } + + public string GenFinalString() + { + string originContent = _tplCode; + + string resultContent = originContent; + + foreach (var c in _regionReplaceContents) + { + resultContent = TemplateUtil.ReplaceRegion(resultContent, c.Key, c.Value); + } + return resultContent; + } + + public void Commit(string outputFile) + { + string resultContent = GenFinalString(); + var utf8WithoutBOM = new System.Text.UTF8Encoding(false); + File.WriteAllText(outputFile, resultContent, utf8WithoutBOM); + } + } +} diff --git a/Editor/Template/FileRegionReplace.cs.meta b/Editor/Template/FileRegionReplace.cs.meta new file mode 100644 index 0000000..14a642a --- /dev/null +++ b/Editor/Template/FileRegionReplace.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 15d4563ad83546c42bc65c99be9bd54a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Template/TemplateUtil.cs b/Editor/Template/TemplateUtil.cs new file mode 100644 index 0000000..75e93d3 --- /dev/null +++ b/Editor/Template/TemplateUtil.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HybridCLR.Editor.Template +{ + public static class TemplateUtil + { + + public static string ReplaceRegion(string resultText, string region, string replaceContent) + { + int startIndex = resultText.IndexOf("//!!!{{" + region); + if (startIndex == -1) + { + throw new Exception($"region:{region} start not find"); + } + int endIndex = resultText.IndexOf("//!!!}}" + region); + if (endIndex == -1) + { + throw new Exception($"region:{region} end not find"); + } + int replaceStart = resultText.IndexOf('\n', startIndex); + int replaceEnd = resultText.LastIndexOf('\n', endIndex); + if (replaceStart == -1 || replaceEnd == -1) + { + throw new Exception($"region:{region} not find"); + } + if (resultText.Substring(replaceStart, replaceEnd - replaceStart) == replaceContent) + { + return resultText; + } + resultText = resultText.Substring(0, replaceStart) + "\n" + replaceContent + "\n" + resultText.Substring(replaceEnd); + return resultText; + } + } +} diff --git a/Editor/Template/TemplateUtil.cs.meta b/Editor/Template/TemplateUtil.cs.meta new file mode 100644 index 0000000..1791b45 --- /dev/null +++ b/Editor/Template/TemplateUtil.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 69e748de769c24948afd4f8a9df82b8c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/LICENSE.meta b/LICENSE.meta new file mode 100644 index 0000000..d5478d5 --- /dev/null +++ b/LICENSE.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f09e582706a5776448316f6c584e63a6 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins.meta b/Plugins.meta new file mode 100644 index 0000000..ccdd127 --- /dev/null +++ b/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a26873724919287449e2c9eec68ef1da +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip.meta b/Plugins/7zip.meta new file mode 100644 index 0000000..48fedff --- /dev/null +++ b/Plugins/7zip.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 08f44f6e8bfbc2c45afae7bdd2d7f21f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Common.meta b/Plugins/7zip/Common.meta new file mode 100644 index 0000000..0e4cc9e --- /dev/null +++ b/Plugins/7zip/Common.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5cedddbfa873eb94496b496e2a3ce8aa +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Common/CRC.cs b/Plugins/7zip/Common/CRC.cs new file mode 100644 index 0000000..d03fcec --- /dev/null +++ b/Plugins/7zip/Common/CRC.cs @@ -0,0 +1,55 @@ +// Common/CRC.cs + +namespace SevenZip +{ + public class CRC + { + public static readonly uint[] Table; + + static CRC() + { + Table = new uint[256]; + const uint kPoly = 0xEDB88320; + for (uint i = 0; i < 256; i++) + { + uint r = i; + for (int j = 0; j < 8; j++) + if ((r & 1) != 0) + r = (r >> 1) ^ kPoly; + else + r >>= 1; + Table[i] = r; + } + } + + uint _value = 0xFFFFFFFF; + + public void Init() { _value = 0xFFFFFFFF; } + + public void UpdateByte(byte b) + { + _value = Table[(((byte)(_value)) ^ b)] ^ (_value >> 8); + } + + public void Update(byte[] data, uint offset, uint size) + { + for (uint i = 0; i < size; i++) + _value = Table[(((byte)(_value)) ^ data[offset + i])] ^ (_value >> 8); + } + + public uint GetDigest() { return _value ^ 0xFFFFFFFF; } + + static uint CalculateDigest(byte[] data, uint offset, uint size) + { + CRC crc = new CRC(); + // crc.Init(); + crc.Update(data, offset, size); + return crc.GetDigest(); + } + + static bool VerifyDigest(uint digest, byte[] data, uint offset, uint size) + { + return (CalculateDigest(data, offset, size) == digest); + } + } +} diff --git a/Plugins/7zip/Common/CRC.cs.meta b/Plugins/7zip/Common/CRC.cs.meta new file mode 100644 index 0000000..e366fdf --- /dev/null +++ b/Plugins/7zip/Common/CRC.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d0bb58ecc915d9d47b8567adaa2d0ca6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Common/CommandLineParser.cs b/Plugins/7zip/Common/CommandLineParser.cs new file mode 100644 index 0000000..8eabf59 --- /dev/null +++ b/Plugins/7zip/Common/CommandLineParser.cs @@ -0,0 +1,274 @@ +// CommandLineParser.cs + +using System; +using System.Collections; + +namespace SevenZip.CommandLineParser +{ + public enum SwitchType + { + Simple, + PostMinus, + LimitedPostString, + UnLimitedPostString, + PostChar + } + + public class SwitchForm + { + public string IDString; + public SwitchType Type; + public bool Multi; + public int MinLen; + public int MaxLen; + public string PostCharSet; + + public SwitchForm(string idString, SwitchType type, bool multi, + int minLen, int maxLen, string postCharSet) + { + IDString = idString; + Type = type; + Multi = multi; + MinLen = minLen; + MaxLen = maxLen; + PostCharSet = postCharSet; + } + public SwitchForm(string idString, SwitchType type, bool multi, int minLen): + this(idString, type, multi, minLen, 0, "") + { + } + public SwitchForm(string idString, SwitchType type, bool multi): + this(idString, type, multi, 0) + { + } + } + + public class SwitchResult + { + public bool ThereIs; + public bool WithMinus; + public ArrayList PostStrings = new ArrayList(); + public int PostCharIndex; + public SwitchResult() + { + ThereIs = false; + } + } + + public class Parser + { + public ArrayList NonSwitchStrings = new ArrayList(); + SwitchResult[] _switches; + + public Parser(int numSwitches) + { + _switches = new SwitchResult[numSwitches]; + for (int i = 0; i < numSwitches; i++) + _switches[i] = new SwitchResult(); + } + + bool ParseString(string srcString, SwitchForm[] switchForms) + { + int len = srcString.Length; + if (len == 0) + return false; + int pos = 0; + if (!IsItSwitchChar(srcString[pos])) + return false; + while (pos < len) + { + if (IsItSwitchChar(srcString[pos])) + pos++; + const int kNoLen = -1; + int matchedSwitchIndex = 0; + int maxLen = kNoLen; + for (int switchIndex = 0; switchIndex < _switches.Length; switchIndex++) + { + int switchLen = switchForms[switchIndex].IDString.Length; + if (switchLen <= maxLen || pos + switchLen > len) + continue; + if (String.Compare(switchForms[switchIndex].IDString, 0, + srcString, pos, switchLen, true) == 0) + { + matchedSwitchIndex = switchIndex; + maxLen = switchLen; + } + } + if (maxLen == kNoLen) + throw new Exception("maxLen == kNoLen"); + SwitchResult matchedSwitch = _switches[matchedSwitchIndex]; + SwitchForm switchForm = switchForms[matchedSwitchIndex]; + if ((!switchForm.Multi) && matchedSwitch.ThereIs) + throw new Exception("switch must be single"); + matchedSwitch.ThereIs = true; + pos += maxLen; + int tailSize = len - pos; + SwitchType type = switchForm.Type; + switch (type) + { + case SwitchType.PostMinus: + { + if (tailSize == 0) + matchedSwitch.WithMinus = false; + else + { + matchedSwitch.WithMinus = (srcString[pos] == kSwitchMinus); + if (matchedSwitch.WithMinus) + pos++; + } + break; + } + case SwitchType.PostChar: + { + if (tailSize < switchForm.MinLen) + throw new Exception("switch is not full"); + string charSet = switchForm.PostCharSet; + const int kEmptyCharValue = -1; + if (tailSize == 0) + matchedSwitch.PostCharIndex = kEmptyCharValue; + else + { + int index = charSet.IndexOf(srcString[pos]); + if (index < 0) + matchedSwitch.PostCharIndex = kEmptyCharValue; + else + { + matchedSwitch.PostCharIndex = index; + pos++; + } + } + break; + } + case SwitchType.LimitedPostString: + case SwitchType.UnLimitedPostString: + { + int minLen = switchForm.MinLen; + if (tailSize < minLen) + throw new Exception("switch is not full"); + if (type == SwitchType.UnLimitedPostString) + { + matchedSwitch.PostStrings.Add(srcString.Substring(pos)); + return true; + } + String stringSwitch = srcString.Substring(pos, minLen); + pos += minLen; + for (int i = minLen; i < switchForm.MaxLen && pos < len; i++, pos++) + { + char c = srcString[pos]; + if (IsItSwitchChar(c)) + break; + stringSwitch += c; + } + matchedSwitch.PostStrings.Add(stringSwitch); + break; + } + } + } + return true; + + } + + public void ParseStrings(SwitchForm[] switchForms, string[] commandStrings) + { + int numCommandStrings = commandStrings.Length; + bool stopSwitch = false; + for (int i = 0; i < numCommandStrings; i++) + { + string s = commandStrings[i]; + if (stopSwitch) + NonSwitchStrings.Add(s); + else + if (s == kStopSwitchParsing) + stopSwitch = true; + else + if (!ParseString(s, switchForms)) + NonSwitchStrings.Add(s); + } + } + + public SwitchResult this[int index] { get { return _switches[index]; } } + + public static int ParseCommand(CommandForm[] commandForms, string commandString, + out string postString) + { + for (int i = 0; i < commandForms.Length; i++) + { + string id = commandForms[i].IDString; + if (commandForms[i].PostStringMode) + { + if (commandString.IndexOf(id) == 0) + { + postString = commandString.Substring(id.Length); + return i; + } + } + else + if (commandString == id) + { + postString = ""; + return i; + } + } + postString = ""; + return -1; + } + + static bool ParseSubCharsCommand(int numForms, CommandSubCharsSet[] forms, + string commandString, ArrayList indices) + { + indices.Clear(); + int numUsedChars = 0; + for (int i = 0; i < numForms; i++) + { + CommandSubCharsSet charsSet = forms[i]; + int currentIndex = -1; + int len = charsSet.Chars.Length; + for (int j = 0; j < len; j++) + { + char c = charsSet.Chars[j]; + int newIndex = commandString.IndexOf(c); + if (newIndex >= 0) + { + if (currentIndex >= 0) + return false; + if (commandString.IndexOf(c, newIndex + 1) >= 0) + return false; + currentIndex = j; + numUsedChars++; + } + } + if (currentIndex == -1 && !charsSet.EmptyAllowed) + return false; + indices.Add(currentIndex); + } + return (numUsedChars == commandString.Length); + } + const char kSwitchID1 = '-'; + const char kSwitchID2 = '/'; + + const char kSwitchMinus = '-'; + const string kStopSwitchParsing = "--"; + + static bool IsItSwitchChar(char c) + { + return (c == kSwitchID1 || c == kSwitchID2); + } + } + + public class CommandForm + { + public string IDString = ""; + public bool PostStringMode = false; + public CommandForm(string idString, bool postStringMode) + { + IDString = idString; + PostStringMode = postStringMode; + } + } + + class CommandSubCharsSet + { + public string Chars = ""; + public bool EmptyAllowed = false; + } +} diff --git a/Plugins/7zip/Common/CommandLineParser.cs.meta b/Plugins/7zip/Common/CommandLineParser.cs.meta new file mode 100644 index 0000000..a35c292 --- /dev/null +++ b/Plugins/7zip/Common/CommandLineParser.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dc0b3921c07ef224cb3656391f4317c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Common/InBuffer.cs b/Plugins/7zip/Common/InBuffer.cs new file mode 100644 index 0000000..7c51f0b --- /dev/null +++ b/Plugins/7zip/Common/InBuffer.cs @@ -0,0 +1,72 @@ +// InBuffer.cs + +namespace SevenZip.Buffer +{ + public class InBuffer + { + byte[] m_Buffer; + uint m_Pos; + uint m_Limit; + uint m_BufferSize; + System.IO.Stream m_Stream; + bool m_StreamWasExhausted; + ulong m_ProcessedSize; + + public InBuffer(uint bufferSize) + { + m_Buffer = new byte[bufferSize]; + m_BufferSize = bufferSize; + } + + public void Init(System.IO.Stream stream) + { + m_Stream = stream; + m_ProcessedSize = 0; + m_Limit = 0; + m_Pos = 0; + m_StreamWasExhausted = false; + } + + public bool ReadBlock() + { + if (m_StreamWasExhausted) + return false; + m_ProcessedSize += m_Pos; + int aNumProcessedBytes = m_Stream.Read(m_Buffer, 0, (int)m_BufferSize); + m_Pos = 0; + m_Limit = (uint)aNumProcessedBytes; + m_StreamWasExhausted = (aNumProcessedBytes == 0); + return (!m_StreamWasExhausted); + } + + + public void ReleaseStream() + { + // m_Stream.Close(); + m_Stream = null; + } + + public bool ReadByte(byte b) // check it + { + if (m_Pos >= m_Limit) + if (!ReadBlock()) + return false; + b = m_Buffer[m_Pos++]; + return true; + } + + public byte ReadByte() + { + // return (byte)m_Stream.ReadByte(); + if (m_Pos >= m_Limit) + if (!ReadBlock()) + return 0xFF; + return m_Buffer[m_Pos++]; + } + + public ulong GetProcessedSize() + { + return m_ProcessedSize + m_Pos; + } + } +} diff --git a/Plugins/7zip/Common/InBuffer.cs.meta b/Plugins/7zip/Common/InBuffer.cs.meta new file mode 100644 index 0000000..843e5cf --- /dev/null +++ b/Plugins/7zip/Common/InBuffer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6ea025f5b275726479ba55e956574898 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Common/OutBuffer.cs b/Plugins/7zip/Common/OutBuffer.cs new file mode 100644 index 0000000..2da16e1 --- /dev/null +++ b/Plugins/7zip/Common/OutBuffer.cs @@ -0,0 +1,47 @@ +// OutBuffer.cs + +namespace SevenZip.Buffer +{ + public class OutBuffer + { + byte[] m_Buffer; + uint m_Pos; + uint m_BufferSize; + System.IO.Stream m_Stream; + ulong m_ProcessedSize; + + public OutBuffer(uint bufferSize) + { + m_Buffer = new byte[bufferSize]; + m_BufferSize = bufferSize; + } + + public void SetStream(System.IO.Stream stream) { m_Stream = stream; } + public void FlushStream() { m_Stream.Flush(); } + public void CloseStream() { m_Stream.Close(); } + public void ReleaseStream() { m_Stream = null; } + + public void Init() + { + m_ProcessedSize = 0; + m_Pos = 0; + } + + public void WriteByte(byte b) + { + m_Buffer[m_Pos++] = b; + if (m_Pos >= m_BufferSize) + FlushData(); + } + + public void FlushData() + { + if (m_Pos == 0) + return; + m_Stream.Write(m_Buffer, 0, (int)m_Pos); + m_Pos = 0; + } + + public ulong GetProcessedSize() { return m_ProcessedSize + m_Pos; } + } +} diff --git a/Plugins/7zip/Common/OutBuffer.cs.meta b/Plugins/7zip/Common/OutBuffer.cs.meta new file mode 100644 index 0000000..38af8c8 --- /dev/null +++ b/Plugins/7zip/Common/OutBuffer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5d61847c152e7384ab15aea89a942f54 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Compress.meta b/Plugins/7zip/Compress.meta new file mode 100644 index 0000000..b67e6ba --- /dev/null +++ b/Plugins/7zip/Compress.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4b6765bf0d03fd243b47d74c9b73c38b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Compress/LZ.meta b/Plugins/7zip/Compress/LZ.meta new file mode 100644 index 0000000..c303414 --- /dev/null +++ b/Plugins/7zip/Compress/LZ.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a83572c42056f474db56e761a4f35e38 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Compress/LZ/IMatchFinder.cs b/Plugins/7zip/Compress/LZ/IMatchFinder.cs new file mode 100644 index 0000000..10ca2b3 --- /dev/null +++ b/Plugins/7zip/Compress/LZ/IMatchFinder.cs @@ -0,0 +1,24 @@ +// IMatchFinder.cs + +using System; + +namespace SevenZip.Compression.LZ +{ + interface IInWindowStream + { + void SetStream(System.IO.Stream inStream); + void Init(); + void ReleaseStream(); + Byte GetIndexByte(Int32 index); + UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit); + UInt32 GetNumAvailableBytes(); + } + + interface IMatchFinder : IInWindowStream + { + void Create(UInt32 historySize, UInt32 keepAddBufferBefore, + UInt32 matchMaxLen, UInt32 keepAddBufferAfter); + UInt32 GetMatches(UInt32[] distances); + void Skip(UInt32 num); + } +} diff --git a/Plugins/7zip/Compress/LZ/IMatchFinder.cs.meta b/Plugins/7zip/Compress/LZ/IMatchFinder.cs.meta new file mode 100644 index 0000000..88c6d3d --- /dev/null +++ b/Plugins/7zip/Compress/LZ/IMatchFinder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bfd1ee430d8108042a2607d92d7f58ad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Compress/LZ/LzBinTree.cs b/Plugins/7zip/Compress/LZ/LzBinTree.cs new file mode 100644 index 0000000..c1c006b --- /dev/null +++ b/Plugins/7zip/Compress/LZ/LzBinTree.cs @@ -0,0 +1,367 @@ +// LzBinTree.cs + +using System; + +namespace SevenZip.Compression.LZ +{ + public class BinTree : InWindow, IMatchFinder + { + UInt32 _cyclicBufferPos; + UInt32 _cyclicBufferSize = 0; + UInt32 _matchMaxLen; + + UInt32[] _son; + UInt32[] _hash; + + UInt32 _cutValue = 0xFF; + UInt32 _hashMask; + UInt32 _hashSizeSum = 0; + + bool HASH_ARRAY = true; + + const UInt32 kHash2Size = 1 << 10; + const UInt32 kHash3Size = 1 << 16; + const UInt32 kBT2HashSize = 1 << 16; + const UInt32 kStartMaxLen = 1; + const UInt32 kHash3Offset = kHash2Size; + const UInt32 kEmptyHashValue = 0; + const UInt32 kMaxValForNormalize = ((UInt32)1 << 31) - 1; + + UInt32 kNumHashDirectBytes = 0; + UInt32 kMinMatchCheck = 4; + UInt32 kFixHashSize = kHash2Size + kHash3Size; + + public void SetType(int numHashBytes) + { + HASH_ARRAY = (numHashBytes > 2); + if (HASH_ARRAY) + { + kNumHashDirectBytes = 0; + kMinMatchCheck = 4; + kFixHashSize = kHash2Size + kHash3Size; + } + else + { + kNumHashDirectBytes = 2; + kMinMatchCheck = 2 + 1; + kFixHashSize = 0; + } + } + + public new void SetStream(System.IO.Stream stream) { base.SetStream(stream); } + public new void ReleaseStream() { base.ReleaseStream(); } + + public new void Init() + { + base.Init(); + for (UInt32 i = 0; i < _hashSizeSum; i++) + _hash[i] = kEmptyHashValue; + _cyclicBufferPos = 0; + ReduceOffsets(-1); + } + + public new void MovePos() + { + if (++_cyclicBufferPos >= _cyclicBufferSize) + _cyclicBufferPos = 0; + base.MovePos(); + if (_pos == kMaxValForNormalize) + Normalize(); + } + + public new Byte GetIndexByte(Int32 index) { return base.GetIndexByte(index); } + + public new UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit) + { return base.GetMatchLen(index, distance, limit); } + + public new UInt32 GetNumAvailableBytes() { return base.GetNumAvailableBytes(); } + + public void Create(UInt32 historySize, UInt32 keepAddBufferBefore, + UInt32 matchMaxLen, UInt32 keepAddBufferAfter) + { + if (historySize > kMaxValForNormalize - 256) + throw new Exception(); + _cutValue = 16 + (matchMaxLen >> 1); + + UInt32 windowReservSize = (historySize + keepAddBufferBefore + + matchMaxLen + keepAddBufferAfter) / 2 + 256; + + base.Create(historySize + keepAddBufferBefore, matchMaxLen + keepAddBufferAfter, windowReservSize); + + _matchMaxLen = matchMaxLen; + + UInt32 cyclicBufferSize = historySize + 1; + if (_cyclicBufferSize != cyclicBufferSize) + _son = new UInt32[(_cyclicBufferSize = cyclicBufferSize) * 2]; + + UInt32 hs = kBT2HashSize; + + if (HASH_ARRAY) + { + hs = historySize - 1; + hs |= (hs >> 1); + hs |= (hs >> 2); + hs |= (hs >> 4); + hs |= (hs >> 8); + hs >>= 1; + hs |= 0xFFFF; + if (hs > (1 << 24)) + hs >>= 1; + _hashMask = hs; + hs++; + hs += kFixHashSize; + } + if (hs != _hashSizeSum) + _hash = new UInt32[_hashSizeSum = hs]; + } + + public UInt32 GetMatches(UInt32[] distances) + { + UInt32 lenLimit; + if (_pos + _matchMaxLen <= _streamPos) + lenLimit = _matchMaxLen; + else + { + lenLimit = _streamPos - _pos; + if (lenLimit < kMinMatchCheck) + { + MovePos(); + return 0; + } + } + + UInt32 offset = 0; + UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0; + UInt32 cur = _bufferOffset + _pos; + UInt32 maxLen = kStartMaxLen; // to avoid items for len < hashSize; + UInt32 hashValue, hash2Value = 0, hash3Value = 0; + + if (HASH_ARRAY) + { + UInt32 temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1]; + hash2Value = temp & (kHash2Size - 1); + temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8); + hash3Value = temp & (kHash3Size - 1); + hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask; + } + else + hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8); + + UInt32 curMatch = _hash[kFixHashSize + hashValue]; + if (HASH_ARRAY) + { + UInt32 curMatch2 = _hash[hash2Value]; + UInt32 curMatch3 = _hash[kHash3Offset + hash3Value]; + _hash[hash2Value] = _pos; + _hash[kHash3Offset + hash3Value] = _pos; + if (curMatch2 > matchMinPos) + if (_bufferBase[_bufferOffset + curMatch2] == _bufferBase[cur]) + { + distances[offset++] = maxLen = 2; + distances[offset++] = _pos - curMatch2 - 1; + } + if (curMatch3 > matchMinPos) + if (_bufferBase[_bufferOffset + curMatch3] == _bufferBase[cur]) + { + if (curMatch3 == curMatch2) + offset -= 2; + distances[offset++] = maxLen = 3; + distances[offset++] = _pos - curMatch3 - 1; + curMatch2 = curMatch3; + } + if (offset != 0 && curMatch2 == curMatch) + { + offset -= 2; + maxLen = kStartMaxLen; + } + } + + _hash[kFixHashSize + hashValue] = _pos; + + UInt32 ptr0 = (_cyclicBufferPos << 1) + 1; + UInt32 ptr1 = (_cyclicBufferPos << 1); + + UInt32 len0, len1; + len0 = len1 = kNumHashDirectBytes; + + if (kNumHashDirectBytes != 0) + { + if (curMatch > matchMinPos) + { + if (_bufferBase[_bufferOffset + curMatch + kNumHashDirectBytes] != + _bufferBase[cur + kNumHashDirectBytes]) + { + distances[offset++] = maxLen = kNumHashDirectBytes; + distances[offset++] = _pos - curMatch - 1; + } + } + } + + UInt32 count = _cutValue; + + while(true) + { + if(curMatch <= matchMinPos || count-- == 0) + { + _son[ptr0] = _son[ptr1] = kEmptyHashValue; + break; + } + UInt32 delta = _pos - curMatch; + UInt32 cyclicPos = ((delta <= _cyclicBufferPos) ? + (_cyclicBufferPos - delta) : + (_cyclicBufferPos - delta + _cyclicBufferSize)) << 1; + + UInt32 pby1 = _bufferOffset + curMatch; + UInt32 len = Math.Min(len0, len1); + if (_bufferBase[pby1 + len] == _bufferBase[cur + len]) + { + while(++len != lenLimit) + if (_bufferBase[pby1 + len] != _bufferBase[cur + len]) + break; + if (maxLen < len) + { + distances[offset++] = maxLen = len; + distances[offset++] = delta - 1; + if (len == lenLimit) + { + _son[ptr1] = _son[cyclicPos]; + _son[ptr0] = _son[cyclicPos + 1]; + break; + } + } + } + if (_bufferBase[pby1 + len] < _bufferBase[cur + len]) + { + _son[ptr1] = curMatch; + ptr1 = cyclicPos + 1; + curMatch = _son[ptr1]; + len1 = len; + } + else + { + _son[ptr0] = curMatch; + ptr0 = cyclicPos; + curMatch = _son[ptr0]; + len0 = len; + } + } + MovePos(); + return offset; + } + + public void Skip(UInt32 num) + { + do + { + UInt32 lenLimit; + if (_pos + _matchMaxLen <= _streamPos) + lenLimit = _matchMaxLen; + else + { + lenLimit = _streamPos - _pos; + if (lenLimit < kMinMatchCheck) + { + MovePos(); + continue; + } + } + + UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0; + UInt32 cur = _bufferOffset + _pos; + + UInt32 hashValue; + + if (HASH_ARRAY) + { + UInt32 temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1]; + UInt32 hash2Value = temp & (kHash2Size - 1); + _hash[hash2Value] = _pos; + temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8); + UInt32 hash3Value = temp & (kHash3Size - 1); + _hash[kHash3Offset + hash3Value] = _pos; + hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask; + } + else + hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8); + + UInt32 curMatch = _hash[kFixHashSize + hashValue]; + _hash[kFixHashSize + hashValue] = _pos; + + UInt32 ptr0 = (_cyclicBufferPos << 1) + 1; + UInt32 ptr1 = (_cyclicBufferPos << 1); + + UInt32 len0, len1; + len0 = len1 = kNumHashDirectBytes; + + UInt32 count = _cutValue; + while (true) + { + if (curMatch <= matchMinPos || count-- == 0) + { + _son[ptr0] = _son[ptr1] = kEmptyHashValue; + break; + } + + UInt32 delta = _pos - curMatch; + UInt32 cyclicPos = ((delta <= _cyclicBufferPos) ? + (_cyclicBufferPos - delta) : + (_cyclicBufferPos - delta + _cyclicBufferSize)) << 1; + + UInt32 pby1 = _bufferOffset + curMatch; + UInt32 len = Math.Min(len0, len1); + if (_bufferBase[pby1 + len] == _bufferBase[cur + len]) + { + while (++len != lenLimit) + if (_bufferBase[pby1 + len] != _bufferBase[cur + len]) + break; + if (len == lenLimit) + { + _son[ptr1] = _son[cyclicPos]; + _son[ptr0] = _son[cyclicPos + 1]; + break; + } + } + if (_bufferBase[pby1 + len] < _bufferBase[cur + len]) + { + _son[ptr1] = curMatch; + ptr1 = cyclicPos + 1; + curMatch = _son[ptr1]; + len1 = len; + } + else + { + _son[ptr0] = curMatch; + ptr0 = cyclicPos; + curMatch = _son[ptr0]; + len0 = len; + } + } + MovePos(); + } + while (--num != 0); + } + + void NormalizeLinks(UInt32[] items, UInt32 numItems, UInt32 subValue) + { + for (UInt32 i = 0; i < numItems; i++) + { + UInt32 value = items[i]; + if (value <= subValue) + value = kEmptyHashValue; + else + value -= subValue; + items[i] = value; + } + } + + void Normalize() + { + UInt32 subValue = _pos - _cyclicBufferSize; + NormalizeLinks(_son, _cyclicBufferSize * 2, subValue); + NormalizeLinks(_hash, _hashSizeSum, subValue); + ReduceOffsets((Int32)subValue); + } + + public void SetCutValue(UInt32 cutValue) { _cutValue = cutValue; } + } +} diff --git a/Plugins/7zip/Compress/LZ/LzBinTree.cs.meta b/Plugins/7zip/Compress/LZ/LzBinTree.cs.meta new file mode 100644 index 0000000..49637e8 --- /dev/null +++ b/Plugins/7zip/Compress/LZ/LzBinTree.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3ba8015d2d5df3a459615ffc06635e6d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Compress/LZ/LzInWindow.cs b/Plugins/7zip/Compress/LZ/LzInWindow.cs new file mode 100644 index 0000000..52d23ce --- /dev/null +++ b/Plugins/7zip/Compress/LZ/LzInWindow.cs @@ -0,0 +1,132 @@ +// LzInWindow.cs + +using System; + +namespace SevenZip.Compression.LZ +{ + public class InWindow + { + public Byte[] _bufferBase = null; // pointer to buffer with data + System.IO.Stream _stream; + UInt32 _posLimit; // offset (from _buffer) of first byte when new block reading must be done + bool _streamEndWasReached; // if (true) then _streamPos shows real end of stream + + UInt32 _pointerToLastSafePosition; + + public UInt32 _bufferOffset; + + public UInt32 _blockSize; // Size of Allocated memory block + public UInt32 _pos; // offset (from _buffer) of curent byte + UInt32 _keepSizeBefore; // how many BYTEs must be kept in buffer before _pos + UInt32 _keepSizeAfter; // how many BYTEs must be kept buffer after _pos + public UInt32 _streamPos; // offset (from _buffer) of first not read byte from Stream + + public void MoveBlock() + { + UInt32 offset = (UInt32)(_bufferOffset) + _pos - _keepSizeBefore; + // we need one additional byte, since MovePos moves on 1 byte. + if (offset > 0) + offset--; + + UInt32 numBytes = (UInt32)(_bufferOffset) + _streamPos - offset; + + // check negative offset ???? + for (UInt32 i = 0; i < numBytes; i++) + _bufferBase[i] = _bufferBase[offset + i]; + _bufferOffset -= offset; + } + + public virtual void ReadBlock() + { + if (_streamEndWasReached) + return; + while (true) + { + int size = (int)((0 - _bufferOffset) + _blockSize - _streamPos); + if (size == 0) + return; + int numReadBytes = _stream.Read(_bufferBase, (int)(_bufferOffset + _streamPos), size); + if (numReadBytes == 0) + { + _posLimit = _streamPos; + UInt32 pointerToPostion = _bufferOffset + _posLimit; + if (pointerToPostion > _pointerToLastSafePosition) + _posLimit = (UInt32)(_pointerToLastSafePosition - _bufferOffset); + + _streamEndWasReached = true; + return; + } + _streamPos += (UInt32)numReadBytes; + if (_streamPos >= _pos + _keepSizeAfter) + _posLimit = _streamPos - _keepSizeAfter; + } + } + + void Free() { _bufferBase = null; } + + public void Create(UInt32 keepSizeBefore, UInt32 keepSizeAfter, UInt32 keepSizeReserv) + { + _keepSizeBefore = keepSizeBefore; + _keepSizeAfter = keepSizeAfter; + UInt32 blockSize = keepSizeBefore + keepSizeAfter + keepSizeReserv; + if (_bufferBase == null || _blockSize != blockSize) + { + Free(); + _blockSize = blockSize; + _bufferBase = new Byte[_blockSize]; + } + _pointerToLastSafePosition = _blockSize - keepSizeAfter; + } + + public void SetStream(System.IO.Stream stream) { _stream = stream; } + public void ReleaseStream() { _stream = null; } + + public void Init() + { + _bufferOffset = 0; + _pos = 0; + _streamPos = 0; + _streamEndWasReached = false; + ReadBlock(); + } + + public void MovePos() + { + _pos++; + if (_pos > _posLimit) + { + UInt32 pointerToPostion = _bufferOffset + _pos; + if (pointerToPostion > _pointerToLastSafePosition) + MoveBlock(); + ReadBlock(); + } + } + + public Byte GetIndexByte(Int32 index) { return _bufferBase[_bufferOffset + _pos + index]; } + + // index + limit have not to exceed _keepSizeAfter; + public UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit) + { + if (_streamEndWasReached) + if ((_pos + index) + limit > _streamPos) + limit = _streamPos - (UInt32)(_pos + index); + distance++; + // Byte *pby = _buffer + (size_t)_pos + index; + UInt32 pby = _bufferOffset + _pos + (UInt32)index; + + UInt32 i; + for (i = 0; i < limit && _bufferBase[pby + i] == _bufferBase[pby + i - distance]; i++); + return i; + } + + public UInt32 GetNumAvailableBytes() { return _streamPos - _pos; } + + public void ReduceOffsets(Int32 subValue) + { + _bufferOffset += (UInt32)subValue; + _posLimit -= (UInt32)subValue; + _pos -= (UInt32)subValue; + _streamPos -= (UInt32)subValue; + } + } +} diff --git a/Plugins/7zip/Compress/LZ/LzInWindow.cs.meta b/Plugins/7zip/Compress/LZ/LzInWindow.cs.meta new file mode 100644 index 0000000..f1e45d6 --- /dev/null +++ b/Plugins/7zip/Compress/LZ/LzInWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 333112c3785a6ec4cbbfec8515081cac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Compress/LZ/LzOutWindow.cs b/Plugins/7zip/Compress/LZ/LzOutWindow.cs new file mode 100644 index 0000000..c998584 --- /dev/null +++ b/Plugins/7zip/Compress/LZ/LzOutWindow.cs @@ -0,0 +1,110 @@ +// LzOutWindow.cs + +namespace SevenZip.Compression.LZ +{ + public class OutWindow + { + byte[] _buffer = null; + uint _pos; + uint _windowSize = 0; + uint _streamPos; + System.IO.Stream _stream; + + public uint TrainSize = 0; + + public void Create(uint windowSize) + { + if (_windowSize != windowSize) + { + // System.GC.Collect(); + _buffer = new byte[windowSize]; + } + _windowSize = windowSize; + _pos = 0; + _streamPos = 0; + } + + public void Init(System.IO.Stream stream, bool solid) + { + ReleaseStream(); + _stream = stream; + if (!solid) + { + _streamPos = 0; + _pos = 0; + TrainSize = 0; + } + } + + public bool Train(System.IO.Stream stream) + { + long len = stream.Length; + uint size = (len < _windowSize) ? (uint)len : _windowSize; + TrainSize = size; + stream.Position = len - size; + _streamPos = _pos = 0; + while (size > 0) + { + uint curSize = _windowSize - _pos; + if (size < curSize) + curSize = size; + int numReadBytes = stream.Read(_buffer, (int)_pos, (int)curSize); + if (numReadBytes == 0) + return false; + size -= (uint)numReadBytes; + _pos += (uint)numReadBytes; + _streamPos += (uint)numReadBytes; + if (_pos == _windowSize) + _streamPos = _pos = 0; + } + return true; + } + + public void ReleaseStream() + { + Flush(); + _stream = null; + } + + public void Flush() + { + uint size = _pos - _streamPos; + if (size == 0) + return; + _stream.Write(_buffer, (int)_streamPos, (int)size); + if (_pos >= _windowSize) + _pos = 0; + _streamPos = _pos; + } + + public void CopyBlock(uint distance, uint len) + { + uint pos = _pos - distance - 1; + if (pos >= _windowSize) + pos += _windowSize; + for (; len > 0; len--) + { + if (pos >= _windowSize) + pos = 0; + _buffer[_pos++] = _buffer[pos++]; + if (_pos >= _windowSize) + Flush(); + } + } + + public void PutByte(byte b) + { + _buffer[_pos++] = b; + if (_pos >= _windowSize) + Flush(); + } + + public byte GetByte(uint distance) + { + uint pos = _pos - distance - 1; + if (pos >= _windowSize) + pos += _windowSize; + return _buffer[pos]; + } + } +} diff --git a/Plugins/7zip/Compress/LZ/LzOutWindow.cs.meta b/Plugins/7zip/Compress/LZ/LzOutWindow.cs.meta new file mode 100644 index 0000000..7b3f534 --- /dev/null +++ b/Plugins/7zip/Compress/LZ/LzOutWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb296a6e14ab7c44cb6388693d581a62 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Compress/LZMA.meta b/Plugins/7zip/Compress/LZMA.meta new file mode 100644 index 0000000..5c45968 --- /dev/null +++ b/Plugins/7zip/Compress/LZMA.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5fcd2f5c8b724164a8a01cee73896d1a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Compress/LZMA/LzmaBase.cs b/Plugins/7zip/Compress/LZMA/LzmaBase.cs new file mode 100644 index 0000000..c7bca86 --- /dev/null +++ b/Plugins/7zip/Compress/LZMA/LzmaBase.cs @@ -0,0 +1,76 @@ +// LzmaBase.cs + +namespace SevenZip.Compression.LZMA +{ + internal abstract class Base + { + public const uint kNumRepDistances = 4; + public const uint kNumStates = 12; + + // static byte []kLiteralNextStates = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; + // static byte []kMatchNextStates = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; + // static byte []kRepNextStates = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; + // static byte []kShortRepNextStates = {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; + + public struct State + { + public uint Index; + public void Init() { Index = 0; } + public void UpdateChar() + { + if (Index < 4) Index = 0; + else if (Index < 10) Index -= 3; + else Index -= 6; + } + public void UpdateMatch() { Index = (uint)(Index < 7 ? 7 : 10); } + public void UpdateRep() { Index = (uint)(Index < 7 ? 8 : 11); } + public void UpdateShortRep() { Index = (uint)(Index < 7 ? 9 : 11); } + public bool IsCharState() { return Index < 7; } + } + + public const int kNumPosSlotBits = 6; + public const int kDicLogSizeMin = 0; + // public const int kDicLogSizeMax = 30; + // public const uint kDistTableSizeMax = kDicLogSizeMax * 2; + + public const int kNumLenToPosStatesBits = 2; // it's for speed optimization + public const uint kNumLenToPosStates = 1 << kNumLenToPosStatesBits; + + public const uint kMatchMinLen = 2; + + public static uint GetLenToPosState(uint len) + { + len -= kMatchMinLen; + if (len < kNumLenToPosStates) + return len; + return (uint)(kNumLenToPosStates - 1); + } + + public const int kNumAlignBits = 4; + public const uint kAlignTableSize = 1 << kNumAlignBits; + public const uint kAlignMask = (kAlignTableSize - 1); + + public const uint kStartPosModelIndex = 4; + public const uint kEndPosModelIndex = 14; + public const uint kNumPosModels = kEndPosModelIndex - kStartPosModelIndex; + + public const uint kNumFullDistances = 1 << ((int)kEndPosModelIndex / 2); + + public const uint kNumLitPosStatesBitsEncodingMax = 4; + public const uint kNumLitContextBitsMax = 8; + + public const int kNumPosStatesBitsMax = 4; + public const uint kNumPosStatesMax = (1 << kNumPosStatesBitsMax); + public const int kNumPosStatesBitsEncodingMax = 4; + public const uint kNumPosStatesEncodingMax = (1 << kNumPosStatesBitsEncodingMax); + + public const int kNumLowLenBits = 3; + public const int kNumMidLenBits = 3; + public const int kNumHighLenBits = 8; + public const uint kNumLowLenSymbols = 1 << kNumLowLenBits; + public const uint kNumMidLenSymbols = 1 << kNumMidLenBits; + public const uint kNumLenSymbols = kNumLowLenSymbols + kNumMidLenSymbols + + (1 << kNumHighLenBits); + public const uint kMatchMaxLen = kMatchMinLen + kNumLenSymbols - 1; + } +} diff --git a/Plugins/7zip/Compress/LZMA/LzmaBase.cs.meta b/Plugins/7zip/Compress/LZMA/LzmaBase.cs.meta new file mode 100644 index 0000000..10b16ed --- /dev/null +++ b/Plugins/7zip/Compress/LZMA/LzmaBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 62f365191077342479f8227edadf57e3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Compress/LZMA/LzmaDecoder.cs b/Plugins/7zip/Compress/LZMA/LzmaDecoder.cs new file mode 100644 index 0000000..a9be39f --- /dev/null +++ b/Plugins/7zip/Compress/LZMA/LzmaDecoder.cs @@ -0,0 +1,398 @@ +// LzmaDecoder.cs + +using System; + +namespace SevenZip.Compression.LZMA +{ + using RangeCoder; + + public class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream + { + class LenDecoder + { + BitDecoder m_Choice = new BitDecoder(); + BitDecoder m_Choice2 = new BitDecoder(); + BitTreeDecoder[] m_LowCoder = new BitTreeDecoder[Base.kNumPosStatesMax]; + BitTreeDecoder[] m_MidCoder = new BitTreeDecoder[Base.kNumPosStatesMax]; + BitTreeDecoder m_HighCoder = new BitTreeDecoder(Base.kNumHighLenBits); + uint m_NumPosStates = 0; + + public void Create(uint numPosStates) + { + for (uint posState = m_NumPosStates; posState < numPosStates; posState++) + { + m_LowCoder[posState] = new BitTreeDecoder(Base.kNumLowLenBits); + m_MidCoder[posState] = new BitTreeDecoder(Base.kNumMidLenBits); + } + m_NumPosStates = numPosStates; + } + + public void Init() + { + m_Choice.Init(); + for (uint posState = 0; posState < m_NumPosStates; posState++) + { + m_LowCoder[posState].Init(); + m_MidCoder[posState].Init(); + } + m_Choice2.Init(); + m_HighCoder.Init(); + } + + public uint Decode(RangeCoder.Decoder rangeDecoder, uint posState) + { + if (m_Choice.Decode(rangeDecoder) == 0) + return m_LowCoder[posState].Decode(rangeDecoder); + else + { + uint symbol = Base.kNumLowLenSymbols; + if (m_Choice2.Decode(rangeDecoder) == 0) + symbol += m_MidCoder[posState].Decode(rangeDecoder); + else + { + symbol += Base.kNumMidLenSymbols; + symbol += m_HighCoder.Decode(rangeDecoder); + } + return symbol; + } + } + } + + class LiteralDecoder + { + struct Decoder2 + { + BitDecoder[] m_Decoders; + public void Create() { m_Decoders = new BitDecoder[0x300]; } + public void Init() { for (int i = 0; i < 0x300; i++) m_Decoders[i].Init(); } + + public byte DecodeNormal(RangeCoder.Decoder rangeDecoder) + { + uint symbol = 1; + do + symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder); + while (symbol < 0x100); + return (byte)symbol; + } + + public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, byte matchByte) + { + uint symbol = 1; + do + { + uint matchBit = (uint)(matchByte >> 7) & 1; + matchByte <<= 1; + uint bit = m_Decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder); + symbol = (symbol << 1) | bit; + if (matchBit != bit) + { + while (symbol < 0x100) + symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder); + break; + } + } + while (symbol < 0x100); + return (byte)symbol; + } + } + + Decoder2[] m_Coders; + int m_NumPrevBits; + int m_NumPosBits; + uint m_PosMask; + + public void Create(int numPosBits, int numPrevBits) + { + if (m_Coders != null && m_NumPrevBits == numPrevBits && + m_NumPosBits == numPosBits) + return; + m_NumPosBits = numPosBits; + m_PosMask = ((uint)1 << numPosBits) - 1; + m_NumPrevBits = numPrevBits; + uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); + m_Coders = new Decoder2[numStates]; + for (uint i = 0; i < numStates; i++) + m_Coders[i].Create(); + } + + public void Init() + { + uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); + for (uint i = 0; i < numStates; i++) + m_Coders[i].Init(); + } + + uint GetState(uint pos, byte prevByte) + { return ((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> (8 - m_NumPrevBits)); } + + public byte DecodeNormal(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte) + { return m_Coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder); } + + public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte, byte matchByte) + { return m_Coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte); } + }; + + LZ.OutWindow m_OutWindow = new LZ.OutWindow(); + RangeCoder.Decoder m_RangeDecoder = new RangeCoder.Decoder(); + + BitDecoder[] m_IsMatchDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + BitDecoder[] m_IsRepDecoders = new BitDecoder[Base.kNumStates]; + BitDecoder[] m_IsRepG0Decoders = new BitDecoder[Base.kNumStates]; + BitDecoder[] m_IsRepG1Decoders = new BitDecoder[Base.kNumStates]; + BitDecoder[] m_IsRepG2Decoders = new BitDecoder[Base.kNumStates]; + BitDecoder[] m_IsRep0LongDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + + BitTreeDecoder[] m_PosSlotDecoder = new BitTreeDecoder[Base.kNumLenToPosStates]; + BitDecoder[] m_PosDecoders = new BitDecoder[Base.kNumFullDistances - Base.kEndPosModelIndex]; + + BitTreeDecoder m_PosAlignDecoder = new BitTreeDecoder(Base.kNumAlignBits); + + LenDecoder m_LenDecoder = new LenDecoder(); + LenDecoder m_RepLenDecoder = new LenDecoder(); + + LiteralDecoder m_LiteralDecoder = new LiteralDecoder(); + + uint m_DictionarySize; + uint m_DictionarySizeCheck; + + uint m_PosStateMask; + + public Decoder() + { + m_DictionarySize = 0xFFFFFFFF; + for (int i = 0; i < Base.kNumLenToPosStates; i++) + m_PosSlotDecoder[i] = new BitTreeDecoder(Base.kNumPosSlotBits); + } + + void SetDictionarySize(uint dictionarySize) + { + if (m_DictionarySize != dictionarySize) + { + m_DictionarySize = dictionarySize; + m_DictionarySizeCheck = Math.Max(m_DictionarySize, 1); + uint blockSize = Math.Max(m_DictionarySizeCheck, (1 << 12)); + m_OutWindow.Create(blockSize); + } + } + + void SetLiteralProperties(int lp, int lc) + { + if (lp > 8) + throw new InvalidParamException(); + if (lc > 8) + throw new InvalidParamException(); + m_LiteralDecoder.Create(lp, lc); + } + + void SetPosBitsProperties(int pb) + { + if (pb > Base.kNumPosStatesBitsMax) + throw new InvalidParamException(); + uint numPosStates = (uint)1 << pb; + m_LenDecoder.Create(numPosStates); + m_RepLenDecoder.Create(numPosStates); + m_PosStateMask = numPosStates - 1; + } + + bool _solid = false; + void Init(System.IO.Stream inStream, System.IO.Stream outStream) + { + m_RangeDecoder.Init(inStream); + m_OutWindow.Init(outStream, _solid); + + uint i; + for (i = 0; i < Base.kNumStates; i++) + { + for (uint j = 0; j <= m_PosStateMask; j++) + { + uint index = (i << Base.kNumPosStatesBitsMax) + j; + m_IsMatchDecoders[index].Init(); + m_IsRep0LongDecoders[index].Init(); + } + m_IsRepDecoders[i].Init(); + m_IsRepG0Decoders[i].Init(); + m_IsRepG1Decoders[i].Init(); + m_IsRepG2Decoders[i].Init(); + } + + m_LiteralDecoder.Init(); + for (i = 0; i < Base.kNumLenToPosStates; i++) + m_PosSlotDecoder[i].Init(); + // m_PosSpecDecoder.Init(); + for (i = 0; i < Base.kNumFullDistances - Base.kEndPosModelIndex; i++) + m_PosDecoders[i].Init(); + + m_LenDecoder.Init(); + m_RepLenDecoder.Init(); + m_PosAlignDecoder.Init(); + } + + public void Code(System.IO.Stream inStream, System.IO.Stream outStream, + Int64 inSize, Int64 outSize, ICodeProgress progress) + { + Init(inStream, outStream); + + Base.State state = new Base.State(); + state.Init(); + uint rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0; + + UInt64 nowPos64 = 0; + UInt64 outSize64 = (UInt64)outSize; + if (nowPos64 < outSize64) + { + if (m_IsMatchDecoders[state.Index << Base.kNumPosStatesBitsMax].Decode(m_RangeDecoder) != 0) + throw new DataErrorException(); + state.UpdateChar(); + byte b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, 0, 0); + m_OutWindow.PutByte(b); + nowPos64++; + } + while (nowPos64 < outSize64) + { + // UInt64 next = Math.Min(nowPos64 + (1 << 18), outSize64); + // while(nowPos64 < next) + { + uint posState = (uint)nowPos64 & m_PosStateMask; + if (m_IsMatchDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(m_RangeDecoder) == 0) + { + byte b; + byte prevByte = m_OutWindow.GetByte(0); + if (!state.IsCharState()) + b = m_LiteralDecoder.DecodeWithMatchByte(m_RangeDecoder, + (uint)nowPos64, prevByte, m_OutWindow.GetByte(rep0)); + else + b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, (uint)nowPos64, prevByte); + m_OutWindow.PutByte(b); + state.UpdateChar(); + nowPos64++; + } + else + { + uint len; + if (m_IsRepDecoders[state.Index].Decode(m_RangeDecoder) == 1) + { + if (m_IsRepG0Decoders[state.Index].Decode(m_RangeDecoder) == 0) + { + if (m_IsRep0LongDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(m_RangeDecoder) == 0) + { + state.UpdateShortRep(); + m_OutWindow.PutByte(m_OutWindow.GetByte(rep0)); + nowPos64++; + continue; + } + } + else + { + UInt32 distance; + if (m_IsRepG1Decoders[state.Index].Decode(m_RangeDecoder) == 0) + { + distance = rep1; + } + else + { + if (m_IsRepG2Decoders[state.Index].Decode(m_RangeDecoder) == 0) + distance = rep2; + else + { + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + len = m_RepLenDecoder.Decode(m_RangeDecoder, posState) + Base.kMatchMinLen; + state.UpdateRep(); + } + else + { + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + len = Base.kMatchMinLen + m_LenDecoder.Decode(m_RangeDecoder, posState); + state.UpdateMatch(); + uint posSlot = m_PosSlotDecoder[Base.GetLenToPosState(len)].Decode(m_RangeDecoder); + if (posSlot >= Base.kStartPosModelIndex) + { + int numDirectBits = (int)((posSlot >> 1) - 1); + rep0 = ((2 | (posSlot & 1)) << numDirectBits); + if (posSlot < Base.kEndPosModelIndex) + rep0 += BitTreeDecoder.ReverseDecode(m_PosDecoders, + rep0 - posSlot - 1, m_RangeDecoder, numDirectBits); + else + { + rep0 += (m_RangeDecoder.DecodeDirectBits( + numDirectBits - Base.kNumAlignBits) << Base.kNumAlignBits); + rep0 += m_PosAlignDecoder.ReverseDecode(m_RangeDecoder); + } + } + else + rep0 = posSlot; + } + if (rep0 >= m_OutWindow.TrainSize + nowPos64 || rep0 >= m_DictionarySizeCheck) + { + if (rep0 == 0xFFFFFFFF) + break; + throw new DataErrorException(); + } + m_OutWindow.CopyBlock(rep0, len); + nowPos64 += len; + } + } + } + m_OutWindow.Flush(); + m_OutWindow.ReleaseStream(); + m_RangeDecoder.ReleaseStream(); + } + + public void SetDecoderProperties(byte[] properties) + { + if (properties.Length < 5) + throw new InvalidParamException(); + int lc = properties[0] % 9; + int remainder = properties[0] / 9; + int lp = remainder % 5; + int pb = remainder / 5; + if (pb > Base.kNumPosStatesBitsMax) + throw new InvalidParamException(); + UInt32 dictionarySize = 0; + for (int i = 0; i < 4; i++) + dictionarySize += ((UInt32)(properties[1 + i])) << (i * 8); + SetDictionarySize(dictionarySize); + SetLiteralProperties(lp, lc); + SetPosBitsProperties(pb); + } + + public bool Train(System.IO.Stream stream) + { + _solid = true; + return m_OutWindow.Train(stream); + } + + /* + public override bool CanRead { get { return true; }} + public override bool CanWrite { get { return true; }} + public override bool CanSeek { get { return true; }} + public override long Length { get { return 0; }} + public override long Position + { + get { return 0; } + set { } + } + public override void Flush() { } + public override int Read(byte[] buffer, int offset, int count) + { + return 0; + } + public override void Write(byte[] buffer, int offset, int count) + { + } + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + return 0; + } + public override void SetLength(long value) {} + */ + } +} diff --git a/Plugins/7zip/Compress/LZMA/LzmaDecoder.cs.meta b/Plugins/7zip/Compress/LZMA/LzmaDecoder.cs.meta new file mode 100644 index 0000000..2725167 --- /dev/null +++ b/Plugins/7zip/Compress/LZMA/LzmaDecoder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c60a9ce2c6df6774498e337ad69a356b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Compress/LZMA/LzmaEncoder.cs b/Plugins/7zip/Compress/LZMA/LzmaEncoder.cs new file mode 100644 index 0000000..0237c51 --- /dev/null +++ b/Plugins/7zip/Compress/LZMA/LzmaEncoder.cs @@ -0,0 +1,1480 @@ +// LzmaEncoder.cs + +using System; + +namespace SevenZip.Compression.LZMA +{ + using RangeCoder; + + public class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties + { + enum EMatchFinderType + { + BT2, + BT4, + }; + + const UInt32 kIfinityPrice = 0xFFFFFFF; + + static Byte[] g_FastPos = new Byte[1 << 11]; + + static Encoder() + { + const Byte kFastSlots = 22; + int c = 2; + g_FastPos[0] = 0; + g_FastPos[1] = 1; + for (Byte slotFast = 2; slotFast < kFastSlots; slotFast++) + { + UInt32 k = ((UInt32)1 << ((slotFast >> 1) - 1)); + for (UInt32 j = 0; j < k; j++, c++) + g_FastPos[c] = slotFast; + } + } + + static UInt32 GetPosSlot(UInt32 pos) + { + if (pos < (1 << 11)) + return g_FastPos[pos]; + if (pos < (1 << 21)) + return (UInt32)(g_FastPos[pos >> 10] + 20); + return (UInt32)(g_FastPos[pos >> 20] + 40); + } + + static UInt32 GetPosSlot2(UInt32 pos) + { + if (pos < (1 << 17)) + return (UInt32)(g_FastPos[pos >> 6] + 12); + if (pos < (1 << 27)) + return (UInt32)(g_FastPos[pos >> 16] + 32); + return (UInt32)(g_FastPos[pos >> 26] + 52); + } + + Base.State _state = new Base.State(); + Byte _previousByte; + UInt32[] _repDistances = new UInt32[Base.kNumRepDistances]; + + void BaseInit() + { + _state.Init(); + _previousByte = 0; + for (UInt32 i = 0; i < Base.kNumRepDistances; i++) + _repDistances[i] = 0; + } + + const int kDefaultDictionaryLogSize = 22; + const UInt32 kNumFastBytesDefault = 0x20; + + class LiteralEncoder + { + public struct Encoder2 + { + BitEncoder[] m_Encoders; + + public void Create() { m_Encoders = new BitEncoder[0x300]; } + + public void Init() { for (int i = 0; i < 0x300; i++) m_Encoders[i].Init(); } + + public void Encode(RangeCoder.Encoder rangeEncoder, byte symbol) + { + uint context = 1; + for (int i = 7; i >= 0; i--) + { + uint bit = (uint)((symbol >> i) & 1); + m_Encoders[context].Encode(rangeEncoder, bit); + context = (context << 1) | bit; + } + } + + public void EncodeMatched(RangeCoder.Encoder rangeEncoder, byte matchByte, byte symbol) + { + uint context = 1; + bool same = true; + for (int i = 7; i >= 0; i--) + { + uint bit = (uint)((symbol >> i) & 1); + uint state = context; + if (same) + { + uint matchBit = (uint)((matchByte >> i) & 1); + state += ((1 + matchBit) << 8); + same = (matchBit == bit); + } + m_Encoders[state].Encode(rangeEncoder, bit); + context = (context << 1) | bit; + } + } + + public uint GetPrice(bool matchMode, byte matchByte, byte symbol) + { + uint price = 0; + uint context = 1; + int i = 7; + if (matchMode) + { + for (; i >= 0; i--) + { + uint matchBit = (uint)(matchByte >> i) & 1; + uint bit = (uint)(symbol >> i) & 1; + price += m_Encoders[((1 + matchBit) << 8) + context].GetPrice(bit); + context = (context << 1) | bit; + if (matchBit != bit) + { + i--; + break; + } + } + } + for (; i >= 0; i--) + { + uint bit = (uint)(symbol >> i) & 1; + price += m_Encoders[context].GetPrice(bit); + context = (context << 1) | bit; + } + return price; + } + } + + Encoder2[] m_Coders; + int m_NumPrevBits; + int m_NumPosBits; + uint m_PosMask; + + public void Create(int numPosBits, int numPrevBits) + { + if (m_Coders != null && m_NumPrevBits == numPrevBits && m_NumPosBits == numPosBits) + return; + m_NumPosBits = numPosBits; + m_PosMask = ((uint)1 << numPosBits) - 1; + m_NumPrevBits = numPrevBits; + uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); + m_Coders = new Encoder2[numStates]; + for (uint i = 0; i < numStates; i++) + m_Coders[i].Create(); + } + + public void Init() + { + uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); + for (uint i = 0; i < numStates; i++) + m_Coders[i].Init(); + } + + public Encoder2 GetSubCoder(UInt32 pos, Byte prevByte) + { return m_Coders[((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> (8 - m_NumPrevBits))]; } + } + + class LenEncoder + { + RangeCoder.BitEncoder _choice = new RangeCoder.BitEncoder(); + RangeCoder.BitEncoder _choice2 = new RangeCoder.BitEncoder(); + RangeCoder.BitTreeEncoder[] _lowCoder = new RangeCoder.BitTreeEncoder[Base.kNumPosStatesEncodingMax]; + RangeCoder.BitTreeEncoder[] _midCoder = new RangeCoder.BitTreeEncoder[Base.kNumPosStatesEncodingMax]; + RangeCoder.BitTreeEncoder _highCoder = new RangeCoder.BitTreeEncoder(Base.kNumHighLenBits); + + public LenEncoder() + { + for (UInt32 posState = 0; posState < Base.kNumPosStatesEncodingMax; posState++) + { + _lowCoder[posState] = new RangeCoder.BitTreeEncoder(Base.kNumLowLenBits); + _midCoder[posState] = new RangeCoder.BitTreeEncoder(Base.kNumMidLenBits); + } + } + + public void Init(UInt32 numPosStates) + { + _choice.Init(); + _choice2.Init(); + for (UInt32 posState = 0; posState < numPosStates; posState++) + { + _lowCoder[posState].Init(); + _midCoder[posState].Init(); + } + _highCoder.Init(); + } + + public void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState) + { + if (symbol < Base.kNumLowLenSymbols) + { + _choice.Encode(rangeEncoder, 0); + _lowCoder[posState].Encode(rangeEncoder, symbol); + } + else + { + symbol -= Base.kNumLowLenSymbols; + _choice.Encode(rangeEncoder, 1); + if (symbol < Base.kNumMidLenSymbols) + { + _choice2.Encode(rangeEncoder, 0); + _midCoder[posState].Encode(rangeEncoder, symbol); + } + else + { + _choice2.Encode(rangeEncoder, 1); + _highCoder.Encode(rangeEncoder, symbol - Base.kNumMidLenSymbols); + } + } + } + + public void SetPrices(UInt32 posState, UInt32 numSymbols, UInt32[] prices, UInt32 st) + { + UInt32 a0 = _choice.GetPrice0(); + UInt32 a1 = _choice.GetPrice1(); + UInt32 b0 = a1 + _choice2.GetPrice0(); + UInt32 b1 = a1 + _choice2.GetPrice1(); + UInt32 i = 0; + for (i = 0; i < Base.kNumLowLenSymbols; i++) + { + if (i >= numSymbols) + return; + prices[st + i] = a0 + _lowCoder[posState].GetPrice(i); + } + for (; i < Base.kNumLowLenSymbols + Base.kNumMidLenSymbols; i++) + { + if (i >= numSymbols) + return; + prices[st + i] = b0 + _midCoder[posState].GetPrice(i - Base.kNumLowLenSymbols); + } + for (; i < numSymbols; i++) + prices[st + i] = b1 + _highCoder.GetPrice(i - Base.kNumLowLenSymbols - Base.kNumMidLenSymbols); + } + }; + + const UInt32 kNumLenSpecSymbols = Base.kNumLowLenSymbols + Base.kNumMidLenSymbols; + + class LenPriceTableEncoder : LenEncoder + { + UInt32[] _prices = new UInt32[Base.kNumLenSymbols << Base.kNumPosStatesBitsEncodingMax]; + UInt32 _tableSize; + UInt32[] _counters = new UInt32[Base.kNumPosStatesEncodingMax]; + + public void SetTableSize(UInt32 tableSize) { _tableSize = tableSize; } + + public UInt32 GetPrice(UInt32 symbol, UInt32 posState) + { + return _prices[posState * Base.kNumLenSymbols + symbol]; + } + + void UpdateTable(UInt32 posState) + { + SetPrices(posState, _tableSize, _prices, posState * Base.kNumLenSymbols); + _counters[posState] = _tableSize; + } + + public void UpdateTables(UInt32 numPosStates) + { + for (UInt32 posState = 0; posState < numPosStates; posState++) + UpdateTable(posState); + } + + public new void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState) + { + base.Encode(rangeEncoder, symbol, posState); + if (--_counters[posState] == 0) + UpdateTable(posState); + } + } + + const UInt32 kNumOpts = 1 << 12; + class Optimal + { + public Base.State State; + + public bool Prev1IsChar; + public bool Prev2; + + public UInt32 PosPrev2; + public UInt32 BackPrev2; + + public UInt32 Price; + public UInt32 PosPrev; + public UInt32 BackPrev; + + public UInt32 Backs0; + public UInt32 Backs1; + public UInt32 Backs2; + public UInt32 Backs3; + + public void MakeAsChar() { BackPrev = 0xFFFFFFFF; Prev1IsChar = false; } + public void MakeAsShortRep() { BackPrev = 0; ; Prev1IsChar = false; } + public bool IsShortRep() { return (BackPrev == 0); } + }; + Optimal[] _optimum = new Optimal[kNumOpts]; + LZ.IMatchFinder _matchFinder = null; + RangeCoder.Encoder _rangeEncoder = new RangeCoder.Encoder(); + + RangeCoder.BitEncoder[] _isMatch = new RangeCoder.BitEncoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + RangeCoder.BitEncoder[] _isRep = new RangeCoder.BitEncoder[Base.kNumStates]; + RangeCoder.BitEncoder[] _isRepG0 = new RangeCoder.BitEncoder[Base.kNumStates]; + RangeCoder.BitEncoder[] _isRepG1 = new RangeCoder.BitEncoder[Base.kNumStates]; + RangeCoder.BitEncoder[] _isRepG2 = new RangeCoder.BitEncoder[Base.kNumStates]; + RangeCoder.BitEncoder[] _isRep0Long = new RangeCoder.BitEncoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + + RangeCoder.BitTreeEncoder[] _posSlotEncoder = new RangeCoder.BitTreeEncoder[Base.kNumLenToPosStates]; + + RangeCoder.BitEncoder[] _posEncoders = new RangeCoder.BitEncoder[Base.kNumFullDistances - Base.kEndPosModelIndex]; + RangeCoder.BitTreeEncoder _posAlignEncoder = new RangeCoder.BitTreeEncoder(Base.kNumAlignBits); + + LenPriceTableEncoder _lenEncoder = new LenPriceTableEncoder(); + LenPriceTableEncoder _repMatchLenEncoder = new LenPriceTableEncoder(); + + LiteralEncoder _literalEncoder = new LiteralEncoder(); + + UInt32[] _matchDistances = new UInt32[Base.kMatchMaxLen * 2 + 2]; + + UInt32 _numFastBytes = kNumFastBytesDefault; + UInt32 _longestMatchLength; + UInt32 _numDistancePairs; + + UInt32 _additionalOffset; + + UInt32 _optimumEndIndex; + UInt32 _optimumCurrentIndex; + + bool _longestMatchWasFound; + + UInt32[] _posSlotPrices = new UInt32[1 << (Base.kNumPosSlotBits + Base.kNumLenToPosStatesBits)]; + UInt32[] _distancesPrices = new UInt32[Base.kNumFullDistances << Base.kNumLenToPosStatesBits]; + UInt32[] _alignPrices = new UInt32[Base.kAlignTableSize]; + UInt32 _alignPriceCount; + + UInt32 _distTableSize = (kDefaultDictionaryLogSize * 2); + + int _posStateBits = 2; + UInt32 _posStateMask = (4 - 1); + int _numLiteralPosStateBits = 0; + int _numLiteralContextBits = 3; + + UInt32 _dictionarySize = (1 << kDefaultDictionaryLogSize); + UInt32 _dictionarySizePrev = 0xFFFFFFFF; + UInt32 _numFastBytesPrev = 0xFFFFFFFF; + + Int64 nowPos64; + bool _finished; + System.IO.Stream _inStream; + + EMatchFinderType _matchFinderType = EMatchFinderType.BT4; + bool _writeEndMark = false; + + bool _needReleaseMFStream; + + void Create() + { + if (_matchFinder == null) + { + LZ.BinTree bt = new LZ.BinTree(); + int numHashBytes = 4; + if (_matchFinderType == EMatchFinderType.BT2) + numHashBytes = 2; + bt.SetType(numHashBytes); + _matchFinder = bt; + } + _literalEncoder.Create(_numLiteralPosStateBits, _numLiteralContextBits); + + if (_dictionarySize == _dictionarySizePrev && _numFastBytesPrev == _numFastBytes) + return; + _matchFinder.Create(_dictionarySize, kNumOpts, _numFastBytes, Base.kMatchMaxLen + 1); + _dictionarySizePrev = _dictionarySize; + _numFastBytesPrev = _numFastBytes; + } + + public Encoder() + { + for (int i = 0; i < kNumOpts; i++) + _optimum[i] = new Optimal(); + for (int i = 0; i < Base.kNumLenToPosStates; i++) + _posSlotEncoder[i] = new RangeCoder.BitTreeEncoder(Base.kNumPosSlotBits); + } + + void SetWriteEndMarkerMode(bool writeEndMarker) + { + _writeEndMark = writeEndMarker; + } + + void Init() + { + BaseInit(); + _rangeEncoder.Init(); + + uint i; + for (i = 0; i < Base.kNumStates; i++) + { + for (uint j = 0; j <= _posStateMask; j++) + { + uint complexState = (i << Base.kNumPosStatesBitsMax) + j; + _isMatch[complexState].Init(); + _isRep0Long[complexState].Init(); + } + _isRep[i].Init(); + _isRepG0[i].Init(); + _isRepG1[i].Init(); + _isRepG2[i].Init(); + } + _literalEncoder.Init(); + for (i = 0; i < Base.kNumLenToPosStates; i++) + _posSlotEncoder[i].Init(); + for (i = 0; i < Base.kNumFullDistances - Base.kEndPosModelIndex; i++) + _posEncoders[i].Init(); + + _lenEncoder.Init((UInt32)1 << _posStateBits); + _repMatchLenEncoder.Init((UInt32)1 << _posStateBits); + + _posAlignEncoder.Init(); + + _longestMatchWasFound = false; + _optimumEndIndex = 0; + _optimumCurrentIndex = 0; + _additionalOffset = 0; + } + + void ReadMatchDistances(out UInt32 lenRes, out UInt32 numDistancePairs) + { + lenRes = 0; + numDistancePairs = _matchFinder.GetMatches(_matchDistances); + if (numDistancePairs > 0) + { + lenRes = _matchDistances[numDistancePairs - 2]; + if (lenRes == _numFastBytes) + lenRes += _matchFinder.GetMatchLen((int)lenRes - 1, _matchDistances[numDistancePairs - 1], + Base.kMatchMaxLen - lenRes); + } + _additionalOffset++; + } + + + void MovePos(UInt32 num) + { + if (num > 0) + { + _matchFinder.Skip(num); + _additionalOffset += num; + } + } + + UInt32 GetRepLen1Price(Base.State state, UInt32 posState) + { + return _isRepG0[state.Index].GetPrice0() + + _isRep0Long[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0(); + } + + UInt32 GetPureRepPrice(UInt32 repIndex, Base.State state, UInt32 posState) + { + UInt32 price; + if (repIndex == 0) + { + price = _isRepG0[state.Index].GetPrice0(); + price += _isRep0Long[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1(); + } + else + { + price = _isRepG0[state.Index].GetPrice1(); + if (repIndex == 1) + price += _isRepG1[state.Index].GetPrice0(); + else + { + price += _isRepG1[state.Index].GetPrice1(); + price += _isRepG2[state.Index].GetPrice(repIndex - 2); + } + } + return price; + } + + UInt32 GetRepPrice(UInt32 repIndex, UInt32 len, Base.State state, UInt32 posState) + { + UInt32 price = _repMatchLenEncoder.GetPrice(len - Base.kMatchMinLen, posState); + return price + GetPureRepPrice(repIndex, state, posState); + } + + UInt32 GetPosLenPrice(UInt32 pos, UInt32 len, UInt32 posState) + { + UInt32 price; + UInt32 lenToPosState = Base.GetLenToPosState(len); + if (pos < Base.kNumFullDistances) + price = _distancesPrices[(lenToPosState * Base.kNumFullDistances) + pos]; + else + price = _posSlotPrices[(lenToPosState << Base.kNumPosSlotBits) + GetPosSlot2(pos)] + + _alignPrices[pos & Base.kAlignMask]; + return price + _lenEncoder.GetPrice(len - Base.kMatchMinLen, posState); + } + + UInt32 Backward(out UInt32 backRes, UInt32 cur) + { + _optimumEndIndex = cur; + UInt32 posMem = _optimum[cur].PosPrev; + UInt32 backMem = _optimum[cur].BackPrev; + do + { + if (_optimum[cur].Prev1IsChar) + { + _optimum[posMem].MakeAsChar(); + _optimum[posMem].PosPrev = posMem - 1; + if (_optimum[cur].Prev2) + { + _optimum[posMem - 1].Prev1IsChar = false; + _optimum[posMem - 1].PosPrev = _optimum[cur].PosPrev2; + _optimum[posMem - 1].BackPrev = _optimum[cur].BackPrev2; + } + } + UInt32 posPrev = posMem; + UInt32 backCur = backMem; + + backMem = _optimum[posPrev].BackPrev; + posMem = _optimum[posPrev].PosPrev; + + _optimum[posPrev].BackPrev = backCur; + _optimum[posPrev].PosPrev = cur; + cur = posPrev; + } + while (cur > 0); + backRes = _optimum[0].BackPrev; + _optimumCurrentIndex = _optimum[0].PosPrev; + return _optimumCurrentIndex; + } + + UInt32[] reps = new UInt32[Base.kNumRepDistances]; + UInt32[] repLens = new UInt32[Base.kNumRepDistances]; + + + UInt32 GetOptimum(UInt32 position, out UInt32 backRes) + { + if (_optimumEndIndex != _optimumCurrentIndex) + { + UInt32 lenRes = _optimum[_optimumCurrentIndex].PosPrev - _optimumCurrentIndex; + backRes = _optimum[_optimumCurrentIndex].BackPrev; + _optimumCurrentIndex = _optimum[_optimumCurrentIndex].PosPrev; + return lenRes; + } + _optimumCurrentIndex = _optimumEndIndex = 0; + + UInt32 lenMain, numDistancePairs; + if (!_longestMatchWasFound) + { + ReadMatchDistances(out lenMain, out numDistancePairs); + } + else + { + lenMain = _longestMatchLength; + numDistancePairs = _numDistancePairs; + _longestMatchWasFound = false; + } + + UInt32 numAvailableBytes = _matchFinder.GetNumAvailableBytes() + 1; + if (numAvailableBytes < 2) + { + backRes = 0xFFFFFFFF; + return 1; + } + if (numAvailableBytes > Base.kMatchMaxLen) + numAvailableBytes = Base.kMatchMaxLen; + + UInt32 repMaxIndex = 0; + UInt32 i; + for (i = 0; i < Base.kNumRepDistances; i++) + { + reps[i] = _repDistances[i]; + repLens[i] = _matchFinder.GetMatchLen(0 - 1, reps[i], Base.kMatchMaxLen); + if (repLens[i] > repLens[repMaxIndex]) + repMaxIndex = i; + } + if (repLens[repMaxIndex] >= _numFastBytes) + { + backRes = repMaxIndex; + UInt32 lenRes = repLens[repMaxIndex]; + MovePos(lenRes - 1); + return lenRes; + } + + if (lenMain >= _numFastBytes) + { + backRes = _matchDistances[numDistancePairs - 1] + Base.kNumRepDistances; + MovePos(lenMain - 1); + return lenMain; + } + + Byte currentByte = _matchFinder.GetIndexByte(0 - 1); + Byte matchByte = _matchFinder.GetIndexByte((Int32)(0 - _repDistances[0] - 1 - 1)); + + if (lenMain < 2 && currentByte != matchByte && repLens[repMaxIndex] < 2) + { + backRes = (UInt32)0xFFFFFFFF; + return 1; + } + + _optimum[0].State = _state; + + UInt32 posState = (position & _posStateMask); + + _optimum[1].Price = _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0() + + _literalEncoder.GetSubCoder(position, _previousByte).GetPrice(!_state.IsCharState(), matchByte, currentByte); + _optimum[1].MakeAsChar(); + + UInt32 matchPrice = _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1(); + UInt32 repMatchPrice = matchPrice + _isRep[_state.Index].GetPrice1(); + + if (matchByte == currentByte) + { + UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(_state, posState); + if (shortRepPrice < _optimum[1].Price) + { + _optimum[1].Price = shortRepPrice; + _optimum[1].MakeAsShortRep(); + } + } + + UInt32 lenEnd = ((lenMain >= repLens[repMaxIndex]) ? lenMain : repLens[repMaxIndex]); + + if(lenEnd < 2) + { + backRes = _optimum[1].BackPrev; + return 1; + } + + _optimum[1].PosPrev = 0; + + _optimum[0].Backs0 = reps[0]; + _optimum[0].Backs1 = reps[1]; + _optimum[0].Backs2 = reps[2]; + _optimum[0].Backs3 = reps[3]; + + UInt32 len = lenEnd; + do + _optimum[len--].Price = kIfinityPrice; + while (len >= 2); + + for (i = 0; i < Base.kNumRepDistances; i++) + { + UInt32 repLen = repLens[i]; + if (repLen < 2) + continue; + UInt32 price = repMatchPrice + GetPureRepPrice(i, _state, posState); + do + { + UInt32 curAndLenPrice = price + _repMatchLenEncoder.GetPrice(repLen - 2, posState); + Optimal optimum = _optimum[repLen]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = 0; + optimum.BackPrev = i; + optimum.Prev1IsChar = false; + } + } + while (--repLen >= 2); + } + + UInt32 normalMatchPrice = matchPrice + _isRep[_state.Index].GetPrice0(); + + len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); + if (len <= lenMain) + { + UInt32 offs = 0; + while (len > _matchDistances[offs]) + offs += 2; + for (; ; len++) + { + UInt32 distance = _matchDistances[offs + 1]; + UInt32 curAndLenPrice = normalMatchPrice + GetPosLenPrice(distance, len, posState); + Optimal optimum = _optimum[len]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = 0; + optimum.BackPrev = distance + Base.kNumRepDistances; + optimum.Prev1IsChar = false; + } + if (len == _matchDistances[offs]) + { + offs += 2; + if (offs == numDistancePairs) + break; + } + } + } + + UInt32 cur = 0; + + while (true) + { + cur++; + if (cur == lenEnd) + return Backward(out backRes, cur); + UInt32 newLen; + ReadMatchDistances(out newLen, out numDistancePairs); + if (newLen >= _numFastBytes) + { + _numDistancePairs = numDistancePairs; + _longestMatchLength = newLen; + _longestMatchWasFound = true; + return Backward(out backRes, cur); + } + position++; + UInt32 posPrev = _optimum[cur].PosPrev; + Base.State state; + if (_optimum[cur].Prev1IsChar) + { + posPrev--; + if (_optimum[cur].Prev2) + { + state = _optimum[_optimum[cur].PosPrev2].State; + if (_optimum[cur].BackPrev2 < Base.kNumRepDistances) + state.UpdateRep(); + else + state.UpdateMatch(); + } + else + state = _optimum[posPrev].State; + state.UpdateChar(); + } + else + state = _optimum[posPrev].State; + if (posPrev == cur - 1) + { + if (_optimum[cur].IsShortRep()) + state.UpdateShortRep(); + else + state.UpdateChar(); + } + else + { + UInt32 pos; + if (_optimum[cur].Prev1IsChar && _optimum[cur].Prev2) + { + posPrev = _optimum[cur].PosPrev2; + pos = _optimum[cur].BackPrev2; + state.UpdateRep(); + } + else + { + pos = _optimum[cur].BackPrev; + if (pos < Base.kNumRepDistances) + state.UpdateRep(); + else + state.UpdateMatch(); + } + Optimal opt = _optimum[posPrev]; + if (pos < Base.kNumRepDistances) + { + if (pos == 0) + { + reps[0] = opt.Backs0; + reps[1] = opt.Backs1; + reps[2] = opt.Backs2; + reps[3] = opt.Backs3; + } + else if (pos == 1) + { + reps[0] = opt.Backs1; + reps[1] = opt.Backs0; + reps[2] = opt.Backs2; + reps[3] = opt.Backs3; + } + else if (pos == 2) + { + reps[0] = opt.Backs2; + reps[1] = opt.Backs0; + reps[2] = opt.Backs1; + reps[3] = opt.Backs3; + } + else + { + reps[0] = opt.Backs3; + reps[1] = opt.Backs0; + reps[2] = opt.Backs1; + reps[3] = opt.Backs2; + } + } + else + { + reps[0] = (pos - Base.kNumRepDistances); + reps[1] = opt.Backs0; + reps[2] = opt.Backs1; + reps[3] = opt.Backs2; + } + } + _optimum[cur].State = state; + _optimum[cur].Backs0 = reps[0]; + _optimum[cur].Backs1 = reps[1]; + _optimum[cur].Backs2 = reps[2]; + _optimum[cur].Backs3 = reps[3]; + UInt32 curPrice = _optimum[cur].Price; + + currentByte = _matchFinder.GetIndexByte(0 - 1); + matchByte = _matchFinder.GetIndexByte((Int32)(0 - reps[0] - 1 - 1)); + + posState = (position & _posStateMask); + + UInt32 curAnd1Price = curPrice + + _isMatch[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0() + + _literalEncoder.GetSubCoder(position, _matchFinder.GetIndexByte(0 - 2)). + GetPrice(!state.IsCharState(), matchByte, currentByte); + + Optimal nextOptimum = _optimum[cur + 1]; + + bool nextIsChar = false; + if (curAnd1Price < nextOptimum.Price) + { + nextOptimum.Price = curAnd1Price; + nextOptimum.PosPrev = cur; + nextOptimum.MakeAsChar(); + nextIsChar = true; + } + + matchPrice = curPrice + _isMatch[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1(); + repMatchPrice = matchPrice + _isRep[state.Index].GetPrice1(); + + if (matchByte == currentByte && + !(nextOptimum.PosPrev < cur && nextOptimum.BackPrev == 0)) + { + UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(state, posState); + if (shortRepPrice <= nextOptimum.Price) + { + nextOptimum.Price = shortRepPrice; + nextOptimum.PosPrev = cur; + nextOptimum.MakeAsShortRep(); + nextIsChar = true; + } + } + + UInt32 numAvailableBytesFull = _matchFinder.GetNumAvailableBytes() + 1; + numAvailableBytesFull = Math.Min(kNumOpts - 1 - cur, numAvailableBytesFull); + numAvailableBytes = numAvailableBytesFull; + + if (numAvailableBytes < 2) + continue; + if (numAvailableBytes > _numFastBytes) + numAvailableBytes = _numFastBytes; + if (!nextIsChar && matchByte != currentByte) + { + // try Literal + rep0 + UInt32 t = Math.Min(numAvailableBytesFull - 1, _numFastBytes); + UInt32 lenTest2 = _matchFinder.GetMatchLen(0, reps[0], t); + if (lenTest2 >= 2) + { + Base.State state2 = state; + state2.UpdateChar(); + UInt32 posStateNext = (position + 1) & _posStateMask; + UInt32 nextRepMatchPrice = curAnd1Price + + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1() + + _isRep[state2.Index].GetPrice1(); + { + UInt32 offset = cur + 1 + lenTest2; + while (lenEnd < offset) + _optimum[++lenEnd].Price = kIfinityPrice; + UInt32 curAndLenPrice = nextRepMatchPrice + GetRepPrice( + 0, lenTest2, state2, posStateNext); + Optimal optimum = _optimum[offset]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur + 1; + optimum.BackPrev = 0; + optimum.Prev1IsChar = true; + optimum.Prev2 = false; + } + } + } + } + + UInt32 startLen = 2; // speed optimization + + for (UInt32 repIndex = 0; repIndex < Base.kNumRepDistances; repIndex++) + { + UInt32 lenTest = _matchFinder.GetMatchLen(0 - 1, reps[repIndex], numAvailableBytes); + if (lenTest < 2) + continue; + UInt32 lenTestTemp = lenTest; + do + { + while (lenEnd < cur + lenTest) + _optimum[++lenEnd].Price = kIfinityPrice; + UInt32 curAndLenPrice = repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState); + Optimal optimum = _optimum[cur + lenTest]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur; + optimum.BackPrev = repIndex; + optimum.Prev1IsChar = false; + } + } + while(--lenTest >= 2); + lenTest = lenTestTemp; + + if (repIndex == 0) + startLen = lenTest + 1; + + // if (_maxMode) + if (lenTest < numAvailableBytesFull) + { + UInt32 t = Math.Min(numAvailableBytesFull - 1 - lenTest, _numFastBytes); + UInt32 lenTest2 = _matchFinder.GetMatchLen((Int32)lenTest, reps[repIndex], t); + if (lenTest2 >= 2) + { + Base.State state2 = state; + state2.UpdateRep(); + UInt32 posStateNext = (position + lenTest) & _posStateMask; + UInt32 curAndLenCharPrice = + repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState) + + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice0() + + _literalEncoder.GetSubCoder(position + lenTest, + _matchFinder.GetIndexByte((Int32)lenTest - 1 - 1)).GetPrice(true, + _matchFinder.GetIndexByte((Int32)((Int32)lenTest - 1 - (Int32)(reps[repIndex] + 1))), + _matchFinder.GetIndexByte((Int32)lenTest - 1)); + state2.UpdateChar(); + posStateNext = (position + lenTest + 1) & _posStateMask; + UInt32 nextMatchPrice = curAndLenCharPrice + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1(); + UInt32 nextRepMatchPrice = nextMatchPrice + _isRep[state2.Index].GetPrice1(); + + // for(; lenTest2 >= 2; lenTest2--) + { + UInt32 offset = lenTest + 1 + lenTest2; + while(lenEnd < cur + offset) + _optimum[++lenEnd].Price = kIfinityPrice; + UInt32 curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); + Optimal optimum = _optimum[cur + offset]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur + lenTest + 1; + optimum.BackPrev = 0; + optimum.Prev1IsChar = true; + optimum.Prev2 = true; + optimum.PosPrev2 = cur; + optimum.BackPrev2 = repIndex; + } + } + } + } + } + + if (newLen > numAvailableBytes) + { + newLen = numAvailableBytes; + for (numDistancePairs = 0; newLen > _matchDistances[numDistancePairs]; numDistancePairs += 2) ; + _matchDistances[numDistancePairs] = newLen; + numDistancePairs += 2; + } + if (newLen >= startLen) + { + normalMatchPrice = matchPrice + _isRep[state.Index].GetPrice0(); + while (lenEnd < cur + newLen) + _optimum[++lenEnd].Price = kIfinityPrice; + + UInt32 offs = 0; + while (startLen > _matchDistances[offs]) + offs += 2; + + for (UInt32 lenTest = startLen; ; lenTest++) + { + UInt32 curBack = _matchDistances[offs + 1]; + UInt32 curAndLenPrice = normalMatchPrice + GetPosLenPrice(curBack, lenTest, posState); + Optimal optimum = _optimum[cur + lenTest]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur; + optimum.BackPrev = curBack + Base.kNumRepDistances; + optimum.Prev1IsChar = false; + } + + if (lenTest == _matchDistances[offs]) + { + if (lenTest < numAvailableBytesFull) + { + UInt32 t = Math.Min(numAvailableBytesFull - 1 - lenTest, _numFastBytes); + UInt32 lenTest2 = _matchFinder.GetMatchLen((Int32)lenTest, curBack, t); + if (lenTest2 >= 2) + { + Base.State state2 = state; + state2.UpdateMatch(); + UInt32 posStateNext = (position + lenTest) & _posStateMask; + UInt32 curAndLenCharPrice = curAndLenPrice + + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice0() + + _literalEncoder.GetSubCoder(position + lenTest, + _matchFinder.GetIndexByte((Int32)lenTest - 1 - 1)). + GetPrice(true, + _matchFinder.GetIndexByte((Int32)lenTest - (Int32)(curBack + 1) - 1), + _matchFinder.GetIndexByte((Int32)lenTest - 1)); + state2.UpdateChar(); + posStateNext = (position + lenTest + 1) & _posStateMask; + UInt32 nextMatchPrice = curAndLenCharPrice + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1(); + UInt32 nextRepMatchPrice = nextMatchPrice + _isRep[state2.Index].GetPrice1(); + + UInt32 offset = lenTest + 1 + lenTest2; + while (lenEnd < cur + offset) + _optimum[++lenEnd].Price = kIfinityPrice; + curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); + optimum = _optimum[cur + offset]; + if (curAndLenPrice < optimum.Price) + { + optimum.Price = curAndLenPrice; + optimum.PosPrev = cur + lenTest + 1; + optimum.BackPrev = 0; + optimum.Prev1IsChar = true; + optimum.Prev2 = true; + optimum.PosPrev2 = cur; + optimum.BackPrev2 = curBack + Base.kNumRepDistances; + } + } + } + offs += 2; + if (offs == numDistancePairs) + break; + } + } + } + } + } + + bool ChangePair(UInt32 smallDist, UInt32 bigDist) + { + const int kDif = 7; + return (smallDist < ((UInt32)(1) << (32 - kDif)) && bigDist >= (smallDist << kDif)); + } + + void WriteEndMarker(UInt32 posState) + { + if (!_writeEndMark) + return; + + _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].Encode(_rangeEncoder, 1); + _isRep[_state.Index].Encode(_rangeEncoder, 0); + _state.UpdateMatch(); + UInt32 len = Base.kMatchMinLen; + _lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState); + UInt32 posSlot = (1 << Base.kNumPosSlotBits) - 1; + UInt32 lenToPosState = Base.GetLenToPosState(len); + _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot); + int footerBits = 30; + UInt32 posReduced = (((UInt32)1) << footerBits) - 1; + _rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits); + _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask); + } + + void Flush(UInt32 nowPos) + { + ReleaseMFStream(); + WriteEndMarker(nowPos & _posStateMask); + _rangeEncoder.FlushData(); + _rangeEncoder.FlushStream(); + } + + public void CodeOneBlock(out Int64 inSize, out Int64 outSize, out bool finished) + { + inSize = 0; + outSize = 0; + finished = true; + + if (_inStream != null) + { + _matchFinder.SetStream(_inStream); + _matchFinder.Init(); + _needReleaseMFStream = true; + _inStream = null; + if (_trainSize > 0) + _matchFinder.Skip(_trainSize); + } + + if (_finished) + return; + _finished = true; + + + Int64 progressPosValuePrev = nowPos64; + if (nowPos64 == 0) + { + if (_matchFinder.GetNumAvailableBytes() == 0) + { + Flush((UInt32)nowPos64); + return; + } + UInt32 len, numDistancePairs; // it's not used + ReadMatchDistances(out len, out numDistancePairs); + UInt32 posState = (UInt32)(nowPos64) & _posStateMask; + _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].Encode(_rangeEncoder, 0); + _state.UpdateChar(); + Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset)); + _literalEncoder.GetSubCoder((UInt32)(nowPos64), _previousByte).Encode(_rangeEncoder, curByte); + _previousByte = curByte; + _additionalOffset--; + nowPos64++; + } + if (_matchFinder.GetNumAvailableBytes() == 0) + { + Flush((UInt32)nowPos64); + return; + } + while (true) + { + UInt32 pos; + UInt32 len = GetOptimum((UInt32)nowPos64, out pos); + + UInt32 posState = ((UInt32)nowPos64) & _posStateMask; + UInt32 complexState = (_state.Index << Base.kNumPosStatesBitsMax) + posState; + if (len == 1 && pos == 0xFFFFFFFF) + { + _isMatch[complexState].Encode(_rangeEncoder, 0); + Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset)); + LiteralEncoder.Encoder2 subCoder = _literalEncoder.GetSubCoder((UInt32)nowPos64, _previousByte); + if (!_state.IsCharState()) + { + Byte matchByte = _matchFinder.GetIndexByte((Int32)(0 - _repDistances[0] - 1 - _additionalOffset)); + subCoder.EncodeMatched(_rangeEncoder, matchByte, curByte); + } + else + subCoder.Encode(_rangeEncoder, curByte); + _previousByte = curByte; + _state.UpdateChar(); + } + else + { + _isMatch[complexState].Encode(_rangeEncoder, 1); + if (pos < Base.kNumRepDistances) + { + _isRep[_state.Index].Encode(_rangeEncoder, 1); + if (pos == 0) + { + _isRepG0[_state.Index].Encode(_rangeEncoder, 0); + if (len == 1) + _isRep0Long[complexState].Encode(_rangeEncoder, 0); + else + _isRep0Long[complexState].Encode(_rangeEncoder, 1); + } + else + { + _isRepG0[_state.Index].Encode(_rangeEncoder, 1); + if (pos == 1) + _isRepG1[_state.Index].Encode(_rangeEncoder, 0); + else + { + _isRepG1[_state.Index].Encode(_rangeEncoder, 1); + _isRepG2[_state.Index].Encode(_rangeEncoder, pos - 2); + } + } + if (len == 1) + _state.UpdateShortRep(); + else + { + _repMatchLenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState); + _state.UpdateRep(); + } + UInt32 distance = _repDistances[pos]; + if (pos != 0) + { + for (UInt32 i = pos; i >= 1; i--) + _repDistances[i] = _repDistances[i - 1]; + _repDistances[0] = distance; + } + } + else + { + _isRep[_state.Index].Encode(_rangeEncoder, 0); + _state.UpdateMatch(); + _lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState); + pos -= Base.kNumRepDistances; + UInt32 posSlot = GetPosSlot(pos); + UInt32 lenToPosState = Base.GetLenToPosState(len); + _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot); + + if (posSlot >= Base.kStartPosModelIndex) + { + int footerBits = (int)((posSlot >> 1) - 1); + UInt32 baseVal = ((2 | (posSlot & 1)) << footerBits); + UInt32 posReduced = pos - baseVal; + + if (posSlot < Base.kEndPosModelIndex) + RangeCoder.BitTreeEncoder.ReverseEncode(_posEncoders, + baseVal - posSlot - 1, _rangeEncoder, footerBits, posReduced); + else + { + _rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits); + _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask); + _alignPriceCount++; + } + } + UInt32 distance = pos; + for (UInt32 i = Base.kNumRepDistances - 1; i >= 1; i--) + _repDistances[i] = _repDistances[i - 1]; + _repDistances[0] = distance; + _matchPriceCount++; + } + _previousByte = _matchFinder.GetIndexByte((Int32)(len - 1 - _additionalOffset)); + } + _additionalOffset -= len; + nowPos64 += len; + if (_additionalOffset == 0) + { + // if (!_fastMode) + if (_matchPriceCount >= (1 << 7)) + FillDistancesPrices(); + if (_alignPriceCount >= Base.kAlignTableSize) + FillAlignPrices(); + inSize = nowPos64; + outSize = _rangeEncoder.GetProcessedSizeAdd(); + if (_matchFinder.GetNumAvailableBytes() == 0) + { + Flush((UInt32)nowPos64); + return; + } + + if (nowPos64 - progressPosValuePrev >= (1 << 12)) + { + _finished = false; + finished = false; + return; + } + } + } + } + + void ReleaseMFStream() + { + if (_matchFinder != null && _needReleaseMFStream) + { + _matchFinder.ReleaseStream(); + _needReleaseMFStream = false; + } + } + + void SetOutStream(System.IO.Stream outStream) { _rangeEncoder.SetStream(outStream); } + void ReleaseOutStream() { _rangeEncoder.ReleaseStream(); } + + void ReleaseStreams() + { + ReleaseMFStream(); + ReleaseOutStream(); + } + + void SetStreams(System.IO.Stream inStream, System.IO.Stream outStream, + Int64 inSize, Int64 outSize) + { + _inStream = inStream; + _finished = false; + Create(); + SetOutStream(outStream); + Init(); + + // if (!_fastMode) + { + FillDistancesPrices(); + FillAlignPrices(); + } + + _lenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen); + _lenEncoder.UpdateTables((UInt32)1 << _posStateBits); + _repMatchLenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen); + _repMatchLenEncoder.UpdateTables((UInt32)1 << _posStateBits); + + nowPos64 = 0; + } + + + public void Code(System.IO.Stream inStream, System.IO.Stream outStream, + Int64 inSize, Int64 outSize, ICodeProgress progress) + { + _needReleaseMFStream = false; + try + { + SetStreams(inStream, outStream, inSize, outSize); + while (true) + { + Int64 processedInSize; + Int64 processedOutSize; + bool finished; + CodeOneBlock(out processedInSize, out processedOutSize, out finished); + if (finished) + return; + if (progress != null) + { + progress.SetProgress(processedInSize, processedOutSize); + } + } + } + finally + { + ReleaseStreams(); + } + } + + const int kPropSize = 5; + Byte[] properties = new Byte[kPropSize]; + + public void WriteCoderProperties(System.IO.Stream outStream) + { + properties[0] = (Byte)((_posStateBits * 5 + _numLiteralPosStateBits) * 9 + _numLiteralContextBits); + for (int i = 0; i < 4; i++) + properties[1 + i] = (Byte)((_dictionarySize >> (8 * i)) & 0xFF); + outStream.Write(properties, 0, kPropSize); + } + + UInt32[] tempPrices = new UInt32[Base.kNumFullDistances]; + UInt32 _matchPriceCount; + + void FillDistancesPrices() + { + for (UInt32 i = Base.kStartPosModelIndex; i < Base.kNumFullDistances; i++) + { + UInt32 posSlot = GetPosSlot(i); + int footerBits = (int)((posSlot >> 1) - 1); + UInt32 baseVal = ((2 | (posSlot & 1)) << footerBits); + tempPrices[i] = BitTreeEncoder.ReverseGetPrice(_posEncoders, + baseVal - posSlot - 1, footerBits, i - baseVal); + } + + for (UInt32 lenToPosState = 0; lenToPosState < Base.kNumLenToPosStates; lenToPosState++) + { + UInt32 posSlot; + RangeCoder.BitTreeEncoder encoder = _posSlotEncoder[lenToPosState]; + + UInt32 st = (lenToPosState << Base.kNumPosSlotBits); + for (posSlot = 0; posSlot < _distTableSize; posSlot++) + _posSlotPrices[st + posSlot] = encoder.GetPrice(posSlot); + for (posSlot = Base.kEndPosModelIndex; posSlot < _distTableSize; posSlot++) + _posSlotPrices[st + posSlot] += ((((posSlot >> 1) - 1) - Base.kNumAlignBits) << RangeCoder.BitEncoder.kNumBitPriceShiftBits); + + UInt32 st2 = lenToPosState * Base.kNumFullDistances; + UInt32 i; + for (i = 0; i < Base.kStartPosModelIndex; i++) + _distancesPrices[st2 + i] = _posSlotPrices[st + i]; + for (; i < Base.kNumFullDistances; i++) + _distancesPrices[st2 + i] = _posSlotPrices[st + GetPosSlot(i)] + tempPrices[i]; + } + _matchPriceCount = 0; + } + + void FillAlignPrices() + { + for (UInt32 i = 0; i < Base.kAlignTableSize; i++) + _alignPrices[i] = _posAlignEncoder.ReverseGetPrice(i); + _alignPriceCount = 0; + } + + + static string[] kMatchFinderIDs = + { + "BT2", + "BT4", + }; + + static int FindMatchFinder(string s) + { + for (int m = 0; m < kMatchFinderIDs.Length; m++) + if (s == kMatchFinderIDs[m]) + return m; + return -1; + } + + public void SetCoderProperties(CoderPropID[] propIDs, object[] properties) + { + for (UInt32 i = 0; i < properties.Length; i++) + { + object prop = properties[i]; + switch (propIDs[i]) + { + case CoderPropID.NumFastBytes: + { + if (!(prop is Int32)) + throw new InvalidParamException(); + Int32 numFastBytes = (Int32)prop; + if (numFastBytes < 5 || numFastBytes > Base.kMatchMaxLen) + throw new InvalidParamException(); + _numFastBytes = (UInt32)numFastBytes; + break; + } + case CoderPropID.Algorithm: + { + /* + if (!(prop is Int32)) + throw new InvalidParamException(); + Int32 maximize = (Int32)prop; + _fastMode = (maximize == 0); + _maxMode = (maximize >= 2); + */ + break; + } + case CoderPropID.MatchFinder: + { + if (!(prop is String)) + throw new InvalidParamException(); + EMatchFinderType matchFinderIndexPrev = _matchFinderType; + int m = FindMatchFinder(((string)prop).ToUpper()); + if (m < 0) + throw new InvalidParamException(); + _matchFinderType = (EMatchFinderType)m; + if (_matchFinder != null && matchFinderIndexPrev != _matchFinderType) + { + _dictionarySizePrev = 0xFFFFFFFF; + _matchFinder = null; + } + break; + } + case CoderPropID.DictionarySize: + { + const int kDicLogSizeMaxCompress = 30; + if (!(prop is Int32)) + throw new InvalidParamException(); ; + Int32 dictionarySize = (Int32)prop; + if (dictionarySize < (UInt32)(1 << Base.kDicLogSizeMin) || + dictionarySize > (UInt32)(1 << kDicLogSizeMaxCompress)) + throw new InvalidParamException(); + _dictionarySize = (UInt32)dictionarySize; + int dicLogSize; + for (dicLogSize = 0; dicLogSize < (UInt32)kDicLogSizeMaxCompress; dicLogSize++) + if (dictionarySize <= ((UInt32)(1) << dicLogSize)) + break; + _distTableSize = (UInt32)dicLogSize * 2; + break; + } + case CoderPropID.PosStateBits: + { + if (!(prop is Int32)) + throw new InvalidParamException(); + Int32 v = (Int32)prop; + if (v < 0 || v > (UInt32)Base.kNumPosStatesBitsEncodingMax) + throw new InvalidParamException(); + _posStateBits = (int)v; + _posStateMask = (((UInt32)1) << (int)_posStateBits) - 1; + break; + } + case CoderPropID.LitPosBits: + { + if (!(prop is Int32)) + throw new InvalidParamException(); + Int32 v = (Int32)prop; + if (v < 0 || v > (UInt32)Base.kNumLitPosStatesBitsEncodingMax) + throw new InvalidParamException(); + _numLiteralPosStateBits = (int)v; + break; + } + case CoderPropID.LitContextBits: + { + if (!(prop is Int32)) + throw new InvalidParamException(); + Int32 v = (Int32)prop; + if (v < 0 || v > (UInt32)Base.kNumLitContextBitsMax) + throw new InvalidParamException(); ; + _numLiteralContextBits = (int)v; + break; + } + case CoderPropID.EndMarker: + { + if (!(prop is Boolean)) + throw new InvalidParamException(); + SetWriteEndMarkerMode((Boolean)prop); + break; + } + default: + throw new InvalidParamException(); + } + } + } + + uint _trainSize = 0; + public void SetTrainSize(uint trainSize) + { + _trainSize = trainSize; + } + + } +} diff --git a/Plugins/7zip/Compress/LZMA/LzmaEncoder.cs.meta b/Plugins/7zip/Compress/LZMA/LzmaEncoder.cs.meta new file mode 100644 index 0000000..fedcb56 --- /dev/null +++ b/Plugins/7zip/Compress/LZMA/LzmaEncoder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 37dcefc9d952dc34f9fba085a160a968 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Compress/RangeCoder.meta b/Plugins/7zip/Compress/RangeCoder.meta new file mode 100644 index 0000000..cf24bb3 --- /dev/null +++ b/Plugins/7zip/Compress/RangeCoder.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 73c80167cede90a4a8fc4938bab053c8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Compress/RangeCoder/RangeCoder.cs b/Plugins/7zip/Compress/RangeCoder/RangeCoder.cs new file mode 100644 index 0000000..949c6bb --- /dev/null +++ b/Plugins/7zip/Compress/RangeCoder/RangeCoder.cs @@ -0,0 +1,234 @@ +using System; + +namespace SevenZip.Compression.RangeCoder +{ + class Encoder + { + public const uint kTopValue = (1 << 24); + + System.IO.Stream Stream; + + public UInt64 Low; + public uint Range; + uint _cacheSize; + byte _cache; + + long StartPosition; + + public void SetStream(System.IO.Stream stream) + { + Stream = stream; + } + + public void ReleaseStream() + { + Stream = null; + } + + public void Init() + { + StartPosition = Stream.Position; + + Low = 0; + Range = 0xFFFFFFFF; + _cacheSize = 1; + _cache = 0; + } + + public void FlushData() + { + for (int i = 0; i < 5; i++) + ShiftLow(); + } + + public void FlushStream() + { + Stream.Flush(); + } + + public void CloseStream() + { + Stream.Close(); + } + + public void Encode(uint start, uint size, uint total) + { + Low += start * (Range /= total); + Range *= size; + while (Range < kTopValue) + { + Range <<= 8; + ShiftLow(); + } + } + + public void ShiftLow() + { + if ((uint)Low < (uint)0xFF000000 || (uint)(Low >> 32) == 1) + { + byte temp = _cache; + do + { + Stream.WriteByte((byte)(temp + (Low >> 32))); + temp = 0xFF; + } + while (--_cacheSize != 0); + _cache = (byte)(((uint)Low) >> 24); + } + _cacheSize++; + Low = ((uint)Low) << 8; + } + + public void EncodeDirectBits(uint v, int numTotalBits) + { + for (int i = numTotalBits - 1; i >= 0; i--) + { + Range >>= 1; + if (((v >> i) & 1) == 1) + Low += Range; + if (Range < kTopValue) + { + Range <<= 8; + ShiftLow(); + } + } + } + + public void EncodeBit(uint size0, int numTotalBits, uint symbol) + { + uint newBound = (Range >> numTotalBits) * size0; + if (symbol == 0) + Range = newBound; + else + { + Low += newBound; + Range -= newBound; + } + while (Range < kTopValue) + { + Range <<= 8; + ShiftLow(); + } + } + + public long GetProcessedSizeAdd() + { + return _cacheSize + + Stream.Position - StartPosition + 4; + // (long)Stream.GetProcessedSize(); + } + } + + class Decoder + { + public const uint kTopValue = (1 << 24); + public uint Range; + public uint Code; + // public Buffer.InBuffer Stream = new Buffer.InBuffer(1 << 16); + public System.IO.Stream Stream; + + public void Init(System.IO.Stream stream) + { + // Stream.Init(stream); + Stream = stream; + + Code = 0; + Range = 0xFFFFFFFF; + for (int i = 0; i < 5; i++) + Code = (Code << 8) | (byte)Stream.ReadByte(); + } + + public void ReleaseStream() + { + // Stream.ReleaseStream(); + Stream = null; + } + + public void CloseStream() + { + Stream.Close(); + } + + public void Normalize() + { + while (Range < kTopValue) + { + Code = (Code << 8) | (byte)Stream.ReadByte(); + Range <<= 8; + } + } + + public void Normalize2() + { + if (Range < kTopValue) + { + Code = (Code << 8) | (byte)Stream.ReadByte(); + Range <<= 8; + } + } + + public uint GetThreshold(uint total) + { + return Code / (Range /= total); + } + + public void Decode(uint start, uint size, uint total) + { + Code -= start * Range; + Range *= size; + Normalize(); + } + + public uint DecodeDirectBits(int numTotalBits) + { + uint range = Range; + uint code = Code; + uint result = 0; + for (int i = numTotalBits; i > 0; i--) + { + range >>= 1; + /* + result <<= 1; + if (code >= range) + { + code -= range; + result |= 1; + } + */ + uint t = (code - range) >> 31; + code -= range & (t - 1); + result = (result << 1) | (1 - t); + + if (range < kTopValue) + { + code = (code << 8) | (byte)Stream.ReadByte(); + range <<= 8; + } + } + Range = range; + Code = code; + return result; + } + + public uint DecodeBit(uint size0, int numTotalBits) + { + uint newBound = (Range >> numTotalBits) * size0; + uint symbol; + if (Code < newBound) + { + symbol = 0; + Range = newBound; + } + else + { + symbol = 1; + Code -= newBound; + Range -= newBound; + } + Normalize(); + return symbol; + } + + // ulong GetProcessedSize() {return Stream.GetProcessedSize(); } + } +} diff --git a/Plugins/7zip/Compress/RangeCoder/RangeCoder.cs.meta b/Plugins/7zip/Compress/RangeCoder/RangeCoder.cs.meta new file mode 100644 index 0000000..fdbc75b --- /dev/null +++ b/Plugins/7zip/Compress/RangeCoder/RangeCoder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a855cbfe314340a43b3d71672dcbb058 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Compress/RangeCoder/RangeCoderBit.cs b/Plugins/7zip/Compress/RangeCoder/RangeCoderBit.cs new file mode 100644 index 0000000..4f0346d --- /dev/null +++ b/Plugins/7zip/Compress/RangeCoder/RangeCoderBit.cs @@ -0,0 +1,117 @@ +using System; + +namespace SevenZip.Compression.RangeCoder +{ + struct BitEncoder + { + public const int kNumBitModelTotalBits = 11; + public const uint kBitModelTotal = (1 << kNumBitModelTotalBits); + const int kNumMoveBits = 5; + const int kNumMoveReducingBits = 2; + public const int kNumBitPriceShiftBits = 6; + + uint Prob; + + public void Init() { Prob = kBitModelTotal >> 1; } + + public void UpdateModel(uint symbol) + { + if (symbol == 0) + Prob += (kBitModelTotal - Prob) >> kNumMoveBits; + else + Prob -= (Prob) >> kNumMoveBits; + } + + public void Encode(Encoder encoder, uint symbol) + { + // encoder.EncodeBit(Prob, kNumBitModelTotalBits, symbol); + // UpdateModel(symbol); + uint newBound = (encoder.Range >> kNumBitModelTotalBits) * Prob; + if (symbol == 0) + { + encoder.Range = newBound; + Prob += (kBitModelTotal - Prob) >> kNumMoveBits; + } + else + { + encoder.Low += newBound; + encoder.Range -= newBound; + Prob -= (Prob) >> kNumMoveBits; + } + if (encoder.Range < Encoder.kTopValue) + { + encoder.Range <<= 8; + encoder.ShiftLow(); + } + } + + private static UInt32[] ProbPrices = new UInt32[kBitModelTotal >> kNumMoveReducingBits]; + + static BitEncoder() + { + const int kNumBits = (kNumBitModelTotalBits - kNumMoveReducingBits); + for (int i = kNumBits - 1; i >= 0; i--) + { + UInt32 start = (UInt32)1 << (kNumBits - i - 1); + UInt32 end = (UInt32)1 << (kNumBits - i); + for (UInt32 j = start; j < end; j++) + ProbPrices[j] = ((UInt32)i << kNumBitPriceShiftBits) + + (((end - j) << kNumBitPriceShiftBits) >> (kNumBits - i - 1)); + } + } + + public uint GetPrice(uint symbol) + { + return ProbPrices[(((Prob - symbol) ^ ((-(int)symbol))) & (kBitModelTotal - 1)) >> kNumMoveReducingBits]; + } + public uint GetPrice0() { return ProbPrices[Prob >> kNumMoveReducingBits]; } + public uint GetPrice1() { return ProbPrices[(kBitModelTotal - Prob) >> kNumMoveReducingBits]; } + } + + struct BitDecoder + { + public const int kNumBitModelTotalBits = 11; + public const uint kBitModelTotal = (1 << kNumBitModelTotalBits); + const int kNumMoveBits = 5; + + uint Prob; + + public void UpdateModel(int numMoveBits, uint symbol) + { + if (symbol == 0) + Prob += (kBitModelTotal - Prob) >> numMoveBits; + else + Prob -= (Prob) >> numMoveBits; + } + + public void Init() { Prob = kBitModelTotal >> 1; } + + public uint Decode(RangeCoder.Decoder rangeDecoder) + { + uint newBound = (uint)(rangeDecoder.Range >> kNumBitModelTotalBits) * (uint)Prob; + if (rangeDecoder.Code < newBound) + { + rangeDecoder.Range = newBound; + Prob += (kBitModelTotal - Prob) >> kNumMoveBits; + if (rangeDecoder.Range < Decoder.kTopValue) + { + rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte(); + rangeDecoder.Range <<= 8; + } + return 0; + } + else + { + rangeDecoder.Range -= newBound; + rangeDecoder.Code -= newBound; + Prob -= (Prob) >> kNumMoveBits; + if (rangeDecoder.Range < Decoder.kTopValue) + { + rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte(); + rangeDecoder.Range <<= 8; + } + return 1; + } + } + } +} diff --git a/Plugins/7zip/Compress/RangeCoder/RangeCoderBit.cs.meta b/Plugins/7zip/Compress/RangeCoder/RangeCoderBit.cs.meta new file mode 100644 index 0000000..2a0f148 --- /dev/null +++ b/Plugins/7zip/Compress/RangeCoder/RangeCoderBit.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f3fd3305ff6e02448a7b0470cfe65b77 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/Compress/RangeCoder/RangeCoderBitTree.cs b/Plugins/7zip/Compress/RangeCoder/RangeCoderBitTree.cs new file mode 100644 index 0000000..4b4506f --- /dev/null +++ b/Plugins/7zip/Compress/RangeCoder/RangeCoderBitTree.cs @@ -0,0 +1,157 @@ +using System; + +namespace SevenZip.Compression.RangeCoder +{ + struct BitTreeEncoder + { + BitEncoder[] Models; + int NumBitLevels; + + public BitTreeEncoder(int numBitLevels) + { + NumBitLevels = numBitLevels; + Models = new BitEncoder[1 << numBitLevels]; + } + + public void Init() + { + for (uint i = 1; i < (1 << NumBitLevels); i++) + Models[i].Init(); + } + + public void Encode(Encoder rangeEncoder, UInt32 symbol) + { + UInt32 m = 1; + for (int bitIndex = NumBitLevels; bitIndex > 0; ) + { + bitIndex--; + UInt32 bit = (symbol >> bitIndex) & 1; + Models[m].Encode(rangeEncoder, bit); + m = (m << 1) | bit; + } + } + + public void ReverseEncode(Encoder rangeEncoder, UInt32 symbol) + { + UInt32 m = 1; + for (UInt32 i = 0; i < NumBitLevels; i++) + { + UInt32 bit = symbol & 1; + Models[m].Encode(rangeEncoder, bit); + m = (m << 1) | bit; + symbol >>= 1; + } + } + + public UInt32 GetPrice(UInt32 symbol) + { + UInt32 price = 0; + UInt32 m = 1; + for (int bitIndex = NumBitLevels; bitIndex > 0; ) + { + bitIndex--; + UInt32 bit = (symbol >> bitIndex) & 1; + price += Models[m].GetPrice(bit); + m = (m << 1) + bit; + } + return price; + } + + public UInt32 ReverseGetPrice(UInt32 symbol) + { + UInt32 price = 0; + UInt32 m = 1; + for (int i = NumBitLevels; i > 0; i--) + { + UInt32 bit = symbol & 1; + symbol >>= 1; + price += Models[m].GetPrice(bit); + m = (m << 1) | bit; + } + return price; + } + + public static UInt32 ReverseGetPrice(BitEncoder[] Models, UInt32 startIndex, + int NumBitLevels, UInt32 symbol) + { + UInt32 price = 0; + UInt32 m = 1; + for (int i = NumBitLevels; i > 0; i--) + { + UInt32 bit = symbol & 1; + symbol >>= 1; + price += Models[startIndex + m].GetPrice(bit); + m = (m << 1) | bit; + } + return price; + } + + public static void ReverseEncode(BitEncoder[] Models, UInt32 startIndex, + Encoder rangeEncoder, int NumBitLevels, UInt32 symbol) + { + UInt32 m = 1; + for (int i = 0; i < NumBitLevels; i++) + { + UInt32 bit = symbol & 1; + Models[startIndex + m].Encode(rangeEncoder, bit); + m = (m << 1) | bit; + symbol >>= 1; + } + } + } + + struct BitTreeDecoder + { + BitDecoder[] Models; + int NumBitLevels; + + public BitTreeDecoder(int numBitLevels) + { + NumBitLevels = numBitLevels; + Models = new BitDecoder[1 << numBitLevels]; + } + + public void Init() + { + for (uint i = 1; i < (1 << NumBitLevels); i++) + Models[i].Init(); + } + + public uint Decode(RangeCoder.Decoder rangeDecoder) + { + uint m = 1; + for (int bitIndex = NumBitLevels; bitIndex > 0; bitIndex--) + m = (m << 1) + Models[m].Decode(rangeDecoder); + return m - ((uint)1 << NumBitLevels); + } + + public uint ReverseDecode(RangeCoder.Decoder rangeDecoder) + { + uint m = 1; + uint symbol = 0; + for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++) + { + uint bit = Models[m].Decode(rangeDecoder); + m <<= 1; + m += bit; + symbol |= (bit << bitIndex); + } + return symbol; + } + + public static uint ReverseDecode(BitDecoder[] Models, UInt32 startIndex, + RangeCoder.Decoder rangeDecoder, int NumBitLevels) + { + uint m = 1; + uint symbol = 0; + for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++) + { + uint bit = Models[startIndex + m].Decode(rangeDecoder); + m <<= 1; + m += bit; + symbol |= (bit << bitIndex); + } + return symbol; + } + } +} diff --git a/Plugins/7zip/Compress/RangeCoder/RangeCoderBitTree.cs.meta b/Plugins/7zip/Compress/RangeCoder/RangeCoderBitTree.cs.meta new file mode 100644 index 0000000..df4ba12 --- /dev/null +++ b/Plugins/7zip/Compress/RangeCoder/RangeCoderBitTree.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ef79f786b772db344a4792a5cba382ff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/ICoder.cs b/Plugins/7zip/ICoder.cs new file mode 100644 index 0000000..c8b95c8 --- /dev/null +++ b/Plugins/7zip/ICoder.cs @@ -0,0 +1,157 @@ +// ICoder.h + +using System; + +namespace SevenZip +{ + /// + /// The exception that is thrown when an error in input stream occurs during decoding. + /// + class DataErrorException : ApplicationException + { + public DataErrorException(): base("Data Error") { } + } + + /// + /// The exception that is thrown when the value of an argument is outside the allowable range. + /// + class InvalidParamException : ApplicationException + { + public InvalidParamException(): base("Invalid Parameter") { } + } + + public interface ICodeProgress + { + /// + /// Callback progress. + /// + /// + /// input size. -1 if unknown. + /// + /// + /// output size. -1 if unknown. + /// + void SetProgress(Int64 inSize, Int64 outSize); + }; + + public interface ICoder + { + /// + /// Codes streams. + /// + /// + /// input Stream. + /// + /// + /// output Stream. + /// + /// + /// input Size. -1 if unknown. + /// + /// + /// output Size. -1 if unknown. + /// + /// + /// callback progress reference. + /// + /// + /// if input stream is not valid + /// + void Code(System.IO.Stream inStream, System.IO.Stream outStream, + Int64 inSize, Int64 outSize, ICodeProgress progress); + }; + + /* + public interface ICoder2 + { + void Code(ISequentialInStream []inStreams, + const UInt64 []inSizes, + ISequentialOutStream []outStreams, + UInt64 []outSizes, + ICodeProgress progress); + }; + */ + + /// + /// Provides the fields that represent properties idenitifiers for compressing. + /// + public enum CoderPropID + { + /// + /// Specifies default property. + /// + DefaultProp = 0, + /// + /// Specifies size of dictionary. + /// + DictionarySize, + /// + /// Specifies size of memory for PPM*. + /// + UsedMemorySize, + /// + /// Specifies order for PPM methods. + /// + Order, + /// + /// Specifies Block Size. + /// + BlockSize, + /// + /// Specifies number of postion state bits for LZMA (0 <= x <= 4). + /// + PosStateBits, + /// + /// Specifies number of literal context bits for LZMA (0 <= x <= 8). + /// + LitContextBits, + /// + /// Specifies number of literal position bits for LZMA (0 <= x <= 4). + /// + LitPosBits, + /// + /// Specifies number of fast bytes for LZ*. + /// + NumFastBytes, + /// + /// Specifies match finder. LZMA: "BT2", "BT4" or "BT4B". + /// + MatchFinder, + /// + /// Specifies the number of match finder cyckes. + /// + MatchFinderCycles, + /// + /// Specifies number of passes. + /// + NumPasses, + /// + /// Specifies number of algorithm. + /// + Algorithm, + /// + /// Specifies the number of threads. + /// + NumThreads, + /// + /// Specifies mode with end marker. + /// + EndMarker + }; + + + public interface ISetCoderProperties + { + void SetCoderProperties(CoderPropID[] propIDs, object[] properties); + }; + + public interface IWriteCoderProperties + { + void WriteCoderProperties(System.IO.Stream outStream); + } + + public interface ISetDecoderProperties + { + void SetDecoderProperties(byte[] properties); + } +} diff --git a/Plugins/7zip/ICoder.cs.meta b/Plugins/7zip/ICoder.cs.meta new file mode 100644 index 0000000..f4ca52a --- /dev/null +++ b/Plugins/7zip/ICoder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9f4fba00efa28594ea18888f574d028a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/7zip/SevenZip.asmdef b/Plugins/7zip/SevenZip.asmdef new file mode 100644 index 0000000..2e1b01c --- /dev/null +++ b/Plugins/7zip/SevenZip.asmdef @@ -0,0 +1,16 @@ +{ + "name": "SevenZip", + "rootNamespace": "", + "references": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Plugins/7zip/SevenZip.asmdef.meta b/Plugins/7zip/SevenZip.asmdef.meta new file mode 100644 index 0000000..01db39f --- /dev/null +++ b/Plugins/7zip/SevenZip.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2cbbab4859728cc4786e422ed5e6e91e +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/LZ4.dll b/Plugins/LZ4.dll new file mode 100644 index 0000000..f1aeba3 Binary files /dev/null and b/Plugins/LZ4.dll differ diff --git a/Plugins/LZ4.dll.meta b/Plugins/LZ4.dll.meta new file mode 100644 index 0000000..527366c --- /dev/null +++ b/Plugins/LZ4.dll.meta @@ -0,0 +1,87 @@ +fileFormatVersion: 2 +guid: ca69cf4ffd628394a9dd1ef1c56f7f20 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS.meta b/Plugins/UnityFS.meta new file mode 100644 index 0000000..bee3c03 --- /dev/null +++ b/Plugins/UnityFS.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 59ab7ccacd9a9944bb9456da235d6760 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/ArchiveFlags.cs b/Plugins/UnityFS/ArchiveFlags.cs new file mode 100644 index 0000000..6f46f5d --- /dev/null +++ b/Plugins/UnityFS/ArchiveFlags.cs @@ -0,0 +1,14 @@ +using System; + +namespace UnityFS +{ + [Flags] + public enum ArchiveFlags + { + CompressionTypeMask = 0x3f, + BlocksAndDirectoryInfoCombined = 0x40, + BlocksInfoAtTheEnd = 0x80, + OldWebPluginCompatibility = 0x100, + BlockInfoNeedPaddingAtStart = 0x200 + } +} diff --git a/Plugins/UnityFS/ArchiveFlags.cs.meta b/Plugins/UnityFS/ArchiveFlags.cs.meta new file mode 100644 index 0000000..95b0a25 --- /dev/null +++ b/Plugins/UnityFS/ArchiveFlags.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 099de05eebdffaf4baeed3290dc98aaf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/BinaryReaderExtensions.cs b/Plugins/UnityFS/BinaryReaderExtensions.cs new file mode 100644 index 0000000..848d820 --- /dev/null +++ b/Plugins/UnityFS/BinaryReaderExtensions.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace UnityFS +{ + public static class BinaryReaderExtensions + { + + public static void AlignStream(this BinaryReader reader, int alignment) + { + var pos = reader.BaseStream.Position; + var mod = pos % alignment; + if (mod != 0) + { + reader.BaseStream.Position += alignment - mod; + } + } + + public static string ReadAlignedString(this BinaryReader reader) + { + var length = reader.ReadInt32(); + if (length > 0 && length <= reader.BaseStream.Length - reader.BaseStream.Position) + { + var stringData = reader.ReadBytes(length); + var result = Encoding.UTF8.GetString(stringData); + reader.AlignStream(4); + return result; + } + return ""; + } + + public static string ReadStringToNull(this BinaryReader reader, int maxLength = 32767) + { + var bytes = new List(); + int count = 0; + while (reader.BaseStream.Position != reader.BaseStream.Length && count < maxLength) + { + var b = reader.ReadByte(); + if (b == 0) + { + break; + } + bytes.Add(b); + count++; + } + return Encoding.UTF8.GetString(bytes.ToArray()); + } + } +} diff --git a/Plugins/UnityFS/BinaryReaderExtensions.cs.meta b/Plugins/UnityFS/BinaryReaderExtensions.cs.meta new file mode 100644 index 0000000..224d27e --- /dev/null +++ b/Plugins/UnityFS/BinaryReaderExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a84e427dd05adde44b4345b3fd49007a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/BinaryWriterExtensions.cs b/Plugins/UnityFS/BinaryWriterExtensions.cs new file mode 100644 index 0000000..8015515 --- /dev/null +++ b/Plugins/UnityFS/BinaryWriterExtensions.cs @@ -0,0 +1,33 @@ +using System; +using System.IO; +using System.Text; + +namespace UnityFS +{ + public static class BinaryWriterExtensions + { + public static void AlignStream(this BinaryWriter writer, int alignment) + { + var pos = writer.BaseStream.Position; + var mod = pos % alignment; + if (mod != 0) + { + writer.Write(new byte[alignment - mod]); + } + } + + public static void WriteAlignedString(this BinaryWriter writer, string str) + { + var bytes = Encoding.UTF8.GetBytes(str); + writer.Write(bytes.Length); + writer.Write(bytes); + writer.AlignStream(4); + } + + public static void WriteNullEndString(this BinaryWriter writer, string str) + { + writer.Write(Encoding.UTF8.GetBytes(str)); + writer.Write((byte)0); + } + } +} diff --git a/Plugins/UnityFS/BinaryWriterExtensions.cs.meta b/Plugins/UnityFS/BinaryWriterExtensions.cs.meta new file mode 100644 index 0000000..053416c --- /dev/null +++ b/Plugins/UnityFS/BinaryWriterExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 02eda21769c083346a5bd9b7dca49427 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/BundleFileInfo.cs b/Plugins/UnityFS/BundleFileInfo.cs new file mode 100644 index 0000000..d2a9eab --- /dev/null +++ b/Plugins/UnityFS/BundleFileInfo.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UnityFS +{ + public class BundleSubFile + { + public string file; + public byte[] data; + } + + public class BundleFileInfo + { + public string signature; + public uint version; + public string unityVersion; + public string unityRevision; + public ArchiveFlags flags; + public List files; + } +} diff --git a/Plugins/UnityFS/BundleFileInfo.cs.meta b/Plugins/UnityFS/BundleFileInfo.cs.meta new file mode 100644 index 0000000..b33b9b2 --- /dev/null +++ b/Plugins/UnityFS/BundleFileInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1a72550a949e322419b9c5d6e4fe495d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/BundleFileReader.cs b/Plugins/UnityFS/BundleFileReader.cs new file mode 100644 index 0000000..1804cf4 --- /dev/null +++ b/Plugins/UnityFS/BundleFileReader.cs @@ -0,0 +1,212 @@ +using LZ4; +using System; +using System.IO; +using System.Linq; +using UnityEngine; + +namespace UnityFS +{ + + public class BundleFileReader + { + + private Header m_Header; + private StorageBlock[] m_BlocksInfo; + private Node[] m_DirectoryInfo; + + private StreamFile[] fileList; + + public BundleFileReader() + { + + } + + public void Load(EndianBinaryReader reader) + { + Debug.Log($"reader. pos:{reader.Position} length:{reader.BaseStream.Length}"); + m_Header = new Header(); + m_Header.signature = reader.ReadStringToNull(); + m_Header.version = reader.ReadUInt32(); + m_Header.unityVersion = reader.ReadStringToNull(); + m_Header.unityRevision = reader.ReadStringToNull(); + System.Diagnostics.Debug.Assert(m_Header.signature == "UnityFS"); + + + m_Header.size = reader.ReadInt64(); + Debug.Log($"header size:{m_Header.size}"); + m_Header.compressedBlocksInfoSize = reader.ReadUInt32(); + m_Header.uncompressedBlocksInfoSize = reader.ReadUInt32(); + m_Header.flags = (ArchiveFlags)reader.ReadUInt32(); + if (m_Header.signature != "UnityFS") + { + reader.ReadByte(); + } + + ReadMetadata(reader); + using (var blocksStream = CreateBlocksStream()) + { + ReadBlocks(reader, blocksStream); + ReadFiles(blocksStream); + } + } + + public BundleFileInfo CreateBundleFileInfo() + { + return new BundleFileInfo + { + signature = m_Header.signature, + version = m_Header.version, + unityVersion = m_Header.unityVersion, + unityRevision = m_Header.unityRevision, + files = fileList.Select(f => new BundleSubFile { file = f.path, data = f.stream.ReadAllBytes() }).ToList(), + }; + } + + private byte[] ReadBlocksInfoAndDirectoryMetadataUnCompressedBytes(EndianBinaryReader reader) + { + byte[] metadataUncompressBytes; + if (m_Header.version >= 7) + { + reader.AlignStream(16); + } + if ((m_Header.flags & ArchiveFlags.BlocksInfoAtTheEnd) != 0) + { + var position = reader.Position; + reader.Position = reader.BaseStream.Length - m_Header.compressedBlocksInfoSize; + metadataUncompressBytes = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize); + reader.Position = position; + } + else //0x40 BlocksAndDirectoryInfoCombined + { + metadataUncompressBytes = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize); + } + return metadataUncompressBytes; + } + + private byte[] DecompressBytes(CompressionType compressionType, byte[] compressedBytes, uint uncompressedSize) + { + switch (compressionType) + { + case CompressionType.None: + { + return compressedBytes; + } + case CompressionType.Lzma: + { + var uncompressedStream = new MemoryStream((int)(uncompressedSize)); + using (var compressedStream = new MemoryStream(compressedBytes)) + { + ComparessHelper.Decompress7Zip(compressedStream, uncompressedStream, m_Header.compressedBlocksInfoSize, m_Header.uncompressedBlocksInfoSize); + } + return uncompressedStream.ReadAllBytes(); + } + case CompressionType.Lz4: + case CompressionType.Lz4HC: + { + var uncompressedBytes = new byte[uncompressedSize]; + var numWrite = LZ4Codec.Decode(compressedBytes, 0, compressedBytes.Length, uncompressedBytes, 0, uncompressedBytes.Length, true); + if (numWrite != uncompressedSize) + { + throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes"); + } + return uncompressedBytes; + } + default: + throw new IOException($"Unsupported compression type {compressionType}"); + } + } + + private void ReadMetadata(EndianBinaryReader reader) + { + byte[] compressMetadataBytes = ReadBlocksInfoAndDirectoryMetadataUnCompressedBytes(reader); + MemoryStream metadataStream = new MemoryStream(DecompressBytes((CompressionType)(m_Header.flags & ArchiveFlags.CompressionTypeMask), compressMetadataBytes, m_Header.uncompressedBlocksInfoSize)); + using (var blocksInfoReader = new EndianBinaryReader(metadataStream)) + { + var uncompressedDataHash = blocksInfoReader.ReadBytes(16); + var blocksInfoCount = blocksInfoReader.ReadInt32(); + m_BlocksInfo = new StorageBlock[blocksInfoCount]; + for (int i = 0; i < blocksInfoCount; i++) + { + m_BlocksInfo[i] = new StorageBlock + { + uncompressedSize = blocksInfoReader.ReadUInt32(), + compressedSize = blocksInfoReader.ReadUInt32(), + flags = (StorageBlockFlags)blocksInfoReader.ReadUInt16() + }; + } + + var nodesCount = blocksInfoReader.ReadInt32(); + m_DirectoryInfo = new Node[nodesCount]; + for (int i = 0; i < nodesCount; i++) + { + m_DirectoryInfo[i] = new Node + { + offset = blocksInfoReader.ReadInt64(), + size = blocksInfoReader.ReadInt64(), + flags = blocksInfoReader.ReadUInt32(), + path = blocksInfoReader.ReadStringToNull(), + }; + } + } + if (m_Header.flags.HasFlag(ArchiveFlags.BlockInfoNeedPaddingAtStart)) + { + reader.AlignStream(16); + } + } + + + private Stream CreateBlocksStream() + { + Stream blocksStream; + var uncompressedSizeSum = m_BlocksInfo.Sum(x => x.uncompressedSize); + if (uncompressedSizeSum >= int.MaxValue) + { + throw new Exception($"too fig file"); + } + else + { + blocksStream = new MemoryStream((int)uncompressedSizeSum); + } + return blocksStream; + } + + public void ReadFiles(Stream blocksStream) + { + fileList = new StreamFile[m_DirectoryInfo.Length]; + for (int i = 0; i < m_DirectoryInfo.Length; i++) + { + var node = m_DirectoryInfo[i]; + var file = new StreamFile(); + fileList[i] = file; + file.path = node.path; + file.fileName = Path.GetFileName(node.path); + if (node.size >= int.MaxValue) + { + throw new Exception($"exceed max file size"); + /*var memoryMappedFile = MemoryMappedFile.CreateNew(null, entryinfo_size); + file.stream = memoryMappedFile.CreateViewStream();*/ + //var extractPath = path + "_unpacked" + Path.DirectorySeparatorChar; + //Directory.CreateDirectory(extractPath); + //file.stream = new FileStream(extractPath + file.fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite); + } + file.stream = new MemoryStream((int)node.size); + blocksStream.Position = node.offset; + blocksStream.CopyTo(file.stream, node.size); + file.stream.Position = 0; + } + } + + private void ReadBlocks(EndianBinaryReader reader, Stream blocksStream) + { + foreach (var blockInfo in m_BlocksInfo) + { + var compressedSize = (int)blockInfo.compressedSize; + byte[] compressedBlockBytes = reader.ReadBytes(compressedSize); + var compressionType = (CompressionType)(blockInfo.flags & StorageBlockFlags.CompressionTypeMask); + byte[] uncompressedBlockBytes = DecompressBytes(compressionType, compressedBlockBytes, blockInfo.uncompressedSize); + blocksStream.Write(uncompressedBlockBytes, 0, uncompressedBlockBytes.Length); + } + blocksStream.Position = 0; + } + } +} diff --git a/Plugins/UnityFS/BundleFileReader.cs.meta b/Plugins/UnityFS/BundleFileReader.cs.meta new file mode 100644 index 0000000..8b3cfae --- /dev/null +++ b/Plugins/UnityFS/BundleFileReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f9b938458cc610e4d8a910c4499693cf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/BundleFileWriter.cs b/Plugins/UnityFS/BundleFileWriter.cs new file mode 100644 index 0000000..7e039cb --- /dev/null +++ b/Plugins/UnityFS/BundleFileWriter.cs @@ -0,0 +1,112 @@ +using LZ4; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace UnityFS +{ + public class BundleFileWriter + { + private readonly BundleFileInfo _bundle; + + private readonly List _files = new List(); + private readonly List _blocks = new List(); + + private readonly EndianBinaryWriter _blockDirectoryMetadataStream = new EndianBinaryWriter(new MemoryStream()); + private byte[] _blockBytes; + + public BundleFileWriter(BundleFileInfo bundle) + { + _bundle = bundle; + } + + public void Write(EndianBinaryWriter output) + { + InitBlockAndDirectories(); + + output.WriteNullEndString(_bundle.signature); + output.Write(_bundle.version); + output.WriteNullEndString(_bundle.unityVersion); + output.WriteNullEndString(_bundle.unityRevision); + + BuildBlockDirectoryMetadata(); + + + long sizePos = output.Position; + output.Write(0L); + output.Write((uint)_blockDirectoryMetadataStream.Length); + output.Write((uint)_blockDirectoryMetadataStream.Length); + ArchiveFlags flags = ArchiveFlags.BlocksAndDirectoryInfoCombined | (uint)CompressionType.None; + output.Write((uint)flags); + + if (_bundle.version >= 7) + { + output.AlignStream(16); + } + byte[] metadataBytes = _blockDirectoryMetadataStream.BaseStream.ReadAllBytes(); + output.Write(metadataBytes, 0, metadataBytes.Length); + + byte[] dataBytes = _blockBytes; + output.Write(dataBytes, 0, dataBytes.Length); + + output.Position = sizePos; + output.Write(output.Length); + } + + private void InitBlockAndDirectories() + { + var dataStream = new MemoryStream(); + foreach(var file in _bundle.files) + { + byte[] data = file.data; + _files.Add(new Node { path = file.file, flags = 0, offset = dataStream.Length, size = data.LongLength }); + dataStream.Write(data, 0, data.Length); + } + byte[] dataBytes = dataStream.ToArray(); + + var compressedBlockStream = new MemoryStream(dataBytes.Length / 2); + int blockByteSize = 128 * 1024; + long dataSize = dataBytes.Length; + byte[] tempCompressBlock = new byte[blockByteSize * 2]; + for(long i = 0, blockNum = (dataSize + blockByteSize - 1) / blockByteSize; i < blockNum; i++) + { + long curBlockSize = Math.Min(dataSize, blockByteSize); + dataSize -= curBlockSize; + + int compressedSize = LZ4Codec.Encode(dataBytes, (int)(i * blockByteSize), (int)curBlockSize, tempCompressBlock, 0, tempCompressBlock.Length); + compressedBlockStream.Write(tempCompressBlock, 0, compressedSize); + _blocks.Add(new StorageBlock { flags = (StorageBlockFlags)(int)CompressionType.Lz4, compressedSize = (uint)compressedSize, uncompressedSize = (uint)curBlockSize }); + //Debug.Log($"== block[{i}] uncompressedSize:{curBlockSize} compressedSize:{compressedSize} totalblocksize:{compressedBlockStream.Length}"); + } + _blockBytes = compressedBlockStream.ToArray(); + } + + private void BuildBlockDirectoryMetadata() + { + var hash = new byte[16]; + _blockDirectoryMetadataStream.Write(hash, 0, 16); + + _blockDirectoryMetadataStream.Write((uint)_blocks.Count); + foreach(var b in _blocks) + { + _blockDirectoryMetadataStream.Write(b.uncompressedSize); + _blockDirectoryMetadataStream.Write(b.compressedSize); + _blockDirectoryMetadataStream.Write((ushort)b.flags); + } + + _blockDirectoryMetadataStream.Write((uint)_files.Count); + foreach(var f in _files) + { + _blockDirectoryMetadataStream.Write(f.offset); + _blockDirectoryMetadataStream.Write(f.size); + _blockDirectoryMetadataStream.Write(f.flags); + _blockDirectoryMetadataStream.WriteNullEndString(f.path); + } + //Debug.Log($"block and directory metadata size:{_blockDirectoryMetadataStream.Length}"); + } + } +} diff --git a/Plugins/UnityFS/BundleFileWriter.cs.meta b/Plugins/UnityFS/BundleFileWriter.cs.meta new file mode 100644 index 0000000..98d7466 --- /dev/null +++ b/Plugins/UnityFS/BundleFileWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d6705163b267de54a868f5e84f6c7024 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/CompressionType.cs b/Plugins/UnityFS/CompressionType.cs new file mode 100644 index 0000000..032103b --- /dev/null +++ b/Plugins/UnityFS/CompressionType.cs @@ -0,0 +1,11 @@ +namespace UnityFS +{ + public enum CompressionType + { + None, + Lzma, + Lz4, + Lz4HC, + Lzham + } +} diff --git a/Plugins/UnityFS/CompressionType.cs.meta b/Plugins/UnityFS/CompressionType.cs.meta new file mode 100644 index 0000000..ce8a30f --- /dev/null +++ b/Plugins/UnityFS/CompressionType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ddcd6644c83d2a94f9668d6e913bd80e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/EndianBinaryReader.cs b/Plugins/UnityFS/EndianBinaryReader.cs new file mode 100644 index 0000000..d6716a8 --- /dev/null +++ b/Plugins/UnityFS/EndianBinaryReader.cs @@ -0,0 +1,107 @@ +using System; +using System.IO; + +namespace UnityFS +{ + public class EndianBinaryReader : BinaryReader + { + private readonly byte[] buffer; + + public EndianType Endian; + + public EndianBinaryReader(Stream stream, EndianType endian = EndianType.BigEndian) : base(stream) + { + Endian = endian; + buffer = new byte[8]; + } + + public long Position + { + get => BaseStream.Position; + set => BaseStream.Position = value; + } + + private unsafe void ReadBufferBigEndian(byte* dst, byte[] src, int size) + { + System.Diagnostics.Debug.Assert(BitConverter.IsLittleEndian); + for (int i = 0; i < size; i++) + { + dst[i] = src[size - i - 1]; + } + } + + public override short ReadInt16() + { + return (short)ReadUInt16(); + } + + public unsafe override ushort ReadUInt16() + { + if (Endian == EndianType.BigEndian) + { + Read(buffer, 0, 2); + ushort x = 0; + ReadBufferBigEndian((byte*)&x, buffer, 2); + return x; + } + return base.ReadUInt16(); + } + + public override int ReadInt32() + { + return (int)ReadUInt32(); + } + + public unsafe override uint ReadUInt32() + { + if (Endian == EndianType.BigEndian) + { + Read(buffer, 0, 4); + uint x = 0; + ReadBufferBigEndian((byte*)&x, buffer, 4); + return x; + } + return base.ReadUInt32(); + } + + public override long ReadInt64() + { + return (long)ReadUInt64(); + } + + public unsafe override ulong ReadUInt64() + { + if (Endian == EndianType.BigEndian) + { + Read(buffer, 0, 8); + + ulong x = 0; + ReadBufferBigEndian((byte*)&x, buffer, 8); + return x; + } + return base.ReadUInt64(); + } + + public override float ReadSingle() + { + if (Endian == EndianType.BigEndian) + { + Read(buffer, 0, 4); + Array.Reverse(buffer, 0, 4); + return BitConverter.ToSingle(buffer, 0); + } + return base.ReadSingle(); + } + + public override double ReadDouble() + { + if (Endian == EndianType.BigEndian) + { + Read(buffer, 0, 8); + Array.Reverse(buffer); + return BitConverter.ToDouble(buffer, 0); + } + return base.ReadDouble(); + } + } +} diff --git a/Plugins/UnityFS/EndianBinaryReader.cs.meta b/Plugins/UnityFS/EndianBinaryReader.cs.meta new file mode 100644 index 0000000..53183c0 --- /dev/null +++ b/Plugins/UnityFS/EndianBinaryReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9b02c037f0fa1014da65773804248d8d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/EndianBinaryWriter.cs b/Plugins/UnityFS/EndianBinaryWriter.cs new file mode 100644 index 0000000..03938a9 --- /dev/null +++ b/Plugins/UnityFS/EndianBinaryWriter.cs @@ -0,0 +1,107 @@ +using System; +using System.IO; + +namespace UnityFS +{ + public class EndianBinaryWriter : BinaryWriter + { + private readonly byte[] buffer; + + public EndianType Endian; + + public EndianBinaryWriter(Stream stream, EndianType endian = EndianType.BigEndian) : base(stream) + { + Endian = endian; + buffer = new byte[8]; + } + + public long Position + { + get => BaseStream.Position; + set => BaseStream.Position = value; + } + + public long Length => BaseStream.Length; + + public override void Write(short x) + { + Write((ushort)x); + } + + private unsafe void WriteBufferBigEndian(byte[] dst, byte* src, int size) + { + System.Diagnostics.Debug.Assert(BitConverter.IsLittleEndian); + for(int i = 0; i < size; i++) + { + dst[i] = src[size - i - 1]; + } + } + + public unsafe override void Write(ushort x) + { + if (Endian == EndianType.BigEndian) + { + WriteBufferBigEndian(buffer, (byte*)&x, 2); + Write(buffer, 0, 2); + return; + } + base.Write(x); + } + + public override void Write(int x) + { + Write((uint)x); + } + + public unsafe override void Write(uint x) + { + if (Endian == EndianType.BigEndian) + { + WriteBufferBigEndian(buffer, (byte*)&x, 4); + Write(buffer, 0, 4); + return; + } + base.Write(x); + } + + public override void Write(long x) + { + Write((ulong)x); + } + + public unsafe override void Write(ulong x) + { + if (Endian == EndianType.BigEndian) + { + WriteBufferBigEndian(buffer, (byte*)&x, 8); + Write(buffer, 0, 8); + return; + } + base.Write(x); + } + + public override void Write(float x) + { + if (Endian == EndianType.BigEndian) + { + var buf = BitConverter.GetBytes(x); + Array.Reverse(buf, 0, 4); + Write(buf, 0, 4); + return; + } + base.Write(x); + } + + public override void Write(double x) + { + if (Endian == EndianType.BigEndian) + { + var buf = BitConverter.GetBytes(x); + Array.Reverse(buf, 0, 8); + Write(buf, 0, 8); + return; + } + base.Write(x); + } + } +} diff --git a/Plugins/UnityFS/EndianBinaryWriter.cs.meta b/Plugins/UnityFS/EndianBinaryWriter.cs.meta new file mode 100644 index 0000000..cff8503 --- /dev/null +++ b/Plugins/UnityFS/EndianBinaryWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4107364c7434b2042ad647b28e322513 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/EndianType.cs b/Plugins/UnityFS/EndianType.cs new file mode 100644 index 0000000..53e740f --- /dev/null +++ b/Plugins/UnityFS/EndianType.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace UnityFS +{ + public enum EndianType + { + LittleEndian, + BigEndian + } +} diff --git a/Plugins/UnityFS/EndianType.cs.meta b/Plugins/UnityFS/EndianType.cs.meta new file mode 100644 index 0000000..c878edc --- /dev/null +++ b/Plugins/UnityFS/EndianType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2f6cbab4506c18248b410a164be891d2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/GlobalgamedatasPatcher.cs b/Plugins/UnityFS/GlobalgamedatasPatcher.cs new file mode 100644 index 0000000..115ccf9 --- /dev/null +++ b/Plugins/UnityFS/GlobalgamedatasPatcher.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using UnityFS; + +namespace HybridCLR.Editor.UnityBinFileReader +{ + public class Dataunity3dPatcher + { + + public void ApplyPatch(string dataunity3dFile, List hotUpdateAssemblies) + { + var reader = new BundleFileReader(); + using (var fs = new EndianBinaryReader(new MemoryStream(File.ReadAllBytes(dataunity3dFile)))) + { + reader.Load(fs); + } + + var info = reader.CreateBundleFileInfo(); + //Debug.Log($"name:{info.signature} version:{info.version} files:{info.files.Count}"); + //foreach (var file in info.files) + //{ + // Debug.Log($"file:{file.file} size:{file.data.Length}"); + //} + + var globalgamemanagersFile = info.files.Find(f => f.file == "globalgamemanagers"); + //Debug.LogFormat("gobalgamemanagers origin size:{0}", globalgamemanagersFile.data.Length); + + var ggdBinFile = new UnityBinFile(); + ggdBinFile.LoadFromStream(new MemoryStream(globalgamemanagersFile.data)); + ggdBinFile.AddScriptingAssemblies(hotUpdateAssemblies); + byte[] patchedGlobalgamedatasBytes = ggdBinFile.CreatePatchedBytes(); + //Debug.LogFormat("gobalgamemanagers post patche size:{0}", patchedGlobalgamedatasBytes.Length); + globalgamemanagersFile.data = patchedGlobalgamedatasBytes; + + var writer = new BundleFileWriter(info); + var output = new MemoryStream(); + writer.Write(new EndianBinaryWriter(output)); + Debug.Log($"patch file:{dataunity3dFile} size:{output.Length}"); + + string bakFile = dataunity3dFile + ".bak"; + if (!File.Exists(bakFile)) + { + File.Copy(dataunity3dFile, bakFile); + } + File.WriteAllBytes(dataunity3dFile, output.ToArray()); + } + } +} diff --git a/Plugins/UnityFS/GlobalgamedatasPatcher.cs.meta b/Plugins/UnityFS/GlobalgamedatasPatcher.cs.meta new file mode 100644 index 0000000..b33d83c --- /dev/null +++ b/Plugins/UnityFS/GlobalgamedatasPatcher.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 653a22d285c79f44a8113c5571b2d26b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/Header.cs b/Plugins/UnityFS/Header.cs new file mode 100644 index 0000000..2715cce --- /dev/null +++ b/Plugins/UnityFS/Header.cs @@ -0,0 +1,14 @@ +namespace UnityFS +{ + public class Header + { + public string signature; + public uint version; + public string unityVersion; + public string unityRevision; + public long size; + public uint compressedBlocksInfoSize; + public uint uncompressedBlocksInfoSize; + public ArchiveFlags flags; + } +} diff --git a/Plugins/UnityFS/Header.cs.meta b/Plugins/UnityFS/Header.cs.meta new file mode 100644 index 0000000..e5a516a --- /dev/null +++ b/Plugins/UnityFS/Header.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f121e0520fa65c240884d43fd00b3c2a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/Node.cs b/Plugins/UnityFS/Node.cs new file mode 100644 index 0000000..d9cfd97 --- /dev/null +++ b/Plugins/UnityFS/Node.cs @@ -0,0 +1,10 @@ +namespace UnityFS +{ + public class Node + { + public long offset; + public long size; + public uint flags; + public string path; + } +} diff --git a/Plugins/UnityFS/Node.cs.meta b/Plugins/UnityFS/Node.cs.meta new file mode 100644 index 0000000..0736374 --- /dev/null +++ b/Plugins/UnityFS/Node.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e3eea8a6a32b6ac4ba609b39715e25e2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/ScriptingAssembliesJsonPatcher.cs b/Plugins/UnityFS/ScriptingAssembliesJsonPatcher.cs new file mode 100644 index 0000000..71b9ab3 --- /dev/null +++ b/Plugins/UnityFS/ScriptingAssembliesJsonPatcher.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace UnityFS +{ + public class ScriptingAssembliesJsonPatcher + { + [Serializable] + private class ScriptingAssemblies + { + public List names; + public List types; + } + + private string _file; + ScriptingAssemblies _scriptingAssemblies; + + public void Load(string file) + { + _file = file; + string content = File.ReadAllText(file); + _scriptingAssemblies = JsonUtility.FromJson(content); + } + + public void AddScriptingAssemblies(List assemblies) + { + foreach (string name in assemblies) + { + if (!_scriptingAssemblies.names.Contains(name)) + { + _scriptingAssemblies.names.Add(name); + _scriptingAssemblies.types.Add(16); // user dll type + Debug.Log($"[PatchScriptAssembliesJson] add hotfix assembly:{name} to {_file}"); + } + } + } + + public void Save(string jsonFile) + { + string content = JsonUtility.ToJson(_scriptingAssemblies); + + File.WriteAllText(jsonFile, content); + } + } +} diff --git a/Plugins/UnityFS/ScriptingAssembliesJsonPatcher.cs.meta b/Plugins/UnityFS/ScriptingAssembliesJsonPatcher.cs.meta new file mode 100644 index 0000000..c4c07e0 --- /dev/null +++ b/Plugins/UnityFS/ScriptingAssembliesJsonPatcher.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4455f7304f8678f408dd6cf21734f55e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/SevenZipHelper.cs b/Plugins/UnityFS/SevenZipHelper.cs new file mode 100644 index 0000000..2d17669 --- /dev/null +++ b/Plugins/UnityFS/SevenZipHelper.cs @@ -0,0 +1,49 @@ +using System; +using System.IO; +using SevenZip.Compression.LZMA; + + +namespace UnityFS +{ + public static class ComparessHelper + { + public static MemoryStream Decompress7Zip(MemoryStream inStream) + { + var decoder = new Decoder(); + + inStream.Seek(0, SeekOrigin.Begin); + var newOutStream = new MemoryStream(); + + var properties = new byte[5]; + if (inStream.Read(properties, 0, 5) != 5) + throw new Exception("input .lzma is too short"); + long outSize = 0; + for (var i = 0; i < 8; i++) + { + var v = inStream.ReadByte(); + if (v < 0) + throw new Exception("Can't Read 1"); + outSize |= ((long)(byte)v) << (8 * i); + } + decoder.SetDecoderProperties(properties); + + var compressedSize = inStream.Length - inStream.Position; + decoder.Code(inStream, newOutStream, compressedSize, outSize, null); + + newOutStream.Position = 0; + return newOutStream; + } + + public static void Decompress7Zip(Stream compressedStream, Stream decompressedStream, long compressedSize, long decompressedSize) + { + var basePosition = compressedStream.Position; + var decoder = new Decoder(); + var properties = new byte[5]; + if (compressedStream.Read(properties, 0, 5) != 5) + throw new Exception("input .lzma is too short"); + decoder.SetDecoderProperties(properties); + decoder.Code(compressedStream, decompressedStream, compressedSize - 5, decompressedSize, null); + compressedStream.Position = basePosition + compressedSize; + } + } +} diff --git a/Plugins/UnityFS/SevenZipHelper.cs.meta b/Plugins/UnityFS/SevenZipHelper.cs.meta new file mode 100644 index 0000000..77f375b --- /dev/null +++ b/Plugins/UnityFS/SevenZipHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6606a654e10b3ba48b76b566b903b353 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/StorageBlock.cs b/Plugins/UnityFS/StorageBlock.cs new file mode 100644 index 0000000..148c4d1 --- /dev/null +++ b/Plugins/UnityFS/StorageBlock.cs @@ -0,0 +1,9 @@ +namespace UnityFS +{ + public class StorageBlock + { + public uint compressedSize; + public uint uncompressedSize; + public StorageBlockFlags flags; + } +} diff --git a/Plugins/UnityFS/StorageBlock.cs.meta b/Plugins/UnityFS/StorageBlock.cs.meta new file mode 100644 index 0000000..e1e8ad9 --- /dev/null +++ b/Plugins/UnityFS/StorageBlock.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 40dc58bec5631f14c9c17c8a486496d4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/StorageBlockFlags.cs b/Plugins/UnityFS/StorageBlockFlags.cs new file mode 100644 index 0000000..619fcac --- /dev/null +++ b/Plugins/UnityFS/StorageBlockFlags.cs @@ -0,0 +1,11 @@ +using System; + +namespace UnityFS +{ + [Flags] + public enum StorageBlockFlags + { + CompressionTypeMask = 0x3f, + Streamed = 0x40 + } +} diff --git a/Plugins/UnityFS/StorageBlockFlags.cs.meta b/Plugins/UnityFS/StorageBlockFlags.cs.meta new file mode 100644 index 0000000..d3f199d --- /dev/null +++ b/Plugins/UnityFS/StorageBlockFlags.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 79b9ed6799d3caf459cf2dfae5765a23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/StreamExtensions.cs b/Plugins/UnityFS/StreamExtensions.cs new file mode 100644 index 0000000..ecffd07 --- /dev/null +++ b/Plugins/UnityFS/StreamExtensions.cs @@ -0,0 +1,32 @@ +using System.IO; + +namespace UnityFS +{ + public static class StreamExtensions + { + private const int BufferSize = 81920; + + public static void CopyTo(this Stream source, Stream destination, long size) + { + var buffer = new byte[BufferSize]; + for (var left = size; left > 0; left -= BufferSize) + { + int toRead = BufferSize < left ? BufferSize : (int)left; + int read = source.Read(buffer, 0, toRead); + destination.Write(buffer, 0, read); + if (read != toRead) + { + return; + } + } + } + + public static byte[] ReadAllBytes(this Stream source) + { + source.Position = 0; + var bytes = new byte[source.Length]; + source.Read(bytes, 0, bytes.Length); + return bytes; + } + } +} diff --git a/Plugins/UnityFS/StreamExtensions.cs.meta b/Plugins/UnityFS/StreamExtensions.cs.meta new file mode 100644 index 0000000..a06440e --- /dev/null +++ b/Plugins/UnityFS/StreamExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2262fbf5672028a48b0c63821d7ff0c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/StreamFile.cs b/Plugins/UnityFS/StreamFile.cs new file mode 100644 index 0000000..0fffc06 --- /dev/null +++ b/Plugins/UnityFS/StreamFile.cs @@ -0,0 +1,11 @@ +using System.IO; + +namespace UnityFS +{ + public class StreamFile + { + public string path; + public string fileName; + public Stream stream; + } +} diff --git a/Plugins/UnityFS/StreamFile.cs.meta b/Plugins/UnityFS/StreamFile.cs.meta new file mode 100644 index 0000000..7c14e8d --- /dev/null +++ b/Plugins/UnityFS/StreamFile.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fad7df04825c947489aad0d5d0c191a7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/UnityBinFile.cs b/Plugins/UnityFS/UnityBinFile.cs new file mode 100644 index 0000000..ad4ad5f --- /dev/null +++ b/Plugins/UnityFS/UnityBinFile.cs @@ -0,0 +1,124 @@ +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using System.Text; +using System.Reflection; +using System; +using System.Linq; + +namespace UnityFS +{ + /// + /// Unity 生成的二进制文件(本代码不支持5.x之前的版本) + /// + public unsafe class UnityBinFile + { + /* + * MonoManager: idx: 6; + * type: metaData.types[objects[6].typeID] + */ + public const int kMonoManagerIdx = 6; + + public FileHeader header; + public MetaData metaData; + public ScriptsData scriptsData; + + private Stream _originStream; + + public void LoadFromStream(Stream source) + { + _originStream = source; + using (var br = new BinaryReader(source, Encoding.UTF8, true)) + { + header.LoadFromStream(br); + // 按理说 metaData 应该新开一个buffer来避免加载时的对齐逻辑问题,但由于 sizeof(Header) = 20,已经对齐到4了,所以可以连续读 + metaData.LoadFromStream(br, header.dataOffset); + scriptsData = metaData.GetScriptData(br); + } + } + + public void Load(string path) + { + LoadFromStream(new MemoryStream(File.ReadAllBytes(path))); + } + + public void AddScriptingAssemblies(List assemblies) + { + foreach (string name in assemblies) + { + if (!scriptsData.dllNames.Contains(name)) + { + scriptsData.dllNames.Add(name); + scriptsData.dllTypes.Add(16); // user dll type + Debug.Log($"[PatchScriptAssembliesJson] add dll:{name} to globalgamemanagers"); + } + } + } + + public byte[] CreatePatchedBytes() + { + var fsR = _originStream; + fsR.Position = 0; + var brR = new BinaryReader(fsR, Encoding.UTF8, true); + + var ms = new MemoryStream((int)(header.fileSize * 1.5f)); + var bw = new BinaryWriter(ms, Encoding.UTF8, true); + + /* + * 开始写入data + * dll名称列表存储于 data 区段,修改其数据并不会影响 MetaData 大小,因此 dataOffset 不会改变 + */ + ms.Position = header.dataOffset; + + Dictionary newObjInfos = new Dictionary(); + foreach (var kv in metaData.objects) + { + long objID = kv.Key; + ObjectInfo objInfo = kv.Value; + + byte[] buff = new byte[objInfo.size]; + fsR.Position = objInfo.realPos; + brR.Read(buff, 0, buff.Length); + + + {// unity 的数据偏移貌似会对齐到 8 + int newPos = (((int)ms.Position + 7) >> 3) << 3; + int gapSize = newPos - (int)ms.Position; + + for (int i = 0; i < gapSize; i++) + bw.Write((byte)0); + + objInfo.dataPos = (uint)ms.Position - header.dataOffset; // 重定位数据偏移 + } + + if (objID != kMonoManagerIdx) + bw.Write(buff, 0, buff.Length); + else + objInfo.size = (uint)scriptsData.SaveToStream(bw); + + newObjInfos.Add(objID, objInfo); + } + + metaData.objects = newObjInfos; + header.fileSize = (uint)ms.Position; + + ms.Position = 0; + header.SaveToStream(bw); + metaData.SaveToStream(bw); + + brR.Close(); + + // 写入新文件 + ms.Position = 0; + return ms.ToArray(); + } + + public void Save(string newPath) + { + byte[] patchedBytes = CreatePatchedBytes(); + File.WriteAllBytes(newPath, patchedBytes); + } + } + +} diff --git a/Plugins/UnityFS/UnityBinFile.cs.meta b/Plugins/UnityFS/UnityBinFile.cs.meta new file mode 100644 index 0000000..e53b742 --- /dev/null +++ b/Plugins/UnityFS/UnityBinFile.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ae7ec6e3674077d46898fe821d24bf85 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/UnityBinFileDefines.cs b/Plugins/UnityFS/UnityBinFileDefines.cs new file mode 100644 index 0000000..637dc4e --- /dev/null +++ b/Plugins/UnityFS/UnityBinFileDefines.cs @@ -0,0 +1,397 @@ +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using static UnityFS.UnityBinUtils; + +namespace UnityFS +{ + public struct FileHeader + { + public const int kSize = 20; + + public uint dataSize => fileSize - metadataSize; + + public uint metadataSize; + public uint fileSize; + public uint version; + public uint dataOffset; + public byte endianess; + + public void LoadFromStream(BinaryReader br) + { + long startPos = br.BaseStream.Position; + metadataSize = br.ReadUInt32(); + fileSize = br.ReadUInt32(); + version = br.ReadUInt32(); + dataOffset = br.ReadUInt32(); + endianess = br.ReadByte(); + br.BaseStream.Position = startPos + kSize; + + SwapEndianess(); + } + + public long SaveToStream(BinaryWriter bw) + { + SwapEndianess(); + + long startPos = bw.BaseStream.Position; + bw.Write(metadataSize); + bw.Write(fileSize); + bw.Write(version); + bw.Write(dataOffset); + bw.Write(endianess); + bw.BaseStream.Position = startPos + kSize; + return kSize; + } + + void SwapEndianess() + { + SwapUInt(ref metadataSize); + SwapUInt(ref fileSize); + SwapUInt(ref version); + SwapUInt(ref dataOffset); + } + } + + public struct MetaData + { + public long dataStartPos; + + public string version; + public uint platform; + public bool enableTypeTree; + public int typeCount; + public ObjectType[] types; + public int objectCount; + public Dictionary objects; + public int scriptTypeCount; + public ScriptType[] scriptTypes; + public int externalsCount; + public ExternalInfo[] externals; + +#if UNITY_2019_2_OR_NEWER + public int refTypeCount; + public ObjectType[] refTypes; +#endif + public string dummyStr; + + public void LoadFromStream(BinaryReader br, uint dataOffset) + { + long startPos = br.BaseStream.Position; + dataStartPos = startPos; + + version = br.ReadRawString(); + platform = br.ReadUInt32(); + enableTypeTree = br.ReadBoolean(); + typeCount = br.ReadInt32(); + types = new ObjectType[typeCount]; + + for (int i = 0; i < typeCount; i++) + { + types[i].LoadFromStream(br); + } + + objectCount = br.ReadInt32(); + objects = new Dictionary(); + for(int i = 0; i < objectCount; i++) + { + long id = br.AlignedReadInt64(); + ObjectInfo objInfo = new ObjectInfo(); + objInfo.LoadFromStream(br); + objInfo.realPos = objInfo.dataPos + dataOffset; + + objects.Add(id, objInfo); + } + + scriptTypeCount = br.ReadInt32(); + scriptTypes = new ScriptType[scriptTypeCount]; + for(int i = 0; i < scriptTypeCount; i++) + { + scriptTypes[i].LoadFromStream(br); + } + + externalsCount = br.ReadInt32(); + externals = new ExternalInfo[externalsCount]; + for(int i = 0; i < externalsCount; i++) + { + externals[i].LoadFromStream(br); + } + +#if UNITY_2019_2_OR_NEWER + refTypeCount = br.ReadInt32(); + refTypes = new ObjectType[refTypeCount]; + for(int i = 0; i < refTypeCount; i++) + { + refTypes[i].LoadFromStream(br); + } +#endif + dummyStr = br.ReadRawString(); + } + + public long SaveToStream(BinaryWriter bw) + { + long startPos = bw.BaseStream.Position; + bw.WriteRawString(version); + bw.Write(platform); + bw.Write(enableTypeTree); + + bw.Write(typeCount); + foreach(var type in types) + type.SaveToStream(bw); + + bw.Write(objectCount); + foreach (var kv in objects) + { + bw.AlignedWriteInt64(kv.Key); + kv.Value.SaveToStream(bw); + } + + bw.Write(scriptTypeCount); + foreach(var st in scriptTypes) + st.SaveToStream(bw); + + bw.Write(externalsCount); + foreach(var external in externals) + external.SaveToStream(bw); + +#if UNITY_2019_2_OR_NEWER + bw.Write(refTypeCount); + foreach(var refT in refTypes) + refT.SaveToStream(bw); +#endif + + bw.WriteRawString(dummyStr); + + return bw.BaseStream.Position - startPos; + } + + public ScriptsData GetScriptData(BinaryReader br) + { + ObjectInfo objInfo = objects[UnityBinFile.kMonoManagerIdx]; + br.BaseStream.Seek(objInfo.realPos, SeekOrigin.Begin); + + ScriptsData data = new ScriptsData(); + data.LoadFromStream(br); + return data; + } + } + + public struct ObjectType + { + public int typeID; + public bool isStriped; + public short scriptTypeIndex; + + public bool needReadScriptHash; // dont save + + public Hash scriptSigHash; + public Hash typeHash; + + public void LoadFromStream(BinaryReader br) + { + typeID = br.ReadInt32(); + isStriped = br.ReadBoolean(); + scriptTypeIndex = br.ReadInt16(); + + needReadScriptHash = typeID == -1 || typeID == 0x72; + if(needReadScriptHash) + scriptSigHash.LoadFromStream(br); + + typeHash.LoadFromStream(br); + + // GlobalManagers does not has TypeTrees + } + + public long SaveToStream(BinaryWriter bw) + { + long startPos = bw.BaseStream.Position; + bw.Write(typeID); + bw.Write(isStriped); + bw.Write(scriptTypeIndex); + + if(needReadScriptHash) + scriptSigHash.SaveToStream(bw); + + typeHash.SaveToStream(bw); + return bw.BaseStream.Position - startPos; + } + + public int Size() + { + int ret = 0; + ret += sizeof(int); + ret += sizeof(bool); + ret += sizeof(short); + + if (needReadScriptHash) + ret += Hash.kSize; + + ret += Hash.kSize; + return ret; + } + } + + public struct ObjectInfo + { + public const int kSize = 12; + + public uint dataPos; + public uint size; + public uint typeID; + + public uint realPos; // dataPos + Header.dataOffset; // dont save + + public void LoadFromStream(BinaryReader br) + { + dataPos = br.ReadUInt32(); + size = br.ReadUInt32(); + typeID = br.ReadUInt32(); + } + + public long SaveToStream(BinaryWriter bw) + { + bw.Write(dataPos); + bw.Write(size); + bw.Write(typeID); + return kSize; + } + } + + public struct ScriptType + { + public int localFileIndex; + public long localIdentifierOfBin; + + public void LoadFromStream(BinaryReader br) + { + localFileIndex = br.ReadInt32(); + localIdentifierOfBin = br.AlignedReadInt64(); + } + + public long SaveToStream(BinaryWriter bw) + { + long startPos = bw.BaseStream.Position; + bw.Write(localFileIndex); + bw.AlignedWriteInt64(localIdentifierOfBin); + return bw.BaseStream.Position - startPos; + } + } + + public struct ExternalInfo + { + public string dummy; + public Hash guid; + public int type; + public string name; + + public void LoadFromStream(BinaryReader br) + { + dummy = br.ReadRawString(); + guid.LoadFromStream(br); + type = br.ReadInt32(); + name = br.ReadRawString(); + } + + public long SaveToStream(BinaryWriter bw) + { + long startPos = bw.BaseStream.Position; + bw.WriteRawString(dummy); + guid.SaveToStream(bw); + bw.Write(type); + bw.WriteRawString(name); + return bw.BaseStream.Position - startPos; + } + } + + public struct ScriptsData + { + public ScriptID[] scriptIDs; + public List dllNames; + public List dllTypes; // 16 is user type + + public void LoadFromStream(BinaryReader br) + { + { + int count = br.ReadInt32(); + scriptIDs = new ScriptID[count]; + for(int i = 0; i < count; i++) + scriptIDs[i].LoadFromStream(br); + } + { + int count = br.ReadInt32(); + dllNames = new List(count); + for (var i = 0; i < count; i++) + dllNames.Add(br.ReadSizeString()); + } + { + int count = br.ReadInt32(); + dllTypes = new List(count); + for(var i = 0; i < count; i++) + dllTypes.Add(br.ReadInt32()); + } + } + + public long SaveToStream(BinaryWriter bw) + { + long startPos = bw.BaseStream.Position; + bw.Write(scriptIDs.Length); + for(int i = 0; i < scriptIDs.Length; i++) + scriptIDs[i].SaveToStream(bw); + + bw.Write(dllNames.Count); + for(int i = 0, imax = dllNames.Count; i < imax; i++) + bw.WriteSizeString(dllNames[i]); + + bw.Write(dllTypes.Count); + for(int i = 0, imax = dllTypes.Count; i < imax; i++) + bw.Write(dllTypes[i]); + + return bw.BaseStream.Position - startPos; + } + } + + public struct ScriptID + { + public int fileID; + public long pathID; // localIdentifier + + public void LoadFromStream(BinaryReader br) + { + fileID = br.ReadInt32(); + pathID = br.ReadInt64(); + } + + public long SaveToStream(BinaryWriter bw) + { + bw.Write(fileID); + bw.Write(pathID); + return 4 + 8; + } + } + + public struct Hash + { + public const int kSize = 16; + + public int[] data; + + public void LoadFromStream(BinaryReader br) + { + data = new int[4]; + for(int i = 0; i < data.Length; i++) + { + data[i] = br.ReadInt32(); + } + } + + public long SaveToStream(BinaryWriter bw) + { + for(int i = 0; i < data.Length; i++) + { + bw.Write(data[i]); + } + return kSize; + } + } +} diff --git a/Plugins/UnityFS/UnityBinFileDefines.cs.meta b/Plugins/UnityFS/UnityBinFileDefines.cs.meta new file mode 100644 index 0000000..d9192c7 --- /dev/null +++ b/Plugins/UnityFS/UnityBinFileDefines.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 96788c7fe08d5d54d95a87cfbdcb643a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/UnityBinUtils.cs b/Plugins/UnityFS/UnityBinUtils.cs new file mode 100644 index 0000000..edeefb3 --- /dev/null +++ b/Plugins/UnityFS/UnityBinUtils.cs @@ -0,0 +1,78 @@ +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using System.Text; + +namespace UnityFS +{ + public static class UnityBinUtils + { + public static void SwapUInt(ref uint val) + { + val = (val >> 24) | ((val >> 8) & 0x0000ff00) | ((val << 8) & 0x00ff0000) | (val << 24); + } + + public static string ReadRawString(this BinaryReader br) + { + long startPos = br.BaseStream.Position; + while (true) + { + byte val = br.ReadByte(); + if(val == 0) + break; + } + int size = (int)(br.BaseStream.Position - startPos); + br.BaseStream.Position = startPos; + + byte[] buffer = br.ReadBytes(size); + string ret = Encoding.UTF8.GetString(buffer, 0, size - 1); + + return ret; + } + + public static void WriteRawString(this BinaryWriter bw, string str) + { + byte[] buffer = Encoding.UTF8.GetBytes(str); + bw.Write(buffer, 0, buffer.Length); + bw.Write((byte)0); + } + + public static string ReadSizeString(this BinaryReader br) + { + int size = br.ReadInt32(); + byte[] buff = br.ReadBytes(size); + br.BaseStream.AlignOffset4(); + + string ret = Encoding.UTF8.GetString(buff); + return ret; + } + + public static void WriteSizeString(this BinaryWriter bw, string str) + { + byte[] buff = Encoding.UTF8.GetBytes(str); + bw.Write(buff.Length); + bw.Write(buff, 0, buff.Length); + bw.BaseStream.AlignOffset4(); + } + + public static void AlignOffset4(this Stream stream) + { + int offset = (((int)stream.Position + 3) >> 2) << 2; + stream.Position = offset; + } + + public static long AlignedReadInt64(this BinaryReader br) + { + br.BaseStream.AlignOffset4(); + return br.ReadInt64(); + } + + public static void AlignedWriteInt64(this BinaryWriter bw, long val) + { + bw.BaseStream.AlignOffset4(); + bw.Write(val); + } + } +} + diff --git a/Plugins/UnityFS/UnityBinUtils.cs.meta b/Plugins/UnityFS/UnityBinUtils.cs.meta new file mode 100644 index 0000000..e0f9bf9 --- /dev/null +++ b/Plugins/UnityFS/UnityBinUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bf7c4cf970660614fb54d838ec6e7eda +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UnityFS/UnityFS.asmdef b/Plugins/UnityFS/UnityFS.asmdef new file mode 100644 index 0000000..144651e --- /dev/null +++ b/Plugins/UnityFS/UnityFS.asmdef @@ -0,0 +1,18 @@ +{ + "name": "UnityFS", + "rootNamespace": "", + "references": [ + "SevenZip" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Plugins/UnityFS/UnityFS.asmdef.meta b/Plugins/UnityFS/UnityFS.asmdef.meta new file mode 100644 index 0000000..c7dc14f --- /dev/null +++ b/Plugins/UnityFS/UnityFS.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ec7e894280a62fb439cae3af161d18c3 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/dnlib.dll b/Plugins/dnlib.dll new file mode 100644 index 0000000..170ffde Binary files /dev/null and b/Plugins/dnlib.dll differ diff --git a/Plugins/dnlib.dll.meta b/Plugins/dnlib.dll.meta new file mode 100644 index 0000000..945aef3 --- /dev/null +++ b/Plugins/dnlib.dll.meta @@ -0,0 +1,87 @@ +fileFormatVersion: 2 +guid: 97dc0bbfed2593247a18261f1c2fdf66 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude WebGL: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/README.md b/README.md index 4768061..8f9180e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ -# hybridclr_upm -upm for HybridCLR +# hybridclr_unity +Unity Package for HybridCLR + + diff --git a/README.md.meta b/README.md.meta new file mode 100644 index 0000000..b674416 --- /dev/null +++ b/README.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b4b6051e2483d664facc72a5102dcffc +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime.meta b/Runtime.meta new file mode 100644 index 0000000..d5e99d3 --- /dev/null +++ b/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a6d5e365e1b7d9742bee023ea54b31f2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/HybridCLR.Runtime.asmdef b/Runtime/HybridCLR.Runtime.asmdef new file mode 100644 index 0000000..98f5d3e --- /dev/null +++ b/Runtime/HybridCLR.Runtime.asmdef @@ -0,0 +1,14 @@ +{ + "name": "HybridCLR.Runtime", + "rootNamespace": "", + "references": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Runtime/HybridCLR.Runtime.asmdef.meta b/Runtime/HybridCLR.Runtime.asmdef.meta new file mode 100644 index 0000000..c73a6d4 --- /dev/null +++ b/Runtime/HybridCLR.Runtime.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 13ba8ce62aa80c74598530029cb2d649 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/LoadImageErrorCode.cs b/Runtime/LoadImageErrorCode.cs new file mode 100644 index 0000000..24e0ee6 --- /dev/null +++ b/Runtime/LoadImageErrorCode.cs @@ -0,0 +1,14 @@ + +namespace HybridCLR +{ + public enum LoadImageErrorCode + { + OK = 0, + BAD_IMAGE, // dll 不合法 + NOT_IMPLEMENT, // 不支持的元数据特性 + AOT_ASSEMBLY_NOT_FIND, // 对应的AOT assembly未找到 + HOMOLOGOUS_ONLY_SUPPORT_AOT_ASSEMBLY, // 不能给解释器assembly补充元数据 + HOMOLOGOUS_ASSEMBLY_HAS_LOADED, // 已经补充过了,不能再次补充 + }; +} + diff --git a/Runtime/LoadImageErrorCode.cs.meta b/Runtime/LoadImageErrorCode.cs.meta new file mode 100644 index 0000000..4f77029 --- /dev/null +++ b/Runtime/LoadImageErrorCode.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2c7d5b71981fba643b4c21ed01bcb675 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/RuntimeApi.cs b/Runtime/RuntimeApi.cs new file mode 100644 index 0000000..417c737 --- /dev/null +++ b/Runtime/RuntimeApi.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace HybridCLR +{ + public static class RuntimeApi + { +#if UNITY_STANDALONE_WIN + private const string dllName = "GameAssembly"; +#elif UNITY_IOS || UNITY_STANDALONE_OSX || UNITY_WEBGL + private const string dllName = "__Internal"; +#else + private const string dllName = "il2cpp"; +#endif + + [DllImport(dllName, EntryPoint = "RuntimeApi_LoadMetadataForAOTAssembly")] + public static extern int LoadMetadataForAOTAssembly(IntPtr dllBytes, int dllSize); + + [DllImport(dllName, EntryPoint = "RuntimeApi_GetInterpreterThreadObjectStackSize")] + public static extern int GetInterpreterThreadObjectStackSize(); + + [DllImport(dllName, EntryPoint = "RuntimeApi_SetInterpreterThreadObjectStackSize")] + public static extern void SetInterpreterThreadObjectStackSize(int size); + + [DllImport(dllName, EntryPoint = "RuntimeApi_GetInterpreterThreadFrameStackSize")] + public static extern int GetInterpreterThreadFrameStackSize(); + + [DllImport(dllName, EntryPoint = "RuntimeApi_SetInterpreterThreadFrameStackSize")] + public static extern void SetInterpreterThreadFrameStackSize(int size); + } +} diff --git a/Runtime/RuntimeApi.cs.meta b/Runtime/RuntimeApi.cs.meta new file mode 100644 index 0000000..142d90f --- /dev/null +++ b/Runtime/RuntimeApi.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3c8b35876046d1747ae7d62244ec693f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests.meta b/Tests.meta new file mode 100644 index 0000000..ddf69a7 --- /dev/null +++ b/Tests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2df5b12e9f986254493be40acd36b665 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Editor.meta b/Tests/Editor.meta new file mode 100644 index 0000000..5ee36ac --- /dev/null +++ b/Tests/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c7268273c19288b44a2ac493c8db07cd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/package.json b/package.json new file mode 100644 index 0000000..dffe401 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "com.focus-creative-games.hybridclr_unity", + "version": "0.1.0", + "displayName": "HybridCLR", + "description": "Unity package for HybridCLR. It includes editor and runtime scripts and assets for HybridCLR", + "category": "Runtime", + "documentationUrl": "https://focus-creative-games.github.io/hybridclr/index", + "changelogUrl": "https://github.com/focus-creative-games/hybridclr/commits/main", + "licensesUrl": "https://github.com/focus-creative-games/hybridclr/blob/main/LICENSE", + "keywords": [ + "HybridCLR", + "hotupdate", + "hotfix", + "focus-creative-games" + ], + "author": { + "name": "focus-creative-games", + "email": "hybridclr@focus-creative-games.com", + "url": "https://github.com/focus-creative-games/" + } +} \ No newline at end of file diff --git a/package.json.meta b/package.json.meta new file mode 100644 index 0000000..65ee986 --- /dev/null +++ b/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8ea8eca3a387d9d4988a2fca1036f2e7 +PackageManifestImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: