286 Commits

Author SHA1 Message Date
VaDiM
87151e065b [CLI] Fix for portrait parsing 2025-06-30 00:15:39 +03:00
VaDiM
f3a748ba6b Fix for portrait parsing 2025-06-30 00:03:25 +03:00
VaDiM
d07fc6d6f5 Fix for avg sprites 2025-04-07 16:03:20 +03:00
VaDiM
ee2976aaf1 Update version & dependencies 2025-04-07 13:32:17 +03:00
VaDiM
f144037bc0 Fix for new compression type 2025-04-07 13:23:04 +03:00
VaDiM
0e097bda04 Update README.md 2024-05-01 14:25:55 +03:00
VaDiM
9686eee3b7 Fix for 512x512 avg sprites
temporarily?
2024-04-11 22:30:42 +03:00
VaDiM
b6318e6d9b Fix for empty alpha tex 2024-01-09 11:18:06 +03:00
VaDiM
e0d90d1b1e Update AssetStudioGUI.csproj 2023-12-16 23:17:56 +03:00
VaDiM
dd727758a8 Update ver to v1.1.0 2023-12-16 23:11:27 +03:00
VaDiM
d44ed315e3 Merge branch 'AssetStudioMod' into ArknightsStudio 2023-12-16 22:57:32 +03:00
VaDiM
a00c857ac3 Update version to v0.17.4 2023-12-16 04:41:00 +03:00
VaDiM
5333757843 Update dependencies 2023-12-16 04:16:36 +03:00
VaDiM
4747687bec Update README.md 2023-12-15 15:11:48 +03:00
VaDiM
5c2ac1a5e8 Add .NET8 to the TargetFrameworks 2023-12-15 14:45:07 +03:00
VaDiM
e2eae53ac0 Update Live2DExtractor.cs 2023-12-15 01:24:54 +03:00
VaDiM
87347e8b60 [CLI] Update readme 2023-12-11 01:58:05 +03:00
VaDiM
0fdbddea55 [CLI] Fix asset filter for sprites 2023-12-11 01:58:00 +03:00
VaDiM
823190abb7 Improve parsing of Live2D Fade motions 2023-12-09 02:31:54 +03:00
VaDiM
60aef1b8ed [GUI] Add option to not build a tree structure 2023-12-09 02:25:59 +03:00
VaDiM
f82a73f018 [GUI] Add console logger 2023-12-06 17:28:10 +03:00
VaDiM
d42a1879ab [GUI] Add support for Drag&Drop files from a browser
Co-Authored-By: Luke <17146677+lukefz@users.noreply.github.com>
2023-12-05 00:38:24 +03:00
VaDiM
632e5f8d08 [GUI] Add support for .lnk files via Drag&Drop. Close #13 2023-12-04 04:10:49 +03:00
VaDiM
51d259464b Merge branch 'pr/16' into AssetStudioMod 2023-11-21 02:15:19 +03:00
VaDiM
efd06648ad Add support for separate PreloadData
https://github.com/Perfare/AssetStudio/issues/690

https://docs.unity.cn/550/Documentation/Manual/AssetBundleInternalStructure.html
2023-11-21 01:35:25 +03:00
VaDiM
b27482e22b Don't display SourceRevisionId in titles
https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/8.0/source-link
2023-11-21 00:31:09 +03:00
VaDiM
d572bd0e64 Add support for Live2D Fade motions 2023-11-20 19:56:41 +03:00
VaDiM
e415740373 Some fixes for Live2D export 2023-11-19 02:51:48 +03:00
VaDiM
e30b9e9e89 Fix bug with some avg sprites
- Changed face sprite detection method. We can't rely on the "IsWholeBody" flag
2023-11-08 13:57:57 +03:00
VaDiM
45bf9251c9 Fix crash when SpriteAtlas is missing 2023-11-07 01:31:27 +03:00
VaDiM
9632e88115 Update version to v1.0.1 2023-11-01 06:49:36 +03:00
VaDiM
bb19dd5019 Add support for avg sprite without separate alpha tex 2023-11-01 06:49:36 +03:00
Evaldas Ciakas
75ebe67713 Fix parsing secondary textures 2023-11-01 02:29:06 +02:00
VaDiM
ed7b0a2415 Unity 2022.2+ AnimationClip fix 2023-09-27 23:41:20 +03:00
VaDiM
22ab5c0633 Update README.md
- Added ArknightsStudio link
2023-09-24 16:19:09 +03:00
VaDiM
a2bc935850 [CLI] Improve help message 2023-09-24 16:14:26 +03:00
VaDiM
b6c6ceba1c Update CHANGELOG.md 2023-09-24 15:41:47 +03:00
VaDiM
7c0a6375b1 Update readme files 2023-09-24 15:41:21 +03:00
VaDiM
5c489c5f83 Merge branch 'AssetStudioMod' into ArknightsStudio 2023-09-24 15:40:23 +03:00
VaDiM
cb84c137e5 Add exception checking for gzip decompression 2023-09-24 10:41:19 +03:00
VaDiM
25c611fb9e Update README.md 2023-09-15 16:35:47 +03:00
VaDiM
6d3875cb2c Update version to v0.17.3 2023-09-13 10:55:57 +03:00
VaDiM
be4ced77ea Update readmes 2023-09-12 03:44:18 +03:00
VaDiM
2bd762e8f4 [CLI] Add names for AssetBundle and Animator assets 2023-09-12 03:33:59 +03:00
VaDiM
a926644ff6 [CLI] Add --load-all flag
- Added --load-all flag to load assets of all types
- Improved option grouping on the help screen
2023-09-12 03:33:53 +03:00
VaDiM
a4cdff5934 Fix a bit of incorrect copy-paste
in commit e216abd6be
2023-09-08 23:47:26 +03:00
VaDiM
2e10e627b0 AssemblyLoader fix (#6) 2023-09-08 06:18:41 +03:00
VaDiM
e216abd6be Unity 2022.2+ Texture2D fix 2023-09-08 06:18:34 +03:00
VaDiM
93c7e617d8 [CLI] Show names of all loaded assets in the exported asset list 2023-09-08 06:18:34 +03:00
VaDiM
000916913e [CLI] Fix a bug where the default output folder might not exist 2023-09-08 06:17:34 +03:00
VaDiM
28f9744497 Update README.md 2023-09-08 06:17:34 +03:00
VaDiM
c3f99216b6 [CLI] Add --filter-by-name support for fbx export mode 2023-09-08 01:45:25 +03:00
VaDiM
19c4835ea3 Merge branch 'pr/10' into AssetStudioMod 2023-09-05 19:38:04 +03:00
VaDiM
6b321da695 [CLI] Add FBX native libs (Linux, Mac) 2023-09-05 00:48:44 +03:00
svn
7cca301f7a Add split object fbx export to CLI 2023-08-31 21:07:02 +01:00
svn
5ac597c935 Add CMakeLists for cross platform FBXNative build 2023-08-31 21:03:07 +01:00
VaDiM
e90af43459 Merge branch 'AssetStudioMod' into ArknightsStudio 2023-08-28 02:28:14 +03:00
VaDiM
171962e61f Update version to v0.17.2
- Updated projects and dependencies
2023-08-27 01:47:06 +03:00
VaDiM
c8a21838c9 Some changes to motion list for l2d models
- Motion list is now sorted
- Motions divided into groups (each motion is a separate group)
- Motion names are used as group names
2023-08-27 01:46:06 +03:00
VaDiM
cf67815d53 Minor fixes 2023-08-26 03:22:19 +03:00
VaDiM
381a7d89ae [AK][CLI] Add support for portrait sprites 2023-08-25 01:15:46 +03:00
VaDiM
572e3bf0d6 [AK][GUI] Add support for portrait sprites 2023-08-24 05:50:50 +03:00
VaDiM
3d7d51b54f [AK][CLI] Add support for sprites with an external alpha texture
- Added support for Arknights chararts sprites
- Added support for Arknights avg sprites
2023-08-17 01:22:16 +03:00
VaDiM
abbd27fde7 [AK][GUI] Add support for sprites with an external alpha texture
- Added support for Arknights chararts sprites
- Added support for Arknights avg sprites
2023-08-14 20:25:48 +03:00
VaDiM
94c8b355fe [CLI] Fix asset filter
- Fixed sprite export in sprite only mode
2023-08-14 00:46:27 +03:00
VaDiM
4e41caf203 [CLI] Refactor 2023-08-13 20:58:26 +03:00
VaDiM
74a8555514 Merge branch 'relatedAssets' into AssetStudioMod 2023-08-07 23:20:11 +03:00
VaDiM
e1d883adf6 [CLI] Refactor
- Made some classes static
2023-08-07 23:10:57 +03:00
VaDiM
9784df0e16 [GUI] Add app.manifest for net472 build
- Added long paths support (win10 v1607+)
- Fixed blurring at high DPI with scaling
2023-08-06 20:08:05 +03:00
VaDiM
4d919a2bfe [GUI] Improve Scene Hierarchy tab context menu
- Added "Related assets" item to the context menu
2023-08-05 18:10:36 +03:00
VaDiM
6701f467b7 [CLI] Show the number of successfully loaded assets 2023-07-14 20:20:58 +03:00
VaDiM
50f5da5554 [GUI] Don't count Shaders in unity 2021+ assets 2023-07-14 19:17:56 +03:00
VaDiM
2b6dcca9c8 Update readme files, add CHANGELOG.md 2023-07-12 04:54:30 +03:00
VaDiM
0bdcb89b08 Update version to v0.17.1 2023-07-09 05:59:39 +03:00
VaDiM
dcd7b98229 Some minor fixes 2023-07-09 05:59:33 +03:00
VaDiM
007e5c7e4d [CLI] Update ReadMe.md 2023-07-08 11:48:58 +03:00
VaDiM
63564d5fff Add assembly folder support for Live2D export 2023-07-02 23:40:04 +03:00
VaDiM
aea6cbc97f Add option to export Live2D Cubism models 2023-07-02 23:20:14 +03:00
VaDiM
6d41693b85 Update dependencies 2023-06-27 00:27:41 +03:00
Pierce Thompson
5695afae7b Add support for exporting meshes from the CLI (#5)
* Add support for exporting meshes from the CLI

* Add the Mesh ClassIDType to CLIOptions' supportedAssetTypes
2023-06-23 15:37:40 +03:00
VaDiM
bb9ea7d86b Update version to v0.17.0 2023-06-05 02:03:28 +03:00
VaDiM
547659e151 [GUI] Display progress in the taskbar button 2023-06-05 01:33:48 +03:00
VaDiM
2f8f57c1a6 [GUI] Improve file and folder loading (drag&drop)
- Added support for drag&drop of multiple folders
- Open/Export dialog can now also use a path taken from drag&drop items
2023-05-29 05:14:50 +03:00
VaDiM
b7d5d73f23 [GUI] Fix audio player position in maximized window 2023-05-29 04:35:38 +03:00
VaDiM
f0c237473c [GUI] Some fixes for asset list filtering 2023-05-29 04:20:57 +03:00
VaDiM
09947fd14f [CLI] Update ReadMe.md 2023-05-23 16:21:38 +03:00
VaDiM
da216dace8 Improve "Restore TextAsset extension name" option
- If checked, AssetStudio will first try to find an extension in an asset's name and only then in its container. If no extension is found, ".txt" will be used
2023-05-23 16:18:24 +03:00
VaDiM
c7356875f9 Add grouping option with full container path
* [GUI] - "container path full (with name)"
* [CLI] - "containerFull"
(https://github.com/Perfare/AssetStudio/issues/815)
2023-05-23 16:17:00 +03:00
VaDiM
02e46eaa0d [GUI] Improve asset list filtering
- Added filter history
- Added more filtering modes: Include, Exclude, Regex (Name/Container)
2023-05-18 23:45:54 +03:00
VaDiM
b0bf5e0cfd Optimize drawing performance of packed sprites
- Restored some code from an earlier version (e501940f03), since it works faster for packed sprites with a small number of triangles
2023-05-15 00:07:46 +03:00
VaDiM
11b9ca37da Fix cutout glitch in some packed sprites
- Improved performance
(https://github.com/Perfare/AssetStudio/issues/1015)
2023-05-15 00:06:52 +03:00
VaDiM
5944dd8c58 [GUI] Fix typo
merge https://github.com/Perfare/AssetStudio/pull/1009

Co-Authored-By: Dirk Kok <the@dirkkok.nl>
2023-05-13 12:55:12 +03:00
VaDiM
1c67e39504 [GUI] Add "About" window & update titles 2023-05-13 12:46:43 +03:00
VaDiM
2d81007556 [GUI] Improve de-selection in Scene Hierarchy tab
- improved de-selection perfomance
- selected objects count is now displayed in the status bar
- "Select none" renamed to "Clear selection"
2023-04-26 09:57:38 +03:00
VaDiM
4bae98813b [GUI] One more bugfix
- bugfix for 44a1240f5f
2023-04-13 22:08:01 +03:00
VaDiM
90ec395b2a Block alignment fix for Unity 2019.4.X
- src: 10346b4f02
2023-04-09 15:40:34 +03:00
VaDiM
af3684bab8 [GUI] Bugfix
- bugfix for ea09a8de64
2023-04-06 02:14:53 +03:00
VaDiM
1da51ac95b Add error message on incorrect format of specified unity ver 2023-03-20 19:32:00 +03:00
VaDiM
9e14f1ef00 Add error message for bundles with CN encryption 2023-03-20 19:31:49 +03:00
VaDiM
4edadb19f8 Disable Shader support for Unity > 2020 2023-03-20 06:13:08 +03:00
VaDiM
44a1240f5f [GUI] Improve memory usage of image previews
- also a bit increased performance of alpha mask resizing for previews
2023-03-14 04:10:48 +03:00
VaDiM
01957a9443 [CLI] Add draft readme 2023-03-10 21:18:29 +03:00
VaDiM
ad1b3b4911 [CLI] Some fixes for logging 2023-03-10 21:01:12 +03:00
VaDiM
0925751776 [CLI] Add MovieTexture support 2023-03-10 19:37:53 +03:00
VaDiM
b59ba3ba94 [GUI] Add some videoClip info to preview tab 2023-03-10 11:53:38 +03:00
VaDiM
67898c72ca Update projects files
- Fixed build options
2023-03-10 09:01:27 +03:00
VaDiM
0425423ca9 [CLI] Add debugging info for video 2023-03-08 19:59:19 +03:00
VaDiM
16a7107d4e [CLI] Partial assets reading 2023-03-08 18:28:50 +03:00
VaDiM
10b7e84ffb [CLI] More debugging info for audio 2023-03-08 17:31:34 +03:00
VaDiM
679e7041a6 [GUI] A bit better(?) error handling 2023-03-08 17:12:59 +03:00
VaDiM
629c6248a4 [GUI] Use blocking call to show messages in correct order 2023-03-08 12:11:45 +03:00
VaDiM
7674081df7 [GUI] Add context menu to the Scene Hierarchy tab
- Added context menu with "Select all", "Select none", "Expand all", "Collapse all" options to the "Scene Hierarchy" tab.
https://github.com/aelurum/AssetStudio/issues/1
2023-03-07 18:21:34 +03:00
VaDiM
fb574064c9 [GUI] Avoid overflow in alphanum sorting 2023-03-07 06:55:45 +03:00
VaDiM
c52940abc4 Add AssetStudioCLI project
- Updated projects and dependencies
2023-03-07 06:52:33 +03:00
VaDiM
5c662d64e4 Add exclude mode to asset list filter 2023-02-05 00:47:04 +03:00
VaDiM
b9cf95616b SwapBytesForXbox refactor 2023-01-27 13:46:55 +03:00
VaDiM
39490d4e03 Update fmod to 0.2.0.22 (CLI Preparation) 2023-01-27 13:17:01 +03:00
VaDiM
a96d1a5d5d Fix bug in alphanum sorting 2022-12-27 05:21:42 +03:00
VaDiM
568daafc7f Add progress info to LoadZipFile()
Displays the progress of loading zip(apk) files on the progress bar
2022-12-15 17:43:28 +03:00
VaDiM
2a2216e2bf Update projects and dependencies 2022-12-10 20:23:45 +03:00
VaDiM
dfbe46e1e5 Improve sorting performance
Improved performance of AlphanumComparatorFast sorting for names, as well as the default sorting for containers
2022-12-03 05:16:19 +03:00
VaDiM
6a9aad510c Add image export in WebP format 2022-11-22 03:33:47 +03:00
VaDiM
fd21cafd29 Merge branch 'Perfare_master' into AssetStudio-mod 2022-11-21 02:37:02 +03:00
VaDiM
ea09a8de64 Add support for sprites with alpha mask
- Sprites with alpha mask can now be viewed and exported with transparency
- Added hotkeys to control display of an alpha mask  on the preview tab
- Added an option to the export settings to enable/disable export with alpha mask as well
- Prevented texture2D preview options from being changed with hotkeys outside of texture preview (e.g. when some other asset is selected)
2022-11-21 01:59:16 +03:00
VaDiM
7fa5b4f355 Update ImageSharp.Drawing version
- migrate to ImageSharp v2+
2022-11-18 06:33:26 +03:00
VaDiM
c22d92009a Update project files 2022-11-17 04:39:49 +03:00
Perfare
53f3e8232a support 2022.1 2022-11-17 04:32:05 +03:00
Perfare
2fe57a1c5d update enum 2022-11-17 04:31:30 +03:00
Perfare
ded2dcd54e Fixed #962 2022-11-17 04:31:16 +03:00
Perfare
8ae3df6197 minor improvements 2022-11-17 04:30:52 +03:00
Kanglai Qian
64d9718c34 avoid useless search for non exist files (#967) 2022-11-17 04:30:39 +03:00
Perfare
220004c976 support 2021.3 2022-11-17 04:30:27 +03:00
Perfare
a324366be9 BundleFile fix 2022-11-17 04:30:17 +03:00
Perfare
06b4ae9ffe Fix type conversion bug 2022-11-17 04:29:55 +03:00
Perfare
77b056de5e update project file 2022-11-17 04:29:02 +03:00
Perfare
344edb722f revert 2022-11-17 04:23:17 +03:00
Perfare
98c4d0c3ab minor fixes and improvements 2022-11-17 04:23:00 +03:00
Perfare
23ac590648 Fixed #941 2022-11-17 04:22:31 +03:00
Perfare
54445475a3 Fix and improve Texture2D convert 2022-11-17 04:22:14 +03:00
Perfare
7299bcba6d Create build.yml 2022-11-17 04:22:06 +03:00
Perfare
5487ff4e60 Fixed #924 2022-11-17 04:21:13 +03:00
Perfare
b3621a75b0 Fixed #929 2022-11-17 04:20:20 +03:00
Perfare
8b048b9e1e improved Sprite export
Fixed #944
Fixed #923
2022-11-17 04:20:20 +03:00
Perfare
41a79f485f using IProgress 2022-11-17 04:18:40 +03:00
Perfare
a060a392e7 Fixed #919 2022-11-17 04:18:06 +03:00
Rudolf Kolbe
0798af7c5c fix apk loading issues (#913)
* fix apk loading issues

Someone contacted me some days ago and notified me that some problems showed up with my APK loading implementation.
Namely:

file reference registration (importFilesHash) was missing
split files weren't handled
external resource files weren't registered
This pr fixes those problems.

* revert weird changes

* fix missing }

* fix formatting

* use entry.FullName for the basePath instead of entry.Name
2022-11-17 04:17:33 +03:00
VaDiM
8ebfa16e19 Return to ImageSharp
coz the perfomance issue was fixed by Perfare in later commits
2022-11-17 03:59:59 +03:00
Perfare
d158e864b5 support 2022.1 2022-06-16 18:41:55 +08:00
Perfare
b70b5196e3 update enum 2022-06-16 17:45:54 +08:00
Perfare
4f88841026 Fixed #962 2022-06-01 15:33:01 +08:00
Perfare
dc9429feac minor improvements 2022-05-21 21:16:34 +08:00
Kanglai Qian
a3c16ed3d6 avoid useless search for non exist files (#967) 2022-05-21 21:10:02 +08:00
Perfare
5b83eebdda support 2021.3 2022-05-21 17:38:40 +08:00
Perfare
1fcf7a4364 BundleFile fix 2022-05-21 17:24:54 +08:00
Perfare
973d50ce8b Fix type conversion bug 2022-05-21 05:46:44 +08:00
Perfare
50485a9bd3 update project file 2022-03-24 10:38:52 +08:00
Perfare
dbb3d3fef7 revert 2022-03-24 09:47:36 +08:00
Perfare
e1cfff63c3 minor fixes and improvements 2022-03-23 01:41:59 +08:00
Perfare
44514a4e10 Fixed #941 2022-03-22 22:54:49 +08:00
Perfare
b1205808e2 Fix and improve Texture2D convert 2022-03-22 01:00:20 +08:00
Perfare
7d3a4a10fc Create build.yml 2022-03-20 02:36:32 +08:00
Perfare
b909857820 Fixed #924 2022-03-19 08:06:03 +08:00
Perfare
b674e66407 Fixed #929 2022-03-19 07:48:39 +08:00
Perfare
d7dcd3f405 improved Sprite export
Fixed #944
Fixed #923
2022-03-19 07:43:53 +08:00
Perfare
44145e0b9c using IProgress 2022-03-19 06:40:51 +08:00
Perfare
d4e21f824c Fixed #919 2022-02-17 01:07:24 +08:00
Rudolf Kolbe
e7a4604a65 fix apk loading issues (#913)
* fix apk loading issues

Someone contacted me some days ago and notified me that some problems showed up with my APK loading implementation.
Namely:

file reference registration (importFilesHash) was missing
split files weren't handled
external resource files weren't registered
This pr fixes those problems.

* revert weird changes

* fix missing }

* fix formatting

* use entry.FullName for the basePath instead of entry.Name
2022-02-15 11:19:50 +08:00
VaDiM
74f2c3190b Merge branch 'Perfare_master' into AssetStudio-mod 2022-01-13 18:10:31 +02:00
Rudolf Kolbe
8d193a63cd Zip (including APK) Loading (#902)
* load ZipFile

makes it possible to directly load apk files

* use LoadFile for recursive zip opening

* set System.IO.Compression version

* keep identical format in AssetStudio.csproj

* try/catch the loading of each zip entry

* remove extra new line in FileReader.cs

* apply requested changes
2021-12-27 15:59:18 +08:00
brianpow
e61a317185 use FullPath instead of FileName for easier identification of broken file (#900)
* use FullPath instead of FileName for easier identification of broken file.

* use FullPath instead of FileName for easier identification of broken file
2021-12-24 13:23:09 +08:00
VaDiM
f67965b1dd Texture2DDecoderNative - Linux/macOS compatibility fix
+ CMakeLists.txt
2021-12-15 02:49:28 +02:00
VaDiM
07a81d9bfe Update projects 2021-12-15 00:35:46 +02:00
VaDiM
95fd1823c8 Merge branch 'Perfare_master' into AssetStudio-mod 2021-12-13 04:45:41 +02:00
VaDiM
d25451d5b9 Merge branch 'Perfare_master' into AssetStudio-mod 2021-12-13 00:43:49 +02:00
scriptkitz
0e1a886e0b 修正UV导出计算错误问题。 (#891) 2021-12-11 15:48:54 +08:00
Perfare
97b5f51f3a Fix build 2021-12-09 20:23:25 +08:00
Perfare
7295feda72 Update README.md 2021-12-09 19:16:13 +08:00
Perfare
fe95c91759 Add net6.0 target framework 2021-12-09 19:00:59 +08:00
Perfare
d220315d9b Add detailed export progress in the status bar 2021-12-09 18:08:56 +08:00
Perfare
a94caa5e34 Update project 2021-12-09 17:21:44 +08:00
Perfare
3660b4ed67 Some improvements 2021-12-09 17:13:21 +08:00
Perfare
3370f93037 Fixed bug 2021-12-06 17:37:59 +08:00
Perfare
80653711cd Performance improvement 2021-12-06 13:36:22 +08:00
VaDiM
f0b23bbfe7 Merge branch 'Perfare_master' into AssetStudio-mod 2021-12-05 02:41:24 +02:00
Perfare
88c5804586 Fixed #886 2021-12-04 09:23:41 +08:00
Perfare
e501940f03 Use a better way to crop Sprite 2021-12-04 08:44:33 +08:00
Perfare
d4060cde6d Fixed bug 2021-12-04 02:05:35 +08:00
Perfare
582a779441 Update project file 2021-12-03 19:46:20 +08:00
Perfare
5fa4934787 Add net5.0 target framework 2021-12-03 17:21:24 +08:00
Perfare
18277fbea8 fixed bug 2021-12-03 17:05:06 +08:00
VaDiM
571ea2da4a Don't use ImageSharp for texture processing
- returned to System.Drawing
(ImageSharp is a good lib, but too slow for such app, IMO)
2021-11-24 19:30:38 +02:00
VaDiM
9cbe91decb Improve sorting by asset names
- added alphanumeric sorting for more natural presentation of asset list
2021-11-24 14:56:28 +02:00
VaDiM
19c6c5fe73 Minor UI improvments & bugfixes
- improved "Copy text" option in right click menu, to display what exactly to copy
- added "Dump selected assets" option to right click menu
- added 'selected assets count' info to status strip when you select assets
- added 'exported count / total export count` info to status strip during export
- "Show error message" option on the "Debug" tab renamed to "Show all error messages" and is now disabled by default
- "fixed" an issue with getting stuck during the "Building tree structure" step
- fixed a bug with listSearch that could make it not work in some conditions
- fixed a rare bug for resource files with the same name, that caused their data to be overwritten and become incorrect
2021-11-24 14:55:38 +02:00
VaDiM
792850dbb2 Update projects 2021-11-22 16:00:51 +02:00
Perfare
2ce9cae957 Modify the prompt statement 2021-11-20 03:11:02 +08:00
Perfare
af5e50cfa9 Update README.md 2021-11-20 02:52:53 +08:00
Perfare
ce1172ca9a rename. closed #817 2021-11-19 18:24:18 +08:00
Perfare
a7e6d91f5b Fixed bug 2021-11-19 18:22:39 +08:00
Perfare
34a0af683a Fixed bug 2021-11-19 18:11:00 +08:00
Perfare
91410a33b1 Improve the handling of compressed files. 2021-11-19 17:54:06 +08:00
Perfare
d08b78c2cf support 2021.2 2021-11-12 10:25:22 +08:00
Perfare
2ef52afe1e Fixed #835 2021-11-12 09:30:12 +08:00
Perfare
05a41d2f1e Merge pull request #855 from nikitalita/fix-assembly-loading
Fix AssemblyLoading bug
2021-11-08 09:36:03 +08:00
nikitalita
34c38e1415 Fix AssemblyLoading bug
If the AssemblyLoader attempted to load a non-csil dll
while iterating through the file list, it would catch the exception
OUTSIDE the loop, and wouldn't load the rest.
This fix makes it catch inside the loop so it will continue iterating.
2021-10-07 13:36:03 -07:00
Perfare
c85873b729 0.16.0 2021-07-06 16:03:54 +08:00
Perfare
b146d251a7 add option to not show error message 2021-07-06 15:57:59 +08:00
Perfare
3129d67fc1 Improve ShaderConverter 2021-07-06 12:45:24 +08:00
Perfare
850ba63a10 support exporting VideoClips with a size above 2gb. close #765 2021-07-02 03:26:46 +08:00
Perfare
17b91984d6 fixed bug 2021-07-02 02:29:03 +08:00
Perfare
7ab2cda120 refactor the file reading part 2021-07-02 02:17:59 +08:00
Perfare
4345885cc9 remember the last opened path, close #646 2021-06-30 22:41:16 +08:00
Perfare
53720e37ab Fixed #435 2021-06-30 06:31:05 +08:00
Perfare
7c3cb36630 update project files 2021-06-29 11:03:19 +08:00
Perfare
c1cddce031 added option to support version stripped files, close #766 2021-06-29 10:32:19 +08:00
Perfare
973a1076e4 support exporting AnimationClip versions below 4.3 2021-06-29 02:43:42 +08:00
Perfare
089e164756 improved Sprite export 2021-06-28 07:14:02 +08:00
Perfare
c2b6691fd9 fix build 2021-06-27 09:42:13 +08:00
Perfare
8dec094304 update project files 2021-06-27 09:27:40 +08:00
Perfare
bedee240be Use ImageSharp to process textures 2021-06-27 07:33:20 +08:00
Perfare
d963d71b12 clean up TypeDefinitionConverter code 2021-06-26 13:16:12 +08:00
Perfare
77a0c9c40a Fbx multiple uv export 2021-06-24 13:48:56 +08:00
Perfare
f3e406983b Merge pull request #768 from Fraxul/master
Fix blend shape export with multiple submeshes
2021-06-24 12:59:31 +08:00
Dan Weatherford
08b7bfcf9a Fix blend shape export with multiple submeshes
Share a single vertex list between submeshes in the exported mesh, which
makes the blend target vertex list indices line up correctly.

As a bonus, the exported FBX file will be smaller for meshes with more
than one submesh, since we're not duplicating vertices anymore.
2021-06-20 00:18:22 -05:00
Patrick King
57e4f7cefd Add an option to export all UVs as diffuse maps. 2021-06-11 17:12:15 -06:00
Patrick King
c9cf2d188e Enable FBX export for higher UV maps. 2021-06-11 14:26:37 -06:00
Perfare
ab98585b6a Fix Shader reading. Close #720 2021-05-29 05:11:05 +08:00
Perfare
075d53a455 small improvement 2021-05-28 23:19:31 +08:00
Perfare
432116d834 Improve SerializedFile reading. 2021-05-28 22:23:07 +08:00
Perfare
caa45216ef fix compilation error 2021-05-28 22:16:50 +08:00
Perfare
c9394cd957 Fixed #734 2021-05-28 03:49:46 +08:00
Perfare
46c0e8ffe1 Improve stream file processing 2021-05-28 03:43:32 +08:00
Perfare
d14c232015 Improve the error message 2021-05-28 03:27:50 +08:00
Perfare
4002bdecb8 Update README.md 2021-05-27 03:55:37 +08:00
StarHeart
17259e00c7 correct grammar (#749) 2021-05-27 03:49:40 +08:00
Perfare
44b02b92d8 fix lzma decompression 2021-05-27 03:46:15 +08:00
Joshua May
251854cc41 Adds exporter for list of assets to XML (#710) 2021-04-18 01:27:15 +08:00
Perfare
6f7b77245d Merge pull request #639 from sk-zk/nul-fix
[GUI] Fix \0 chars in TextAssets cutting off preview
2021-04-18 01:21:16 +08:00
Perfare
6d99f5ebf6 Improve vertex format parsing. Close #689 2021-04-12 15:11:17 +08:00
Perfare
f1f2430f97 Support 2021.1 2021-04-11 23:49:44 +08:00
Perfare
b52696c965 Merge pull request #713 from K0lb3/patch-1
Update ClassIDType according to official reference
2021-04-11 21:54:45 +08:00
K0lb3
5fba52dc83 Update ClassIDType according to official reference
I noticed that Unity has made a [ClassID Reference table](https://docs.unity3d.com/Manual/ClassIDReference.html) and thought that copying it over here might be useful for dumps.
2021-03-28 14:24:20 -07:00
Perfare
dfb74baf79 Fix for mesh weights output 2021-01-15 09:32:57 +08:00
Perfare
978e90a403 Fix if AudioClip does not contain subsound. Close #672 2021-01-15 05:39:45 +08:00
Perfare
c17d7d6331 Update README.md 2021-01-15 04:38:54 +08:00
Perfare
9fef18d6ea fixes bug 2021-01-15 04:33:54 +08:00
Perfare
ee0cd4ab52 Fixed #609 2021-01-15 03:43:52 +08:00
Perfare
f904bc138b typo 2021-01-15 02:17:11 +08:00
Perfare
7ed5345b1b Fixed #652 2021-01-15 02:12:54 +08:00
Perfare
d7f652d572 improved Sprite export 2021-01-14 19:27:57 +08:00
Perfare
32ce032655 Support 2020.2 2021-01-14 05:23:19 +08:00
Perfare
e1cf36aa3c Fixed #650 2020-12-14 21:20:04 +08:00
sk-zk
f644396a15 Fix \0 chars in TextAssets cutting off preview 2020-10-30 23:08:06 +01:00
Perfare
3e77c34bd5 Fixed #618 2020-09-29 07:08:14 +08:00
Perfare
052c60f629 Fix file occupation conflict 2020-09-26 23:29:38 +08:00
Perfare
a1f2e3e7fe Merge pull request #613 from Ishotihadus/master
Problems in exporting fbx with multiple blendshapes
2020-09-26 10:18:42 -05:00
Ishotihadus
32ee8b326f fix problems in exporting fbx with multiple blendshapes 2020-09-16 19:44:11 +09:00
Perfare
06ce479eb6 Fixes #591 2020-08-26 09:50:25 +08:00
Perfare
03f74bac64 Fix BlendShape output error 2020-08-24 22:00:53 +08:00
Perfare
344b675745 Update README.md 2020-08-16 18:53:55 +08:00
Perfare
86590d95a5 Add dump viewer 2020-08-14 17:38:43 +08:00
Perfare
bbea1341b2 Update README.md 2020-08-14 15:50:45 +08:00
Perfare
ca60dd9834 Support exporting MonoBehaviour to json. Close #477 2020-08-14 15:45:48 +08:00
Perfare
7aa35b5b8c Fix get class from divided UnityEngine.dll 2020-08-14 01:31:49 +08:00
Perfare
bd2decdb8f Fix element alignment errors 2020-08-14 01:18:43 +08:00
Perfare
9b2c85bcae Refactor MonoBehaviour reading 2020-08-14 00:02:59 +08:00
Perfare
efbab7c43a using Nuget 2020-08-12 22:22:04 +08:00
Perfare
729a8a8263 implemented SPIR-V shader export 2020-08-12 22:11:26 +08:00
Perfare
0ec29f62ca Improve shader conversion, fixes #589 2020-08-12 20:49:36 +08:00
Perfare
796317f9d9 support 2020.1 2020-08-12 19:13:10 +08:00
Perfare
7596dcc7cd keep path when extracting folder 2020-08-12 02:55:02 +08:00
Perfare
422851cdab Fix mesh index errors 2020-08-12 02:24:08 +08:00
Perfare
ec0a2a47f1 can choose the directory to save the extracted files 2020-08-10 14:07:29 +08:00
Perfare
8ce5b947f6 Update README.md 2020-08-10 11:01:37 +08:00
Perfare
419ca63f9d Set Runtime Library to /MT 2020-08-10 09:52:09 +08:00
Perfare
6fdb0c7b0e add ResourceManager 2020-08-06 23:17:44 +08:00
Perfare
4e97b4b898 improve export 2020-08-06 21:07:37 +08:00
Perfare
1766dcbdeb Fixed #570 2020-08-06 13:31:17 +08:00
Perfare
ef38471ff1 move code 2020-08-06 13:11:41 +08:00
Perfare
217a7993e9 fixed bug 2020-08-06 12:56:47 +08:00
Perfare
0a41615763 fix UV problems 2020-08-06 12:34:59 +08:00
Perfare
9d34f668d5 modify project file 2020-08-06 11:59:35 +08:00
Perfare
9269a36725 Merge pull request #555 from Jayatubi/allowdrag
Allow drag and drop files or folder
2020-08-05 21:42:32 -05:00
Jayatubi
813e8b10a6 Export local keywords of shader as well as global keywords (#554)
* Export local keywords of shader

* Export local keywords of shader
2020-08-06 10:39:48 +08:00
Tahvohck
84c75fadf5 Sort code for PathID (change to numeric) (#550)
* Sort code for PathID (change to numeric)

* Remove comments (were only for reference)

* Match Perfare formatting
2020-08-06 10:38:12 +08:00
hozuki
c76e41b1ab Replace C++/CLI components with P/Invoke components (#562)
* Replace C++/CLI components with P/Invoke

* Deleted C++/CLI projects

* Use Utf8StringHandle to marshal UTF-8 strings

* Use plaform-default calling convention

* Handle DLL preloading on Linux and macOS

* Change intermediate and output directories of native projects

* Improve P/Invoke documentation
2020-08-06 10:35:50 +08:00
jayatubi
fefeea5f35 Allow drag and drop files or folder 2020-05-18 14:17:42 +08:00
273 changed files with 29903 additions and 16066 deletions

57
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,57 @@
name: AssetStudioBuild
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
workflow_dispatch:
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: microsoft/setup-msbuild@v1.1
- name: Download FBX SDK
run: |
md fbx
cd fbx
Invoke-WebRequest "https://damassets.autodesk.net/content/dam/autodesk/www/adn/fbx/2020-2-1/fbx202021_fbxsdk_vs2019_win.exe" -OutFile "fbxsdk.exe"
Start-Process -FilePath "fbxsdk.exe" /S -Wait
Invoke-WebRequest "https://damassets.autodesk.net/content/dam/autodesk/www/adn/fbx/2020-2-1/fbx202021_fbxsdk_vs2019_pdbs.exe" -OutFile "fbxpdb.exe"
Start-Process -FilePath "fbxpdb.exe" /S -Wait
cd ..
- name: Nuget Restore
run: nuget restore
- name: Build .Net472
run: msbuild /p:Configuration=Release /p:TargetFramework=net472 /verbosity:minimal
- name: Build .Net5
run: msbuild /t:AssetStudioGUI:publish /p:Configuration=Release /p:TargetFramework=net5.0-windows /p:SelfContained=false /verbosity:minimal
- name: Build .Net6
run: msbuild /t:AssetStudioGUI:publish /p:Configuration=Release /p:TargetFramework=net6.0-windows /p:SelfContained=false /verbosity:minimal
- name: Upload .Net472 Artifact
uses: actions/upload-artifact@v2
with:
name: AssetStudio.net472
path: AssetStudioGUI/bin/Release/net472
- name: Upload .Net5 Artifact
uses: actions/upload-artifact@v2
with:
name: AssetStudio.net5
path: AssetStudioGUI/bin/Release/net5.0-windows/publish
- name: Upload .Net6 Artifact
uses: actions/upload-artifact@v2
with:
name: AssetStudio.net6
path: AssetStudioGUI/bin/Release/net6.0-windows/publish

3
.gitignore vendored
View File

@@ -35,6 +35,9 @@ bld/
# Visual Studio 2017 auto generated files
Generated\ Files/
# Launch Settings
*launchSettings.json
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

View File

@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows;net8.0;net8.0-windows</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>1.2.0</Version>
<Copyright>Copyright © Perfare 2020-2022; Copyright © hozuki 2020</Copyright>
<DebugType>embedded</DebugType>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,66 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
#if NETFRAMEWORK
namespace AssetStudio.PInvoke
{
public static class DllLoader
{
public static void PreloadDll(string dllName)
{
var localPath = Process.GetCurrentProcess().MainModule.FileName;
var localDir = Path.GetDirectoryName(localPath);
// Not using OperatingSystem.Platform.
// See: https://www.mono-project.com/docs/faq/technical/#how-to-detect-the-execution-platform
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Win32.LoadDll(GetDirectedDllDirectory(localDir), dllName);
}
}
private static string GetDirectedDllDirectory(string localDir)
{
var win32Path = Path.Combine("runtimes", "win-x86", "native");
var win64Path = Path.Combine("runtimes", "win-x64", "native");
var subDir = Environment.Is64BitProcess ? win64Path : win32Path;
var directedDllDir = Path.Combine(localDir, subDir);
return directedDllDir;
}
private static class Win32
{
internal static void LoadDll(string dllDir, string dllName)
{
var dllFileName = $"{dllName}.dll";
var directedDllPath = Path.Combine(dllDir, dllFileName);
// Specify SEARCH_DLL_LOAD_DIR to load dependent libraries located in the same platform-specific directory.
var hLibrary = LoadLibraryEx(directedDllPath, IntPtr.Zero, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
if (hLibrary == IntPtr.Zero)
{
var errorCode = Marshal.GetLastWin32Error();
var exception = new Win32Exception(errorCode);
throw new DllNotFoundException(exception.Message, exception);
}
}
// HMODULE LoadLibraryExA(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
// HMODULE LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr LoadLibraryEx(string lpLibFileName, IntPtr hFile, uint dwFlags);
private const uint LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x1000;
private const uint LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x100;
}
}
}
#endif

View File

@@ -0,0 +1,100 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;
namespace AssetStudio.PInvoke
{
// Generally the technique from Steamworks.NET
public class Utf8StringHandle : SafeHandleZeroOrMinusOneIsInvalid
{
static Utf8StringHandle()
{
Utf8 = new UTF8Encoding(false);
}
public Utf8StringHandle(string str)
: base(true)
{
IntPtr buffer;
if (str == null)
{
buffer = IntPtr.Zero;
}
else
{
if (str.Length == 0)
{
buffer = Marshal.AllocHGlobal(1);
unsafe
{
*(byte*)buffer = 0;
}
}
else
{
var strlen = Utf8.GetByteCount(str);
var strBuffer = new byte[strlen + 1];
Utf8.GetBytes(str, 0, str.Length, strBuffer, 0);
buffer = Marshal.AllocHGlobal(strBuffer.Length);
Marshal.Copy(strBuffer, 0, buffer, strBuffer.Length);
}
}
SetHandle(buffer);
}
public static string ReadUtf8StringFromPointer(IntPtr lpstr)
{
if (lpstr == IntPtr.Zero || lpstr == new IntPtr(-1))
{
return null;
}
var byteCount = 0;
unsafe
{
var p = (byte*)lpstr.ToPointer();
while (*p != 0)
{
byteCount += 1;
p += 1;
}
}
if (byteCount == 0)
{
return string.Empty;
}
var strBuffer = new byte[byteCount];
Marshal.Copy(lpstr, strBuffer, 0, byteCount);
var str = Utf8.GetString(strBuffer);
return str;
}
protected override bool ReleaseHandle()
{
if (!IsInvalid)
{
Marshal.FreeHGlobal(handle);
}
return true;
}
private static readonly UTF8Encoding Utf8;
}
}

View File

@@ -1,71 +1,159 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29920.165
# Visual Studio Version 17
VisualStudioVersion = 17.5.33414.496
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudio", "AssetStudio\AssetStudio.csproj", "{7662F8C2-7BFD-442E-A948-A43B4F7EB06E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudio", "AssetStudio\AssetStudio.csproj", "{422FEC21-EF60-4F29-AA56-95DFDA23C913}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AssetStudioFBX", "AssetStudioFBX\AssetStudioFBX.vcxproj", "{B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudio.PInvoke", "AssetStudio.PInvoke\AssetStudio.PInvoke.csproj", "{0B2BE613-3049-4021-85D1-21C325F729F4}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Texture2DDecoder", "Texture2DDecoder\Texture2DDecoder.vcxproj", "{BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudioFBXWrapper", "AssetStudioFBXWrapper\AssetStudioFBXWrapper.csproj", "{E301AFEA-84E7-4BCE-8D65-A2576D8D105B}"
ProjectSection(ProjectDependencies) = postProject
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027} = {11EA25A3-ED68-40EE-A9D0-7FDE3B583027}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudioUtility", "AssetStudioUtility\AssetStudioUtility.csproj", "{80AEC261-21EE-4E4F-A93B-7A744DC84888}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudioGUI", "AssetStudioGUI\AssetStudioGUI.csproj", "{29EAD018-1C67-497A-AB8E-727D595AD756}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudioGUI", "AssetStudioGUI\AssetStudioGUI.csproj", "{52B196FB-4C8A-499B-B877-1A0EB4F33EC0}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudioUtility", "AssetStudioUtility\AssetStudioUtility.csproj", "{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Texture2DDecoderWrapper", "Texture2DDecoderWrapper\Texture2DDecoderWrapper.csproj", "{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}"
ProjectSection(ProjectDependencies) = postProject
{29356642-C46E-4144-83D8-22DC09D0D7FD} = {29356642-C46E-4144-83D8-22DC09D0D7FD}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AssetStudioFBXNative", "AssetStudioFBXNative\AssetStudioFBXNative.vcxproj", "{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Texture2DDecoderNative", "Texture2DDecoderNative\Texture2DDecoderNative.vcxproj", "{29356642-C46E-4144-83D8-22DC09D0D7FD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudioCLI", "AssetStudioCLI\AssetStudioCLI.csproj", "{34B6329B-0E73-45AC-B8CC-015F119F63DC}"
ProjectSection(ProjectDependencies) = postProject
{422FEC21-EF60-4F29-AA56-95DFDA23C913} = {422FEC21-EF60-4F29-AA56-95DFDA23C913}
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF} = {65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Debug|x64.ActiveCfg = Debug|Any CPU
{7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Debug|x64.Build.0 = Debug|Any CPU
{7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Debug|x86.ActiveCfg = Debug|Any CPU
{7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Debug|x86.Build.0 = Debug|Any CPU
{7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Release|x64.ActiveCfg = Release|Any CPU
{7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Release|x64.Build.0 = Release|Any CPU
{7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Release|x86.ActiveCfg = Release|Any CPU
{7662F8C2-7BFD-442E-A948-A43B4F7EB06E}.Release|x86.Build.0 = Release|Any CPU
{B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}.Debug|x64.ActiveCfg = Debug|x64
{B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}.Debug|x64.Build.0 = Debug|x64
{B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}.Debug|x86.ActiveCfg = Debug|Win32
{B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}.Debug|x86.Build.0 = Debug|Win32
{B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}.Release|x64.ActiveCfg = Release|x64
{B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}.Release|x64.Build.0 = Release|x64
{B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}.Release|x86.ActiveCfg = Release|Win32
{B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}.Release|x86.Build.0 = Release|Win32
{BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}.Debug|x64.ActiveCfg = Debug|x64
{BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}.Debug|x64.Build.0 = Debug|x64
{BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}.Debug|x86.ActiveCfg = Debug|Win32
{BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}.Debug|x86.Build.0 = Debug|Win32
{BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}.Release|x64.ActiveCfg = Release|x64
{BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}.Release|x64.Build.0 = Release|x64
{BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}.Release|x86.ActiveCfg = Release|Win32
{BEC7B5E6-0A7B-4824-97A7-EEA04D9EBA29}.Release|x86.Build.0 = Release|Win32
{80AEC261-21EE-4E4F-A93B-7A744DC84888}.Debug|x64.ActiveCfg = Debug|x64
{80AEC261-21EE-4E4F-A93B-7A744DC84888}.Debug|x64.Build.0 = Debug|x64
{80AEC261-21EE-4E4F-A93B-7A744DC84888}.Debug|x86.ActiveCfg = Debug|x86
{80AEC261-21EE-4E4F-A93B-7A744DC84888}.Debug|x86.Build.0 = Debug|x86
{80AEC261-21EE-4E4F-A93B-7A744DC84888}.Release|x64.ActiveCfg = Release|x64
{80AEC261-21EE-4E4F-A93B-7A744DC84888}.Release|x64.Build.0 = Release|x64
{80AEC261-21EE-4E4F-A93B-7A744DC84888}.Release|x86.ActiveCfg = Release|x86
{80AEC261-21EE-4E4F-A93B-7A744DC84888}.Release|x86.Build.0 = Release|x86
{52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Debug|x64.ActiveCfg = Debug|x64
{52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Debug|x64.Build.0 = Debug|x64
{52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Debug|x86.ActiveCfg = Debug|x86
{52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Debug|x86.Build.0 = Debug|x86
{52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Release|x64.ActiveCfg = Release|x64
{52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Release|x64.Build.0 = Release|x64
{52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Release|x86.ActiveCfg = Release|x86
{52B196FB-4C8A-499B-B877-1A0EB4F33EC0}.Release|x86.Build.0 = Release|x86
{422FEC21-EF60-4F29-AA56-95DFDA23C913}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{422FEC21-EF60-4F29-AA56-95DFDA23C913}.Debug|Any CPU.Build.0 = Debug|Any CPU
{422FEC21-EF60-4F29-AA56-95DFDA23C913}.Debug|x64.ActiveCfg = Debug|Any CPU
{422FEC21-EF60-4F29-AA56-95DFDA23C913}.Debug|x64.Build.0 = Debug|Any CPU
{422FEC21-EF60-4F29-AA56-95DFDA23C913}.Debug|x86.ActiveCfg = Debug|Any CPU
{422FEC21-EF60-4F29-AA56-95DFDA23C913}.Debug|x86.Build.0 = Debug|Any CPU
{422FEC21-EF60-4F29-AA56-95DFDA23C913}.Release|Any CPU.ActiveCfg = Release|Any CPU
{422FEC21-EF60-4F29-AA56-95DFDA23C913}.Release|Any CPU.Build.0 = Release|Any CPU
{422FEC21-EF60-4F29-AA56-95DFDA23C913}.Release|x64.ActiveCfg = Release|Any CPU
{422FEC21-EF60-4F29-AA56-95DFDA23C913}.Release|x64.Build.0 = Release|Any CPU
{422FEC21-EF60-4F29-AA56-95DFDA23C913}.Release|x86.ActiveCfg = Release|Any CPU
{422FEC21-EF60-4F29-AA56-95DFDA23C913}.Release|x86.Build.0 = Release|Any CPU
{0B2BE613-3049-4021-85D1-21C325F729F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0B2BE613-3049-4021-85D1-21C325F729F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0B2BE613-3049-4021-85D1-21C325F729F4}.Debug|x64.ActiveCfg = Debug|Any CPU
{0B2BE613-3049-4021-85D1-21C325F729F4}.Debug|x64.Build.0 = Debug|Any CPU
{0B2BE613-3049-4021-85D1-21C325F729F4}.Debug|x86.ActiveCfg = Debug|Any CPU
{0B2BE613-3049-4021-85D1-21C325F729F4}.Debug|x86.Build.0 = Debug|Any CPU
{0B2BE613-3049-4021-85D1-21C325F729F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0B2BE613-3049-4021-85D1-21C325F729F4}.Release|Any CPU.Build.0 = Release|Any CPU
{0B2BE613-3049-4021-85D1-21C325F729F4}.Release|x64.ActiveCfg = Release|Any CPU
{0B2BE613-3049-4021-85D1-21C325F729F4}.Release|x64.Build.0 = Release|Any CPU
{0B2BE613-3049-4021-85D1-21C325F729F4}.Release|x86.ActiveCfg = Release|Any CPU
{0B2BE613-3049-4021-85D1-21C325F729F4}.Release|x86.Build.0 = Release|Any CPU
{E301AFEA-84E7-4BCE-8D65-A2576D8D105B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E301AFEA-84E7-4BCE-8D65-A2576D8D105B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E301AFEA-84E7-4BCE-8D65-A2576D8D105B}.Debug|x64.ActiveCfg = Debug|Any CPU
{E301AFEA-84E7-4BCE-8D65-A2576D8D105B}.Debug|x64.Build.0 = Debug|Any CPU
{E301AFEA-84E7-4BCE-8D65-A2576D8D105B}.Debug|x86.ActiveCfg = Debug|Any CPU
{E301AFEA-84E7-4BCE-8D65-A2576D8D105B}.Debug|x86.Build.0 = Debug|Any CPU
{E301AFEA-84E7-4BCE-8D65-A2576D8D105B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E301AFEA-84E7-4BCE-8D65-A2576D8D105B}.Release|Any CPU.Build.0 = Release|Any CPU
{E301AFEA-84E7-4BCE-8D65-A2576D8D105B}.Release|x64.ActiveCfg = Release|Any CPU
{E301AFEA-84E7-4BCE-8D65-A2576D8D105B}.Release|x64.Build.0 = Release|Any CPU
{E301AFEA-84E7-4BCE-8D65-A2576D8D105B}.Release|x86.ActiveCfg = Release|Any CPU
{E301AFEA-84E7-4BCE-8D65-A2576D8D105B}.Release|x86.Build.0 = Release|Any CPU
{29EAD018-1C67-497A-AB8E-727D595AD756}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{29EAD018-1C67-497A-AB8E-727D595AD756}.Debug|Any CPU.Build.0 = Debug|Any CPU
{29EAD018-1C67-497A-AB8E-727D595AD756}.Debug|x64.ActiveCfg = Debug|Any CPU
{29EAD018-1C67-497A-AB8E-727D595AD756}.Debug|x64.Build.0 = Debug|Any CPU
{29EAD018-1C67-497A-AB8E-727D595AD756}.Debug|x86.ActiveCfg = Debug|Any CPU
{29EAD018-1C67-497A-AB8E-727D595AD756}.Debug|x86.Build.0 = Debug|Any CPU
{29EAD018-1C67-497A-AB8E-727D595AD756}.Release|Any CPU.ActiveCfg = Release|Any CPU
{29EAD018-1C67-497A-AB8E-727D595AD756}.Release|Any CPU.Build.0 = Release|Any CPU
{29EAD018-1C67-497A-AB8E-727D595AD756}.Release|x64.ActiveCfg = Release|Any CPU
{29EAD018-1C67-497A-AB8E-727D595AD756}.Release|x64.Build.0 = Release|Any CPU
{29EAD018-1C67-497A-AB8E-727D595AD756}.Release|x86.ActiveCfg = Release|Any CPU
{29EAD018-1C67-497A-AB8E-727D595AD756}.Release|x86.Build.0 = Release|Any CPU
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Debug|x64.ActiveCfg = Debug|Any CPU
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Debug|x64.Build.0 = Debug|Any CPU
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Debug|x86.ActiveCfg = Debug|Any CPU
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Debug|x86.Build.0 = Debug|Any CPU
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Release|Any CPU.Build.0 = Release|Any CPU
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Release|x64.ActiveCfg = Release|Any CPU
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Release|x64.Build.0 = Release|Any CPU
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Release|x86.ActiveCfg = Release|Any CPU
{65EAFFA3-01D3-4EF5-B092-8B4647E9A1FF}.Release|x86.Build.0 = Release|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Debug|x64.ActiveCfg = Debug|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Debug|x64.Build.0 = Debug|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Debug|x86.ActiveCfg = Debug|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Debug|x86.Build.0 = Debug|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Release|Any CPU.Build.0 = Release|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Release|x64.ActiveCfg = Release|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Release|x64.Build.0 = Release|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Release|x86.ActiveCfg = Release|Any CPU
{6438FEC1-56B0-488C-A5E2-FBDB23E9574B}.Release|x86.Build.0 = Release|Any CPU
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|Any CPU.ActiveCfg = Debug|Win32
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|Any CPU.Build.0 = Debug|Win32
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|x64.ActiveCfg = Debug|x64
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|x64.Build.0 = Debug|x64
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|x86.ActiveCfg = Debug|Win32
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Debug|x86.Build.0 = Debug|Win32
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|Any CPU.ActiveCfg = Release|Win32
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|Any CPU.Build.0 = Release|Win32
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|x64.ActiveCfg = Release|x64
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|x64.Build.0 = Release|x64
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|x86.ActiveCfg = Release|Win32
{11EA25A3-ED68-40EE-A9D0-7FDE3B583027}.Release|x86.Build.0 = Release|Win32
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|Any CPU.ActiveCfg = Debug|Win32
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|Any CPU.Build.0 = Debug|Win32
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|x64.ActiveCfg = Debug|x64
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|x64.Build.0 = Debug|x64
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|x86.ActiveCfg = Debug|Win32
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Debug|x86.Build.0 = Debug|Win32
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|Any CPU.ActiveCfg = Release|Win32
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|Any CPU.Build.0 = Release|Win32
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x64.ActiveCfg = Release|x64
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x64.Build.0 = Release|x64
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x86.ActiveCfg = Release|Win32
{29356642-C46E-4144-83D8-22DC09D0D7FD}.Release|x86.Build.0 = Release|Win32
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Debug|x64.ActiveCfg = Debug|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Debug|x64.Build.0 = Debug|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Debug|x86.ActiveCfg = Debug|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Debug|x86.Build.0 = Debug|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Release|Any CPU.Build.0 = Release|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Release|x64.ActiveCfg = Release|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Release|x64.Build.0 = Release|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Release|x86.ActiveCfg = Release|Any CPU
{34B6329B-0E73-45AC-B8CC-015F119F63DC}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F8734F96-97B6-40CA-B791-6D5467F2F713}
SolutionGuid = {3C074481-9CDD-4780-B9F6-57BBC5092EA2}
EndGlobalSection
EndGlobal

View File

@@ -1,154 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{7662F8C2-7BFD-442E-A948-A43B4F7EB06E}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>AssetStudio</RootNamespace>
<AssemblyName>AssetStudio</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows;net8.0;net8.0-windows</TargetFrameworks>
<Version>1.2.0</Version>
<Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2025</Copyright>
<DebugType>embedded</DebugType>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.8" />
</ItemGroup>
<ItemGroup>
<Compile Include="7zip\Common\CommandLineParser.cs" />
<Compile Include="7zip\Common\CRC.cs" />
<Compile Include="7zip\Common\InBuffer.cs" />
<Compile Include="7zip\Common\OutBuffer.cs" />
<Compile Include="7zip\Compress\LZMA\LzmaBase.cs" />
<Compile Include="7zip\Compress\LZMA\LzmaDecoder.cs" />
<Compile Include="7zip\Compress\LZMA\LzmaEncoder.cs" />
<Compile Include="7zip\Compress\LZ\IMatchFinder.cs" />
<Compile Include="7zip\Compress\LZ\LzBinTree.cs" />
<Compile Include="7zip\Compress\LZ\LzInWindow.cs" />
<Compile Include="7zip\Compress\LZ\LzOutWindow.cs" />
<Compile Include="7zip\Compress\RangeCoder\RangeCoder.cs" />
<Compile Include="7zip\Compress\RangeCoder\RangeCoderBit.cs" />
<Compile Include="7zip\Compress\RangeCoder\RangeCoderBitTree.cs" />
<Compile Include="7zip\ICoder.cs" />
<Compile Include="AssetsManager.cs" />
<Compile Include="Brotli\BitReader.cs" />
<Compile Include="Brotli\BrotliInputStream.cs" />
<Compile Include="Brotli\BrotliRuntimeException.cs" />
<Compile Include="Brotli\Context.cs" />
<Compile Include="Brotli\Decode.cs" />
<Compile Include="Brotli\Dictionary.cs" />
<Compile Include="Brotli\Huffman.cs" />
<Compile Include="Brotli\HuffmanTreeGroup.cs" />
<Compile Include="Brotli\IntReader.cs" />
<Compile Include="Brotli\Prefix.cs" />
<Compile Include="Brotli\RunningState.cs" />
<Compile Include="Brotli\State.cs" />
<Compile Include="Brotli\Transform.cs" />
<Compile Include="Brotli\Utils.cs" />
<Compile Include="Brotli\WordTransformType.cs" />
<Compile Include="BuildTarget.cs" />
<Compile Include="BuildType.cs" />
<Compile Include="BundleFile.cs" />
<Compile Include="Classes\Animation.cs" />
<Compile Include="Classes\AnimationClip.cs" />
<Compile Include="Classes\Animator.cs" />
<Compile Include="Classes\AnimatorController.cs" />
<Compile Include="Classes\AnimatorOverrideController.cs" />
<Compile Include="Classes\AssetBundle.cs" />
<Compile Include="Classes\AudioClip.cs" />
<Compile Include="Classes\Avatar.cs" />
<Compile Include="Classes\Behaviour.cs" />
<Compile Include="Classes\BuildSettings.cs" />
<Compile Include="Classes\Component.cs" />
<Compile Include="Classes\EditorExtension.cs" />
<Compile Include="Classes\Font.cs" />
<Compile Include="Classes\GameObject.cs" />
<Compile Include="Classes\Material.cs" />
<Compile Include="Classes\Mesh.cs" />
<Compile Include="Classes\MeshFilter.cs" />
<Compile Include="Classes\MeshRenderer.cs" />
<Compile Include="Classes\MonoBehaviour.cs" />
<Compile Include="Classes\MonoScript.cs" />
<Compile Include="Classes\MovieTexture.cs" />
<Compile Include="Classes\NamedObject.cs" />
<Compile Include="Classes\Object.cs" />
<Compile Include="Classes\PlayerSettings.cs" />
<Compile Include="Classes\PPtr.cs" />
<Compile Include="Classes\RectTransform.cs" />
<Compile Include="Classes\Renderer.cs" />
<Compile Include="Classes\RuntimeAnimatorController.cs" />
<Compile Include="Classes\Shader.cs" />
<Compile Include="Classes\SkinnedMeshRenderer.cs" />
<Compile Include="Classes\Sprite.cs" />
<Compile Include="Classes\SpriteAtlas.cs" />
<Compile Include="Classes\TextAsset.cs" />
<Compile Include="Classes\Texture.cs" />
<Compile Include="Classes\Texture2D.cs" />
<Compile Include="Classes\Transform.cs" />
<Compile Include="Classes\VideoClip.cs" />
<Compile Include="ClassIDType.cs" />
<Compile Include="CommonString.cs" />
<Compile Include="EndianBinaryReader.cs" />
<Compile Include="Extensions\BinaryReaderExtensions.cs" />
<Compile Include="Extensions\BinaryWriterExtensions.cs" />
<Compile Include="Extensions\StreamExtensions.cs" />
<Compile Include="FileIdentifier.cs" />
<Compile Include="IImported.cs" />
<Compile Include="ILogger.cs" />
<Compile Include="ImportHelper.cs" />
<Compile Include="IProgress.cs" />
<Compile Include="LocalSerializedObjectIdentifier.cs" />
<Compile Include="Logger.cs" />
<Compile Include="Lz4DecoderStream.cs" />
<Compile Include="Math\Color.cs" />
<Compile Include="Math\Half.cs" />
<Compile Include="Math\HalfHelper.cs" />
<Compile Include="Math\Matrix4x4.cs" />
<Compile Include="Math\Quaternion.cs" />
<Compile Include="Math\Vector2.cs" />
<Compile Include="Math\Vector3.cs" />
<Compile Include="Math\Vector4.cs" />
<Compile Include="ObjectInfo.cs" />
<Compile Include="ObjectReader.cs" />
<Compile Include="Progress.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ResourceReader.cs" />
<Compile Include="SerializedFile.cs" />
<Compile Include="SerializedFileHeader.cs" />
<Compile Include="SerializedType.cs" />
<Compile Include="SevenZipHelper.cs" />
<Compile Include="StreamFile.cs" />
<Compile Include="TypeTreeHelper.cs" />
<Compile Include="TypeTreeNode.cs" />
<Compile Include="UType.cs" />
<Compile Include="WebFile.cs" />
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
</Project>

View File

@@ -1,34 +1,108 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using static AssetStudio.ImportHelper;
namespace AssetStudio
{
public class AssetsManager
{
public string SpecifyUnityVersion;
public List<SerializedFile> assetsFileList = new List<SerializedFile>();
private HashSet<ClassIDType> filteredAssetTypesList = new HashSet<ClassIDType>();
internal Dictionary<string, int> assetsFileIndexCache = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
internal Dictionary<string, BinaryReader> resourceFileReaders = new Dictionary<string, BinaryReader>(StringComparer.OrdinalIgnoreCase);
private List<string> importFiles = new List<string>();
private HashSet<string> importFilesHash = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private HashSet<string> noexistFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private HashSet<string> assetsFileListHash = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
public void LoadFiles(params string[] files)
public void SetAssetFilter(params ClassIDType[] classIDTypes)
{
var path = Path.GetDirectoryName(files[0]);
MergeSplitAssets(path);
var toReadFile = ProcessingSplitFiles(files.ToList());
Load(toReadFile);
if (filteredAssetTypesList.Count == 0)
{
filteredAssetTypesList.UnionWith(new HashSet<ClassIDType>
{
ClassIDType.AssetBundle,
ClassIDType.ResourceManager,
});
}
if (classIDTypes.Contains(ClassIDType.MonoBehaviour))
{
filteredAssetTypesList.Add(ClassIDType.MonoScript);
}
if (classIDTypes.Contains(ClassIDType.Sprite) || classIDTypes.Contains(ClassIDType.AkPortraitSprite))
{
filteredAssetTypesList.UnionWith(new HashSet<ClassIDType>
{
ClassIDType.Texture2D,
ClassIDType.SpriteAtlas,
ClassIDType.MonoBehaviour,
ClassIDType.MonoScript
});
}
filteredAssetTypesList.UnionWith(classIDTypes);
}
public void LoadFolder(string path)
public void SetAssetFilter(List<ClassIDType> classIDTypeList)
{
MergeSplitAssets(path, true);
var files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories).ToList();
var toReadFile = ProcessingSplitFiles(files);
SetAssetFilter(classIDTypeList.ToArray());
}
public void LoadFilesAndFolders(params string[] path)
{
List<string> pathList = new List<string>();
pathList.AddRange(path);
LoadFilesAndFolders(out _, pathList);
}
public void LoadFilesAndFolders(out string parentPath, params string[] path)
{
List<string> pathList = new List<string>();
pathList.AddRange(path);
LoadFilesAndFolders(out parentPath, pathList);
}
public void LoadFilesAndFolders(out string parentPath, List<string> pathList)
{
List<string> fileList = new List<string>();
bool filesInPath = false;
parentPath = "";
foreach (var path in pathList)
{
var fullPath = Path.GetFullPath(path);
if (Directory.Exists(fullPath))
{
var parent = Directory.GetParent(fullPath).FullName;
if (!filesInPath && (parentPath == "" || parentPath.Length > parent.Length))
{
parentPath = parent;
}
MergeSplitAssets(fullPath, true);
fileList.AddRange(Directory.GetFiles(fullPath, "*.*", SearchOption.AllDirectories));
}
else if (File.Exists(fullPath))
{
parentPath = Path.GetDirectoryName(fullPath);
fileList.Add(fullPath);
filesInPath = true;
}
}
if (filesInPath)
{
MergeSplitAssets(parentPath);
}
var toReadFile = ProcessingSplitFiles(fileList);
fileList.Clear();
pathList.Clear();
Load(toReadFile);
}
@@ -50,6 +124,7 @@ namespace AssetStudio
importFiles.Clear();
importFilesHash.Clear();
noexistFiles.Clear();
assetsFileListHash.Clear();
ReadAssets();
@@ -58,121 +133,158 @@ namespace AssetStudio
private void LoadFile(string fullName)
{
switch (CheckFileType(fullName, out var reader))
var reader = new FileReader(fullName);
LoadFile(reader);
}
private void LoadFile(FileReader reader)
{
switch (reader?.FileType)
{
case FileType.AssetsFile:
LoadAssetsFile(fullName, reader);
LoadAssetsFile(reader);
break;
case FileType.BundleFile:
LoadBundleFile(fullName, reader);
LoadBundleFile(reader);
break;
case FileType.WebFile:
LoadWebFile(fullName, reader);
LoadWebFile(reader);
break;
case FileType.GZipFile:
LoadFile(DecompressGZip(reader));
break;
case FileType.BrotliFile:
LoadFile(DecompressBrotli(reader));
break;
case FileType.ZipFile:
LoadZipFile(reader);
break;
}
}
private void LoadAssetsFile(string fullName, EndianBinaryReader reader)
private void LoadAssetsFile(FileReader reader)
{
var fileName = Path.GetFileName(fullName);
if (!assetsFileListHash.Contains(fileName))
if (!assetsFileListHash.Contains(reader.FileName))
{
Logger.Info($"Loading {fileName}");
Logger.Info($"Loading {reader.FullPath}");
try
{
var assetsFile = new SerializedFile(this, fullName, reader);
var assetsFile = new SerializedFile(reader, this);
CheckStrippedVersion(assetsFile);
assetsFileList.Add(assetsFile);
assetsFileListHash.Add(assetsFile.fileName);
foreach (var sharedFile in assetsFile.m_Externals)
{
var sharedFilePath = Path.GetDirectoryName(fullName) + "\\" + sharedFile.fileName;
var sharedFileName = sharedFile.fileName;
if (!importFilesHash.Contains(sharedFileName))
{
if (!File.Exists(sharedFilePath))
var sharedFilePath = Path.Combine(Path.GetDirectoryName(reader.FullPath), sharedFileName);
if (!noexistFiles.Contains(sharedFilePath))
{
var findFiles = Directory.GetFiles(Path.GetDirectoryName(fullName), sharedFileName, SearchOption.AllDirectories);
if (findFiles.Length > 0)
if (!File.Exists(sharedFilePath))
{
sharedFilePath = findFiles[0];
var findFiles = Directory.GetFiles(Path.GetDirectoryName(reader.FullPath), sharedFileName, SearchOption.AllDirectories);
if (findFiles.Length > 0)
{
sharedFilePath = findFiles[0];
}
}
if (File.Exists(sharedFilePath))
{
importFiles.Add(sharedFilePath);
importFilesHash.Add(sharedFileName);
}
else
{
noexistFiles.Add(sharedFilePath);
}
}
if (File.Exists(sharedFilePath))
{
importFiles.Add(sharedFilePath);
importFilesHash.Add(sharedFileName);
}
}
}
}
catch
catch (NotSupportedException e)
{
Logger.Error(e.Message);
reader.Dispose();
}
catch (Exception e)
{
Logger.Warning($"Error while reading assets file {reader.FullPath}\r\n{e}");
reader.Dispose();
//Logger.Warning($"Unable to load assets file {fileName}");
}
}
else
{
Logger.Info($"Skipping {reader.FullPath}");
reader.Dispose();
}
}
private void LoadAssetsFromMemory(string fullName, EndianBinaryReader reader, string originalPath, string unityVersion = null)
private void LoadAssetsFromMemory(FileReader reader, string originalPath, string unityVersion = null)
{
var fileName = Path.GetFileName(fullName);
if (!assetsFileListHash.Contains(fileName))
if (!assetsFileListHash.Contains(reader.FileName))
{
try
{
var assetsFile = new SerializedFile(this, fullName, reader);
var assetsFile = new SerializedFile(reader, this);
assetsFile.originalPath = originalPath;
if (assetsFile.header.m_Version < 7)
if (!string.IsNullOrEmpty(unityVersion) && assetsFile.header.m_Version < SerializedFileFormatVersion.Unknown_7)
{
assetsFile.SetVersion(unityVersion);
}
CheckStrippedVersion(assetsFile);
assetsFileList.Add(assetsFile);
assetsFileListHash.Add(assetsFile.fileName);
}
catch
catch (NotSupportedException e)
{
//Logger.Error($"Unable to load assets file {fileName} from {Path.GetFileName(originalPath)}");
resourceFileReaders.Add(fileName, reader);
Logger.Error(e.Message);
resourceFileReaders.Add(reader.FileName, reader);
}
catch (Exception e)
{
Logger.Warning($"Error while reading assets file {reader.FullPath} from {Path.GetFileName(originalPath)}\r\n{e}");
resourceFileReaders.Add(reader.FileName, reader);
}
}
else
Logger.Info($"Skipping {originalPath} ({reader.FileName})");
}
private void LoadBundleFile(string fullName, EndianBinaryReader reader, string parentPath = null)
private void LoadBundleFile(FileReader reader, string originalPath = null)
{
var fileName = Path.GetFileName(fullName);
Logger.Info("Loading " + fileName);
Logger.Info("Loading " + reader.FullPath);
try
{
var bundleFile = new BundleFile(reader, fullName);
var bundleFile = new BundleFile(reader, SpecifyUnityVersion);
foreach (var file in bundleFile.fileList)
{
var subReader = new EndianBinaryReader(file.stream);
if (SerializedFile.IsSerializedFile(subReader))
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName);
var subReader = new FileReader(dummyPath, file.stream);
if (subReader.FileType == FileType.AssetsFile)
{
var dummyPath = Path.GetDirectoryName(fullName) + Path.DirectorySeparatorChar + file.fileName;
LoadAssetsFromMemory(dummyPath, subReader, parentPath ?? fullName, bundleFile.m_Header.unityRevision);
LoadAssetsFromMemory(subReader, originalPath ?? reader.FullPath, bundleFile.m_Header.unityRevision);
}
else
else if (!resourceFileReaders.ContainsKey(file.fileName))
{
resourceFileReaders.Add(file.fileName, subReader);
}
}
}
catch
catch (NotSupportedException e)
{
/*var str = $"Unable to load bundle file {fileName}";
if (parentPath != null)
Logger.Error(e.Message);
}
catch (Exception e)
{
var str = $"Error while reading bundle file {reader.FullPath}";
if (originalPath != null)
{
str += $" from {Path.GetFileName(parentPath)}";
str += $" from {Path.GetFileName(originalPath)}";
}
Logger.Error(str);*/
Logger.Warning($"{str}\r\n{e}");
}
finally
{
@@ -180,36 +292,36 @@ namespace AssetStudio
}
}
private void LoadWebFile(string fullName, EndianBinaryReader reader)
private void LoadWebFile(FileReader reader)
{
var fileName = Path.GetFileName(fullName);
Logger.Info("Loading " + fileName);
Logger.Info("Loading " + reader.FullPath);
try
{
var webFile = new WebFile(reader);
foreach (var file in webFile.fileList)
{
var dummyPath = Path.GetDirectoryName(fullName) + "\\" + file.fileName;
switch (CheckFileType(file.stream, out var fileReader))
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName);
var subReader = new FileReader(dummyPath, file.stream);
switch (subReader.FileType)
{
case FileType.AssetsFile:
LoadAssetsFromMemory(dummyPath, fileReader, fullName);
LoadAssetsFromMemory(subReader, reader.FullPath);
break;
case FileType.BundleFile:
LoadBundleFile(dummyPath, fileReader, fullName);
LoadBundleFile(subReader, reader.FullPath);
break;
case FileType.WebFile:
LoadWebFile(dummyPath, fileReader);
LoadWebFile(subReader);
break;
case FileType.ResourceFile:
resourceFileReaders.Add(file.fileName, fileReader);
resourceFileReaders[file.fileName] = subReader; //TODO
break;
}
}
}
catch
catch (Exception e)
{
//Logger.Error($"Unable to load web file {fileName}");
Logger.Error($"Error while reading web file {reader.FullPath}", e);
}
finally
{
@@ -217,6 +329,122 @@ namespace AssetStudio
}
}
private void LoadZipFile(FileReader reader)
{
Logger.Info("Reading " + reader.FileName);
try
{
using (ZipArchive archive = new ZipArchive(reader.BaseStream, ZipArchiveMode.Read))
{
List<string> splitFiles = new List<string>();
// register all files before parsing the assets so that the external references can be found
// and find split files
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.Name.Contains(".split"))
{
string baseName = Path.GetFileNameWithoutExtension(entry.Name);
string basePath = Path.Combine(Path.GetDirectoryName(entry.FullName), baseName);
if (!splitFiles.Contains(basePath))
{
splitFiles.Add(basePath);
importFilesHash.Add(baseName);
}
}
else
{
importFilesHash.Add(entry.Name);
}
}
// merge split files and load the result
foreach (string basePath in splitFiles)
{
try
{
Stream splitStream = new MemoryStream();
int i = 0;
while (true)
{
string path = $"{basePath}.split{i++}";
ZipArchiveEntry entry = archive.GetEntry(path);
if (entry == null)
break;
using (Stream entryStream = entry.Open())
{
entryStream.CopyTo(splitStream);
}
}
splitStream.Seek(0, SeekOrigin.Begin);
FileReader entryReader = new FileReader(basePath, splitStream);
LoadFile(entryReader);
}
catch (Exception e)
{
Logger.Warning($"Error while reading zip split file {basePath}\r\n{e}");
}
}
// load all entries
var progressCount = archive.Entries.Count;
int k = 0;
Progress.Reset();
foreach (ZipArchiveEntry entry in archive.Entries)
{
try
{
string dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), reader.FileName, entry.FullName);
// create a new stream
// - to store the deflated stream in
// - to keep the data for later extraction
Stream streamReader = new MemoryStream();
using (Stream entryStream = entry.Open())
{
entryStream.CopyTo(streamReader);
}
streamReader.Position = 0;
FileReader entryReader = new FileReader(dummyPath, streamReader);
LoadFile(entryReader);
if (entryReader.FileType == FileType.ResourceFile)
{
entryReader.Position = 0;
if (!resourceFileReaders.ContainsKey(entry.Name))
{
resourceFileReaders.Add(entry.Name, entryReader);
}
}
Progress.Report(++k, progressCount);
}
catch (Exception e)
{
Logger.Warning($"Error while reading zip entry {entry.FullName}\r\n{e}");
}
}
}
}
catch (Exception e)
{
Logger.Error($"Error while reading zip file {reader.FileName}", e);
}
finally
{
reader.Dispose();
}
}
public void CheckStrippedVersion(SerializedFile assetsFile)
{
if (assetsFile.IsVersionStripped && string.IsNullOrEmpty(SpecifyUnityVersion))
{
throw new NotSupportedException("The Unity version has been stripped, please set the version in the options");
}
if (!string.IsNullOrEmpty(SpecifyUnityVersion))
{
assetsFile.SetVersion(SpecifyUnityVersion);
}
}
public void Clear()
{
foreach (var assetsFile in assetsFileList)
@@ -247,9 +475,13 @@ namespace AssetStudio
foreach (var objectInfo in assetsFile.m_Objects)
{
var objectReader = new ObjectReader(assetsFile.reader, assetsFile, objectInfo);
if (filteredAssetTypesList.Count > 0 && !filteredAssetTypesList.Contains(objectReader.type))
{
continue;
}
try
{
Object obj;
Object obj = null;
switch (objectReader.type)
{
case ClassIDType.Animation:
@@ -306,11 +538,15 @@ namespace AssetStudio
case ClassIDType.PlayerSettings:
obj = new PlayerSettings(objectReader);
break;
case ClassIDType.PreloadData:
obj = new PreloadData(objectReader);
break;
case ClassIDType.RectTransform:
obj = new RectTransform(objectReader);
break;
case ClassIDType.Shader:
obj = new Shader(objectReader);
if (objectReader.version[0] < 2021)
obj = new Shader(objectReader);
break;
case ClassIDType.SkinnedMeshRenderer:
obj = new SkinnedMeshRenderer(objectReader);
@@ -333,21 +569,26 @@ namespace AssetStudio
case ClassIDType.VideoClip:
obj = new VideoClip(objectReader);
break;
case ClassIDType.ResourceManager:
obj = new ResourceManager(objectReader);
break;
default:
obj = new Object(objectReader);
break;
}
assetsFile.AddObject(obj);
if (obj != null)
assetsFile.AddObject(obj);
}
catch (Exception e)
{
/*var sb = new StringBuilder();
var sb = new StringBuilder();
sb.AppendLine("Unable to load object")
.AppendLine($"Assets {assetsFile.fileName}")
.AppendLine($"Path {assetsFile.originalPath}")
.AppendLine($"Type {objectReader.type}")
.AppendLine($"PathID {objectInfo.m_PathID}")
.Append(e);
Logger.Error(sb.ToString());*/
Logger.Warning(sb.ToString());
}
Progress.Report(++i, progressCount);
@@ -357,7 +598,7 @@ namespace AssetStudio
private void ProcessAssets()
{
Logger.Info("Process Assets...");
Logger.Info("Process assets...");
foreach (var assetsFile in assetsFileList)
{
@@ -403,6 +644,17 @@ namespace AssetStudio
{
m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas);
}
else if (m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlaOld))
{
if (m_SpriteAtlaOld.m_IsVariant)
{
m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas);
}
}
else
{
Logger.Warning($"\"{m_Sprite.m_Name}\": Sprite loading error. SpriteAtlas with PathID: \"{m_Sprite.m_SpriteAtlas.m_PathID}\" was not found.");
}
}
}
}
@@ -410,4 +662,4 @@ namespace AssetStudio
}
}
}
}
}

View File

@@ -0,0 +1,10 @@
using System.Buffers;
namespace AssetStudio
{
public static class BigArrayPool<T>
{
private static readonly ArrayPool<T> s_shared = ArrayPool<T>.Create(64 * 1024 * 1024, 3);
public static ArrayPool<T> Shared => s_shared;
}
}

View File

@@ -7,8 +7,9 @@ namespace AssetStudio
{
public enum BuildTarget
{
UnknownPlatform = 3716,
DashboardWidget = 1,
NoTarget = -2,
AnyPlayer = -1,
ValidPlayer = 1,
StandaloneOSX = 2,
StandaloneOSXPPC = 3,
StandaloneOSXIntel = 4,
@@ -19,8 +20,10 @@ namespace AssetStudio
iOS = 9,
PS3,
XBOX360,
Broadcom = 12,
Android = 13,
StandaloneGLESEmu = 14,
StandaloneGLES20Emu = 15,
NaCl = 16,
StandaloneLinux = 17,
FlashPlayer = 18,
@@ -42,6 +45,14 @@ namespace AssetStudio
WiiU,
tvOS,
Switch,
NoTarget = -2
Lumin,
Stadia,
CloudRendering,
GameCoreXboxSeries,
GameCoreXboxOne,
PS5,
EmbeddedLinux,
QNX,
UnknownPlatform = 9999
}
}

View File

@@ -1,11 +1,43 @@
using System;
using System.Collections.Generic;
using K4os.Compression.LZ4;
using System;
using System.IO;
using System.Linq;
using Lz4;
namespace AssetStudio
{
[Flags]
public enum ArchiveFlags
{
CompressionTypeMask = 0x3f,
BlocksAndDirectoryInfoCombined = 0x40,
BlocksInfoAtTheEnd = 0x80,
OldWebPluginCompatibility = 0x100,
BlockInfoNeedPaddingAtStart = 0x200
}
[Flags]
public enum CnEncryptionFlags
{
OldFlag = 0x200,
NewFlag = 0x400
}
[Flags]
public enum StorageBlockFlags
{
CompressionTypeMask = 0x3f,
Streamed = 0x40
}
public enum CompressionType
{
None,
Lzma,
Lz4,
Lz4HC,
Lz4Inv,
}
public class BundleFile
{
public class Header
@@ -17,14 +49,14 @@ namespace AssetStudio
public long size;
public uint compressedBlocksInfoSize;
public uint uncompressedBlocksInfoSize;
public uint flags;
public ArchiveFlags flags;
}
public class StorageBlock
{
public uint compressedSize;
public uint uncompressedSize;
public ushort flags;
public StorageBlockFlags flags;
}
public class Node
@@ -41,30 +73,61 @@ namespace AssetStudio
public StreamFile[] fileList;
public BundleFile(EndianBinaryReader reader, string path)
public BundleFile(FileReader reader, string specUnityVer = "")
{
m_Header = new Header();
m_Header.signature = reader.ReadStringToNull();
m_Header.version = reader.ReadUInt32();
m_Header.unityVersion = reader.ReadStringToNull();
m_Header.unityRevision = reader.ReadStringToNull();
switch (m_Header.signature)
{
case "UnityArchive":
break; //TODO
case "UnityWeb":
case "UnityRaw":
if (m_Header.version == 6)
{
goto case "UnityFS";
}
ReadHeaderAndBlocksInfo(reader);
using (var blocksStream = CreateBlocksStream(path))
using (var blocksStream = CreateBlocksStream(reader.FullPath))
{
ReadBlocksAndDirectory(reader, blocksStream);
ReadFiles(blocksStream, path);
ReadFiles(blocksStream, reader.FullPath);
}
break;
case "UnityFS":
ReadHeader(reader);
ReadBlocksInfoAndDirectory(reader);
using (var blocksStream = CreateBlocksStream(path))
bool isUnityCnEnc = false;
string unityVer = string.IsNullOrEmpty(specUnityVer) ? m_Header.unityRevision : specUnityVer;
int[] ver = new string(unityVer.SkipWhile(x => !char.IsDigit(x)).TakeWhile(x => char.IsDigit(x) || x == '.').ToArray()).Split('.').Select(x => int.Parse(x)).ToArray();
if (ver[0] != 0)
{
// https://issuetracker.unity3d.com/issues/files-within-assetbundles-do-not-start-on-aligned-boundaries-breaking-patching-on-nintendo-switch
if (ver[0] < 2020 ||
(ver[0] == 2020 && ver[1] <= 3 && ver[2] < 34) ||
(ver[0] == 2021 && ver[1] <= 3 && ver[2] < 2) ||
(ver[0] == 2022 && ver[1] <= 1 && ver[2] < 1))
{
isUnityCnEnc = ((CnEncryptionFlags)m_Header.flags & CnEncryptionFlags.OldFlag) != 0;
}
else
{
isUnityCnEnc = ((CnEncryptionFlags)m_Header.flags & CnEncryptionFlags.NewFlag) != 0;
}
}
if (isUnityCnEnc)
{
throw new NotSupportedException("Unsupported bundle file. UnityCN encryption was detected.");
}
ReadBlocksInfoAndDirectory(reader, ver);
using (var blocksStream = CreateBlocksStream(reader.FullPath))
{
ReadBlocks(reader, blocksStream);
ReadFiles(blocksStream, path);
ReadFiles(blocksStream, reader.FullPath);
}
break;
}
@@ -72,17 +135,13 @@ namespace AssetStudio
private void ReadHeaderAndBlocksInfo(EndianBinaryReader reader)
{
var isCompressed = m_Header.signature == "UnityWeb";
m_Header.version = reader.ReadUInt32();
m_Header.unityVersion = reader.ReadStringToNull();
m_Header.unityRevision = reader.ReadStringToNull();
if (m_Header.version >= 4)
{
var hash = reader.ReadBytes(16);
var crc = reader.ReadUInt32();
}
var minimumStreamedBytes = reader.ReadUInt32();
var headerSize = reader.ReadUInt32();
m_Header.size = reader.ReadUInt32();
var numberOfLevelsToDownloadBeforeStreaming = reader.ReadUInt32();
var levelCount = reader.ReadInt32();
m_BlocksInfo = new StorageBlock[1];
@@ -92,7 +151,6 @@ namespace AssetStudio
{
compressedSize = reader.ReadUInt32(),
uncompressedSize = reader.ReadUInt32(),
flags = (ushort)(isCompressed ? 1 : 0)
};
if (i == levelCount - 1)
{
@@ -107,7 +165,7 @@ namespace AssetStudio
{
var fileInfoHeaderSize = reader.ReadUInt32();
}
reader.Position = headerSize;
reader.Position = m_Header.size;
}
private Stream CreateBlocksStream(string path)
@@ -116,7 +174,7 @@ namespace AssetStudio
var uncompressedSizeSum = m_BlocksInfo.Sum(x => x.uncompressedSize);
if (uncompressedSizeSum >= int.MaxValue)
{
/*var memoryMappedFile = MemoryMappedFile.CreateNew(Path.GetFileName(path), uncompressedSizeSum);
/*var memoryMappedFile = MemoryMappedFile.CreateNew(null, uncompressedSizeSum);
assetsDataStream = memoryMappedFile.CreateViewStream();*/
blocksStream = new FileStream(path + ".temp", FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.DeleteOnClose);
}
@@ -129,10 +187,11 @@ namespace AssetStudio
private void ReadBlocksAndDirectory(EndianBinaryReader reader, Stream blocksStream)
{
var isCompressed = m_Header.signature == "UnityWeb";
foreach (var blockInfo in m_BlocksInfo)
{
var uncompressedBytes = reader.ReadBytes((int)blockInfo.compressedSize);
if (blockInfo.flags == 1)
if (isCompressed)
{
using (var memoryStream = new MemoryStream(uncompressedBytes))
{
@@ -167,14 +226,15 @@ namespace AssetStudio
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)
{
/*var memoryMappedFile = MemoryMappedFile.CreateNew(file.fileName, entryinfo_size);
/*var memoryMappedFile = MemoryMappedFile.CreateNew(null, entryinfo_size);
file.stream = memoryMappedFile.CreateViewStream();*/
var extractPath = path + "_unpacked" + Path.DirectorySeparatorChar;
Directory.CreateDirectory(extractPath);
file.stream = File.Create(extractPath + file.fileName);
file.stream = new FileStream(extractPath + file.fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
}
else
{
@@ -188,59 +248,82 @@ namespace AssetStudio
private void ReadHeader(EndianBinaryReader reader)
{
m_Header.version = reader.ReadUInt32();
m_Header.unityVersion = reader.ReadStringToNull();
m_Header.unityRevision = reader.ReadStringToNull();
m_Header.size = reader.ReadInt64();
m_Header.compressedBlocksInfoSize = reader.ReadUInt32();
m_Header.uncompressedBlocksInfoSize = reader.ReadUInt32();
m_Header.flags = reader.ReadUInt32();
m_Header.flags = (ArchiveFlags)reader.ReadUInt32();
if (m_Header.signature != "UnityFS")
{
reader.ReadByte();
}
}
private void ReadBlocksInfoAndDirectory(EndianBinaryReader reader)
private void ReadBlocksInfoAndDirectory(EndianBinaryReader reader, int[] unityVer)
{
byte[] blocksInfoBytes;
if ((m_Header.flags & 0x80) != 0) //kArchiveBlocksInfoAtTheEnd
if (m_Header.version >= 7)
{
reader.AlignStream(16);
}
else if (unityVer[0] >= 2019 && unityVer[1] >= 4)
{
//check if we need to align the reader
//- align to 16 bytes and check if all are 0
//- if not, reset the reader to the previous position
var preAlign = reader.Position;
var alignData = reader.ReadBytes((16 - (int)(preAlign % 16)) % 16);
if (alignData.Any(x => x != 0))
{
reader.Position = preAlign;
}
}
if ((m_Header.flags & ArchiveFlags.BlocksInfoAtTheEnd) != 0)
{
var position = reader.Position;
reader.Position = reader.BaseStream.Length - m_Header.compressedBlocksInfoSize;
blocksInfoBytes = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize);
reader.Position = position;
}
else //0x40 kArchiveBlocksAndDirectoryInfoCombined
else //0x40 BlocksAndDirectoryInfoCombined
{
if (m_Header.version >= 7)
{
reader.AlignStream(16);
}
blocksInfoBytes = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize);
}
var blocksInfoCompressedStream = new MemoryStream(blocksInfoBytes);
MemoryStream blocksInfoUncompresseddStream;
switch (m_Header.flags & 0x3F) //kArchiveCompressionTypeMask
var uncompressedSize = m_Header.uncompressedBlocksInfoSize;
var compressionType = (CompressionType)(m_Header.flags & ArchiveFlags.CompressionTypeMask);
switch (compressionType)
{
default: //None
case CompressionType.None:
{
blocksInfoUncompresseddStream = blocksInfoCompressedStream;
blocksInfoUncompresseddStream = new MemoryStream(blocksInfoBytes);
break;
}
case 1: //LZMA
case CompressionType.Lzma:
{
blocksInfoUncompresseddStream = SevenZipHelper.StreamDecompress(blocksInfoCompressedStream);
blocksInfoCompressedStream.Close();
break;
}
case 2: //LZ4
case 3: //LZ4HC
{
var uncompressedBytes = new byte[m_Header.uncompressedBlocksInfoSize];
using (var decoder = new Lz4DecoderStream(blocksInfoCompressedStream))
blocksInfoUncompresseddStream = new MemoryStream((int)(uncompressedSize));
using (var blocksInfoCompressedStream = new MemoryStream(blocksInfoBytes))
{
decoder.Read(uncompressedBytes, 0, uncompressedBytes.Length);
SevenZipHelper.StreamDecompress(blocksInfoCompressedStream, blocksInfoUncompresseddStream, m_Header.compressedBlocksInfoSize, m_Header.uncompressedBlocksInfoSize);
}
blocksInfoUncompresseddStream.Position = 0;
break;
}
case CompressionType.Lz4:
case CompressionType.Lz4HC:
{
var uncompressedBytes = new byte[uncompressedSize];
var numWrite = LZ4Codec.Decode(blocksInfoBytes, uncompressedBytes);
if (numWrite != uncompressedSize)
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
}
blocksInfoUncompresseddStream = new MemoryStream(uncompressedBytes);
break;
}
default:
throw new IOException($"Unsupported compression type {compressionType}");
}
using (var blocksInfoReader = new EndianBinaryReader(blocksInfoUncompresseddStream))
{
@@ -253,7 +336,7 @@ namespace AssetStudio
{
uncompressedSize = blocksInfoReader.ReadUInt32(),
compressedSize = blocksInfoReader.ReadUInt32(),
flags = blocksInfoReader.ReadUInt16()
flags = (StorageBlockFlags)blocksInfoReader.ReadUInt16()
};
}
@@ -270,34 +353,60 @@ namespace AssetStudio
};
}
}
if ((m_Header.flags & ArchiveFlags.BlockInfoNeedPaddingAtStart) != 0)
{
reader.AlignStream(16);
}
}
private void ReadBlocks(EndianBinaryReader reader, Stream blocksStream)
{
foreach (var blockInfo in m_BlocksInfo)
{
switch (blockInfo.flags & 0x3F) //kStorageBlockCompressionTypeMask
var compressionType = (CompressionType)(blockInfo.flags & StorageBlockFlags.CompressionTypeMask);
switch (compressionType)
{
default: //None
case CompressionType.None:
{
reader.BaseStream.CopyTo(blocksStream, blockInfo.compressedSize);
break;
}
case 1: //LZMA
case CompressionType.Lzma:
{
SevenZipHelper.StreamDecompress(reader.BaseStream, blocksStream, blockInfo.compressedSize, blockInfo.uncompressedSize);
break;
}
case 2: //LZ4
case 3: //LZ4HC
case CompressionType.Lz4:
case CompressionType.Lz4HC:
case CompressionType.Lz4Inv:
{
var compressedStream = new MemoryStream(reader.ReadBytes((int)blockInfo.compressedSize));
using (var lz4Stream = new Lz4DecoderStream(compressedStream))
var compressedSize = (int)blockInfo.compressedSize;
var compressedBytes = BigArrayPool<byte>.Shared.Rent(compressedSize);
_ = reader.Read(compressedBytes, 0, compressedSize);
var uncompressedSize = (int)blockInfo.uncompressedSize;
var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize);
try
{
lz4Stream.CopyTo(blocksStream, blockInfo.uncompressedSize);
var compressedSpan = compressedBytes.AsSpan(0, compressedSize);
var uncompressedSpan = uncompressedBytes.AsSpan(0, uncompressedSize);
var numWrite = compressionType == CompressionType.Lz4Inv
? LZ4Inv.Instance.Decompress(compressedSpan, uncompressedSpan)
: LZ4Codec.Decode(compressedSpan, uncompressedSpan);
if (numWrite != uncompressedSize)
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
}
blocksStream.Write(uncompressedBytes, 0, uncompressedSize);
}
finally
{
BigArrayPool<byte>.Shared.Return(compressedBytes);
BigArrayPool<byte>.Shared.Return(uncompressedBytes);
}
break;
}
default:
throw new IOException($"Unsupported compression type {compressionType}");
}
}
blocksStream.Position = 0;

View File

@@ -1,7 +1,9 @@
namespace AssetStudio
// official Class ID Reference: https://docs.unity3d.com/Manual/ClassIDReference.html
namespace AssetStudio
{
public enum ClassIDType
{
AkPortraitSprite = -2,
UnknownType = -1,
Object = 0,
GameObject = 1,
@@ -26,7 +28,7 @@
ParticleRenderer = 26,
Texture = 27,
Texture2D = 28,
SceneSettings = 29,
OcclusionCullingSettings = 29,
GraphicsSettings = 30,
MeshFilter = 33,
OcclusionPortal = 41,
@@ -49,7 +51,7 @@
PhysicsMaterial2D = 62,
MeshCollider = 64,
BoxCollider = 65,
SpriteCollider2D = 66,
CompositeCollider2D = 66,
EdgeCollider2D = 68,
CapsuleCollider2D = 70,
ComputeShader = 72,
@@ -92,6 +94,7 @@
FlareLayer = 124,
HaloLayer = 125,
NavMeshAreas = 126,
NavMeshProjectSettings = 126,
HaloManager = 127,
Font = 128,
PlayerSettings = 129,
@@ -158,7 +161,7 @@
BlendTree = 206,
Motion = 207,
NavMeshObstacle = 208,
TerrainInstance = 210,
SortingGroup = 210,
SpriteRenderer = 212,
Sprite = 213,
CachedSpriteAtlas = 214,
@@ -216,15 +219,19 @@
PerformanceReportingManager = 305,
UnityConnectSettings = 310,
AvatarMask = 319,
PlayableDirector = 320,
VideoPlayer = 328,
VideoClip = 329,
ParticleSystemForceField = 330,
SpriteMask = 331,
WorldAnchor = 362,
OcclusionCullingData = 363,
//kLargestRuntimeClassID = 364
SmallestEditorClassID = 1000,
Prefab = 1001,
PrefabInstance = 1001,
EditorExtensionImpl = 1002,
AssetImporter = 1003,
AssetDatabase = 1004,
AssetDatabaseV1 = 1004,
Mesh3DSImporter = 1005,
TextureImporter = 1006,
ShaderImporter = 1007,
@@ -259,13 +266,13 @@
AnimatorState = 1102,
HumanTemplate = 1105,
AnimatorStateMachine = 1107,
PreviewAssetType = 1108,
PreviewAnimationClip = 1108,
AnimatorTransition = 1109,
SpeedTreeImporter = 1110,
AnimatorTransitionBase = 1111,
SubstanceImporter = 1112,
LightmapParameters = 1113,
LightmapSnapshot = 1120,
LightingDataAsset = 1120,
GISRaster = 1121,
GISRasterImporter = 1122,
CadImporter = 1123,
@@ -276,11 +283,98 @@
ActivationLogComponent = 2000,
//kLargestEditorClassID = 2001
//kClassIdOutOfHierarchy = 100000
SubDerived = 367388927,
//int = 100000,
//bool = 100001,
//float = 100002,
MonoObject = 100003,
Collision = 100004,
Vector3f = 100005,
RootMotionData = 100006,
Collision2D = 100007,
AudioMixerLiveUpdateFloat = 100008,
AudioMixerLiveUpdateBool = 100009,
Polygon2D = 100010,
//void = 100011,
TilemapCollider2D = 19719996,
AssetImporterLog = 41386430,
VFXRenderer = 73398921,
SerializableManagedRefTestClass = 76251197,
Grid = 156049354,
ScenesUsingAssets = 156483287,
ArticulationBody = 171741748,
Preset = 181963792,
EmptyObject = 277625683,
IConstraint = 285090594,
TestObjectWithSpecialLayoutOne = 293259124,
AssemblyDefinitionReferenceImporter = 294290339,
SiblingDerived = 334799969,
TestObjectWithSerializedMapStringNonAlignedStruct = 342846651,
SubDerived = 367388927,
AssetImportInProgressProxy = 369655926,
PluginBuildInfo = 382020655,
EditorProjectAccess = 426301858,
PrefabImporter = 468431735,
TestObjectWithSerializedArray = 478637458,
TestObjectWithSerializedAnimationCurve = 478637459,
TilemapRenderer = 483693784,
ScriptableCamera = 488575907,
SpriteAtlasAsset = 612988286,
SpriteAtlasDatabase = 638013454,
AudioBuildInfo = 641289076,
CachedSpriteAtlasRuntimeData = 644342135,
RendererFake = 646504946,
AssemblyDefinitionReferenceAsset = 662584278,
BuiltAssetBundleInfoSet = 668709126,
SpriteAtlas = 687078895,
RayTracingShaderImporter = 747330370,
RayTracingShader = 825902497,
LightingSettings = 850595691,
PlatformModuleSetup = 877146078,
VersionControlSettings = 890905787,
AimConstraint = 895512359,
VFXManager = 937362698,
VisualEffectSubgraph = 994735392,
VisualEffectSubgraphOperator = 994735403,
VisualEffectSubgraphBlock = 994735404,
LocalizationImporter = 1027052791,
Derived = 1091556383,
PropertyModificationsTargetTestObject = 1111377672,
ReferencesArtifactGenerator = 1114811875,
AssemblyDefinitionAsset = 1152215463,
SceneVisibilityState = 1154873562,
LookAtConstraint = 1183024399,
SpriteAtlasImporter = 1210832254,
MultiArtifactTestImporter = 1223240404,
GameObjectRecorder = 1268269756,
LightingDataAssetParent = 1325145578,
PresetManager = 1386491679,
TestObjectWithSpecialLayoutTwo = 1392443030,
StreamingManager = 1403656975,
LowerResBlitTexture = 1480428607,
RenderPassAttachment = 1571458007
StreamingController = 1542919678,
RenderPassAttachment = 1571458007,
TestObjectVectorPairStringBool = 1628831178,
GridLayout = 1742807556,
AssemblyDefinitionImporter = 1766753193,
ParentConstraint = 1773428102,
FakeComponent = 1803986026,
PositionConstraint = 1818360608,
RotationConstraint = 1818360609,
ScaleConstraint = 1818360610,
Tilemap = 1839735485,
PackageManifest = 1896753125,
PackageManifestImporter = 1896753126,
TerrainLayer = 1953259897,
SpriteShapeRenderer = 1971053207,
NativeObjectType = 1977754360,
TestObjectWithSerializedMapStringBool = 1981279845,
SerializableManagedHost = 1995898324,
VisualEffectAsset = 2058629509,
VisualEffectImporter = 2058629510,
VisualEffectResource = 2058629511,
VisualEffectObject = 2059678085,
VisualEffect = 2083052967,
LocalizationAsset = 2083778819,
ScriptedImporter = 2089858483
}
}

View File

@@ -15,7 +15,6 @@ namespace AssetStudio
public T inWeight;
public T outWeight;
public Keyframe(ObjectReader reader, Func<T> readerFunc)
{
time = reader.ReadSingle();
@@ -294,15 +293,20 @@ namespace AssetStudio
public string path;
public ClassIDType classID;
public PPtr<MonoScript> script;
public int flags;
public FloatCurve(ObjectReader reader)
{
var version = reader.version;
curve = new AnimationCurve<float>(reader, reader.ReadSingle);
attribute = reader.ReadAlignedString();
path = reader.ReadAlignedString();
classID = (ClassIDType)reader.ReadInt32();
script = new PPtr<MonoScript>(reader);
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 2)) //2022.2 and up
{
flags = reader.ReadInt32();
}
}
}
@@ -311,7 +315,6 @@ namespace AssetStudio
public float time;
public PPtr<Object> value;
public PPtrKeyframe(ObjectReader reader)
{
time = reader.ReadSingle();
@@ -326,10 +329,11 @@ namespace AssetStudio
public string path;
public int classID;
public PPtr<MonoScript> script;
public int flags;
public PPtrCurve(ObjectReader reader)
{
var version = reader.version;
int numCurves = reader.ReadInt32();
curve = new PPtrKeyframe[numCurves];
for (int i = 0; i < numCurves; i++)
@@ -341,6 +345,10 @@ namespace AssetStudio
path = reader.ReadAlignedString();
classID = reader.ReadInt32();
script = new PPtr<MonoScript>(reader);
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 2)) //2022.2 and up
{
flags = reader.ReadInt32();
}
}
}
@@ -637,6 +645,50 @@ namespace AssetStudio
m_Binding = new ValueArrayConstant(reader);
}
}
public AnimationClipBindingConstant ConvertValueArrayToGenericBinding()
{
var bindings = new AnimationClipBindingConstant();
var genericBindings = new List<GenericBinding>();
var values = m_Binding;
for (int i = 0; i < values.m_ValueArray.Length;)
{
var curveID = values.m_ValueArray[i].m_ID;
var curveTypeID = values.m_ValueArray[i].m_TypeID;
var binding = new GenericBinding();
genericBindings.Add(binding);
if (curveTypeID == 4174552735) //CRC(PositionX))
{
binding.path = curveID;
binding.attribute = 1; //kBindTransformPosition
binding.typeID = ClassIDType.Transform;
i += 3;
}
else if (curveTypeID == 2211994246) //CRC(QuaternionX))
{
binding.path = curveID;
binding.attribute = 2; //kBindTransformRotation
binding.typeID = ClassIDType.Transform;
i += 4;
}
else if (curveTypeID == 1512518241) //CRC(ScaleX))
{
binding.path = curveID;
binding.attribute = 3; //kBindTransformScale
binding.typeID = ClassIDType.Transform;
i += 3;
}
else
{
binding.typeID = ClassIDType.Animator;
binding.path = 0;
binding.attribute = curveID;
i++;
}
}
bindings.genericBindings = genericBindings.ToArray();
return bindings;
}
}
public class ValueDelta
@@ -753,6 +805,9 @@ namespace AssetStudio
public ClassIDType typeID;
public byte customType;
public byte isPPtrCurve;
public byte isIntCurve;
public GenericBinding() { }
public GenericBinding(ObjectReader reader)
{
@@ -770,6 +825,10 @@ namespace AssetStudio
}
customType = reader.ReadByte();
isPPtrCurve = reader.ReadByte();
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 1)) //2022.1 and up
{
isIntCurve = reader.ReadByte();
}
reader.AlignStream();
}
}
@@ -779,6 +838,8 @@ namespace AssetStudio
public GenericBinding[] genericBindings;
public PPtr<Object>[] pptrCurveMapping;
public AnimationClipBindingConstant() { }
public AnimationClipBindingConstant(ObjectReader reader)
{
int numBindings = reader.ReadInt32();
@@ -861,9 +922,9 @@ namespace AssetStudio
public enum AnimationType
{
kLegacy = 1,
kGeneric = 2,
kHumanoid = 3
Legacy = 1,
Generic = 2,
Humanoid = 3
};
public sealed class AnimationClip : NamedObject
@@ -887,7 +948,6 @@ namespace AssetStudio
public AnimationClipBindingConstant m_ClipBindingConstant;
public AnimationEvent[] m_Events;
public AnimationClip(ObjectReader reader) : base(reader)
{
if (version[0] >= 5)//5.0 and up
@@ -897,7 +957,7 @@ namespace AssetStudio
else if (version[0] >= 4)//4.0 and up
{
m_AnimationType = (AnimationType)reader.ReadInt32();
if (m_AnimationType == AnimationType.kLegacy)
if (m_AnimationType == AnimationType.Legacy)
m_Legacy = true;
}
else

View File

@@ -31,6 +31,10 @@ namespace AssetStudio
if (version[0] >= 5) //5.0 and up
{
var m_LinearVelocityBlending = reader.ReadBoolean();
if (version[0] > 2021 || (version[0] == 2021 && version[1] >= 2)) //2021.2 and up
{
var m_StabilizeFeet = reader.ReadBoolean();
}
reader.AlignStream();
}

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Generic;
namespace AssetStudio
{
@@ -23,22 +20,47 @@ namespace AssetStudio
{
public PPtr<Object>[] m_PreloadTable;
public KeyValuePair<string, AssetInfo>[] m_Container;
public string m_AssetBundleName;
public string[] m_Dependencies;
public bool m_IsStreamedSceneAssetBundle;
public AssetBundle(ObjectReader reader) : base(reader)
{
var m_PreloadTableSize = reader.ReadInt32();
m_PreloadTable = new PPtr<Object>[m_PreloadTableSize];
for (int i = 0; i < m_PreloadTableSize; i++)
for (var i = 0; i < m_PreloadTableSize; i++)
{
m_PreloadTable[i] = new PPtr<Object>(reader);
}
var m_ContainerSize = reader.ReadInt32();
m_Container = new KeyValuePair<string, AssetInfo>[m_ContainerSize];
for (int i = 0; i < m_ContainerSize; i++)
for (var i = 0; i < m_ContainerSize; i++)
{
m_Container[i] = new KeyValuePair<string, AssetInfo>(reader.ReadAlignedString(), new AssetInfo(reader));
}
var m_MainAsset = new AssetInfo(reader);
if (version[0] > 4 || (version[0] == 4 && version[1] >= 2)) //4.2 and up
{
var m_RuntimeCompatibility = reader.ReadUInt32();
}
if (version[0] >= 5) //5.0 and up
{
m_AssetBundleName = reader.ReadAlignedString();
var m_DependenciesSize = reader.ReadInt32();
m_Dependencies = new string[m_DependenciesSize];
for (var i = 0; i < m_DependenciesSize; i++)
{
m_Dependencies[i] = reader.ReadAlignedString();
}
m_IsStreamedSceneAssetBundle = reader.ReadBoolean();
}
}
}
}

View File

@@ -9,7 +9,7 @@ namespace AssetStudio
public sealed class AudioClip : NamedObject
{
public int m_Format;
public AudioType m_Type;
public FMODSoundType m_Type;
public bool m_3D;
public bool m_UseHardware;
@@ -27,8 +27,8 @@ namespace AssetStudio
public AudioCompressionFormat m_CompressionFormat;
public string m_Source;
public long m_Offset;
public long m_Size;
public long m_Offset; //ulong
public long m_Size; //ulong
public ResourceReader m_AudioData;
public AudioClip(ObjectReader reader) : base(reader)
@@ -36,7 +36,7 @@ namespace AssetStudio
if (version[0] < 5)
{
m_Format = reader.ReadInt32();
m_Type = (AudioType)reader.ReadInt32();
m_Type = (FMODSoundType)reader.ReadInt32();
m_3D = reader.ReadBoolean();
m_UseHardware = reader.ReadBoolean();
reader.AlignStream();
@@ -48,7 +48,7 @@ namespace AssetStudio
var tsize = m_Size % 4 != 0 ? m_Size + 4 - m_Size % 4 : m_Size;
if (reader.byteSize + reader.byteStart - reader.Position != tsize)
{
m_Offset = reader.ReadInt32();
m_Offset = reader.ReadUInt32();
m_Source = assetsFile.fullName + ".resS";
}
}
@@ -72,6 +72,7 @@ namespace AssetStudio
m_Legacy3D = reader.ReadBoolean();
reader.AlignStream();
//StreamedResource m_Resource
m_Source = reader.ReadAlignedString();
m_Offset = reader.ReadInt64();
m_Size = reader.ReadInt64();
@@ -81,44 +82,61 @@ namespace AssetStudio
ResourceReader resourceReader;
if (!string.IsNullOrEmpty(m_Source))
{
resourceReader = new ResourceReader(m_Source, assetsFile, m_Offset, (int)m_Size);
resourceReader = new ResourceReader(m_Source, assetsFile, m_Offset, m_Size);
}
else
{
resourceReader = new ResourceReader(reader, reader.BaseStream.Position, (int)m_Size);
resourceReader = new ResourceReader(reader, reader.BaseStream.Position, m_Size);
}
m_AudioData = resourceReader;
}
}
public enum AudioType
public enum FMODSoundType
{
UNKNOWN,
ACC,
AIFF,
UNKNOWN = 0,
ACC = 1,
AIFF = 2,
ASF = 3,
AT3 = 4,
CDDA = 5,
DLS = 6,
FLAC = 7,
FSB = 8,
GCADPCM = 9,
IT = 10,
MIDI = 11,
MOD = 12,
MPEG,
OGGVORBIS,
MPEG = 13,
OGGVORBIS = 14,
PLAYLIST = 15,
RAW = 16,
S3M = 17,
SF2 = 18,
USER = 19,
WAV = 20,
XM,
XMA,
VAG,
AUDIOQUEUE
XM = 21,
XMA = 22,
VAG = 23,
AUDIOQUEUE = 24,
XWMA = 25,
BCWAV = 26,
AT9 = 27,
VORBIS = 28,
MEDIA_FOUNDATION = 29
}
public enum AudioCompressionFormat
{
PCM,
Vorbis,
ADPCM,
MP3,
VAG,
HEVAG,
XMA,
AAC,
GCADPCM,
ATRAC9
PCM = 0,
Vorbis = 1,
ADPCM = 2,
MP3 = 3,
PSMVAG = 4,
HEVAG = 5,
XMA = 6,
AAC = 7,
GCADPCM = 8,
ATRAC9 = 9
}
}

View File

@@ -19,11 +19,14 @@ namespace AssetStudio
public class UnityPropertySheet
{
public KeyValuePair<string, UnityTexEnv>[] m_TexEnvs;
public KeyValuePair<string, int>[] m_Ints;
public KeyValuePair<string, float>[] m_Floats;
public KeyValuePair<string, Color>[] m_Colors;
public UnityPropertySheet(ObjectReader reader)
{
var version = reader.version;
int m_TexEnvsSize = reader.ReadInt32();
m_TexEnvs = new KeyValuePair<string, UnityTexEnv>[m_TexEnvsSize];
for (int i = 0; i < m_TexEnvsSize; i++)
@@ -31,6 +34,16 @@ namespace AssetStudio
m_TexEnvs[i] = new KeyValuePair<string, UnityTexEnv>(reader.ReadAlignedString(), new UnityTexEnv(reader));
}
if (version[0] >= 2021) //2021.1 and up
{
int m_IntsSize = reader.ReadInt32();
m_Ints = new KeyValuePair<string, int>[m_IntsSize];
for (int i = 0; i < m_IntsSize; i++)
{
m_Ints[i] = new KeyValuePair<string, int>(reader.ReadAlignedString(), reader.ReadInt32());
}
}
int m_FloatsSize = reader.ReadInt32();
m_Floats = new KeyValuePair<string, float>[m_FloatsSize];
for (int i = 0; i < m_FloatsSize; i++)
@@ -61,9 +74,18 @@ namespace AssetStudio
var m_ShaderKeywords = reader.ReadStringArray();
}
if (version[0] >= 5) //5.0 and up
if (version[0] > 2021 || (version[0] == 2021 && version[1] >= 3)) //2021.3 and up
{
var m_ValidKeywords = reader.ReadStringArray();
var m_InvalidKeywords = reader.ReadStringArray();
}
else if (version[0] >= 5) //5.0 ~ 2021.2
{
var m_ShaderKeywords = reader.ReadAlignedString();
}
if (version[0] >= 5) //5.0 and up
{
var m_LightmapFlags = reader.ReadUInt32();
}
@@ -95,6 +117,8 @@ namespace AssetStudio
}
m_SavedProperties = new UnityPropertySheet(reader);
//vector m_BuildTextureStacks 2020 and up
}
}
}

View File

@@ -174,7 +174,7 @@ namespace AssetStudio
GetStreams(version);
}
m_DataSize = reader.ReadBytes(reader.ReadInt32());
m_DataSize = reader.ReadUInt8Array();
reader.AlignStream();
}
@@ -195,7 +195,7 @@ namespace AssetStudio
if (m_Channel.dimension > 0)
{
chnMask |= 1u << chn;
stride += m_Channel.dimension * MeshHelper.GetFormatSize(version, m_Channel.format);
stride += m_Channel.dimension * MeshHelper.GetFormatSize(MeshHelper.ToVertexFormat(m_Channel.format, version));
}
}
}
@@ -253,7 +253,7 @@ namespace AssetStudio
m_Channel.dimension = 4;
break;
}
offset += (byte)(m_Channel.dimension * MeshHelper.GetFormatSize(version, m_Channel.format));
offset += (byte)(m_Channel.dimension * MeshHelper.GetFormatSize(MeshHelper.ToVertexFormat(m_Channel.format, version)));
}
}
}
@@ -396,14 +396,14 @@ namespace AssetStudio
}
}
public enum GfxPrimitiveType : int
public enum GfxPrimitiveType
{
kPrimitiveTriangles = 0,
kPrimitiveTriangleStrip = 1,
kPrimitiveQuads = 2,
kPrimitiveLines = 3,
kPrimitiveLineStrip = 4,
kPrimitivePoints = 5,
Triangles = 0,
TriangleStrip = 1,
Quads = 2,
Lines = 3,
LineStrip = 4,
Points = 5
};
public class SubMesh
@@ -446,7 +446,7 @@ namespace AssetStudio
public sealed class Mesh : NamedObject
{
private bool m_Use16BitIndices = true; //3.5.0 and newer always uses 16bit indices;
private bool m_Use16BitIndices = true;
public SubMesh[] m_SubMeshes;
private uint[] m_IndexBuffer;
public BlendShapeData m_Shapes;
@@ -550,6 +550,7 @@ namespace AssetStudio
((version[0] == 2017 && version[1] == 3) && m_MeshCompression == 0))//2017.3.xfx with no compression
{
var m_IndexFormat = reader.ReadInt32();
m_Use16BitIndices = m_IndexFormat == 0;
}
int m_IndexBuffer_size = reader.ReadInt32();
@@ -650,11 +651,16 @@ namespace AssetStudio
int m_MeshUsageFlags = reader.ReadInt32();
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 1)) //2022.1 and up
{
int m_CookingOptions = reader.ReadInt32();
}
if (version[0] >= 5) //5.0 and up
{
var m_BakedConvexCollisionMesh = reader.ReadBytes(reader.ReadInt32());
var m_BakedConvexCollisionMesh = reader.ReadUInt8Array();
reader.AlignStream();
var m_BakedTriangleCollisionMesh = reader.ReadBytes(reader.ReadInt32());
var m_BakedTriangleCollisionMesh = reader.ReadUInt8Array();
reader.AlignStream();
}
@@ -680,7 +686,7 @@ namespace AssetStudio
{
if (m_VertexData.m_VertexCount > 0)
{
var resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, (int)m_StreamData.size);
var resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size);
m_VertexData.m_DataSize = resourceReader.GetData();
}
}
@@ -710,12 +716,13 @@ namespace AssetStudio
var channelMask = new BitArray(new[] { (int)m_Stream.channelMask });
if (channelMask.Get(chn))
{
if (version[0] < 2018 && chn == 2 && m_Channel.format == 2)
if (version[0] < 2018 && chn == 2 && m_Channel.format == 2) //kShaderChannelColor && kChannelFormatColor
{
m_Channel.dimension = 4;
}
var componentByteSize = (int)MeshHelper.GetFormatSize(version, m_Channel.format);
var vertexFormat = MeshHelper.ToVertexFormat(m_Channel.format, version);
var componentByteSize = (int)MeshHelper.GetFormatSize(vertexFormat);
var componentBytes = new byte[m_VertexCount * m_Channel.dimension * componentByteSize];
for (int v = 0; v < m_VertexCount; v++)
{
@@ -727,7 +734,7 @@ namespace AssetStudio
}
}
if (reader.endian == EndianType.BigEndian && componentByteSize > 1) //swap bytes
if (reader.Endian == EndianType.BigEndian && componentByteSize > 1) //swap bytes
{
for (var i = 0; i < componentBytes.Length / componentByteSize; i++)
{
@@ -740,10 +747,10 @@ namespace AssetStudio
int[] componentsIntArray = null;
float[] componentsFloatArray = null;
if (MeshHelper.IsIntFormat(version, m_Channel.format))
componentsIntArray = MeshHelper.BytesToIntArray(componentBytes, componentByteSize);
if (MeshHelper.IsIntFormat(vertexFormat))
componentsIntArray = MeshHelper.BytesToIntArray(componentBytes, vertexFormat);
else
componentsFloatArray = MeshHelper.BytesToFloatArray(componentBytes, componentByteSize);
componentsFloatArray = MeshHelper.BytesToFloatArray(componentBytes, vertexFormat);
if (version[0] >= 2018)
{
@@ -1058,7 +1065,7 @@ namespace AssetStudio
}
var indexCount = m_SubMesh.indexCount;
var topology = m_SubMesh.topology;
if (topology == GfxPrimitiveType.kPrimitiveTriangles)
if (topology == GfxPrimitiveType.Triangles)
{
for (int i = 0; i < indexCount; i += 3)
{
@@ -1067,7 +1074,7 @@ namespace AssetStudio
m_Indices.Add(m_IndexBuffer[firstIndex + i + 2]);
}
}
else if (version[0] < 4 || topology == GfxPrimitiveType.kPrimitiveTriangleStrip)
else if (version[0] < 4 || topology == GfxPrimitiveType.TriangleStrip)
{
// de-stripify :
uint triIndex = 0;
@@ -1098,7 +1105,7 @@ namespace AssetStudio
//fix indexCount
m_SubMesh.indexCount = triIndex;
}
else if (topology == GfxPrimitiveType.kPrimitiveQuads)
else if (topology == GfxPrimitiveType.Quads)
{
for (int q = 0; q < indexCount; q += 4)
{
@@ -1189,189 +1196,188 @@ namespace AssetStudio
public static class MeshHelper
{
private enum VertexChannelFormat
public enum VertexChannelFormat
{
kChannelFormatFloat,
kChannelFormatFloat16,
kChannelFormatColor,
kChannelFormatByte,
kChannelFormatUInt32
Float,
Float16,
Color,
Byte,
UInt32
}
private enum VertexFormat
public enum VertexFormat2017
{
kVertexFormatFloat,
kVertexFormatFloat16,
kVertexFormatColor,
kVertexFormatUNorm8,
kVertexFormatSNorm8,
kVertexFormatUNorm16,
kVertexFormatSNorm16,
kVertexFormatUInt8,
kVertexFormatSInt8,
kVertexFormatUInt16,
kVertexFormatSInt16,
kVertexFormatUInt32,
kVertexFormatSInt32
Float,
Float16,
Color,
UNorm8,
SNorm8,
UNorm16,
SNorm16,
UInt8,
SInt8,
UInt16,
SInt16,
UInt32,
SInt32
}
private enum VertexFormatV2019
public enum VertexFormat
{
kVertexFormatFloat,
kVertexFormatFloat16,
kVertexFormatUNorm8,
kVertexFormatSNorm8,
kVertexFormatUNorm16,
kVertexFormatSNorm16,
kVertexFormatUInt8,
kVertexFormatSInt8,
kVertexFormatUInt16,
kVertexFormatSInt16,
kVertexFormatUInt32,
kVertexFormatSInt32
Float,
Float16,
UNorm8,
SNorm8,
UNorm16,
SNorm16,
UInt8,
SInt8,
UInt16,
SInt16,
UInt32,
SInt32
}
public static uint GetFormatSize(int[] version, int format)
public static VertexFormat ToVertexFormat(int format, int[] version)
{
if (version[0] < 2017)
{
switch ((VertexChannelFormat)format)
{
case VertexChannelFormat.kChannelFormatFloat:
return 4u;
case VertexChannelFormat.kChannelFormatFloat16:
return 2u;
case VertexChannelFormat.kChannelFormatColor: //in 4.x is size 4
return 1u;
case VertexChannelFormat.kChannelFormatByte:
return 1u;
case VertexChannelFormat.kChannelFormatUInt32: //in 5.x
return 4u;
case VertexChannelFormat.Float:
return VertexFormat.Float;
case VertexChannelFormat.Float16:
return VertexFormat.Float16;
case VertexChannelFormat.Color: //in 4.x is size 4
return VertexFormat.UNorm8;
case VertexChannelFormat.Byte:
return VertexFormat.UInt8;
case VertexChannelFormat.UInt32: //in 5.x
return VertexFormat.UInt32;
default:
throw new ArgumentOutOfRangeException(nameof(format), format, null);
}
}
else if (version[0] < 2019)
{
switch ((VertexFormat)format)
switch ((VertexFormat2017)format)
{
case VertexFormat.kVertexFormatFloat:
return 4u;
case VertexFormat.kVertexFormatFloat16:
return 2u;
case VertexFormat.kVertexFormatColor:
return 1u;
case VertexFormat.kVertexFormatUNorm8:
return 1u;
case VertexFormat.kVertexFormatSNorm8:
return 1u;
case VertexFormat.kVertexFormatUNorm16:
return 2u;
case VertexFormat.kVertexFormatSNorm16:
return 2u;
case VertexFormat.kVertexFormatUInt8:
return 1u;
case VertexFormat.kVertexFormatSInt8:
return 1u;
case VertexFormat.kVertexFormatUInt16:
return 2u;
case VertexFormat.kVertexFormatSInt16:
return 2u;
case VertexFormat.kVertexFormatUInt32:
return 4u;
case VertexFormat.kVertexFormatSInt32:
return 4u;
case VertexFormat2017.Float:
return VertexFormat.Float;
case VertexFormat2017.Float16:
return VertexFormat.Float16;
case VertexFormat2017.Color:
case VertexFormat2017.UNorm8:
return VertexFormat.UNorm8;
case VertexFormat2017.SNorm8:
return VertexFormat.SNorm8;
case VertexFormat2017.UNorm16:
return VertexFormat.UNorm16;
case VertexFormat2017.SNorm16:
return VertexFormat.SNorm16;
case VertexFormat2017.UInt8:
return VertexFormat.UInt8;
case VertexFormat2017.SInt8:
return VertexFormat.SInt8;
case VertexFormat2017.UInt16:
return VertexFormat.UInt16;
case VertexFormat2017.SInt16:
return VertexFormat.SInt16;
case VertexFormat2017.UInt32:
return VertexFormat.UInt32;
case VertexFormat2017.SInt32:
return VertexFormat.SInt32;
default:
throw new ArgumentOutOfRangeException(nameof(format), format, null);
}
}
else
{
switch ((VertexFormatV2019)format)
{
case VertexFormatV2019.kVertexFormatFloat:
return 4u;
case VertexFormatV2019.kVertexFormatFloat16:
return 2u;
case VertexFormatV2019.kVertexFormatUNorm8:
return 1u;
case VertexFormatV2019.kVertexFormatSNorm8:
return 1u;
case VertexFormatV2019.kVertexFormatUNorm16:
return 2u;
case VertexFormatV2019.kVertexFormatSNorm16:
return 2u;
case VertexFormatV2019.kVertexFormatUInt8:
return 1u;
case VertexFormatV2019.kVertexFormatSInt8:
return 1u;
case VertexFormatV2019.kVertexFormatUInt16:
return 2u;
case VertexFormatV2019.kVertexFormatSInt16:
return 2u;
case VertexFormatV2019.kVertexFormatUInt32:
return 4u;
case VertexFormatV2019.kVertexFormatSInt32:
return 4u;
default:
throw new ArgumentOutOfRangeException(nameof(format), format, null);
}
return (VertexFormat)format;
}
}
public static bool IsIntFormat(int[] version, int format)
public static uint GetFormatSize(VertexFormat format)
{
if (version[0] < 2017)
switch (format)
{
return format == 4;
}
else if (version[0] < 2019)
{
return format >= 7;
}
else
{
return format >= 6;
case VertexFormat.Float:
case VertexFormat.UInt32:
case VertexFormat.SInt32:
return 4u;
case VertexFormat.Float16:
case VertexFormat.UNorm16:
case VertexFormat.SNorm16:
case VertexFormat.UInt16:
case VertexFormat.SInt16:
return 2u;
case VertexFormat.UNorm8:
case VertexFormat.SNorm8:
case VertexFormat.UInt8:
case VertexFormat.SInt8:
return 1u;
default:
throw new ArgumentOutOfRangeException(nameof(format), format, null);
}
}
public static float[] BytesToFloatArray(byte[] inputBytes, int size)
public static bool IsIntFormat(VertexFormat format)
{
var result = new float[inputBytes.Length / size];
for (int i = 0; i < inputBytes.Length / size; i++)
return format >= VertexFormat.UInt8;
}
public static float[] BytesToFloatArray(byte[] inputBytes, VertexFormat format)
{
var size = GetFormatSize(format);
var len = inputBytes.Length / size;
var result = new float[len];
for (int i = 0; i < len; i++)
{
var value = 0f;
switch (size)
switch (format)
{
case 1:
value = inputBytes[i] / 255.0f;
case VertexFormat.Float:
result[i] = BitConverter.ToSingle(inputBytes, i * 4);
break;
case 2:
value = Half.ToHalf(inputBytes, i * 2);
case VertexFormat.Float16:
result[i] = Half.ToHalf(inputBytes, i * 2);
break;
case 4:
value = BitConverter.ToSingle(inputBytes, i * 4);
case VertexFormat.UNorm8:
result[i] = inputBytes[i] / 255f;
break;
case VertexFormat.SNorm8:
result[i] = Math.Max((sbyte)inputBytes[i] / 127f, -1f);
break;
case VertexFormat.UNorm16:
result[i] = BitConverter.ToUInt16(inputBytes, i * 2) / 65535f;
break;
case VertexFormat.SNorm16:
result[i] = Math.Max(BitConverter.ToInt16(inputBytes, i * 2) / 32767f, -1f);
break;
}
result[i] = value;
}
return result;
}
public static int[] BytesToIntArray(byte[] inputBytes, int size)
public static int[] BytesToIntArray(byte[] inputBytes, VertexFormat format)
{
var result = new int[inputBytes.Length / size];
for (int i = 0; i < inputBytes.Length / size; i++)
var size = GetFormatSize(format);
var len = inputBytes.Length / size;
var result = new int[len];
for (int i = 0; i < len; i++)
{
switch (size)
switch (format)
{
case 1:
case VertexFormat.UInt8:
case VertexFormat.SInt8:
result[i] = inputBytes[i];
break;
case 2:
case VertexFormat.UInt16:
case VertexFormat.SInt16:
result[i] = BitConverter.ToInt16(inputBytes, i * 2);
break;
case 4:
case VertexFormat.UInt32:
case VertexFormat.SInt32:
result[i] = BitConverter.ToInt32(inputBytes, i * 4);
break;
}

View File

@@ -8,7 +8,7 @@ namespace AssetStudio
public sealed class MonoScript : NamedObject
{
public string m_ClassName;
public string m_Namespace = string.Empty;
public string m_Namespace;
public string m_AssemblyName;
public MonoScript(ObjectReader reader) : base(reader)

View File

@@ -15,7 +15,7 @@ namespace AssetStudio
var m_Loop = reader.ReadBoolean();
reader.AlignStream();
m_AudioClip = new PPtr<AudioClip>(reader);
m_MovieData = reader.ReadBytes(reader.ReadInt32());
m_MovieData = reader.ReadUInt8Array();
}
}
}

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;
namespace AssetStudio
{
@@ -36,19 +33,38 @@ namespace AssetStudio
}
}
protected bool HasStructMember(string name)
{
return serializedType?.m_Nodes != null && serializedType.m_Nodes.Any(x => x.m_Name == name);
}
public string Dump()
{
reader.Reset();
if (serializedType?.m_Nodes != null)
if (serializedType?.m_Type != null)
{
var sb = new StringBuilder();
TypeTreeHelper.ReadTypeString(sb, serializedType.m_Nodes, reader);
return sb.ToString();
return TypeTreeHelper.ReadTypeString(serializedType.m_Type, reader);
}
return null;
}
public string Dump(TypeTree m_Type)
{
if (m_Type != null)
{
return TypeTreeHelper.ReadTypeString(m_Type, reader);
}
return null;
}
public OrderedDictionary ToType()
{
if (serializedType?.m_Type != null)
{
return TypeTreeHelper.ReadType(serializedType.m_Type, reader);
}
return null;
}
public OrderedDictionary ToType(TypeTree m_Type)
{
if (m_Type != null)
{
return TypeTreeHelper.ReadType(m_Type, reader);
}
return null;
}

View File

@@ -13,7 +13,7 @@ namespace AssetStudio
public PPtr(ObjectReader reader)
{
m_FileID = reader.ReadInt32();
m_PathID = reader.m_Version < 14 ? reader.ReadInt32() : reader.ReadInt64();
m_PathID = reader.m_Version < SerializedFileFormatVersion.Unknown_14 ? reader.ReadInt32() : reader.ReadInt64();
assetsFile = reader.assetsFile;
}

View File

@@ -0,0 +1,35 @@
namespace AssetStudio
{
public sealed class PreloadData : NamedObject
{
public PPtr<Object>[] m_Assets;
public PreloadData(ObjectReader reader) : base(reader)
{
var m_PreloadTableSize = reader.ReadInt32();
m_Assets = new PPtr<Object>[m_PreloadTableSize];
for (var i = 0; i < m_PreloadTableSize; i++)
{
m_Assets[i] = new PPtr<Object>(reader);
}
/*
if (version[0] >= 5) //5.0 and up
{
var m_DependenciesSize = reader.ReadInt32();
var m_Dependencies = new string[m_DependenciesSize];
for (var i = 0; i < m_DependenciesSize; i++)
{
m_Dependencies[i] = reader.ReadAlignedString();
}
}
if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up
{
var m_ExplicitDataLayout = reader.ReadBoolean();
}
*/
}
}
}

View File

@@ -43,6 +43,10 @@ namespace AssetStudio
{
var m_DynamicOccludee = reader.ReadByte();
}
if (version[0] >= 2021) //2021.1 and up
{
var m_StaticShadowCaster = reader.ReadByte();
}
var m_MotionVectors = reader.ReadByte();
var m_LightProbeUsage = reader.ReadByte();
var m_ReflectionProbeUsage = reader.ReadByte();
@@ -50,6 +54,10 @@ namespace AssetStudio
{
var m_RayTracingMode = reader.ReadByte();
}
if (version[0] >= 2020) //2020.1 and up
{
var m_RayTraceProcedural = reader.ReadByte();
}
reader.AlignStream();
}
else

View File

@@ -0,0 +1,19 @@
using System.Collections.Generic;
namespace AssetStudio
{
public class ResourceManager : Object
{
public KeyValuePair<string, PPtr<Object>>[] m_Container;
public ResourceManager(ObjectReader reader) : base(reader)
{
var m_ContainerSize = reader.ReadInt32();
m_Container = new KeyValuePair<string, PPtr<Object>>[m_ContainerSize];
for (int i = 0; i < m_ContainerSize; i++)
{
m_Container[i] = new KeyValuePair<string, PPtr<Object>>(reader.ReadAlignedString(), new PPtr<Object>(reader));
}
}
}
}

View File

@@ -5,6 +5,16 @@ using System.Linq;
namespace AssetStudio
{
public class Hash128
{
public byte[] bytes;
public Hash128(BinaryReader reader)
{
bytes = reader.ReadBytes(16);
}
}
public class StructParameter
{
public MatrixParameter[] m_MatrixParams;
@@ -46,15 +56,14 @@ namespace AssetStudio
}
public enum TextureDimension
{
kTexDimUnknown = -1,
kTexDimNone = 0,
kTexDimAny = 1,
kTexDim2D = 2,
kTexDim3D = 3,
kTexDimCUBE = 4,
kTexDim2DArray = 5,
kTexDimCubeArray = 6,
kTexDimForce32Bit = 2147483647
Unknown = -1,
None = 0,
Any = 1,
Tex2D = 2,
Tex3D = 3,
Cube = 4,
Tex2DArray = 5,
CubeArray = 6
};
public class SerializedTextureProperty
@@ -71,11 +80,12 @@ namespace AssetStudio
public enum SerializedPropertyType
{
kColor = 0,
kVector = 1,
kFloat = 2,
kRange = 3,
kTexture = 4
Color = 0,
Vector = 1,
Float = 2,
Range = 3,
Texture = 4,
Int = 5
};
public class SerializedProperty
@@ -185,11 +195,11 @@ namespace AssetStudio
public enum FogMode
{
kFogUnknown = -1,
kFogDisabled = 0,
kFogLinear = 1,
kFogExp = 2,
kFogExp2 = 3
Unknown = -1,
Disabled = 0,
Linear = 1,
Exp = 2,
Exp2 = 3
};
public class SerializedShaderState
@@ -201,6 +211,7 @@ namespace AssetStudio
public SerializedShaderFloatValue zTest;
public SerializedShaderFloatValue zWrite;
public SerializedShaderFloatValue culling;
public SerializedShaderFloatValue conservative;
public SerializedShaderFloatValue offsetFactor;
public SerializedShaderFloatValue offsetUnits;
public SerializedShaderFloatValue alphaToMask;
@@ -239,6 +250,10 @@ namespace AssetStudio
zTest = new SerializedShaderFloatValue(reader);
zWrite = new SerializedShaderFloatValue(reader);
culling = new SerializedShaderFloatValue(reader);
if (version[0] >= 2020) //2020.1 and up
{
conservative = new SerializedShaderFloatValue(reader);
}
offsetFactor = new SerializedShaderFloatValue(reader);
offsetUnits = new SerializedShaderFloatValue(reader);
alphaToMask = new SerializedShaderFloatValue(reader);
@@ -357,11 +372,18 @@ namespace AssetStudio
{
public int m_NameIndex;
public int m_Index;
public int m_ArraySize;
public BufferBinding(BinaryReader reader)
public BufferBinding(ObjectReader reader)
{
var version = reader.version;
m_NameIndex = reader.ReadInt32();
m_Index = reader.ReadInt32();
if (version[0] >= 2020) //2020.1 and up
{
m_ArraySize = reader.ReadInt32();
}
}
}
@@ -372,6 +394,7 @@ namespace AssetStudio
public VectorParameter[] m_VectorParams;
public StructParameter[] m_StructParams;
public int m_Size;
public bool m_IsPartialCB;
public ConstantBuffer(ObjectReader reader)
{
@@ -402,6 +425,16 @@ namespace AssetStudio
}
}
m_Size = reader.ReadInt32();
if ((version[0] == 2020 && version[1] > 3) ||
(version[0] == 2020 && version[1] == 3 && version[2] >= 2) || //2020.3.2f1 and up
(version[0] > 2021) ||
(version[0] == 2021 && version[1] > 1) ||
(version[0] == 2021 && version[1] == 1 && version[2] >= 4)) //2021.1.4f1 and up
{
m_IsPartialCB = reader.ReadBoolean();
reader.AlignStream();
}
}
}
@@ -421,42 +454,43 @@ namespace AssetStudio
public enum ShaderGpuProgramType
{
kShaderGpuProgramUnknown = 0,
kShaderGpuProgramGLLegacy = 1,
kShaderGpuProgramGLES31AEP = 2,
kShaderGpuProgramGLES31 = 3,
kShaderGpuProgramGLES3 = 4,
kShaderGpuProgramGLES = 5,
kShaderGpuProgramGLCore32 = 6,
kShaderGpuProgramGLCore41 = 7,
kShaderGpuProgramGLCore43 = 8,
kShaderGpuProgramDX9VertexSM20 = 9,
kShaderGpuProgramDX9VertexSM30 = 10,
kShaderGpuProgramDX9PixelSM20 = 11,
kShaderGpuProgramDX9PixelSM30 = 12,
kShaderGpuProgramDX10Level9Vertex = 13,
kShaderGpuProgramDX10Level9Pixel = 14,
kShaderGpuProgramDX11VertexSM40 = 15,
kShaderGpuProgramDX11VertexSM50 = 16,
kShaderGpuProgramDX11PixelSM40 = 17,
kShaderGpuProgramDX11PixelSM50 = 18,
kShaderGpuProgramDX11GeometrySM40 = 19,
kShaderGpuProgramDX11GeometrySM50 = 20,
kShaderGpuProgramDX11HullSM50 = 21,
kShaderGpuProgramDX11DomainSM50 = 22,
kShaderGpuProgramMetalVS = 23,
kShaderGpuProgramMetalFS = 24,
kShaderGpuProgramSPIRV = 25,
kShaderGpuProgramConsole = 26
Unknown = 0,
GLLegacy = 1,
GLES31AEP = 2,
GLES31 = 3,
GLES3 = 4,
GLES = 5,
GLCore32 = 6,
GLCore41 = 7,
GLCore43 = 8,
DX9VertexSM20 = 9,
DX9VertexSM30 = 10,
DX9PixelSM20 = 11,
DX9PixelSM30 = 12,
DX10Level9Vertex = 13,
DX10Level9Pixel = 14,
DX11VertexSM40 = 15,
DX11VertexSM50 = 16,
DX11PixelSM40 = 17,
DX11PixelSM50 = 18,
DX11GeometrySM40 = 19,
DX11GeometrySM50 = 20,
DX11HullSM50 = 21,
DX11DomainSM50 = 22,
MetalVS = 23,
MetalFS = 24,
SPIRV = 25,
ConsoleVS = 26,
ConsoleFS = 27,
ConsoleHS = 28,
ConsoleDS = 29,
ConsoleGS = 30,
RayTracing = 31,
PS5NGGC = 32
};
public class SerializedSubProgram
public class SerializedProgramParameters
{
public uint m_BlobIndex;
public ParserBindChannels m_Channels;
public ushort[] m_KeywordIndices;
public sbyte m_ShaderHardwareTier;
public ShaderGpuProgramType m_GpuProgramType;
public VectorParameter[] m_VectorParams;
public MatrixParameter[] m_MatrixParams;
public TextureParameter[] m_TextureParams;
@@ -466,33 +500,8 @@ namespace AssetStudio
public UAVParameter[] m_UAVParams;
public SamplerParameter[] m_Samplers;
public SerializedSubProgram(ObjectReader reader)
public SerializedProgramParameters(ObjectReader reader)
{
var version = reader.version;
m_BlobIndex = reader.ReadUInt32();
m_Channels = new ParserBindChannels(reader);
if (version[0] >= 2019) //2019 and up
{
var m_GlobalKeywordIndices = reader.ReadUInt16Array();
reader.AlignStream();
var m_LocalKeywordIndices = reader.ReadUInt16Array();
reader.AlignStream();
}
else
{
m_KeywordIndices = reader.ReadUInt16Array();
if (version[0] >= 2017) //2017 and up
{
reader.AlignStream();
}
}
m_ShaderHardwareTier = reader.ReadSByte();
m_GpuProgramType = (ShaderGpuProgramType)reader.ReadSByte();
reader.AlignStream();
int numVectorParams = reader.ReadInt32();
m_VectorParams = new VectorParameter[numVectorParams];
for (int i = 0; i < numVectorParams; i++)
@@ -542,18 +551,139 @@ namespace AssetStudio
m_UAVParams[i] = new UAVParameter(reader);
}
if (version[0] >= 2017) //2017 and up
int numSamplers = reader.ReadInt32();
m_Samplers = new SamplerParameter[numSamplers];
for (int i = 0; i < numSamplers; i++)
{
int numSamplers = reader.ReadInt32();
m_Samplers = new SamplerParameter[numSamplers];
for (int i = 0; i < numSamplers; i++)
m_Samplers[i] = new SamplerParameter(reader);
}
}
}
public class SerializedSubProgram
{
public uint m_BlobIndex;
public ParserBindChannels m_Channels;
public ushort[] m_KeywordIndices;
public sbyte m_ShaderHardwareTier;
public ShaderGpuProgramType m_GpuProgramType;
public SerializedProgramParameters m_Parameters;
public VectorParameter[] m_VectorParams;
public MatrixParameter[] m_MatrixParams;
public TextureParameter[] m_TextureParams;
public BufferBinding[] m_BufferParams;
public ConstantBuffer[] m_ConstantBuffers;
public BufferBinding[] m_ConstantBufferBindings;
public UAVParameter[] m_UAVParams;
public SamplerParameter[] m_Samplers;
public SerializedSubProgram(ObjectReader reader)
{
var version = reader.version;
m_BlobIndex = reader.ReadUInt32();
m_Channels = new ParserBindChannels(reader);
if ((version[0] >= 2019 && version[0] < 2021) || (version[0] == 2021 && version[1] < 2)) //2019 ~2021.1
{
var m_GlobalKeywordIndices = reader.ReadUInt16Array();
reader.AlignStream();
var m_LocalKeywordIndices = reader.ReadUInt16Array();
reader.AlignStream();
}
else
{
m_KeywordIndices = reader.ReadUInt16Array();
if (version[0] >= 2017) //2017 and up
{
m_Samplers[i] = new SamplerParameter(reader);
reader.AlignStream();
}
}
m_ShaderHardwareTier = reader.ReadSByte();
m_GpuProgramType = (ShaderGpuProgramType)reader.ReadSByte();
reader.AlignStream();
if ((version[0] == 2020 && version[1] > 3) ||
(version[0] == 2020 && version[1] == 3 && version[2] >= 2) || //2020.3.2f1 and up
(version[0] > 2021) ||
(version[0] == 2021 && version[1] > 1) ||
(version[0] == 2021 && version[1] == 1 && version[2] >= 1)) //2021.1.1f1 and up
{
m_Parameters = new SerializedProgramParameters(reader);
}
else
{
int numVectorParams = reader.ReadInt32();
m_VectorParams = new VectorParameter[numVectorParams];
for (int i = 0; i < numVectorParams; i++)
{
m_VectorParams[i] = new VectorParameter(reader);
}
int numMatrixParams = reader.ReadInt32();
m_MatrixParams = new MatrixParameter[numMatrixParams];
for (int i = 0; i < numMatrixParams; i++)
{
m_MatrixParams[i] = new MatrixParameter(reader);
}
int numTextureParams = reader.ReadInt32();
m_TextureParams = new TextureParameter[numTextureParams];
for (int i = 0; i < numTextureParams; i++)
{
m_TextureParams[i] = new TextureParameter(reader);
}
int numBufferParams = reader.ReadInt32();
m_BufferParams = new BufferBinding[numBufferParams];
for (int i = 0; i < numBufferParams; i++)
{
m_BufferParams[i] = new BufferBinding(reader);
}
int numConstantBuffers = reader.ReadInt32();
m_ConstantBuffers = new ConstantBuffer[numConstantBuffers];
for (int i = 0; i < numConstantBuffers; i++)
{
m_ConstantBuffers[i] = new ConstantBuffer(reader);
}
int numConstantBufferBindings = reader.ReadInt32();
m_ConstantBufferBindings = new BufferBinding[numConstantBufferBindings];
for (int i = 0; i < numConstantBufferBindings; i++)
{
m_ConstantBufferBindings[i] = new BufferBinding(reader);
}
int numUAVParams = reader.ReadInt32();
m_UAVParams = new UAVParameter[numUAVParams];
for (int i = 0; i < numUAVParams; i++)
{
m_UAVParams[i] = new UAVParameter(reader);
}
if (version[0] >= 2017) //2017 and up
{
int numSamplers = reader.ReadInt32();
m_Samplers = new SamplerParameter[numSamplers];
for (int i = 0; i < numSamplers; i++)
{
m_Samplers[i] = new SamplerParameter(reader);
}
}
}
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 2)) //2017.2 and up
{
var m_ShaderRequirements = reader.ReadInt32();
if (version[0] >= 2021) //2021.1 and up
{
var m_ShaderRequirements = reader.ReadInt64();
}
else
{
var m_ShaderRequirements = reader.ReadInt32();
}
}
}
}
@@ -561,27 +691,50 @@ namespace AssetStudio
public class SerializedProgram
{
public SerializedSubProgram[] m_SubPrograms;
public SerializedProgramParameters m_CommonParameters;
public ushort[] m_SerializedKeywordStateMask;
public SerializedProgram(ObjectReader reader)
{
var version = reader.version;
int numSubPrograms = reader.ReadInt32();
m_SubPrograms = new SerializedSubProgram[numSubPrograms];
for (int i = 0; i < numSubPrograms; i++)
{
m_SubPrograms[i] = new SerializedSubProgram(reader);
}
if ((version[0] == 2020 && version[1] > 3) ||
(version[0] == 2020 && version[1] == 3 && version[2] >= 2) || //2020.3.2f1 and up
(version[0] > 2021) ||
(version[0] == 2021 && version[1] > 1) ||
(version[0] == 2021 && version[1] == 1 && version[2] >= 1)) //2021.1.1f1 and up
{
m_CommonParameters = new SerializedProgramParameters(reader);
}
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 1)) //2022.1 and up
{
m_SerializedKeywordStateMask = reader.ReadUInt16Array();
reader.AlignStream();
}
}
}
public enum PassType
{
kPassTypeNormal = 0,
kPassTypeUse = 1,
kPassTypeGrab = 2
Normal = 0,
Use = 1,
Grab = 2
};
public class SerializedPass
{
public Hash128[] m_EditorDataHash;
public byte[] m_Platforms;
public ushort[] m_LocalKeywordMask;
public ushort[] m_GlobalKeywordMask;
public KeyValuePair<string, int>[] m_NameIndices;
public PassType m_Type;
public SerializedShaderState m_State;
@@ -597,11 +750,32 @@ namespace AssetStudio
public string m_Name;
public string m_TextureName;
public SerializedTagMap m_Tags;
public ushort[] m_SerializedKeywordStateMask;
public SerializedPass(ObjectReader reader)
{
var version = reader.version;
if (version[0] > 2020 || (version[0] == 2020 && version[1] >= 2)) //2020.2 and up
{
int numEditorDataHash = reader.ReadInt32();
m_EditorDataHash = new Hash128[numEditorDataHash];
for (int i = 0; i < numEditorDataHash; i++)
{
m_EditorDataHash[i] = new Hash128(reader);
}
reader.AlignStream();
m_Platforms = reader.ReadUInt8Array();
reader.AlignStream();
if (version[0] < 2021 || (version[0] == 2021 && version[1] < 2)) //2021.1 and down
{
m_LocalKeywordMask = reader.ReadUInt16Array();
reader.AlignStream();
m_GlobalKeywordMask = reader.ReadUInt16Array();
reader.AlignStream();
}
}
int numIndices = reader.ReadInt32();
m_NameIndices = new KeyValuePair<string, int>[numIndices];
for (int i = 0; i < numIndices; i++)
@@ -631,6 +805,11 @@ namespace AssetStudio
m_Name = reader.ReadAlignedString();
m_TextureName = reader.ReadAlignedString();
m_Tags = new SerializedTagMap(reader);
if (version[0] == 2021 && version[1] >= 2) //2021.2 ~2021.x
{
m_SerializedKeywordStateMask = reader.ReadUInt16Array();
reader.AlignStream();
}
}
}
@@ -681,18 +860,35 @@ namespace AssetStudio
}
}
public class SerializedCustomEditorForRenderPipeline
{
public string customEditorName;
public string renderPipelineType;
public SerializedCustomEditorForRenderPipeline(BinaryReader reader)
{
customEditorName = reader.ReadAlignedString();
renderPipelineType = reader.ReadAlignedString();
}
}
public class SerializedShader
{
public SerializedProperties m_PropInfo;
public SerializedSubShader[] m_SubShaders;
public string[] m_KeywordNames;
public byte[] m_KeywordFlags;
public string m_Name;
public string m_CustomEditorName;
public string m_FallbackName;
public SerializedShaderDependency[] m_Dependencies;
public SerializedCustomEditorForRenderPipeline[] m_CustomEditorForRenderPipelines;
public bool m_DisableNoSubshadersMessage;
public SerializedShader(ObjectReader reader)
{
var version = reader.version;
m_PropInfo = new SerializedProperties(reader);
int numSubShaders = reader.ReadInt32();
@@ -702,6 +898,13 @@ namespace AssetStudio
m_SubShaders[i] = new SerializedSubShader(reader);
}
if (version[0] > 2021 || (version[0] == 2021 && version[1] >= 2)) //2021.2 and up
{
m_KeywordNames = reader.ReadStringArray();
m_KeywordFlags = reader.ReadUInt8Array();
reader.AlignStream();
}
m_Name = reader.ReadAlignedString();
m_CustomEditorName = reader.ReadAlignedString();
m_FallbackName = reader.ReadAlignedString();
@@ -713,6 +916,16 @@ namespace AssetStudio
m_Dependencies[i] = new SerializedShaderDependency(reader);
}
if (version[0] >= 2021) //2021.1 and up
{
int m_CustomEditorForRenderPipelinesSize = reader.ReadInt32();
m_CustomEditorForRenderPipelines = new SerializedCustomEditorForRenderPipeline[m_CustomEditorForRenderPipelinesSize];
for (int i = 0; i < m_CustomEditorForRenderPipelinesSize; i++)
{
m_CustomEditorForRenderPipelines[i] = new SerializedCustomEditorForRenderPipeline(reader);
}
}
m_DisableNoSubshadersMessage = reader.ReadBoolean();
reader.AlignStream();
}
@@ -720,28 +933,32 @@ namespace AssetStudio
public enum ShaderCompilerPlatform
{
kShaderCompPlatformNone = -1,
kShaderCompPlatformGL = 0,
kShaderCompPlatformD3D9 = 1,
kShaderCompPlatformXbox360 = 2,
kShaderCompPlatformPS3 = 3,
kShaderCompPlatformD3D11 = 4,
kShaderCompPlatformGLES20 = 5,
kShaderCompPlatformNaCl = 6,
kShaderCompPlatformFlash = 7,
kShaderCompPlatformD3D11_9x = 8,
kShaderCompPlatformGLES3Plus = 9,
kShaderCompPlatformPSP2 = 10,
kShaderCompPlatformPS4 = 11,
kShaderCompPlatformXboxOne = 12,
kShaderCompPlatformPSM = 13,
kShaderCompPlatformMetal = 14,
kShaderCompPlatformOpenGLCore = 15,
kShaderCompPlatformN3DS = 16,
kShaderCompPlatformWiiU = 17,
kShaderCompPlatformVulkan = 18,
kShaderCompPlatformSwitch = 19,
kShaderCompPlatformXboxOneD3D12 = 20
None = -1,
GL = 0,
D3D9 = 1,
Xbox360 = 2,
PS3 = 3,
D3D11 = 4,
GLES20 = 5,
NaCl = 6,
Flash = 7,
D3D11_9x = 8,
GLES3Plus = 9,
PSP2 = 10,
PS4 = 11,
XboxOne = 12,
PSM = 13,
Metal = 14,
OpenGLCore = 15,
N3DS = 16,
WiiU = 17,
Vulkan = 18,
Switch = 19,
XboxOneD3D12 = 20,
GameCoreXboxOne = 21,
GameCoreScarlett = 22,
PS5 = 23,
PS5NGGC = 24
};
public class Shader : NamedObject
@@ -753,9 +970,9 @@ namespace AssetStudio
//5.5 and up
public SerializedShader m_ParsedForm;
public ShaderCompilerPlatform[] platforms;
public uint[] offsets;
public uint[] compressedLengths;
public uint[] decompressedLengths;
public uint[][] offsets;
public uint[][] compressedLengths;
public uint[][] decompressedLengths;
public byte[] compressedBlob;
public Shader(ObjectReader reader) : base(reader)
@@ -766,27 +983,47 @@ namespace AssetStudio
platforms = reader.ReadUInt32Array().Select(x => (ShaderCompilerPlatform)x).ToArray();
if (version[0] > 2019 || (version[0] == 2019 && version[1] >= 3)) //2019.3 and up
{
offsets = reader.ReadUInt32ArrayArray().Select(x => x[0]).ToArray();
compressedLengths = reader.ReadUInt32ArrayArray().Select(x => x[0]).ToArray();
decompressedLengths = reader.ReadUInt32ArrayArray().Select(x => x[0]).ToArray();
offsets = reader.ReadUInt32ArrayArray();
compressedLengths = reader.ReadUInt32ArrayArray();
decompressedLengths = reader.ReadUInt32ArrayArray();
}
else
{
offsets = reader.ReadUInt32Array();
compressedLengths = reader.ReadUInt32Array();
decompressedLengths = reader.ReadUInt32Array();
offsets = reader.ReadUInt32Array().Select(x => new[] { x }).ToArray();
compressedLengths = reader.ReadUInt32Array().Select(x => new[] { x }).ToArray();
decompressedLengths = reader.ReadUInt32Array().Select(x => new[] { x }).ToArray();
}
compressedBlob = reader.ReadBytes(reader.ReadInt32());
compressedBlob = reader.ReadUInt8Array();
reader.AlignStream();
var m_DependenciesCount = reader.ReadInt32();
for (int i = 0; i < m_DependenciesCount; i++)
{
new PPtr<Shader>(reader);
}
if (version[0] >= 2018)
{
var m_NonModifiableTexturesCount = reader.ReadInt32();
for (int i = 0; i < m_NonModifiableTexturesCount; i++)
{
var first = reader.ReadAlignedString();
new PPtr<Texture>(reader);
}
}
var m_ShaderIsBaked = reader.ReadBoolean();
reader.AlignStream();
}
else
{
m_Script = reader.ReadBytes(reader.ReadInt32());
m_Script = reader.ReadUInt8Array();
reader.AlignStream();
var m_PathName = reader.ReadAlignedString();
if (version[0] == 5 && version[1] >= 3) //5.3 - 5.4
{
decompressedSize = reader.ReadUInt32();
m_SubProgramBlob = reader.ReadBytes(reader.ReadInt32());
m_SubProgramBlob = reader.ReadUInt8Array();
}
}
}

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
namespace AssetStudio
@@ -13,29 +12,29 @@ namespace AssetStudio
public SecondarySpriteTexture(ObjectReader reader)
{
texture = new PPtr<Texture2D>(reader);
name = reader.ReadStringToNull();
name = reader.ReadAlignedString();
}
}
public enum SpritePackingRotation
{
kSPRNone = 0,
kSPRFlipHorizontal = 1,
kSPRFlipVertical = 2,
kSPRRotate180 = 3,
kSPRRotate90 = 4
None = 0,
FlipHorizontal = 1,
FlipVertical = 2,
Rotate180 = 3,
Rotate90 = 4
};
public enum SpritePackingMode
{
kSPMTight = 0,
kSPMRectangle
Tight = 0,
Rectangle
};
public enum SpriteMeshType
{
kSpriteMeshTypeFullRect,
kSpriteMeshTypeTight
FullRect,
Tight
};
public class SpriteSettings
@@ -88,7 +87,7 @@ namespace AssetStudio
public ushort[] indices;
public Matrix4x4[] m_Bindpose;
public BoneWeights4[] m_SourceSkin;
public RectangleF textureRect;
public Rectf textureRect;
public Vector2 textureRectOffset;
public Vector2 atlasRectOffset;
public SpriteSettings settingsRaw;
@@ -124,7 +123,7 @@ namespace AssetStudio
m_SubMeshes[i] = new SubMesh(reader);
}
m_IndexBuffer = reader.ReadBytes(reader.ReadInt32());
m_IndexBuffer = reader.ReadUInt8Array();
reader.AlignStream();
m_VertexData = new VertexData(reader);
@@ -156,7 +155,7 @@ namespace AssetStudio
}
}
textureRect = reader.ReadRectangleF();
textureRect = new Rectf(reader);
textureRectOffset = reader.ReadVector2();
if (version[0] > 5 || (version[0] == 5 && version[1] >= 6)) //5.6 and up
{
@@ -176,13 +175,36 @@ namespace AssetStudio
}
}
public class Rectf
{
public float x;
public float y;
public float width;
public float height;
public Rectf(float x, float y, float w, float h) {
this.x = x;
this.y = y;
width = w;
height = h;
}
public Rectf(BinaryReader reader)
{
x = reader.ReadSingle();
y = reader.ReadSingle();
width = reader.ReadSingle();
height = reader.ReadSingle();
}
}
public sealed class Sprite : NamedObject
{
public RectangleF m_Rect;
public Rectf m_Rect;
public Vector2 m_Offset;
public Vector4 m_Border;
public float m_PixelsToUnits;
public Vector2 m_Pivot;
public Vector2 m_Pivot = new Vector2(0.5f, 0.5f);
public uint m_Extrude;
public bool m_IsPolygon;
public KeyValuePair<Guid, long> m_RenderDataKey;
@@ -190,10 +212,11 @@ namespace AssetStudio
public PPtr<SpriteAtlas> m_SpriteAtlas;
public SpriteRenderData m_RD;
public Vector2[][] m_PhysicsShape;
public bool akSplitAlpha;
public Sprite(ObjectReader reader) : base(reader)
{
m_Rect = reader.ReadRectangleF();
m_Rect = new Rectf(reader);
m_Offset = reader.ReadVector2();
if (version[0] > 4 || (version[0] == 4 && version[1] >= 5)) //4.5 and up
{
@@ -239,6 +262,8 @@ namespace AssetStudio
}
}
akSplitAlpha = false;
//vector m_Bones 2018 and up
}
}

View File

@@ -7,19 +7,20 @@ namespace AssetStudio
{
public PPtr<Texture2D> texture;
public PPtr<Texture2D> alphaTexture;
public System.Drawing.RectangleF textureRect;
public Rectf textureRect;
public Vector2 textureRectOffset;
public Vector2 atlasRectOffset;
public Vector4 uvTransform;
public float downscaleMultiplier;
public SpriteSettings settingsRaw;
public SecondarySpriteTexture[] secondaryTextures;
public SpriteAtlasData(ObjectReader reader)
{
var version = reader.version;
texture = new PPtr<Texture2D>(reader);
alphaTexture = new PPtr<Texture2D>(reader);
textureRect = reader.ReadRectangleF();
textureRect = new Rectf(reader);
textureRectOffset = reader.ReadVector2();
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 2)) //2017.2 and up
{
@@ -28,6 +29,16 @@ namespace AssetStudio
uvTransform = reader.ReadVector4();
downscaleMultiplier = reader.ReadSingle();
settingsRaw = new SpriteSettings(reader);
if (version[0] > 2020 || (version[0] == 2020 && version[1] >= 2)) //2020.2 and up
{
var secondaryTexturesSize = reader.ReadInt32();
secondaryTextures = new SecondarySpriteTexture[secondaryTexturesSize];
for (int i = 0; i < secondaryTexturesSize; i++)
{
secondaryTextures[i] = new SecondarySpriteTexture(reader);
}
reader.AlignStream();
}
}
}
@@ -35,6 +46,7 @@ namespace AssetStudio
{
public PPtr<Sprite>[] m_PackedSprites;
public Dictionary<KeyValuePair<Guid, long>, SpriteAtlasData> m_RenderDataMap;
public bool m_IsVariant;
public SpriteAtlas(ObjectReader reader) : base(reader)
{
@@ -56,8 +68,9 @@ namespace AssetStudio
var value = new SpriteAtlasData(reader);
m_RenderDataMap.Add(new KeyValuePair<Guid, long>(first, second), value);
}
//string m_Tag
//bool m_IsVariant
var m_Tag = reader.ReadAlignedString();
m_IsVariant = reader.ReadBoolean();
reader.AlignStream();
}
}
}

View File

@@ -12,7 +12,7 @@ namespace AssetStudio
public TextAsset(ObjectReader reader) : base(reader)
{
m_Script = reader.ReadBytes(reader.ReadInt32());
m_Script = reader.ReadUInt8Array();
}
}
}

View File

@@ -13,6 +13,10 @@ namespace AssetStudio
{
var m_ForcedFallbackFormat = reader.ReadInt32();
var m_DownscaleFallback = reader.ReadBoolean();
if (version[0] > 2020 || (version[0] == 2020 && version[1] >= 2)) //2020.2 and up
{
var m_IsAlphaChannelOptional = reader.ReadBoolean();
}
reader.AlignStream();
}
}

View File

@@ -4,13 +4,22 @@ namespace AssetStudio
{
public class StreamingInfo
{
public uint offset;
public long offset; //ulong
public uint size;
public string path;
public StreamingInfo(ObjectReader reader)
{
offset = reader.ReadUInt32();
var version = reader.version;
if (version[0] >= 2020) //2020.1 and up
{
offset = reader.ReadInt64();
}
else
{
offset = reader.ReadUInt32();
}
size = reader.ReadUInt32();
path = reader.ReadAlignedString();
}
@@ -59,6 +68,10 @@ namespace AssetStudio
m_Width = reader.ReadInt32();
m_Height = reader.ReadInt32();
var m_CompleteImageSize = reader.ReadInt32();
if (version[0] >= 2020) //2020.1 and up
{
var m_MipsStripped = reader.ReadInt32();
}
m_TextureFormat = (TextureFormat)reader.ReadInt32();
if (version[0] < 5 || (version[0] == 5 && version[1] < 2)) //5.2 down
{
@@ -68,9 +81,41 @@ namespace AssetStudio
{
m_MipCount = reader.ReadInt32();
}
var m_IsReadable = reader.ReadBoolean(); //2.6.0 and up
var m_ReadAllowed = reader.ReadBoolean(); //3.0.0 - 5.4
//bool m_StreamingMipmaps 2018.2 and up
if (version[0] > 2 || (version[0] == 2 && version[1] >= 6)) //2.6.0 and up
{
var m_IsReadable = reader.ReadBoolean();
}
if (version[0] >= 2020) //2020.1 and up
{
var m_IsPreProcessed = reader.ReadBoolean();
}
if (version[0] > 2019 || (version[0] == 2019 && version[1] >= 3)) //2019.3 and up
{
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 2)) //2022.2 and up
{
var m_IgnoreMipmapLimit = reader.ReadBoolean();
}
else
{
var m_IgnoreMasterTextureLimit = reader.ReadBoolean();
}
}
if (version[0] >= 3) //3.0.0 - 5.4
{
if (version[0] < 5 || (version[0] == 5 && version[1] <= 4))
{
var m_ReadAllowed = reader.ReadBoolean();
}
}
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 2)) //2022.2 and up
{
var m_MipmapLimitGroupName = reader.ReadAlignedString();
reader.AlignStream();
}
if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up
{
var m_StreamingMipmaps = reader.ReadBoolean();
}
reader.AlignStream();
if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up
{
@@ -87,6 +132,11 @@ namespace AssetStudio
{
var m_ColorSpace = reader.ReadInt32();
}
if (version[0] > 2020 || (version[0] == 2020 && version[1] >= 2)) //2020.2 and up
{
var m_PlatformBlob = reader.ReadUInt8Array();
reader.AlignStream();
}
var image_data_size = reader.ReadInt32();
if (image_data_size == 0 && ((version[0] == 5 && version[1] >= 3) || version[0] > 5))//5.3.0 and up
{
@@ -96,7 +146,7 @@ namespace AssetStudio
ResourceReader resourceReader;
if (!string.IsNullOrEmpty(m_StreamData?.path))
{
resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, (int)m_StreamData.size);
resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size);
}
else
{
@@ -113,10 +163,13 @@ namespace AssetStudio
RGB24,
RGBA32,
ARGB32,
RGB565 = 7,
R16 = 9,
ARGBFloat,
RGB565,
BGR24,
R16,
DXT1,
DXT5 = 12,
DXT3,
DXT5,
RGBA4444,
BGRA32,
RHalf,
@@ -127,11 +180,12 @@ namespace AssetStudio
RGBAFloat,
YUY2,
RGB9e5Float,
BC4 = 26,
BC5,
BC6H = 24,
RGBFloat,
BC6H,
BC7,
DXT1Crunched = 28,
BC4,
BC5,
DXT1Crunched,
DXT5Crunched,
PVRTC_RGB2,
PVRTC_RGBA2,
@@ -171,5 +225,8 @@ namespace AssetStudio
ASTC_HDR_8x8,
ASTC_HDR_10x10,
ASTC_HDR_12x12,
RG32,
RGB48,
RGBA64
}
}

View File

@@ -1,51 +1,75 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.IO;
namespace AssetStudio
{
public class StreamedResource
{
public string m_Source;
public long m_Offset; //ulong
public long m_Size; //ulong
public StreamedResource(BinaryReader reader)
{
m_Source = reader.ReadAlignedString();
m_Offset = reader.ReadInt64();
m_Size = reader.ReadInt64();
}
}
public sealed class VideoClip : NamedObject
{
public ResourceReader m_VideoData;
public string m_OriginalPath;
public string m_Source;
public ulong m_Size;
public StreamedResource m_ExternalResources;
public uint Width;
public uint Height;
public double m_FrameRate;
public int m_Format;
public bool m_HasSplitAlpha;
public VideoClip(ObjectReader reader) : base(reader)
{
m_OriginalPath = reader.ReadAlignedString();
var m_ProxyWidth = reader.ReadUInt32();
var m_ProxyHeight = reader.ReadUInt32();
var Width = reader.ReadUInt32();
var Height = reader.ReadUInt32();
Width = reader.ReadUInt32();
Height = reader.ReadUInt32();
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 2)) //2017.2 and up
{
var m_PixelAspecRatioNum = reader.ReadUInt32();
var m_PixelAspecRatioDen = reader.ReadUInt32();
}
var m_FrameRate = reader.ReadDouble();
m_FrameRate = reader.ReadDouble();
var m_FrameCount = reader.ReadUInt64();
var m_Format = reader.ReadInt32();
m_Format = reader.ReadInt32();
var m_AudioChannelCount = reader.ReadUInt16Array();
reader.AlignStream();
var m_AudioSampleRate = reader.ReadUInt32Array();
var m_AudioLanguage = reader.ReadStringArray();
//StreamedResource m_ExternalResources
m_Source = reader.ReadAlignedString();
var m_Offset = reader.ReadUInt64();
m_Size = reader.ReadUInt64();
var m_HasSplitAlpha = reader.ReadBoolean();
if (version[0] >= 2020) //2020.1 and up
{
var m_VideoShadersSize = reader.ReadInt32();
var m_VideoShaders = new PPtr<Shader>[m_VideoShadersSize];
for (int i = 0; i < m_VideoShadersSize; i++)
{
m_VideoShaders[i] = new PPtr<Shader>(reader);
}
}
m_ExternalResources = new StreamedResource(reader);
m_HasSplitAlpha = reader.ReadBoolean();
if (version[0] >= 2020) //2020.1 and up
{
var m_sRGB = reader.ReadBoolean();
}
ResourceReader resourceReader;
if (!string.IsNullOrEmpty(m_Source))
if (!string.IsNullOrEmpty(m_ExternalResources.m_Source))
{
resourceReader = new ResourceReader(m_Source, assetsFile, (long)m_Offset, (int)m_Size);
resourceReader = new ResourceReader(m_ExternalResources.m_Source, assetsFile, m_ExternalResources.m_Offset, m_ExternalResources.m_Size);
}
else
{
resourceReader = new ResourceReader(reader, reader.BaseStream.Position, (int)m_Size);
resourceReader = new ResourceReader(reader, reader.BaseStream.Position, m_ExternalResources.m_Size);
}
m_VideoData = resourceReader;
}

View File

@@ -0,0 +1,49 @@
using System;
namespace AssetStudio
{
// Represents set with 16 base colors using ANSI escape codes, which should be supported in most terminals
// (well, except for windows editions before windows 10)
public static class ColorConsole
{
public static readonly string
Black = "\u001b[30m",
Red = "\u001b[31m",
Green = "\u001b[32m",
Yellow = "\u001b[33m", //remapped to ~BrightWhite in Windows PowerShell 6
Blue = "\u001b[34m",
Magenta = "\u001b[35m", //remapped to ~Blue in Windows PowerShell 6
Cyan = "\u001b[36m",
White = "\u001b[37m",
BrightBlack = "\u001b[30;1m",
BrightRed = "\u001b[31;1m",
BrightGreen = "\u001b[32;1m",
BrightYellow = "\u001b[33;1m",
BrightBlue = "\u001b[34;1m",
BrightMagenta = "\u001b[35;1m",
BrightCyan = "\u001b[36;1m",
BrightWhite = "\u001b[37;1m";
private static readonly string Reset = "\u001b[0m";
public static string Color(this string str, string ansiColor)
{
if (!ColorConsoleHelper.isAnsiCodesSupported)
{
return str;
}
return $"{ansiColor}{str}{Reset}";
}
public static void AnsiCodesTest()
{
Console.WriteLine("ANSI escape codes test");
Console.WriteLine($"Supported: {ColorConsoleHelper.isAnsiCodesSupported}");
Console.WriteLine("\u001b[30m A \u001b[31m B \u001b[32m C \u001b[33m D \u001b[0m");
Console.WriteLine("\u001b[34m E \u001b[35m F \u001b[36m G \u001b[37m H \u001b[0m");
Console.WriteLine("\u001b[30;1m A \u001b[31;1m B \u001b[32;1m C \u001b[33;1m D \u001b[0m");
Console.WriteLine("\u001b[34;1m E \u001b[35;1m F \u001b[36;1m G \u001b[37;1m H \u001b[0m");
}
}
}

View File

@@ -0,0 +1,57 @@
// Based on code by tomzorz (https://gist.github.com/tomzorz/6142d69852f831fb5393654c90a1f22e)
using System;
using System.Runtime.InteropServices;
namespace AssetStudio
{
internal static class ColorConsoleHelper
{
public static readonly bool isAnsiCodesSupported;
private const int STD_OUTPUT_HANDLE = -11;
private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
[DllImport("kernel32.dll")]
private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
[DllImport("kernel32.dll")]
private static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
[DllImport("kernel32.dll")]
private static extern IntPtr GetStdHandle(int nStdHandle);
static ColorConsoleHelper()
{
var isWin = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
if (isWin)
{
isAnsiCodesSupported = TryEnableVTMode();
if (!isAnsiCodesSupported)
{
//Check for bash terminal emulator. E.g., Git Bash, Cmder
isAnsiCodesSupported = Environment.GetEnvironmentVariable("TERM") != null;
}
}
else
{
isAnsiCodesSupported = true;
}
}
// Enable support for ANSI escape codes
// (but probably only suitable for windows 10+)
// https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
private static bool TryEnableVTMode()
{
var iStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (!GetConsoleMode(iStdOut, out uint outConsoleMode))
{
return false;
}
outConsoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
return SetConsoleMode(iStdOut, outConsoleMode);
}
}
}

View File

@@ -113,7 +113,8 @@ namespace AssetStudio
{1093, "m_CorrespondingSourceObject"},
{1121, "m_PrefabInstance"},
{1138, "m_PrefabAsset"},
{1152, "FileSize"}
{1152, "FileSize"},
{1161, "Hash128"}
};
}
}

View File

@@ -1,23 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Buffers.Binary;
using System.IO;
namespace AssetStudio
{
public enum EndianType
{
LittleEndian,
BigEndian
}
public class EndianBinaryReader : BinaryReader
{
public EndianType endian;
private readonly byte[] buffer;
public EndianType Endian;
public EndianBinaryReader(Stream stream, EndianType endian = EndianType.BigEndian) : base(stream)
{
this.endian = endian;
Endian = endian;
buffer = new byte[8];
}
public long Position
@@ -28,88 +24,82 @@ namespace AssetStudio
public override short ReadInt16()
{
if (endian == EndianType.BigEndian)
if (Endian == EndianType.BigEndian)
{
var buff = ReadBytes(2);
Array.Reverse(buff);
return BitConverter.ToInt16(buff, 0);
Read(buffer, 0, 2);
return BinaryPrimitives.ReadInt16BigEndian(buffer);
}
return base.ReadInt16();
}
public override int ReadInt32()
{
if (endian == EndianType.BigEndian)
if (Endian == EndianType.BigEndian)
{
var buff = ReadBytes(4);
Array.Reverse(buff);
return BitConverter.ToInt32(buff, 0);
Read(buffer, 0, 4);
return BinaryPrimitives.ReadInt32BigEndian(buffer);
}
return base.ReadInt32();
}
public override long ReadInt64()
{
if (endian == EndianType.BigEndian)
if (Endian == EndianType.BigEndian)
{
var buff = ReadBytes(8);
Array.Reverse(buff);
return BitConverter.ToInt64(buff, 0);
Read(buffer, 0, 8);
return BinaryPrimitives.ReadInt64BigEndian(buffer);
}
return base.ReadInt64();
}
public override ushort ReadUInt16()
{
if (endian == EndianType.BigEndian)
if (Endian == EndianType.BigEndian)
{
var buff = ReadBytes(2);
Array.Reverse(buff);
return BitConverter.ToUInt16(buff, 0);
Read(buffer, 0, 2);
return BinaryPrimitives.ReadUInt16BigEndian(buffer);
}
return base.ReadUInt16();
}
public override uint ReadUInt32()
{
if (endian == EndianType.BigEndian)
if (Endian == EndianType.BigEndian)
{
var buff = ReadBytes(4);
Array.Reverse(buff);
return BitConverter.ToUInt32(buff, 0);
Read(buffer, 0, 4);
return BinaryPrimitives.ReadUInt32BigEndian(buffer);
}
return base.ReadUInt32();
}
public override ulong ReadUInt64()
{
if (endian == EndianType.BigEndian)
if (Endian == EndianType.BigEndian)
{
var buff = ReadBytes(8);
Array.Reverse(buff);
return BitConverter.ToUInt64(buff, 0);
Read(buffer, 0, 8);
return BinaryPrimitives.ReadUInt64BigEndian(buffer);
}
return base.ReadUInt64();
}
public override float ReadSingle()
{
if (endian == EndianType.BigEndian)
if (Endian == EndianType.BigEndian)
{
var buff = ReadBytes(4);
Array.Reverse(buff);
return BitConverter.ToSingle(buff, 0);
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)
if (Endian == EndianType.BigEndian)
{
var buff = ReadBytes(8);
Array.Reverse(buff);
return BitConverter.ToUInt64(buff, 0);
Read(buffer, 0, 8);
Array.Reverse(buffer);
return BitConverter.ToDouble(buffer, 0);
}
return base.ReadDouble();
}

14
AssetStudio/EndianType.cs Normal file
View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AssetStudio
{
public enum EndianType
{
LittleEndian,
BigEndian
}
}

View File

@@ -35,10 +35,13 @@ namespace AssetStudio
return "";
}
public static string ReadStringToNull(this BinaryReader reader, int maxLength = 32767)
public static string ReadStringToNull(this BinaryReader reader, int maxLength = 32767, Encoding encoding = null)
{
if (encoding?.CodePage == 1200) //Unicode (UTF-16LE)
return reader.ReadUnicodeStringToNull(maxLength * 2);
var bytes = new List<byte>();
int count = 0;
var count = 0;
while (reader.BaseStream.Position != reader.BaseStream.Length && count < maxLength)
{
var b = reader.ReadByte();
@@ -49,7 +52,24 @@ namespace AssetStudio
bytes.Add(b);
count++;
}
return Encoding.UTF8.GetString(bytes.ToArray());
return encoding?.GetString(bytes.ToArray()) ?? Encoding.UTF8.GetString(bytes.ToArray());
}
private static string ReadUnicodeStringToNull(this BinaryReader reader, int maxLength)
{
var bytes = new List<byte>();
var count = 0;
while (reader.BaseStream.Position != reader.BaseStream.Length && count < maxLength)
{
var b = reader.ReadBytes(2);
if (b.Length < 2 || (b[0] == 0 && b[1] == 0))
{
break;
}
bytes.AddRange(b);
count += 2;
}
return Encoding.Unicode.GetString(bytes.ToArray());
}
public static Quaternion ReadQuaternion(this BinaryReader reader)
@@ -72,11 +92,6 @@ namespace AssetStudio
return new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
}
public static System.Drawing.RectangleF ReadRectangleF(this BinaryReader reader)
{
return new System.Drawing.RectangleF(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
}
public static Color ReadColor4(this BinaryReader reader)
{
return new Color(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());
@@ -102,6 +117,11 @@ namespace AssetStudio
return ReadArray(reader.ReadBoolean, reader.ReadInt32());
}
public static byte[] ReadUInt8Array(this BinaryReader reader)
{
return reader.ReadBytes(reader.ReadInt32());
}
public static ushort[] ReadUInt16Array(this BinaryReader reader)
{
return ReadArray(reader.ReadUInt16, reader.ReadInt32());

103
AssetStudio/FileReader.cs Normal file
View File

@@ -0,0 +1,103 @@
using System.IO;
using System.Linq;
namespace AssetStudio
{
public class FileReader : EndianBinaryReader
{
public string FullPath;
public string FileName;
public FileType FileType;
private static readonly byte[] gzipMagic = { 0x1f, 0x8b };
private static readonly byte[] brotliMagic = { 0x62, 0x72, 0x6F, 0x74, 0x6C, 0x69 };
private static readonly byte[] zipMagic = { 0x50, 0x4B, 0x03, 0x04 };
private static readonly byte[] zipSpannedMagic = { 0x50, 0x4B, 0x07, 0x08 };
public FileReader(string path) : this(path, File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { }
public FileReader(string path, Stream stream) : base(stream, EndianType.BigEndian)
{
FullPath = Path.GetFullPath(path);
FileName = Path.GetFileName(path);
FileType = CheckFileType();
}
private FileType CheckFileType()
{
var signature = this.ReadStringToNull(20);
Position = 0;
switch (signature)
{
case "UnityWeb":
case "UnityRaw":
case "UnityArchive":
case "UnityFS":
return FileType.BundleFile;
case "UnityWebData1.0":
return FileType.WebFile;
default:
{
byte[] magic = ReadBytes(2);
Position = 0;
if (gzipMagic.SequenceEqual(magic))
{
return FileType.GZipFile;
}
Position = 0x20;
magic = ReadBytes(6);
Position = 0;
if (brotliMagic.SequenceEqual(magic))
{
return FileType.BrotliFile;
}
if (IsSerializedFile())
{
return FileType.AssetsFile;
}
magic = ReadBytes(4);
Position = 0;
if (zipMagic.SequenceEqual(magic) || zipSpannedMagic.SequenceEqual(magic))
return FileType.ZipFile;
return FileType.ResourceFile;
}
}
}
private bool IsSerializedFile()
{
var fileSize = BaseStream.Length;
if (fileSize < 20)
{
return false;
}
var m_MetadataSize = ReadUInt32();
long m_FileSize = ReadUInt32();
var m_Version = ReadUInt32();
long m_DataOffset = ReadUInt32();
var m_Endianess = ReadByte();
var m_Reserved = ReadBytes(3);
if (m_Version >= 22)
{
if (fileSize < 48)
{
Position = 0;
return false;
}
m_MetadataSize = ReadUInt32();
m_FileSize = ReadInt64();
m_DataOffset = ReadInt64();
}
Position = 0;
if (m_FileSize != fileSize)
{
return false;
}
if (m_DataOffset > fileSize)
{
return false;
}
return true;
}
}
}

19
AssetStudio/FileType.cs Normal file
View File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AssetStudio
{
public enum FileType
{
AssetsFile,
BundleFile,
WebFile,
ResourceFile,
GZipFile,
BrotliFile,
ZipFile
}
}

View File

@@ -73,6 +73,28 @@ namespace AssetStudio
return null;
}
public ImportedFrame FindRelativeFrameWithPath(string path)
{
var subs = path.Split(new[] { '/' }, 2);
foreach (var child in children)
{
if (child.Name == subs[0])
{
if (subs.Length == 1)
{
return child;
}
else
{
var result = child.FindRelativeFrameWithPath(subs[1]);
if (result != null)
return result;
}
}
}
return null;
}
public ImportedFrame FindFrame(string name)
{
if (Name == name)
@@ -132,6 +154,7 @@ namespace AssetStudio
public class ImportedMesh
{
public string Path { get; set; }
public List<ImportedVertex> VertexList { get; set; }
public List<ImportedSubmesh> SubmeshList { get; set; }
public List<ImportedBone> BoneList { get; set; }
public bool hasNormal { get; set; }
@@ -142,9 +165,9 @@ namespace AssetStudio
public class ImportedSubmesh
{
public List<ImportedVertex> VertexList { get; set; }
public List<ImportedFace> FaceList { get; set; }
public string Material { get; set; }
public int BaseVertex { get; set; }
}
public class ImportedVertex

View File

@@ -16,11 +16,11 @@ namespace AssetStudio
public interface ILogger
{
void Log(LoggerEvent loggerEvent, string message);
void Log(LoggerEvent loggerEvent, string message, bool ignoreLevel = false);
}
public sealed class DummyLogger : ILogger
{
public void Log(LoggerEvent loggerEvent, string message) { }
public void Log(LoggerEvent loggerEvent, string message, bool ignoreLevel) { }
}
}

View File

@@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
{
public interface IProgress
{
void Report(int value);
}
public sealed class DummyProgress : IProgress
{
public void Report(int value) { }
}
}

View File

@@ -1,17 +1,11 @@
using System.Collections.Generic;
using Org.Brotli.Dec;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
namespace AssetStudio
{
public enum FileType
{
AssetsFile,
BundleFile,
WebFile,
ResourceFile
}
public static class ImportHelper
{
public static void MergeSplitAssets(string path, bool allDirectories = false)
@@ -20,8 +14,8 @@ namespace AssetStudio
foreach (var splitFile in splitFiles)
{
var destFile = Path.GetFileNameWithoutExtension(splitFile);
var destPath = Path.GetDirectoryName(splitFile) + "\\";
var destFull = destPath + destFile;
var destPath = Path.GetDirectoryName(splitFile);
var destFull = Path.Combine(destPath, destFile);
if (!File.Exists(destFull))
{
var splitParts = Directory.GetFiles(destPath, destFile + ".split*");
@@ -43,7 +37,7 @@ namespace AssetStudio
public static string[] ProcessingSplitFiles(List<string> selectFile)
{
var splitFiles = selectFile.Where(x => x.Contains(".split"))
.Select(x => Path.GetDirectoryName(x) + "\\" + Path.GetFileNameWithoutExtension(x))
.Select(x => Path.Combine(Path.GetDirectoryName(x), Path.GetFileNameWithoutExtension(x)))
.Distinct()
.ToList();
selectFile.RemoveAll(x => x.Contains(".split"));
@@ -57,55 +51,40 @@ namespace AssetStudio
return selectFile.Distinct().ToArray();
}
public static FileType CheckFileType(Stream stream, out EndianBinaryReader reader)
public static FileReader DecompressGZip(FileReader reader)
{
reader = new EndianBinaryReader(stream);
return CheckFileType(reader);
}
public static FileType CheckFileType(string fileName, out EndianBinaryReader reader)
{
reader = new EndianBinaryReader(File.OpenRead(fileName));
return CheckFileType(reader);
}
private static FileType CheckFileType(EndianBinaryReader reader)
{
var signature = reader.ReadStringToNull(20);
reader.Position = 0;
switch (signature)
try
{
case "UnityWeb":
case "UnityRaw":
case "UnityArchive":
case "UnityFS":
return FileType.BundleFile;
case "UnityWebData1.0":
return FileType.WebFile;
default:
using (reader)
{
var stream = new MemoryStream();
using (var gs = new GZipStream(reader.BaseStream, CompressionMode.Decompress))
{
var magic = reader.ReadBytes(2);
reader.Position = 0;
if (WebFile.gzipMagic.SequenceEqual(magic))
{
return FileType.WebFile;
}
reader.Position = 0x20;
magic = reader.ReadBytes(6);
reader.Position = 0;
if (WebFile.brotliMagic.SequenceEqual(magic))
{
return FileType.WebFile;
}
if (SerializedFile.IsSerializedFile(reader))
{
return FileType.AssetsFile;
}
else
{
return FileType.ResourceFile;
}
gs.CopyTo(stream);
}
stream.Position = 0;
return new FileReader(reader.FullPath, stream);
}
}
catch (System.Exception e)
{
Logger.Warning($"Error while decompressing gzip file {reader.FullPath}\r\n{e}");
reader.Dispose();
return null;
}
}
public static FileReader DecompressBrotli(FileReader reader)
{
using (reader)
{
var stream = new MemoryStream();
using (var brotliStream = new BrotliInputStream(reader.BaseStream))
{
brotliStream.CopyTo(stream);
}
stream.Position = 0;
return new FileReader(reader.FullPath, stream);
}
}
}

75
AssetStudio/LZ4/LZ4.cs Normal file
View File

@@ -0,0 +1,75 @@
using System;
namespace AssetStudio
{
public class LZ4
{
public static LZ4 Instance => new LZ4();
public virtual int Decompress(ReadOnlySpan<byte> cmp, Span<byte> dec)
{
int cmpPos = 0;
int decPos = 0;
do
{
var (encCount, litCount) = GetLiteralToken(cmp, ref cmpPos);
//Copy literal chunk
litCount = GetLength(litCount, cmp, ref cmpPos);
cmp.Slice(cmpPos, litCount).CopyTo(dec.Slice(decPos));
cmpPos += litCount;
decPos += litCount;
if (cmpPos >= cmp.Length)
{
break;
}
//Copy compressed chunk
int back = GetChunkEnd(cmp, ref cmpPos);
encCount = GetLength(encCount, cmp, ref cmpPos) + 4;
int encPos = decPos - back;
if (encCount <= back)
{
dec.Slice(encPos, encCount).CopyTo(dec.Slice(decPos));
decPos += encCount;
}
else
{
while (encCount-- > 0)
{
dec[decPos++] = dec[encPos++];
}
}
} while (cmpPos < cmp.Length && decPos < dec.Length);
return decPos;
}
protected virtual (int encCount, int litCount) GetLiteralToken(ReadOnlySpan<byte> cmp, ref int cmpPos) =>
((cmp[cmpPos] >> 0) & 0xf, (cmp[cmpPos++] >> 4) & 0xf);
protected virtual int GetChunkEnd(ReadOnlySpan<byte> cmp, ref int cmpPos) =>
cmp[cmpPos++] << 0 | cmp[cmpPos++] << 8;
protected virtual int GetLength(int length, ReadOnlySpan<byte> cmp, ref int cmpPos)
{
byte sum;
if (length == 0xf)
{
do
{
length += sum = cmp[cmpPos++];
} while (sum == 0xff);
}
return length;
}
}
}

15
AssetStudio/LZ4/LZ4Inv.cs Normal file
View File

@@ -0,0 +1,15 @@
using System;
namespace AssetStudio
{
public class LZ4Inv : LZ4
{
public new static LZ4Inv Instance => new LZ4Inv();
protected override (int encCount, int litCount) GetLiteralToken(ReadOnlySpan<byte> cmp, ref int cmpPos) =>
((cmp[cmpPos] >> 4) & 0xf, (cmp[cmpPos++] >> 0) & 0xf);
protected override int GetChunkEnd(ReadOnlySpan<byte> cmp, ref int cmpPos) =>
cmp[cmpPos++] << 8 | cmp[cmpPos++] << 0;
}
}

View File

@@ -14,5 +14,14 @@ namespace AssetStudio
public static void Info(string message) => Default.Log(LoggerEvent.Info, message);
public static void Warning(string message) => Default.Log(LoggerEvent.Warning, message);
public static void Error(string message) => Default.Log(LoggerEvent.Error, message);
public static void Error(string message, Exception e)
{
var sb = new StringBuilder();
sb.AppendLine(message);
sb.AppendLine();
sb.AppendLine(e.ToString());
Default.Log(LoggerEvent.Error, sb.ToString());
}
}
}

View File

@@ -1,540 +0,0 @@
#define CHECK_ARGS
#define CHECK_EOF
//#define LOCAL_SHADOW
using System;
using System.IO;
namespace Lz4
{
public class Lz4DecoderStream : Stream
{
public Lz4DecoderStream(Stream input, long inputLength = long.MaxValue)
{
Reset(input, inputLength);
}
private void Reset(Stream input, long inputLength = long.MaxValue)
{
this.inputLength = inputLength;
this.input = input;
phase = DecodePhase.ReadToken;
decodeBufferPos = 0;
litLen = 0;
matLen = 0;
matDst = 0;
inBufPos = DecBufLen;
inBufEnd = DecBufLen;
}
protected override void Dispose(bool disposing)
{
try
{
if (disposing && input != null)
{
input.Close();
}
input = null;
decodeBuffer = null;
}
finally
{
base.Dispose(disposing);
}
}
private long inputLength;
private Stream input;
//because we might not be able to match back across invocations,
//we have to keep the last window's worth of bytes around for reuse
//we use a circular buffer for this - every time we write into this
//buffer, we also write the same into our output buffer
private const int DecBufLen = 0x10000;
private const int DecBufMask = 0xFFFF;
private const int InBufLen = 128;
private byte[] decodeBuffer = new byte[DecBufLen + InBufLen];
private int decodeBufferPos, inBufPos, inBufEnd;
//we keep track of which phase we're in so that we can jump right back
//into the correct part of decoding
private DecodePhase phase;
private enum DecodePhase
{
ReadToken,
ReadExLiteralLength,
CopyLiteral,
ReadOffset,
ReadExMatchLength,
CopyMatch,
}
//state within interruptable phases and across phase boundaries is
//kept here - again, so that we can punt out and restart freely
private int litLen, matLen, matDst;
public override int Read(byte[] buffer, int offset, int count)
{
#if CHECK_ARGS
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset < 0 || count < 0 || buffer.Length - count < offset)
throw new ArgumentOutOfRangeException();
if (input == null)
throw new InvalidOperationException();
#endif
int nRead, nToRead = count;
var decBuf = decodeBuffer;
//the stringy gotos are obnoxious, but their purpose is to
//make it *blindingly* obvious how the state machine transitions
//back and forth as it reads - remember, we can yield out of
//this routine in several places, and we must be able to re-enter
//and pick up where we left off!
#if LOCAL_SHADOW
var phase = this.phase;
var inBufPos = this.inBufPos;
var inBufEnd = this.inBufEnd;
#endif
switch (phase)
{
case DecodePhase.ReadToken:
goto readToken;
case DecodePhase.ReadExLiteralLength:
goto readExLiteralLength;
case DecodePhase.CopyLiteral:
goto copyLiteral;
case DecodePhase.ReadOffset:
goto readOffset;
case DecodePhase.ReadExMatchLength:
goto readExMatchLength;
case DecodePhase.CopyMatch:
goto copyMatch;
}
readToken:
int tok;
if (inBufPos < inBufEnd)
{
tok = decBuf[inBufPos++];
}
else
{
#if LOCAL_SHADOW
this.inBufPos = inBufPos;
#endif
tok = ReadByteCore();
#if LOCAL_SHADOW
inBufPos = this.inBufPos;
inBufEnd = this.inBufEnd;
#endif
#if CHECK_EOF
if (tok == -1)
goto finish;
#endif
}
litLen = tok >> 4;
matLen = (tok & 0xF) + 4;
switch (litLen)
{
case 0:
phase = DecodePhase.ReadOffset;
goto readOffset;
case 0xF:
phase = DecodePhase.ReadExLiteralLength;
goto readExLiteralLength;
default:
phase = DecodePhase.CopyLiteral;
goto copyLiteral;
}
readExLiteralLength:
int exLitLen;
if (inBufPos < inBufEnd)
{
exLitLen = decBuf[inBufPos++];
}
else
{
#if LOCAL_SHADOW
this.inBufPos = inBufPos;
#endif
exLitLen = ReadByteCore();
#if LOCAL_SHADOW
inBufPos = this.inBufPos;
inBufEnd = this.inBufEnd;
#endif
#if CHECK_EOF
if (exLitLen == -1)
goto finish;
#endif
}
litLen += exLitLen;
if (exLitLen == 255)
goto readExLiteralLength;
phase = DecodePhase.CopyLiteral;
goto copyLiteral;
copyLiteral:
int nReadLit = litLen < nToRead ? litLen : nToRead;
if (nReadLit != 0)
{
if (inBufPos + nReadLit <= inBufEnd)
{
int ofs = offset;
for (int c = nReadLit; c-- != 0;)
buffer[ofs++] = decBuf[inBufPos++];
nRead = nReadLit;
}
else
{
#if LOCAL_SHADOW
this.inBufPos = inBufPos;
#endif
nRead = ReadCore(buffer, offset, nReadLit);
#if LOCAL_SHADOW
inBufPos = this.inBufPos;
inBufEnd = this.inBufEnd;
#endif
#if CHECK_EOF
if (nRead == 0)
goto finish;
#endif
}
offset += nRead;
nToRead -= nRead;
litLen -= nRead;
if (litLen != 0)
goto copyLiteral;
}
if (nToRead == 0)
goto finish;
phase = DecodePhase.ReadOffset;
goto readOffset;
readOffset:
if (inBufPos + 1 < inBufEnd)
{
matDst = (decBuf[inBufPos + 1] << 8) | decBuf[inBufPos];
inBufPos += 2;
}
else
{
#if LOCAL_SHADOW
this.inBufPos = inBufPos;
#endif
matDst = ReadOffsetCore();
#if LOCAL_SHADOW
inBufPos = this.inBufPos;
inBufEnd = this.inBufEnd;
#endif
#if CHECK_EOF
if (matDst == -1)
goto finish;
#endif
}
if (matLen == 15 + 4)
{
phase = DecodePhase.ReadExMatchLength;
goto readExMatchLength;
}
else
{
phase = DecodePhase.CopyMatch;
goto copyMatch;
}
readExMatchLength:
int exMatLen;
if (inBufPos < inBufEnd)
{
exMatLen = decBuf[inBufPos++];
}
else
{
#if LOCAL_SHADOW
this.inBufPos = inBufPos;
#endif
exMatLen = ReadByteCore();
#if LOCAL_SHADOW
inBufPos = this.inBufPos;
inBufEnd = this.inBufEnd;
#endif
#if CHECK_EOF
if (exMatLen == -1)
goto finish;
#endif
}
matLen += exMatLen;
if (exMatLen == 255)
goto readExMatchLength;
phase = DecodePhase.CopyMatch;
goto copyMatch;
copyMatch:
int nCpyMat = matLen < nToRead ? matLen : nToRead;
if (nCpyMat != 0)
{
nRead = count - nToRead;
int bufDst = matDst - nRead;
if (bufDst > 0)
{
//offset is fairly far back, we need to pull from the buffer
int bufSrc = decodeBufferPos - bufDst;
if (bufSrc < 0)
bufSrc += DecBufLen;
int bufCnt = bufDst < nCpyMat ? bufDst : nCpyMat;
for (int c = bufCnt; c-- != 0;)
buffer[offset++] = decBuf[bufSrc++ & DecBufMask];
}
else
{
bufDst = 0;
}
int sOfs = offset - matDst;
for (int i = bufDst; i < nCpyMat; i++)
buffer[offset++] = buffer[sOfs++];
nToRead -= nCpyMat;
matLen -= nCpyMat;
}
if (nToRead == 0)
goto finish;
phase = DecodePhase.ReadToken;
goto readToken;
finish:
nRead = count - nToRead;
int nToBuf = nRead < DecBufLen ? nRead : DecBufLen;
int repPos = offset - nToBuf;
if (nToBuf == DecBufLen)
{
Buffer.BlockCopy(buffer, repPos, decBuf, 0, DecBufLen);
decodeBufferPos = 0;
}
else
{
int decPos = decodeBufferPos;
while (nToBuf-- != 0)
decBuf[decPos++ & DecBufMask] = buffer[repPos++];
decodeBufferPos = decPos & DecBufMask;
}
#if LOCAL_SHADOW
this.phase = phase;
this.inBufPos = inBufPos;
#endif
return nRead;
}
private int ReadByteCore()
{
var buf = decodeBuffer;
if (inBufPos == inBufEnd)
{
int nRead = input.Read(buf, DecBufLen,
InBufLen < inputLength ? InBufLen : (int)inputLength);
#if CHECK_EOF
if (nRead == 0)
return -1;
#endif
inputLength -= nRead;
inBufPos = DecBufLen;
inBufEnd = DecBufLen + nRead;
}
return buf[inBufPos++];
}
private int ReadOffsetCore()
{
var buf = decodeBuffer;
if (inBufPos == inBufEnd)
{
int nRead = input.Read(buf, DecBufLen,
InBufLen < inputLength ? InBufLen : (int)inputLength);
#if CHECK_EOF
if (nRead == 0)
return -1;
#endif
inputLength -= nRead;
inBufPos = DecBufLen;
inBufEnd = DecBufLen + nRead;
}
if (inBufEnd - inBufPos == 1)
{
buf[DecBufLen] = buf[inBufPos];
int nRead = input.Read(buf, DecBufLen + 1,
InBufLen - 1 < inputLength ? InBufLen - 1 : (int)inputLength);
#if CHECK_EOF
if (nRead == 0)
{
inBufPos = DecBufLen;
inBufEnd = DecBufLen + 1;
return -1;
}
#endif
inputLength -= nRead;
inBufPos = DecBufLen;
inBufEnd = DecBufLen + nRead + 1;
}
int ret = (buf[inBufPos + 1] << 8) | buf[inBufPos];
inBufPos += 2;
return ret;
}
private int ReadCore(byte[] buffer, int offset, int count)
{
int nToRead = count;
var buf = decodeBuffer;
int inBufLen = inBufEnd - inBufPos;
int fromBuf = nToRead < inBufLen ? nToRead : inBufLen;
if (fromBuf != 0)
{
var bufPos = inBufPos;
for (int c = fromBuf; c-- != 0;)
buffer[offset++] = buf[bufPos++];
inBufPos = bufPos;
nToRead -= fromBuf;
}
if (nToRead != 0)
{
int nRead;
if (nToRead >= InBufLen)
{
nRead = input.Read(buffer, offset,
nToRead < inputLength ? nToRead : (int)inputLength);
nToRead -= nRead;
}
else
{
nRead = input.Read(buf, DecBufLen,
InBufLen < inputLength ? InBufLen : (int)inputLength);
inBufPos = DecBufLen;
inBufEnd = DecBufLen + nRead;
fromBuf = nToRead < nRead ? nToRead : nRead;
var bufPos = inBufPos;
for (int c = fromBuf; c-- != 0;)
buffer[offset++] = buf[bufPos++];
inBufPos = bufPos;
nToRead -= fromBuf;
}
inputLength -= nRead;
}
return count - nToRead;
}
#region Stream internals
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override void Flush()
{
}
public override long Length => throw new NotSupportedException();
public override long Position
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
#endregion
}
}

View File

@@ -1,8 +1,8 @@
using System.Diagnostics;
using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.InteropServices;
namespace System
namespace AssetStudio
{
/// <summary>
/// Represents a half-precision floating point number.

View File

@@ -1,6 +1,7 @@
using System.Runtime.InteropServices;
using System;
using System.Runtime.InteropServices;
namespace System
namespace AssetStudio
{
/// <summary>
/// Helper class for Half conversions and some low level operations.

View File

@@ -11,6 +11,8 @@ namespace AssetStudio
public uint byteSize;
public int typeID;
public int classID;
public ushort isDestroyed;
public byte stripped;
public long m_PathID;
public SerializedType serializedType;

View File

@@ -15,12 +15,12 @@ namespace AssetStudio
public ClassIDType type;
public SerializedType serializedType;
public BuildTarget platform;
public uint m_Version;
public SerializedFileFormatVersion m_Version;
public int[] version => assetsFile.version;
public BuildType buildType => assetsFile.buildType;
public ObjectReader(EndianBinaryReader reader, SerializedFile assetsFile, ObjectInfo objectInfo) : base(reader.BaseStream, reader.endian)
public ObjectReader(EndianBinaryReader reader, SerializedFile assetsFile, ObjectInfo objectInfo) : base(reader.BaseStream, reader.Endian)
{
this.assetsFile = assetsFile;
m_PathID = objectInfo.m_PathID;

View File

@@ -1,8 +1,10 @@
namespace AssetStudio
using System;
namespace AssetStudio
{
public static class Progress
{
public static IProgress Default = new DummyProgress();
public static IProgress<int> Default = new Progress<int>();
private static int preValue;
public static void Reset()

View File

@@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("AssetStudio")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AssetStudio")]
[assembly: AssemblyCopyright("Copyright © Perfare 2018-2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("7662f8c2-7bfd-442e-a948-a43b4f7eb06e")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -8,11 +8,12 @@ namespace AssetStudio
private string path;
private SerializedFile assetsFile;
private long offset;
private int size;
private long size;
private BinaryReader reader;
public int Size { get => (int)size; }
public ResourceReader(string path, SerializedFile assetsFile, long offset, int size)
public ResourceReader(string path, SerializedFile assetsFile, long offset, long size)
{
needSearch = true;
this.path = path;
@@ -21,28 +22,25 @@ namespace AssetStudio
this.size = size;
}
public ResourceReader(BinaryReader reader, long offset, int size)
public ResourceReader(BinaryReader reader, long offset, long size)
{
this.reader = reader;
this.offset = offset;
this.size = size;
}
public byte[] GetData()
private BinaryReader GetReader()
{
if (needSearch)
{
var resourceFileName = Path.GetFileName(path);
if (assetsFile.assetsManager.resourceFileReaders.TryGetValue(resourceFileName, out reader))
{
needSearch = false;
reader.BaseStream.Position = offset;
return reader.ReadBytes(size);
return reader;
}
var assetsFileDirectory = Path.GetDirectoryName(assetsFile.fullName);
var resourceFilePath = assetsFileDirectory + Path.DirectorySeparatorChar + resourceFileName;
var resourceFilePath = Path.Combine(assetsFileDirectory, resourceFileName);
if (!File.Exists(resourceFilePath))
{
var findFiles = Directory.GetFiles(assetsFileDirectory, resourceFileName, SearchOption.AllDirectories);
@@ -53,18 +51,41 @@ namespace AssetStudio
}
if (File.Exists(resourceFilePath))
{
reader = new BinaryReader(File.OpenRead(resourceFilePath));
needSearch = false;
reader = new BinaryReader(File.OpenRead(resourceFilePath));
assetsFile.assetsManager.resourceFileReaders.Add(resourceFileName, reader);
reader.BaseStream.Position = offset;
return reader.ReadBytes(size);
return reader;
}
throw new FileNotFoundException($"Can't find the resource file {resourceFileName}");
}
else
{
return reader;
}
}
reader.BaseStream.Position = offset;
return reader.ReadBytes(size);
public byte[] GetData()
{
var binaryReader = GetReader();
binaryReader.BaseStream.Position = offset;
return binaryReader.ReadBytes((int)size);
}
public void GetData(byte[] buff)
{
var binaryReader = GetReader();
binaryReader.BaseStream.Position = offset;
binaryReader.Read(buff, 0, (int)size);
}
public void WriteData(string path)
{
var binaryReader = GetReader();
binaryReader.BaseStream.Position = offset;
using (var writer = File.OpenWrite(path))
{
binaryReader.BaseStream.CopyTo(writer, size);
}
}
}
}

View File

@@ -9,7 +9,7 @@ namespace AssetStudio
public class SerializedFile
{
public AssetsManager assetsManager;
public EndianBinaryReader reader;
public FileReader reader;
public string fullName;
public string originalPath;
public string fileName;
@@ -19,43 +19,45 @@ namespace AssetStudio
public Dictionary<long, Object> ObjectsDic;
public SerializedFileHeader header;
private EndianType m_FileEndianess;
private byte m_FileEndianess;
public string unityVersion = "2.5.0f5";
public BuildTarget m_TargetPlatform = BuildTarget.UnknownPlatform;
private bool m_EnableTypeTree = true;
public List<SerializedType> m_Types;
public List<SerializedType> m_RefTypes;
public int bigIDEnabled = 0;
public List<ObjectInfo> m_Objects;
private List<LocalSerializedObjectIdentifier> m_ScriptTypes;
public List<FileIdentifier> m_Externals;
public List<SerializedType> m_RefTypes;
public string userInformation;
public SerializedFile(AssetsManager assetsManager, string fullName, EndianBinaryReader reader)
public SerializedFile(FileReader reader, AssetsManager assetsManager)
{
this.assetsManager = assetsManager;
this.reader = reader;
this.fullName = fullName;
fileName = Path.GetFileName(fullName);
fullName = reader.FullPath;
fileName = reader.FileName;
//ReadHeader
// ReadHeader
header = new SerializedFileHeader();
header.m_MetadataSize = reader.ReadUInt32();
header.m_FileSize = reader.ReadUInt32();
header.m_Version = reader.ReadUInt32();
header.m_Version = (SerializedFileFormatVersion)reader.ReadUInt32();
header.m_DataOffset = reader.ReadUInt32();
if (header.m_Version >= 9)
if (header.m_Version >= SerializedFileFormatVersion.Unknown_9)
{
header.m_Endianess = reader.ReadByte();
header.m_Reserved = reader.ReadBytes(3);
m_FileEndianess = (EndianType)header.m_Endianess;
m_FileEndianess = header.m_Endianess;
}
else
{
reader.Position = header.m_FileSize - header.m_MetadataSize;
m_FileEndianess = (EndianType)reader.ReadByte();
m_FileEndianess = reader.ReadByte();
}
if (header.m_Version >= 22)
if (header.m_Version >= SerializedFileFormatVersion.LargeFilesSupport)
{
header.m_MetadataSize = reader.ReadUInt32();
header.m_FileSize = reader.ReadInt64();
@@ -63,17 +65,17 @@ namespace AssetStudio
reader.ReadInt64(); // unknown
}
//ReadMetadata
if (m_FileEndianess == EndianType.LittleEndian)
// ReadMetadata
if (m_FileEndianess == 0)
{
reader.endian = EndianType.LittleEndian;
reader.Endian = EndianType.LittleEndian;
}
if (header.m_Version >= 7)
if (header.m_Version >= SerializedFileFormatVersion.Unknown_7)
{
unityVersion = reader.ReadStringToNull();
SetVersion(unityVersion);
}
if (header.m_Version >= 8)
if (header.m_Version >= SerializedFileFormatVersion.Unknown_8)
{
m_TargetPlatform = (BuildTarget)reader.ReadInt32();
if (!Enum.IsDefined(typeof(BuildTarget), m_TargetPlatform))
@@ -81,26 +83,25 @@ namespace AssetStudio
m_TargetPlatform = BuildTarget.UnknownPlatform;
}
}
if (header.m_Version >= 13)
if (header.m_Version >= SerializedFileFormatVersion.HasTypeTreeHashes)
{
m_EnableTypeTree = reader.ReadBoolean();
}
//ReadTypes
// Read Types
int typeCount = reader.ReadInt32();
m_Types = new List<SerializedType>(typeCount);
for (int i = 0; i < typeCount; i++)
{
m_Types.Add(ReadSerializedType());
m_Types.Add(ReadSerializedType(false));
}
var bigIDEnabled = 0;
if (header.m_Version >= 7 && header.m_Version < 14)
if (header.m_Version >= SerializedFileFormatVersion.Unknown_7 && header.m_Version < SerializedFileFormatVersion.Unknown_14)
{
bigIDEnabled = reader.ReadInt32();
}
//ReadObjects
// Read Objects
int objectCount = reader.ReadInt32();
m_Objects = new List<ObjectInfo>(objectCount);
Objects = new List<Object>(objectCount);
@@ -112,7 +113,7 @@ namespace AssetStudio
{
objectInfo.m_PathID = reader.ReadInt64();
}
else if (header.m_Version < 14)
else if (header.m_Version < SerializedFileFormatVersion.Unknown_14)
{
objectInfo.m_PathID = reader.ReadInt32();
}
@@ -122,7 +123,7 @@ namespace AssetStudio
objectInfo.m_PathID = reader.ReadInt64();
}
if (header.m_Version >= 22)
if (header.m_Version >= SerializedFileFormatVersion.LargeFilesSupport)
objectInfo.byteStart = reader.ReadInt64();
else
objectInfo.byteStart = reader.ReadUInt32();
@@ -130,7 +131,7 @@ namespace AssetStudio
objectInfo.byteStart += header.m_DataOffset;
objectInfo.byteSize = reader.ReadUInt32();
objectInfo.typeID = reader.ReadInt32();
if (header.m_Version < 16)
if (header.m_Version < SerializedFileFormatVersion.RefactoredClassId)
{
objectInfo.classID = reader.ReadUInt16();
objectInfo.serializedType = m_Types.Find(x => x.classID == objectInfo.typeID);
@@ -141,24 +142,24 @@ namespace AssetStudio
objectInfo.serializedType = type;
objectInfo.classID = type.classID;
}
if (header.m_Version < 11)
if (header.m_Version < SerializedFileFormatVersion.HasScriptTypeIndex)
{
var isDestroyed = reader.ReadUInt16();
objectInfo.isDestroyed = reader.ReadUInt16();
}
if (header.m_Version >= 11 && header.m_Version < 17)
if (header.m_Version >= SerializedFileFormatVersion.HasScriptTypeIndex && header.m_Version < SerializedFileFormatVersion.RefactorTypeData)
{
var m_ScriptTypeIndex = reader.ReadInt16();
if (objectInfo.serializedType != null)
objectInfo.serializedType.m_ScriptTypeIndex = m_ScriptTypeIndex;
}
if (header.m_Version == 15 || header.m_Version == 16)
if (header.m_Version == SerializedFileFormatVersion.SupportsStrippedObject || header.m_Version == SerializedFileFormatVersion.RefactoredClassId)
{
var stripped = reader.ReadByte();
objectInfo.stripped = reader.ReadByte();
}
m_Objects.Add(objectInfo);
}
if (header.m_Version >= 11)
if (header.m_Version >= SerializedFileFormatVersion.HasScriptTypeIndex)
{
int scriptCount = reader.ReadInt32();
m_ScriptTypes = new List<LocalSerializedObjectIdentifier>(scriptCount);
@@ -166,7 +167,7 @@ namespace AssetStudio
{
var m_ScriptType = new LocalSerializedObjectIdentifier();
m_ScriptType.localSerializedFileIndex = reader.ReadInt32();
if (header.m_Version < 14)
if (header.m_Version < SerializedFileFormatVersion.Unknown_14)
{
m_ScriptType.localIdentifierInFile = reader.ReadInt32();
}
@@ -184,11 +185,11 @@ namespace AssetStudio
for (int i = 0; i < externalsCount; i++)
{
var m_External = new FileIdentifier();
if (header.m_Version >= 6)
if (header.m_Version >= SerializedFileFormatVersion.Unknown_6)
{
var tempEmpty = reader.ReadStringToNull();
}
if (header.m_Version >= 5)
if (header.m_Version >= SerializedFileFormatVersion.Unknown_5)
{
m_External.guid = new Guid(reader.ReadBytes(16));
m_External.type = reader.ReadInt32();
@@ -198,19 +199,19 @@ namespace AssetStudio
m_Externals.Add(m_External);
}
if (header.m_Version >= 20)
if (header.m_Version >= SerializedFileFormatVersion.SupportsRefObject)
{
int refTypesCount = reader.ReadInt32();
m_RefTypes = new List<SerializedType>(refTypesCount);
for (int i = 0; i < refTypesCount; i++)
{
m_RefTypes.Add(ReadSerializedType());
m_RefTypes.Add(ReadSerializedType(true));
}
}
if (header.m_Version >= 5)
if (header.m_Version >= SerializedFileFormatVersion.Unknown_5)
{
var userInformation = reader.ReadStringToNull();
userInformation = reader.ReadStringToNull();
}
//reader.AlignStream(16);
@@ -218,80 +219,98 @@ namespace AssetStudio
public void SetVersion(string stringVersion)
{
unityVersion = stringVersion;
var buildSplit = Regex.Replace(stringVersion, @"\d", "").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries);
buildType = new BuildType(buildSplit[0]);
var versionSplit = Regex.Replace(stringVersion, @"\D", ".").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries);
version = versionSplit.Select(int.Parse).ToArray();
if (stringVersion != strippedVersion)
{
unityVersion = stringVersion;
var buildSplit = Regex.Replace(stringVersion, @"\d", "").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries);
if (buildSplit.Length == 0)
throw new NotSupportedException("Specified Unity version is not in a correct format.\n" +
"Specify full Unity version, including letters at the end.\n" +
"Example: 2017.4.39f1");
buildType = new BuildType(buildSplit[0]);
var versionSplit = Regex.Replace(stringVersion, @"\D", ".").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries);
version = versionSplit.Select(int.Parse).ToArray();
}
}
private SerializedType ReadSerializedType()
private SerializedType ReadSerializedType(bool isRefType)
{
var type = new SerializedType();
type.classID = reader.ReadInt32();
if (header.m_Version >= 16)
if (header.m_Version >= SerializedFileFormatVersion.RefactoredClassId)
{
type.m_IsStrippedType = reader.ReadBoolean();
}
if (header.m_Version >= 17)
if (header.m_Version >= SerializedFileFormatVersion.RefactorTypeData)
{
type.m_ScriptTypeIndex = reader.ReadInt16();
}
if (header.m_Version >= 13)
if (header.m_Version >= SerializedFileFormatVersion.HasTypeTreeHashes)
{
if ((header.m_Version < 16 && type.classID < 0) || (header.m_Version >= 16 && type.classID == 114))
if (isRefType && type.m_ScriptTypeIndex >= 0)
{
type.m_ScriptID = reader.ReadBytes(16); //Hash128
type.m_ScriptID = reader.ReadBytes(16);
}
type.m_OldTypeHash = reader.ReadBytes(16); //Hash128
else if ((header.m_Version < SerializedFileFormatVersion.RefactoredClassId && type.classID < 0) || (header.m_Version >= SerializedFileFormatVersion.RefactoredClassId && type.classID == 114))
{
type.m_ScriptID = reader.ReadBytes(16);
}
type.m_OldTypeHash = reader.ReadBytes(16);
}
if (m_EnableTypeTree)
{
var typeTree = new List<TypeTreeNode>();
if (header.m_Version >= 12 || header.m_Version == 10)
type.m_Type = new TypeTree();
type.m_Type.m_Nodes = new List<TypeTreeNode>();
if (header.m_Version >= SerializedFileFormatVersion.Unknown_12 || header.m_Version == SerializedFileFormatVersion.Unknown_10)
{
TypeTreeBlobRead(typeTree);
TypeTreeBlobRead(type.m_Type);
}
else
{
ReadTypeTree(typeTree);
ReadTypeTree(type.m_Type);
}
if (header.m_Version >= 21)
if (header.m_Version >= SerializedFileFormatVersion.StoresTypeDependencies)
{
type.m_TypeDependencies = reader.ReadInt32Array();
if (isRefType)
{
type.m_KlassName = reader.ReadStringToNull();
type.m_NameSpace = reader.ReadStringToNull();
type.m_AsmName = reader.ReadStringToNull();
}
else
{
type.m_TypeDependencies = reader.ReadInt32Array();
}
}
type.m_Nodes = typeTree;
}
return type;
}
private void ReadTypeTree(List<TypeTreeNode> typeTree, int level = 0)
private void ReadTypeTree(TypeTree m_Type, int level = 0)
{
var typeTreeNode = new TypeTreeNode();
typeTree.Add(typeTreeNode);
m_Type.m_Nodes.Add(typeTreeNode);
typeTreeNode.m_Level = level;
typeTreeNode.m_Type = reader.ReadStringToNull();
typeTreeNode.m_Name = reader.ReadStringToNull();
typeTreeNode.m_ByteSize = reader.ReadInt32();
if (header.m_Version == 2)
if (header.m_Version == SerializedFileFormatVersion.Unknown_2)
{
var variableCount = reader.ReadInt32();
}
if (header.m_Version != 3)
if (header.m_Version != SerializedFileFormatVersion.Unknown_3)
{
typeTreeNode.m_Index = reader.ReadInt32();
}
typeTreeNode.m_IsArray = reader.ReadInt32();
typeTreeNode.m_TypeFlags = reader.ReadInt32();
typeTreeNode.m_Version = reader.ReadInt32();
if (header.m_Version != 3)
if (header.m_Version != SerializedFileFormatVersion.Unknown_3)
{
typeTreeNode.m_MetaFlag = reader.ReadInt32();
}
@@ -299,40 +318,40 @@ namespace AssetStudio
int childrenCount = reader.ReadInt32();
for (int i = 0; i < childrenCount; i++)
{
ReadTypeTree(typeTree, level + 1);
ReadTypeTree(m_Type, level + 1);
}
}
private void TypeTreeBlobRead(List<TypeTreeNode> typeTree)
private void TypeTreeBlobRead(TypeTree m_Type)
{
int numberOfNodes = reader.ReadInt32();
int stringBufferSize = reader.ReadInt32();
for (int i = 0; i < numberOfNodes; i++)
{
var typeTreeNode = new TypeTreeNode();
typeTree.Add(typeTreeNode);
m_Type.m_Nodes.Add(typeTreeNode);
typeTreeNode.m_Version = reader.ReadUInt16();
typeTreeNode.m_Level = reader.ReadByte();
typeTreeNode.m_IsArray = reader.ReadBoolean() ? 1 : 0;
typeTreeNode.m_TypeFlags = reader.ReadByte();
typeTreeNode.m_TypeStrOffset = reader.ReadUInt32();
typeTreeNode.m_NameStrOffset = reader.ReadUInt32();
typeTreeNode.m_ByteSize = reader.ReadInt32();
typeTreeNode.m_Index = reader.ReadInt32();
typeTreeNode.m_MetaFlag = reader.ReadInt32();
if (header.m_Version >= 19)
if (header.m_Version >= SerializedFileFormatVersion.TypeTreeNodeWithTypeFlags)
{
typeTreeNode.m_RefTypeHash = reader.ReadUInt64();
}
}
var m_StringBuffer = reader.ReadBytes(stringBufferSize);
m_Type.m_StringBuffer = reader.ReadBytes(stringBufferSize);
using (var stringBufferReader = new BinaryReader(new MemoryStream(m_StringBuffer)))
using (var stringBufferReader = new BinaryReader(new MemoryStream(m_Type.m_StringBuffer)))
{
for (int i = 0; i < numberOfNodes; i++)
{
var typeTreeNode = typeTree[i];
typeTreeNode.m_Type = ReadString(stringBufferReader, typeTreeNode.m_TypeStrOffset);
typeTreeNode.m_Name = ReadString(stringBufferReader, typeTreeNode.m_NameStrOffset);
var m_Node = m_Type.m_Nodes[i];
m_Node.m_Type = ReadString(stringBufferReader, m_Node.m_TypeStrOffset);
m_Node.m_Name = ReadString(stringBufferReader, m_Node.m_NameStrOffset);
}
}
@@ -359,41 +378,8 @@ namespace AssetStudio
ObjectsDic.Add(obj.m_PathID, obj);
}
public static bool IsSerializedFile(EndianBinaryReader reader)
{
var fileSize = reader.BaseStream.Length;
if (fileSize < 20)
{
return false;
}
var m_MetadataSize = reader.ReadUInt32();
long m_FileSize = reader.ReadUInt32();
var m_Version = reader.ReadUInt32();
long m_DataOffset = reader.ReadUInt32();
var m_Endianess = reader.ReadByte();
var m_Reserved = reader.ReadBytes(3);
if (m_Version >= 22)
{
if (fileSize < 48)
{
return false;
}
m_MetadataSize = reader.ReadUInt32();
m_FileSize = reader.ReadInt64();
m_DataOffset = reader.ReadInt64();
}
if (m_FileSize != fileSize)
{
reader.Position = 0;
return false;
}
if (m_DataOffset > fileSize)
{
reader.Position = 0;
return false;
}
reader.Position = 0;
return true;
}
public bool IsVersionStripped => unityVersion == strippedVersion;
private const string strippedVersion = "0.0.0";
}
}

View File

@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AssetStudio
{
public enum SerializedFileFormatVersion
{
Unsupported = 1,
Unknown_2 = 2,
Unknown_3 = 3,
/// <summary>
/// 1.2.0 to 2.0.0
/// </summary>
Unknown_5 = 5,
/// <summary>
/// 2.1.0 to 2.6.1
/// </summary>
Unknown_6 = 6,
/// <summary>
/// 3.0.0b
/// </summary>
Unknown_7 = 7,
/// <summary>
/// 3.0.0 to 3.4.2
/// </summary>
Unknown_8 = 8,
/// <summary>
/// 3.5.0 to 4.7.2
/// </summary>
Unknown_9 = 9,
/// <summary>
/// 5.0.0aunk1
/// </summary>
Unknown_10 = 10,
/// <summary>
/// 5.0.0aunk2
/// </summary>
HasScriptTypeIndex = 11,
/// <summary>
/// 5.0.0aunk3
/// </summary>
Unknown_12 = 12,
/// <summary>
/// 5.0.0aunk4
/// </summary>
HasTypeTreeHashes = 13,
/// <summary>
/// 5.0.0unk
/// </summary>
Unknown_14 = 14,
/// <summary>
/// 5.0.1 to 5.4.0
/// </summary>
SupportsStrippedObject = 15,
/// <summary>
/// 5.5.0a
/// </summary>
RefactoredClassId = 16,
/// <summary>
/// 5.5.0unk to 2018.4
/// </summary>
RefactorTypeData = 17,
/// <summary>
/// 2019.1a
/// </summary>
RefactorShareableTypeTreeData = 18,
/// <summary>
/// 2019.1unk
/// </summary>
TypeTreeNodeWithTypeFlags = 19,
/// <summary>
/// 2019.2
/// </summary>
SupportsRefObject = 20,
/// <summary>
/// 2019.3 to 2019.4
/// </summary>
StoresTypeDependencies = 21,
/// <summary>
/// 2020.1 to x
/// </summary>
LargeFilesSupport = 22
}
}

View File

@@ -9,7 +9,7 @@ namespace AssetStudio
{
public uint m_MetadataSize;
public long m_FileSize;
public uint m_Version;
public SerializedFileFormatVersion m_Version;
public long m_DataOffset;
public byte m_Endianess;
public byte[] m_Reserved;

View File

@@ -10,9 +10,12 @@ namespace AssetStudio
public int classID;
public bool m_IsStrippedType;
public short m_ScriptTypeIndex = -1;
public List<TypeTreeNode> m_Nodes;
public TypeTree m_Type;
public byte[] m_ScriptID; //Hash128
public byte[] m_OldTypeHash; //Hash128
public int[] m_TypeDependencies;
public string m_KlassName;
public string m_NameSpace;
public string m_AsmName;
}
}

View File

@@ -34,15 +34,16 @@ namespace AssetStudio
return newOutStream;
}
public static void StreamDecompress(Stream inStream, Stream outStream, long inSize, long outSize)
public static void StreamDecompress(Stream compressedStream, Stream decompressedStream, long compressedSize, long decompressedSize)
{
var basePosition = compressedStream.Position;
var decoder = new Decoder();
var properties = new byte[5];
if (inStream.Read(properties, 0, 5) != 5)
if (compressedStream.Read(properties, 0, 5) != 5)
throw new Exception("input .lzma is too short");
decoder.SetDecoderProperties(properties);
inSize -= 5L;
decoder.Code(inStream, outStream, inSize, outSize, null);
decoder.Code(compressedStream, decompressedStream, compressedSize - 5, decompressedSize, null);
compressedStream.Position = basePosition + compressedSize;
}
}
}

View File

@@ -4,6 +4,7 @@ namespace AssetStudio
{
public class StreamFile
{
public string path;
public string fileName;
public Stream stream;
}

14
AssetStudio/TypeTree.cs Normal file
View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AssetStudio
{
public class TypeTree
{
public List<TypeTreeNode> m_Nodes;
public byte[] m_StringBuffer;
}
}

View File

@@ -1,39 +1,50 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Text;
namespace AssetStudio
{
public static class TypeTreeHelper
{
public static void ReadTypeString(StringBuilder sb, List<TypeTreeNode> members, BinaryReader reader)
public static string ReadTypeString(TypeTree m_Type, ObjectReader reader)
{
for (int i = 0; i < members.Count; i++)
reader.Reset();
var sb = new StringBuilder();
var m_Nodes = m_Type.m_Nodes;
for (int i = 0; i < m_Nodes.Count; i++)
{
ReadStringValue(sb, members, reader, ref i);
ReadStringValue(sb, m_Nodes, reader, ref i);
}
var readed = reader.Position - reader.byteStart;
if (readed != reader.byteSize)
{
Logger.Info($"Error while read type, read {readed} bytes but expected {reader.byteSize} bytes");
}
return sb.ToString();
}
private static void ReadStringValue(StringBuilder sb, List<TypeTreeNode> members, BinaryReader reader, ref int i)
private static void ReadStringValue(StringBuilder sb, List<TypeTreeNode> m_Nodes, BinaryReader reader, ref int i)
{
var member = members[i];
var level = member.m_Level;
var varTypeStr = member.m_Type;
var varNameStr = member.m_Name;
var m_Node = m_Nodes[i];
var level = m_Node.m_Level;
var varTypeStr = m_Node.m_Type;
var varNameStr = m_Node.m_Name;
object value = null;
var append = true;
var align = (member.m_MetaFlag & 0x4000) != 0;
var align = (m_Node.m_MetaFlag & 0x4000) != 0;
switch (varTypeStr)
{
case "SInt8":
value = reader.ReadSByte();
break;
case "UInt8":
case "char":
value = reader.ReadByte();
break;
case "char":
value = BitConverter.ToChar(reader.ReadBytes(2), 0);
break;
case "short":
case "SInt16":
value = reader.ReadInt16();
@@ -73,22 +84,23 @@ namespace AssetStudio
append = false;
var str = reader.ReadAlignedString();
sb.AppendFormat("{0}{1} {2} = \"{3}\"\r\n", (new string('\t', level)), varTypeStr, varNameStr, str);
i += 3;
var toSkip = GetNodes(m_Nodes, i);
i += toSkip.Count - 1;
break;
case "map":
{
if ((members[i + 1].m_MetaFlag & 0x4000) != 0)
if ((m_Nodes[i + 1].m_MetaFlag & 0x4000) != 0)
align = true;
append = false;
sb.AppendFormat("{0}{1} {2}\r\n", (new string('\t', level)), varTypeStr, varNameStr);
sb.AppendFormat("{0}{1} {2}\r\n", (new string('\t', level + 1)), "Array", "Array");
var size = reader.ReadInt32();
sb.AppendFormat("{0}{1} {2} = {3}\r\n", (new string('\t', level + 1)), "int", "size", size);
var map = GetMembers(members, i);
var map = GetNodes(m_Nodes, i);
i += map.Count - 1;
var first = GetMembers(map, 4);
var first = GetNodes(map, 4);
var next = 4 + first.Count;
var second = GetMembers(map, next);
var second = GetNodes(map, next);
for (int j = 0; j < size; j++)
{
sb.AppendFormat("{0}[{1}]\r\n", (new string('\t', level + 2)), j);
@@ -112,16 +124,16 @@ namespace AssetStudio
}
default:
{
if (i < members.Count - 1 && members[i + 1].m_Type == "Array") //Array
if (i < m_Nodes.Count - 1 && m_Nodes[i + 1].m_Type == "Array") //Array
{
if ((members[i + 1].m_MetaFlag & 0x4000) != 0)
if ((m_Nodes[i + 1].m_MetaFlag & 0x4000) != 0)
align = true;
append = false;
sb.AppendFormat("{0}{1} {2}\r\n", (new string('\t', level)), varTypeStr, varNameStr);
sb.AppendFormat("{0}{1} {2}\r\n", (new string('\t', level + 1)), "Array", "Array");
var size = reader.ReadInt32();
sb.AppendFormat("{0}{1} {2} = {3}\r\n", (new string('\t', level + 1)), "int", "size", size);
var vector = GetMembers(members, i);
var vector = GetNodes(m_Nodes, i);
i += vector.Count - 1;
for (int j = 0; j < size; j++)
{
@@ -135,7 +147,7 @@ namespace AssetStudio
{
append = false;
sb.AppendFormat("{0}{1} {2}\r\n", (new string('\t', level)), varTypeStr, varNameStr);
var @class = GetMembers(members, i);
var @class = GetNodes(m_Nodes, i);
i += @class.Count - 1;
for (int j = 1; j < @class.Count; j++)
{
@@ -151,33 +163,42 @@ namespace AssetStudio
reader.AlignStream();
}
public static UType ReadUType(List<TypeTreeNode> members, BinaryReader reader)
public static OrderedDictionary ReadType(TypeTree m_Types, ObjectReader reader)
{
var obj = new UType();
for (int i = 1; i < members.Count; i++)
reader.Reset();
var obj = new OrderedDictionary();
var m_Nodes = m_Types.m_Nodes;
for (int i = 1; i < m_Nodes.Count; i++)
{
var member = members[i];
var varNameStr = member.m_Name;
obj[varNameStr] = ReadValue(members, reader, ref i);
var m_Node = m_Nodes[i];
var varNameStr = m_Node.m_Name;
obj[varNameStr] = ReadValue(m_Nodes, reader, ref i);
}
var readed = reader.Position - reader.byteStart;
if (readed != reader.byteSize)
{
Logger.Info($"Error while read type, read {readed} bytes but expected {reader.byteSize} bytes");
}
return obj;
}
private static object ReadValue(List<TypeTreeNode> members, BinaryReader reader, ref int i)
private static object ReadValue(List<TypeTreeNode> m_Nodes, BinaryReader reader, ref int i)
{
var member = members[i];
var varTypeStr = member.m_Type;
var m_Node = m_Nodes[i];
var varTypeStr = m_Node.m_Type;
object value;
var align = (member.m_MetaFlag & 0x4000) != 0;
var align = (m_Node.m_MetaFlag & 0x4000) != 0;
switch (varTypeStr)
{
case "SInt8":
value = reader.ReadSByte();
break;
case "UInt8":
case "char":
value = reader.ReadByte();
break;
case "char":
value = BitConverter.ToChar(reader.ReadBytes(2), 0);
break;
case "short":
case "SInt16":
value = reader.ReadInt16();
@@ -215,17 +236,18 @@ namespace AssetStudio
break;
case "string":
value = reader.ReadAlignedString();
i += 3;
var toSkip = GetNodes(m_Nodes, i);
i += toSkip.Count - 1;
break;
case "map":
{
if ((members[i + 1].m_MetaFlag & 0x4000) != 0)
if ((m_Nodes[i + 1].m_MetaFlag & 0x4000) != 0)
align = true;
var map = GetMembers(members, i);
var map = GetNodes(m_Nodes, i);
i += map.Count - 1;
var first = GetMembers(map, 4);
var first = GetNodes(map, 4);
var next = 4 + first.Count;
var second = GetMembers(map, next);
var second = GetNodes(map, next);
var size = reader.ReadInt32();
var dic = new List<KeyValuePair<object, object>>(size);
for (int j = 0; j < size; j++)
@@ -246,11 +268,11 @@ namespace AssetStudio
}
default:
{
if (i < members.Count - 1 && members[i + 1].m_Type == "Array") //Array
if (i < m_Nodes.Count - 1 && m_Nodes[i + 1].m_Type == "Array") //Array
{
if ((members[i + 1].m_MetaFlag & 0x4000) != 0)
if ((m_Nodes[i + 1].m_MetaFlag & 0x4000) != 0)
align = true;
var vector = GetMembers(members, i);
var vector = GetNodes(m_Nodes, i);
i += vector.Count - 1;
var size = reader.ReadInt32();
var list = new List<object>(size);
@@ -264,9 +286,9 @@ namespace AssetStudio
}
else //Class
{
var @class = GetMembers(members, i);
var @class = GetNodes(m_Nodes, i);
i += @class.Count - 1;
var obj = new UType();
var obj = new OrderedDictionary();
for (int j = 1; j < @class.Count; j++)
{
var classmember = @class[j];
@@ -283,22 +305,22 @@ namespace AssetStudio
return value;
}
private static List<TypeTreeNode> GetMembers(List<TypeTreeNode> members, int index)
private static List<TypeTreeNode> GetNodes(List<TypeTreeNode> m_Nodes, int index)
{
var member2 = new List<TypeTreeNode>();
member2.Add(members[index]);
var level = members[index].m_Level;
for (int i = index + 1; i < members.Count; i++)
var nodes = new List<TypeTreeNode>();
nodes.Add(m_Nodes[index]);
var level = m_Nodes[index].m_Level;
for (int i = index + 1; i < m_Nodes.Count; i++)
{
var member = members[i];
var member = m_Nodes[i];
var level2 = member.m_Level;
if (level2 <= level)
{
return member2;
return nodes;
}
member2.Add(member);
nodes.Add(member);
}
return member2;
return nodes;
}
}
}

View File

@@ -11,12 +11,22 @@ namespace AssetStudio
public string m_Name;
public int m_ByteSize;
public int m_Index;
public int m_IsArray; //m_TypeFlags
public int m_TypeFlags; //m_IsArray
public int m_Version;
public int m_MetaFlag;
public int m_Level;
public uint m_TypeStrOffset;
public uint m_NameStrOffset;
public ulong m_RefTypeHash;
public TypeTreeNode() { }
public TypeTreeNode(string type, string name, int level, bool align)
{
m_Type = type;
m_Name = name;
m_Level = level;
m_MetaFlag = align ? 0x4000 : 0;
}
}
}

View File

@@ -1,108 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
{
public class UType : IDictionary<string, object>
{
private List<string> keys;
private IDictionary<string, object> values;
public UType()
{
keys = new List<string>();
values = new Dictionary<string, object>();
}
public object this[string key]
{
get
{
if (!values.ContainsKey(key))
{
return null;
}
return values[key];
}
set
{
if (!values.ContainsKey(key))
{
keys.Add(key);
}
values[key] = value;
}
}
public ICollection<string> Keys => keys;
public ICollection<object> Values => values.Values;
public int Count => keys.Count;
public bool IsReadOnly => false;
public void Add(string key, object value)
{
keys.Add(key);
values.Add(key, value);
}
public void Add(KeyValuePair<string, object> item)
{
keys.Add(item.Key);
values.Add(item);
}
public void Clear()
{
keys.Clear();
values.Clear();
}
public bool Contains(KeyValuePair<string, object> item)
{
return values.Contains(item);
}
public bool ContainsKey(string key)
{
return values.ContainsKey(key);
}
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
values.CopyTo(array, arrayIndex);
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return values.GetEnumerator();
}
public bool Remove(string key)
{
keys.Remove(key);
return values.Remove(key);
}
public bool Remove(KeyValuePair<string, object> item)
{
keys.Remove(item.Key);
return values.Remove(item);
}
public bool TryGetValue(string key, out object value)
{
return values.TryGetValue(key, out value);
}
IEnumerator IEnumerable.GetEnumerator()
{
return values.GetEnumerator();
}
}
}

View File

@@ -1,17 +1,11 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using Org.Brotli.Dec;
namespace AssetStudio
{
public class WebFile
{
public static byte[] gzipMagic = { 0x1f, 0x8b };
public static byte[] brotliMagic = { 0x62, 0x72, 0x6F, 0x74, 0x6C, 0x69 };
public StreamFile[] fileList;
private class WebData
@@ -23,50 +17,8 @@ namespace AssetStudio
public WebFile(EndianBinaryReader reader)
{
var magic = reader.ReadBytes(2);
reader.Position = 0;
if (gzipMagic.SequenceEqual(magic))
{
var stream = new MemoryStream();
using (var gs = new GZipStream(reader.BaseStream, CompressionMode.Decompress))
{
gs.CopyTo(stream);
}
stream.Position = 0;
using (var binaryReader = new BinaryReader(stream))
{
ReadWebData(binaryReader);
}
}
else
{
reader.Position = 0x20;
magic = reader.ReadBytes(6);
reader.Position = 0;
if (brotliMagic.SequenceEqual(magic))
{
var brotliStream = new BrotliInputStream(reader.BaseStream);
var stream = new MemoryStream();
brotliStream.CopyTo(stream);
stream.Position = 0;
using (var binaryReader = new BinaryReader(stream))
{
ReadWebData(binaryReader);
}
}
else
{
reader.endian = EndianType.LittleEndian;
ReadWebData(reader);
}
}
}
private void ReadWebData(BinaryReader reader)
{
reader.Endian = EndianType.LittleEndian;
var signature = reader.ReadStringToNull();
if (signature != "UnityWebData1.0")
return;
var headLength = reader.ReadInt32();
var dataList = new List<WebData>();
while (reader.BaseStream.Position < headLength)
@@ -83,6 +35,7 @@ namespace AssetStudio
{
var data = dataList[i];
var file = new StreamFile();
file.path = data.path;
file.fileName = Path.GetFileName(data.path);
reader.BaseStream.Position = data.dataOffset;
file.stream = new MemoryStream(reader.ReadBytes(data.dataLength));

View File

@@ -0,0 +1,114 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net472;net6.0;net7.0;net8.0</TargetFrameworks>
<AssemblyTitle>ArknightsStudio by aelurum</AssemblyTitle>
<AssemblyName>ArknightsStudioCLI</AssemblyName>
<Version>1.2.0</Version>
<Copyright>Copyright © Perfare; Copyright © aelurum 2025</Copyright>
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>embedded</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AssetStudioUtility\AssetStudioUtility.csproj" />
<ProjectReference Include="..\AssetStudio\AssetStudio.csproj" />
</ItemGroup>
<!-- Use local compiled win-x86 and win-x64 Texture2DDecoder libs, because libs from Kyaru.Texture2DDecoder.Windows were compiled with /MD flag -->
<Target Name="CopyExtraFilesPortable" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == '' ">
<Message Text="Copying windows extra files for $(TargetFramework)... " Importance="high" />
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\Win32\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\x64\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)runtimes\win-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\Win32\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\x64\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)runtimes\win-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x86\fmod.dll" DestinationFolder="$(TargetDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x64\fmod.dll" DestinationFolder="$(TargetDir)runtimes\win-x64\native" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesPortableNet" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == '' AND '$(TargetFramework)' != 'net472' ">
<Message Text="Copying other platforms extra files for $(TargetFramework)... " Importance="high" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libAssetStudioFBXNative.so" DestinationFolder="$(TargetDir)runtimes\linux-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\osx-x64\libAssetStudioFBXNative.dylib" DestinationFolder="$(TargetDir)runtimes\osx-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\osx-arm64\libAssetStudioFBXNative.dylib" DestinationFolder="$(TargetDir)runtimes\osx-arm64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x86\libfmod.so" DestinationFolder="$(TargetDir)runtimes\linux-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libfmod.so" DestinationFolder="$(TargetDir)runtimes\linux-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\osx-x64\libfmod.dylib" DestinationFolder="$(TargetDir)runtimes\osx-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\osx-arm64\libfmod.dylib" DestinationFolder="$(TargetDir)runtimes\osx-arm64\native" ContinueOnError="false" />
</Target>
<!-- Publishing an app as framework-dependent produces a cross-platform binary as a dll file, and a platform-specific executable that targets your current platform.
The dll is cross-platform while the executable isn't -->
<Target Name="PublishExtraFilesPortable" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == '' ">
<Message Text="Publishing windows extra files for Portable build ($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x86\native\AssetStudioFBXNative.dll" DestinationFolder="$(PublishDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x64\native\AssetStudioFBXNative.dll" DestinationFolder="$(PublishDir)runtimes\win-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x86\native\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x64\native\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)runtimes\win-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x86\native\fmod.dll" DestinationFolder="$(PublishDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\win-x64\native\fmod.dll" DestinationFolder="$(PublishDir)runtimes\win-x64\native" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesPortableNet" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == '' AND '$(TargetFramework)' != 'net472' ">
<Message Text="Publishing other platforms extra files for Portable build ($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(TargetDir)runtimes\linux-x64\native\libAssetStudioFBXNative.so" DestinationFolder="$(PublishDir)runtimes\linux-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\osx-x64\native\libAssetStudioFBXNative.dylib" DestinationFolder="$(PublishDir)runtimes\osx-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\osx-arm64\native\libAssetStudioFBXNative.dylib" DestinationFolder="$(PublishDir)runtimes\osx-arm64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\linux-x86\native\libfmod.so" DestinationFolder="$(PublishDir)runtimes\linux-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\linux-x64\native\libfmod.so" DestinationFolder="$(PublishDir)runtimes\linux-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\osx-x64\native\libfmod.dylib" DestinationFolder="$(PublishDir)runtimes\osx-x64\native" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)runtimes\osx-arm64\native\libfmod.dylib" DestinationFolder="$(PublishDir)runtimes\osx-arm64\native" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesWin86" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'win-x86' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\Win32\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\Win32\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x86\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesWin64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'win-x64' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\x64\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\x64\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\win-x64\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesWin" AfterTargets="Publish" Condition=" $(RuntimeIdentifier.Contains('win-x')) ">
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(TargetDir)\AssetStudioFBXNative.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\fmod.dll" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesLinux64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'linux-x64' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libAssetStudioFBXNative.so" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\linux-x64\libfmod.so" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesLinux64" AfterTargets="Publish" Condition=" '$(RuntimeIdentifier)' == 'linux-x64' ">
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(TargetDir)\libAssetStudioFBXNative.so" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\libfmod.so" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesMac" AfterTargets="AfterBuild" Condition=" $(RuntimeIdentifier.Contains('osx-')) ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(ProjectDir)Libraries\$(RuntimeIdentifier)\libAssetStudioFBXNative.dylib" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\$(RuntimeIdentifier)\libfmod.dylib" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesMac" AfterTargets="Publish" Condition=" $(RuntimeIdentifier.Contains('osx-')) ">
<Message Text="Publishing extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(TargetDir)\libAssetStudioFBXNative.dylib" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
<Copy SourceFiles="$(TargetDir)\libfmod.dylib" DestinationFolder="$(PublishDir)" ContinueOnError="false" />
</Target>
</Project>

119
AssetStudioCLI/CLILogger.cs Normal file
View File

@@ -0,0 +1,119 @@
using AssetStudio;
using AssetStudioCLI.Options;
using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace AssetStudioCLI
{
internal enum LogOutputMode
{
Console,
File,
Both,
}
internal class CLILogger : ILogger
{
private readonly LogOutputMode logOutput;
private readonly LoggerEvent logMinLevel;
public string LogName;
public string LogPath;
public CLILogger()
{
logOutput = CLIOptions.o_logOutput.Value;
logMinLevel = CLIOptions.o_logLevel.Value;
var appAssembly = typeof(Program).Assembly.GetName();
LogName = $"{appAssembly.Name}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log";
LogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, LogName);
var arch = Environment.Is64BitProcess ? "x64" : "x32";
Console.OutputEncoding = System.Text.Encoding.UTF8;
LogToFile(LoggerEvent.Verbose, $"---{appAssembly.Name} v{appAssembly.Version} [{arch}] | Logger launched---\n" +
$"CMD Args: {string.Join(" ", CLIOptions.cliArgs)}");
}
private static string ColorLogLevel(LoggerEvent logLevel)
{
var formattedLevel = $"[{logLevel}]";
switch (logLevel)
{
case LoggerEvent.Info:
return $"{formattedLevel.Color(ColorConsole.BrightCyan)}";
case LoggerEvent.Warning:
return $"{formattedLevel.Color(ColorConsole.BrightYellow)}";
case LoggerEvent.Error:
return $"{formattedLevel.Color(ColorConsole.BrightRed)}";
default:
return formattedLevel;
}
}
private static string FormatMessage(LoggerEvent logMsgLevel, string message, bool consoleMode = false)
{
var curTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
message = message.TrimEnd();
var multiLine = message.Contains('\n');
string formattedMessage;
if (consoleMode)
{
var colorLogLevel = ColorLogLevel(logMsgLevel);
formattedMessage = $"{colorLogLevel} {message}";
if (multiLine)
{
formattedMessage = formattedMessage.Replace("\n", $"\n{colorLogLevel} ");
}
}
else
{
message = Regex.Replace(message, @"\e\[[0-9;]*m(?:\e\[K)?", ""); //Delete ANSI colors
var logLevel = $"{logMsgLevel.ToString().ToUpper(),-7}";
formattedMessage = $"{curTime} | {logLevel} | {message}";
if (multiLine)
{
formattedMessage = formattedMessage.Replace("\n", $"\n{curTime} | {logLevel} | ");
}
}
return formattedMessage;
}
public void LogToConsole(LoggerEvent logMsgLevel, string message)
{
if (logOutput != LogOutputMode.File)
{
Console.WriteLine(FormatMessage(logMsgLevel, message, consoleMode: true));
}
}
public async void LogToFile(LoggerEvent logMsgLevel, string message)
{
if (logOutput != LogOutputMode.Console)
{
using (var sw = new StreamWriter(LogPath, append: true, System.Text.Encoding.UTF8))
{
await sw.WriteLineAsync(FormatMessage(logMsgLevel, message));
}
}
}
public void Log(LoggerEvent logMsgLevel, string message, bool ignoreLevel)
{
if ((logMsgLevel < logMinLevel && !ignoreLevel) || string.IsNullOrEmpty(message))
{
return;
}
if (logOutput != LogOutputMode.File)
{
LogToConsole(logMsgLevel, message);
}
if (logOutput != LogOutputMode.Console)
{
LogToFile(logMsgLevel, message);
}
}
}
}

View File

@@ -0,0 +1,231 @@
using Arknights.PortraitSpriteMono;
using AssetStudio;
using AssetStudioCLI;
using AssetStudioCLI.Options;
using Newtonsoft.Json;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System;
using System.Collections.Generic;
using System.IO;
namespace Arknights
{
internal static class AkSpriteHelper
{
public static Texture2D TryFindAlphaTex(AssetItem assetItem, AvgSprite avgSprite, bool isAvgSprite)
{
Sprite m_Sprite = (Sprite)assetItem.Asset;
var imgType = "arts/characters";
if (m_Sprite.m_RD.alphaTexture.m_PathID == 0)
{
if (isAvgSprite)
{
if (avgSprite?.FullAlphaTexture != null)
return avgSprite.FullAlphaTexture;
imgType = "avg/characters"; //since the avg hub was not found for some reason, let's try to find alpha tex by name
}
var spriteFullName = Path.GetFileNameWithoutExtension(assetItem.Container);
foreach (var item in Studio.loadedAssetsList)
{
if (item.Type == ClassIDType.Texture2D)
{
if (item.Container.Contains(imgType) && item.Container.Contains($"illust_{m_Sprite.m_Name}_material") && item.Text.Contains("[alpha]"))
return (Texture2D)item.Asset;
if (item.Container.Contains(imgType) && item.Container.Contains(spriteFullName) && item.Text == $"{m_Sprite.m_Name}[alpha]")
return (Texture2D)item.Asset;
}
}
}
return null;
}
public static Image<Bgra32> AkGetImage(this Sprite m_Sprite, AvgSprite avgSprite = null, SpriteMaskMode spriteMaskMode = SpriteMaskMode.On)
{
if (m_Sprite.m_RD.texture.TryGet(out var m_Texture2D) && m_Sprite.m_RD.alphaTexture.TryGet(out var m_AlphaTexture2D) && spriteMaskMode != SpriteMaskMode.Off)
{
Image<Bgra32> tex;
Image<Bgra32> alphaTex;
if (avgSprite != null && avgSprite.IsHubParsed)
{
alphaTex = m_AlphaTexture2D.ConvertToImage(true);
if (avgSprite.IsFaceSprite)
{
var faceImage = m_Texture2D.ConvertToImage(true);
var faceAlpha = avgSprite.FaceSpriteAlphaTexture.ConvertToImage(true);
if (new Size(faceImage.Width, faceImage.Height) != avgSprite.FaceSize)
{
faceImage.Mutate(x => x.Resize(new ResizeOptions { Size = avgSprite.FaceSize, Sampler = KnownResamplers.Lanczos3, Mode = ResizeMode.Stretch }));
faceAlpha.Mutate(x => x.Resize(new ResizeOptions { Size = avgSprite.FaceSize, Sampler = KnownResamplers.Lanczos3, Mode = ResizeMode.Stretch }));
}
tex = avgSprite.FullTexture.ConvertToImage(true);
tex.Mutate(x => x.DrawImage(faceImage, avgSprite.FacePos, opacity: 1f));
alphaTex.Mutate(x => x.DrawImage(faceAlpha, avgSprite.FacePos, opacity: 1f));
}
else
{
tex = m_Texture2D.ConvertToImage(true);
}
}
else
{
tex = CutImage(m_Texture2D.ConvertToImage(false), m_Sprite.m_RD.textureRect, m_Sprite.m_RD.downscaleMultiplier);
alphaTex = CutImage(m_AlphaTexture2D.ConvertToImage(false), m_Sprite.m_RD.textureRect, m_Sprite.m_RD.downscaleMultiplier);
}
tex.ApplyRGBMask(alphaTex);
return tex;
}
else if (m_Sprite.m_RD.texture.TryGet(out m_Texture2D) && avgSprite != null && avgSprite.IsHubParsed)
{
if (!avgSprite.IsFaceSprite)
{
return m_Texture2D.ConvertToImage(true);
}
var faceImage = m_Texture2D.ConvertToImage(true);
var tex = avgSprite.FullTexture.ConvertToImage(true);
if (new Size(faceImage.Width, faceImage.Height) != avgSprite.FaceSize)
{
faceImage.Mutate(x => x.Resize(new ResizeOptions { Size = avgSprite.FaceSize, Sampler = KnownResamplers.Lanczos3, Mode = ResizeMode.Stretch }));
}
tex.Mutate(x => x.DrawImage(faceImage, avgSprite.FacePos, opacity: 1f));
return tex;
}
else if (m_Sprite.m_RD.texture.TryGet(out m_Texture2D))
{
return CutImage(m_Texture2D.ConvertToImage(false), m_Sprite.m_RD.textureRect, m_Sprite.m_RD.downscaleMultiplier);
}
return null;
}
public static Image<Bgra32> AkGetImage(this PortraitSprite portraitSprite, SpriteMaskMode spriteMaskMode = SpriteMaskMode.On)
{
if (portraitSprite.Texture != null && portraitSprite.AlphaTexture != null)
{
var tex = CutImage(portraitSprite.Texture.ConvertToImage(false), portraitSprite.TextureRect, portraitSprite.DownscaleMultiplier, portraitSprite.Rotate);
if (spriteMaskMode == SpriteMaskMode.Off)
{
return tex;
}
else
{
var alphaTex = CutImage(portraitSprite.AlphaTexture.ConvertToImage(false), portraitSprite.TextureRect, portraitSprite.DownscaleMultiplier, portraitSprite.Rotate);
tex.ApplyRGBMask(alphaTex);
return tex;
}
}
return null;
}
public static List<PortraitSprite> GeneratePortraits(AssetItem asset)
{
var portraits = new List<PortraitSprite>();
var portraitsDict = ((MonoBehaviour)asset.Asset).ToType();
if (portraitsDict == null)
{
Logger.Warning("Portraits MonoBehaviour is not readable.");
return portraits;
}
var portraitsJson = JsonConvert.SerializeObject(portraitsDict);
var portraitsData = JsonConvert.DeserializeObject<PortraitSpriteConfig>(portraitsJson);
if (portraitsData._sprites.Length == 0)
return portraits;
var atlasTex = (Texture2D)Studio.loadedAssetsList.Find(x => x.m_PathID == portraitsData._atlas.Texture.m_PathID).Asset;
var atlasAlpha = (Texture2D)Studio.loadedAssetsList.Find(x => x.m_PathID == portraitsData._atlas.Alpha.m_PathID).Asset;
foreach (var portraitData in portraitsData._sprites)
{
var portraitSprite = new PortraitSprite()
{
Name = portraitData.Name,
AssetsFile = atlasTex.assetsFile,
Container = asset.Container,
Texture = atlasTex,
AlphaTexture = atlasAlpha,
TextureRect = new Rectf(portraitData.Rect.X, portraitData.Rect.Y, portraitData.Rect.W, portraitData.Rect.H),
Rotate = portraitData.Rotate,
};
portraits.Add(portraitSprite);
}
return portraits;
}
private static void ApplyRGBMask(this Image<Bgra32> tex, Image<Bgra32> texMask)
{
using (texMask)
{
bool resized = false;
if (tex.Width != texMask.Width || tex.Height != texMask.Height)
{
texMask.Mutate(x => x.Resize(tex.Width, tex.Height, CLIOptions.o_akAlphaTexResampler.Value));
resized = true;
}
var invGamma = 1.0 / (1.0 + CLIOptions.o_akShadowGamma.Value / 10.0);
if (CLIOptions.akResizedOnly && !resized)
{
invGamma = 1.0;
}
tex.ProcessPixelRows(texMask, (sourceTex, targetTexMask) =>
{
for (int y = 0; y < texMask.Height; y++)
{
var texRow = sourceTex.GetRowSpan(y);
var maskRow = targetTexMask.GetRowSpan(y);
for (int x = 0; x < maskRow.Length; x++)
{
var grayscale = (maskRow[x].R + maskRow[x].G + maskRow[x].B) / 3.0;
if (invGamma != 1.0)
{
grayscale = 255 - Math.Pow((255 - grayscale) / 255, invGamma) * 255;
}
texRow[x].A = (byte)grayscale;
}
}
});
}
}
private static Image<Bgra32> CutImage(Image<Bgra32> originalImage, Rectf textureRect, float downscaleMultiplier, bool rotate = false)
{
if (originalImage != null)
{
if (downscaleMultiplier > 0f && downscaleMultiplier != 1f)
{
var newSize = (Size)(new Size(originalImage.Width, originalImage.Height) / downscaleMultiplier);
originalImage.Mutate(x => x.Resize(newSize, KnownResamplers.Lanczos3, compand: true));
}
var rectX = (int)Math.Floor(textureRect.x);
var rectY = (int)Math.Floor(textureRect.y);
var rectRight = (int)Math.Ceiling(textureRect.x + textureRect.width);
var rectBottom = (int)Math.Ceiling(textureRect.y + textureRect.height);
rectRight = Math.Min(rectRight, originalImage.Width);
rectBottom = Math.Min(rectBottom, originalImage.Height);
var rect = new Rectangle(rectX, rectY, rectRight - rectX, rectBottom - rectY);
var spriteImage = originalImage.Clone(x => x.Crop(rect));
originalImage.Dispose();
if (rotate)
{
spriteImage.Mutate(x => x.Rotate(RotateMode.Rotate270));
}
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
return spriteImage;
}
return null;
}
}
}

View File

@@ -0,0 +1,149 @@
using Arknights.AvgCharHubMono;
using AssetStudio;
using AssetStudioCLI;
using SixLabors.ImageSharp;
using System.Linq;
using System;
using System.Collections.Specialized;
using Newtonsoft.Json;
namespace Arknights
{
internal class AvgSprite
{
public Texture2D FaceSpriteAlphaTexture { get; }
public Texture2D FullTexture { get; }
public Texture2D FullAlphaTexture { get; }
public Point FacePos { get; }
public Size FaceSize { get; }
public string Alias { get; }
public bool IsWholeBodySprite { get; }
public bool IsFaceSprite { get; }
public bool IsHubParsed { get; }
private AvgSpriteConfig GetCurSpriteGroup(AvgSpriteConfigGroup spriteHubDataGrouped, long spriteItemID, string spriteName)
{
if (spriteHubDataGrouped.SpriteGroups.Length > 1)
{
if (!string.IsNullOrEmpty(spriteName))
{
var groupFromName = int.TryParse(spriteName?.Substring(spriteName.IndexOf('$') + 1, 1), out int groupIndex);
if (groupFromName)
{
return spriteHubDataGrouped.SpriteGroups[groupIndex - 1];
}
}
return spriteHubDataGrouped.SpriteGroups.FirstOrDefault(x => x.Sprites.Any(y => y.Sprite.m_PathID == spriteItemID));
}
else
{
return spriteHubDataGrouped.SpriteGroups[0];
}
}
private bool TryGetSpriteHub(AssetItem assetItem, out AvgSpriteConfig spriteHubData)
{
spriteHubData = null;
var scriptAssets = Studio.loadedAssetsList.FindAll(x =>
x.Type == ClassIDType.MonoBehaviour
&& x.Container == assetItem.Container);
if (scriptAssets.Count == 0)
{
Logger.Warning("No MonoBehaviours were found.");
return false;
}
OrderedDictionary spriteHubDict = null;
var isGrouped = false;
foreach (var scriptAsset in scriptAssets)
{
var scriptAssetDict = ((MonoBehaviour)scriptAsset.Asset).ToType();
if (scriptAssetDict == null)
{
Logger.Warning("MonoBehaviour is not readable.");
return false;
}
if (scriptAssetDict.Contains("spriteGroups"))
{
spriteHubDict = scriptAssetDict;
isGrouped = true;
break;
}
if (scriptAssetDict.Contains("sprites"))
{
spriteHubDict = scriptAssetDict;
break;
}
}
if (spriteHubDict == null)
{
Logger.Warning("AVGCharacterSpriteHub is not readable.");
return false;
}
var spriteHubJson = JsonConvert.SerializeObject(spriteHubDict);
if (isGrouped)
{
var groupedSpriteHub = JsonConvert.DeserializeObject<AvgSpriteConfigGroup>(spriteHubJson);
spriteHubData = GetCurSpriteGroup(groupedSpriteHub, assetItem.m_PathID, assetItem.Text);
}
else
{
spriteHubData = JsonConvert.DeserializeObject<AvgSpriteConfig>(spriteHubJson);
}
return true;
}
public AvgSprite(AssetItem assetItem)
{
if (TryGetSpriteHub(assetItem, out var spriteHubData))
{
IsHubParsed = spriteHubData?.Sprites.Length > 0;
}
if (IsHubParsed)
{
var curSpriteData = spriteHubData.Sprites.FirstOrDefault(x => x.Sprite.m_PathID == assetItem.m_PathID);
if (curSpriteData == null)
{
Logger.Warning($"Sprite \"{assetItem.Text}\" was not found in the avg sprite hub");
return;
}
Alias = curSpriteData.Alias;
IsWholeBodySprite = curSpriteData.IsWholeBody;
if (spriteHubData.FaceSize.X > 0 && spriteHubData.FaceSize.Y > 0) //If face data exist
{
var fullTexSpriteData = spriteHubData.Sprites.Last(); //Last sprite item in the list usually contains PathID of Sprite with full texture
var curSprite = (Sprite)assetItem.Asset;
IsFaceSprite = curSprite.m_Rect.width <= 256 && curSprite.m_Rect.height <= 256 && curSprite.m_PathID != fullTexSpriteData.Sprite.m_PathID;
var curSpriteAlphaID = curSpriteData.AlphaTex.m_PathID;
var curSpriteAlphaTex = (Texture2D)Studio.loadedAssetsList.Find(x => x.m_PathID == curSpriteAlphaID)?.Asset;
if (curSpriteAlphaTex != null)
{
FaceSpriteAlphaTexture = IsFaceSprite ? curSpriteAlphaTex : null;
fullTexSpriteData = IsFaceSprite ? fullTexSpriteData : curSpriteData;
}
var fullTexSpriteID = fullTexSpriteData.Sprite.m_PathID;
var fullTexAlphaID = fullTexSpriteData.AlphaTex.m_PathID;
var fullTexSprite = (Sprite)Studio.loadedAssetsList.Find(x => x.m_PathID == fullTexSpriteID).Asset;
FullTexture = fullTexSprite.m_RD.texture.TryGet(out var fullTex) ? fullTex : null;
FullAlphaTexture = (Texture2D)Studio.loadedAssetsList.Find(x => x.m_PathID == fullTexAlphaID)?.Asset;
FacePos = new Point((int)Math.Round(spriteHubData.FacePos.X), (int)Math.Round(spriteHubData.FacePos.Y));
FaceSize = new Size((int)Math.Round(spriteHubData.FaceSize.X), (int)Math.Round(spriteHubData.FaceSize.Y));
}
else
{
FullAlphaTexture = (Texture2D)Studio.loadedAssetsList.Find(x => x.m_PathID == curSpriteData.AlphaTex.m_PathID).Asset;
}
}
}
}
}

View File

@@ -0,0 +1,30 @@
using AssetStudio;
namespace Arknights.AvgCharHubMono
{
internal class AvgAssetIDs
{
public int m_FileID { get; set; }
public long m_PathID { get; set; }
}
internal class AvgSpriteData
{
public AvgAssetIDs Sprite { get; set; }
public AvgAssetIDs AlphaTex { get; set; }
public string Alias { get; set; }
public bool IsWholeBody { get; set; }
}
internal class AvgSpriteConfig
{
public AvgSpriteData[] Sprites { get; set; }
public Vector2 FaceSize { get; set; }
public Vector3 FacePos { get; set; }
}
internal class AvgSpriteConfigGroup
{
public AvgSpriteConfig[] SpriteGroups { get; set; }
}
}

View File

@@ -0,0 +1,24 @@
using AssetStudio;
namespace Arknights
{
internal class PortraitSprite
{
public string Name { get; set; }
public ClassIDType Type { get; }
public SerializedFile AssetsFile { get; set; }
public string Container { get; set; }
public Texture2D Texture { get; set; }
public Texture2D AlphaTexture { get; set; }
public Rectf TextureRect { get; set; }
public bool Rotate { get; set; }
public float DownscaleMultiplier { get; }
public PortraitSprite()
{
Type = ClassIDType.AkPortraitSprite;
DownscaleMultiplier = 1f;
}
}
}

View File

@@ -0,0 +1,41 @@
namespace Arknights.PortraitSpriteMono
{
internal class PortraitRect
{
public float X { get; set; }
public float Y { get; set; }
public float W { get; set; }
public float H { get; set; }
}
internal class AtlasSprite
{
public string Name { get; set; }
public string Guid { get; set; }
public int Atlas { get; set; }
public PortraitRect Rect { get; set; }
public bool Rotate { get; set; }
}
internal class TextureIDs
{
public int m_FileID { get; set; }
public long m_PathID { get; set; }
}
internal class AtlasInfo
{
public int Index { get; set; }
public TextureIDs Texture { get; set; }
public TextureIDs Alpha { get; set; }
public int Size { get; set; }
}
internal class PortraitSpriteConfig
{
public string m_Name { get; set; }
public AtlasSprite[] _sprites { get; set; }
public AtlasInfo _atlas { get; set; }
public int _index { get; set; }
}
}

View File

@@ -0,0 +1,42 @@
using Arknights;
using AssetStudio;
namespace AssetStudioCLI
{
internal class AssetItem
{
public Object Asset;
public SerializedFile SourceFile;
public string Container = string.Empty;
public string TypeString;
public long m_PathID;
public long FullSize;
public ClassIDType Type;
public string Text;
public string UniqueID;
public GameObjectNode Node;
public PortraitSprite AkPortraitSprite;
public AssetItem(Object asset)
{
Asset = asset;
SourceFile = asset.assetsFile;
Type = asset.type;
TypeString = Type.ToString();
m_PathID = asset.m_PathID;
FullSize = asset.byteSize;
}
public AssetItem(PortraitSprite akPortraitSprite)
{
Asset = null;
SourceFile = akPortraitSprite.AssetsFile;
Container = akPortraitSprite.Container;
Type = akPortraitSprite.Type;
TypeString = Type.ToString();
Text = akPortraitSprite.Name;
m_PathID = -1;
AkPortraitSprite = akPortraitSprite;
}
}
}

View File

@@ -0,0 +1,16 @@
using AssetStudio;
using System.Collections.Generic;
namespace AssetStudioCLI
{
internal class BaseNode
{
public List<BaseNode> nodes = new List<BaseNode>();
public BaseNode()
{
}
}
}

View File

@@ -0,0 +1,16 @@
using AssetStudio;
using System.Collections.Generic;
namespace AssetStudioCLI
{
internal class GameObjectNode : BaseNode
{
public GameObject gameObject;
public GameObjectNode(GameObject gameObject)
{
this.gameObject = gameObject;
}
}
}

550
AssetStudioCLI/Exporter.cs Normal file
View File

@@ -0,0 +1,550 @@
using Arknights;
using AssetStudio;
using AssetStudioCLI.Options;
using Newtonsoft.Json;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace AssetStudioCLI
{
internal static class Exporter
{
public static bool ExportTexture2D(AssetItem item, string exportPath)
{
var m_Texture2D = (Texture2D)item.Asset;
if (CLIOptions.convertTexture)
{
var type = CLIOptions.o_imageFormat.Value;
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
return false;
if (CLIOptions.o_logLevel.Value <= LoggerEvent.Debug)
{
var sb = new StringBuilder();
sb.AppendLine($"Converting \"{m_Texture2D.m_Name}\" to {type}..");
sb.AppendLine($"Width: {m_Texture2D.m_Width}");
sb.AppendLine($"Height: {m_Texture2D.m_Height}");
sb.AppendLine($"Format: {m_Texture2D.m_TextureFormat}");
switch (m_Texture2D.m_TextureSettings.m_FilterMode)
{
case 0: sb.AppendLine("Filter Mode: Point "); break;
case 1: sb.AppendLine("Filter Mode: Bilinear "); break;
case 2: sb.AppendLine("Filter Mode: Trilinear "); break;
}
sb.AppendLine($"Anisotropic level: {m_Texture2D.m_TextureSettings.m_Aniso}");
sb.AppendLine($"Mip map bias: {m_Texture2D.m_TextureSettings.m_MipBias}");
switch (m_Texture2D.m_TextureSettings.m_WrapMode)
{
case 0: sb.AppendLine($"Wrap mode: Repeat"); break;
case 1: sb.AppendLine($"Wrap mode: Clamp"); break;
}
Logger.Debug(sb.ToString());
}
var image = m_Texture2D.ConvertToImage(flip: true);
if (image == null)
{
Logger.Error($"Export error. Failed to convert texture \"{m_Texture2D.m_Name}\" into image");
return false;
}
using (image)
{
using (var file = File.OpenWrite(exportFullPath))
{
image.WriteToStream(file, type);
}
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
}
else
{
if (!TryExportFile(exportPath, item, ".tex", out var exportFullPath))
return false;
File.WriteAllBytes(exportFullPath, m_Texture2D.image_data.GetData());
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
}
public static bool ExportAudioClip(AssetItem item, string exportPath)
{
string exportFullPath;
var m_AudioClip = (AudioClip)item.Asset;
var m_AudioData = m_AudioClip.m_AudioData.GetData();
if (m_AudioData == null || m_AudioData.Length == 0)
{
Logger.Error($"Export error. \"{item.Text}\": AudioData was not found");
return false;
}
var converter = new AudioClipConverter(m_AudioClip);
if (CLIOptions.o_audioFormat.Value != AudioFormat.None && converter.IsSupport)
{
if (!TryExportFile(exportPath, item, ".wav", out exportFullPath))
return false;
if (CLIOptions.o_logLevel.Value <= LoggerEvent.Debug)
{
var sb = new StringBuilder();
sb.AppendLine($"Converting \"{m_AudioClip.m_Name}\" to wav..");
sb.AppendLine(m_AudioClip.version[0] < 5 ? $"AudioClip type: {m_AudioClip.m_Type}" : $"AudioClip compression format: {m_AudioClip.m_CompressionFormat}");
sb.AppendLine($"AudioClip channel count: {m_AudioClip.m_Channels}");
sb.AppendLine($"AudioClip sample rate: {m_AudioClip.m_Frequency}");
sb.AppendLine($"AudioClip bit depth: {m_AudioClip.m_BitsPerSample}");
Logger.Debug(sb.ToString());
}
var buffer = converter.ConvertToWav(m_AudioData);
if (buffer == null)
{
Logger.Error($"Export error. \"{item.Text}\": Failed to convert to Wav");
return false;
}
File.WriteAllBytes(exportFullPath, buffer);
}
else
{
if (!TryExportFile(exportPath, item, converter.GetExtensionName(), out exportFullPath))
return false;
File.WriteAllBytes(exportFullPath, m_AudioData);
}
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
public static bool ExportVideoClip(AssetItem item, string exportPath)
{
var m_VideoClip = (VideoClip)item.Asset;
if (m_VideoClip.m_ExternalResources.m_Size > 0)
{
if (!TryExportFile(exportPath, item, Path.GetExtension(m_VideoClip.m_OriginalPath), out var exportFullPath))
return false;
if (CLIOptions.o_logLevel.Value <= LoggerEvent.Debug)
{
var sb = new StringBuilder();
sb.AppendLine($"VideoClip format: {m_VideoClip.m_Format}");
sb.AppendLine($"VideoClip width: {m_VideoClip.Width}");
sb.AppendLine($"VideoClip height: {m_VideoClip.Height}");
sb.AppendLine($"VideoClip frame rate: {m_VideoClip.m_FrameRate:.0##}");
sb.AppendLine($"VideoClip split alpha: {m_VideoClip.m_HasSplitAlpha}");
Logger.Debug(sb.ToString());
}
m_VideoClip.m_VideoData.WriteData(exportFullPath);
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
return false;
}
public static bool ExportMovieTexture(AssetItem item, string exportPath)
{
var m_MovieTexture = (MovieTexture)item.Asset;
if (!TryExportFile(exportPath, item, ".ogv", out var exportFullPath))
return false;
File.WriteAllBytes(exportFullPath, m_MovieTexture.m_MovieData);
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
public static bool ExportShader(AssetItem item, string exportPath)
{
if (!TryExportFile(exportPath, item, ".shader", out var exportFullPath))
return false;
var m_Shader = (Shader)item.Asset;
var str = m_Shader.Convert();
File.WriteAllText(exportFullPath, str);
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
public static bool ExportTextAsset(AssetItem item, string exportPath)
{
var m_TextAsset = (TextAsset)item.Asset;
var extension = ".txt";
var assetExtension = Path.GetExtension(m_TextAsset.m_Name);
if (!CLIOptions.f_notRestoreExtensionName.Value)
{
if (!string.IsNullOrEmpty(assetExtension))
{
extension = "";
}
else if (!string.IsNullOrEmpty(item.Container))
{
var ext = Path.GetExtension(item.Container);
if (!string.IsNullOrEmpty(item.Container))
{
extension = ext;
}
}
}
if (!TryExportFile(exportPath, item, extension, out var exportFullPath))
return false;
File.WriteAllBytes(exportFullPath, m_TextAsset.m_Script);
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
public static bool ExportMonoBehaviour(AssetItem item, string exportPath)
{
if (!TryExportFile(exportPath, item, ".json", out var exportFullPath))
return false;
var m_MonoBehaviour = (MonoBehaviour)item.Asset;
var type = m_MonoBehaviour.ToType();
if (type == null)
{
var m_Type = m_MonoBehaviour.ConvertToTypeTree(Studio.assemblyLoader);
type = m_MonoBehaviour.ToType(m_Type);
}
if (type != null)
{
var str = JsonConvert.SerializeObject(type, Formatting.Indented);
File.WriteAllText(exportFullPath, str);
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
return false;
}
public static bool ExportFont(AssetItem item, string exportPath)
{
var m_Font = (Font)item.Asset;
if (m_Font.m_FontData != null)
{
var extension = ".ttf";
if (m_Font.m_FontData[0] == 79 && m_Font.m_FontData[1] == 84 && m_Font.m_FontData[2] == 84 && m_Font.m_FontData[3] == 79)
{
extension = ".otf";
}
if (!TryExportFile(exportPath, item, extension, out var exportFullPath))
return false;
File.WriteAllBytes(exportFullPath, m_Font.m_FontData);
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
return false;
}
public static bool ExportSprite(AssetItem item, string exportPath)
{
Image<Bgra32> image;
AvgSprite avgSprite = null;
var alias = "";
var m_Sprite = (Sprite)item.Asset;
var type = CLIOptions.o_imageFormat.Value;
var spriteMaskMode = CLIOptions.o_akSpriteAlphaMode.Value != AkSpriteAlphaMode.None ? SpriteMaskMode.Export : SpriteMaskMode.Off;
var isCharAvgSprite = item.Container.Contains("avg/characters");
var isCharArt = item.Container.Contains("arts/characters");
if (isCharAvgSprite)
{
avgSprite = new AvgSprite(item);
if (CLIOptions.f_akAddAliases.Value && !string.IsNullOrEmpty(avgSprite.Alias))
{
alias = $"_{avgSprite.Alias}";
}
if (!CLIOptions.f_akOriginalAvgNames.Value)
{
var groupedPattern = new Regex(@"^\d{1,2}\$\d{1,2}$"); // "spriteIndex$groupIndex"
var notGroupedPattern = new Regex(@"^\d{1,2}$"); // "spriteIndex"
if (groupedPattern.IsMatch(m_Sprite.m_Name) || notGroupedPattern.IsMatch(m_Sprite.m_Name))
{
var fullName = Path.GetFileNameWithoutExtension(item.Container);
item.Text = $"{fullName}#{m_Sprite.m_Name}";
}
}
}
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath, alias))
return false;
if (CLIOptions.o_akSpriteAlphaMode.Value == AkSpriteAlphaMode.SearchExternal && (isCharAvgSprite || isCharArt))
{
if (m_Sprite.m_RD.alphaTexture.IsNull)
{
var charAlphaAtlas = AkSpriteHelper.TryFindAlphaTex(item, avgSprite, isCharAvgSprite);
if (charAlphaAtlas != null)
{
m_Sprite.m_RD.alphaTexture.Set(charAlphaAtlas);
m_Sprite.akSplitAlpha = true;
}
}
image = m_Sprite.AkGetImage(avgSprite, spriteMaskMode);
}
else
{
image = m_Sprite.GetImage(spriteMaskMode);
}
if (image != null)
{
using (image)
{
using (var file = File.OpenWrite(exportFullPath))
{
image.WriteToStream(file, type);
}
Logger.Debug($"{item.TypeString}: \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
}
return false;
}
public static bool ExportPortraitSprite(AssetItem item, string exportPath)
{
var type = CLIOptions.o_imageFormat.Value;
var spriteMaskMode = CLIOptions.o_akSpriteAlphaMode.Value != AkSpriteAlphaMode.None ? SpriteMaskMode.Export : SpriteMaskMode.Off;
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
return false;
var image = item.AkPortraitSprite.AkGetImage(spriteMaskMode: spriteMaskMode);
if (image != null)
{
using (image)
{
using (var file = File.OpenWrite(exportFullPath))
{
image.WriteToStream(file, type);
}
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
}
return false;
}
public static bool ExportRawFile(AssetItem item, string exportPath)
{
if (item.Asset == null)
{
Logger.Warning($"Raw export is not supported for \"{item.Text}\" ({item.TypeString}) file");
return false;
}
if (!TryExportFile(exportPath, item, ".dat", out var exportFullPath))
return false;
File.WriteAllBytes(exportFullPath, item.Asset.GetRawData());
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
public static void ExportGameObject(GameObject gameObject, string exportPath, List<AssetItem> animationList = null)
{
var convert = animationList != null
? new ModelConverter(gameObject, CLIOptions.o_imageFormat.Value, animationList.Select(x => (AnimationClip)x.Asset).ToArray())
: new ModelConverter(gameObject, CLIOptions.o_imageFormat.Value);
exportPath = exportPath + FixFileName(gameObject.m_Name) + ".fbx";
ExportFbx(convert, exportPath);
}
private static void ExportFbx(IImported convert, string exportPath)
{
var eulerFilter = true;
var filterPrecision = (float)0.25f;
var exportAllNodes = true;
var exportSkins = true;
var exportAnimations = true;
var exportBlendShape = true;
var castToBone = false;
var boneSize = CLIOptions.o_fbxBoneSize.Value;
var exportAllUvsAsDiffuseMaps = false;
var scaleFactor = CLIOptions.o_fbxScaleFactor.Value;
var fbxVersion = 3;
var fbxFormat = 0;
ModelExporter.ExportFbx(exportPath, convert, eulerFilter, filterPrecision,
exportAllNodes, exportSkins, exportAnimations, exportBlendShape, castToBone, boneSize, exportAllUvsAsDiffuseMaps, scaleFactor, fbxVersion, fbxFormat == 1);
}
public static bool ExportDumpFile(AssetItem item, string exportPath)
{
if (item.Asset == null)
{
Logger.Warning($"Dump is not supported for \"{item.Text}\" ({item.TypeString}) file");
return false;
}
if (!TryExportFile(exportPath, item, ".txt", out var exportFullPath))
return false;
var str = item.Asset.Dump();
if (str == null && item.Asset is MonoBehaviour m_MonoBehaviour)
{
var m_Type = m_MonoBehaviour.ConvertToTypeTree(Studio.assemblyLoader);
str = m_MonoBehaviour.Dump(m_Type);
}
if (str != null)
{
File.WriteAllText(exportFullPath, str);
Logger.Debug($"{item.TypeString} \"{item.Text}\" saved to \"{exportFullPath}\"");
return true;
}
return false;
}
private static bool TryExportFile(string dir, AssetItem item, string extension, out string fullPath, string alias = "")
{
var fileName = FixFileName(item.Text) + alias;
fullPath = Path.Combine(dir, fileName + extension);
if (!File.Exists(fullPath))
{
Directory.CreateDirectory(dir);
return true;
}
fullPath = Path.Combine(dir, fileName + item.UniqueID + extension);
if (!File.Exists(fullPath))
{
Directory.CreateDirectory(dir);
return true;
}
Logger.Error($"Export error. File \"{fullPath.Color(ColorConsole.BrightRed)}\" already exist");
return false;
}
private static bool ExportMesh(AssetItem item, string exportPath)
{
var m_Mesh = (Mesh)item.Asset;
if (m_Mesh.m_VertexCount <= 0)
return false;
if (!TryExportFile(exportPath, item, ".obj", out var exportFullPath))
return false;
var sb = new StringBuilder();
sb.AppendLine("g " + m_Mesh.m_Name);
#region Vertices
if (m_Mesh.m_Vertices == null || m_Mesh.m_Vertices.Length == 0)
{
return false;
}
int c = 3;
if (m_Mesh.m_Vertices.Length == m_Mesh.m_VertexCount * 4)
{
c = 4;
}
for (int v = 0; v < m_Mesh.m_VertexCount; v++)
{
sb.Append($"v {-m_Mesh.m_Vertices[v * c]} {m_Mesh.m_Vertices[v * c + 1]} {m_Mesh.m_Vertices[v * c + 2]}\r\n");
}
#endregion
#region UV
if (m_Mesh.m_UV0?.Length > 0)
{
c = 4;
if (m_Mesh.m_UV0.Length == m_Mesh.m_VertexCount * 2)
{
c = 2;
}
else if (m_Mesh.m_UV0.Length == m_Mesh.m_VertexCount * 3)
{
c = 3;
}
for (int v = 0; v < m_Mesh.m_VertexCount; v++)
{
sb.AppendFormat("vt {0} {1}\r\n", m_Mesh.m_UV0[v * c], m_Mesh.m_UV0[v * c + 1]);
}
}
#endregion
#region Normals
if (m_Mesh.m_Normals?.Length > 0)
{
if (m_Mesh.m_Normals.Length == m_Mesh.m_VertexCount * 3)
{
c = 3;
}
else if (m_Mesh.m_Normals.Length == m_Mesh.m_VertexCount * 4)
{
c = 4;
}
for (int v = 0; v < m_Mesh.m_VertexCount; v++)
{
sb.AppendFormat("vn {0} {1} {2}\r\n", -m_Mesh.m_Normals[v * c], m_Mesh.m_Normals[v * c + 1], m_Mesh.m_Normals[v * c + 2]);
}
}
#endregion
#region Face
int sum = 0;
for (var i = 0; i < m_Mesh.m_SubMeshes.Length; i++)
{
sb.AppendLine($"g {m_Mesh.m_Name}_{i}");
int indexCount = (int)m_Mesh.m_SubMeshes[i].indexCount;
var end = sum + indexCount / 3;
for (int f = sum; f < end; f++)
{
sb.AppendFormat("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\r\n", m_Mesh.m_Indices[f * 3 + 2] + 1, m_Mesh.m_Indices[f * 3 + 1] + 1, m_Mesh.m_Indices[f * 3] + 1);
}
sum = end;
}
#endregion
sb.Replace("NaN", "0");
File.WriteAllText(exportFullPath, sb.ToString());
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
public static bool ExportConvertFile(AssetItem item, string exportPath)
{
switch (item.Type)
{
case ClassIDType.Texture2D:
return ExportTexture2D(item, exportPath);
case ClassIDType.AudioClip:
return ExportAudioClip(item, exportPath);
case ClassIDType.VideoClip:
return ExportVideoClip(item, exportPath);
case ClassIDType.MovieTexture:
return ExportMovieTexture(item, exportPath);
case ClassIDType.Shader:
return ExportShader(item, exportPath);
case ClassIDType.TextAsset:
return ExportTextAsset(item, exportPath);
case ClassIDType.MonoBehaviour:
return ExportMonoBehaviour(item, exportPath);
case ClassIDType.Font:
return ExportFont(item, exportPath);
case ClassIDType.Sprite:
return ExportSprite(item, exportPath);
case ClassIDType.AkPortraitSprite:
return ExportPortraitSprite(item, exportPath);
case ClassIDType.Mesh:
return ExportMesh(item, exportPath);
default:
return ExportRawFile(item, exportPath);
}
}
public static string FixFileName(string str)
{
if (str.Length >= 260) return Path.GetRandomFileName();
return Path.GetInvalidFileNameChars().Aggregate(str, (current, c) => current.Replace(c, '_'));
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
namespace AssetStudioCLI.Options
{
internal class Option<T>
{
public string Name { get; }
public string Description { get; }
public T Value { get; set; }
public T DefaultValue { get; }
public HelpGroups HelpGroup { get; }
public bool IsFlag { get; }
public Option(T optionDefaultValue, string optionName, string optionDescription, HelpGroups optionHelpGroup, bool isFlag)
{
Name = optionName;
Description = optionDescription;
DefaultValue = optionDefaultValue;
Value = DefaultValue;
HelpGroup = optionHelpGroup;
IsFlag = isFlag;
}
public override string ToString()
{
return Value != null ? Value.ToString() : string.Empty;
}
}
}

View File

@@ -0,0 +1,17 @@
using System;
namespace AssetStudioCLI.Options
{
internal static class OptionExtensions
{
public static Action<string, string, HelpGroups, bool> OptionGrouping = (name, desc, group, isFlag) => { };
}
internal class GroupedOption<T> : Option<T>
{
public GroupedOption(T optionDefaultValue, string optionName, string optionDescription, HelpGroups optionHelpGroup, bool isFlag = false) : base(optionDefaultValue, optionName, optionDescription, optionHelpGroup, isFlag)
{
OptionExtensions.OptionGrouping(optionName, optionDescription, optionHelpGroup, isFlag);
}
}
}

73
AssetStudioCLI/Program.cs Normal file
View File

@@ -0,0 +1,73 @@
using AssetStudio;
using AssetStudioCLI.Options;
using System;
namespace AssetStudioCLI
{
class Program
{
public static void Main(string[] args)
{
CLIOptions.ParseArgs(args);
if (CLIOptions.isParsed)
{
CLIRun();
}
else if (CLIOptions.showHelp)
{
CLIOptions.ShowHelp();
}
else
{
Console.WriteLine();
CLIOptions.ShowHelp(showUsageOnly: true);
}
}
private static void CLIRun()
{
var cliLogger = new CLILogger();
Logger.Default = cliLogger;
CLIOptions.ShowCurrentOptions();
try
{
if (Studio.LoadAssets())
{
Studio.ParseAssets();
if (CLIOptions.filterBy != FilterBy.None)
{
Studio.Filter();
}
if (CLIOptions.o_exportAssetList.Value != ExportListType.None)
{
Studio.ExportAssetList();
}
switch (CLIOptions.o_workMode.Value)
{
case WorkMode.Info:
Studio.ShowExportableAssetsInfo();
break;
case WorkMode.ExportLive2D:
Studio.ExportLive2D();
break;
case WorkMode.SplitObjects:
Studio.ExportSplitObjects();
break;
default:
Studio.ExportAssets();
break;
}
}
}
catch (Exception ex)
{
Logger.Error(ex.ToString());
}
finally
{
cliLogger.LogToFile(LoggerEvent.Verbose, "---Program ended---");
}
}
}
}

159
AssetStudioCLI/ReadMe.md Normal file
View File

@@ -0,0 +1,159 @@
## ArknightsStudioCLI
CLI version of ArknightsStudio.
- Supported asset types for export: `Texture2D`, `Sprite`, `AkPortraitSprite`, `TextAsset`, `MonoBehaviour`, `Font`, `Shader`, `MovieTexture`, `AudioClip`, `VideoClip`, `Mesh`.
- *There are no plans to add support for `AnimationClip`, `Animator` for now.*
### Usage
```
ArknightsStudioCLI <input path to asset file/folder> [-m, --mode <value>]
[-t, --asset-type <value(s)>] [-g, --group-option <value>]
[-o, --output <path>] [-h, --help]
[--log-level <value>] [--log-output <value>]
[--image-format <value>] [--audio-format <value>]
[--l2d-motion-mode <value>] [--l2d-force-bezier]
[--fbx-scale-factor <value>] [--fbx-bone-size <value>]
[--filter-by-name <text>] [--filter-by-container <text>]
[--filter-by-pathid <text>] [--filter-by-text <text>]
[--spritealpha-mode <value>] [--alphatex-resampler <value>]
[--shadow-gamma <value>] [--original-avg-names]
[--add-aliases] [--export-asset-list <value>]
[--assembly-folder <path>] [--unity-version <text>]
[--not-restore-extension] [--load-all]
General Options:
-m, --mode <value> Specify working mode
<Value: export(default) | exportRaw | dump | info | live2d | splitObjects>
Export - Exports converted assets
ExportRaw - Exports raw data
Dump - Makes asset dumps
Info - Loads file(s), shows the number of available for export assets and exits
Live2D - Exports Live2D Cubism 3 models
SplitObjects - Exports split objects (fbx)
Example: "-m info"
-t, --asset-type <value(s)> Specify asset type(s) to export
<Value(s): tex2d, sprite, akPortrait, textAsset, monoBehaviour, font, shader,
movieTexture, audio, video, mesh | all(default)>
All - export all asset types, which are listed in the values
*To specify multiple asset types, write them separated by ',' or ';' without spaces
Examples: "-t sprite" or "-t tex2d,sprite,audio" or "-t tex2d;sprite;font"
-g, --group-option <value> Specify the way in which exported assets should be grouped
<Value: none | type | container(default) | containerFull | filename>
None - Do not group exported assets
Type - Group exported assets by type name
Container - Group exported assets by container path
ContainerFull - Group exported assets by full container path (e.g. with prefab name)
Filename - Group exported assets by source file name
Example: "-g container"
-o, --output <path> Specify path to the output folder
If path isn't specified, 'ASExport' folder will be created in the program's work folder
-h, --help Display help and exit
Logger Options:
--log-level <value> Specify the log level
<Value: verbose | debug | info(default) | warning | error>
Example: "--log-level warning"
--log-output <value> Specify the log output
<Value: console(default) | file | both>
Example: "--log-output both"
Convert Options:
--image-format <value> Specify the format for converting image assets
<Value: none | jpg | png(default) | bmp | tga | webp>
None - Do not convert images and export them as texture data (.tex)
Example: "--image-format jpg"
--audio-format <value> Specify the format for converting audio assets
<Value: none | wav(default)>
None - Do not convert audios and export them in their own format
Example: "--audio-format wav"
Live2D Options:
--l2d-motion-mode <value> Specify Live2D motion export mode
<Value: monoBehaviour(default) | animationClip>
MonoBehaviour - Try to export motions from MonoBehaviour Fade motions
If no Fade motions are found, the AnimationClip method will be used
AnimationClip - Try to export motions using AnimationClip assets
Example: "--l2d-motion-mode animationClip"
--l2d-force-bezier (Flag) If specified, Linear motion segments will be calculated as Bezier segments
(May help if the exported motions look jerky/not smooth enough)
FBX Options:
--fbx-scale-factor <value> Specify the FBX Scale Factor
<Value: float number from 0 to 100 (default=1)
Example: "--fbx-scale-factor 50"
--fbx-bone-size <value> Specify the FBX Bone Size
<Value: integer number from 0 to 100 (default=10)
Example: "--fbx-bone-size 10"
Filter Options:
--filter-by-name <text> Specify the name by which assets should be filtered
*To specify multiple names write them separated by ',' or ';' without spaces
Example: "--filter-by-name char" or "--filter-by-name char,bg"
--filter-by-container <text> Specify the container by which assets should be filtered
*To specify multiple containers write them separated by ',' or ';' without spaces
Example: "--filter-by-container arts" or "--filter-by-container arts,icons"
--filter-by-pathid <text> Specify the PathID by which assets should be filtered
*To specify multiple PathIDs write them separated by ',' or ';' without spaces
Example: "--filter-by-pathid 7238605633795851352,-2430306240205277265"
--filter-by-text <text> Specify the text by which assets should be filtered
Looks for assets that contain the specified text in their names or containers
*To specify multiple values write them separated by ',' or ';' without spaces
Example: "--filter-by-text portrait" or "--filter-by-text portrait,art"
Arknights Options:
--spritealpha-mode <value> Specify the mode in which you want to export sprites with alpha texture
<Value: none | internalOnly | searchExternal(default)>
None - Export sprites without alpha texture applied
InternalOnly - Export sprites with internal alpha texture applied (if exist)
SearchExternal - Export sprites with internal alpha texture applied,
and in case it doesn't exist, Studio will try to find an external alpha texture
Example: "--spritealpha-mode internalOnly"
--alphatex-resampler <value> Specify the alpha texture upscale algorithm for 2048x2048 sprites
<Value: nearest | bilinear | bicubic | mitchell(default) | spline | welch>
Mitchell - Mitchell Netravali algorithm. Yields good equilibrium between
sharpness and smoothness (produces less artifacts than bicubic in the current use case)
Spline - Similar to Mitchell Netravali but yielding smoother results
Welch - A high speed algorithm that delivers very sharpened results
Example: "--alphatex-resampler bicubic"
--shadow-gamma <value> Specify the gamma correction of semi-transparent shadow for 2048x2048 sprites
<Value: integer number from -5 to 5 (default=2)>
<0 - Make the shadow darker
0 - Do not change the brightness of the shadow
>0 - Make the shadow lighter
Example: "--shadow-gamma 0"
--original-avg-names (Flag) If specified, names of avg character sprites will not be restored
--add-aliases (Flag) If specified, aliases will be added to avg character sprite names (if exist)
Advanced Options:
--export-asset-list <value> Specify the format in which you want to export asset list
<Value: none(default) | xml>
None - Do not export asset list
Example: "--export-asset-list xml"
--assembly-folder <path> Specify the path to the assembly folder
--unity-version <text> Specify Unity version
Example: "--unity-version 2017.4.39f1"
--not-restore-extension (Flag) If specified, Studio will not try to use/restore original TextAsset
extension name, and will just export all TextAssets with the ".txt" extension
--load-all (Flag) If specified, Studio will load assets of all types
(Only for Dump, Info and ExportRaw modes)
```

721
AssetStudioCLI/Studio.cs Normal file
View File

@@ -0,0 +1,721 @@
using AssetStudio;
using AssetStudioCLI.Options;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using static AssetStudioCLI.Exporter;
using static CubismLive2DExtractor.Live2DExtractor;
using Ansi = AssetStudio.ColorConsole;
namespace AssetStudioCLI
{
internal static class Studio
{
public static AssetsManager assetsManager = new AssetsManager();
public static List<AssetItem> exportableAssetsList = new List<AssetItem>();
public static List<AssetItem> loadedAssetsList = new List<AssetItem>();
public static List<BaseNode> gameObjectTree = new List<BaseNode>();
public static AssemblyLoader assemblyLoader = new AssemblyLoader();
private static Dictionary<AssetStudio.Object, string> containers = new Dictionary<AssetStudio.Object, string>();
static Studio()
{
Progress.Default = new Progress<int>(ShowCurProgressValue);
}
private static void ShowCurProgressValue(int value)
{
Console.Write($"[{value:000}%]\r");
}
public static bool LoadAssets()
{
var isLoaded = false;
assetsManager.SpecifyUnityVersion = CLIOptions.o_unityVersion.Value;
if (!CLIOptions.f_loadAllAssets.Value)
{
assetsManager.SetAssetFilter(CLIOptions.o_exportAssetTypes.Value);
}
assetsManager.LoadFilesAndFolders(CLIOptions.inputPath);
if (assetsManager.assetsFileList.Count == 0)
{
Logger.Warning("No Unity file can be loaded.");
}
else
{
isLoaded = true;
}
return isLoaded;
}
public static void ParseAssets()
{
Logger.Info("Parse assets...");
var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count);
var objectAssetItemDic = new Dictionary<AssetStudio.Object, AssetItem>(objectCount);
Progress.Reset();
var i = 0;
foreach (var assetsFile in assetsManager.assetsFileList)
{
var preloadTable = Array.Empty<PPtr<AssetStudio.Object>>();
foreach (var asset in assetsFile.Objects)
{
var assetItem = new AssetItem(asset);
objectAssetItemDic.Add(asset, assetItem);
assetItem.UniqueID = "_#" + i;
var isExportable = false;
switch (asset)
{
case PreloadData m_PreloadData:
preloadTable = m_PreloadData.m_Assets;
break;
case AssetBundle m_AssetBundle:
var isStreamedSceneAssetBundle = m_AssetBundle.m_IsStreamedSceneAssetBundle;
if (!isStreamedSceneAssetBundle)
{
preloadTable = m_AssetBundle.m_PreloadTable;
}
assetItem.Text = string.IsNullOrEmpty(m_AssetBundle.m_AssetBundleName) ? m_AssetBundle.m_Name : m_AssetBundle.m_AssetBundleName;
foreach (var m_Container in m_AssetBundle.m_Container)
{
var preloadIndex = m_Container.Value.preloadIndex;
var preloadSize = isStreamedSceneAssetBundle ? preloadTable.Length : m_Container.Value.preloadSize;
var preloadEnd = preloadIndex + preloadSize;
for (var k = preloadIndex; k < preloadEnd; k++)
{
var pptr = preloadTable[k];
if (pptr.TryGet(out var obj))
{
containers[obj] = m_Container.Key;
}
}
}
break;
case ResourceManager m_ResourceManager:
foreach (var m_Container in m_ResourceManager.m_Container)
{
if (m_Container.Value.TryGet(out var obj))
{
containers[obj] = m_Container.Key;
}
}
break;
case Texture2D m_Texture2D:
if (!string.IsNullOrEmpty(m_Texture2D.m_StreamData?.path))
assetItem.FullSize = asset.byteSize + m_Texture2D.m_StreamData.size;
assetItem.Text = m_Texture2D.m_Name;
break;
case AudioClip m_AudioClip:
if (!string.IsNullOrEmpty(m_AudioClip.m_Source))
assetItem.FullSize = asset.byteSize + m_AudioClip.m_Size;
assetItem.Text = m_AudioClip.m_Name;
break;
case VideoClip m_VideoClip:
if (!string.IsNullOrEmpty(m_VideoClip.m_OriginalPath))
assetItem.FullSize = asset.byteSize + m_VideoClip.m_ExternalResources.m_Size;
assetItem.Text = m_VideoClip.m_Name;
break;
case Shader m_Shader:
assetItem.Text = m_Shader.m_ParsedForm?.m_Name ?? m_Shader.m_Name;
break;
case MonoBehaviour m_MonoBehaviour:
if (m_MonoBehaviour.m_Name == "" && m_MonoBehaviour.m_Script.TryGet(out var m_Script))
{
assetItem.Text = m_Script.m_ClassName;
}
else
{
assetItem.Text = m_MonoBehaviour.m_Name;
}
break;
case GameObject m_GameObject:
assetItem.Text = m_GameObject.m_Name;
break;
case Animator m_Animator:
if (m_Animator.m_GameObject.TryGet(out var gameObject))
{
assetItem.Text = gameObject.m_Name;
}
break;
case NamedObject m_NamedObject:
assetItem.Text = m_NamedObject.m_Name;
break;
}
if (string.IsNullOrEmpty(assetItem.Text))
{
assetItem.Text = assetItem.TypeString + assetItem.UniqueID;
}
loadedAssetsList.Add(assetItem);
isExportable = CLIOptions.o_exportAssetTypes.Value.Contains(asset.type);
if (isExportable || (CLIOptions.f_loadAllAssets.Value && CLIOptions.o_exportAssetTypes.Value == CLIOptions.o_exportAssetTypes.DefaultValue))
{
exportableAssetsList.Add(assetItem);
}
Progress.Report(++i, objectCount);
}
foreach (var asset in loadedAssetsList)
{
if (containers.TryGetValue(asset.Asset, out var container))
{
asset.Container = container;
if (asset.Type == ClassIDType.MonoBehaviour && container.Contains("/arts/charportraits/portraits"))
{
var portraitsList = Arknights.AkSpriteHelper.GeneratePortraits(asset);
foreach (var portrait in portraitsList)
{
exportableAssetsList.Add(new AssetItem(portrait));
}
}
}
}
if (CLIOptions.o_workMode.Value != WorkMode.ExportLive2D)
{
containers.Clear();
}
}
if (CLIOptions.o_workMode.Value == WorkMode.SplitObjects)
{
BuildTreeStructure(objectAssetItemDic);
}
var log = $"Finished loading {assetsManager.assetsFileList.Count} files with {exportableAssetsList.Count} exportable assets";
var unityVer = assetsManager.assetsFileList[0].version;
long m_ObjectsCount;
if (unityVer[0] > 2020)
{
m_ObjectsCount = assetsManager.assetsFileList.Sum(x => x.m_Objects.LongCount(y =>
y.classID != (int)ClassIDType.Shader
&& CLIOptions.o_exportAssetTypes.Value.Any(k => (int)k == y.classID))
);
}
else
{
m_ObjectsCount = assetsManager.assetsFileList.Sum(x => x.m_Objects.LongCount(y => CLIOptions.o_exportAssetTypes.Value.Any(k => (int)k == y.classID)));
}
var objectsCount = assetsManager.assetsFileList.Sum(x => x.Objects.LongCount(y => CLIOptions.o_exportAssetTypes.Value.Any(k => k == y.type)));
if (m_ObjectsCount != objectsCount)
{
log += $" and {m_ObjectsCount - objectsCount} assets failed to read";
}
Logger.Info(log);
}
public static void BuildTreeStructure(Dictionary<AssetStudio.Object, AssetItem> objectAssetItemDic)
{
Logger.Info("Building tree structure...");
var treeNodeDictionary = new Dictionary<GameObject, GameObjectNode>();
var assetsFileCount = assetsManager.assetsFileList.Count;
int j = 0;
Progress.Reset();
foreach (var assetsFile in assetsManager.assetsFileList)
{
var fileNode = new BaseNode(); //RootNode
foreach (var obj in assetsFile.Objects)
{
if (obj is GameObject m_GameObject)
{
if (!treeNodeDictionary.TryGetValue(m_GameObject, out var currentNode))
{
currentNode = new GameObjectNode(m_GameObject);
treeNodeDictionary.Add(m_GameObject, currentNode);
}
foreach (var pptr in m_GameObject.m_Components)
{
if (pptr.TryGet(out var m_Component))
{
objectAssetItemDic[m_Component].Node = currentNode;
if (m_Component is MeshFilter m_MeshFilter)
{
if (m_MeshFilter.m_Mesh.TryGet(out var m_Mesh))
{
objectAssetItemDic[m_Mesh].Node = currentNode;
}
}
else if (m_Component is SkinnedMeshRenderer m_SkinnedMeshRenderer)
{
if (m_SkinnedMeshRenderer.m_Mesh.TryGet(out var m_Mesh))
{
objectAssetItemDic[m_Mesh].Node = currentNode;
}
}
}
}
var parentNode = fileNode;
if (m_GameObject.m_Transform != null)
{
if (m_GameObject.m_Transform.m_Father.TryGet(out var m_Father))
{
if (m_Father.m_GameObject.TryGet(out var parentGameObject))
{
if (!treeNodeDictionary.TryGetValue(parentGameObject, out var parentGameObjectNode))
{
parentGameObjectNode = new GameObjectNode(parentGameObject);
treeNodeDictionary.Add(parentGameObject, parentGameObjectNode);
}
parentNode = parentGameObjectNode;
}
}
}
parentNode.nodes.Add(currentNode);
}
}
if (fileNode.nodes.Count > 0)
{
gameObjectTree.Add(fileNode);
}
Progress.Report(++j, assetsFileCount);
}
treeNodeDictionary.Clear();
objectAssetItemDic.Clear();
}
public static void ShowExportableAssetsInfo()
{
var exportableAssetsCountDict = new Dictionary<ClassIDType, int>();
string info = "";
if (exportableAssetsList.Count > 0)
{
foreach (var asset in exportableAssetsList)
{
if (exportableAssetsCountDict.ContainsKey(asset.Type))
{
exportableAssetsCountDict[asset.Type] += 1;
}
else
{
exportableAssetsCountDict.Add(asset.Type, 1);
}
}
info += "\n[Exportable Assets Count]\n";
foreach (var assetType in exportableAssetsCountDict.Keys)
{
info += $"# {assetType}: {exportableAssetsCountDict[assetType]}\n";
}
if (exportableAssetsCountDict.Count > 1)
{
info += $"#\n# Total: {exportableAssetsList.Count} assets";
}
}
else
{
info += "No exportable assets found.";
}
if (CLIOptions.o_logLevel.Value > LoggerEvent.Info)
{
Console.WriteLine(info);
}
else
{
Logger.Info(info);
}
}
public static void Filter()
{
switch (CLIOptions.o_workMode.Value)
{
case WorkMode.ExportLive2D:
case WorkMode.SplitObjects:
break;
default:
FilterAssets();
break;
}
}
private static void FilterAssets()
{
var assetsCount = exportableAssetsList.Count;
var filteredAssets = new List<AssetItem>();
switch(CLIOptions.filterBy)
{
case FilterBy.Name:
filteredAssets = exportableAssetsList.FindAll(x => CLIOptions.o_filterByName.Value.Any(y => x.Text.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
Logger.Info(
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names."
);
break;
case FilterBy.Container:
filteredAssets = exportableAssetsList.FindAll(x => CLIOptions.o_filterByContainer.Value.Any(y => x.Container.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
Logger.Info(
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByContainer.Value)}\"".Color(Ansi.BrightYellow)} in their Containers."
);
break;
case FilterBy.PathID:
filteredAssets = exportableAssetsList.FindAll(x => CLIOptions.o_filterByPathID.Value.Any(y => x.m_PathID.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0));
Logger.Info(
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByPathID.Value)}\"".Color(Ansi.BrightYellow)} in their PathIDs."
);
break;
case FilterBy.NameOrContainer:
filteredAssets = exportableAssetsList.FindAll(x =>
CLIOptions.o_filterByText.Value.Any(y => x.Text.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) ||
CLIOptions.o_filterByText.Value.Any(y => x.Container.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)
);
Logger.Info(
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByText.Value)}\"".Color(Ansi.BrightYellow)} in their Names or Contaniers."
);
break;
case FilterBy.NameAndContainer:
filteredAssets = exportableAssetsList.FindAll(x =>
CLIOptions.o_filterByName.Value.Any(y => x.Text.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) &&
CLIOptions.o_filterByContainer.Value.Any(y => x.Container.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)
);
Logger.Info(
$"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " +
$"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByContainer.Value)}\"".Color(Ansi.BrightYellow)} in their Containers " +
$"and {$"\"{string.Join("\", \"", CLIOptions.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names."
);
break;
}
exportableAssetsList.Clear();
exportableAssetsList = filteredAssets;
}
public static void ExportAssets()
{
var savePath = CLIOptions.o_outputFolder.Value;
var toExportCount = exportableAssetsList.Count;
var exportedCount = 0;
var groupOption = CLIOptions.o_groupAssetsBy.Value;
foreach (var asset in exportableAssetsList)
{
string exportPath;
switch (groupOption)
{
case AssetGroupOption.TypeName:
exportPath = Path.Combine(savePath, asset.TypeString);
break;
case AssetGroupOption.ContainerPath:
case AssetGroupOption.ContainerPathFull:
if (!string.IsNullOrEmpty(asset.Container))
{
exportPath = Path.Combine(savePath, Path.GetDirectoryName(asset.Container));
if (groupOption == AssetGroupOption.ContainerPathFull)
{
exportPath = Path.Combine(exportPath, Path.GetFileNameWithoutExtension(asset.Container));
}
}
else
{
exportPath = savePath;
}
break;
case AssetGroupOption.SourceFileName:
if (string.IsNullOrEmpty(asset.SourceFile.originalPath))
{
exportPath = Path.Combine(savePath, asset.SourceFile.fileName + "_export");
}
else
{
exportPath = Path.Combine(savePath, Path.GetFileName(asset.SourceFile.originalPath) + "_export", asset.SourceFile.fileName);
}
break;
default:
exportPath = savePath;
break;
}
exportPath += Path.DirectorySeparatorChar;
try
{
switch (CLIOptions.o_workMode.Value)
{
case WorkMode.ExportRaw:
Logger.Debug($"{CLIOptions.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
if (ExportRawFile(asset, exportPath))
{
exportedCount++;
}
break;
case WorkMode.Dump:
Logger.Debug($"{CLIOptions.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
if (ExportDumpFile(asset, exportPath))
{
exportedCount++;
}
break;
case WorkMode.Export:
Logger.Debug($"{CLIOptions.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
if (ExportConvertFile(asset, exportPath))
{
exportedCount++;
}
break;
}
}
catch (Exception ex)
{
Logger.Error($"{asset.SourceFile.originalPath}: [{$"{asset.Type}: {asset.Text}".Color(Ansi.BrightRed)}] : Export error\n{ex}");
}
Console.Write($"Exported [{exportedCount}/{toExportCount}]\r");
}
Console.WriteLine("");
if (exportedCount == 0)
{
Logger.Default.Log(LoggerEvent.Info, "Nothing exported.", ignoreLevel: true);
}
else if (toExportCount > exportedCount)
{
Logger.Default.Log(LoggerEvent.Info, $"Finished exporting {exportedCount} asset(s) to \"{CLIOptions.o_outputFolder.Value.Color(Ansi.BrightYellow)}\".", ignoreLevel: true);
}
else
{
Logger.Default.Log(LoggerEvent.Info, $"Finished exporting {exportedCount} asset(s) to \"{CLIOptions.o_outputFolder.Value.Color(Ansi.BrightGreen)}\".", ignoreLevel: true);
}
if (toExportCount > exportedCount)
{
Logger.Default.Log(LoggerEvent.Info, $"{toExportCount - exportedCount} asset(s) skipped (not extractable or file(s) already exist).", ignoreLevel: true);
}
}
public static void ExportAssetList()
{
var savePath = CLIOptions.o_outputFolder.Value;
switch (CLIOptions.o_exportAssetList.Value)
{
case ExportListType.XML:
var filename = Path.Combine(savePath, "assets.xml");
var doc = new XDocument(
new XElement("Assets",
new XAttribute("filename", filename),
new XAttribute("createdAt", DateTime.UtcNow.ToString("s")),
exportableAssetsList.Select(
asset => new XElement("Asset",
new XElement("Name", asset.Text),
new XElement("Container", asset.Container),
new XElement("Type", new XAttribute("id", (int)asset.Type), asset.TypeString),
new XElement("PathID", asset.m_PathID),
new XElement("Source", asset.SourceFile.fullName),
new XElement("Size", asset.FullSize)
)
)
)
);
doc.Save(filename);
break;
}
Logger.Info($"Finished exporting asset list with {exportableAssetsList.Count} items.");
}
public static void ExportSplitObjects()
{
var savePath = CLIOptions.o_outputFolder.Value;
var searchList = CLIOptions.o_filterByName.Value;
var isFiltered = CLIOptions.filterBy == FilterBy.Name;
var exportableObjects = new List<GameObjectNode>();
var exportedCount = 0;
var k = 0;
Logger.Info($"Searching for objects to export..");
Progress.Reset();
var count = gameObjectTree.Sum(x => x.nodes.Count);
foreach (var node in gameObjectTree)
{
foreach (GameObjectNode j in node.nodes)
{
if (isFiltered)
{
if (!searchList.Any(searchText => j.gameObject.m_Name.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0))
continue;
}
var gameObjects = new List<GameObject>();
CollectNode(j, gameObjects);
if (gameObjects.All(x => x.m_SkinnedMeshRenderer == null && x.m_MeshFilter == null))
{
Progress.Report(++k, count);
continue;
}
exportableObjects.Add(j);
}
}
gameObjectTree.Clear();
var exportableCount = exportableObjects.Count;
var log = $"Found {exportableCount} exportable object(s) ";
if (isFiltered)
{
log += $"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names";
}
Logger.Info(log);
if (exportableCount > 0)
{
Progress.Reset();
k = 0;
foreach (var gameObjectNode in exportableObjects)
{
var gameObject = gameObjectNode.gameObject;
var filename = FixFileName(gameObject.m_Name);
var targetPath = $"{savePath}{filename}{Path.DirectorySeparatorChar}";
//重名文件处理
for (int i = 1; ; i++)
{
if (Directory.Exists(targetPath))
{
targetPath = $"{savePath}{filename} ({i}){Path.DirectorySeparatorChar}";
}
else
{
break;
}
}
Directory.CreateDirectory(targetPath);
//导出FBX
Logger.Info($"Exporting {filename}.fbx");
Progress.Report(k, exportableCount);
try
{
ExportGameObject(gameObject, targetPath);
Logger.Debug($"{gameObject.type} \"{filename}\" saved to \"{targetPath}\"");
exportedCount++;
}
catch (Exception ex)
{
Logger.Error($"Export GameObject:{gameObject.m_Name} error", ex);
}
k++;
}
}
var status = exportedCount > 0
? $"Finished exporting [{exportedCount}/{exportableCount}] object(s) to \"{CLIOptions.o_outputFolder.Value.Color(Ansi.BrightCyan)}\""
: "Nothing exported";
Logger.Default.Log(LoggerEvent.Info, status, ignoreLevel: true);
}
private static void CollectNode(GameObjectNode node, List<GameObject> gameObjects)
{
gameObjects.Add(node.gameObject);
foreach (GameObjectNode i in node.nodes)
{
CollectNode(i, gameObjects);
}
}
public static void ExportLive2D()
{
var baseDestPath = Path.Combine(CLIOptions.o_outputFolder.Value, "Live2DOutput");
var useFullContainerPath = false;
var motionMode = CLIOptions.o_l2dMotionMode.Value;
var forceBezier = CLIOptions.f_l2dForceBezier.Value;
Progress.Reset();
Logger.Info($"Searching for Live2D files...");
var cubismMocs = exportableAssetsList.Where(x =>
{
if (x.Type == ClassIDType.MonoBehaviour)
{
((MonoBehaviour)x.Asset).m_Script.TryGet(out var m_Script);
return m_Script?.m_ClassName == "CubismMoc";
}
return false;
}).Select(x => x.Asset).ToArray();
if (cubismMocs.Length == 0)
{
Logger.Default.Log(LoggerEvent.Info, "Live2D Cubism models were not found.", ignoreLevel: true);
return;
}
if (cubismMocs.Length > 1)
{
var basePathSet = cubismMocs.Select(x =>
{
var pathLen = containers.TryGetValue(x, out var itemContainer) ? itemContainer.LastIndexOf("/") : 0;
pathLen = pathLen < 0 ? containers[x].Length : pathLen;
return itemContainer?.Substring(0, pathLen);
}).ToHashSet();
if (basePathSet.All(x => x == null))
{
Logger.Error($"Live2D Cubism export error: Cannot find any model related files.");
return;
}
if (basePathSet.Count != cubismMocs.Length)
{
useFullContainerPath = true;
Logger.Debug($"useFullContainerPath: {useFullContainerPath}");
}
}
var basePathList = cubismMocs.Select(x =>
{
containers.TryGetValue(x, out var container);
container = useFullContainerPath
? container
: container?.Substring(0, container.LastIndexOf("/"));
return container;
}).Where(x => x != null).ToList();
var lookup = containers.ToLookup(
x => basePathList.Find(b => x.Value.Contains(b) && x.Value.Split('/').Any(y => y == b.Substring(b.LastIndexOf("/") + 1))),
x => x.Key
);
var totalModelCount = lookup.LongCount(x => x.Key != null);
Logger.Info($"Found {totalModelCount} model(s).");
var modelCounter = 0;
foreach (var assets in lookup)
{
var srcContainer = assets.Key;
if (srcContainer == null)
continue;
var container = srcContainer;
Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{srcContainer.Color(Ansi.BrightCyan)}\"");
try
{
var modelName = useFullContainerPath ? Path.GetFileNameWithoutExtension(container) : container.Substring(container.LastIndexOf('/') + 1);
container = Path.HasExtension(container) ? container.Replace(Path.GetExtension(container), "") : container;
var destPath = Path.Combine(baseDestPath, container) + Path.DirectorySeparatorChar;
ExtractLive2D(assets, destPath, modelName, assemblyLoader, motionMode, forceBezier);
modelCounter++;
}
catch (Exception ex)
{
Logger.Error($"Live2D model export error: \"{srcContainer}\"", ex);
}
Progress.Report(modelCounter, (int)totalModelCount);
}
var status = modelCounter > 0 ?
$"Finished exporting [{modelCounter}/{totalModelCount}] Live2D model(s) to \"{CLIOptions.o_outputFolder.Value.Color(Ansi.BrightCyan)}\"" :
"Nothing exported.";
Logger.Default.Log(LoggerEvent.Info, status, ignoreLevel: true);
}
}
}

View File

@@ -1,20 +0,0 @@
using namespace System;
using namespace System::Reflection;
using namespace System::Runtime::CompilerServices;
using namespace System::Runtime::InteropServices;
using namespace System::Security::Permissions;
[assembly:AssemblyTitleAttribute(L"AssetStudioFBX")];
[assembly:AssemblyDescriptionAttribute(L"")];
[assembly:AssemblyConfigurationAttribute(L"")];
[assembly:AssemblyCompanyAttribute(L"")];
[assembly:AssemblyProductAttribute(L"AssetStudioFBX")];
[assembly:AssemblyCopyrightAttribute(L"Copyright © Perfare 2018-2020")];
[assembly:AssemblyTrademarkAttribute(L"")];
[assembly:AssemblyCultureAttribute(L"")];
[assembly:AssemblyVersionAttribute("1.0.*")];
[assembly:ComVisible(false)];
[assembly:CLSCompliantAttribute(true)];

View File

@@ -1,43 +0,0 @@
#include "AssetStudioFBX.h"
namespace AssetStudio
{
char* Fbx::StringToUTF8(String^ s)
{
auto bytes = Text::Encoding::UTF8->GetBytes(s);
auto chars = new char[bytes->Length + 1];
pin_ptr<unsigned char> ptr = &bytes[0];
memcpy(chars, ptr, bytes->Length);
chars[bytes->Length] = '\0';
return chars;
}
void Fbx::Init(FbxManager** pSdkManager, FbxScene** pScene)
{
*pSdkManager = FbxManager::Create();
if (!pSdkManager)
{
throw gcnew Exception(gcnew String("Unable to create the FBX SDK manager"));
}
FbxIOSettings* ios = FbxIOSettings::Create(*pSdkManager, IOSROOT);
(*pSdkManager)->SetIOSettings(ios);
*pScene = FbxScene::Create(*pSdkManager, "");
}
Vector3 Fbx::QuaternionToEuler(Quaternion q)
{
FbxAMatrix lMatrixRot;
lMatrixRot.SetQ(FbxQuaternion(q.X, q.Y, q.Z, q.W));
FbxVector4 lEuler = lMatrixRot.GetR();
return Vector3((float)lEuler[0], (float)lEuler[1], (float)lEuler[2]);
}
Quaternion Fbx::EulerToQuaternion(Vector3 v)
{
FbxAMatrix lMatrixRot;
lMatrixRot.SetR(FbxVector4(v.X, v.Y, v.Z));
FbxQuaternion lQuaternion = lMatrixRot.GetQ();
return Quaternion((float)lQuaternion[0], (float)lQuaternion[1], (float)lQuaternion[2], (float)lQuaternion[3]);
}
}

View File

@@ -1,86 +0,0 @@
#pragma once
#include <fbxsdk.h>
#ifdef IOS_REF
#undef IOS_REF
#define IOS_REF (*(pSdkManager->GetIOSettings()))
#endif
using namespace System;
using namespace System::Collections::Generic;
using namespace System::IO;
#define WITH_MARSHALLED_STRING(name,str,block)\
{ \
char* name; \
try \
{ \
name = StringToUTF8(str); \
block \
} \
finally \
{ \
delete name; \
} \
}
static char* FBXVersion[] =
{
FBX_2010_00_COMPATIBLE,
FBX_2011_00_COMPATIBLE,
FBX_2012_00_COMPATIBLE,
FBX_2013_00_COMPATIBLE,
FBX_2014_00_COMPATIBLE,
FBX_2016_00_COMPATIBLE
};
namespace AssetStudio {
public ref class Fbx
{
public:
static Vector3 QuaternionToEuler(Quaternion q);
static Quaternion EulerToQuaternion(Vector3 v);
static char* StringToUTF8(String^ s);
static void Init(FbxManager** pSdkManager, FbxScene** pScene);
ref class Exporter
{
public:
static void Export(String^ path, IImported^ imported, bool eulerFilter, float filterPrecision,
bool allNodes, bool skins, bool animation, bool blendShape, bool castToBone, float boneSize, float scaleFactor, int versionIndex, bool isAscii);
private:
bool exportSkins;
float boneSize;
IImported^ imported;
HashSet<String^>^ framePaths;
Dictionary<ImportedFrame^, size_t>^ frameToNode;
List<ImportedFrame^>^ meshFrames;
char* cDest;
FbxManager* pSdkManager;
FbxScene* pScene;
FbxExporter* pExporter;
FbxArray<FbxSurfacePhong*>* pMaterials;
FbxArray<FbxFileTexture*>* pTextures;
FbxPose* pBindPose;
Exporter(String^ name, IImported^ imported, bool allNodes, bool skins, bool castToBone, float boneSize, float scaleFactor, int versionIndex, bool isAscii);
~Exporter();
void Exporter::LinkTexture(ImportedMaterialTexture^ texture, FbxFileTexture* pTexture, FbxProperty& prop);
void SetJointsNode(ImportedFrame^ frame, HashSet<String^>^ bonePaths, bool allBones);
HashSet<String^>^ SearchHierarchy();
void SearchHierarchy(ImportedFrame^ frame, HashSet<String^>^ exportFrames);
void SetJointsFromImportedMeshes(bool allBones);
void ExportFrame(FbxNode* pParentNode, ImportedFrame^ frame);
void ExportMesh(FbxNode* pFrameNode, ImportedMesh^ iMesh);
FbxFileTexture* ExportTexture(ImportedTexture^ matTex);
void ExportAnimations(bool eulerFilter, float filterValue);
void ExportKeyframedAnimation(ImportedKeyframedAnimation^ parser, FbxString& kTakeName, FbxAnimCurveFilterUnroll* eulerFilter, float filterPrecision);
void ExportMorphs();
};
};
}

View File

@@ -1,141 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{B82DD1BA-4EEC-4F29-A686-03D7F0DF39B8}</ProjectGuid>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<Keyword>ManagedCProj</Keyword>
<RootNamespace>AssetStudioFBX</RootNamespace>
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CLRSupport>true</CLRSupport>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CLRSupport>true</CLRSupport>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CLRSupport>true</CLRSupport>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CLRSupport>true</CLRSupport>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>FBXSDK_SHARED;WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>libfbxsdk.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\lib\vs2017\x86\release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>FBXSDK_SHARED;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>libfbxsdk.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\lib\vs2017\x64\release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>FBXSDK_SHARED;WIN32;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>libfbxsdk.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\lib\vs2017\x86\release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>FBXSDK_SHARED;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>libfbxsdk.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>C:\Program Files\Autodesk\FBX\FBX SDK\2020.0.1\lib\vs2017\x64\release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="AssetStudioFBX.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="AssemblyInfo.cpp" />
<ClCompile Include="AssetStudioFBX.cpp" />
<ClCompile Include="AssetStudioFBXExporter.cpp" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AssetStudio\AssetStudio.csproj">
<Project>{7662f8c2-7bfd-442e-a948-a43b4f7eb06e}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="源文件">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="头文件">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="资源文件">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="AssetStudioFBX.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="AssemblyInfo.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="AssetStudioFBX.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="AssetStudioFBXExporter.cpp">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>
</Project>

Some files were not shown because too many files have changed in this diff Show More