177 Commits

Author SHA1 Message Date
VaDiM
bb7accd04d Update version to v0.18.0
- Updated dependencies
2024-04-04 22:44:20 +03:00
VaDiM
5120999026 Update readme and other text files 2024-04-04 21:55:59 +03:00
VaDiM
1fc504e587 Optimize memory consumption of swizzled textures 2024-04-02 00:40:24 +03:00
VaDiM
4e991d85fb More fixes for unity 2019.4 assets 2024-04-01 00:56:48 +03:00
VaDiM
29b4bb59a0 [GUI] Move "loading via typetree" option to a better place 2024-04-01 00:56:48 +03:00
VaDiM
9750f486d5 Fix moc3 export 2024-04-01 00:21:51 +03:00
VaDiM
97bbfe8fbb Fix type of public "offset" property in ResourceReader 2024-04-01 00:21:50 +03:00
VaDiM
684bf5a874 Show assumed unity ver in some cases for assets with stripped ver 2024-04-01 00:21:50 +03:00
VaDiM
3cc6bed844 Raplace Unity ver array with Unity ver class 2024-04-01 00:21:50 +03:00
VaDiM
c9e9bc840c Add parallel export support for some asset types 2024-04-01 00:21:50 +03:00
VaDiM
cadcf0b492 Fix displayed info for non-fmod audio clips 2024-03-28 01:52:43 +03:00
VaDiM
1e8085da2d Fix AssetBundle structure for Unity v5.4.x. Close #31 2024-03-28 01:52:43 +03:00
VaDiM
770d7e7532 [GUI] Fix compatibility with High Contrast modes 2024-03-15 18:19:05 +03:00
VaDiM
d0d8a35f67 [GUI] Fixes for Tex2DArrayImage 2024-03-14 00:42:56 +03:00
VaDiM
3f004f74d1 Add option to disable asset loading via typetree 2024-03-13 22:52:43 +03:00
VaDiM
3effd06e64 Unity 2023.2+ Texture2D fix 2024-03-13 22:52:43 +03:00
VaDiM
4f0afffeba Add AnimationClip loading via typetree 2024-03-13 22:52:43 +03:00
VaDiM
9b69b5607c Fix DisplayInfo default value in model3.json 2024-03-10 23:17:25 +03:00
VaDiM
9b16ea8d41 Fix bug with Blend type in l2d expressions 2024-03-09 22:47:11 +03:00
VaDiM
ae4548f1c3 Add support for swizzled Switch textures
Co-Authored-By: nesrak1 <12544505+nesrak1@users.noreply.github.com>
2024-03-09 02:35:23 +03:00
VaDiM
4cd246592b [CLI] Fix wrong error handler. Fix option description msgs 2024-03-07 04:38:39 +03:00
VaDiM
f54cc004bd Add support for Zstd block compression 2024-03-07 03:49:06 +03:00
VaDiM
afdc81bca8 Some minor fixes 2024-03-05 23:42:35 +03:00
VaDiM
045f6c0ff4 Add Texture2DArray loading via typetree 2024-03-05 23:42:29 +03:00
VaDiM
bc9380b8be Add Texture2D loading via typetree 2024-03-05 23:42:29 +03:00
VaDiM
46f8023756 [GUI] Improve export of Texture2DArray images
- Do not export an entire tex2darray array if both the tex2darray and some of its images were selected for exporting
2024-03-05 23:42:07 +03:00
VaDiM
0f9afa60d7 Add support for Texture2DArray 2024-03-04 19:46:28 +03:00
VaDiM
ec7f2c393d [CLI] Update readme 2024-02-13 05:43:34 +03:00
VaDiM
3ec8169b08 [GUI] Add more details to log when exporting assets 2024-02-13 04:55:25 +03:00
VaDiM
5c24183d18 Add more options to work with Scene Hierarchy (#23)
- Added option to group exported assets by node path in scene hierarchy
- Added field with node path to exported xml asset list
2024-02-13 04:52:59 +03:00
VaDiM
c68eaa5e3c Fixes for previous fixes 2024-02-13 04:51:14 +03:00
VaDiM
8460ecef8d Show/Export object dump if typetree dump is not available 2024-02-12 05:21:23 +03:00
VaDiM
f90c0ecc00 Add option to export assets with PathID in filename (#25)
https://github.com/Perfare/AssetStudio/issues/1050
https://github.com/Perfare/AssetStudio/issues/975
https://github.com/Perfare/AssetStudio/issues/762
2024-02-11 03:01:40 +03:00
VaDiM
535153be6b Add cdi3.json support for l2d export (beta) 2024-02-09 22:35:14 +03:00
VaDiM
d4162161be [GUI] Replace "Show all error msgs" option with "Show debug msgs" 2024-02-08 22:44:57 +03:00
VaDiM
6fe12d274f [CLI] Add colors to help message 2024-02-08 22:35:48 +03:00
VaDiM
70213e3012 Improve integration with Live2D assets
- Improved export method of AnimationClip motions
- [GUI] Added display of model info on the preview tab
- [GUI] Added support for partial export:
  - selected models
  - model + selected AnimationClip motions
  - model + selected Fade motions
  - model + selected Fade Motion List
2024-02-08 22:34:00 +03:00
VaDiM
2f6d9ec77f Fixes & Cleanup 2024-01-31 22:34:12 +03:00
VaDiM
0d2aebc1f4 Fix a bug where UV is exported incorrectly.
Co-Authored-By: Razmoth <32140579+razmoth@users.noreply.github.com>
2024-01-24 23:26:13 +03:00
VaDiM
115074fdc0 [GUI] Preserve selection order of AnimationClips (#24) 2023-12-30 18:51:01 +03:00
VaDiM
c2d3b8c5f9 Improve Live2D export 2023-12-30 18:13:40 +03:00
VaDiM
67a74fd395 Bugfix for unity 2022.2+ Texture2D reader 2023-12-30 17:08:07 +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
45bf9251c9 Fix crash when SpriteAtlas is missing 2023-11-07 01:31:27 +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
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
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
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
VaDiM
74f2c3190b Merge branch 'Perfare_master' into AssetStudio-mod 2022-01-13 18:10:31 +02: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
VaDiM
f0b23bbfe7 Merge branch 'Perfare_master' into AssetStudio-mod 2021-12-05 02:41:24 +02: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
150 changed files with 16230 additions and 5946 deletions

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

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

View File

@@ -4,33 +4,29 @@ 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 dllDir = GetDirectedDllDirectory();
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(dllDir, dllName);
}
else
{
Posix.LoadDll(dllDir, dllName);
Win32.LoadDll(GetDirectedDllDirectory(localDir), dllName);
}
}
private static string GetDirectedDllDirectory()
private static string GetDirectedDllDirectory(string localDir)
{
var localPath = Process.GetCurrentProcess().MainModule.FileName;
var localDir = Path.GetDirectoryName(localPath);
var subDir = Environment.Is64BitProcess ? "x64" : "x86";
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);
@@ -64,61 +60,7 @@ namespace AssetStudio.PInvoke
private const uint LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x1000;
private const uint LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x100;
}
private static class Posix
{
internal static void LoadDll(string dllDir, string dllName)
{
string dllExtension;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
dllExtension = ".so";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
dllExtension = ".dylib";
}
else
{
throw new NotSupportedException();
}
var dllFileName = $"lib{dllName}{dllExtension}";
var directedDllPath = Path.Combine(dllDir, dllFileName);
const int ldFlags = RTLD_NOW | RTLD_GLOBAL;
var hLibrary = DlOpen(directedDllPath, ldFlags);
if (hLibrary == IntPtr.Zero)
{
var pErrStr = DlError();
// `PtrToStringAnsi` always uses the specific constructor of `String` (see dotnet/core#2325),
// which in turn interprets the byte sequence with system default codepage. On OSX and Linux
// the codepage is UTF-8 so the error message should be handled correctly.
var errorMessage = Marshal.PtrToStringAnsi(pErrStr);
throw new DllNotFoundException(errorMessage);
}
}
// OSX and most Linux OS use LP64 so `int` is still 32-bit even on 64-bit platforms.
// void *dlopen(const char *filename, int flag);
[DllImport("libdl", EntryPoint = "dlopen")]
private static extern IntPtr DlOpen([MarshalAs(UnmanagedType.LPStr)] string fileName, int flags);
// char *dlerror(void);
[DllImport("libdl", EntryPoint = "dlerror")]
private static extern IntPtr DlError();
private const int RTLD_LAZY = 0x1;
private const int RTLD_NOW = 0x2;
private const int RTLD_GLOBAL = 0x100;
}
}
}
#endif

View File

@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31410.357
# Visual Studio Version 17
VisualStudioVersion = 17.5.33414.496
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssetStudio", "AssetStudio\AssetStudio.csproj", "{422FEC21-EF60-4F29-AA56-95DFDA23C913}"
EndProject
@@ -25,6 +25,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AssetStudioFBXNative", "Ass
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
@@ -131,6 +137,18 @@ Global
{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

View File

@@ -22,26 +22,26 @@ namespace SevenZip
}
}
uint _value = 0xFFFFFFFF;
private uint _value = 0xFFFFFFFF;
public void Init() { _value = 0xFFFFFFFF; }
public void UpdateByte(byte b)
{
_value = Table[(((byte)(_value)) ^ b)] ^ (_value >> 8);
_value = Table[(byte)_value ^ b] ^ (_value >> 8);
}
public void Update(byte[] data, uint offset, uint size)
{
for (uint i = 0; i < size; i++)
_value = Table[(((byte)(_value)) ^ data[offset + i])] ^ (_value >> 8);
_value = Table[(byte)_value ^ data[offset + i]] ^ (_value >> 8);
}
public uint GetDigest() { return _value ^ 0xFFFFFFFF; }
static uint CalculateDigest(byte[] data, uint offset, uint size)
public static uint CalculateDigest(byte[] data, uint offset, uint size)
{
CRC crc = new CRC();
var crc = new CRC();
// crc.Init();
crc.Update(data, offset, size);
return crc.GetDigest();
@@ -49,7 +49,7 @@ namespace SevenZip
static bool VerifyDigest(uint digest, byte[] data, uint offset, uint size)
{
return (CalculateDigest(data, offset, size) == digest);
return CalculateDigest(data, offset, size) == digest;
}
}
}

View File

@@ -1,22 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net472;netstandard2.0;net5.0;net6.0</TargetFrameworks>
<Version>0.16.0.0</Version>
<AssemblyVersion>0.16.0.0</AssemblyVersion>
<FileVersion>0.16.0.0</FileVersion>
<Copyright>Copyright © Perfare 2018-2022</Copyright>
<TargetFrameworks>net472;net6.0;net6.0-windows;net7.0;net7.0-windows;net8.0;net8.0-windows</TargetFrameworks>
<Version>0.18.0.0</Version>
<Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2023-2024</Copyright>
<DebugType>embedded</DebugType>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net472' ">
<PackageReference Include="K4os.Compression.LZ4" Version="1.2.16" />
<PackageReference Include="K4os.Compression.LZ4" Version="1.3.8" />
<PackageReference Include="ZstdSharp.Port" Version="0.7.6" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
<PackageReference Include="System.Memory" Version="4.5.4" />
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="System.IO.Compression" Version="4.0.0" />
<PackageReference Include="K4os.Compression.LZ4" Version="1.1.11" />
<PackageReference Include="ZstdSharp.Port" Version="0.6.5" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
@@ -10,30 +11,126 @@ namespace AssetStudio
{
public class AssetsManager
{
public string SpecifyUnityVersion;
public bool ZstdEnabled = true;
public bool LoadingViaTypeTreeEnabled = true;
public List<SerializedFile> assetsFileList = new List<SerializedFile>();
internal Dictionary<string, int> assetsFileIndexCache = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
internal Dictionary<string, BinaryReader> resourceFileReaders = new Dictionary<string, BinaryReader>(StringComparer.OrdinalIgnoreCase);
internal ConcurrentDictionary<string, BinaryReader> resourceFileReaders = new ConcurrentDictionary<string, BinaryReader>(StringComparer.OrdinalIgnoreCase);
private UnityVersion specifiedUnityVersion;
private List<string> importFiles = new List<string>();
private HashSet<ClassIDType> filteredAssetTypesList = new HashSet<ClassIDType>();
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 UnityVersion SpecifyUnityVersion
{
var path = Path.GetDirectoryName(Path.GetFullPath(files[0]));
MergeSplitAssets(path);
var toReadFile = ProcessingSplitFiles(files.ToList());
Load(toReadFile);
get => specifiedUnityVersion;
set
{
if (specifiedUnityVersion == value)
{
return;
}
if (value == null)
{
specifiedUnityVersion = null;
Logger.Info("Specified Unity version: None");
return;
}
if (string.IsNullOrEmpty(value.BuildType))
{
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");
}
specifiedUnityVersion = value;
Logger.Info($"Specified Unity version: {specifiedUnityVersion}");
}
}
public void LoadFolder(string path)
public void SetAssetFilter(params ClassIDType[] classIDTypes)
{
MergeSplitAssets(path, true);
var files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories).ToList();
var toReadFile = ProcessingSplitFiles(files);
if (filteredAssetTypesList.Count == 0)
{
filteredAssetTypesList.UnionWith(new HashSet<ClassIDType>
{
ClassIDType.AssetBundle,
ClassIDType.ResourceManager,
ClassIDType.GameObject,
ClassIDType.Transform,
});
}
if (classIDTypes.Contains(ClassIDType.MonoBehaviour))
{
filteredAssetTypesList.Add(ClassIDType.MonoScript);
}
if (classIDTypes.Contains(ClassIDType.Sprite))
{
filteredAssetTypesList.Add(ClassIDType.Texture2D);
filteredAssetTypesList.Add(ClassIDType.SpriteAtlas);
}
filteredAssetTypesList.UnionWith(classIDTypes);
}
public void SetAssetFilter(List<ClassIDType> classIDTypeList)
{
SetAssetFilter(classIDTypeList.ToArray());
}
public void LoadFilesAndFolders(params string[] path)
{
var pathList = new List<string>();
pathList.AddRange(path);
LoadFilesAndFolders(out _, pathList);
}
public void LoadFilesAndFolders(out string parentPath, params string[] path)
{
var pathList = new List<string>();
pathList.AddRange(path);
LoadFilesAndFolders(out parentPath, pathList);
}
public void LoadFilesAndFolders(out string parentPath, List<string> pathList)
{
var 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);
}
@@ -49,8 +146,14 @@ namespace AssetStudio
//use a for loop because list size can change
for (var i = 0; i < importFiles.Count; i++)
{
LoadFile(importFiles[i]);
Progress.Report(i + 1, importFiles.Count);
if (LoadFile(importFiles[i]))
{
Progress.Report(i + 1, importFiles.Count);
}
else
{
break;
}
}
importFiles.Clear();
@@ -62,22 +165,20 @@ namespace AssetStudio
ProcessAssets();
}
private void LoadFile(string fullName)
private bool LoadFile(string fullName)
{
var reader = new FileReader(fullName);
LoadFile(reader);
return LoadFile(reader);
}
private void LoadFile(FileReader reader)
private bool LoadFile(FileReader reader)
{
switch (reader.FileType)
switch (reader?.FileType)
{
case FileType.AssetsFile:
LoadAssetsFile(reader);
break;
return LoadAssetsFile(reader);
case FileType.BundleFile:
LoadBundleFile(reader);
break;
return LoadBundleFile(reader);
case FileType.WebFile:
LoadWebFile(reader);
break;
@@ -91,9 +192,10 @@ namespace AssetStudio
LoadZipFile(reader);
break;
}
return true;
}
private void LoadAssetsFile(FileReader reader)
private bool LoadAssetsFile(FileReader reader)
{
if (!assetsFileListHash.Contains(reader.FileName))
{
@@ -101,6 +203,7 @@ namespace AssetStudio
try
{
var assetsFile = new SerializedFile(reader, this);
var dirName = Path.GetDirectoryName(reader.FullPath);
CheckStrippedVersion(assetsFile);
assetsFileList.Add(assetsFile);
assetsFileListHash.Add(assetsFile.fileName);
@@ -111,12 +214,12 @@ namespace AssetStudio
if (!importFilesHash.Contains(sharedFileName))
{
var sharedFilePath = Path.Combine(Path.GetDirectoryName(reader.FullPath), sharedFileName);
var sharedFilePath = Path.Combine(dirName, sharedFileName);
if (!noexistFiles.Contains(sharedFilePath))
{
if (!File.Exists(sharedFilePath))
{
var findFiles = Directory.GetFiles(Path.GetDirectoryName(reader.FullPath), sharedFileName, SearchOption.AllDirectories);
var findFiles = Directory.GetFiles(dirName, sharedFileName, SearchOption.AllDirectories);
if (findFiles.Length > 0)
{
sharedFilePath = findFiles[0];
@@ -135,9 +238,15 @@ namespace AssetStudio
}
}
}
catch (NotSupportedException e)
{
Logger.Error(e.Message);
reader.Dispose();
return false;
}
catch (Exception e)
{
Logger.Error($"Error while reading assets file {reader.FullPath}", e);
Logger.Warning($"Failed to read assets file {reader.FullPath}\r\n{e}");
reader.Dispose();
}
}
@@ -146,9 +255,10 @@ namespace AssetStudio
Logger.Info($"Skipping {reader.FullPath}");
reader.Dispose();
}
return true;
}
private void LoadAssetsFromMemory(FileReader reader, string originalPath, string unityVersion = null)
private bool LoadAssetsFromMemory(FileReader reader, string originalPath, UnityVersion assetBundleUnityVer = null)
{
if (!assetsFileListHash.Contains(reader.FileName))
{
@@ -156,43 +266,59 @@ namespace AssetStudio
{
var assetsFile = new SerializedFile(reader, this);
assetsFile.originalPath = originalPath;
if (!string.IsNullOrEmpty(unityVersion) && assetsFile.header.m_Version < SerializedFileFormatVersion.Unknown_7)
if (assetBundleUnityVer != null && assetsFile.header.m_Version < SerializedFileFormatVersion.Unknown_7)
{
assetsFile.SetVersion(unityVersion);
assetsFile.SetVersion(assetBundleUnityVer);
}
CheckStrippedVersion(assetsFile);
CheckStrippedVersion(assetsFile, assetBundleUnityVer);
assetsFileList.Add(assetsFile);
assetsFileListHash.Add(assetsFile.fileName);
}
catch (NotSupportedException e)
{
Logger.Error(e.Message);
resourceFileReaders.TryAdd(reader.FileName, reader);
return false;
}
catch (Exception e)
{
Logger.Error($"Error while reading assets file {reader.FullPath} from {Path.GetFileName(originalPath)}", e);
resourceFileReaders.Add(reader.FileName, reader);
Logger.Warning($"Failed to read assets file {reader.FullPath} from {Path.GetFileName(originalPath)}\r\n{e}");
resourceFileReaders.TryAdd(reader.FileName, reader);
}
}
else
{
Logger.Info($"Skipping {originalPath} ({reader.FileName})");
}
return true;
}
private void LoadBundleFile(FileReader reader, string originalPath = null)
private bool LoadBundleFile(FileReader reader, string originalPath = null)
{
Logger.Info("Loading " + reader.FullPath);
try
{
var bundleFile = new BundleFile(reader);
var bundleFile = new BundleFile(reader, ZstdEnabled, specifiedUnityVersion);
foreach (var file in bundleFile.fileList)
{
var dummyPath = Path.Combine(Path.GetDirectoryName(reader.FullPath), file.fileName);
var subReader = new FileReader(dummyPath, file.stream);
if (subReader.FileType == FileType.AssetsFile)
{
LoadAssetsFromMemory(subReader, originalPath ?? reader.FullPath, bundleFile.m_Header.unityRevision);
if (!LoadAssetsFromMemory(subReader, originalPath ?? reader.FullPath, bundleFile.m_Header.unityRevision))
return false;
}
else
{
resourceFileReaders[file.fileName] = subReader; //TODO
resourceFileReaders.TryAdd(file.fileName, subReader);
}
}
return true;
}
catch (NotSupportedException e)
{
Logger.Error(e.Message);
return false;
}
catch (Exception e)
{
@@ -201,7 +327,8 @@ namespace AssetStudio
{
str += $" from {Path.GetFileName(originalPath)}";
}
Logger.Error(str, e);
Logger.Warning($"{str}\r\n{e}");
return true;
}
finally
{
@@ -248,7 +375,7 @@ namespace AssetStudio
private void LoadZipFile(FileReader reader)
{
Logger.Info("Loading " + reader.FileName);
Logger.Info("Reading " + reader.FileName);
try
{
using (ZipArchive archive = new ZipArchive(reader.BaseStream, ZipArchiveMode.Read))
@@ -298,11 +425,14 @@ namespace AssetStudio
}
catch (Exception e)
{
Logger.Error($"Error while reading zip split file {basePath}", 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
@@ -323,15 +453,13 @@ namespace AssetStudio
if (entryReader.FileType == FileType.ResourceFile)
{
entryReader.Position = 0;
if (!resourceFileReaders.ContainsKey(entry.Name))
{
resourceFileReaders.Add(entry.Name, entryReader);
}
resourceFileReaders.TryAdd(entry.Name, entryReader);
}
Progress.Report(++k, progressCount);
}
catch (Exception e)
{
Logger.Error($"Error while reading zip entry {entry.FullName}", e);
Logger.Warning($"Error while reading zip entry {entry.FullName}\r\n{e}");
}
}
}
@@ -346,13 +474,16 @@ namespace AssetStudio
}
}
public void CheckStrippedVersion(SerializedFile assetsFile)
public void CheckStrippedVersion(SerializedFile assetsFile, UnityVersion bundleUnityVer = null)
{
if (assetsFile.IsVersionStripped && string.IsNullOrEmpty(SpecifyUnityVersion))
if (assetsFile.version.IsStripped && specifiedUnityVersion == null)
{
throw new Exception("The Unity version has been stripped, please set the version in the options");
var msg = "The asset's Unity version has been stripped, please set the version in the options.";
if (bundleUnityVer != null && !bundleUnityVer.IsStripped)
msg += $"\n\nAssumed Unity version based on asset bundle: {bundleUnityVer}";
throw new NotSupportedException(msg);
}
if (!string.IsNullOrEmpty(SpecifyUnityVersion))
if (specifiedUnityVersion != null)
{
assetsFile.SetVersion(SpecifyUnityVersion);
}
@@ -388,16 +519,22 @@ 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:
obj = new Animation(objectReader);
break;
case ClassIDType.AnimationClip:
obj = new AnimationClip(objectReader);
obj = objectReader.serializedType?.m_Type != null && LoadingViaTypeTreeEnabled
? new AnimationClip(objectReader, TypeTreeHelper.ReadType(objectReader.serializedType.m_Type, objectReader))
: new AnimationClip(objectReader);
break;
case ClassIDType.Animator:
obj = new Animator(objectReader);
@@ -447,11 +584,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 < 2021)
obj = new Shader(objectReader);
break;
case ClassIDType.SkinnedMeshRenderer:
obj = new SkinnedMeshRenderer(objectReader);
@@ -466,7 +607,14 @@ namespace AssetStudio
obj = new TextAsset(objectReader);
break;
case ClassIDType.Texture2D:
obj = new Texture2D(objectReader);
obj = objectReader.serializedType?.m_Type != null && LoadingViaTypeTreeEnabled
? new Texture2D(objectReader, TypeTreeHelper.ReadType(objectReader.serializedType.m_Type, objectReader))
: new Texture2D(objectReader);
break;
case ClassIDType.Texture2DArray:
obj = objectReader.serializedType?.m_Type != null && LoadingViaTypeTreeEnabled
? new Texture2DArray(objectReader, TypeTreeHelper.ReadType(objectReader.serializedType.m_Type, objectReader))
: new Texture2DArray(objectReader);
break;
case ClassIDType.Transform:
obj = new Transform(objectReader);
@@ -481,7 +629,10 @@ namespace AssetStudio
obj = new Object(objectReader);
break;
}
assetsFile.AddObject(obj);
if (obj != null)
{
assetsFile.AddObject(obj);
}
}
catch (Exception e)
{
@@ -492,7 +643,7 @@ namespace AssetStudio
.AppendLine($"Type {objectReader.type}")
.AppendLine($"PathID {objectInfo.m_PathID}")
.Append(e);
Logger.Error(sb.ToString());
Logger.Warning(sb.ToString());
}
Progress.Report(++i, progressCount);
@@ -502,7 +653,7 @@ namespace AssetStudio
private void ProcessAssets()
{
Logger.Info("Process Assets...");
Logger.Info("Process assets...");
foreach (var assetsFile in assetsFileList)
{
@@ -548,14 +699,17 @@ namespace AssetStudio
{
m_Sprite.m_SpriteAtlas.Set(m_SpriteAtlas);
}
else
else if (m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlaOld))
{
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.");
}
}
}
}

View File

@@ -4,7 +4,11 @@ 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;
public static ArrayPool<T> Shared { get; }
static BigArrayPool()
{
Shared = ArrayPool<T>.Create(256 * 1024 * 1024, 5);
}
}
}

View File

@@ -1,4 +1,5 @@
using K4os.Compression.LZ4;
using ZstdSharp;
using System;
using System.IO;
using System.Linq;
@@ -15,6 +16,13 @@ namespace AssetStudio
BlockInfoNeedPaddingAtStart = 0x200
}
[Flags]
public enum CnEncryptionFlags
{
OldFlag = 0x200,
NewFlag = 0x400
}
[Flags]
public enum StorageBlockFlags
{
@@ -28,7 +36,8 @@ namespace AssetStudio
Lzma,
Lz4,
Lz4HC,
Lzham
Lzham,
Custom,
}
public class BundleFile
@@ -38,7 +47,7 @@ namespace AssetStudio
public string signature;
public uint version;
public string unityVersion;
public string unityRevision;
public UnityVersion unityRevision;
public long size;
public uint compressedBlocksInfoSize;
public uint uncompressedBlocksInfoSize;
@@ -66,13 +75,13 @@ namespace AssetStudio
public StreamFile[] fileList;
public BundleFile(FileReader reader)
public BundleFile(FileReader reader, bool useZstd, UnityVersion specUnityVer = null)
{
m_Header = new Header();
m_Header.signature = reader.ReadStringToNull();
m_Header.version = reader.ReadUInt32();
m_Header.unityVersion = reader.ReadStringToNull();
m_Header.unityRevision = reader.ReadStringToNull();
m_Header.unityRevision = new UnityVersion(reader.ReadStringToNull());
switch (m_Header.signature)
{
case "UnityArchive":
@@ -92,17 +101,55 @@ namespace AssetStudio
break;
case "UnityFS":
ReadHeader(reader);
ReadBlocksInfoAndDirectory(reader);
var isUnityCnEnc = false;
var unityVer = m_Header.unityRevision;
if (specUnityVer != null)
{
if (!unityVer.IsStripped && specUnityVer != unityVer)
{
Logger.Warning($"Detected Unity version is different from the specified one ({specUnityVer.FullVersion.Color(ColorConsole.BrightCyan)}).\n" +
$"Assets may load with errors.\n" +
$"It is recommended to specify the detected Unity version: {unityVer.FullVersion.Color(ColorConsole.BrightCyan)}");
}
unityVer = specUnityVer;
}
if (!unityVer.IsStripped)
{
// https://issuetracker.unity3d.com/issues/files-within-assetbundles-do-not-start-on-aligned-boundaries-breaking-patching-on-nintendo-switch
if (unityVer < 2020
|| unityVer.IsInRange(2020, (2020, 3, 34))
|| unityVer.IsInRange(2021, (2021, 3, 2))
|| unityVer.IsInRange(2022, (2022, 1, 1)))
{
isUnityCnEnc = ((CnEncryptionFlags)m_Header.flags & CnEncryptionFlags.OldFlag) != 0;
}
else
{
isUnityCnEnc = ((CnEncryptionFlags)m_Header.flags & CnEncryptionFlags.NewFlag) != 0;
}
}
if (isUnityCnEnc)
{
var msg = "Unsupported bundle file. ";
msg += specUnityVer != null
? "UnityCN encryption was detected or the specified Unity version is incorrect."
: "UnityCN encryption was detected.";
throw new NotSupportedException(msg);
}
ReadBlocksInfoAndDirectory(reader, unityVer);
using (var blocksStream = CreateBlocksStream(reader.FullPath))
{
ReadBlocks(reader, blocksStream);
ReadBlocks(reader, blocksStream, useZstd);
ReadFiles(blocksStream, reader.FullPath);
}
break;
}
}
private void ReadHeaderAndBlocksInfo(EndianBinaryReader reader)
private void ReadHeaderAndBlocksInfo(FileReader reader)
{
if (m_Header.version >= 4)
{
@@ -154,7 +201,7 @@ namespace AssetStudio
return blocksStream;
}
private void ReadBlocksAndDirectory(EndianBinaryReader reader, Stream blocksStream)
private void ReadBlocksAndDirectory(FileReader reader, Stream blocksStream)
{
var isCompressed = m_Header.signature == "UnityWeb";
foreach (var blockInfo in m_BlocksInfo)
@@ -215,7 +262,7 @@ namespace AssetStudio
}
}
private void ReadHeader(EndianBinaryReader reader)
private void ReadHeader(FileReader reader)
{
m_Header.size = reader.ReadInt64();
m_Header.compressedBlocksInfoSize = reader.ReadUInt32();
@@ -227,13 +274,27 @@ namespace AssetStudio
}
}
private void ReadBlocksInfoAndDirectory(EndianBinaryReader reader)
private void ReadBlocksInfoAndDirectory(FileReader reader, UnityVersion unityVer)
{
byte[] blocksInfoBytes;
if (m_Header.version >= 7)
{
reader.AlignStream(16);
}
else if (unityVer >= (2019, 4) && m_Header.flags != ArchiveFlags.BlocksAndDirectoryInfoCombined)
{
//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;
@@ -245,42 +306,44 @@ namespace AssetStudio
{
blocksInfoBytes = reader.ReadBytes((int)m_Header.compressedBlocksInfoSize);
}
MemoryStream blocksInfoUncompresseddStream;
MemoryStream blocksInfoUncompressedStream;
var uncompressedSize = m_Header.uncompressedBlocksInfoSize;
var compressionType = (CompressionType)(m_Header.flags & ArchiveFlags.CompressionTypeMask);
switch (compressionType)
{
case CompressionType.None:
{
blocksInfoUncompresseddStream = new MemoryStream(blocksInfoBytes);
break;
}
{
blocksInfoUncompressedStream = new MemoryStream(blocksInfoBytes);
break;
}
case CompressionType.Lzma:
{
blocksInfoUncompressedStream = new MemoryStream((int) (uncompressedSize));
using (var blocksInfoCompressedStream = new MemoryStream(blocksInfoBytes))
{
blocksInfoUncompresseddStream = new MemoryStream((int)(uncompressedSize));
using (var blocksInfoCompressedStream = new MemoryStream(blocksInfoBytes))
{
SevenZipHelper.StreamDecompress(blocksInfoCompressedStream, blocksInfoUncompresseddStream, m_Header.compressedBlocksInfoSize, m_Header.uncompressedBlocksInfoSize);
}
blocksInfoUncompresseddStream.Position = 0;
break;
SevenZipHelper.StreamDecompress(blocksInfoCompressedStream, blocksInfoUncompressedStream,
m_Header.compressedBlocksInfoSize, m_Header.uncompressedBlocksInfoSize);
}
blocksInfoUncompressedStream.Position = 0;
break;
}
case CompressionType.Lz4:
case CompressionType.Lz4HC:
{
var uncompressedBytes = new byte[uncompressedSize];
var numWrite = LZ4Codec.Decode(blocksInfoBytes, uncompressedBytes);
if (numWrite != uncompressedSize)
{
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;
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
}
blocksInfoUncompressedStream = new MemoryStream(uncompressedBytes);
break;
}
default:
throw new IOException($"Unsupported compression type {compressionType}");
throw new IOException($"Unsupported block info compression type {compressionType}");
}
using (var blocksInfoReader = new EndianBinaryReader(blocksInfoUncompresseddStream))
using (var blocksInfoReader = new EndianBinaryReader(blocksInfoUncompressedStream))
{
var uncompressedDataHash = blocksInfoReader.ReadBytes(16);
var blocksInfoCount = blocksInfoReader.ReadInt32();
@@ -314,43 +377,72 @@ namespace AssetStudio
}
}
private void ReadBlocks(EndianBinaryReader reader, Stream blocksStream)
private void ReadBlocks(FileReader reader, Stream blocksStream, bool useZstd)
{
var zstdCodec = new Decompressor();
var i = 0;
foreach (var blockInfo in m_BlocksInfo)
{
var compressionType = (CompressionType)(blockInfo.flags & StorageBlockFlags.CompressionTypeMask);
switch (compressionType)
{
case CompressionType.None:
{
reader.BaseStream.CopyTo(blocksStream, blockInfo.compressedSize);
break;
}
{
reader.BaseStream.CopyTo(blocksStream, blockInfo.compressedSize);
break;
}
case CompressionType.Lzma:
{
SevenZipHelper.StreamDecompress(reader.BaseStream, blocksStream, blockInfo.compressedSize, blockInfo.uncompressedSize);
break;
}
{
SevenZipHelper.StreamDecompress(reader.BaseStream, blocksStream, blockInfo.compressedSize, blockInfo.uncompressedSize);
break;
}
case CompressionType.Lz4:
case CompressionType.Lz4HC:
case CompressionType.Custom:
{
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
{
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);
var numWrite = LZ4Codec.Decode(compressedBytes, 0, compressedSize, uncompressedBytes, 0, uncompressedSize);
var compTypeStr = compressionType.ToString();
if (compressionType == CompressionType.Custom)
{
compTypeStr = useZstd ? "Zstd" : "Lz4";
if (i == 0)
{
Logger.Debug($"Custom block compression type was detected. Trying to decompress as {compTypeStr} archive..");
i++;
}
}
int numWrite;
if (compressionType == CompressionType.Custom && useZstd)
{
numWrite = zstdCodec.Unwrap(compressedBytes, 0, compressedSize, uncompressedBytes, 0, uncompressedSize);
}
else
{
numWrite = LZ4Codec.Decode(compressedBytes, 0, compressedSize, uncompressedBytes, 0, uncompressedSize);
}
if (numWrite != uncompressedSize)
{
throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
throw new IOException($"{compTypeStr} block decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");
}
blocksStream.Write(uncompressedBytes, 0, uncompressedSize);
BigArrayPool<byte>.Shared.Return(compressedBytes);
BigArrayPool<byte>.Shared.Return(uncompressedBytes);
break;
}
finally
{
BigArrayPool<byte>.Shared.Return(compressedBytes, clearArray: true);
BigArrayPool<byte>.Shared.Return(uncompressedBytes, clearArray: true);
}
break;
}
default:
throw new IOException($"Unsupported compression type {compressionType}");
throw new IOException($"Unsupported block compression type {compressionType}");
}
}
blocksStream.Position = 0;

View File

@@ -145,6 +145,7 @@ namespace AssetStudio
ProceduralMaterial = 185,
ProceduralTexture = 186,
Texture2DArray = 187,
Texture2DArrayImage = -187, //fake type
CubemapArray = 188,
OffMeshLink = 191,
OcclusionArea = 192,

View File

@@ -1,4 +1,6 @@
using System;
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -15,6 +17,7 @@ namespace AssetStudio
public T inWeight;
public T outWeight;
public Keyframe() { }
public Keyframe(ObjectReader reader, Func<T> readerFunc)
{
@@ -22,7 +25,7 @@ namespace AssetStudio
value = readerFunc();
inSlope = readerFunc();
outSlope = readerFunc();
if (reader.version[0] >= 2018) //2018 and up
if (reader.version >= 2018) //2018 and up
{
weightedMode = reader.ReadInt32();
inWeight = readerFunc();
@@ -38,6 +41,8 @@ namespace AssetStudio
public int m_PostInfinity;
public int m_RotationOrder;
public AnimationCurve() { }
public AnimationCurve(ObjectReader reader, Func<T> readerFunc)
{
var version = reader.version;
@@ -50,7 +55,7 @@ namespace AssetStudio
m_PreInfinity = reader.ReadInt32();
m_PostInfinity = reader.ReadInt32();
if (version[0] > 5 || (version[0] == 5 && version[1] >= 3))//5.3 and up
if (version >= (5, 3)) //5.3 and up
{
m_RotationOrder = reader.ReadInt32();
}
@@ -62,6 +67,8 @@ namespace AssetStudio
public AnimationCurve<Quaternion> curve;
public string path;
public QuaternionCurve() { }
public QuaternionCurve(ObjectReader reader)
{
curve = new AnimationCurve<Quaternion>(reader, reader.ReadQuaternion);
@@ -77,6 +84,8 @@ namespace AssetStudio
public byte[] m_Data;
public byte m_BitSize;
public PackedFloatVector() { }
public PackedFloatVector(ObjectReader reader)
{
m_NumItems = reader.ReadUInt32();
@@ -136,6 +145,8 @@ namespace AssetStudio
public byte[] m_Data;
public byte m_BitSize;
public PackedIntVector() { }
public PackedIntVector(ObjectReader reader)
{
m_NumItems = reader.ReadUInt32();
@@ -180,6 +191,8 @@ namespace AssetStudio
public uint m_NumItems;
public byte[] m_Data;
public PackedQuatVector() { }
public PackedQuatVector(ObjectReader reader)
{
m_NumItems = reader.ReadUInt32();
@@ -264,6 +277,8 @@ namespace AssetStudio
public int m_PreInfinity;
public int m_PostInfinity;
public CompressedAnimationCurve() { }
public CompressedAnimationCurve(ObjectReader reader)
{
m_Path = reader.ReadAlignedString();
@@ -280,6 +295,8 @@ namespace AssetStudio
public AnimationCurve<Vector3> curve;
public string path;
public Vector3Curve() { }
public Vector3Curve(ObjectReader reader)
{
curve = new AnimationCurve<Vector3>(reader, reader.ReadVector3);
@@ -294,15 +311,22 @@ namespace AssetStudio
public string path;
public ClassIDType classID;
public PPtr<MonoScript> script;
public int flags;
public FloatCurve() { }
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 >= (2022, 2)) //2022.2 and up
{
flags = reader.ReadInt32();
}
}
}
@@ -311,6 +335,7 @@ namespace AssetStudio
public float time;
public PPtr<Object> value;
public PPtrKeyframe() { }
public PPtrKeyframe(ObjectReader reader)
{
@@ -326,10 +351,13 @@ namespace AssetStudio
public string path;
public int classID;
public PPtr<MonoScript> script;
public int flags;
public PPtrCurve() { }
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 +369,10 @@ namespace AssetStudio
path = reader.ReadAlignedString();
classID = reader.ReadInt32();
script = new PPtr<MonoScript>(reader);
if (version >= (2022, 2)) //2022.2 and up
{
flags = reader.ReadInt32();
}
}
}
@@ -349,6 +381,8 @@ namespace AssetStudio
public Vector3 m_Center;
public Vector3 m_Extent;
public AABB() { }
public AABB(ObjectReader reader)
{
m_Center = reader.ReadVector3();
@@ -362,12 +396,14 @@ namespace AssetStudio
public Quaternion q;
public Vector3 s;
public xform() { }
public xform(ObjectReader reader)
{
var version = reader.version;
t = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4();//5.4 and up
t = version >= (5, 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4();//5.4 and up
q = reader.ReadQuaternion();
s = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4();//5.4 and up
s = version >= (5, 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4();//5.4 and up
}
}
@@ -380,6 +416,8 @@ namespace AssetStudio
public float m_InOut;
public float m_Grab;
public HandPose() { }
public HandPose(ObjectReader reader)
{
m_GrabX = new xform(reader);
@@ -399,15 +437,17 @@ namespace AssetStudio
public Vector3 m_HintT;
public float m_HintWeightT;
public HumanGoal() { }
public HumanGoal(ObjectReader reader)
{
var version = reader.version;
m_X = new xform(reader);
m_WeightT = reader.ReadSingle();
m_WeightR = reader.ReadSingle();
if (version[0] >= 5)//5.0 and up
if (version >= 5)//5.0 and up
{
m_HintT = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4();//5.4 and up
m_HintT = version >= (5, 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4();//5.4 and up
m_HintWeightT = reader.ReadSingle();
}
}
@@ -424,11 +464,13 @@ namespace AssetStudio
public float[] m_DoFArray;
public Vector3[] m_TDoFArray;
public HumanPose() { }
public HumanPose(ObjectReader reader)
{
var version = reader.version;
m_RootX = new xform(reader);
m_LookAtPosition = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4();//5.4 and up
m_LookAtPosition = version >= (5, 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4();//5.4 and up
m_LookAtWeight = reader.ReadVector4();
int numGoals = reader.ReadInt32();
@@ -443,13 +485,13 @@ namespace AssetStudio
m_DoFArray = reader.ReadSingleArray();
if (version[0] > 5 || (version[0] == 5 && version[1] >= 2))//5.2 and up
if (version >= (5, 2))//5.2 and up
{
int numTDof = reader.ReadInt32();
m_TDoFArray = new Vector3[numTDof];
for (int i = 0; i < numTDof; i++)
{
m_TDoFArray[i] = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4();//5.4 and up
m_TDoFArray[i] = version >= (5, 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4();//5.4 and up
}
}
}
@@ -460,6 +502,8 @@ namespace AssetStudio
public uint[] data;
public uint curveCount;
public StreamedClip() { }
public StreamedClip(ObjectReader reader)
{
data = reader.ReadUInt32Array();
@@ -475,6 +519,8 @@ namespace AssetStudio
public float outSlope;
public float inSlope;
public StreamedCurveKey() { }
public StreamedCurveKey(BinaryReader reader)
{
index = reader.ReadInt32();
@@ -506,6 +552,8 @@ namespace AssetStudio
public float time;
public StreamedCurveKey[] keyList;
public StreamedFrame() { }
public StreamedFrame(BinaryReader reader)
{
time = reader.ReadSingle();
@@ -561,6 +609,8 @@ namespace AssetStudio
public float m_BeginTime;
public float[] m_SampleArray;
public DenseClip() { }
public DenseClip(ObjectReader reader)
{
m_FrameCount = reader.ReadInt32();
@@ -575,6 +625,8 @@ namespace AssetStudio
{
public float[] data;
public ConstantClip() { }
public ConstantClip(ObjectReader reader)
{
data = reader.ReadSingleArray();
@@ -588,11 +640,13 @@ namespace AssetStudio
public uint m_Type;
public uint m_Index;
public ValueConstant() { }
public ValueConstant(ObjectReader reader)
{
var version = reader.version;
m_ID = reader.ReadUInt32();
if (version[0] < 5 || (version[0] == 5 && version[1] < 5))//5.5 down
if (version < (5, 5)) //5.5 down
{
m_TypeID = reader.ReadUInt32();
}
@@ -605,6 +659,8 @@ namespace AssetStudio
{
public ValueConstant[] m_ValueArray;
public ValueArrayConstant() { }
public ValueArrayConstant(ObjectReader reader)
{
int numVals = reader.ReadInt32();
@@ -616,6 +672,18 @@ namespace AssetStudio
}
}
public class OffsetPtr
{
public Clip data;
public OffsetPtr() { }
public OffsetPtr(ObjectReader reader)
{
data = new Clip(reader);
}
}
public class Clip
{
public StreamedClip m_StreamedClip;
@@ -623,16 +691,18 @@ namespace AssetStudio
public ConstantClip m_ConstantClip;
public ValueArrayConstant m_Binding;
public Clip() { }
public Clip(ObjectReader reader)
{
var version = reader.version;
m_StreamedClip = new StreamedClip(reader);
m_DenseClip = new DenseClip(reader);
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
if (version >= (4, 3)) //4.3 and up
{
m_ConstantClip = new ConstantClip(reader);
}
if (version[0] < 2018 || (version[0] == 2018 && version[1] < 3)) //2018.3 down
if (version < (2018, 3)) //2018.3 down
{
m_Binding = new ValueArrayConstant(reader);
}
@@ -688,6 +758,8 @@ namespace AssetStudio
public float m_Start;
public float m_Stop;
public ValueDelta() { }
public ValueDelta(ObjectReader reader)
{
m_Start = reader.ReadSingle();
@@ -705,7 +777,7 @@ namespace AssetStudio
public xform m_MotionStartX;
public xform m_MotionStopX;
public Vector3 m_AverageSpeed;
public Clip m_Clip;
public OffsetPtr m_Clip;
public float m_StartTime;
public float m_StopTime;
public float m_OrientationOffsetY;
@@ -727,24 +799,26 @@ namespace AssetStudio
public bool m_KeepOriginalPositionXZ;
public bool m_HeightFromFeet;
public ClipMuscleConstant() { }
public ClipMuscleConstant(ObjectReader reader)
{
var version = reader.version;
m_DeltaPose = new HumanPose(reader);
m_StartX = new xform(reader);
if (version[0] > 5 || (version[0] == 5 && version[1] >= 5))//5.5 and up
if (version >= (5, 5)) //5.5 and up
{
m_StopX = new xform(reader);
}
m_LeftFootStartX = new xform(reader);
m_RightFootStartX = new xform(reader);
if (version[0] < 5)//5.0 down
if (version < 5)//5.0 down
{
m_MotionStartX = new xform(reader);
m_MotionStopX = new xform(reader);
}
m_AverageSpeed = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4();//5.4 and up
m_Clip = new Clip(reader);
m_AverageSpeed = version >= (5, 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4();//5.4 and up
m_Clip = new OffsetPtr(reader);
m_StartTime = reader.ReadSingle();
m_StopTime = reader.ReadSingle();
m_OrientationOffsetY = reader.ReadSingle();
@@ -753,7 +827,7 @@ namespace AssetStudio
m_AverageAngularSpeed = reader.ReadSingle();
m_IndexArray = reader.ReadInt32Array();
if (version[0] < 4 || (version[0] == 4 && version[1] < 3)) //4.3 down
if (version < (4, 3)) //4.3 down
{
var m_AdditionalCurveIndexArray = reader.ReadInt32Array();
}
@@ -763,13 +837,13 @@ namespace AssetStudio
{
m_ValueArrayDelta[i] = new ValueDelta(reader);
}
if (version[0] > 5 || (version[0] == 5 && version[1] >= 3))//5.3 and up
if (version >= (5, 3))//5.3 and up
{
m_ValueArrayReferencePose = reader.ReadSingleArray();
}
m_Mirror = reader.ReadBoolean();
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
if (version >= (4, 3)) //4.3 and up
{
m_LoopTime = reader.ReadBoolean();
}
@@ -777,7 +851,7 @@ namespace AssetStudio
m_LoopBlendOrientation = reader.ReadBoolean();
m_LoopBlendPositionY = reader.ReadBoolean();
m_LoopBlendPositionXZ = reader.ReadBoolean();
if (version[0] > 5 || (version[0] == 5 && version[1] >= 5))//5.5 and up
if (version >= (5, 5))//5.5 and up
{
m_StartAtOrigin = reader.ReadBoolean();
}
@@ -807,7 +881,7 @@ namespace AssetStudio
path = reader.ReadUInt32();
attribute = reader.ReadUInt32();
script = new PPtr<Object>(reader);
if (version[0] > 5 || (version[0] == 5 && version[1] >= 6)) //5.6 and up
if (version >= (5, 6)) //5.6 and up
{
typeID = (ClassIDType)reader.ReadInt32();
}
@@ -817,7 +891,7 @@ namespace AssetStudio
}
customType = reader.ReadByte();
isPPtrCurve = reader.ReadByte();
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 1)) //2022.1 and up
if (version >= (2022, 1)) //2022.1 and up
{
isIntCurve = reader.ReadByte();
}
@@ -895,6 +969,8 @@ namespace AssetStudio
public int intParameter;
public int messageOptions;
public AnimationEvent() { }
public AnimationEvent(ObjectReader reader)
{
var version = reader.version;
@@ -904,7 +980,7 @@ namespace AssetStudio
data = reader.ReadAlignedString();
objectReferenceParameter = new PPtr<Object>(reader);
floatParameter = reader.ReadSingle();
if (version[0] >= 3) //3 and up
if (version >= 3) //3 and up
{
intParameter = reader.ReadInt32();
}
@@ -940,14 +1016,40 @@ namespace AssetStudio
public AnimationClipBindingConstant m_ClipBindingConstant;
public AnimationEvent[] m_Events;
public AnimationClip() { }
public AnimationClip(ObjectReader reader, IDictionary typeDict) : base(reader)
{
var parsedAnimClip = JsonConvert.DeserializeObject<AnimationClip>(JsonConvert.SerializeObject(typeDict));
m_AnimationType = parsedAnimClip.m_AnimationType;
m_Legacy = parsedAnimClip.m_Legacy;
m_Compressed = parsedAnimClip.m_Compressed;
m_UseHighQualityCurve = parsedAnimClip.m_UseHighQualityCurve;
m_RotationCurves = parsedAnimClip.m_RotationCurves;
m_CompressedRotationCurves = parsedAnimClip.m_CompressedRotationCurves;
m_EulerCurves = parsedAnimClip.m_EulerCurves;
m_PositionCurves = parsedAnimClip.m_PositionCurves;
m_ScaleCurves = parsedAnimClip.m_ScaleCurves;
m_FloatCurves = parsedAnimClip.m_FloatCurves;
m_PPtrCurves = parsedAnimClip.m_PPtrCurves;
m_SampleRate = parsedAnimClip.m_SampleRate;
m_WrapMode = parsedAnimClip.m_WrapMode;
m_Bounds = parsedAnimClip.m_Bounds;
m_MuscleClipSize = parsedAnimClip.m_MuscleClipSize;
m_MuscleClip = parsedAnimClip.m_MuscleClip;
m_ClipBindingConstant = parsedAnimClip.m_ClipBindingConstant;
m_Events = parsedAnimClip.m_Events;
typeDict.Clear();
}
public AnimationClip(ObjectReader reader) : base(reader)
{
if (version[0] >= 5)//5.0 and up
if (version >= 5)//5.0 and up
{
m_Legacy = reader.ReadBoolean();
}
else if (version[0] >= 4)//4.0 and up
else if (version >= 4)//4.0 and up
{
m_AnimationType = (AnimationType)reader.ReadInt32();
if (m_AnimationType == AnimationType.Legacy)
@@ -958,7 +1060,7 @@ namespace AssetStudio
m_Legacy = true;
}
m_Compressed = reader.ReadBoolean();
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3))//4.3 and up
if (version >= (4, 3))//4.3 and up
{
m_UseHighQualityCurve = reader.ReadBoolean();
}
@@ -977,7 +1079,7 @@ namespace AssetStudio
m_CompressedRotationCurves[i] = new CompressedAnimationCurve(reader);
}
if (version[0] > 5 || (version[0] == 5 && version[1] >= 3))//5.3 and up
if (version >= (5, 3))//5.3 and up
{
int numEulerCurves = reader.ReadInt32();
m_EulerCurves = new Vector3Curve[numEulerCurves];
@@ -1008,7 +1110,7 @@ namespace AssetStudio
m_FloatCurves[i] = new FloatCurve(reader);
}
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
if (version >= (4, 3)) //4.3 and up
{
int numPtrCurves = reader.ReadInt32();
m_PPtrCurves = new PPtrCurve[numPtrCurves];
@@ -1020,20 +1122,20 @@ namespace AssetStudio
m_SampleRate = reader.ReadSingle();
m_WrapMode = reader.ReadInt32();
if (version[0] > 3 || (version[0] == 3 && version[1] >= 4)) //3.4 and up
if (version >= (3, 4)) //3.4 and up
{
m_Bounds = new AABB(reader);
}
if (version[0] >= 4)//4.0 and up
if (version >= 4)//4.0 and up
{
m_MuscleClipSize = reader.ReadUInt32();
m_MuscleClip = new ClipMuscleConstant(reader);
}
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
if (version >= (4, 3)) //4.3 and up
{
m_ClipBindingConstant = new AnimationClipBindingConstant(reader);
}
if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 3)) //2018.3 and up
if (version >= (2018, 3)) //2018.3 and up
{
var m_HasGenericRootTransform = reader.ReadBoolean();
var m_HasMotionFloatCurves = reader.ReadBoolean();
@@ -1045,7 +1147,7 @@ namespace AssetStudio
{
m_Events[i] = new AnimationEvent(reader);
}
if (version[0] >= 2017) //2017 and up
if (version >= 2017) //2017 and up
{
reader.AlignStream();
}

View File

@@ -17,47 +17,47 @@ namespace AssetStudio
m_Controller = new PPtr<RuntimeAnimatorController>(reader);
var m_CullingMode = reader.ReadInt32();
if (version[0] > 4 || (version[0] == 4 && version[1] >= 5)) //4.5 and up
if (version >= (4, 5)) //4.5 and up
{
var m_UpdateMode = reader.ReadInt32();
}
var m_ApplyRootMotion = reader.ReadBoolean();
if (version[0] == 4 && version[1] >= 5) //4.5 and up - 5.0 down
if (version == 4 && version.Minor >= 5) //4.5 and up - 5.0 down
{
reader.AlignStream();
}
if (version[0] >= 5) //5.0 and up
if (version >= 5) //5.0 and up
{
var m_LinearVelocityBlending = reader.ReadBoolean();
if (version[0] > 2021 || (version[0] == 2021 && version[1] >= 2)) //2021.2 and up
if (version >= (2021, 2)) //2021.2 and up
{
var m_StabilizeFeet = reader.ReadBoolean();
}
reader.AlignStream();
}
if (version[0] < 4 || (version[0] == 4 && version[1] < 5)) //4.5 down
if (version < (4, 5)) //4.5 down
{
var m_AnimatePhysics = reader.ReadBoolean();
}
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
if (version >= (4, 3)) //4.3 and up
{
m_HasTransformHierarchy = reader.ReadBoolean();
}
if (version[0] > 4 || (version[0] == 4 && version[1] >= 5)) //4.5 and up
if (version >= (4, 5)) //4.5 and up
{
var m_AllowConstantClipSamplingOptimization = reader.ReadBoolean();
}
if (version[0] >= 5 && version[0] < 2018) //5.0 and up - 2018 down
if (version.IsInRange(5, 2018)) //5.0 and up - 2018 down
{
reader.AlignStream();
}
if (version[0] >= 2018) //2018 and up
if (version >= 2018) //2018 and up
{
var m_KeepAnimatorControllerStateOnDisable = reader.ReadBoolean();
reader.AlignStream();

View File

@@ -17,7 +17,7 @@ namespace AssetStudio
word0 = reader.ReadUInt32();
word1 = reader.ReadUInt32();
if (version[0] > 5 || (version[0] == 5 && version[1] >= 2)) //5.2 and up
if (version >= (5, 2)) //5.2 and up
{
word2 = reader.ReadUInt32();
}
@@ -73,12 +73,12 @@ namespace AssetStudio
m_SkeletonMask = new SkeletonMask(reader);
m_Binding = reader.ReadUInt32();
m_LayerBlendingMode = reader.ReadInt32();
if (version[0] > 4 || (version[0] == 4 && version[1] >= 2)) //4.2 and up
if (version >= (4, 2)) //4.2 and up
{
m_DefaultWeight = reader.ReadSingle();
}
m_IKPass = reader.ReadBoolean();
if (version[0] > 4 || (version[0] == 4 && version[1] >= 2)) //4.2 and up
if (version >= (4, 2)) //4.2 and up
{
m_SyncedLayerAffectsTiming = reader.ReadBoolean();
}
@@ -131,7 +131,7 @@ namespace AssetStudio
}
m_DestinationState = reader.ReadUInt32();
if (version[0] >= 5) //5.0 and up
if (version >= 5) //5.0 and up
{
m_FullPathID = reader.ReadUInt32();
}
@@ -140,7 +140,7 @@ namespace AssetStudio
m_UserID = reader.ReadUInt32();
m_TransitionDuration = reader.ReadSingle();
m_TransitionOffset = reader.ReadSingle();
if (version[0] >= 5) //5.0 and up
if (version >= 5) //5.0 and up
{
m_ExitTime = reader.ReadSingle();
m_HasExitTime = reader.ReadBoolean();
@@ -154,7 +154,7 @@ namespace AssetStudio
m_Atomic = reader.ReadBoolean();
}
if (version[0] > 4 || (version[0] == 4 && version[1] >= 5)) //4.5 and up
if (version >= (4, 5)) //4.5 and up
{
m_CanTransitionToSelf = reader.ReadBoolean();
}
@@ -252,43 +252,41 @@ namespace AssetStudio
{
var version = reader.version;
if (version[0] > 4 || (version[0] == 4 && version[1] >= 1)) //4.1 and up
if (version >= (4, 1)) //4.1 and up
{
m_BlendType = reader.ReadUInt32();
}
m_BlendEventID = reader.ReadUInt32();
if (version[0] > 4 || (version[0] == 4 && version[1] >= 1)) //4.1 and up
if (version >= (4, 1)) //4.1 and up
{
m_BlendEventYID = reader.ReadUInt32();
}
m_ChildIndices = reader.ReadUInt32Array();
if (version[0] < 4 || (version[0] == 4 && version[1] < 1)) //4.1 down
if (version < (4, 1)) //4.1 down
{
m_ChildThresholdArray = reader.ReadSingleArray();
}
if (version[0] > 4 || (version[0] == 4 && version[1] >= 1)) //4.1 and up
if (version >= (4, 1)) //4.1 and up
{
m_Blend1dData = new Blend1dDataConstant(reader);
m_Blend2dData = new Blend2dDataConstant(reader);
}
if (version[0] >= 5) //5.0 and up
if (version >= 5) //5.0 and up
{
m_BlendDirectData = new BlendDirectDataConstant(reader);
}
m_ClipID = reader.ReadUInt32();
if (version[0] == 4 && version[1] >= 5) //4.5 - 5.0
if (version == 4 && version.Minor >= 5) //4.5 - 5.0
{
m_ClipIndex = reader.ReadUInt32();
}
m_Duration = reader.ReadSingle();
if (version[0] > 4
|| (version[0] == 4 && version[1] > 1)
|| (version[0] == 4 && version[1] == 1 && version[2] >= 3)) //4.1.3 and up
if (version >= (4, 1, 3)) //4.1.3 and up
{
m_CycleOffset = reader.ReadSingle();
m_Mirror = reader.ReadBoolean();
@@ -313,7 +311,7 @@ namespace AssetStudio
m_NodeArray[i] = new BlendTreeNodeConstant(reader);
}
if (version[0] < 4 || (version[0] == 4 && version[1] < 5)) //4.5 down
if (version < (4, 5)) //4.5 down
{
m_BlendEventArrayConstant = new ValueArrayConstant(reader);
}
@@ -354,7 +352,7 @@ namespace AssetStudio
m_BlendTreeConstantIndexArray = reader.ReadInt32Array();
if (version[0] < 5 || (version[0] == 5 && version[1] < 2)) //5.2 down
if (version < (5, 2)) //5.2 down
{
int numInfos = reader.ReadInt32();
m_LeafInfoArray = new LeafInfoConstant[numInfos];
@@ -372,41 +370,41 @@ namespace AssetStudio
}
m_NameID = reader.ReadUInt32();
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
if (version >= (4, 3)) //4.3 and up
{
m_PathID = reader.ReadUInt32();
}
if (version[0] >= 5) //5.0 and up
if (version >= 5) //5.0 and up
{
m_FullPathID = reader.ReadUInt32();
}
m_TagID = reader.ReadUInt32();
if (version[0] > 5 || (version[0] == 5 && version[1] >= 1)) //5.1 and up
if (version >= (5, 1)) //5.1 and up
{
m_SpeedParamID = reader.ReadUInt32();
m_MirrorParamID = reader.ReadUInt32();
m_CycleOffsetParamID = reader.ReadUInt32();
}
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 2)) //2017.2 and up
if (version >= (2017, 2)) //2017.2 and up
{
var m_TimeParamID = reader.ReadUInt32();
}
m_Speed = reader.ReadSingle();
if (version[0] > 4 || (version[0] == 4 && version[1] >= 1)) //4.1 and up
if (version >= (4, 1)) //4.1 and up
{
m_CycleOffset = reader.ReadSingle();
}
m_IKOnFeet = reader.ReadBoolean();
if (version[0] >= 5) //5.0 and up
if (version >= 5) //5.0 and up
{
m_WriteDefaultValues = reader.ReadBoolean();
}
m_Loop = reader.ReadBoolean();
if (version[0] > 4 || (version[0] == 4 && version[1] >= 1)) //4.1 and up
if (version >= (4, 1)) //4.1 and up
{
m_Mirror = reader.ReadBoolean();
}
@@ -480,7 +478,7 @@ namespace AssetStudio
m_AnyStateTransitionConstantArray[i] = new TransitionConstant(reader);
}
if (version[0] >= 5) //5.0 and up
if (version >= 5) //5.0 and up
{
int numSelectors = reader.ReadInt32();
m_SelectorStateConstantArray = new SelectorStateConstant[numSelectors];
@@ -509,7 +507,7 @@ namespace AssetStudio
{
var version = reader.version;
if (version[0] < 5 || (version[0] == 5 && version[1] < 5)) //5.5 down
if (version < (5, 5)) //5.5 down
{
m_BoolValues = reader.ReadBooleanArray();
reader.AlignStream();
@@ -517,7 +515,7 @@ namespace AssetStudio
m_FloatValues = reader.ReadSingleArray();
}
if (version[0] < 4 || (version[0] == 4 && version[1] < 3)) //4.3 down
if (version < (4, 3)) //4.3 down
{
m_VectorValues = reader.ReadVector4Array();
}
@@ -527,7 +525,7 @@ namespace AssetStudio
m_PositionValues = new Vector3[numPosValues];
for (int i = 0; i < numPosValues; i++)
{
m_PositionValues[i] = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4(); //5.4 and up
m_PositionValues[i] = version >= (5, 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4(); //5.4 and up
}
m_QuaternionValues = reader.ReadVector4Array();
@@ -536,10 +534,10 @@ namespace AssetStudio
m_ScaleValues = new Vector3[numScaleValues];
for (int i = 0; i < numScaleValues; i++)
{
m_ScaleValues[i] = version[0] > 5 || (version[0] == 5 && version[1] >= 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4(); //5.4 and up
m_ScaleValues[i] = version >= (5, 4) ? reader.ReadVector3() : (Vector3)reader.ReadVector4(); //5.4 and up
}
if (version[0] > 5 || (version[0] == 5 && version[1] >= 5)) //5.5 and up
if (version >= (5, 5)) //5.5 and up
{
m_FloatValues = reader.ReadSingleArray();
m_IntValues = reader.ReadInt32Array();

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,57 @@ 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 == (5, 4)) //5.4.x
{
var m_ClassVersionMapSize = reader.ReadInt32();
for (var i = 0; i < m_ClassVersionMapSize; i++)
{
var first = reader.ReadInt32();
var second = reader.ReadInt32();
}
}
if (version >= (4, 2)) //4.2 and up
{
var m_RuntimeCompatibility = reader.ReadUInt32();
}
if (version >= 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

@@ -33,7 +33,7 @@ namespace AssetStudio
public AudioClip(ObjectReader reader) : base(reader)
{
if (version[0] < 5)
if (version < 5)
{
m_Format = reader.ReadInt32();
m_Type = (FMODSoundType)reader.ReadInt32();
@@ -41,7 +41,7 @@ namespace AssetStudio
m_UseHardware = reader.ReadBoolean();
reader.AlignStream();
if (version[0] >= 4 || (version[0] == 3 && version[1] >= 2)) //3.2.0 to 5
if (version >= (3, 2)) //3.2.0 to 5
{
int m_Stream = reader.ReadInt32();
m_Size = reader.ReadInt32();
@@ -95,7 +95,7 @@ namespace AssetStudio
public enum FMODSoundType
{
UNKNOWN = 0,
ACC = 1,
AAC = 1,
AIFF = 2,
ASF = 3,
AT3 = 4,

View File

@@ -23,7 +23,7 @@ namespace AssetStudio
public Limit(ObjectReader reader)
{
var version = reader.version;
if (version[0] > 5 || (version[0] == 5 && version[1] >= 4))//5.4 and up
if (version >= (5, 4))//5.4 and up
{
m_Min = reader.ReadVector3();
m_Max = reader.ReadVector3();
@@ -50,7 +50,7 @@ namespace AssetStudio
var version = reader.version;
m_PreQ = reader.ReadVector4();
m_PostQ = reader.ReadVector4();
if (version[0] > 5 || (version[0] == 5 && version[1] >= 4)) //5.4 and up
if (version >= (5, 4)) //5.4 and up
{
m_Sgn = reader.ReadVector3();
}
@@ -189,7 +189,7 @@ namespace AssetStudio
m_LeftHand = new Hand(reader);
m_RightHand = new Hand(reader);
if (version[0] < 2018 || (version[0] == 2018 && version[1] < 2)) //2018.2 down
if (version < (2018, 2)) //2018.2 down
{
int numHandles = reader.ReadInt32();
m_Handles = new Handle[numHandles];
@@ -210,7 +210,7 @@ namespace AssetStudio
m_HumanBoneMass = reader.ReadSingleArray();
if (version[0] < 2018 || (version[0] == 2018 && version[1] < 2)) //2018.2 down
if (version < (2018, 2)) //2018.2 down
{
m_ColliderIndex = reader.ReadInt32Array();
}
@@ -225,7 +225,7 @@ namespace AssetStudio
m_FeetSpacing = reader.ReadSingle();
m_HasLeftHand = reader.ReadBoolean();
m_HasRightHand = reader.ReadBoolean();
if (version[0] > 5 || (version[0] == 5 && version[1] >= 2)) //5.2 and up
if (version >= (5, 2)) //5.2 and up
{
m_HasTDoF = reader.ReadBoolean();
}
@@ -254,7 +254,7 @@ namespace AssetStudio
m_AvatarSkeleton = new Skeleton(reader);
m_AvatarSkeletonPose = new SkeletonPose(reader);
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
if (version >= (4, 3)) //4.3 and up
{
m_DefaultPose = new SkeletonPose(reader);
@@ -265,7 +265,7 @@ namespace AssetStudio
m_HumanSkeletonIndexArray = reader.ReadInt32Array();
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
if (version >= (4, 3)) //4.3 and up
{
m_HumanSkeletonReverseIndexArray = reader.ReadInt32Array();
}
@@ -273,7 +273,7 @@ namespace AssetStudio
m_RootMotionBoneIndex = reader.ReadInt32();
m_RootMotionBoneX = new xform(reader);
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
if (version >= (4, 3)) //4.3 and up
{
m_RootMotionSkeleton = new Skeleton(reader);
m_RootMotionSkeletonPose = new SkeletonPose(reader);

View File

@@ -7,6 +7,8 @@ namespace AssetStudio
{
public abstract class EditorExtension : Object
{
protected EditorExtension() { }
protected EditorExtension(ObjectReader reader) : base(reader)
{
if (platform == BuildTarget.NoTarget)

View File

@@ -11,7 +11,7 @@ namespace AssetStudio
public Font(ObjectReader reader) : base(reader)
{
if ((version[0] == 5 && version[1] >= 5) || version[0] > 5)//5.5 and up
if (version >= (5, 5))//5.5 and up
{
var m_LineSpacing = reader.ReadSingle();
var m_DefaultMaterial = new PPtr<Material>(reader);
@@ -43,7 +43,7 @@ namespace AssetStudio
{
int m_AsciiStartOffset = reader.ReadInt32();
if (version[0] <= 3)
if (version <= 3)
{
int m_FontCountX = reader.ReadInt32();
int m_FontCountY = reader.ReadInt32();
@@ -52,7 +52,7 @@ namespace AssetStudio
float m_Kerning = reader.ReadSingle();
float m_LineSpacing = reader.ReadSingle();
if (version[0] <= 3)
if (version <= 3)
{
int m_PerCharacterKerning_size = reader.ReadInt32();
for (int i = 0; i < m_PerCharacterKerning_size; i++)
@@ -86,7 +86,7 @@ namespace AssetStudio
float vertheight = reader.ReadSingle();
float width = reader.ReadSingle();
if (version[0] >= 4)
if (version >= 4)
{
var flipped = reader.ReadBoolean();
reader.AlignStream();
@@ -103,7 +103,7 @@ namespace AssetStudio
float second = reader.ReadSingle();
}
if (version[0] <= 3)
if (version <= 3)
{
var m_GridFont = reader.ReadBoolean();
reader.AlignStream();

View File

@@ -0,0 +1,31 @@
namespace AssetStudio
{
public class GLTextureSettings
{
public int m_FilterMode;
public int m_Aniso;
public float m_MipBias;
public int m_WrapMode;
public GLTextureSettings() { }
public GLTextureSettings(ObjectReader reader)
{
var version = reader.version;
m_FilterMode = reader.ReadInt32();
m_Aniso = reader.ReadInt32();
m_MipBias = reader.ReadSingle();
if (version >= 2017)//2017.x and up
{
m_WrapMode = reader.ReadInt32(); //m_WrapU
int m_WrapV = reader.ReadInt32();
int m_WrapW = reader.ReadInt32();
}
else
{
m_WrapMode = reader.ReadInt32();
}
}
}
}

View File

@@ -23,7 +23,7 @@ namespace AssetStudio
m_Components = new PPtr<Component>[m_Component_size];
for (int i = 0; i < m_Component_size; i++)
{
if ((version[0] == 5 && version[1] < 5) || version[0] < 5) //5.5 down
if (version < (5, 5)) //5.5 down
{
int first = reader.ReadInt32();
}

View File

@@ -0,0 +1,711 @@
namespace AssetStudio
{
public enum GraphicsFormat
{
/// <summary>
/// The format is not specified.
/// </summary>
None,
/// <summary>
/// A one-component, 8-bit unsigned normalized format that has a single 8-bit R component stored with sRGB nonlinear encoding.
/// </summary>
R8_SRGB,
/// <summary>
/// A two-component, 16-bit unsigned normalized format that has an 8-bit R component stored with sRGB nonlinear encoding in byte 0, and an 8-bit G component stored with sRGB nonlinear encoding in byte 1.
/// </summary>
R8G8_SRGB,
/// <summary>
/// A three-component, 24-bit unsigned normalized format that has an 8-bit R component stored with sRGB nonlinear encoding in byte 0, an 8-bit G component stored with sRGB nonlinear encoding in byte 1, and an 8-bit B component stored with sRGB nonlinear encoding in byte 2.
/// </summary>
R8G8B8_SRGB,
/// <summary>
/// A four-component, 32-bit unsigned normalized format that has an 8-bit R component stored with sRGB nonlinear encoding in byte 0, an 8-bit G component stored with sRGB nonlinear encoding in byte 1, an 8-bit B component stored with sRGB nonlinear encoding in byte 2, and an 8-bit A component in byte 3.
/// </summary>
R8G8B8A8_SRGB,
/// <summary>
/// A one-component, 8-bit unsigned normalized format that has a single 8-bit R component.
/// </summary>
R8_UNorm,
/// <summary>
/// A two-component, 16-bit unsigned normalized format that has an 8-bit R component stored with sRGB nonlinear encoding in byte 0, and an 8-bit G component stored with sRGB nonlinear encoding in byte 1.
/// </summary>
R8G8_UNorm,
/// <summary>
/// A three-component, 24-bit unsigned normalized format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, and an 8-bit B component in byte 2.
/// </summary>
R8G8B8_UNorm,
/// <summary>
/// A four-component, 32-bit unsigned normalized format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, an 8-bit B component in byte 2, and an 8-bit A component in byte 3.
/// </summary>
R8G8B8A8_UNorm,
/// <summary>
/// A one-component, 8-bit signed normalized format that has a single 8-bit R component.
/// </summary>
R8_SNorm,
/// <summary>
/// A two-component, 16-bit signed normalized format that has an 8-bit R component stored with sRGB nonlinear encoding in byte 0, and an 8-bit G component stored with sRGB nonlinear encoding in byte 1.
/// </summary>
R8G8_SNorm,
/// <summary>
/// A three-component, 24-bit signed normalized format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, and an 8-bit B component in byte 2.
/// </summary>
R8G8B8_SNorm,
/// <summary>
/// A four-component, 32-bit signed normalized format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, an 8-bit B component in byte 2, and an 8-bit A component in byte 3.
/// </summary>
R8G8B8A8_SNorm,
/// <summary>
/// A one-component, 8-bit unsigned integer format that has a single 8-bit R component.
/// </summary>
R8_UInt,
/// <summary>
/// A two-component, 16-bit unsigned integer format that has an 8-bit R component in byte 0, and an 8-bit G component in byte 1.
/// </summary>
R8G8_UInt,
/// <summary>
/// A three-component, 24-bit unsigned integer format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, and an 8-bit B component in byte 2.
/// </summary>
R8G8B8_UInt,
/// <summary>
/// A four-component, 32-bit unsigned integer format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, an 8-bit B component in byte 2, and an 8-bit A component in byte 3.
/// </summary>
R8G8B8A8_UInt,
/// <summary>
/// A one-component, 8-bit signed integer format that has a single 8-bit R component.
/// </summary>
R8_SInt,
/// <summary>
/// A two-component, 16-bit signed integer format that has an 8-bit R component in byte 0, and an 8-bit G component in byte 1.
/// </summary>
R8G8_SInt,
/// <summary>
/// A three-component, 24-bit signed integer format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, and an 8-bit B component in byte 2.
/// </summary>
R8G8B8_SInt,
/// <summary>
/// A four-component, 32-bit signed integer format that has an 8-bit R component in byte 0, an 8-bit G component in byte 1, an 8-bit B component in byte 2, and an 8-bit A component in byte 3.
/// </summary>
R8G8B8A8_SInt,
/// <summary>
/// A one-component, 16-bit unsigned normalized format that has a single 16-bit R component.
/// </summary>
R16_UNorm,
/// <summary>
/// A two-component, 32-bit unsigned normalized format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3.
/// </summary>
R16G16_UNorm,
/// <summary>
/// A three-component, 48-bit unsigned normalized format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, and a 16-bit B component in bytes 4..5.
/// </summary>
R16G16B16_UNorm,
/// <summary>
/// A four-component, 64-bit unsigned normalized format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7.
/// </summary>
R16G16B16A16_UNorm,
/// <summary>
/// A one-component, 16-bit signed normalized format that has a single 16-bit R component.
/// </summary>
R16_SNorm,
/// <summary>
/// A two-component, 32-bit signed normalized format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3.
/// </summary>
R16G16_SNorm,
/// <summary>
/// A three-component, 48-bit signed normalized format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, and a 16-bit B component in bytes 4..5.
/// </summary>
R16G16B16_SNorm,
/// <summary>
/// A four-component, 64-bit signed normalized format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7.
/// </summary>
R16G16B16A16_SNorm,
/// <summary>
/// A one-component, 16-bit unsigned integer format that has a single 16-bit R component.
/// </summary>
R16_UInt,
/// <summary>
/// A two-component, 32-bit unsigned integer format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3.
/// </summary>
R16G16_UInt,
/// <summary>
/// A three-component, 48-bit unsigned integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, and a 16-bit B component in bytes 4..5.
/// </summary>
R16G16B16_UInt,
/// <summary>
/// A four-component, 64-bit unsigned integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7.
/// </summary>
R16G16B16A16_UInt,
/// <summary>
/// A one-component, 16-bit signed integer format that has a single 16-bit R component.
/// </summary>
R16_SInt,
/// <summary>
/// A two-component, 32-bit signed integer format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3.
/// </summary>
R16G16_SInt,
/// <summary>
/// A three-component, 48-bit signed integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, and a 16-bit B component in bytes 4..5.
/// </summary>
R16G16B16_SInt,
/// <summary>
/// A four-component, 64-bit signed integer format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7.
/// </summary>
R16G16B16A16_SInt,
/// <summary>
/// A one-component, 32-bit unsigned integer format that has a single 32-bit R component.
/// </summary>
R32_UInt,
/// <summary>
/// A two-component, 64-bit unsigned integer format that has a 32-bit R component in bytes 0..3, and a 32-bit G component in bytes 4..7.
/// </summary>
R32G32_UInt,
/// <summary>
/// A three-component, 96-bit unsigned integer format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, and a 32-bit B component in bytes 8..11.
/// </summary>
R32G32B32_UInt,
/// <summary>
/// A four-component, 128-bit unsigned integer format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, a 32-bit B component in bytes 8..11, and a 32-bit A component in bytes 12..15.
/// </summary>
R32G32B32A32_UInt,
/// <summary>
/// A one-component, 32-bit signed integer format that has a single 32-bit R component.
/// </summary>
R32_SInt,
/// <summary>
/// A two-component, 64-bit signed integer format that has a 32-bit R component in bytes 0..3, and a 32-bit G component in bytes 4..7.
/// </summary>
R32G32_SInt,
/// <summary>
/// A three-component, 96-bit signed integer format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, and a 32-bit B component in bytes 8..11.
/// </summary>
R32G32B32_SInt,
/// <summary>
/// A four-component, 128-bit signed integer format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, a 32-bit B component in bytes 8..11, and a 32-bit A component in bytes 12..15.
/// </summary>
R32G32B32A32_SInt,
/// <summary>
/// A one-component, 16-bit signed floating-point format that has a single 16-bit R component.
/// </summary>
R16_SFloat,
/// <summary>
/// A two-component, 32-bit signed floating-point format that has a 16-bit R component in bytes 0..1, and a 16-bit G component in bytes 2..3.
/// </summary>
R16G16_SFloat,
/// <summary>
/// A three-component, 48-bit signed floating-point format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, and a 16-bit B component in bytes 4..5.
/// </summary>
R16G16B16_SFloat,
/// <summary>
/// A four-component, 64-bit signed floating-point format that has a 16-bit R component in bytes 0..1, a 16-bit G component in bytes 2..3, a 16-bit B component in bytes 4..5, and a 16-bit A component in bytes 6..7.
/// </summary>
R16G16B16A16_SFloat,
/// <summary>
/// A one-component, 32-bit signed floating-point format that has a single 32-bit R component.
/// </summary>
R32_SFloat,
/// <summary>
/// A two-component, 64-bit signed floating-point format that has a 32-bit R component in bytes 0..3, and a 32-bit G component in bytes 4..7.
/// </summary>
R32G32_SFloat,
/// <summary>
/// A three-component, 96-bit signed floating-point format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, and a 32-bit B component in bytes 8..11.
/// </summary>
R32G32B32_SFloat,
/// <summary>
/// A four-component, 128-bit signed floating-point format that has a 32-bit R component in bytes 0..3, a 32-bit G component in bytes 4..7, a 32-bit B component in bytes 8..11, and a 32-bit A component in bytes 12..15.
/// </summary>
R32G32B32A32_SFloat,
/// <summary>
/// A three-component, 24-bit unsigned normalized format that has an 8-bit B component stored with sRGB nonlinear encoding in byte 0, an 8-bit G component stored with sRGB nonlinear encoding in byte 1, and an 8-bit R component stored with sRGB nonlinear encoding in byte 2.
/// </summary>
B8G8R8_SRGB = 56,
/// <summary>
/// A four-component, 32-bit unsigned normalized format that has an 8-bit B component stored with sRGB nonlinear encoding in byte 0, an 8-bit G component stored with sRGB nonlinear encoding in byte 1, an 8-bit R component stored with sRGB nonlinear encoding in byte 2, and an 8-bit A component in byte 3.
/// </summary>
B8G8R8A8_SRGB,
/// <summary>
/// A three-component, 24-bit unsigned normalized format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, and an 8-bit R component in byte 2.
/// </summary>
B8G8R8_UNorm,
/// <summary>
/// A four-component, 32-bit unsigned normalized format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, an 8-bit R component in byte 2, and an 8-bit A component in byte 3.
/// </summary>
B8G8R8A8_UNorm,
/// <summary>
/// A three-component, 24-bit signed normalized format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, and an 8-bit R component in byte 2.
/// </summary>
B8G8R8_SNorm,
/// <summary>
/// A four-component, 32-bit signed normalized format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, an 8-bit R component in byte 2, and an 8-bit A component in byte 3.
/// </summary>
B8G8R8A8_SNorm,
/// <summary>
/// A three-component, 24-bit unsigned integer format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, and an 8-bit R component in byte 2
/// </summary>
B8G8R8_UInt,
/// <summary>
/// A four-component, 32-bit unsigned integer format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, an 8-bit R component in byte 2, and an 8-bit A component in byte 3.
/// </summary>
B8G8R8A8_UInt,
/// <summary>
/// A three-component, 24-bit signed integer format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, and an 8-bit R component in byte 2.
/// </summary>
B8G8R8_SInt,
/// <summary>
/// A four-component, 32-bit signed integer format that has an 8-bit B component in byte 0, an 8-bit G component in byte 1, an 8-bit R component in byte 2, and an 8-bit A component in byte 3.
/// </summary>
B8G8R8A8_SInt,
/// <summary>
/// A four-component, 16-bit packed unsigned normalized format that has a 4-bit R component in bits 12..15, a 4-bit G component in bits 8..11, a 4-bit B component in bits 4..7, and a 4-bit A component in bits 0..3.
/// </summary>
R4G4B4A4_UNormPack16,
/// <summary>
/// A four-component, 16-bit packed unsigned normalized format that has a 4-bit B component in bits 12..15, a 4-bit G component in bits 8..11, a 4-bit R component in bits 4..7, and a 4-bit A component in bits 0..3.
/// </summary>
B4G4R4A4_UNormPack16,
/// <summary>
/// A three-component, 16-bit packed unsigned normalized format that has a 5-bit R component in bits 11..15, a 6-bit G component in bits 5..10, and a 5-bit B component in bits 0..4.
/// </summary>
R5G6B5_UNormPack16,
/// <summary>
/// A three-component, 16-bit packed unsigned normalized format that has a 5-bit B component in bits 11..15, a 6-bit G component in bits 5..10, and a 5-bit R component in bits 0..4.
/// </summary>
B5G6R5_UNormPack16,
/// <summary>
/// A four-component, 16-bit packed unsigned normalized format that has a 5-bit R component in bits 11..15, a 5-bit G component in bits 6..10, a 5-bit B component in bits 1..5, and a 1-bit A component in bit 0.
/// </summary>
R5G5B5A1_UNormPack16,
/// <summary>
/// A four-component, 16-bit packed unsigned normalized format that has a 5-bit B component in bits 11..15, a 5-bit G component in bits 6..10, a 5-bit R component in bits 1..5, and a 1-bit A component in bit 0.
/// </summary>
B5G5R5A1_UNormPack16,
/// <summary>
/// A four-component, 16-bit packed unsigned normalized format that has a 1-bit A component in bit 15, a 5-bit R component in bits 10..14, a 5-bit G component in bits 5..9, and a 5-bit B component in bits 0..4.
/// </summary>
A1R5G5B5_UNormPack16,
/// <summary>
/// A three-component, 32-bit packed unsigned floating-point format that has a 5-bit shared exponent in bits 27..31, a 9-bit B component mantissa in bits 18..26, a 9-bit G component mantissa in bits 9..17, and a 9-bit R component mantissa in bits 0..8.
/// </summary>
E5B9G9R9_UFloatPack32,
/// <summary>
/// A three-component, 32-bit packed unsigned floating-point format that has a 10-bit B component in bits 22..31, an 11-bit G component in bits 11..21, an 11-bit R component in bits 0..10.
/// </summary>
B10G11R11_UFloatPack32,
/// <summary>
/// A four-component, 32-bit packed unsigned normalized format that has a 2-bit A component in bits 30..31, a 10-bit B component in bits 20..29, a 10-bit G component in bits 10..19, and a 10-bit R component in bits 0..9.
/// </summary>
A2B10G10R10_UNormPack32,
/// <summary>
/// A four-component, 32-bit packed unsigned integer format that has a 2-bit A component in bits 30..31, a 10-bit B component in bits 20..29, a 10-bit G component in bits 10..19, and a 10-bit R component in bits 0..9.
/// </summary>
A2B10G10R10_UIntPack32,
/// <summary>
/// A four-component, 32-bit packed signed integer format that has a 2-bit A component in bits 30..31, a 10-bit B component in bits 20..29, a 10-bit G component in bits 10..19, and a 10-bit R component in bits 0..9.
/// </summary>
A2B10G10R10_SIntPack32,
/// <summary>
/// A four-component, 32-bit packed unsigned normalized format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9.
/// </summary>
A2R10G10B10_UNormPack32,
/// <summary>
/// A four-component, 32-bit packed unsigned integer format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9.
/// </summary>
A2R10G10B10_UIntPack32,
/// <summary>
/// A four-component, 32-bit packed signed integer format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9.
/// </summary>
A2R10G10B10_SIntPack32,
/// <summary>
/// A four-component, 32-bit packed unsigned normalized format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. The components are gamma encoded and their values range from -0.5271 to 1.66894. The alpha component is clamped to either 0.0 or 1.0 on sampling, rendering, and writing operations.
/// </summary>
A2R10G10B10_XRSRGBPack32,
/// <summary>
/// A four-component, 32-bit packed unsigned normalized format that has a 2-bit A component in bits 30..31, a 10-bit R component in bits 20..29, a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. The components are linearly encoded and their values range from -0.752941 to 1.25098 (pre-expansion). The alpha component is clamped to either 0.0 or 1.0 on sampling, rendering, and writing operations.
/// </summary>
A2R10G10B10_XRUNormPack32,
/// <summary>
/// A four-component, 32-bit packed unsigned normalized format that has a 10-bit R component in bits 20..29, a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. The components are gamma encoded and their values range from -0.5271 to 1.66894. The alpha component is clamped to either 0.0 or 1.0 on sampling, rendering, and writing operations.
/// </summary>
R10G10B10_XRSRGBPack32,
/// <summary>
/// A four-component, 32-bit packed unsigned normalized format that has a 10-bit R component in bits 20..29, a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. The components are linearly encoded and their values range from -0.752941 to 1.25098 (pre-expansion).
/// </summary>
R10G10B10_XRUNormPack32,
/// <summary>
/// A four-component, 64-bit packed unsigned normalized format that has a 10-bit A component in bits 30..39, a 10-bit R component in bits 20..29, a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. The components are gamma encoded and their values range from -0.5271 to 1.66894. The alpha component is clamped to either 0.0 or 1.0 on sampling, rendering, and writing operations.
/// </summary>
A10R10G10B10_XRSRGBPack32,
/// <summary>
/// A four-component, 64-bit packed unsigned normalized format that has a 10-bit A component in bits 30..39, a 10-bit R component in bits 20..29, a 10-bit G component in bits 10..19, and a 10-bit B component in bits 0..9. The components are linearly encoded and their values range from -0.752941 to 1.25098 (pre-expansion). The alpha component is clamped to either 0.0 or 1.0 on sampling, rendering, and writing operations.
/// </summary>
A10R10G10B10_XRUNormPack32,
/// <summary>
/// A one-component, 16-bit unsigned normalized format that has a single 16-bit depth component.
/// </summary>
D16_UNorm = 90,
/// <summary>
/// A two-component, 32-bit format that has 24 unsigned normalized bits in the depth component and, optionally: 8 bits that are unused.
/// </summary>
D24_UNorm,
/// <summary>
/// A two-component, 32-bit packed format that has 8 unsigned integer bits in the stencil component, and 24 unsigned normalized bits in the depth component.
/// </summary>
D24_UNorm_S8_UInt,
/// <summary>
/// A one-component, 32-bit signed floating-point format that has 32-bits in the depth component.
/// </summary>
D32_SFloat,
/// <summary>
/// A two-component format that has 32 signed float bits in the depth component and 8 unsigned integer bits in the stencil component. There are optionally: 24-bits that are unused.
/// </summary>
D32_SFloat_S8_UInt,
/// <summary>
/// A one-component, 8-bit unsigned integer format that has 8-bits in the stencil component.
/// </summary>
S8_UInt,
/// <summary>
/// A three-component, block-compressed format (also known as BC1). Each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data with sRGB nonlinear encoding. This format has a 1 bit alpha channel.
/// </summary>
RGBA_DXT1_SRGB,
/// <summary>
/// A three-component, block-compressed format (also known as BC1). Each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data. This format has a 1 bit alpha channel.
/// </summary>
RGBA_DXT1_UNorm,
/// <summary>
/// A four-component, block-compressed format (also known as BC2) where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values with sRGB nonlinear encoding.
/// </summary>
RGBA_DXT3_SRGB,
/// <summary>
/// A four-component, block-compressed format (also known as BC2) where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values.
/// </summary>
RGBA_DXT3_UNorm,
/// <summary>
/// A four-component, block-compressed format (also known as BC3) where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values with sRGB nonlinear encoding.
/// </summary>
RGBA_DXT5_SRGB,
/// <summary>
/// A four-component, block-compressed format (also known as BC3) where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values.
/// </summary>
RGBA_DXT5_UNorm,
/// <summary>
/// A one-component, block-compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized red texel data.
/// </summary>
R_BC4_UNorm,
/// <summary>
/// A one-component, block-compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of signed normalized red texel data.
/// </summary>
R_BC4_SNorm,
/// <summary>
/// A two-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RG texel data with the first 64 bits encoding red values followed by 64 bits encoding green values.
/// </summary>
RG_BC5_UNorm,
/// <summary>
/// A two-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of signed normalized RG texel data with the first 64 bits encoding red values followed by 64 bits encoding green values.
/// </summary>
RG_BC5_SNorm,
/// <summary>
/// A three-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned floating-point RGB texel data.
/// </summary>
RGB_BC6H_UFloat,
/// <summary>
/// A three-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of signed floating-point RGB texel data.
/// </summary>
RGB_BC6H_SFloat,
/// <summary>
/// A four-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components.
/// </summary>
RGBA_BC7_SRGB,
/// <summary>
/// A four-component, block-compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data.
/// </summary>
RGBA_BC7_UNorm,
/// <summary>
/// A three-component, PVRTC compressed format where each 64-bit compressed texel block encodes a 8×4 rectangle of unsigned normalized RGB texel data with sRGB nonlinear encoding. This format has no alpha and is considered opaque.
/// </summary>
RGB_PVRTC_2Bpp_SRGB,
/// <summary>
/// A three-component, PVRTC compressed format where each 64-bit compressed texel block encodes a 8×4 rectangle of unsigned normalized RGB texel data. This format has no alpha and is considered opaque.
/// </summary>
RGB_PVRTC_2Bpp_UNorm,
/// <summary>
/// A three-component, PVRTC compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data with sRGB nonlinear encoding. This format has no alpha and is considered opaque.
/// </summary>
RGB_PVRTC_4Bpp_SRGB,
/// <summary>
/// A three-component, PVRTC compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data. This format has no alpha and is considered opaque.
/// </summary>
RGB_PVRTC_4Bpp_UNorm,
/// <summary>
/// A four-component, PVRTC compressed format where each 64-bit compressed texel block encodes a 8×4 rectangle of unsigned normalized RGBA texel data with the first 32 bits encoding alpha values followed by 32 bits encoding RGB values with sRGB nonlinear encoding applied.
/// </summary>
RGBA_PVRTC_2Bpp_SRGB,
/// <summary>
/// A four-component, PVRTC compressed format where each 64-bit compressed texel block encodes a 8×4 rectangle of unsigned normalized RGBA texel data with the first 32 bits encoding alpha values followed by 32 bits encoding RGB values.
/// </summary>
RGBA_PVRTC_2Bpp_UNorm,
/// <summary>
/// A four-component, PVRTC compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 32 bits encoding alpha values followed by 32 bits encoding RGB values with sRGB nonlinear encoding applied.
/// </summary>
RGBA_PVRTC_4Bpp_SRGB,
/// <summary>
/// A four-component, PVRTC compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 32 bits encoding alpha values followed by 32 bits encoding RGB values.
/// </summary>
RGBA_PVRTC_4Bpp_UNorm,
/// <summary>
/// A three-component, ETC compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data. This format has no alpha and is considered opaque.
/// </summary>
RGB_ETC_UNorm,
/// <summary>
/// A three-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data with sRGB nonlinear encoding. This format has no alpha and is considered opaque.
/// </summary>
RGB_ETC2_SRGB,
/// <summary>
/// A three-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data. This format has no alpha and is considered opaque.
/// </summary>
RGB_ETC2_UNorm,
/// <summary>
/// A four-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data with sRGB nonlinear encoding, and provides 1 bit of alpha.
/// </summary>
RGB_A1_ETC2_SRGB,
/// <summary>
/// A four-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGB texel data, and provides 1 bit of alpha.
/// </summary>
RGB_A1_ETC2_UNorm,
/// <summary>
/// A four-component, ETC2 compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values with sRGB nonlinear encoding applied.
/// </summary>
RGBA_ETC2_SRGB,
/// <summary>
/// A four-component, ETC2 compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with the first 64 bits encoding alpha values followed by 64 bits encoding RGB values.
/// </summary>
RGBA_ETC2_UNorm,
/// <summary>
/// A one-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized red texel data.
/// </summary>
R_EAC_UNorm,
/// <summary>
/// A one-component, ETC2 compressed format where each 64-bit compressed texel block encodes a 4×4 rectangle of signed normalized red texel data.
/// </summary>
R_EAC_SNorm,
/// <summary>
/// A two-component, ETC2 compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RG texel data with the first 64 bits encoding red values followed by 64 bits encoding green values.
/// </summary>
RG_EAC_UNorm,
/// <summary>
/// A two-component, ETC2 compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of signed normalized RG texel data with the first 64 bits encoding red values followed by 64 bits encoding green values.
/// </summary>
RG_EAC_SNorm,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components.
/// </summary>
RGBA_ASTC4X4_SRGB,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of unsigned normalized RGBA texel data.
/// </summary>
RGBA_ASTC4X4_UNorm,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 5×5 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components.
/// </summary>
RGBA_ASTC5X5_SRGB,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 5×5 rectangle of unsigned normalized RGBA texel data.
/// </summary>
RGBA_ASTC5X5_UNorm,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 6×6 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components.
/// </summary>
RGBA_ASTC6X6_SRGB,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 6×6 rectangle of unsigned normalized RGBA texel data.
/// </summary>
RGBA_ASTC6X6_UNorm,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes an 8×8 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components.
/// </summary>
RGBA_ASTC8X8_SRGB,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes an 8×8 rectangle of unsigned normalized RGBA texel data.
/// </summary>
RGBA_ASTC8X8_UNorm,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×10 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components.
/// </summary>
RGBA_ASTC10X10_SRGB,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×10 rectangle of unsigned normalized RGBA texel data.
/// </summary>
RGBA_ASTC10X10_UNorm,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 12×12 rectangle of unsigned normalized RGBA texel data with sRGB nonlinear encoding applied to the RGB components.
/// </summary>
RGBA_ASTC12X12_SRGB,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 12×12 rectangle of unsigned normalized RGBA texel data.
/// </summary>
RGBA_ASTC12X12_UNorm,
/// <summary>
/// YUV 4:2:2 Video resource format.
/// </summary>
YUV2,
/// <summary>
/// GraphicsFormat.YUV2.
/// </summary>
VideoAuto = 144,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 4×4 rectangle of float RGBA texel data.
/// </summary>
RGBA_ASTC4X4_UFloat,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 5×5 rectangle of float RGBA texel data.
/// </summary>
RGBA_ASTC5X5_UFloat,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 6×6 rectangle of float RGBA texel data.
/// </summary>
RGBA_ASTC6X6_UFloat,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes an 8×8 rectangle of float RGBA texel data.
/// </summary>
RGBA_ASTC8X8_UFloat,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 10×10 rectangle of float RGBA texel data.
/// </summary>
RGBA_ASTC10X10_UFloat,
/// <summary>
/// A four-component, ASTC compressed format where each 128-bit compressed texel block encodes a 12×12 rectangle of float RGBA texel data.
/// </summary>
RGBA_ASTC12X12_UFloat,
/// <summary>
/// A two-component, 24-bit format that has 16 unsigned normalized bits in the depth component and 8 unsigned integer bits in the stencil component. Most platforms do not support this format.
/// </summary>
D16_UNorm_S8_UInt,
}
public static class GraphicsFormatExtension
{
public static TextureFormat ToTextureFormat(this GraphicsFormat graphicsFormat)
{
switch (graphicsFormat)
{
case GraphicsFormat.R8_SRGB:
case GraphicsFormat.R8_UInt:
case GraphicsFormat.R8_UNorm:
return TextureFormat.R8;
case GraphicsFormat.R8G8_SRGB:
case GraphicsFormat.R8G8_UInt:
case GraphicsFormat.R8G8_UNorm:
return TextureFormat.RG16;
case GraphicsFormat.R8G8B8_SRGB:
case GraphicsFormat.R8G8B8_UInt:
case GraphicsFormat.R8G8B8_UNorm:
return TextureFormat.RGB24;
case GraphicsFormat.R8G8B8A8_SRGB:
case GraphicsFormat.R8G8B8A8_UInt:
case GraphicsFormat.R8G8B8A8_UNorm:
return TextureFormat.RGBA32;
case GraphicsFormat.R16_UInt:
case GraphicsFormat.R16_UNorm:
return TextureFormat.R16;
case GraphicsFormat.R16G16_UInt:
case GraphicsFormat.R16G16_UNorm:
return TextureFormat.RG32;
case GraphicsFormat.R16G16B16_UInt:
case GraphicsFormat.R16G16B16_UNorm:
return TextureFormat.RGB48;
case GraphicsFormat.R16G16B16A16_UInt:
case GraphicsFormat.R16G16B16A16_UNorm:
return TextureFormat.RGBA64;
case GraphicsFormat.R16_SFloat:
return TextureFormat.RHalf;
case GraphicsFormat.R16G16_SFloat:
return TextureFormat.RGHalf;
case GraphicsFormat.R16G16B16_SFloat: //?
case GraphicsFormat.R16G16B16A16_SFloat:
return TextureFormat.RGBAHalf;
case GraphicsFormat.R32_SFloat:
return TextureFormat.RFloat;
case GraphicsFormat.R32G32_SFloat:
return TextureFormat.RGFloat;
case GraphicsFormat.R32G32B32_SFloat: //?
case GraphicsFormat.R32G32B32A32_SFloat:
return TextureFormat.RGBAFloat;
case GraphicsFormat.B8G8R8A8_SRGB:
case GraphicsFormat.B8G8R8A8_UInt:
case GraphicsFormat.B8G8R8A8_UNorm:
return TextureFormat.BGRA32;
case GraphicsFormat.E5B9G9R9_UFloatPack32:
return TextureFormat.RGB9e5Float;
case GraphicsFormat.RGBA_DXT1_SRGB:
case GraphicsFormat.RGBA_DXT1_UNorm:
return TextureFormat.DXT1;
case GraphicsFormat.RGBA_DXT3_SRGB:
case GraphicsFormat.RGBA_DXT3_UNorm:
return TextureFormat.DXT3;
case GraphicsFormat.RGBA_DXT5_SRGB:
case GraphicsFormat.RGBA_DXT5_UNorm:
return TextureFormat.DXT5;
case GraphicsFormat.R_BC4_UNorm:
return TextureFormat.BC4;
case GraphicsFormat.RG_BC5_UNorm:
return TextureFormat.BC5;
case GraphicsFormat.RGB_BC6H_SFloat:
case GraphicsFormat.RGB_BC6H_UFloat:
return TextureFormat.BC6H;
case GraphicsFormat.RGBA_BC7_SRGB:
case GraphicsFormat.RGBA_BC7_UNorm:
return TextureFormat.BC7;
case GraphicsFormat.RGB_PVRTC_2Bpp_SRGB:
case GraphicsFormat.RGB_PVRTC_2Bpp_UNorm:
case GraphicsFormat.RGBA_PVRTC_2Bpp_SRGB:
case GraphicsFormat.RGBA_PVRTC_2Bpp_UNorm:
return TextureFormat.PVRTC_RGBA2;
case GraphicsFormat.RGB_PVRTC_4Bpp_SRGB:
case GraphicsFormat.RGB_PVRTC_4Bpp_UNorm:
case GraphicsFormat.RGBA_PVRTC_4Bpp_SRGB:
case GraphicsFormat.RGBA_PVRTC_4Bpp_UNorm:
return TextureFormat.PVRTC_RGBA4;
case GraphicsFormat.RGB_ETC_UNorm:
return TextureFormat.ETC_RGB4;
case GraphicsFormat.RGB_ETC2_SRGB:
case GraphicsFormat.RGB_ETC2_UNorm:
return TextureFormat.ETC2_RGB;
case GraphicsFormat.RGB_A1_ETC2_SRGB:
case GraphicsFormat.RGB_A1_ETC2_UNorm:
return TextureFormat.ETC2_RGBA1;
case GraphicsFormat.RGBA_ETC2_SRGB:
case GraphicsFormat.RGBA_ETC2_UNorm:
return TextureFormat.ETC2_RGBA8;
case GraphicsFormat.R_EAC_UNorm:
return TextureFormat.EAC_R;
case GraphicsFormat.R_EAC_SNorm:
return TextureFormat.EAC_R_SIGNED;
case GraphicsFormat.RG_EAC_UNorm:
return TextureFormat.EAC_RG;
case GraphicsFormat.RG_EAC_SNorm:
return TextureFormat.EAC_RG_SIGNED;
case GraphicsFormat.RGBA_ASTC4X4_SRGB:
case GraphicsFormat.RGBA_ASTC4X4_UNorm:
return TextureFormat.ASTC_RGBA_4x4;
case GraphicsFormat.RGBA_ASTC5X5_SRGB:
case GraphicsFormat.RGBA_ASTC5X5_UNorm:
return TextureFormat.ASTC_RGBA_5x5;
case GraphicsFormat.RGBA_ASTC6X6_SRGB:
case GraphicsFormat.RGBA_ASTC6X6_UNorm:
return TextureFormat.ASTC_RGBA_6x6;
case GraphicsFormat.RGBA_ASTC8X8_SRGB:
case GraphicsFormat.RGBA_ASTC8X8_UNorm:
return TextureFormat.ASTC_RGBA_8x8;
case GraphicsFormat.RGBA_ASTC10X10_SRGB:
case GraphicsFormat.RGBA_ASTC10X10_UNorm:
return TextureFormat.ASTC_RGBA_10x10;
case GraphicsFormat.RGBA_ASTC12X12_SRGB:
case GraphicsFormat.RGBA_ASTC12X12_UNorm:
return TextureFormat.ASTC_RGBA_12x12;
case GraphicsFormat.YUV2:
case GraphicsFormat.VideoAuto:
return TextureFormat.YUY2;
default:
return 0;
}
}
}
}

View File

@@ -34,7 +34,7 @@ namespace AssetStudio
m_TexEnvs[i] = new KeyValuePair<string, UnityTexEnv>(reader.ReadAlignedString(), new UnityTexEnv(reader));
}
if (version[0] >= 2021) //2021.1 and up
if (version >= 2021) //2021.1 and up
{
int m_IntsSize = reader.ReadInt32();
m_Ints = new KeyValuePair<string, int>[m_IntsSize];
@@ -69,39 +69,39 @@ namespace AssetStudio
{
m_Shader = new PPtr<Shader>(reader);
if (version[0] == 4 && version[1] >= 1) //4.x
if (version == 4 && version.Minor >= 1) //4.x
{
var m_ShaderKeywords = reader.ReadStringArray();
}
if (version[0] > 2021 || (version[0] == 2021 && version[1] >= 3)) //2021.3 and up
if (version >= (2021, 3)) //2021.3 and up
{
var m_ValidKeywords = reader.ReadStringArray();
var m_InvalidKeywords = reader.ReadStringArray();
}
else if (version[0] >= 5) //5.0 ~ 2021.2
else if (version >= 5) //5.0 ~ 2021.2
{
var m_ShaderKeywords = reader.ReadAlignedString();
}
if (version[0] >= 5) //5.0 and up
if (version >= 5) //5.0 and up
{
var m_LightmapFlags = reader.ReadUInt32();
}
if (version[0] > 5 || (version[0] == 5 && version[1] >= 6)) //5.6 and up
if (version >= (5, 6)) //5.6 and up
{
var m_EnableInstancingVariants = reader.ReadBoolean();
//var m_DoubleSidedGI = a_Stream.ReadBoolean(); //2017 and up
reader.AlignStream();
}
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
if (version >= (4, 3)) //4.3 and up
{
var m_CustomRenderQueue = reader.ReadInt32();
}
if (version[0] > 5 || (version[0] == 5 && version[1] >= 1)) //5.1 and up
if (version >= (5, 1)) //5.1 and up
{
var stringTagMapSize = reader.ReadInt32();
for (int i = 0; i < stringTagMapSize; i++)
@@ -111,7 +111,7 @@ namespace AssetStudio
}
}
if (version[0] > 5 || (version[0] == 5 && version[1] >= 6)) //5.6 and up
if (version >= (5, 6)) //5.6 and up
{
var disabledShaderPasses = reader.ReadStringArray();
}

View File

@@ -40,7 +40,7 @@ namespace AssetStudio
m_Vertices = new PackedFloatVector(reader);
m_UV = new PackedFloatVector(reader);
if (version[0] < 5)
if (version < 5)
{
m_BindPoses = new PackedFloatVector(reader);
}
@@ -49,15 +49,15 @@ namespace AssetStudio
m_Weights = new PackedIntVector(reader);
m_NormalSigns = new PackedIntVector(reader);
m_TangentSigns = new PackedIntVector(reader);
if (version[0] >= 5)
if (version >= 5)
{
m_FloatColors = new PackedFloatVector(reader);
}
m_BoneIndices = new PackedIntVector(reader);
m_Triangles = new PackedIntVector(reader);
if (version[0] > 3 || (version[0] == 3 && version[1] >= 5)) //3.5 and up
if (version >= (3, 5)) //3.5 and up
{
if (version[0] < 5)
if (version < 5)
{
m_Colors = new PackedIntVector(reader);
}
@@ -87,7 +87,7 @@ namespace AssetStudio
channelMask = reader.ReadUInt32();
offset = reader.ReadUInt32();
if (version[0] < 4) //4.0 down
if (version < 4) //4.0 down
{
stride = reader.ReadUInt32();
align = reader.ReadUInt32();
@@ -131,14 +131,14 @@ namespace AssetStudio
{
var version = reader.version;
if (version[0] < 2018)//2018 down
if (version < 2018)//2018 down
{
m_CurrentChannels = reader.ReadUInt32();
}
m_VertexCount = reader.ReadUInt32();
if (version[0] >= 4) //4.0 and up
if (version >= 4) //4.0 and up
{
var m_ChannelsSize = reader.ReadInt32();
m_Channels = new ChannelInfo[m_ChannelsSize];
@@ -148,9 +148,9 @@ namespace AssetStudio
}
}
if (version[0] < 5) //5.0 down
if (version < 5) //5.0 down
{
if (version[0] < 4)
if (version < 4)
{
m_Streams = new StreamInfo[4];
}
@@ -164,7 +164,7 @@ namespace AssetStudio
m_Streams[i] = new StreamInfo(reader);
}
if (version[0] < 4) //4.0 down
if (version < 4) //4.0 down
{
GetChannels(version);
}
@@ -178,7 +178,7 @@ namespace AssetStudio
reader.AlignStream();
}
private void GetStreams(int[] version)
private void GetStreams(UnityVersion version)
{
var streamCount = m_Channels.Max(x => x.stream) + 1;
m_Streams = new StreamInfo[streamCount];
@@ -213,7 +213,7 @@ namespace AssetStudio
}
}
private void GetChannels(int[] version)
private void GetChannels(UnityVersion version)
{
m_Channels = new ChannelInfo[6];
for (int i = 0; i < 6; i++)
@@ -305,20 +305,20 @@ namespace AssetStudio
{
var version = reader.version;
if (version[0] == 4 && version[1] < 3) //4.3 down
if (version < (4, 3)) //4.3 down
{
var name = reader.ReadAlignedString();
}
firstVertex = reader.ReadUInt32();
vertexCount = reader.ReadUInt32();
if (version[0] == 4 && version[1] < 3) //4.3 down
if (version < (4, 3)) //4.3 down
{
var aabbMinDelta = reader.ReadVector3();
var aabbMaxDelta = reader.ReadVector3();
}
hasNormals = reader.ReadBoolean();
hasTangents = reader.ReadBoolean();
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
if (version >= (4, 3)) //4.3 and up
{
reader.AlignStream();
}
@@ -352,7 +352,7 @@ namespace AssetStudio
{
var version = reader.version;
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
if (version >= (4, 3)) //4.3 and up
{
int numVerts = reader.ReadInt32();
vertices = new BlendShapeVertex[numVerts];
@@ -425,17 +425,17 @@ namespace AssetStudio
indexCount = reader.ReadUInt32();
topology = (GfxPrimitiveType)reader.ReadInt32();
if (version[0] < 4) //4.0 down
if (version < 4) //4.0 down
{
triangleCount = reader.ReadUInt32();
}
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3)) //2017.3 and up
if (version >= (2017, 3)) //2017.3 and up
{
baseVertex = reader.ReadUInt32();
}
if (version[0] >= 3) //3.0 and up
if (version >= 3) //3.0 and up
{
firstVertex = reader.ReadUInt32();
vertexCount = reader.ReadUInt32();
@@ -474,12 +474,12 @@ namespace AssetStudio
public Mesh(ObjectReader reader) : base(reader)
{
if (version[0] < 3 || (version[0] == 3 && version[1] < 5)) //3.5 down
if (version < (3, 5)) //3.5 down
{
m_Use16BitIndices = reader.ReadInt32() > 0;
}
if (version[0] == 2 && version[1] <= 5) //2.5 and down
if (version <= (2, 5)) //2.5 and down
{
int m_IndexBuffer_size = reader.ReadInt32();
@@ -505,21 +505,21 @@ namespace AssetStudio
m_SubMeshes[i] = new SubMesh(reader);
}
if (version[0] > 4 || (version[0] == 4 && version[1] >= 1)) //4.1 and up
if (version >= (4, 1)) //4.1 and up
{
m_Shapes = new BlendShapeData(reader);
}
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
if (version >= (4, 3)) //4.3 and up
{
m_BindPose = reader.ReadMatrixArray();
m_BoneNameHashes = reader.ReadUInt32Array();
var m_RootBoneNameHash = reader.ReadUInt32();
}
if (version[0] > 2 || (version[0] == 2 && version[1] >= 6)) //2.6.0 and up
if (version >= (2, 6)) //2.6.0 and up
{
if (version[0] >= 2019) //2019 and up
if (version >= 2019) //2019 and up
{
var m_BonesAABBSize = reader.ReadInt32();
var m_BonesAABB = new MinMaxAABB[m_BonesAABBSize];
@@ -532,9 +532,9 @@ namespace AssetStudio
}
var m_MeshCompression = reader.ReadByte();
if (version[0] >= 4)
if (version >= 4)
{
if (version[0] < 5)
if (version < 5)
{
var m_StreamCompression = reader.ReadByte();
}
@@ -545,9 +545,9 @@ namespace AssetStudio
reader.AlignStream();
//Unity fixed it in 2017.3.1p1 and later versions
if ((version[0] > 2017 || (version[0] == 2017 && version[1] >= 4)) || //2017.4
((version[0] == 2017 && version[1] == 3 && version[2] == 1) && buildType.IsPatch) || //fixed after 2017.3.1px
((version[0] == 2017 && version[1] == 3) && m_MeshCompression == 0))//2017.3.xfx with no compression
if (version >= (2017, 4) //2017.4
|| version == (2017, 3, 1) && buildType.IsPatch //fixed after 2017.3.1px
|| version == (2017, 3) && m_MeshCompression == 0)//2017.3.xfx with no compression
{
var m_IndexFormat = reader.ReadInt32();
m_Use16BitIndices = m_IndexFormat == 0;
@@ -569,7 +569,7 @@ namespace AssetStudio
}
}
if (version[0] < 3 || (version[0] == 3 && version[1] < 5)) //3.4.2 and earlier
if (version < (3, 5)) //3.4.2 and earlier
{
m_VertexCount = reader.ReadInt32();
m_Vertices = reader.ReadSingleArray(m_VertexCount * 3); //Vector3
@@ -586,7 +586,7 @@ namespace AssetStudio
m_UV1 = reader.ReadSingleArray(reader.ReadInt32() * 2); //Vector2
if (version[0] == 2 && version[1] <= 5) //2.5 and down
if (version <= (2, 5)) //2.5 and down
{
int m_TangentSpace_size = reader.ReadInt32();
m_Normals = new float[m_TangentSpace_size * 3];
@@ -611,7 +611,7 @@ namespace AssetStudio
}
else
{
if (version[0] < 2018 || (version[0] == 2018 && version[1] < 2)) //2018.2 down
if (version < (2018, 2)) //2018.2 down
{
m_Skin = new BoneWeights4[reader.ReadInt32()];
for (int s = 0; s < m_Skin.Length; s++)
@@ -620,7 +620,7 @@ namespace AssetStudio
}
}
if (version[0] == 3 || (version[0] == 4 && version[1] <= 2)) //4.2 and down
if (version <= (4, 2)) //4.2 and down
{
m_BindPose = reader.ReadMatrixArray();
}
@@ -628,14 +628,14 @@ namespace AssetStudio
m_VertexData = new VertexData(reader);
}
if (version[0] > 2 || (version[0] == 2 && version[1] >= 6)) //2.6.0 and later
if (version >= (2, 6)) //2.6.0 and later
{
m_CompressedMesh = new CompressedMesh(reader);
}
reader.Position += 24; //AABB m_LocalAABB
if (version[0] < 3 || (version[0] == 3 && version[1] <= 4)) //3.4.2 and earlier
if (version <= (3, 4)) //3.4.2 and earlier
{
int m_Colors_size = reader.ReadInt32();
m_Colors = new float[m_Colors_size * 4];
@@ -651,12 +651,12 @@ namespace AssetStudio
int m_MeshUsageFlags = reader.ReadInt32();
if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 1)) //2022.1 and up
if (version >= (2022, 1)) //2022.1 and up
{
int m_CookingOptions = reader.ReadInt32();
}
if (version[0] >= 5) //5.0 and up
if (version >= 5) //5.0 and up
{
var m_BakedConvexCollisionMesh = reader.ReadUInt8Array();
reader.AlignStream();
@@ -664,14 +664,14 @@ namespace AssetStudio
reader.AlignStream();
}
if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up
if (version >= (2018, 2)) //2018.2 and up
{
var m_MeshMetrics = new float[2];
m_MeshMetrics[0] = reader.ReadSingle();
m_MeshMetrics[1] = reader.ReadSingle();
}
if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 3)) //2018.3 and up
if (version >= (2018, 3)) //2018.3 and up
{
reader.AlignStream();
m_StreamData = new StreamingInfo(reader);
@@ -690,12 +690,12 @@ namespace AssetStudio
m_VertexData.m_DataSize = resourceReader.GetData();
}
}
if (version[0] > 3 || (version[0] == 3 && version[1] >= 5)) //3.5 and up
if (version >= (3, 5)) //3.5 and up
{
ReadVertexData();
}
if (version[0] > 2 || (version[0] == 2 && version[1] >= 6)) //2.6.0 and later
if (version >= (2, 6)) //2.6.0 and later
{
DecompressCompressedMesh();
}
@@ -716,7 +716,7 @@ 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) //kShaderChannelColor && kChannelFormatColor
if (version < 2018 && chn == 2 && m_Channel.format == 2) //kShaderChannelColor && kChannelFormatColor
{
m_Channel.dimension = 4;
}
@@ -752,7 +752,7 @@ namespace AssetStudio
else
componentsFloatArray = MeshHelper.BytesToFloatArray(componentBytes, vertexFormat);
if (version[0] >= 2018)
if (version >= 2018)
{
switch (chn)
{
@@ -841,7 +841,7 @@ namespace AssetStudio
m_UV1 = componentsFloatArray;
break;
case 5:
if (version[0] >= 5) //kShaderChannelTexCoord2
if (version >= 5) //kShaderChannelTexCoord2
{
m_UV2 = componentsFloatArray;
}
@@ -906,7 +906,7 @@ namespace AssetStudio
}
}
//BindPose
if (version[0] < 5)
if (version < 5)
{
if (m_CompressedMesh.m_BindPoses.m_NumItems > 0)
{
@@ -983,7 +983,7 @@ namespace AssetStudio
}
}
//FloatColor
if (version[0] >= 5)
if (version >= 5)
{
if (m_CompressedMesh.m_FloatColors.m_NumItems > 0)
{
@@ -1074,7 +1074,7 @@ namespace AssetStudio
m_Indices.Add(m_IndexBuffer[firstIndex + i + 2]);
}
}
else if (version[0] < 4 || topology == GfxPrimitiveType.TriangleStrip)
else if (version < 4 || topology == GfxPrimitiveType.TriangleStrip)
{
// de-stripify :
uint triIndex = 0;
@@ -1238,9 +1238,9 @@ namespace AssetStudio
SInt32
}
public static VertexFormat ToVertexFormat(int format, int[] version)
public static VertexFormat ToVertexFormat(int format, UnityVersion version)
{
if (version[0] < 2017)
if (version < 2017)
{
switch ((VertexChannelFormat)format)
{
@@ -1258,7 +1258,7 @@ namespace AssetStudio
throw new ArgumentOutOfRangeException(nameof(format), format, null);
}
}
else if (version[0] < 2019)
else if (version < 2019)
{
switch ((VertexFormat2017)format)
{

View File

@@ -13,11 +13,11 @@ namespace AssetStudio
public MonoScript(ObjectReader reader) : base(reader)
{
if (version[0] > 3 || (version[0] == 3 && version[1] >= 4)) //3.4 and up
if (version >= (3, 4)) //3.4 and up
{
var m_ExecutionOrder = reader.ReadInt32();
}
if (version[0] < 5) //5.0 down
if (version < 5) //5.0 down
{
var m_PropertiesHash = reader.ReadUInt32();
}
@@ -25,17 +25,17 @@ namespace AssetStudio
{
var m_PropertiesHash = reader.ReadBytes(16);
}
if (version[0] < 3) //3.0 down
if (version < 3) //3.0 down
{
var m_PathName = reader.ReadAlignedString();
}
m_ClassName = reader.ReadAlignedString();
if (version[0] >= 3) //3.0 and up
if (version >= 3) //3.0 and up
{
m_Namespace = reader.ReadAlignedString();
}
m_AssemblyName = reader.ReadAlignedString();
if (version[0] < 2018 || (version[0] == 2018 && version[1] < 2)) //2018.2 down
if (version < (2018, 2)) //2018.2 down
{
var m_IsEditorScript = reader.ReadBoolean();
}

View File

@@ -9,6 +9,8 @@ namespace AssetStudio
{
public string m_Name;
protected NamedObject() { }
protected NamedObject(ObjectReader reader) : base(reader)
{
m_Name = reader.ReadAlignedString();

View File

@@ -1,19 +1,27 @@
using System.Collections.Specialized;
using Newtonsoft.Json;
using System.Collections.Specialized;
namespace AssetStudio
{
public class Object
{
[JsonIgnore]
public SerializedFile assetsFile;
[JsonIgnore]
public ObjectReader reader;
public long m_PathID;
public int[] version;
[JsonIgnore]
public UnityVersion version;
protected BuildType buildType;
[JsonIgnore]
public BuildTarget platform;
public ClassIDType type;
[JsonIgnore]
public SerializedType serializedType;
public uint byteSize;
public Object() { }
public Object(ObjectReader reader)
{
this.reader = reader;
@@ -51,6 +59,24 @@ namespace AssetStudio
return null;
}
public string DumpObject()
{
string str = null;
try
{
str = JsonConvert.SerializeObject(this, new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
}).Replace(" ", " ");
}
catch
{
//ignore
}
return str;
}
public OrderedDictionary ToType()
{
if (serializedType?.m_Type != null)

View File

@@ -7,45 +7,47 @@ namespace AssetStudio
public int m_FileID;
public long m_PathID;
private SerializedFile assetsFile;
private int index = -2; //-2 - Prepare, -1 - Missing
private SerializedFile _assetsFile;
private int _index = -2; //-2 - Prepare, -1 - Missing
public PPtr(ObjectReader reader)
{
m_FileID = reader.ReadInt32();
m_PathID = reader.m_Version < SerializedFileFormatVersion.Unknown_14 ? reader.ReadInt32() : reader.ReadInt64();
assetsFile = reader.assetsFile;
_assetsFile = reader.assetsFile;
}
public PPtr() { }
private bool TryGetAssetsFile(out SerializedFile result)
{
result = null;
if (m_FileID == 0)
{
result = assetsFile;
result = _assetsFile;
return true;
}
if (m_FileID > 0 && m_FileID - 1 < assetsFile.m_Externals.Count)
if (m_FileID > 0 && m_FileID - 1 < _assetsFile.m_Externals.Count)
{
var assetsManager = assetsFile.assetsManager;
var assetsManager = _assetsFile.assetsManager;
var assetsFileList = assetsManager.assetsFileList;
var assetsFileIndexCache = assetsManager.assetsFileIndexCache;
if (index == -2)
if (_index == -2)
{
var m_External = assetsFile.m_Externals[m_FileID - 1];
var m_External = _assetsFile.m_Externals[m_FileID - 1];
var name = m_External.fileName;
if (!assetsFileIndexCache.TryGetValue(name, out index))
if (!assetsFileIndexCache.TryGetValue(name, out _index))
{
index = assetsFileList.FindIndex(x => x.fileName.Equals(name, StringComparison.OrdinalIgnoreCase));
assetsFileIndexCache.Add(name, index);
_index = assetsFileList.FindIndex(x => x.fileName.Equals(name, StringComparison.OrdinalIgnoreCase));
assetsFileIndexCache.Add(name, _index);
}
}
if (index >= 0)
if (_index >= 0)
{
result = assetsFileList[index];
result = assetsFileList[_index];
return true;
}
}
@@ -53,8 +55,9 @@ namespace AssetStudio
return false;
}
public bool TryGet(out T result)
public bool TryGet(out T result, SerializedFile assetsFile = null)
{
_assetsFile = _assetsFile ?? assetsFile;
if (TryGetAssetsFile(out var sourceFile))
{
if (sourceFile.ObjectsDic.TryGetValue(m_PathID, out var obj))
@@ -71,8 +74,9 @@ namespace AssetStudio
return false;
}
public bool TryGet<T2>(out T2 result) where T2 : Object
public bool TryGet<T2>(out T2 result, SerializedFile assetsFile = null) where T2 : Object
{
_assetsFile = _assetsFile ?? assetsFile;
if (TryGetAssetsFile(out var sourceFile))
{
if (sourceFile.ObjectsDic.TryGetValue(m_PathID, out var obj))
@@ -92,20 +96,20 @@ namespace AssetStudio
public void Set(T m_Object)
{
var name = m_Object.assetsFile.fileName;
if (string.Equals(assetsFile.fileName, name, StringComparison.OrdinalIgnoreCase))
if (string.Equals(_assetsFile.fileName, name, StringComparison.OrdinalIgnoreCase))
{
m_FileID = 0;
}
else
{
m_FileID = assetsFile.m_Externals.FindIndex(x => string.Equals(x.fileName, name, StringComparison.OrdinalIgnoreCase));
m_FileID = _assetsFile.m_Externals.FindIndex(x => string.Equals(x.fileName, name, StringComparison.OrdinalIgnoreCase));
if (m_FileID == -1)
{
assetsFile.m_Externals.Add(new FileIdentifier
_assetsFile.m_Externals.Add(new FileIdentifier
{
fileName = m_Object.assetsFile.fileName
});
m_FileID = assetsFile.m_Externals.Count;
m_FileID = _assetsFile.m_Externals.Count;
}
else
{
@@ -113,14 +117,14 @@ namespace AssetStudio
}
}
var assetsManager = assetsFile.assetsManager;
var assetsManager = _assetsFile.assetsManager;
var assetsFileList = assetsManager.assetsFileList;
var assetsFileIndexCache = assetsManager.assetsFileIndexCache;
if (!assetsFileIndexCache.TryGetValue(name, out index))
if (!assetsFileIndexCache.TryGetValue(name, out _index))
{
index = assetsFileList.FindIndex(x => x.fileName.Equals(name, StringComparison.OrdinalIgnoreCase));
assetsFileIndexCache.Add(name, index);
_index = assetsFileList.FindIndex(x => x.fileName.Equals(name, StringComparison.OrdinalIgnoreCase));
assetsFileIndexCache.Add(name, _index);
}
m_PathID = m_Object.m_PathID;

View File

@@ -12,7 +12,7 @@ namespace AssetStudio
public PlayerSettings(ObjectReader reader) : base(reader)
{
if (version[0] > 5 || (version[0] == 5 && version[1] >= 4)) //5.4.0 nad up
if (version >= (5, 4)) //5.4.0 and up
{
var productGUID = reader.ReadBytes(16);
}
@@ -23,12 +23,12 @@ namespace AssetStudio
reader.AlignStream();
int defaultScreenOrientation = reader.ReadInt32();
int targetDevice = reader.ReadInt32();
if (version[0] < 5 || (version[0] == 5 && version[1] < 3)) //5.3 down
if (version < (5, 3)) //5.3 down
{
if (version[0] < 5) //5.0 down
if (version < 5) //5.0 down
{
int targetPlatform = reader.ReadInt32(); //4.0 and up targetGlesGraphics
if (version[0] > 4 || (version[0] == 4 && version[1] >= 6)) //4.6 and up
if (version >= (4, 6)) //4.6 and up
{
var targetIOSGraphics = reader.ReadInt32();
}
@@ -40,7 +40,7 @@ namespace AssetStudio
var useOnDemandResources = reader.ReadBoolean();
reader.AlignStream();
}
if (version[0] > 3 || (version[0] == 3 && version[1] >= 5)) //3.5 and up
if (version >= (3, 5)) //3.5 and up
{
var accelerometerFrequency = reader.ReadInt32();
}

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

@@ -25,7 +25,7 @@ namespace AssetStudio
protected Renderer(ObjectReader reader) : base(reader)
{
if (version[0] < 5) //5.0 down
if (version < 5) //5.0 down
{
var m_Enabled = reader.ReadBoolean();
var m_CastShadows = reader.ReadBoolean();
@@ -34,27 +34,27 @@ namespace AssetStudio
}
else //5.0 and up
{
if (version[0] > 5 || (version[0] == 5 && version[1] >= 4)) //5.4 and up
if (version >= (5, 4)) //5.4 and up
{
var m_Enabled = reader.ReadBoolean();
var m_CastShadows = reader.ReadByte();
var m_ReceiveShadows = reader.ReadByte();
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 2)) //2017.2 and up
if (version >= (2017, 2)) //2017.2 and up
{
var m_DynamicOccludee = reader.ReadByte();
}
if (version[0] >= 2021) //2021.1 and up
if (version >= 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();
if (version[0] > 2019 || (version[0] == 2019 && version[1] >= 3)) //2019.3 and up
if (version >= (2019, 3)) //2019.3 and up
{
var m_RayTracingMode = reader.ReadByte();
}
if (version[0] >= 2020) //2020.1 and up
if (version >= 2020) //2020.1 and up
{
var m_RayTraceProcedural = reader.ReadByte();
}
@@ -69,12 +69,12 @@ namespace AssetStudio
reader.AlignStream();
}
if (version[0] >= 2018) //2018 and up
if (version >= 2018) //2018 and up
{
var m_RenderingLayerMask = reader.ReadUInt32();
}
if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 3)) //2018.3 and up
if (version >= (2018, 3)) //2018.3 and up
{
var m_RendererPriority = reader.ReadInt32();
}
@@ -83,12 +83,12 @@ namespace AssetStudio
var m_LightmapIndexDynamic = reader.ReadUInt16();
}
if (version[0] >= 3) //3.0 and up
if (version >= 3) //3.0 and up
{
var m_LightmapTilingOffset = reader.ReadVector4();
}
if (version[0] >= 5) //5.0 and up
if (version >= 5) //5.0 and up
{
var m_LightmapTilingOffsetDynamic = reader.ReadVector4();
}
@@ -100,13 +100,13 @@ namespace AssetStudio
m_Materials[i] = new PPtr<Material>(reader);
}
if (version[0] < 3) //3.0 down
if (version < 3) //3.0 down
{
var m_LightmapTilingOffset = reader.ReadVector4();
}
else //3.0 and up
{
if (version[0] > 5 || (version[0] == 5 && version[1] >= 5)) //5.5 and up
if (version >= (5, 5)) //5.5 and up
{
m_StaticBatchInfo = new StaticBatchInfo(reader);
}
@@ -118,17 +118,17 @@ namespace AssetStudio
var m_StaticBatchRoot = new PPtr<Transform>(reader);
}
if (version[0] > 5 || (version[0] == 5 && version[1] >= 4)) //5.4 and up
if (version >= (5, 4)) //5.4 and up
{
var m_ProbeAnchor = new PPtr<Transform>(reader);
var m_LightProbeVolumeOverride = new PPtr<GameObject>(reader);
}
else if (version[0] > 3 || (version[0] == 3 && version[1] >= 5)) //3.5 - 5.3
else if (version >= (3, 5)) //3.5 - 5.3
{
var m_UseLightProbes = reader.ReadBoolean();
reader.AlignStream();
if (version[0] >= 5)//5.0 and up
if (version >= 5)//5.0 and up
{
var m_ReflectionProbeUsage = reader.ReadInt32();
}
@@ -136,9 +136,9 @@ namespace AssetStudio
var m_LightProbeAnchor = new PPtr<Transform>(reader); //5.0 and up m_ProbeAnchor
}
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
if (version >= (4, 3)) //4.3 and up
{
if (version[0] == 4 && version[1] == 3) //4.3
if (version == (4, 3)) //4.3
{
var m_SortingLayer = reader.ReadInt16();
}

View File

@@ -243,14 +243,14 @@ namespace AssetStudio
}
rtSeparateBlend = reader.ReadBoolean();
reader.AlignStream();
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 2)) //2017.2 and up
if (version >= (2017, 2)) //2017.2 and up
{
zClip = new SerializedShaderFloatValue(reader);
}
zTest = new SerializedShaderFloatValue(reader);
zWrite = new SerializedShaderFloatValue(reader);
culling = new SerializedShaderFloatValue(reader);
if (version[0] >= 2020) //2020.1 and up
if (version >= 2020) //2020.1 and up
{
conservative = new SerializedShaderFloatValue(reader);
}
@@ -359,7 +359,7 @@ namespace AssetStudio
m_NameIndex = reader.ReadInt32();
m_Index = reader.ReadInt32();
m_SamplerIndex = reader.ReadInt32();
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3)) //2017.3 and up
if (version >= (2017, 3)) //2017.3 and up
{
var m_MultiSampled = reader.ReadBoolean();
}
@@ -380,7 +380,7 @@ namespace AssetStudio
m_NameIndex = reader.ReadInt32();
m_Index = reader.ReadInt32();
if (version[0] >= 2020) //2020.1 and up
if (version >= 2020) //2020.1 and up
{
m_ArraySize = reader.ReadInt32();
}
@@ -415,7 +415,7 @@ namespace AssetStudio
{
m_VectorParams[i] = new VectorParameter(reader);
}
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3)) //2017.3 and up
if (version >= (2017, 3)) //2017.3 and up
{
int numStructParams = reader.ReadInt32();
m_StructParams = new StructParameter[numStructParams];
@@ -426,11 +426,8 @@ 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
if (version.IsInRange((2020, 3, 2), 2021) //2020.3.2f1 and up
|| version >= (2021, 1, 4)) //2021.1.4f1 and up
{
m_IsPartialCB = reader.ReadBoolean();
reader.AlignStream();
@@ -584,7 +581,7 @@ namespace AssetStudio
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
if (version.IsInRange(2019, (2021, 2))) //2019 ~2021.1
{
var m_GlobalKeywordIndices = reader.ReadUInt16Array();
reader.AlignStream();
@@ -594,7 +591,7 @@ namespace AssetStudio
else
{
m_KeywordIndices = reader.ReadUInt16Array();
if (version[0] >= 2017) //2017 and up
if (version >= 2017) //2017 and up
{
reader.AlignStream();
}
@@ -604,11 +601,8 @@ namespace AssetStudio
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
if (version.IsInRange((2020, 3, 2), 2021) //2020.3.2f1 and up
|| version >= (2021, 1, 1)) //2021.1.1f1 and up
{
m_Parameters = new SerializedProgramParameters(reader);
}
@@ -663,7 +657,7 @@ namespace AssetStudio
m_UAVParams[i] = new UAVParameter(reader);
}
if (version[0] >= 2017) //2017 and up
if (version >= 2017) //2017 and up
{
int numSamplers = reader.ReadInt32();
m_Samplers = new SamplerParameter[numSamplers];
@@ -674,9 +668,9 @@ namespace AssetStudio
}
}
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 2)) //2017.2 and up
if (version >= (2017, 2)) //2017.2 and up
{
if (version[0] >= 2021) //2021.1 and up
if (version >= 2021) //2021.1 and up
{
var m_ShaderRequirements = reader.ReadInt64();
}
@@ -705,16 +699,13 @@ namespace AssetStudio
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
if (version.IsInRange((2020, 3, 2), 2021) //2020.3.2f1 and up
|| version >= (2021, 1, 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
if (version >= (2022, 1)) //2022.1 and up
{
m_SerializedKeywordStateMask = reader.ReadUInt16Array();
reader.AlignStream();
@@ -756,7 +747,7 @@ namespace AssetStudio
{
var version = reader.version;
if (version[0] > 2020 || (version[0] == 2020 && version[1] >= 2)) //2020.2 and up
if (version >= (2020, 2)) //2020.2 and up
{
int numEditorDataHash = reader.ReadInt32();
m_EditorDataHash = new Hash128[numEditorDataHash];
@@ -767,7 +758,7 @@ namespace AssetStudio
reader.AlignStream();
m_Platforms = reader.ReadUInt8Array();
reader.AlignStream();
if (version[0] < 2021 || (version[0] == 2021 && version[1] < 2)) //2021.1 and down
if (version <= (2021, 1)) //2021.1 and down
{
m_LocalKeywordMask = reader.ReadUInt16Array();
reader.AlignStream();
@@ -791,12 +782,12 @@ namespace AssetStudio
progGeometry = new SerializedProgram(reader);
progHull = new SerializedProgram(reader);
progDomain = new SerializedProgram(reader);
if (version[0] > 2019 || (version[0] == 2019 && version[1] >= 3)) //2019.3 and up
if (version >= (2019, 3)) //2019.3 and up
{
progRayTracing = new SerializedProgram(reader);
}
m_HasInstancingVariant = reader.ReadBoolean();
if (version[0] >= 2018) //2018 and up
if (version >= 2018) //2018 and up
{
var m_HasProceduralInstancingVariant = reader.ReadBoolean();
}
@@ -805,7 +796,7 @@ 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
if (version == 2021 && version.Minor >= 2) //2021.2 ~2021.x
{
m_SerializedKeywordStateMask = reader.ReadUInt16Array();
reader.AlignStream();
@@ -898,7 +889,7 @@ namespace AssetStudio
m_SubShaders[i] = new SerializedSubShader(reader);
}
if (version[0] > 2021 || (version[0] == 2021 && version[1] >= 2)) //2021.2 and up
if (version >= (2021, 2)) //2021.2 and up
{
m_KeywordNames = reader.ReadStringArray();
m_KeywordFlags = reader.ReadUInt8Array();
@@ -916,7 +907,7 @@ namespace AssetStudio
m_Dependencies[i] = new SerializedShaderDependency(reader);
}
if (version[0] >= 2021) //2021.1 and up
if (version >= 2021) //2021.1 and up
{
int m_CustomEditorForRenderPipelinesSize = reader.ReadInt32();
m_CustomEditorForRenderPipelines = new SerializedCustomEditorForRenderPipeline[m_CustomEditorForRenderPipelinesSize];
@@ -977,11 +968,11 @@ namespace AssetStudio
public Shader(ObjectReader reader) : base(reader)
{
if (version[0] == 5 && version[1] >= 5 || version[0] > 5) //5.5 and up
if (version >= (5, 5)) //5.5 and up
{
m_ParsedForm = new SerializedShader(reader);
platforms = reader.ReadUInt32Array().Select(x => (ShaderCompilerPlatform)x).ToArray();
if (version[0] > 2019 || (version[0] == 2019 && version[1] >= 3)) //2019.3 and up
if (version >= (2019, 3)) //2019.3 and up
{
offsets = reader.ReadUInt32ArrayArray();
compressedLengths = reader.ReadUInt32ArrayArray();
@@ -1002,7 +993,7 @@ namespace AssetStudio
new PPtr<Shader>(reader);
}
if (version[0] >= 2018)
if (version >= 2018)
{
var m_NonModifiableTexturesCount = reader.ReadInt32();
for (int i = 0; i < m_NonModifiableTexturesCount; i++)
@@ -1020,7 +1011,7 @@ namespace AssetStudio
m_Script = reader.ReadUInt8Array();
reader.AlignStream();
var m_PathName = reader.ReadAlignedString();
if (version[0] == 5 && version[1] >= 3) //5.3 - 5.4
if (version == 5 && version.Minor >= 3) //5.3 - 5.4
{
decompressedSize = reader.ReadUInt32();
m_SubProgramBlob = reader.ReadUInt8Array();

View File

@@ -18,7 +18,7 @@ namespace AssetStudio
var m_SkinNormals = reader.ReadBoolean(); //3.1.0 and below
reader.AlignStream();
if (version[0] == 2 && version[1] < 6) //2.6 down
if (version < (2, 6)) //2.6 down
{
var m_DisableAnimationWhenOffscreen = new PPtr<Animation>(reader);
}
@@ -31,7 +31,7 @@ namespace AssetStudio
m_Bones[b] = new PPtr<Transform>(reader);
}
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
if (version >= (4, 3)) //4.3 and up
{
m_BlendShapeWeights = reader.ReadSingleArray();
}

View File

@@ -12,7 +12,7 @@ namespace AssetStudio
public SecondarySpriteTexture(ObjectReader reader)
{
texture = new PPtr<Texture2D>(reader);
name = reader.ReadStringToNull();
name = reader.ReadAlignedString();
}
}
@@ -68,7 +68,7 @@ namespace AssetStudio
var version = reader.version;
pos = reader.ReadVector3();
if (version[0] < 4 || (version[0] == 4 && version[1] <= 3)) //4.3 and down
if (version <= (4, 3)) //4.3 and down
{
uv = reader.ReadVector2();
}
@@ -99,12 +99,12 @@ namespace AssetStudio
var version = reader.version;
texture = new PPtr<Texture2D>(reader);
if (version[0] > 5 || (version[0] == 5 && version[1] >= 2)) //5.2 and up
if (version >= (5, 2)) //5.2 and up
{
alphaTexture = new PPtr<Texture2D>(reader);
}
if (version[0] >= 2019) //2019 and up
if (version >= 2019) //2019 and up
{
var secondaryTexturesSize = reader.ReadInt32();
secondaryTextures = new SecondarySpriteTexture[secondaryTexturesSize];
@@ -114,7 +114,7 @@ namespace AssetStudio
}
}
if (version[0] > 5 || (version[0] == 5 && version[1] >= 6)) //5.6 and up
if (version >= (5, 6)) //5.6 and up
{
var m_SubMeshesSize = reader.ReadInt32();
m_SubMeshes = new SubMesh[m_SubMeshesSize];
@@ -141,11 +141,11 @@ namespace AssetStudio
reader.AlignStream();
}
if (version[0] >= 2018) //2018 and up
if (version >= 2018) //2018 and up
{
m_Bindpose = reader.ReadMatrixArray();
if (version[0] == 2018 && version[1] < 2) //2018.2 down
if (version < (2018, 2)) //2018.2 down
{
var m_SourceSkinSize = reader.ReadInt32();
for (int i = 0; i < m_SourceSkinSize; i++)
@@ -157,18 +157,18 @@ namespace AssetStudio
textureRect = new Rectf(reader);
textureRectOffset = reader.ReadVector2();
if (version[0] > 5 || (version[0] == 5 && version[1] >= 6)) //5.6 and up
if (version >= (5, 6)) //5.6 and up
{
atlasRectOffset = reader.ReadVector2();
}
settingsRaw = new SpriteSettings(reader);
if (version[0] > 4 || (version[0] == 4 && version[1] >= 5)) //4.5 and up
if (version >= (4, 5)) //4.5 and up
{
uvTransform = reader.ReadVector4();
}
if (version[0] >= 2017) //2017 and up
if (version >= 2017) //2017 and up
{
downscaleMultiplier = reader.ReadSingle();
}
@@ -210,28 +210,26 @@ namespace AssetStudio
{
m_Rect = new Rectf(reader);
m_Offset = reader.ReadVector2();
if (version[0] > 4 || (version[0] == 4 && version[1] >= 5)) //4.5 and up
if (version >= (4, 5)) //4.5 and up
{
m_Border = reader.ReadVector4();
}
m_PixelsToUnits = reader.ReadSingle();
if (version[0] > 5
|| (version[0] == 5 && version[1] > 4)
|| (version[0] == 5 && version[1] == 4 && version[2] >= 2)
|| (version[0] == 5 && version[1] == 4 && version[2] == 1 && buildType.IsPatch && version[3] >= 3)) //5.4.1p3 and up
if (version >= (5, 4, 2)
|| version == (5, 4, 1) && buildType.IsPatch && version.Build >= 3) //5.4.1p3 and up
{
m_Pivot = reader.ReadVector2();
}
m_Extrude = reader.ReadUInt32();
if (version[0] > 5 || (version[0] == 5 && version[1] >= 3)) //5.3 and up
if (version >= (5, 3)) //5.3 and up
{
m_IsPolygon = reader.ReadBoolean();
reader.AlignStream();
}
if (version[0] >= 2017) //2017 and up
if (version >= 2017) //2017 and up
{
var first = new Guid(reader.ReadBytes(16));
var second = reader.ReadInt64();
@@ -244,7 +242,7 @@ namespace AssetStudio
m_RD = new SpriteRenderData(reader);
if (version[0] >= 2017) //2017 and up
if (version >= 2017) //2017 and up
{
var m_PhysicsShapeSize = reader.ReadInt32();
m_PhysicsShape = new Vector2[m_PhysicsShapeSize][];

View File

@@ -22,14 +22,14 @@ namespace AssetStudio
alphaTexture = new PPtr<Texture2D>(reader);
textureRect = new Rectf(reader);
textureRectOffset = reader.ReadVector2();
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 2)) //2017.2 and up
if (version >= (2017, 2)) //2017.2 and up
{
atlasRectOffset = reader.ReadVector2();
}
uvTransform = reader.ReadVector4();
downscaleMultiplier = reader.ReadSingle();
settingsRaw = new SpriteSettings(reader);
if (version[0] > 2020 || (version[0] == 2020 && version[1] >= 2)) //2020.2 and up
if (version >= (2020, 2)) //2020.2 and up
{
var secondaryTexturesSize = reader.ReadInt32();
secondaryTextures = new SecondarySpriteTexture[secondaryTexturesSize];

View File

@@ -0,0 +1,27 @@
namespace AssetStudio
{
public class StreamingInfo
{
public long offset; //ulong
public uint size;
public string path;
public StreamingInfo() { }
public StreamingInfo(ObjectReader reader)
{
var version = reader.version;
if (version >= 2020) //2020.1 and up
{
offset = reader.ReadInt64();
}
else
{
offset = reader.ReadUInt32();
}
size = reader.ReadUInt32();
path = reader.ReadAlignedString();
}
}
}

View File

@@ -1,19 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace AssetStudio
namespace AssetStudio
{
public abstract class Texture : NamedObject
{
protected Texture() { }
protected Texture(ObjectReader reader) : base(reader)
{
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3)) //2017.3 and up
if (version >= (2017, 3)) //2017.3 and up
{
var m_ForcedFallbackFormat = reader.ReadInt32();
var m_DownscaleFallback = reader.ReadBoolean();
if (version[0] > 2020 || (version[0] == 2020 && version[1] >= 2)) //2020.2 and up
if (version < (2023, 2)) //2023.2 down
{
var m_ForcedFallbackFormat = reader.ReadInt32();
var m_DownscaleFallback = reader.ReadBoolean();
}
if (version >= (2020, 2)) //2020.2 and up
{
var m_IsAlphaChannelOptional = reader.ReadBoolean();
}

View File

@@ -1,79 +1,89 @@
using System;
using System.Collections;
using Newtonsoft.Json;
namespace AssetStudio
{
public class StreamingInfo
{
public long offset; //ulong
public uint size;
public string path;
public StreamingInfo(ObjectReader reader)
{
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();
}
}
public class GLTextureSettings
{
public int m_FilterMode;
public int m_Aniso;
public float m_MipBias;
public int m_WrapMode;
public GLTextureSettings(ObjectReader reader)
{
var version = reader.version;
m_FilterMode = reader.ReadInt32();
m_Aniso = reader.ReadInt32();
m_MipBias = reader.ReadSingle();
if (version[0] >= 2017)//2017.x and up
{
m_WrapMode = reader.ReadInt32(); //m_WrapU
int m_WrapV = reader.ReadInt32();
int m_WrapW = reader.ReadInt32();
}
else
{
m_WrapMode = reader.ReadInt32();
}
}
}
public sealed class Texture2D : Texture
{
public int m_Width;
public int m_Height;
public int m_CompleteImageSize;
public TextureFormat m_TextureFormat;
public bool m_MipMap;
public int m_MipCount;
public GLTextureSettings m_TextureSettings;
public int m_ImageCount;
public byte[] m_PlatformBlob;
public ResourceReader image_data;
public StreamingInfo m_StreamData;
public Texture2D() { }
public Texture2D(Texture2DArray m_Texture2DArray, int layer) // Texture2DArrayImage
{
reader = m_Texture2DArray.reader;
assetsFile = m_Texture2DArray.assetsFile;
version = m_Texture2DArray.version;
platform = m_Texture2DArray.platform;
m_Name = $"{m_Texture2DArray.m_Name}_{layer + 1}";
type = ClassIDType.Texture2DArrayImage;
m_PathID = m_Texture2DArray.m_PathID;
m_Width = m_Texture2DArray.m_Width;
m_Height = m_Texture2DArray.m_Height;
m_TextureFormat = m_Texture2DArray.m_Format.ToTextureFormat();
m_MipCount = m_Texture2DArray.m_MipCount;
m_TextureSettings = m_Texture2DArray.m_TextureSettings;
m_StreamData = m_Texture2DArray.m_StreamData;
m_PlatformBlob = Array.Empty<byte>();
m_MipMap = m_MipCount > 1;
m_ImageCount = 1;
//var imgActualDataSize = GetImageDataSize(m_TextureFormat);
//var mipmapSize = (int)(m_Texture2DArray.m_DataSize / m_Texture2DArray.m_Depth - imgActualDataSize);
m_CompleteImageSize = (int)m_Texture2DArray.m_DataSize / m_Texture2DArray.m_Depth;
var offset = layer * m_CompleteImageSize + m_Texture2DArray.image_data.Offset;
image_data = !string.IsNullOrEmpty(m_StreamData?.path)
? new ResourceReader(m_StreamData.path, assetsFile, offset, m_CompleteImageSize)
: new ResourceReader(reader, offset, m_CompleteImageSize);
byteSize = (uint)(m_Width * m_Height) * 4;
}
public Texture2D(ObjectReader reader, IDictionary typeDict) : base(reader)
{
var parsedTex2d = JsonConvert.DeserializeObject<Texture2D>(JsonConvert.SerializeObject(typeDict));
m_Width = parsedTex2d.m_Width;
m_Height = parsedTex2d.m_Height;
m_CompleteImageSize = parsedTex2d.m_CompleteImageSize;
m_TextureFormat = parsedTex2d.m_TextureFormat;
m_MipMap = parsedTex2d.m_MipMap;
m_MipCount = parsedTex2d.m_MipCount;
m_ImageCount = parsedTex2d.m_ImageCount;
m_TextureSettings = parsedTex2d.m_TextureSettings;
m_StreamData = parsedTex2d.m_StreamData;
m_PlatformBlob = parsedTex2d.m_PlatformBlob ?? Array.Empty<byte>();
image_data = !string.IsNullOrEmpty(m_StreamData?.path)
? new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size)
: new ResourceReader(reader, parsedTex2d.image_data.Offset, parsedTex2d.image_data.Size);
typeDict.Clear();
}
public Texture2D(ObjectReader reader) : base(reader)
{
m_Width = reader.ReadInt32();
m_Height = reader.ReadInt32();
var m_CompleteImageSize = reader.ReadInt32();
if (version[0] >= 2020) //2020.1 and up
m_CompleteImageSize = reader.ReadInt32();
if (version >= 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
if (version < (5, 2)) //5.2 down
{
m_MipMap = reader.ReadBoolean();
}
@@ -81,140 +91,132 @@ namespace AssetStudio
{
m_MipCount = reader.ReadInt32();
}
if (version[0] > 2 || (version[0] == 2 && version[1] >= 6)) //2.6.0 and up
if (version >= (2, 6)) //2.6.0 and up
{
var m_IsReadable = reader.ReadBoolean();
}
if (version[0] >= 2020) //2020.1 and up
if (version >= 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 >= (2019, 3)) //2019.3 and up
{
var m_IgnoreMasterTextureLimit = reader.ReadBoolean();
}
if (version[0] >= 3) //3.0.0 - 5.4
{
if (version[0] < 5 || (version[0] == 5 && version[1] <= 4))
if (version >= (2022, 2)) //2022.2 and up
{
var m_ReadAllowed = reader.ReadBoolean();
var m_IgnoreMipmapLimit = reader.ReadBoolean();
reader.AlignStream();
}
else
{
var m_IgnoreMasterTextureLimit = reader.ReadBoolean();
}
}
if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up
if (version.IsInRange(3, (5, 5))) //3.0.0 - 5.4
{
var m_ReadAllowed = reader.ReadBoolean();
}
if (version >= (2022, 2)) //2022.2 and up
{
var m_MipmapLimitGroupName = reader.ReadAlignedString();
}
if (version >= (2018, 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
if (version >= (2018, 2)) //2018.2 and up
{
var m_StreamingMipmapsPriority = reader.ReadInt32();
}
var m_ImageCount = reader.ReadInt32();
m_ImageCount = reader.ReadInt32();
var m_TextureDimension = reader.ReadInt32();
m_TextureSettings = new GLTextureSettings(reader);
if (version[0] >= 3) //3.0 and up
if (version >= 3) //3.0 and up
{
var m_LightmapFormat = reader.ReadInt32();
}
if (version[0] > 3 || (version[0] == 3 && version[1] >= 5)) //3.5.0 and up
if (version >= (3, 5)) //3.5.0 and up
{
var m_ColorSpace = reader.ReadInt32();
}
if (version[0] > 2020 || (version[0] == 2020 && version[1] >= 2)) //2020.2 and up
if (version >= (2020, 2)) //2020.2 and up
{
var m_PlatformBlob = reader.ReadUInt8Array();
m_PlatformBlob = reader.ReadUInt8Array();
reader.AlignStream();
}
else
{
m_PlatformBlob = Array.Empty<byte>();
}
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
if (image_data_size == 0 && version >= (5, 3))//5.3.0 and up
{
m_StreamData = new StreamingInfo(reader);
}
ResourceReader resourceReader;
if (!string.IsNullOrEmpty(m_StreamData?.path))
image_data = !string.IsNullOrEmpty(m_StreamData?.path)
? new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size)
: new ResourceReader(reader, reader.BaseStream.Position, image_data_size);
}
// https://docs.unity3d.com/2023.3/Documentation/Manual/class-TextureImporterOverride.html
private int GetImageDataSize(TextureFormat textureFormat)
{
var imgDataSize = m_Width * m_Height;
switch (textureFormat)
{
resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size);
case TextureFormat.ASTC_RGBA_5x5:
// https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_astc/
imgDataSize = (int)(Math.Floor((m_Width + 4) / 5f) * Math.Floor((m_Height + 4) / 5f) * 16);
break;
case TextureFormat.ASTC_RGBA_6x6:
imgDataSize = (int)(Math.Floor((m_Width + 5) / 6f) * Math.Floor((m_Height + 5) / 6f) * 16);
break;
case TextureFormat.ASTC_RGBA_8x8:
imgDataSize = (int)(Math.Floor((m_Width + 7) / 8f) * Math.Floor((m_Height + 7) / 8f) * 16);
break;
case TextureFormat.ASTC_RGBA_10x10:
imgDataSize = (int)(Math.Floor((m_Width + 9) / 10f) * Math.Floor((m_Height + 9) / 10f) * 16);
break;
case TextureFormat.ASTC_RGBA_12x12:
imgDataSize = (int)(Math.Floor((m_Width + 11) / 12f) * Math.Floor((m_Height + 11) / 12f) * 16);
break;
case TextureFormat.DXT1:
case TextureFormat.EAC_R:
case TextureFormat.EAC_R_SIGNED:
case TextureFormat.ATC_RGB4:
case TextureFormat.ETC_RGB4:
case TextureFormat.ETC2_RGB:
case TextureFormat.ETC2_RGBA1:
case TextureFormat.PVRTC_RGBA4:
imgDataSize /= 2;
break;
case TextureFormat.PVRTC_RGBA2:
imgDataSize /= 4;
break;
case TextureFormat.R16:
case TextureFormat.RGB565:
imgDataSize *= 2;
break;
case TextureFormat.RGB24:
imgDataSize *= 3;
break;
case TextureFormat.RG32:
case TextureFormat.RGBA32:
case TextureFormat.ARGB32:
case TextureFormat.BGRA32:
case TextureFormat.RGB9e5Float:
imgDataSize *= 4;
break;
case TextureFormat.RGB48:
imgDataSize *= 6;
break;
case TextureFormat.RGBAHalf:
case TextureFormat.RGBA64:
imgDataSize *= 8;
break;
}
else
{
resourceReader = new ResourceReader(reader, reader.BaseStream.Position, image_data_size);
}
image_data = resourceReader;
return imgDataSize;
}
}
public enum TextureFormat
{
Alpha8 = 1,
ARGB4444,
RGB24,
RGBA32,
ARGB32,
ARGBFloat,
RGB565,
BGR24,
R16,
DXT1,
DXT3,
DXT5,
RGBA4444,
BGRA32,
RHalf,
RGHalf,
RGBAHalf,
RFloat,
RGFloat,
RGBAFloat,
YUY2,
RGB9e5Float,
RGBFloat,
BC6H,
BC7,
BC4,
BC5,
DXT1Crunched,
DXT5Crunched,
PVRTC_RGB2,
PVRTC_RGBA2,
PVRTC_RGB4,
PVRTC_RGBA4,
ETC_RGB4,
ATC_RGB4,
ATC_RGBA8,
EAC_R = 41,
EAC_R_SIGNED,
EAC_RG,
EAC_RG_SIGNED,
ETC2_RGB,
ETC2_RGBA1,
ETC2_RGBA8,
ASTC_RGB_4x4,
ASTC_RGB_5x5,
ASTC_RGB_6x6,
ASTC_RGB_8x8,
ASTC_RGB_10x10,
ASTC_RGB_12x12,
ASTC_RGBA_4x4,
ASTC_RGBA_5x5,
ASTC_RGBA_6x6,
ASTC_RGBA_8x8,
ASTC_RGBA_10x10,
ASTC_RGBA_12x12,
ETC_RGB4_3DS,
ETC_RGBA8_3DS,
RG16,
R8,
ETC_RGB4Crunched,
ETC2_RGBA8Crunched,
ASTC_HDR_4x4,
ASTC_HDR_5x5,
ASTC_HDR_6x6,
ASTC_HDR_8x8,
ASTC_HDR_10x10,
ASTC_HDR_12x12,
RG32,
RGB48,
RGBA64
}
}

View File

@@ -0,0 +1,73 @@
using Newtonsoft.Json;
using System.Collections;
using System.Collections.Generic;
namespace AssetStudio
{
public sealed class Texture2DArray : Texture
{
public int m_Width;
public int m_Height;
public int m_Depth;
public GraphicsFormat m_Format;
public int m_MipCount;
public uint m_DataSize;
public GLTextureSettings m_TextureSettings;
public int m_ColorSpace;
public ResourceReader image_data;
public StreamingInfo m_StreamData;
public List<Texture2D> TextureList;
public Texture2DArray() { }
public Texture2DArray(ObjectReader reader) : base(reader)
{
m_ColorSpace = reader.ReadInt32();
m_Format = (GraphicsFormat)reader.ReadInt32();
m_Width = reader.ReadInt32();
m_Height = reader.ReadInt32();
m_Depth = reader.ReadInt32();
m_MipCount = reader.ReadInt32();
m_DataSize = reader.ReadUInt32();
m_TextureSettings = new GLTextureSettings(reader);
if (version >= (2020, 2)) //2020.2 and up
{
var m_UsageMode = reader.ReadInt32();
}
var m_IsReadable = reader.ReadBoolean();
reader.AlignStream();
var image_data_size = reader.ReadInt32();
if (image_data_size == 0)
{
m_StreamData = new StreamingInfo(reader);
}
image_data = !string.IsNullOrEmpty(m_StreamData?.path)
? new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, (int)m_StreamData.size)
: new ResourceReader(reader, reader.BaseStream.Position, image_data_size);
TextureList = new List<Texture2D>();
}
public Texture2DArray(ObjectReader reader, IDictionary typeDict) : base(reader)
{
var parsedTex2dArray = JsonConvert.DeserializeObject<Texture2DArray>(JsonConvert.SerializeObject(typeDict));
m_Width = parsedTex2dArray.m_Width;
m_Height = parsedTex2dArray.m_Height;
m_Depth = parsedTex2dArray.m_Depth;
m_Format = parsedTex2dArray.m_Format;
m_MipCount = parsedTex2dArray.m_MipCount;
m_DataSize = parsedTex2dArray.m_DataSize;
m_TextureSettings = parsedTex2dArray.m_TextureSettings;
m_StreamData = parsedTex2dArray.m_StreamData;
image_data = !string.IsNullOrEmpty(m_StreamData?.path)
? new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size)
: new ResourceReader(reader, parsedTex2dArray.image_data.Offset, parsedTex2dArray.image_data.Size);
typeDict.Clear();
TextureList = new List<Texture2D>();
}
}
}

View File

@@ -0,0 +1,282 @@
namespace AssetStudio
{
public enum TextureFormat
{
/// <summary>
/// Alpha-only texture format, 8 bit integer.
/// </summary>
Alpha8 = 1,
/// <summary>
/// A 16 bits/pixel texture format. Texture stores color with an alpha channel.
/// </summary>
ARGB4444,
/// <summary>
/// Three channel (RGB) texture format, 8-bits unsigned integer per channel.
/// </summary>
RGB24,
/// <summary>
/// Four channel (RGBA) texture format, 8-bits unsigned integer per channel.
/// </summary>
RGBA32,
/// <summary>
/// Color with alpha texture format, 8-bits per channel.
/// </summary>
ARGB32,
/// <summary>
/// </summary>
ARGBFloat,
/// <summary>
/// A 16 bit color texture format.
/// </summary>
RGB565,
/// <summary>
/// </summary>
BGR24,
/// <summary>
/// Single channel (R) texture format, 16 bit integer.
/// </summary>
R16,
/// <summary>
/// Compressed color texture format.
/// </summary>
DXT1,
/// <summary>
/// </summary>
DXT3,
/// <summary>
/// Compressed color with alpha channel texture format.
/// </summary>
DXT5,
/// <summary>
/// Color and alpha texture format, 4 bit per channel.
/// </summary>
RGBA4444,
/// <summary>
/// Color with alpha texture format, 8-bits per channel.
/// </summary>
BGRA32,
/// <summary>
/// Scalar (R) texture format, 16 bit floating point.
/// </summary>
RHalf,
/// <summary>
/// Two color (RG) texture format, 16 bit floating point per channel.
/// </summary>
RGHalf,
/// <summary>
/// RGB color and alpha texture format, 16 bit floating point per channel.
/// </summary>
RGBAHalf,
/// <summary>
/// Scalar (R) texture format, 32 bit floating point.
/// </summary>
RFloat,
/// <summary>
/// Two color (RG) texture format, 32 bit floating point per channel.
/// </summary>
RGFloat,
/// <summary>
/// RGB color and alpha texture format, 32-bit floats per channel.
/// </summary>
RGBAFloat,
/// <summary>
/// A format that uses the YUV color space and is often used for video encoding or playback.
/// </summary>
YUY2,
/// <summary>
/// RGB HDR format, with 9 bit mantissa per channel and a 5 bit shared exponent.
/// </summary>
RGB9e5Float,
/// <summary>
/// </summary>
RGBFloat,
/// <summary>
/// HDR compressed color texture format.
/// </summary>
BC6H,
/// <summary>
/// High quality compressed color texture format.
/// </summary>
BC7,
/// <summary>
/// Compressed one channel (R) texture format.
/// </summary>
BC4,
/// <summary>
/// Compressed two-channel (RG) texture format.
/// </summary>
BC5,
/// <summary>
/// Compressed color texture format with Crunch compression for smaller storage sizes.
/// </summary>
DXT1Crunched,
/// <summary>
/// Compressed color with alpha channel texture format with Crunch compression for smaller storage sizes.
/// </summary>
DXT5Crunched,
/// <summary>
/// PowerVR (iOS) 2 bits/pixel compressed color texture format.
/// </summary>
PVRTC_RGB2,
/// <summary>
/// PowerVR (iOS) 2 bits/pixel compressed with alpha channel texture format.
/// </summary>
PVRTC_RGBA2,
/// <summary>
/// PowerVR (iOS) 4 bits/pixel compressed color texture format.
/// </summary>
PVRTC_RGB4,
/// <summary>
/// PowerVR (iOS) 4 bits/pixel compressed with alpha channel texture format.
/// </summary>
PVRTC_RGBA4,
/// <summary>
/// ETC (GLES2.0) 4 bits/pixel compressed RGB texture format.
/// </summary>
ETC_RGB4,
/// <summary>
/// ATC (ATITC) 4 bits/pixel compressed RGB texture format.
/// </summary>
ATC_RGB4,
/// <summary>
/// ATC (ATITC) 8 bits/pixel compressed RGB texture format.
/// </summary>
ATC_RGBA8,
/// <summary>
/// ETC2 / EAC (GL ES 3.0) 4 bits/pixel compressed unsigned single-channel texture format.
/// </summary>
EAC_R = 41,
/// <summary>
/// ETC2 / EAC (GL ES 3.0) 4 bits/pixel compressed signed single-channel texture format.
/// </summary>
EAC_R_SIGNED,
/// <summary>
/// ETC2 / EAC (GL ES 3.0) 8 bits/pixel compressed unsigned dual-channel (RG) texture format.
/// </summary>
EAC_RG,
/// <summary>
/// ETC2 / EAC (GL ES 3.0) 8 bits/pixel compressed signed dual-channel (RG) texture format.
/// </summary>
EAC_RG_SIGNED,
/// <summary>
/// ETC2 (GL ES 3.0) 4 bits/pixel compressed RGB texture format.
/// </summary>
ETC2_RGB,
/// <summary>
/// ETC2 (GL ES 3.0) 4 bits/pixel RGB+1-bit alpha texture format.
/// </summary>
ETC2_RGBA1,
/// <summary>
/// ETC2 (GL ES 3.0) 8 bits/pixel compressed RGBA texture format.
/// </summary>
ETC2_RGBA8,
/// <summary>
/// ASTC (4x4 pixel block in 128 bits) compressed RGB texture format.
/// </summary>
ASTC_RGB_4x4,
/// <summary>
/// ASTC (5x5 pixel block in 128 bits) compressed RGB texture format.
/// </summary>
ASTC_RGB_5x5,
/// <summary>
/// ASTC (6x6 pixel block in 128 bits) compressed RGB texture format.
/// </summary>
ASTC_RGB_6x6,
/// <summary>
/// ASTC (8x8 pixel block in 128 bits) compressed RGB texture format.
/// </summary>
ASTC_RGB_8x8,
/// <summary>
/// ASTC (10x10 pixel block in 128 bits) compressed RGB texture format.
/// </summary>
ASTC_RGB_10x10,
/// <summary>
/// ASTC (12x12 pixel block in 128 bits) compressed RGB texture format.
/// </summary>
ASTC_RGB_12x12,
/// <summary>
/// ASTC (4x4 pixel block in 128 bits) compressed RGBA texture format.
/// </summary>
ASTC_RGBA_4x4,
/// <summary>
/// ASTC (5x5 pixel block in 128 bits) compressed RGBA texture format.
/// </summary>
ASTC_RGBA_5x5,
/// <summary>
/// ASTC (6x6 pixel block in 128 bits) compressed RGBA texture format.
/// </summary>
ASTC_RGBA_6x6,
/// <summary>
/// ASTC (8x8 pixel block in 128 bits) compressed RGBA texture format.
/// </summary>
ASTC_RGBA_8x8,
/// <summary>
/// ASTC (10x10 pixel block in 128 bits) compressed RGBA texture format.
/// </summary>
ASTC_RGBA_10x10,
/// <summary>
/// ASTC (12x12 pixel block in 128 bits) compressed RGBA texture format.
/// </summary>
ASTC_RGBA_12x12,
/// <summary>
/// ETC 4 bits/pixel compressed RGB texture format.
/// </summary>
ETC_RGB4_3DS,
/// <summary>
/// ETC 4 bits/pixel RGB + 4 bits/pixel Alpha compressed texture format.
/// </summary>
ETC_RGBA8_3DS,
/// <summary>
/// Two color (RG) texture format, 8-bits per channel.
/// </summary>
RG16,
/// <summary>
/// Single channel (R) texture format, 8 bit integer.
/// </summary>
R8,
/// <summary>
/// Compressed color texture format with Crunch compression for smaller storage sizes.
/// </summary>
ETC_RGB4Crunched,
/// <summary>
/// Compressed color with alpha channel texture format using Crunch compression for smaller storage sizes.
/// </summary>
ETC2_RGBA8Crunched,
/// <summary>
/// ASTC (4x4 pixel block in 128 bits) compressed RGB(A) HDR texture format.
/// </summary>
ASTC_HDR_4x4,
/// <summary>
/// ASTC (5x5 pixel block in 128 bits) compressed RGB(A) HDR texture format.
/// </summary>
ASTC_HDR_5x5,
/// <summary>
/// ASTC (6x6 pixel block in 128 bits) compressed RGB(A) HDR texture format.
/// </summary>
ASTC_HDR_6x6,
/// <summary>
/// ASTC (8x8 pixel block in 128 bits) compressed RGB(A) texture format.
/// </summary>
ASTC_HDR_8x8,
/// <summary>
/// ASTC (10x10 pixel block in 128 bits) compressed RGB(A) HDR texture format.
/// </summary>
ASTC_HDR_10x10,
/// <summary>
/// ASTC (12x12 pixel block in 128 bits) compressed RGB(A) HDR texture format.
/// </summary>
ASTC_HDR_12x12,
/// <summary>
/// Two channel (RG) texture format, 16-bits unsigned integer per channel.
/// </summary>
RG32,
/// <summary>
/// Three channel (RGB) texture format, 16-bits unsigned integer per channel.
/// </summary>
RGB48,
/// <summary>
/// Four channel (RGBA) texture format, 16-bits unsigned integer per channel.
/// </summary>
RGBA64,
}
}

View File

@@ -21,27 +21,32 @@ namespace AssetStudio
public ResourceReader m_VideoData;
public string m_OriginalPath;
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();
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 2)) //2017.2 and up
Width = reader.ReadUInt32();
Height = reader.ReadUInt32();
if (version >= (2017, 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();
if (version[0] >= 2020) //2020.1 and up
if (version >= 2020) //2020.1 and up
{
var m_VideoShadersSize = reader.ReadInt32();
var m_VideoShaders = new PPtr<Shader>[m_VideoShadersSize];
@@ -51,8 +56,8 @@ namespace AssetStudio
}
}
m_ExternalResources = new StreamedResource(reader);
var m_HasSplitAlpha = reader.ReadBoolean();
if (version[0] >= 2020) //2020.1 and up
m_HasSplitAlpha = reader.ReadBoolean();
if (version >= 2020) //2020.1 and up
{
var m_sRGB = reader.ReadBoolean();
}

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);
}
}
}

149
AssetStudio/CubismModel.cs Normal file
View File

@@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using static AssetStudio.EndianSpanReader;
namespace AssetStudio
{
public enum CubismSDKVersion : byte
{
V30 = 1,
V33,
V40,
V42,
V50
}
public sealed class CubismModel : IDisposable
{
public CubismSDKVersion Version { get; }
public string VersionDescription { get; }
public float CanvasWidth { get; }
public float CanvasHeight { get; }
public float CentralPosX { get; }
public float CentralPosY { get; }
public float PixelPerUnit { get; }
public uint PartCount { get; }
public uint ParamCount { get; }
public HashSet<string> PartNames { get; }
public HashSet<string> ParamNames { get; }
private byte[] modelData;
private int modelDataSize;
private bool isBigEndian;
public CubismModel(MonoBehaviour moc)
{
var reader = moc.reader;
reader.Reset();
reader.Position += 28; //PPtr<GameObject> m_GameObject, m_Enabled, PPtr<MonoScript>
reader.ReadAlignedString(); //m_Name
modelDataSize = (int)reader.ReadUInt32();
modelData = BigArrayPool<byte>.Shared.Rent(modelDataSize);
_ = reader.Read(modelData, 0, modelDataSize);
var sdkVer = modelData[4];
if (Enum.IsDefined(typeof(CubismSDKVersion), sdkVer))
{
Version = (CubismSDKVersion)sdkVer;
VersionDescription = ParseVersion();
}
else
{
var msg = $"Unknown SDK version ({sdkVer})";
VersionDescription = msg;
Version = 0;
Logger.Warning($"Live2D model \"{moc.m_Name}\": " + msg);
return;
}
isBigEndian = BitConverter.ToBoolean(modelData, 5);
//offsets
var countInfoTableOffset = (int)SpanToUint32(modelData, 64, isBigEndian);
var canvasInfoOffset = (int)SpanToUint32(modelData, 68, isBigEndian);
var partIdsOffset = SpanToUint32(modelData, 76, isBigEndian);
var parameterIdsOffset = SpanToUint32(modelData, 264, isBigEndian);
//canvas
PixelPerUnit = ToSingle(modelData, canvasInfoOffset, isBigEndian);
CentralPosX = ToSingle(modelData, canvasInfoOffset + 4, isBigEndian);
CentralPosY = ToSingle(modelData, canvasInfoOffset + 8, isBigEndian);
CanvasWidth = ToSingle(modelData, canvasInfoOffset + 12, isBigEndian);
CanvasHeight = ToSingle(modelData, canvasInfoOffset + 16, isBigEndian);
//model
PartCount = SpanToUint32(modelData, countInfoTableOffset, isBigEndian);
ParamCount = SpanToUint32(modelData, countInfoTableOffset + 20, isBigEndian);
PartNames = ReadMocStringHashSet(modelData, (int)partIdsOffset, (int)PartCount);
ParamNames = ReadMocStringHashSet(modelData, (int)parameterIdsOffset, (int)ParamCount);
}
public void SaveMoc3(string savePath)
{
if (!savePath.EndsWith(".moc3"))
savePath += ".moc3";
using (var file = File.OpenWrite(savePath))
{
file.Write(modelData, 0, modelDataSize);
}
}
private string ParseVersion()
{
switch (Version)
{
case CubismSDKVersion.V30:
return "SDK3.0/Cubism3.0(3.2)";
case CubismSDKVersion.V33:
return "SDK3.3/Cubism3.3";
case CubismSDKVersion.V40:
return "SDK4.0/Cubism4.0";
case CubismSDKVersion.V42:
return "SDK4.2/Cubism4.2";
case CubismSDKVersion.V50:
return "SDK5.0/Cubism5.0";
default:
return "";
}
}
private static float ToSingle(ReadOnlySpan<byte> data, int index, bool isBigEndian) //net framework ver
{
var bytes = data.Slice(index, index + 4).ToArray();
if ((isBigEndian && BitConverter.IsLittleEndian) || (!isBigEndian && !BitConverter.IsLittleEndian))
(bytes[0], bytes[1], bytes[2], bytes[3]) = (bytes[3], bytes[2], bytes[1], bytes[0]);
return BitConverter.ToSingle(bytes, 0);
}
private static HashSet<string> ReadMocStringHashSet(ReadOnlySpan<byte> data, int index, int count)
{
const int strLen = 64;
var strHashSet = new HashSet<string>();
for (var i = 0; i < count; i++)
{
if (index + i * strLen <= data.Length)
{
var buff = data.Slice(index + i * strLen, strLen);
strHashSet.Add(Encoding.UTF8.GetString(buff.ToArray()).TrimEnd('\0'));
}
}
return strHashSet;
}
private void Dispose(bool disposing)
{
if (disposing)
{
BigArrayPool<byte>.Shared.Return(modelData, clearArray: true);
}
}
public void Dispose()
{
Dispose(true);
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Buffers.Binary;
namespace AssetStudio
{
public static class EndianSpanReader
{
public static uint SpanToUint32(Span<byte> data, int start, bool isBigEndian)
{
return isBigEndian
? BinaryPrimitives.ReadUInt32BigEndian(data.Slice(start))
: BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(start));
}
public static uint SpanToUint16(Span<byte> data, int start, bool isBigEndian)
{
return isBigEndian
? BinaryPrimitives.ReadUInt16BigEndian(data.Slice(start))
: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(start));
}
public static long SpanToInt64(Span<byte> data, int start, bool isBigEndian)
{
return isBigEndian
? BinaryPrimitives.ReadInt64BigEndian(data.Slice(start))
: BinaryPrimitives.ReadInt64LittleEndian(data.Slice(start));
}
}
}

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)
{
var bytes = new List<byte>();
int count = 0;
if (encoding?.CodePage == 1200) //Unicode (UTF-16LE)
return reader.ReadUnicodeStringToNull(maxLength * 2);
Span<byte> bytes = stackalloc byte[maxLength];
var count = 0;
while (reader.BaseStream.Position != reader.BaseStream.Length && count < maxLength)
{
var b = reader.ReadByte();
@@ -46,10 +49,28 @@ namespace AssetStudio
{
break;
}
bytes.Add(b);
bytes[count] = b;
count++;
}
return Encoding.UTF8.GetString(bytes.ToArray());
bytes = bytes.Slice(0, count);
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)

View File

@@ -1,5 +1,6 @@
using System.IO;
using System.Linq;
using System;
using System.IO;
using static AssetStudio.EndianSpanReader;
namespace AssetStudio
{
@@ -37,66 +38,69 @@ namespace AssetStudio
case "UnityWebData1.0":
return FileType.WebFile;
default:
{
var buff = ReadBytes(40).AsSpan();
var magic = Span<byte>.Empty;
Position = 0;
magic = buff.Length > 2 ? buff.Slice(0, 2) : magic;
if (magic.SequenceEqual(gzipMagic))
{
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;
return FileType.GZipFile;
}
magic = buff.Length > 38 ? buff.Slice(32, 6) : magic;
if (magic.SequenceEqual(brotliMagic))
{
return FileType.BrotliFile;
}
if (IsSerializedFile(buff))
{
return FileType.AssetsFile;
}
magic = buff.Length > 4 ? buff.Slice(0, 4): magic;
if (magic.SequenceEqual(zipMagic) || magic.SequenceEqual(zipSpannedMagic))
{
return FileType.ZipFile;
}
return FileType.ResourceFile;
}
}
}
private bool IsSerializedFile()
private bool IsSerializedFile(Span<byte> buff)
{
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);
var isBigEndian = Endian == EndianType.BigEndian;
//var m_MetadataSize = SpanToUint32(buff, 0, isBigEndian);
long m_FileSize = SpanToUint32(buff, 4, isBigEndian);
var m_Version = SpanToUint32(buff, 8, isBigEndian);
long m_DataOffset = SpanToUint32(buff, 12, isBigEndian);
//var m_Endianess = buff[16];
//var m_Reserved = buff.Slice(17, 3);
if (m_Version >= 22)
{
if (fileSize < 48)
{
Position = 0;
return false;
}
m_MetadataSize = ReadUInt32();
m_FileSize = ReadInt64();
m_DataOffset = ReadInt64();
//m_MetadataSize = SpanToUint32(buff, 20, isBigEndian);
m_FileSize = SpanToInt64(buff, 24, isBigEndian);
m_DataOffset = SpanToInt64(buff, 32, isBigEndian);
}
Position = 0;
if (m_FileSize != fileSize)
{
return false;
}
if (m_DataOffset > fileSize)
if (m_FileSize != fileSize || m_DataOffset > fileSize)
{
return false;
}
return true;
}
}

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

@@ -53,15 +53,24 @@ namespace AssetStudio
public static FileReader DecompressGZip(FileReader reader)
{
using (reader)
try
{
var stream = new MemoryStream();
using (var gs = new GZipStream(reader.BaseStream, CompressionMode.Decompress))
using (reader)
{
gs.CopyTo(stream);
var stream = new MemoryStream();
using (var gs = new GZipStream(reader.BaseStream, CompressionMode.Decompress))
{
gs.CopyTo(stream);
}
stream.Position = 0;
return new FileReader(reader.FullPath, 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;
}
}

View File

@@ -19,6 +19,7 @@ namespace AssetStudio
{
var sb = new StringBuilder();
sb.AppendLine(message);
sb.AppendLine();
sb.AppendLine(e.ToString());
Default.Log(LoggerEvent.Error, sb.ToString());
}

View File

@@ -17,7 +17,7 @@ namespace AssetStudio
public BuildTarget platform;
public SerializedFileFormatVersion m_Version;
public int[] version => assetsFile.version;
public UnityVersion version => assetsFile.version;
public BuildType buildType => assetsFile.buildType;
public ObjectReader(EndianBinaryReader reader, SerializedFile assetsFile, ObjectInfo objectInfo) : base(reader.BaseStream, reader.Endian)

View File

@@ -7,25 +7,31 @@ namespace AssetStudio
private bool needSearch;
private string path;
private SerializedFile assetsFile;
private long offset;
private long size;
private BinaryReader reader;
public int Size { get => (int)size; }
public int Size
{
get => (int)size;
set => size = value;
}
public long Offset { get; set; }
public ResourceReader() { }
public ResourceReader(string path, SerializedFile assetsFile, long offset, long size)
{
needSearch = true;
this.path = path;
this.assetsFile = assetsFile;
this.offset = offset;
this.Offset = offset;
this.size = size;
}
public ResourceReader(BinaryReader reader, long offset, long size)
{
this.reader = reader;
this.offset = offset;
this.Offset = offset;
this.size = size;
}
@@ -52,8 +58,12 @@ namespace AssetStudio
if (File.Exists(resourceFilePath))
{
needSearch = false;
if (assetsFile.assetsManager.resourceFileReaders.TryGetValue(resourceFileName, out reader))
{
return reader;
}
reader = new BinaryReader(File.OpenRead(resourceFilePath));
assetsFile.assetsManager.resourceFileReaders.Add(resourceFileName, reader);
assetsFile.assetsManager.resourceFileReaders.TryAdd(resourceFileName, reader);
return reader;
}
throw new FileNotFoundException($"Can't find the resource file {resourceFileName}");
@@ -67,21 +77,27 @@ namespace AssetStudio
public byte[] GetData()
{
var binaryReader = GetReader();
binaryReader.BaseStream.Position = offset;
return binaryReader.ReadBytes((int)size);
lock (binaryReader)
{
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);
lock (binaryReader)
{
binaryReader.BaseStream.Position = Offset;
binaryReader.Read(buff, 0, (int) size);
}
}
public void WriteData(string path)
{
var binaryReader = GetReader();
binaryReader.BaseStream.Position = offset;
binaryReader.BaseStream.Position = Offset;
using (var writer = File.OpenWrite(path))
{
binaryReader.BaseStream.CopyTo(writer, size);

View File

@@ -13,7 +13,7 @@ namespace AssetStudio
public string fullName;
public string originalPath;
public string fileName;
public int[] version = { 0, 0, 0, 0 };
public UnityVersion version = new UnityVersion();
public BuildType buildType;
public List<Object> Objects;
public Dictionary<long, Object> ObjectsDic;
@@ -73,7 +73,7 @@ namespace AssetStudio
if (header.m_Version >= SerializedFileFormatVersion.Unknown_7)
{
unityVersion = reader.ReadStringToNull();
SetVersion(unityVersion);
SetVersion(new UnityVersion(unityVersion));
}
if (header.m_Version >= SerializedFileFormatVersion.Unknown_8)
{
@@ -217,15 +217,13 @@ namespace AssetStudio
//reader.AlignStream(16);
}
public void SetVersion(string stringVersion)
public void SetVersion(UnityVersion unityVer)
{
if (stringVersion != strippedVersion)
if (unityVer != null && !unityVer.IsStripped)
{
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();
unityVersion = unityVer.FullVersion;
buildType = new BuildType(unityVer.BuildType);
version = unityVer;
}
}
@@ -373,9 +371,5 @@ namespace AssetStudio
Objects.Add(obj);
ObjectsDic.Add(obj.m_PathID, obj);
}
public bool IsVersionStripped => unityVersion == strippedVersion;
private const string strippedVersion = "0.0.0";
}
}

View File

@@ -20,7 +20,7 @@ namespace AssetStudio
var readed = reader.Position - reader.byteStart;
if (readed != reader.byteSize)
{
Logger.Info($"Error while read type, read {readed} bytes but expected {reader.byteSize} bytes");
Logger.Info($"Failed to read type, read {readed} bytes but expected {reader.byteSize} bytes");
}
return sb.ToString();
}
@@ -116,7 +116,7 @@ namespace AssetStudio
{
append = false;
var size = reader.ReadInt32();
reader.ReadBytes(size);
reader.BaseStream.Position += size;
i += 2;
sb.AppendFormat("{0}{1} {2}\r\n", (new string('\t', level)), varTypeStr, varNameStr);
sb.AppendFormat("{0}{1} {2} = {3}\r\n", (new string('\t', level)), "int", "size", size);
@@ -171,13 +171,13 @@ namespace AssetStudio
for (int i = 1; i < m_Nodes.Count; i++)
{
var m_Node = m_Nodes[i];
var varNameStr = m_Node.m_Name;
var varNameStr = m_Node.m_Name.Replace("image data", "image_data");
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");
Logger.Info($"Failed to read type, read {readed} bytes but expected {reader.byteSize} bytes");
}
return obj;
}
@@ -262,7 +262,13 @@ namespace AssetStudio
case "TypelessData":
{
var size = reader.ReadInt32();
value = reader.ReadBytes(size);
var dic = new OrderedDictionary
{
{ "Offset", reader.BaseStream.Position },
{ "Size", size }
};
value = dic;
reader.BaseStream.Position += size;
i += 2;
break;
}

449
AssetStudio/UnityVersion.cs Normal file
View File

@@ -0,0 +1,449 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
namespace AssetStudio
{
public class UnityVersion : IComparable
{
public int Major { get; }
public int Minor { get; }
public int Patch { get; }
public int Build { get; }
public string BuildType { get; }
public string FullVersion { get; }
public bool IsStripped => this == (0, 0, 0);
public UnityVersion(string version)
{
if (string.IsNullOrEmpty(version))
throw new ArgumentException("Unity version cannot be empty.");
try
{
int[] ver = Regex.Matches(version, @"\d+").Cast<Match>().Select(x => int.Parse(x.Value)).ToArray();
(Major, Minor, Patch) = (ver[0], ver[1], ver[2]);
if (ver.Length == 4)
Build = ver[3];
FullVersion = version;
}
catch (Exception)
{
throw new NotSupportedException($"Failed to parse Unity version: \"{version}\".");
}
string[] build = Regex.Matches(version, @"\D+").Cast<Match>().Select(x => x.Value).ToArray();
if (build.Length > 2)
{
BuildType = build[2];
}
}
public UnityVersion(int major = 0, int minor = 0, int patch = 0)
{
(Major, Minor, Patch) = (major, minor, patch);
FullVersion = $"{Major}.{Minor}.{Patch}";
if (!IsStripped)
{
Build = 1;
BuildType = "f";
FullVersion += $"{BuildType}{Build}";
}
}
#region UnityVer, UnityVer
public static bool operator ==(UnityVersion left, UnityVersion right)
{
return Equals(left, right);
}
public static bool operator !=(UnityVersion left, UnityVersion right)
{
return !Equals(left, right);
}
public static bool operator >(UnityVersion left, UnityVersion right)
{
return left?.CompareTo(right) > 0;
}
public static bool operator <(UnityVersion left, UnityVersion right)
{
return left?.CompareTo(right) < 0;
}
public static bool operator >=(UnityVersion left, UnityVersion right)
{
return left == right || left > right;
}
public static bool operator <=(UnityVersion left, UnityVersion right)
{
return left == right || left < right;
}
#endregion
#region UnityVer, int
public static bool operator ==(UnityVersion left, int right)
{
return left?.Major == right;
}
public static bool operator !=(UnityVersion left, int right)
{
return left?.Major != right;
}
public static bool operator >(UnityVersion left, int right)
{
return left?.Major > right;
}
public static bool operator <(UnityVersion left, int right)
{
return left?.Major < right;
}
public static bool operator >=(UnityVersion left, int right)
{
return left?.Major >= right;
}
public static bool operator <=(UnityVersion left, int right)
{
return left?.Major <= right;
}
#endregion
#region UnityVer, (int, int)
public static bool operator ==(UnityVersion left, (int, int) right)
{
return (left?.Major, left?.Minor) == (right.Item1, right.Item2);
}
public static bool operator !=(UnityVersion left, (int, int) right)
{
return (left?.Major, left?.Minor) != (right.Item1, right.Item2);
}
public static bool operator >(UnityVersion left, (int, int) right)
{
return left?.CompareTo(right) > 0;
}
public static bool operator <(UnityVersion left, (int, int) right)
{
return left?.CompareTo(right) < 0;
}
public static bool operator >=(UnityVersion left, (int, int) right)
{
return left == right || left > right;
}
public static bool operator <=(UnityVersion left, (int, int) right)
{
return left == right || left < right;
}
#endregion
#region UnityVer, (int, int, int)
public static bool operator ==(UnityVersion left, (int, int, int) right)
{
return (left?.Major, left?.Minor, left?.Patch) == (right.Item1, right.Item2, right.Item3);
}
public static bool operator !=(UnityVersion left, (int, int, int) right)
{
return (left?.Major, left?.Minor, left?.Patch) != (right.Item1, right.Item2, right.Item3);
}
public static bool operator >(UnityVersion left, (int, int, int) right)
{
return left?.CompareTo(right) > 0;
}
public static bool operator <(UnityVersion left, (int, int, int) right)
{
return left?.CompareTo(right) < 0;
}
public static bool operator >=(UnityVersion left, (int, int, int) right)
{
return left == right || left > right;
}
public static bool operator <=(UnityVersion left, (int, int, int) right)
{
return left == right || left < right;
}
#endregion
#region int, UnityVer
public static bool operator ==(int left, UnityVersion right)
{
return left == right?.Major;
}
public static bool operator !=(int left, UnityVersion right)
{
return left != right?.Major;
}
public static bool operator >(int left, UnityVersion right)
{
return left > right?.Major;
}
public static bool operator <(int left, UnityVersion right)
{
return left < right?.Major;
}
public static bool operator >=(int left, UnityVersion right)
{
return left >= right?.Major;
}
public static bool operator <=(int left, UnityVersion right)
{
return left <= right?.Major;
}
#endregion
#region (int, int), UnityVer
public static bool operator ==((int, int) left, UnityVersion right)
{
return (left.Item1, left.Item2) == (right?.Major, right?.Minor);
}
public static bool operator !=((int, int) left, UnityVersion right)
{
return (left.Item1, left.Item2) != (right?.Major, right?.Minor);
}
public static bool operator >((int, int) left, UnityVersion right)
{
return right?.CompareTo(left) < 0;
}
public static bool operator <((int, int) left, UnityVersion right)
{
return right?.CompareTo(left) > 0;
}
public static bool operator >=((int, int) left, UnityVersion right)
{
return left == right || left > right;
}
public static bool operator <=((int, int) left, UnityVersion right)
{
return left == right || left < right;
}
#endregion
#region (int, int, int), UnityVer
public static bool operator ==((int, int, int) left, UnityVersion right)
{
return (left.Item1, left.Item2, left.Item3) == (right?.Major, right?.Minor, right?.Patch);
}
public static bool operator !=((int, int, int) left, UnityVersion right)
{
return (left.Item1, left.Item2, left.Item3) != (right?.Major, right?.Minor, right?.Patch);
}
public static bool operator >((int, int, int) left, UnityVersion right)
{
return right?.CompareTo(left) < 0;
}
public static bool operator <((int, int, int) left, UnityVersion right)
{
return right?.CompareTo(left) > 0;
}
public static bool operator >=((int, int, int) left, UnityVersion right)
{
return left == right || left > right;
}
public static bool operator <=((int, int, int) left, UnityVersion right)
{
return left == right || left < right;
}
#endregion
private int CompareTo((int, int) other)
{
var result = Major.CompareTo(other.Item1);
if (result != 0)
{
return result;
}
result = Minor.CompareTo(other.Item2);
if (result != 0)
{
return result;
}
return 0;
}
private int CompareTo((int, int, int) other)
{
var result = CompareTo((other.Item1, other.Item2));
if (result != 0)
{
return result;
}
result = Patch.CompareTo(other.Item3);
if (result != 0)
{
return result;
}
return 0;
}
private int CompareTo(UnityVersion other)
{
return CompareTo((other.Major, other.Minor, other.Patch));
}
private bool Equals(UnityVersion other)
{
return (Major, Minor, Patch) == (other.Major, other.Minor, other.Patch);
}
public override bool Equals(object other)
{
return other is UnityVersion otherUnityVer && Equals(otherUnityVer);
}
public override int GetHashCode()
{
var result = Major * 31;
result = result * 31 + Minor;
result = result * 31 + Patch;
result = result * 31 + Build;
return result.GetHashCode();
}
public int CompareTo(object obj)
{
return CompareTo((UnityVersion)obj);
}
public sealed override string ToString()
{
return FullVersion;
}
public Tuple<int, int, int> ToTuple()
{
return new Tuple<int, int, int>(Major, Minor, Patch);
}
public int[] ToArray()
{
return new[] {Major, Minor, Patch};
}
}
public static class UnityVersionExtensions
{
/// <summary>
/// Checks if the Unity version is within the range limits specified by the "lowerLimit" and "upperLimit" attributes.
/// </summary>
/// <param name="ver"></param>
/// <param name="lowerLimit">Minimal version. Included in the range.</param>
/// <param name="upperLimit">Maximal version. Not included in the range.</param>
/// <returns><see langword="true"/> if the Unity version is within the specified range; otherwise <see langword="false"/>.</returns>
/// <remarks>[lowerLimit, upperLimit)</remarks>
public static bool IsInRange(this UnityVersion ver, UnityVersion lowerLimit, UnityVersion upperLimit)
{
return ver >= lowerLimit && ver < upperLimit;
}
public static bool IsInRange(this UnityVersion ver, int lowerLimit, UnityVersion upperLimit)
{
return ver.Major >= lowerLimit && ver < upperLimit;
}
public static bool IsInRange(this UnityVersion ver, (int, int) lowerLimit, UnityVersion upperLimit)
{
return ver >= lowerLimit && ver < upperLimit;
}
public static bool IsInRange(this UnityVersion ver, (int, int, int) lowerLimit, UnityVersion upperLimit)
{
return ver >= lowerLimit && ver < upperLimit;
}
public static bool IsInRange(this UnityVersion ver, UnityVersion lowerLimit, int upperLimit)
{
return ver >= lowerLimit && ver.Major < upperLimit;
}
public static bool IsInRange(this UnityVersion ver, UnityVersion lowerLimit, (int, int) upperLimit)
{
return ver >= lowerLimit && ver < upperLimit;
}
public static bool IsInRange(this UnityVersion ver, UnityVersion lowerLimit, (int, int, int) upperLimit)
{
return ver >= lowerLimit && ver < upperLimit;
}
public static bool IsInRange(this UnityVersion ver, int lowerLimit, int upperLimit)
{
return ver.Major >= lowerLimit && ver.Major < upperLimit;
}
public static bool IsInRange(this UnityVersion ver, int lowerLimit, (int, int) upperLimit)
{
return ver.Major >= lowerLimit && ver < upperLimit;
}
public static bool IsInRange(this UnityVersion ver, int lowerLimit, (int, int, int) upperLimit)
{
return ver.Major >= lowerLimit && ver < upperLimit;
}
public static bool IsInRange(this UnityVersion ver, (int, int) lowerLimit, int upperLimit)
{
return ver >= lowerLimit && ver.Major < upperLimit;
}
public static bool IsInRange(this UnityVersion ver, (int, int, int) lowerLimit, int upperLimit)
{
return ver >= lowerLimit && ver.Major < upperLimit;
}
public static bool IsInRange(this UnityVersion ver, (int, int) lowerLimit, (int, int) upperLimit)
{
return ver >= lowerLimit && ver < upperLimit;
}
public static bool IsInRange(this UnityVersion ver, (int, int) lowerLimit, (int, int, int) upperLimit)
{
return ver >= lowerLimit && ver < upperLimit;
}
public static bool IsInRange(this UnityVersion ver, (int, int, int) lowerLimit, (int, int) upperLimit)
{
return ver >= lowerLimit && ver < upperLimit;
}
public static bool IsInRange(this UnityVersion ver, (int, int, int) lowerLimit, (int, int, int) upperLimit)
{
return ver >= lowerLimit && ver < upperLimit;
}
}
}

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>AssetStudioMod by aelurum</AssemblyTitle>
<AssemblyName>AssetStudioModCLI</AssemblyName>
<Version>0.18.0.0</Version>
<Copyright>Copyright © Perfare; Copyright © aelurum 2023-2024</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>

140
AssetStudioCLI/CLILogger.cs Normal file
View File

@@ -0,0 +1,140 @@
using AssetStudio;
using AssetStudioCLI.Options;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace AssetStudioCLI
{
internal enum LogOutputMode
{
Console,
File,
Both,
}
internal class CLILogger : ILogger
{
public string LogName;
public string LogPath;
private static BlockingCollection<string> logMessageCollection = new BlockingCollection<string>();
private readonly LogOutputMode logOutput;
private readonly LoggerEvent logMinLevel;
public CLILogger()
{
logOutput = CLIOptions.o_logOutput.Value;
logMinLevel = CLIOptions.o_logLevel.Value;
var appAssembly = typeof(Program).Assembly.GetName();
var arch = Environment.Is64BitProcess ? "x64" : "x32";
LogName = $"{appAssembly.Name}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log";
LogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, LogName);
Console.OutputEncoding = System.Text.Encoding.UTF8;
if (logOutput != LogOutputMode.Console)
{
ConcurrentFileWriter();
}
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} ") + $"\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} | ") + $"\n{curTime} | {logLevel} |";
}
}
return formattedMessage;
}
public void LogToConsole(LoggerEvent logMsgLevel, string message)
{
if (logOutput != LogOutputMode.File)
{
Console.WriteLine(FormatMessage(logMsgLevel, message, consoleMode: true));
}
}
public void LogToFile(LoggerEvent logMsgLevel, string message)
{
if (logOutput != LogOutputMode.Console)
{
logMessageCollection.Add(FormatMessage(logMsgLevel, message));
}
}
private void ConcurrentFileWriter()
{
Task.Run(() =>
{
using (var sw = new StreamWriter(LogPath, append: true, System.Text.Encoding.UTF8))
{
sw.AutoFlush = true;
foreach (var msg in logMessageCollection.GetConsumingEnumerable())
{
sw.WriteLine(msg);
}
}
});
}
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,28 @@
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 AssetItem(Object asset)
{
Asset = asset;
SourceFile = asset.assetsFile;
Type = asset.type;
TypeString = Type.ToString();
m_PathID = asset.m_PathID;
FullSize = asset.byteSize;
}
}
}

View File

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

View File

@@ -0,0 +1,14 @@
using AssetStudio;
namespace AssetStudioCLI
{
internal class GameObjectNode : BaseNode
{
public GameObject gameObject;
public GameObjectNode(GameObject gameObject) : base(gameObject.m_Name)
{
this.gameObject = gameObject;
}
}
}

356
AssetStudioCLI/Exporter.cs Normal file
View File

@@ -0,0 +1,356 @@
using AssetStudio;
using AssetStudioCLI.Options;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace AssetStudioCLI
{
internal static class Exporter
{
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 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 = 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 ExportRawFile(AssetItem item, string exportPath)
{
if (!TryExportFile(exportPath, item, ".dat", out var exportFullPath, mode: "ExportRaw"))
return false;
File.WriteAllBytes(exportFullPath, item.Asset.GetRawData());
Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"");
return true;
}
public static bool ExportDumpFile(AssetItem item, string exportPath)
{
if (!TryExportFile(exportPath, item, ".txt", out var exportFullPath, mode: "Dump"))
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 (string.IsNullOrEmpty(str))
{
str = item.Asset.DumpObject();
}
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 mode = "Export")
{
var fileName = FixFileName(item.Text);
var filenameFormat = CLIOptions.o_filenameFormat.Value;
switch (filenameFormat)
{
case FilenameFormat.AssetName_PathID:
fileName = $"{fileName} @{item.m_PathID}";
break;
case FilenameFormat.PathID:
fileName = item.m_PathID.ToString();
break;
}
fullPath = Path.Combine(dir, fileName + extension);
if (!File.Exists(fullPath))
{
Directory.CreateDirectory(dir);
return true;
}
if (filenameFormat == FilenameFormat.AssetName)
{
fullPath = Path.Combine(dir, fileName + item.UniqueID + extension);
if (!File.Exists(fullPath))
{
Directory.CreateDirectory(dir);
return true;
}
}
Logger.Error($"{mode} 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:
case ClassIDType.Texture2DArray:
case ClassIDType.Sprite:
case ClassIDType.AudioClip:
throw new System.NotImplementedException();
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.Mesh:
return ExportMesh(item, exportPath);
default:
return ExportRawFile(item, exportPath);
}
}
public static string FixFileName(string str)
{
return str.Length >= 260
? Path.GetRandomFileName()
: 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,29 @@
namespace AssetStudioCLI.Options
{
internal class Option<T>
{
public string Name { get; }
public string Description { get; }
public string Example { 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, string optionExample, HelpGroups optionHelpGroup, bool isFlag)
{
Name = optionName;
Description = optionDescription;
Example = optionExample;
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, string, HelpGroups, bool> OptionGrouping = (name, desc, example, group, isFlag) => { };
}
internal class GroupedOption<T> : Option<T>
{
public GroupedOption(T optionDefaultValue, string optionName, string optionDescription, string optionExample, HelpGroups optionHelpGroup, bool isFlag = false) : base(optionDefaultValue, optionName, optionDescription, optionExample, optionHelpGroup, isFlag)
{
OptionExtensions.OptionGrouping(optionName, optionDescription, optionExample, optionHelpGroup, isFlag);
}
}
}

View File

@@ -0,0 +1,223 @@
using AssetStudio;
using AssetStudioCLI.Options;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Text;
namespace AssetStudioCLI
{
internal static class ParallelExporter
{
private static ConcurrentDictionary<string, bool> savePathHash = new ConcurrentDictionary<string, bool>();
public static bool ExportTexture2D(AssetItem item, string exportPath, out string debugLog)
{
debugLog = "";
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 {item.TypeString} \"{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;
}
debugLog += sb.ToString();
}
var image = m_Texture2D.ConvertToImage(flip: true);
if (image == null)
{
Logger.Error($"{debugLog}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);
}
debugLog += $"{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());
debugLog += $"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"";
return true;
}
}
public static bool ExportSprite(AssetItem item, string exportPath, out string debugLog)
{
debugLog = "";
var type = CLIOptions.o_imageFormat.Value;
var alphaMask = SpriteMaskMode.On;
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
return false;
var image = ((Sprite)item.Asset).GetImage(alphaMask);
if (image != null)
{
using (image)
{
using (var file = File.OpenWrite(exportFullPath))
{
image.WriteToStream(file, type);
}
debugLog += $"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"";
return true;
}
}
return false;
}
public static bool ExportAudioClip(AssetItem item, string exportPath, out string debugLog)
{
debugLog = "";
string exportFullPath;
var m_AudioClip = (AudioClip)item.Asset;
var m_AudioData = BigArrayPool<byte>.Shared.Rent(m_AudioClip.m_AudioData.Size);
try
{
m_AudioClip.m_AudioData.GetData(m_AudioData);
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 {item.TypeString} \"{m_AudioClip.m_Name}\" to wav..");
sb.AppendLine(m_AudioClip.version < 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}");
debugLog += sb.ToString();
}
var buffer = converter.ConvertToWav(m_AudioData, out var debugLogConverter);
debugLog += debugLogConverter;
if (buffer == null)
{
Logger.Error($"{debugLog}Export error. \"{item.Text}\": Failed to convert fmod audio to Wav");
return false;
}
File.WriteAllBytes(exportFullPath, buffer);
}
else
{
if (!TryExportFile(exportPath, item, converter.GetExtensionName(), out exportFullPath))
return false;
if (CLIOptions.o_logLevel.Value <= LoggerEvent.Debug)
{
var sb = new StringBuilder();
sb.AppendLine($"Exporting non-fmod {item.TypeString} \"{m_AudioClip.m_Name}\"..");
sb.AppendLine(m_AudioClip.version < 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}");
debugLog += sb.ToString();
}
File.WriteAllBytes(exportFullPath, m_AudioData);
}
debugLog += $"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\"";
return true;
}
finally
{
BigArrayPool<byte>.Shared.Return(m_AudioData, clearArray: true);
}
}
private static bool TryExportFile(string dir, AssetItem item, string extension, out string fullPath)
{
var fileName = FixFileName(item.Text);
var filenameFormat = CLIOptions.o_filenameFormat.Value;
switch (filenameFormat)
{
case FilenameFormat.AssetName_PathID:
fileName = $"{fileName} @{item.m_PathID}";
break;
case FilenameFormat.PathID:
fileName = item.m_PathID.ToString();
break;
}
fullPath = Path.Combine(dir, fileName + extension);
if (savePathHash.TryAdd(fullPath.ToLower(), true) && !File.Exists(fullPath))
{
Directory.CreateDirectory(dir);
return true;
}
if (filenameFormat == FilenameFormat.AssetName)
{
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;
}
public static bool ParallelExportConvertFile(AssetItem item, string exportPath, out string debugLog)
{
switch (item.Type)
{
case ClassIDType.Texture2D:
case ClassIDType.Texture2DArrayImage:
return ExportTexture2D(item, exportPath, out debugLog);
case ClassIDType.Sprite:
return ExportSprite(item, exportPath, out debugLog);
case ClassIDType.AudioClip:
return ExportAudioClip(item, exportPath, out debugLog);
default:
throw new NotImplementedException();
}
}
private static string FixFileName(string str)
{
return str.Length >= 260
? Path.GetRandomFileName()
: Path.GetInvalidFileNameChars().Aggregate(str, (current, c) => current.Replace(c, '_'));
}
public static void ClearHash()
{
savePathHash.Clear();
}
}
}

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.Live2D:
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---");
}
}
}
}

152
AssetStudioCLI/ReadMe.md Normal file
View File

@@ -0,0 +1,152 @@
## AssetStudioModCLI
CLI version of AssetStudioMod.
- Supported asset types for export: `Texture2D`, `Sprite`, `TextAsset`, `MonoBehaviour`, `Font`, `Shader`, `MovieTexture`, `AudioClip`, `VideoClip`, `Mesh`.
- *There are no plans to add support for `AnimationClip`, `Animator` for now.*
### Usage
```
AssetStudioModCLI <input path to asset file/folder> [-m, --mode <value>]
[-t, --asset-type <value(s)>] [-g, --group-option <value>]
[-f, --filename-format <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>] [--custom-compression <value>]
[--max-export-tasks <value>] [--export-asset-list <value>]
[--assembly-folder <path>] [--unity-version <text>]
[--not-restore-extension] [--avoid-typetree-loading]
[--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 models
SplitObjects - Exports split objects (fbx)
Example: "-m info"
-t, --asset-type <value(s)> Specify asset type(s) to export
<Value(s): tex2d, tex2dArray, sprite, 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 | sceneHierarchy>
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)
SceneHierarchy - Group exported assets by their node path in scene hierarchy
Filename - Group exported assets by source file name
Example: "-g containerFull"
-f, --filename-format <value> Specify the file name format for exported assets
<Value: assetName(default) | assetName_pathID | pathID>
AssetName - Asset file names will look like "assetName.extension"
AssetName_pathID - Asset file names will look like "assetName @pathID.extension"
PathID - Asset file names will look like "pathID.extension"
Example: "-f assetName_pathID"
-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 FMOD audio assets
<Value: none | wav(default)>
None - Do not convert fmod 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"
Advanced Options:
--custom-compression <value> Specify the compression type for assets that use custom compression
<Value: zstd(default) | lz4>
Zstd - Try to decompress as zstd archive
Lz4 - Try to decompress as lz4 archive
Example: "--custom-compression lz4"
--max-export-tasks <value> Specify the number of parallel tasks for asset export
<Value: integer number from 1 to max number of cores (default=max)>
Max - Number of cores in your CPU
Example: "--max-export-tasks 8"
--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, AssetStudio will not try to use/restore original TextAsset
extension name, and will just export all TextAssets with the ".txt" extension
--avoid-typetree-loading (Flag) If specified, AssetStudio will not try to parse assets at load time
using their type tree
--load-all (Flag) If specified, AssetStudio will load assets of all types
(Only for Dump, Info and ExportRaw modes)
```

820
AssetStudioCLI/Studio.cs Normal file
View File

@@ -0,0 +1,820 @@
using AssetStudio;
using AssetStudioCLI.Options;
using CubismLive2DExtractor;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using static AssetStudioCLI.Exporter;
using Ansi = AssetStudio.ColorConsole;
namespace AssetStudioCLI
{
internal static class Studio
{
public static AssetsManager assetsManager = new AssetsManager();
public static List<AssetItem> parsedAssetsList = new List<AssetItem>();
public static List<BaseNode> gameObjectTree = new List<BaseNode>();
public static AssemblyLoader assemblyLoader = new AssemblyLoader();
public static List<MonoBehaviour> cubismMocList = new List<MonoBehaviour>();
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;
assetsManager.ZstdEnabled = CLIOptions.o_customCompressionType.Value == CustomCompressionType.Zstd;
assetsManager.LoadingViaTypeTreeEnabled = !CLIOptions.f_avoidLoadingViaTypetree.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 fileAssetsList = new List<AssetItem>();
var tex2dArrayAssetList = new List<AssetItem>();
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 Texture2DArray m_Texture2DArray:
if (!string.IsNullOrEmpty(m_Texture2DArray.m_StreamData?.path))
assetItem.FullSize = asset.byteSize + m_Texture2DArray.m_StreamData.size;
assetItem.Text = m_Texture2DArray.m_Name;
tex2dArrayAssetList.Add(assetItem);
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:
var assetName = m_MonoBehaviour.m_Name;
if (m_MonoBehaviour.m_Script.TryGet(out var m_Script))
{
assetName = assetName == "" ? m_Script.m_ClassName : assetName;
if (m_Script.m_ClassName == "CubismMoc")
{
cubismMocList.Add(m_MonoBehaviour);
}
}
assetItem.Text = assetName;
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;
}
isExportable = CLIOptions.o_exportAssetTypes.Value.Contains(asset.type);
if (isExportable || (CLIOptions.f_loadAllAssets.Value && CLIOptions.o_exportAssetTypes.Value == CLIOptions.o_exportAssetTypes.DefaultValue))
{
fileAssetsList.Add(assetItem);
}
Progress.Report(++i, objectCount);
}
foreach (var asset in fileAssetsList)
{
if (containers.TryGetValue(asset.Asset, out var container))
{
asset.Container = container;
}
}
foreach (var tex2dAssetItem in tex2dArrayAssetList)
{
var m_Texture2DArray = (Texture2DArray)tex2dAssetItem.Asset;
for (var layer = 0; layer < m_Texture2DArray.m_Depth; layer++)
{
var fakeObj = new Texture2D(m_Texture2DArray, layer);
m_Texture2DArray.TextureList.Add(fakeObj);
}
}
parsedAssetsList.AddRange(fileAssetsList);
fileAssetsList.Clear();
tex2dArrayAssetList.Clear();
if (CLIOptions.o_workMode.Value != WorkMode.Live2D)
{
containers.Clear();
}
}
if (CLIOptions.o_workMode.Value == WorkMode.SplitObjects || CLIOptions.o_groupAssetsBy.Value == AssetGroupOption.SceneHierarchy)
{
BuildTreeStructure(objectAssetItemDic);
}
var log = $"Finished loading {assetsManager.assetsFileList.Count} files with {parsedAssetsList.Count} exportable assets";
var unityVer = assetsManager.assetsFileList[0].version;
long m_ObjectsCount;
if (unityVer > 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(assetsFile.fileName); //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)
{
GenerateFullPath(fileNode, fileNode.Text);
gameObjectTree.Add(fileNode);
}
Progress.Report(++j, assetsFileCount);
}
treeNodeDictionary.Clear();
objectAssetItemDic.Clear();
}
private static void GenerateFullPath(BaseNode treeNode, string path)
{
treeNode.FullPath = path;
foreach (var node in treeNode.nodes)
{
if (node.nodes.Count > 0)
{
GenerateFullPath(node, Path.Combine(path, node.Text));
}
else
{
node.FullPath = Path.Combine(path, node.Text);
}
}
}
public static void ShowExportableAssetsInfo()
{
var exportableAssetsCountDict = new Dictionary<ClassIDType, int>();
string info = "";
if (parsedAssetsList.Count > 0)
{
foreach (var asset in parsedAssetsList)
{
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: {parsedAssetsList.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.Live2D:
case WorkMode.SplitObjects:
break;
default:
FilterAssets();
break;
}
}
private static void FilterAssets()
{
var assetsCount = parsedAssetsList.Count;
var filteredAssets = new List<AssetItem>();
switch(CLIOptions.filterBy)
{
case FilterBy.Name:
filteredAssets = parsedAssetsList.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 = parsedAssetsList.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 = parsedAssetsList.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 = parsedAssetsList.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 = parsedAssetsList.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;
}
parsedAssetsList.Clear();
parsedAssetsList = filteredAssets;
}
public static void ExportAssets()
{
var savePath = CLIOptions.o_outputFolder.Value;
var toExportCount = parsedAssetsList.Count;
var exportedCount = 0;
var groupOption = CLIOptions.o_groupAssetsBy.Value;
var parallelExportCount = CLIOptions.o_maxParallelExportTasks.Value;
var toExportAssetDict = new ConcurrentDictionary<AssetItem, string>();
var toParallelExportAssetDict = new ConcurrentDictionary<AssetItem, string>();
Parallel.ForEach(parsedAssetsList, asset =>
{
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;
case AssetGroupOption.SceneHierarchy:
if (asset.Node != null)
{
exportPath = Path.Combine(savePath, asset.Node.FullPath);
}
else
{
exportPath = Path.Combine(savePath, "_sceneRoot", asset.TypeString);
}
break;
default:
exportPath = savePath;
break;
}
exportPath += Path.DirectorySeparatorChar;
if (CLIOptions.o_workMode.Value == WorkMode.Export)
{
switch (asset.Type)
{
case ClassIDType.Texture2D:
case ClassIDType.Sprite:
case ClassIDType.AudioClip:
toParallelExportAssetDict.TryAdd(asset, exportPath);
break;
case ClassIDType.Texture2DArray:
var m_Texture2DArray = (Texture2DArray)asset.Asset;
toExportCount += m_Texture2DArray.TextureList.Count - 1;
foreach (var texture in m_Texture2DArray.TextureList)
{
var fakeItem = new AssetItem(texture)
{
Text = texture.m_Name,
Container = asset.Container,
};
toParallelExportAssetDict.TryAdd(fakeItem, exportPath);
}
break;
default:
toExportAssetDict.TryAdd(asset, exportPath);
break;
}
}
else
{
toExportAssetDict.TryAdd(asset, exportPath);
}
});
foreach (var toExportAsset in toExportAssetDict)
{
var asset = toExportAsset.Key;
var exportPath = toExportAsset.Value;
var isExported = false;
try
{
switch (CLIOptions.o_workMode.Value)
{
case WorkMode.ExportRaw:
Logger.Debug($"{CLIOptions.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
isExported = ExportRawFile(asset, exportPath);
break;
case WorkMode.Dump:
Logger.Debug($"{CLIOptions.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
isExported = ExportDumpFile(asset, exportPath);
break;
case WorkMode.Export:
Logger.Debug($"{CLIOptions.o_workMode}: {asset.Type} : {asset.Container} : {asset.Text}");
isExported = ExportConvertFile(asset, exportPath);
break;
}
}
catch (Exception ex)
{
Logger.Error($"{asset.SourceFile.originalPath}: [{$"{asset.Type}: {asset.Text}".Color(Ansi.BrightRed)}] : Export error\n{ex}");
}
if (isExported)
{
exportedCount++;
}
Console.Write($"Exported [{exportedCount}/{toExportCount}]\r");
}
Parallel.ForEach(toParallelExportAssetDict, new ParallelOptions { MaxDegreeOfParallelism = parallelExportCount }, toExportAsset =>
{
var asset = toExportAsset.Key;
var exportPath = toExportAsset.Value;
try
{
if (ParallelExporter.ParallelExportConvertFile(asset, exportPath, out var debugLog))
{
Interlocked.Increment(ref exportedCount);
Logger.Debug(debugLog);
Console.Write($"Exported [{exportedCount}/{toExportCount}]\r");
}
}
catch (Exception ex)
{
Logger.Error($"{asset.SourceFile.originalPath}: [{$"{asset.Type}: {asset.Text}".Color(Ansi.BrightRed)}] : Export error\n{ex}");
}
});
ParallelExporter.ClearHash();
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")),
parsedAssetsList.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("TreeNode", asset.Node != null ? asset.Node.FullPath : ""),
new XElement("Size", asset.FullSize)
)
)
)
);
doc.Save(filename);
break;
}
Logger.Info($"Finished exporting asset list with {parsedAssetsList.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.Text.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 = true;
var mocPathList = new List<string>();
var basePathSet = new HashSet<string>();
var motionMode = CLIOptions.o_l2dMotionMode.Value;
var forceBezier = CLIOptions.f_l2dForceBezier.Value;
if (cubismMocList.Count == 0)
{
Logger.Default.Log(LoggerEvent.Info, "Live2D Cubism models were not found.", ignoreLevel: true);
return;
}
Progress.Reset();
Logger.Info($"Searching for Live2D files...");
foreach (var mocMonoBehaviour in cubismMocList)
{
if (!containers.TryGetValue(mocMonoBehaviour, out var fullContainerPath))
continue;
var pathSepIndex = fullContainerPath.LastIndexOf('/');
var basePath = pathSepIndex > 0
? fullContainerPath.Substring(0, pathSepIndex)
: fullContainerPath;
basePathSet.Add(basePath);
mocPathList.Add(fullContainerPath);
}
if (mocPathList.Count == 0)
{
Logger.Error("Live2D Cubism export error: Cannot find any model related files.");
return;
}
if (basePathSet.Count == mocPathList.Count)
{
mocPathList = basePathSet.ToList();
useFullContainerPath = false;
Logger.Debug($"useFullContainerPath: {useFullContainerPath}");
}
basePathSet.Clear();
var lookup = containers.AsParallel().ToLookup(
x => mocPathList.Find(b => x.Value.Contains(b) && x.Value.Split('/').Any(y => y == b.Substring(b.LastIndexOf("/") + 1))),
x => x.Key
);
if (cubismMocList[0].serializedType?.m_Type == null && CLIOptions.o_assemblyPath.Value == "")
{
Logger.Warning("Specifying the assembly folder may be needed for proper extraction");
}
var totalModelCount = lookup.LongCount(x => x.Key != null);
Logger.Info($"Found {totalModelCount} model(s).");
var parallelTaskCount = CLIOptions.o_maxParallelExportTasks.Value;
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;
var modelExtractor = new Live2DExtractor(assets);
modelExtractor.ExtractCubismModel(destPath, modelName, motionMode, assemblyLoader, forceBezier, parallelTaskCount);
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

@@ -121,6 +121,7 @@
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>C:\Program Files\Autodesk\FBX\FBX SDK\2020.2.1\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@@ -158,6 +159,7 @@
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>C:\Program Files\Autodesk\FBX\FBX SDK\2020.2.1\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>

View File

@@ -0,0 +1,22 @@
# Set the minimum version of CMake that can be used
cmake_minimum_required (VERSION 3.8)
# Set the project name
project("AssetStudioFBXNative")
# Set the C++ standard to C++ 14
set(CMAKE_CXX_STANDARD 14)
# Generate the shared library from the library sources
add_library(AssetStudioFBXNative SHARED
asfbx_skin_context.cpp
asfbx_morph_context.cpp
api.cpp
utils.cpp
asfbx_context.cpp
asfbx_anim_context.cpp)
# Add the given directories to those the compiler uses to search for include files
target_include_directories(AssetStudioFBXNative PRIVATE .)
target_link_libraries(AssetStudioFBXNative PRIVATE fbxsdk xml2)

View File

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

View File

@@ -1,16 +1,20 @@
using AssetStudio.FbxInterop;
using AssetStudio.PInvoke;
using System.IO;
#if NETFRAMEWORK
using AssetStudio.PInvoke;
#endif
namespace AssetStudio
{
public static partial class Fbx
{
#if NETFRAMEWORK
static Fbx()
{
DllLoader.PreloadDll(FbxDll.DllName);
}
#endif
public static Vector3 QuaternionToEuler(Quaternion q)
{

504
AssetStudioGUI/AboutForm.Designer.cs generated Normal file
View File

@@ -0,0 +1,504 @@
namespace AssetStudioGUI
{
partial class AboutForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AboutForm));
this.tabControl1 = new System.Windows.Forms.TabControl();
this.tabPage1 = new System.Windows.Forms.TabPage();
this.panel1 = new System.Windows.Forms.Panel();
this.label2 = new System.Windows.Forms.Label();
this.textBox2 = new System.Windows.Forms.TextBox();
this.label11 = new System.Windows.Forms.Label();
this.label10 = new System.Windows.Forms.Label();
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
this.label16 = new System.Windows.Forms.Label();
this.label17 = new System.Windows.Forms.Label();
this.gitPerfareLinkLabel = new System.Windows.Forms.LinkLabel();
this.label18 = new System.Windows.Forms.Label();
this.label19 = new System.Windows.Forms.Label();
this.gitAelurumLinkLabel = new System.Windows.Forms.LinkLabel();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.label5 = new System.Windows.Forms.Label();
this.productNamelabel = new System.Windows.Forms.Label();
this.label7 = new System.Windows.Forms.Label();
this.modVersionLabel = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.label8 = new System.Windows.Forms.Label();
this.checkUpdatesLinkLabel = new System.Windows.Forms.LinkLabel();
this.tabPage2 = new System.Windows.Forms.TabPage();
this.licenseRichTextBox = new System.Windows.Forms.RichTextBox();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.productTitleLabel = new System.Windows.Forms.Label();
this.CloseButton = new System.Windows.Forms.Button();
this.productVersionLabel = new System.Windows.Forms.Label();
this.panel2 = new System.Windows.Forms.Panel();
this.panel3 = new System.Windows.Forms.Panel();
this.tabControl1.SuspendLayout();
this.tabPage1.SuspendLayout();
this.panel1.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout();
this.tabPage2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.panel2.SuspendLayout();
this.SuspendLayout();
//
// tabControl1
//
this.tabControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.tabControl1.Controls.Add(this.tabPage1);
this.tabControl1.Controls.Add(this.tabPage2);
this.tabControl1.Location = new System.Drawing.Point(8, 103);
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(370, 315);
this.tabControl1.TabIndex = 0;
//
// tabPage1
//
this.tabPage1.BackColor = System.Drawing.Color.White;
this.tabPage1.Controls.Add(this.panel1);
this.tabPage1.Controls.Add(this.textBox2);
this.tabPage1.Controls.Add(this.label11);
this.tabPage1.Controls.Add(this.label10);
this.tabPage1.Controls.Add(this.tableLayoutPanel2);
this.tabPage1.Controls.Add(this.tableLayoutPanel1);
this.tabPage1.Location = new System.Drawing.Point(4, 22);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
this.tabPage1.Size = new System.Drawing.Size(362, 289);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "Info";
//
// panel1
//
this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panel1.BackColor = System.Drawing.Color.White;
this.panel1.Controls.Add(this.label2);
this.panel1.Location = new System.Drawing.Point(9, 16);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(347, 46);
this.panel1.TabIndex = 21;
//
// label2
//
this.label2.Dock = System.Windows.Forms.DockStyle.Fill;
this.label2.Location = new System.Drawing.Point(0, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(347, 46);
this.label2.TabIndex = 0;
this.label2.Text = "AssetStudio is a tool for exploring, extracting, and exporting assets and asset b" +
"undles.";
this.label2.UseCompatibleTextRendering = true;
//
// textBox2
//
this.textBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBox2.BackColor = System.Drawing.SystemColors.Window;
this.textBox2.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.textBox2.Cursor = System.Windows.Forms.Cursors.Default;
this.textBox2.Enabled = false;
this.textBox2.ForeColor = System.Drawing.SystemColors.GrayText;
this.textBox2.Location = new System.Drawing.Point(9, 232);
this.textBox2.Multiline = true;
this.textBox2.Name = "textBox2";
this.textBox2.ReadOnly = true;
this.textBox2.Size = new System.Drawing.Size(347, 51);
this.textBox2.TabIndex = 20;
this.textBox2.TabStop = false;
this.textBox2.Text = "* Neither the repository, nor the tool, nor the author of the tool, nor the autho" +
"r of the modification is affiliated with, sponsored, or authorized by Unity Tech" +
"nologies or its affiliates.";
//
// label11
//
this.label11.AutoSize = true;
this.label11.Location = new System.Drawing.Point(6, 150);
this.label11.Name = "label11";
this.label11.Size = new System.Drawing.Size(43, 13);
this.label11.TabIndex = 17;
this.label11.Text = "Authors";
//
// label10
//
this.label10.AutoSize = true;
this.label10.Location = new System.Drawing.Point(8, 65);
this.label10.Name = "label10";
this.label10.Size = new System.Drawing.Size(42, 13);
this.label10.TabIndex = 16;
this.label10.Text = "Version";
//
// tableLayoutPanel2
//
this.tableLayoutPanel2.BackColor = System.Drawing.Color.WhiteSmoke;
this.tableLayoutPanel2.ColumnCount = 3;
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 41.37931F));
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 58.62069F));
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 112F));
this.tableLayoutPanel2.Controls.Add(this.label16, 0, 0);
this.tableLayoutPanel2.Controls.Add(this.label17, 1, 0);
this.tableLayoutPanel2.Controls.Add(this.gitPerfareLinkLabel, 2, 0);
this.tableLayoutPanel2.Controls.Add(this.label18, 0, 1);
this.tableLayoutPanel2.Controls.Add(this.label19, 1, 1);
this.tableLayoutPanel2.Controls.Add(this.gitAelurumLinkLabel, 2, 1);
this.tableLayoutPanel2.Location = new System.Drawing.Point(6, 165);
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
this.tableLayoutPanel2.Padding = new System.Windows.Forms.Padding(2);
this.tableLayoutPanel2.RowCount = 2;
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel2.Size = new System.Drawing.Size(350, 40);
this.tableLayoutPanel2.TabIndex = 15;
//
// label16
//
this.label16.AutoSize = true;
this.label16.BackColor = System.Drawing.Color.Transparent;
this.label16.Location = new System.Drawing.Point(5, 2);
this.label16.Name = "label16";
this.label16.Size = new System.Drawing.Size(78, 13);
this.label16.TabIndex = 9;
this.label16.Text = "Original author:";
//
// label17
//
this.label17.AutoSize = true;
this.label17.BackColor = System.Drawing.Color.Transparent;
this.label17.Location = new System.Drawing.Point(101, 2);
this.label17.Name = "label17";
this.label17.Size = new System.Drawing.Size(110, 13);
this.label17.TabIndex = 10;
this.label17.Text = "Perfare (c) 2016-2022";
//
// gitPerfareLinkLabel
//
this.gitPerfareLinkLabel.AutoSize = true;
this.gitPerfareLinkLabel.BackColor = System.Drawing.Color.Transparent;
this.gitPerfareLinkLabel.Location = new System.Drawing.Point(238, 2);
this.gitPerfareLinkLabel.Name = "gitPerfareLinkLabel";
this.gitPerfareLinkLabel.Size = new System.Drawing.Size(67, 13);
this.gitPerfareLinkLabel.TabIndex = 11;
this.gitPerfareLinkLabel.TabStop = true;
this.gitPerfareLinkLabel.Text = "GitHub page";
this.gitPerfareLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.gitPerfareLinkLabel_LinkClicked);
//
// label18
//
this.label18.AutoSize = true;
this.label18.BackColor = System.Drawing.Color.Transparent;
this.label18.Location = new System.Drawing.Point(5, 20);
this.label18.Name = "label18";
this.label18.Size = new System.Drawing.Size(76, 13);
this.label18.TabIndex = 12;
this.label18.Text = "Author of mod:";
//
// label19
//
this.label19.AutoSize = true;
this.label19.BackColor = System.Drawing.Color.Transparent;
this.label19.Location = new System.Drawing.Point(101, 20);
this.label19.Name = "label19";
this.label19.Size = new System.Drawing.Size(113, 13);
this.label19.TabIndex = 13;
this.label19.Text = "aelurum (c) 2021-2024";
//
// gitAelurumLinkLabel
//
this.gitAelurumLinkLabel.AutoSize = true;
this.gitAelurumLinkLabel.BackColor = System.Drawing.Color.Transparent;
this.gitAelurumLinkLabel.Location = new System.Drawing.Point(238, 20);
this.gitAelurumLinkLabel.Name = "gitAelurumLinkLabel";
this.gitAelurumLinkLabel.Size = new System.Drawing.Size(67, 13);
this.gitAelurumLinkLabel.TabIndex = 14;
this.gitAelurumLinkLabel.TabStop = true;
this.gitAelurumLinkLabel.Text = "GitHub page";
this.gitAelurumLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.gitAelurumLinkLabel_LinkClicked);
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.BackColor = System.Drawing.Color.WhiteSmoke;
this.tableLayoutPanel1.ColumnCount = 3;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 41.37931F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 58.62069F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 112F));
this.tableLayoutPanel1.Controls.Add(this.label5, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.productNamelabel, 1, 0);
this.tableLayoutPanel1.Controls.Add(this.label7, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.modVersionLabel, 1, 1);
this.tableLayoutPanel1.Controls.Add(this.label4, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.label8, 1, 2);
this.tableLayoutPanel1.Controls.Add(this.checkUpdatesLinkLabel, 2, 1);
this.tableLayoutPanel1.Location = new System.Drawing.Point(6, 80);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(2);
this.tableLayoutPanel1.RowCount = 3;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33334F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(350, 60);
this.tableLayoutPanel1.TabIndex = 5;
//
// label5
//
this.label5.AutoSize = true;
this.label5.BackColor = System.Drawing.Color.Transparent;
this.label5.Location = new System.Drawing.Point(5, 2);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(76, 13);
this.label5.TabIndex = 0;
this.label5.Text = "Product name:";
//
// productNamelabel
//
this.productNamelabel.AutoSize = true;
this.productNamelabel.BackColor = System.Drawing.Color.Transparent;
this.productNamelabel.Location = new System.Drawing.Point(101, 2);
this.productNamelabel.Name = "productNamelabel";
this.productNamelabel.Size = new System.Drawing.Size(103, 13);
this.productNamelabel.TabIndex = 1;
this.productNamelabel.Text = "AssetStudioModGUI";
//
// label7
//
this.label7.AutoSize = true;
this.label7.BackColor = System.Drawing.Color.Transparent;
this.label7.Location = new System.Drawing.Point(5, 20);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(68, 13);
this.label7.TabIndex = 2;
this.label7.Text = "Mod version:";
//
// modVersionLabel
//
this.modVersionLabel.AutoSize = true;
this.modVersionLabel.BackColor = System.Drawing.Color.Transparent;
this.modVersionLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.modVersionLabel.Location = new System.Drawing.Point(101, 20);
this.modVersionLabel.Name = "modVersionLabel";
this.modVersionLabel.Size = new System.Drawing.Size(46, 13);
this.modVersionLabel.TabIndex = 3;
this.modVersionLabel.Text = "0.18.0.0";
//
// label4
//
this.label4.AutoSize = true;
this.label4.BackColor = System.Drawing.Color.Transparent;
this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label4.Location = new System.Drawing.Point(5, 38);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(55, 13);
this.label4.TabIndex = 4;
this.label4.Text = "Based on:";
//
// label8
//
this.label8.AutoSize = true;
this.label8.BackColor = System.Drawing.Color.Transparent;
this.label8.Location = new System.Drawing.Point(101, 38);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(108, 13);
this.label8.TabIndex = 5;
this.label8.Text = "AssetStudio v0.16.47";
//
// checkUpdatesLinkLabel
//
this.checkUpdatesLinkLabel.AutoSize = true;
this.checkUpdatesLinkLabel.BackColor = System.Drawing.Color.Transparent;
this.checkUpdatesLinkLabel.Location = new System.Drawing.Point(238, 20);
this.checkUpdatesLinkLabel.Name = "checkUpdatesLinkLabel";
this.checkUpdatesLinkLabel.Size = new System.Drawing.Size(96, 13);
this.checkUpdatesLinkLabel.TabIndex = 6;
this.checkUpdatesLinkLabel.TabStop = true;
this.checkUpdatesLinkLabel.Text = "Check for Updates";
this.checkUpdatesLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.checkUpdatesLinkLabel_LinkClicked);
//
// tabPage2
//
this.tabPage2.Controls.Add(this.licenseRichTextBox);
this.tabPage2.Location = new System.Drawing.Point(4, 22);
this.tabPage2.Name = "tabPage2";
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
this.tabPage2.Size = new System.Drawing.Size(362, 289);
this.tabPage2.TabIndex = 1;
this.tabPage2.Text = "License";
this.tabPage2.UseVisualStyleBackColor = true;
//
// licenseRichTextBox
//
this.licenseRichTextBox.BackColor = System.Drawing.Color.White;
this.licenseRichTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.licenseRichTextBox.DetectUrls = false;
this.licenseRichTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.licenseRichTextBox.Location = new System.Drawing.Point(3, 3);
this.licenseRichTextBox.Name = "licenseRichTextBox";
this.licenseRichTextBox.ReadOnly = true;
this.licenseRichTextBox.Size = new System.Drawing.Size(356, 283);
this.licenseRichTextBox.TabIndex = 0;
this.licenseRichTextBox.Text = "MIT License";
this.licenseRichTextBox.ZoomFactor = 1.1F;
//
// pictureBox1
//
this.pictureBox1.BackColor = System.Drawing.Color.Transparent;
this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Top;
this.pictureBox1.Image = global::AssetStudioGUI.Properties.Resources.as_logo;
this.pictureBox1.Location = new System.Drawing.Point(0, 0);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(384, 50);
this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
//
// productTitleLabel
//
this.productTitleLabel.BackColor = System.Drawing.Color.Transparent;
this.productTitleLabel.Dock = System.Windows.Forms.DockStyle.Bottom;
this.productTitleLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.productTitleLabel.Location = new System.Drawing.Point(0, 52);
this.productTitleLabel.Name = "productTitleLabel";
this.productTitleLabel.Size = new System.Drawing.Size(384, 30);
this.productTitleLabel.TabIndex = 1;
this.productTitleLabel.Text = "AssetStudioModGUI";
this.productTitleLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// CloseButton
//
this.CloseButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.CloseButton.Dock = System.Windows.Forms.DockStyle.Bottom;
this.CloseButton.Location = new System.Drawing.Point(0, 422);
this.CloseButton.Name = "CloseButton";
this.CloseButton.Size = new System.Drawing.Size(384, 39);
this.CloseButton.TabIndex = 1;
this.CloseButton.Text = "Close";
this.CloseButton.UseVisualStyleBackColor = true;
//
// productVersionLabel
//
this.productVersionLabel.Dock = System.Windows.Forms.DockStyle.Bottom;
this.productVersionLabel.ForeColor = System.Drawing.SystemColors.GrayText;
this.productVersionLabel.Location = new System.Drawing.Point(0, 82);
this.productVersionLabel.Name = "productVersionLabel";
this.productVersionLabel.Padding = new System.Windows.Forms.Padding(0, 0, 5, 0);
this.productVersionLabel.Size = new System.Drawing.Size(384, 13);
this.productVersionLabel.TabIndex = 2;
this.productVersionLabel.Text = "v0.18.0.0 [x64]";
this.productVersionLabel.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
//
// panel2
//
this.panel2.Controls.Add(this.productTitleLabel);
this.panel2.Controls.Add(this.pictureBox1);
this.panel2.Controls.Add(this.productVersionLabel);
this.panel2.Dock = System.Windows.Forms.DockStyle.Top;
this.panel2.Location = new System.Drawing.Point(0, 5);
this.panel2.Name = "panel2";
this.panel2.Size = new System.Drawing.Size(384, 95);
this.panel2.TabIndex = 3;
//
// panel3
//
this.panel3.Dock = System.Windows.Forms.DockStyle.Top;
this.panel3.Location = new System.Drawing.Point(0, 0);
this.panel3.Name = "panel3";
this.panel3.Size = new System.Drawing.Size(384, 5);
this.panel3.TabIndex = 3;
//
// AboutForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.CloseButton;
this.ClientSize = new System.Drawing.Size(384, 461);
this.Controls.Add(this.panel2);
this.Controls.Add(this.panel3);
this.Controls.Add(this.CloseButton);
this.Controls.Add(this.tabControl1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "AboutForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "About";
this.tabControl1.ResumeLayout(false);
this.tabPage1.ResumeLayout(false);
this.tabPage1.PerformLayout();
this.panel1.ResumeLayout(false);
this.tableLayoutPanel2.ResumeLayout(false);
this.tableLayoutPanel2.PerformLayout();
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.tabPage2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.panel2.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TabControl tabControl1;
private System.Windows.Forms.PictureBox pictureBox1;
private System.Windows.Forms.TabPage tabPage2;
private System.Windows.Forms.Label productTitleLabel;
private System.Windows.Forms.Button CloseButton;
private System.Windows.Forms.Label productVersionLabel;
private System.Windows.Forms.TabPage tabPage1;
private System.Windows.Forms.Label label11;
private System.Windows.Forms.Label label10;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
private System.Windows.Forms.Label label16;
private System.Windows.Forms.Label label17;
private System.Windows.Forms.LinkLabel gitPerfareLinkLabel;
private System.Windows.Forms.Label label18;
private System.Windows.Forms.Label label19;
private System.Windows.Forms.LinkLabel gitAelurumLinkLabel;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.Label productNamelabel;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.Label modVersionLabel;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label8;
private System.Windows.Forms.LinkLabel checkUpdatesLinkLabel;
private System.Windows.Forms.RichTextBox licenseRichTextBox;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Panel panel2;
private System.Windows.Forms.Panel panel3;
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
namespace AssetStudioGUI
{
public partial class AboutForm : Form
{
public AboutForm()
{
InitializeComponent();
var arch = Environment.Is64BitProcess ? "x64" : "x32";
var appAssembly = typeof(Program).Assembly.GetName();
var productName = appAssembly.Name;
var productVer = appAssembly.Version.ToString();
Text += " " + productName;
productTitleLabel.Text = productName;
productVersionLabel.Text = $"v{productVer} [{arch}]";
productNamelabel.Text = productName;
modVersionLabel.Text = productVer;
licenseRichTextBox.Text = GetLicenseText();
}
private string GetLicenseText()
{
string license = "MIT License";
if (File.Exists("LICENSE"))
{
string text = File.ReadAllText("LICENSE");
license = text.Replace("\r", "")
.Replace("\n\n", "\r")
.Replace("\nCopyright", "\tCopyright")
.Replace("\n", " ")
.Replace("\r", "\n\n")
.Replace("\tCopyright", "\nCopyright");
}
return license;
}
private void checkUpdatesLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
var ps = new ProcessStartInfo("https://github.com/aelurum/AssetStudio/releases")
{
UseShellExecute = true
};
Process.Start(ps);
}
private void gitPerfareLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
var ps = new ProcessStartInfo("https://github.com/Perfare")
{
UseShellExecute = true
};
Process.Start(ps);
}
private void gitAelurumLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
var ps = new ProcessStartInfo("https://github.com/aelurum")
{
UseShellExecute = true
};
Process.Start(ps);
}
}
}

View File

@@ -0,0 +1,754 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAoAEBAQAAEABAAoAQAApgAAABAQAAABAAgAaAUAAM4BAAAQEAAAAQAgAGgEAAA2BwAAICAQAAEA
BADoAgAAngsAACAgAAABAAgAqAgAAIYOAAAgIAAAAQAgAKgQAAAuFwAAMDAQAAEABABoBgAA1icAADAw
AAABAAgAqA4AAD4uAAAwMAAAAQAgAKglAADmPAAAAAAAAAEAIADMMAAAjmIAACgAAAAQAAAAIAAAAAEA
BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAACAAACAAIAAgIAAAAAAgACAgIAAAICAAMDA
wAAAAP8A////AP8A/wAAAAAAAAAAAAAAAAAAAAAAABcXAQQBcDABcQBgYFISFUUCVAACFRUhAEMVFlMj
IDIGaGZqubk2ABAIiKiTlpMhBxaDppYzlgUBdoapY5aTQCMBiIk5aTIFEHCKaWllIwQHEGqWmWaTJQEj
CJZWmWcBRRBxcBV3EBcAcQYBcQEGAAYGAEUGAjBxABAjAEBRIBCAAQAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAAKAAAABAAAAAgAAAAAQAIAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwKCYANzZSADI2ZQAwKSoAMCgsADInSAAxLlUAMDZeADAq
LAAyKigAmqDgADpR/gA0M20AOiWTADk0vwA2VfwANVj+ADNQ1wAwKi4AXlZUAOfj7QBDSv0APzrTAEEj
nAA+RewAPDvDAEArrAA6TPQAMzZvAJyUkQDz7egAdG33AEU99QBGIpgAQ0D0ADcwbAAyJzEAMis7ADEq
MAAxKScA18/JANTH1QCuoe8ATDf/AEwmrgBKMdUARznrAEA1sgA2LV8AV05LAOTY1gCCXKUA4NfjAFMw
/gBMKr8ARiNwAE8qwgBPMOMATDX6ADQqRwCSiIIAu6G7AFwjiQDm29oAfFT2AFMn6QA2J0QARCNbAFge
hwBUKucAPiqAAMq+tQDdzssA1sTKAOfc1gCvjeoAXh7/AF0f/wBYH7sAXR2YAFoj8gA/J3QAUEdDAN/Q
xgCRXpcAhF2FAJSKhQDbydoAZRb+AGMX8wBiGP4AYRn/AFcd2gAyKDAARDw4AF9TUQBAJD4AOCYzADkw
LgBnXloARSdlAEEiXgBAI1IAPiNfADInLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAV9gYWJjZGVmZ2hpAQEBAQFTVFVWV1hZWltcXV4BAQEBAUhJSktMTU5PUFFS
AQEBAQE9Pj9AQUJDREVGRwEBAQEBMjM0NTY3ODk6OzwBAQEBASgpKissLS4vMDEBAQEBAQEBHh8gISIj
JCUmJwEBAQEBARQVFhcYGRobHB0BAQEBAQEKCwwNDg8QERITAQEBAQEBAQIDBAUGBwgJAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEAgAEAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAEAACgAAAAQAAAAIAAAAAEA
IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwKCY3MCgm2DAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJtgwKCY3MCgm2DAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm2DAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/0Q8OP9fU1H/QCQ+/zgm
M/85MC7/Z15a/0UnZf9BIl7/QCNS/z4jX/8yJy3/MCgm/zAoJv8wKCb/MCgm/zAoJv9QR0P/39DG/5Fe
l/+EXYX/lIqF/9vJ2v9lFv7/Yxfz/2IY/v9hGf//Vx3a/zIoMP8wKCb/MCgm/zAoJv8wKCb/MCgm/8q+
tf/dzsv/1sTK/+fc1v+vjer/Xh7//10f//9YH7v/XR2Y/1oj8v8/J3T/MCgm/zAoJv8wKCb/MCgm/zAo
Jv+SiIL/u6G7/1wjif/m29r/fFT2/1Mn6f82J0T/RCNb/1geh/9UKuf/PiqA/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/V05L/+TY1v+CXKX/4Nfj/1Mw/v9MKr//RiNw/08qwv9PMOP/TDX6/zQqR/8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zEpJ//Xz8n/1MfV/66h7/9MN///TCau/0ox1f9HOev/QDWy/zYtX/8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/nJSR//Pt6P90bff/RT31/0YimP9DQPT/NzBs/zInMf8yKzv/MSow/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/15WVP/n4+3/Q0r9/z860/9BI5z/PkXs/zw7w/9AK6z/Okz0/zM2
b/8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8yKij/mqDg/zpR/v80M23/OiWT/zk0v/82Vfz/NVj+/zNQ
1/8wKi7/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zc2Uv8yNmX/MCkq/zAoLP8yJ0j/MS5V/zA2
Xv8wKiz/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJtgwKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJtgwKCY3MCgm2DAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJtgwKCY3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAgAAAAQAAAAAEABAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAgAAAAACAAACAAIAAgIAAAAAAgACAgIAAAICAAAAA/wDAwMAA////AAD/
/wD/AP8AAAAAAAAAAAAAAAAAAAAwIVBFIVBAVAFFEhcDAAAXEjAGADISMEUCUGBQQCADIVBFQDIBUCMB
cQQAQDAyBFAhUjAhUhcQcBcQVFEgRRUhVAECMEUBcQRQcSAXAwACFUUHFxcEUgRUBAMEUEBxAyAQQQAB
BRAwUFFSMARRAgRRJmYWMjRpNzY3M3FxBUUVIVaZMzEJmYyDjIaDAjIQAjAjqZaWmanIbIaMjHEFIwMh
UpmampqWjIaDOGg3EhAEBUVqmZmprIg4NlOGgQVFFSEBaaY1mpiMc0UzaMJUAAIwcQmmM6mYxwBTY4hl
EHEVQBcJmTaqhoNzNoaGMCMFAjBwFpo5qYyGOGjIaGAyEgMgEGCaaamGg2hohoYBIDAEAyMCmpqoaGOG
hlYFBgMgFSECMGmqmGhlhoQAEhAgMgIwcQIaqmhoNoaEVnNwMhADIBcVCZpoaGiGhlOGhAMHBFFwEjap
hoZWhoaGhoIwIRUhAXBAqYaDU4aGi4ZUAjACFQYBUJhoYGVlhoaGADAjAyBAUEVlaCUVVldzUlQGEARR
BxIwIXEEAXEBBgQBUHAwIyEDIDIAYFIVJUBQMgQBAjAFQAYBVAEjBAFUASMAMgMhcQcQJUUCMHAXBxJQ
YSUEBQRRIwEBcQQDIQFRIQcBAGBgBFBgIwcQVFBFIwIwEAABAjAEBRIBUhAGABJRAgDAAAADgAAAAQAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAABwAAAAygA
AAAgAAAAQAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMCgmAEhFVgA0Q6UAM0OlADAs
NwAyJ0AANCdlADQmbgA0K3UAMT+UADBGogAwQYkAMC4/ALCsqwBHXvsAOFP+ADM5egA1JmkAOSWjADgy
uQA1VfcANFn+ADNa/gAyWfYAMTluADYuLADt6eYAhI32ADtP/gA4RMEANSZUADwkoAA8KakAOFD4ADhU
/gA3Vf4ANlb+ADVX/gA1VvgAMS9GAGxlYwD18e0AwcHwAD5K/gA9SvkAPSaVAD8jngA9OtAAPTrPAD4u
sgA8OckAOVH8ADRBoACrpaEA9O/rAPHs7ABNT/sAQkX+AEFH/gBBMMAAQiObAD9B5gA5P7cAPyOKAD4+
2AA4R9AANCwqAOXe2gDz7egAhYD0AEVB/gBEQv4ARTnfAEUimABCQO4AODeQADMnNgA0Jz0AMytJADMw
VQAyL04AZl5bAPDp5ADx6+YAvrfsAEg9/gBHPv4ARz37AEgjmwBIIZYARTrlAD86wwCjm5cA7ubgAOLZ
3ADx6uUA7ebnAFNB/ABKOf4ASTv+AEopuABLIZMASS/KAEc9/gA/NrIAODB1ADUtWQAwKCkAMysoAN3U
zgDu5d8Ak3axAOrh4ACGc/UATTX/AE0v2wBOIJAATSKZAEs29ABLN/8ANSxPAGBXVADr4toA7OPcAGE1
lQDArMgA8OjjALus6QBRL/8AUDH6AFAgjwBRH44AUCOgAE4x6QBJN+wAMik4AJ2TjQDq39gAzr3JAFQf
iwCTcK0A6ODhAFoz/ABTLf8APSlyADkmQQA/JFEATyB+AFIjoQBSJrIAUC3bAE8z/wBONP8APy6WANXK
wgDo3dQAooKwAFceiABoNZIA7eTeAIdl8QBWKf8ASSm0ADQnMgBVHoUAUivgAEou0ABaUU0A5trRAHlJ
lgBZHYYA0sDMAO3k3QC5oecAWyL/AFkl/wBXJfQAOydhADYoQwBRIHIAViS8AFQr/wBQK+QAlImDAOXY
zgDFrsEA3c/RAOTZ3QBhJf0AXCD/AE0ktQBZHnwAXB2DAFkizgBQJ9oAzcC2AOPWzADp3tYA6uDYAIpX
8gBfG/8AXR37AF4evgBeHZwASiWrAFVLRwDi1MkAv6OzALeZrwC4mrEAsZ2lAKqgmgDc0ckAuJblAGMX
/wBhGf8AOCZOAI2BegDh0cYA2snCAGkhfQBlG3sAQSRBAJmPigDo3tUA4NLXAGgX/QBlE/8AZRT/AGUX
zwBkFfsAYBj5AEIjdgCCdm8AmIuDAIRwdwBQIFYAMicpAJ6UjgBhNKAAURqnAFEdgwBQH2YATxyVAE8c
pgBJH4wANiZBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAerr7O3t7e4Bzonv8PHx8vP09fb3AWwBAQEBAQEB
AQEB2tvc3d7e3wHg4eLj5OXm5+XX1+jpAQEBAQEBAQEBAQHOz8/Q0dLT1NWK1tfX19jJycnJysrZAQEB
AQEBAQEBAQHExbmpqZzGx3vIycnJycrLzMu+r80BAQEBAQEBAQEBAbi5qbq6urt7vL2vvq+vv8DBwcKw
wwEBAQEBAQEBAQEBqKmpqqurrK2ur7CxsrKztKurtba3AQEBAQEBAQEBAQEBm5ydnp+gXqGioqMBAaSl
np6mkKcBAQEBAQEBAQEBAQGJiouMjV6Oj5CQkZKTlJWWl5iZmgEBAQEBAQEBAQEBAXp7fH1+f4CBgYKD
hIWGc3N4eIeIAQEBAQEBAQEBAQEBbW5vcHFgcnNzdHV2d3hjZGRJeQEBAQEBAQEBAQEBAQEBXV5fYGFi
Y2RlZmdWaGlqa2wBAQEBAQEBAQEBAQEBAQFSU1RFVVZXWFlaW0dcAQEBAQEBAQEBAQEBAQEBAQEBAUNE
RTdGR0hJSkpLOkxNTk5PUFEBAQEBAQEBAQEBAQEBATY3ODk6Ozw9PT4sP0A9PUEdQgEBAQEBAQEBAQEB
AQEBKSorLCwtLi8vMB0dMTIzNBA1AQEBAQEBAQEBAQEBAQEaGxwdHR4fICAhIiMkJCUmJygBAQEBAQEB
AQEBAQEBAQEODxAQEQESExMUFRYXFxgZAQEBAQEBAQEBAQEBAQEBAQIDBAQFAQEGBwgJCgsMDQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAMAAAAOAAAABAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAHAAAADKAAAACAA
AABAAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwKCYKMCgmgzAoJt8wKCb+MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv4wKCbfMCgmgzAoJgoAAAAAMCgmCjAoJsgwKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgmyDAoJgowKCaDMCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgmgzAo
Jt8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCbfMCgm/jAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv4wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv+Cdm//mIuD/4Rw
d/9QIFb/UCBW/1AgVf8yJyn/MCgm/1JKRv+dk43/npSO/2E0oP9RGaf/URqn/1Edg/9QH2b/TxyV/08c
pv9JH4z/NiZB/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/42B
ev/g0MX/2snC/2khff9lG3v/ZRt7/0EkQf8wKCb/mY+K/+je1f/g0tf/aBf9/2UT//9lFP//ZRfP/2QV
+/9jFv//Yxb//2IX//9gGPn/QiN2/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/VUtH/+HSx//i1Mn/v6Oz/7eZr/+4mrH/sZ2l/6qgmv/c0cn/6t/Y/7iW5f9jF///Yhf//2IY
//9hGf7/YRn//2Aa//9gG///Xxz//14c//9dHfv/OCZO/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8xKSf/zcC2/+PWzP/k2M7/5tnQ/+fb0v/o3dT/6d7W/+rg2P/r4tr/ilfy/18b
//9fHP//Xx3//14d//9eHv7/Xh2//14dnP9dHr3/WyH9/1si//9KJav/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv+UiYP/5djO/+ba0P/Frb//xa3A/8Wuwv/dz9H/6+Lb/+TZ
3f9hJf3/XCD//1wg//9bIf//WyH//00ktf9ZHnz/XB2D/1wdg/9ZIs7/WCb//1An2v8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/1pRTf/m2tH/59vT/3lJlv9ZHYb/WR2G/9LA
zP/t5N3/uaHn/1oj//9ZJP//VyX0/zsnYf87J2H/NihD/1Egcv9ZHYb/WR2G/1YkvP9UK///UCvk/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MSkn/9XKwv/o3tX/ooKw/1ce
iP9oNZL/7eTe/+7m4P+HZfH/Vij//1Yp//9JKbT/MCgm/zAoJv80JzL/VR6F/1ceiP9XHon/Uivg/1Ev
//9KLtD/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/nJKN/+rf
2P/Ovcn/VB+L/5Nwrf/u5uD/6ODh/1oz/P9TLP//Uy3//z0pcv85JkH/PyRR/08gfv9SI6H/Uiay/1At
2/9PM///TjT//z8ulv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv9gV1T/6+La/+zj3P9hNZX/wKzI//Do4/+7rOn/UTD//1Ax//9QMfr/UCCP/1Efjv9QI6D/TjHp/001
//9NNv//TDf//0s3//9JN+z/Mik4/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zMrKP/d1M7/7uXf/5N2sf/q4eD/8erl/4Zz9f9NNf//TTX//00v2/9OIJD/TSKZ/0s2
9P9LOf7/Sjn+/0k6/v9JO/7/RTne/zUsT/8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/6Obl//v5+H/4tnc//Hq5f/t5uf/U0H8/0o5/v9KOv7/Sim4/0sh
k/9JL8r/SDz+/0c9/v8/NrL/ODB1/zUtWf8wKCn/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/Zl5b//Dp5P/x6+b/8+3o/7637P9IPf7/Rz7+/0c9
+/9II5v/SCGW/0U65f9FQf7/PzrD/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv80LCr/5d7a//Pt6P/07ur/hYD0/0RB
/v9EQv7/RDnf/0UimP9FIpj/QkDu/0JF/v84N5D/Myc2/zQnPf80Jz3/MytJ/zMwVf8yL07/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv+rpaH/9O/r//Hs
7P9NT/v/QUb+/0FH/v9BMMD/QiOb/0Ijm/8/Qeb/Pkr+/zk/t/8/I4r/QiOb/0Ijm/8+Ptj/O0/+/zhH
0P8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/2xl
Y//18e3/wcHw/z9K/v8+Sv7/PUr5/z0mlf8/I57/PyOe/z060P87Tv7/O0/+/z06z/8+LrL/PDnJ/zlR
/P84U/7/NEGg/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/Ni4s/+3p5v+Ejfb/O07+/ztP/v84RMH/NSZU/zwkoP88JKD/PCmp/zhQ+P84VP7/N1T+/zZV
/v82Vv7/NVf+/zVW+P8xL0b/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/sKyr/0de+/84U/7/OFP+/zM5ev8wKCb/NSZp/zklov85JaP/ODK5/zVV
9/80Wf7/M1r+/zNa/v8yWfb/MTlu/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv9IRVb/NEOl/zNDpf8zQ6X/MCw3/zAoJv8wKCb/MidA/zQn
Zf80Jm7/NCt1/zE/lP8wRqL/MEGJ/zAuP/8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv4wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb+MCgm3zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJt8wKCaDMCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgmgzAoJgowKCbIMCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJsgwKCYKAAAAADAoJgowKCaDMCgm3zAo
Jv4wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/jAoJt8wKCaDMCgmCgAAAACAAAABAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAASgA
AAAwAAAAYAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAACAAACAAIAAgIAAAAAA
gACAgIAAAICAAAAA/wDAwMAAAP//AP///wD/AP8AAAAAAAAAAAAAAAAAAAAAYAMhUhJRJRcBIVJRIyAS
AyMhUgAAAARUAGBUBFBAVAFwFwEGABUjBAAwIVAAAABRcQIQUBcXECMBcBcQBgQBcBcSMEAAADISAyFU
BgAQcVIXFxBxcQVFAyAwIwYAAyFRcVBFASMHEEUAEAcQBFQAQFQCMCBUEgUhAgYAYFIQRQEjJUAXFQBg
MhBRIDBFBUAXAwEjAhFwMhcQBFFwBFQARQYEUSMABFFwEjJRIyUEADIGAwIQYAFUUEADBxBxAyEBcBBA
UBIVBgAwIGBUBUUgFxVAIQcQElBxBgcDIXAyEHEEUQEjISEDIBJRBgEGBUAXAQESEAEgMhBFFxcAUDBg
cXAXBFIwBAMgMpmWNjcVQGmZaDjGNoaDMAMgFQEgAyFUBpmZMzMyBZubPIyDjIyGhlQDIGBUElAhUGuZ
YzYxYZuZhoaMhoyMjIRSMBIVBUAwIWmbmZmZmbm8jIyGjIaMhoMBIHFSBFElFSmZm5uZubm2hoaMiDOG
jIYHFRIQAyFUAha5uZm5ubmYyMjIk1ZThoxxBAUjEgUhUXG5mZmZm5vIaGiDhjM2OGgwcGASBUAwIQeZ
uTc1m5s4yGU2UDc1OMhxAQYFBFEgMlGZuTMzm5uGjIElBjM2hoY3FSASAyFUUEBpuWNWubmMhlcQRTVj
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
wwA+JUUAVSOqADspYwA0LCoA1Mi/AMWvvwBmM/kAWiP/AEEmfgA8KWwANyY2AFcglwBeVVEA0r+9ALOV
tgC0lrgAzrvGAJNr7gBeHf8AVCThAFgfpgCYjIYA4tXLAMOp4wBaIuIA0MK4AGoo+gBhGv8AXh3KAF8b
qwBcH+cAVCPcAFlPSwDh0sYA39DEAN/RygCUYO4AZBX+AJGFfgB/R4oAZyN8AEAtPABNQ0EAv6HfAGQW
9ABTH8QAx7muAGUbegBfHHAAbBv6AGUYqQBSHbsAMycyAMu8sQCgfpUAYBxtAE0hUQDUycEAhU3YAGIT
6QBhE+cAYRmWAF8W1QBdF90AUhuzAD4jYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAABAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQYBAQEBAQEBAQEBAQEBAQEBAQYGBgEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB3+rq
6uvs7Ozl7QEBATa57u7v8PHw8PLl0vPx9PX2AQEBAQEBAQEBAQEBAQEBAQEBBuPX18Lk5OTk5QYGAUGf
n5Tm2tra4efh2tra0Nra6OkBAQEBAQEBAQEBAQEBAQEBAdvWy9bc3d3d3d6C34OfjODa2tra4dra0NDa
0NDQ0OIGAQEBAQEBAQEBAQEBAQEBAdXWy8vXntjW2NjLsJ+fn9na0NDQ0NDQ0MfHx8fHx8e9AQEBAQEB
AQEBAQEBAQEBAQHOy7CwsIODn4OfaoyEas/Qx8fHx8fH0NHS0tPHvLzUAQEBAQEBAQEBAQEBAQEBAQHK
y8uwsIODn5+fn4yEzMfHx8fHx7y8yaysrKzNvKmprgEBAQEBAQEBAQEBAQEBAQHBsLCDwsPDxMPFhISE
xsfHvLy8x7zItaysrKzJqamptwEBAQEBAQEBAQEBAQEBAQG4uYODuqysrKyghISMu7y8vLyqvb2+v6ys
rKzAqamWvgEBAQEBAQEBAQEBAQEBAQEBr4ODsLGyrLKUTk6zqampqbQBAQEBtayskqy2lpaQtwEBAQEB
AQEBAQEBAQEBAQEBpZ+fn6aSkqeEhHGoqamWqaoBAQEGq5KskpKtkJaQrgEBAQEBAQEBAQEBAQEBAQEB
gp6fn6CSiaFxTk6ilpaWlkwBICajkpKSmqSQfJB9AQEBAQEBAQEBAQEBAQEBAQEBAZOMhJSJiXlxcZWW
lpCQl5iZkpKam5x8fJB8fHydAQEBAQEBAQEBAQEBAQEBAQEBAYuMhISNjoRxYo+QkJCQkZKSbYp8fHxz
c3xzc24gAQEBAQEBAQEBAQEBAQEBAQEBAYKDhISFhmJicYd8fHx8iG2JinN8c3xzc3NzbhcBAQEBAQEB
AQEBAQEBAQEBAQEBAQF4Tk55emJie3x8fHx9bW1nfnNzZHNkf4CBIAEBAQEBAQEBAQEBAQEBAQEBAQEB
AQFwTnFOTk9icnNzc3N0bW11ZGRkXHZ3IAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQFpamJiYk9Za2Rk
ZGRsbWduZGRvXwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBYWJPT09jZGRlZGZnZ2dcUlFoAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBWFlZLCxaUVFRUVtGRkZcUlJdBlVeXl5fYGBgTAEBAQEB
AQEBAQEBAQEBAQEBAQEBTU5PLDdQUVJSUlNGRkZUOUNKVUZGRkZWJCQkVwEBAQEBAQEBAQEBAQEBAQEB
AQEBAUE3N0JDQ0M5REVGRUVHLy8kSEVGRUlKJBRLTAEBAQEBAQEBAQEBAQEBAQEBAQEBATY3Nzg5OTk5
OjMzMzM7JCQUJDw9Pj8UFBVAAQEBAQEBAQEBAQEBAQEBAQEBAQEBASssLS4vLyQkMDEyMjMzNBQVFRUV
FRUVFRU1AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEhIiMUFCQlASYnGRkZKCkVFRUVHBwcHSoBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQESExQUFRUWAQEXGBkZGRobHB0dHh4eHyABAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQECAwQEBAQFAQEBBgcICQoLDA0ODxARAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAABAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAADwAAAAAA8AAOAAAAAABwAAwAAAAAADAACAAAAAAAEAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAgAAAAAABAADAAAAAAAMAAOAAAAAABwAA8AAAAAAPAAAoAAAAMAAAAGAA
AAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMCgmLDAoJpkwKCbeMCgm/TAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb9MCgm3jAoJpkwKCYsAAAAAAAAAAAAAAAAAAAAADAoJgEwKCZ3MCgm+TAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb5MCgmdzAoJgEAAAAAAAAAADAo
JncwKCb+MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/jAo
JncAAAAAMCgmLDAoJvkwKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJvkwKCYsMCgmmTAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCaZMCgm3jAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCbeMCgm/TAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb9MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/05EQf/Lu7D/y7yx/8y9s/+gfpX/YBxt/2Acbf9gHG3/YBxt/00hUf8wKCb/MCgm/zAo
Jv99dG//08jA/9TJwf/VysP/hU3Y/2IS5/9iEuf/YRPo/2IT6f9hGZb/YBt4/18Yrv9fFtX/YBXm/10X
3f9SG7P/PiNi/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zIqKP/Hua7/39DE/+DRxf/Sv73/Zhx7/2Ybe/9mG3v/Zht7/18c
cP8xKCf/MCgm/zAoJv+/tK3/6N3V/+ne1v/k2Nb/bBv6/2YS//9mEv//ZRP//2UU+P9lGKn/ZBX1/2QU
//9kFf//YxX//2MW//9jFv//Yhf//1Idu/8zJzL/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv+RhX7/4NHG/+HSx//h08j/f0eK/2cj
fP9nI3z/ZyN8/2cjfP9ALTz/Ny8t/0tCQP/l2tL/6d7W/+rf2P+/od//ZBT//2QV//9kFf//Yxb//2MX
8v9jF/v/Yhf//2IX//9iGP//YRj//2EZ//9hGv//YBr//2Ab//9TH8T/MSgp/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv9ZT0v/4dLH/+LT
yf/i1Mr/39DI/93PyP/e0Mn/39HK/+DSzP/f08z/4NXM/+LYz//p3tf/6t/Y/+vh2f+UYO7/Yhf//2IY
//9hGP//YRn//2EZ//9hGf//YBr//2Ab//9gG///Xxz//18c//9eHf//Xh3//14e//9dHv//QiR9/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8xKSf/0MK4/+PVyv/j1sz/5NfN/+XYzv/m2dD/5trR/+fb0//o3NT/6N7V/+nf1//q4Nj/6+HZ/+ba
2/9qKPr/YBr//2Ab//9fG///Xxz//18c//9fHP//Xh3//14d/P9eHcr/Xh2o/10ds/9cH+f/XCD//1wh
//9bIf//VCPc/zAoJ/8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/mIyG/+PWzP/k183/5djP/+bZ0P/m2tL/59zT/+jd1P/p3tb/6d/X/+rg
2P/r4dr/7OLb/8Op4/9eHf//Xh3//14e//9dHv//XR///10f//9cH///XCD+/1gfpv9eHIL/XhyC/14c
gv9dHYb/WiLi/1kk//9ZJP//WCX9/zUoQv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/XlVR/+TXzv/l2M//5trQ/8+8xP+zlLX/s5W2/7OV
t/+0lrj/zbnH/+vh2v/s4tv/7OTd/5Nr7v9cIP//XCD//1sh//9bIf//WyL+/1si//9aIv//VCTh/z8k
Rv9cHYT/XB2E/1wdhP9cHYT/WSCp/1cm//9XJ///Vyf//zsoYv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/NCwq/9TIv//m2tH/59vS/8Wv
v/9aHYX/Wh2F/1odhf9aHYX/up++/+zj3P/t5N3/6d/e/2Yz+f9aI///WiP//1kk//9YJPv/QSZ+/0Em
f/9BJ3//PCdo/zcmNv9aHYX/Wh2F/1odhf9aHYX/VyCX/1Up//9VKv//VCv//zwpbP8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/56U
jv/n29L/6NzU/+TY0v9kLoz/WB6H/1geh/9bIon/49bX/+3k3f/u5d//xbLj/1gl//9YJv//WCb//1cn
//9MJ8P/MCgm/zAoJv8wKCb/MCgm/zwlQ/9YHof/WB6H/1geh/9YHof/VSOq/1Mt//9TLf//Ui7//zoq
Y/8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/2NaV//o3NT/6N7V/+nf1/+JYqL/Vh6J/1Yeif97T57/7eTe/+7l3//u5uD/k3Xt/1Yo
//9WKf//VSn//1Uq//8/KX3/MCgm/zAoJv8wKCb/MSgo/0whcP9WHon/Vh6J/1Yeif9WHor/Uivg/1Ev
//9RMP//UDD9/zUpRf8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zYuLP/az8j/6d/X/+rg2P+1nLv/VB+L/1Qfi/+nirj/7ubf/+/n
4f/t5eH/YTv5/1Qr//9ULP//Uyz//1It+v8zKDv/MCgn/zEoKv82Jjn/SSJt/1Qfi/9UH4v/VCCP/1Mi
nf9QLNv/TzL//08y//9PM///SjLg/zAoKP8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv+lm5b/6uDZ/+vh2v/g1NT/VCKN/1Mg
jP/UxdL/7+fh/+/o4v/IuuX/Ui7//1Iu//9SL///US///0wt0/9EI2P/TSB+/1EfiP9SH4z/USKb/1Ar
zf9PMO7/TjP4/040//9ONP//TTX//002//9MNv//PS6I/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv9pYF3/6+La/+zj
3P/t5N3/dE2e/2Y7mf/t5eD/8Ojj//Dp5P+Uf+//UDH//1Ax//9PMv//TzL//1Anvv9QH47/UB+O/1Af
jv9OKLj/TTT6/001//9NNv//TDb//0w3//9LN///Szj//0s4/v9FNdX/MSgs/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv82Liz/4tnS/+3k3f/u5d//ooi3/5N1s//w6eP/8erk/+7n5f9hSPr/TjT//040//9NNf//TTX9/04i
nP9OIJD/TiCQ/00otP9LN/7/Szj//0s4/v9KOf7/Sjn+/0o6/v9JOv7/STv+/0Q30/8zKj3/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/rKOe/+7l3//u5uD/0cPR/8KwzP/x6uT/8evm/8zB6v9NN///TDf//0w3
//9LOP//SzLl/0wgkv9MIJL/TCKX/0o48/9JOv7/STv+/0k7/v9IPP7/SDz8/0Y88/9DOdL/OjGD/zEp
Lf8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/b2dj/+/n4f/v6OL/7+fj/+7m4//y6+b/8uzo/5WJ
8P9KOf7/Sjr+/0o6/v9JO/7/SizC/0ohk/9KIZP/SSy6/0g9/v9HPf7/Rz7+/0Q85/83L2v/Mio1/zEp
Lv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/OjIw/+be2f/w6eT/8erl//Lr
5//y7Oj/8Ovo/15T9v9IPP7/SD3+/0c9/v9HPv7/SCSh/0ghlf9IIZX/RjXV/0ZA/v9FQP7/RUD7/zQt
VP8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/7Or
p//x6uX/8uzn//Pt6P/z7ur/z8np/0Y//v9GP/7/RkD+/0VA/v9FOuj/RiKX/0Yil/9GIpf/RDvj/0ND
/v9DQ/7/Pz7V/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/3Vtav/y7Of/8+3o//Pu6v/07+v/lpLx/0RC/v9EQv7/Q0P+/0ND/v9EMcj/RCKZ/0Qi
mf9EIpn/Qj7n/0FG/v9BRv7/PD7C/zEoKv82Jkf/NiZI/zYmSP82Jkj/NilR/zQ0bf80NG3/NDRt/zEr
Of8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/z01M//r5eH/9O7q//Tv6//18O3/W1z3/0JF/v9CRf7/QUb+/0FG
/f9CJ6n/QyKa/0Mimv9DIpr/QD7e/z9J/v8/Sf7/PUbk/zQnQv9DIpr/QyKa/0Mimv9DIpr/QDG9/zxN
/v88Tv7/O07+/zIwVP8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv+6tLD/9fDs//Xx7f/U0ez/QEf+/0BI
/v9ASP7/P0n+/z9C6/9BI53/QSOc/0EjnP9BI5z/PzjO/z1M/v89TP7/PE3+/zw1sv9BI5z/QSOc/0Ej
nP9BJJ7/PEbn/zpQ/v85Uf7/OVH5/zErOP8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv97dHH/9fHt//by
7/+Ym/H/Pkr+/z5L/v89S/7/PUz+/zk4pv8/I53/PyOe/z8jnv8/I57/Piyx/ztP/v87T/7/OlD+/zpQ
/P87P9j/PDS8/zw2w/86SOn/OFP+/zhT/v83VP7/NUrM/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8+NjT/8u7r//by8P9bZ/j/PE3+/zxO/v87Tv7/O078/zIuSv85JX3/PSSg/z0koP89JKD/PSSg/zpG
4/84Uv7/OFP+/zhT/v83VP7/N1T+/zdV/v82Vf7/Nlb+/zZW/v81V/7/Mjdt/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/wby6/9fX7/88Uf3/OlD+/zpR/v85Uf7/N0rY/zAoJ/8xJzX/OiSX/zsk
of87JKH/OySh/zorrf83UfX/Nlb+/zZW/v81V/7/NVf+/zVY/v80WP7/NFn+/zNZ/v8ySbf/MCgn/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/gXt5/5qk8/84U/7/OFP+/zdU/v83VP7/ND6T/zAo
Jv8wKCb/Mic9/zglkf85JaP/OSWj/zklo/84LrH/NFHv/zRZ/v8zWv7/M1r+/zJb/v8yW/7/Mlz+/zFK
sv8wKi7/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/Qzw6/1Zp3P81UOT/NVDk/zVR
5P81UeT/MTBL/zAoJv8wKCb/MCgm/zAoK/8zJ1v/NSaB/zYmj/82JZP/NieW/zQ8tf8yTtH/MVXh/zBU
2P8wSq//MDZe/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/TAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb9MCgm3jAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCbeMCgmmTAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCaZMCgmLDAoJvkwKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJvkwKCYsAAAAADAoJncwKCb+MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/jAoJncAAAAAAAAAADAoJgEwKCZ3MCgm+TAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb5MCgmdzAoJgEAAAAAAAAAAAAA
AAAAAAAAMCgmLDAoJpkwKCbeMCgm/TAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAo
Jv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb/MCgm/zAoJv8wKCb9MCgm3jAoJpkwKCYsAAAAAAAA
AAAAAAAA4AAAAAAHAACAAAAAAAEAAIAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
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
</value>
</data>
</root>

View File

@@ -1,84 +1,126 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFrameworks>net472;net5.0-windows;net6.0-windows</TargetFrameworks>
<UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>Resources\as.ico</ApplicationIcon>
<Version>0.16.0.0</Version>
<AssemblyVersion>0.16.0.0</AssemblyVersion>
<FileVersion>0.16.0.0</FileVersion>
<Copyright>Copyright © Perfare 2018-2022</Copyright>
<DebugType>embedded</DebugType>
</PropertyGroup>
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFrameworks>net472;net6.0-windows;net7.0-windows;net8.0-windows</TargetFrameworks>
<UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>Resources\as.ico</ApplicationIcon>
<AssemblyTitle>AssetStudioMod by aelurum</AssemblyTitle>
<AssemblyName>AssetStudioModGUI</AssemblyName>
<Version>0.18.0.0</Version>
<Copyright>Copyright © Perfare 2018-2022; Copyright © aelurum 2021-2024</Copyright>
<DebugType>embedded</DebugType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\AssetStudioUtility\AssetStudioUtility.csproj" />
<ProjectReference Include="..\AssetStudio\AssetStudio.csproj" />
</ItemGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net472' ">
<IsPublishable>false</IsPublishable>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Update="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AssetStudioUtility\AssetStudioUtility.csproj" />
<ProjectReference Include="..\AssetStudio\AssetStudio.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Update="Properties\Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<Compile Update="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<Compile Update="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Libraries\x86\fmod.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>x86\fmod.dll</TargetPath>
</ContentWithTargetPath>
<ContentWithTargetPath Include="Libraries\x64\fmod.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>x64\fmod.dll</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="..\LICENSE">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>LICENSE</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net472' ">
<PackageReference Include="OpenTK" Version="4.6.7" />
<Reference Include="OpenTK.WinForms">
<HintPath>Libraries\OpenTK.WinForms.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft-WindowsAPICodePack-Core-6.0" Version="1.1.6" />
<PackageReference Include="Microsoft-WindowsAPICodePack-Shell-6.0" Version="1.1.6" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
<PackageReference Include="OpenTK" Version="3.1.0" />
<PackageReference Include="OpenTK.GLControl" Version="3.1.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net472' ">
<PackageReference Include="OpenTK.Graphics" Version="4.8.2" />
<PackageReference Include="OpenTK.Windowing.Desktop" Version="4.8.2" />
<Reference Include="OpenTK.WinForms">
<HintPath>Libraries\OpenTK.WinForms.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
<PackageReference Include="OpenTK" Version="3.3.3" />
<PackageReference Include="OpenTK.GLControl" Version="3.3.3" />
</ItemGroup>
<Target Name="CopyExtraFiles" AfterTargets="AfterBuild">
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\Win32\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)x86" ContinueOnError="true" />
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\x64\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)x64" ContinueOnError="true" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\Win32\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)x86" ContinueOnError="true" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\x64\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)x64" ContinueOnError="true" />
</Target>
<!-- 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)' == '' OR '$(TargetFramework)' == 'net472' ">
<Message Text="Copying extra files for $(TargetFramework)... " Importance="high" />
<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="$(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="$(ProjectDir)Libraries\x86\fmod.dll" DestinationFolder="$(TargetDir)runtimes\win-x86\native" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\x64\fmod.dll" DestinationFolder="$(TargetDir)runtimes\win-x64\native" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFiles" AfterTargets="Publish">
<Copy SourceFiles="$(TargetDir)x86\AssetStudioFBXNative.dll" DestinationFolder="$(PublishDir)x86" ContinueOnError="true" />
<Copy SourceFiles="$(TargetDir)x64\AssetStudioFBXNative.dll" DestinationFolder="$(PublishDir)x64" ContinueOnError="true" />
<Copy SourceFiles="$(TargetDir)x86\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)x86" ContinueOnError="true" />
<Copy SourceFiles="$(TargetDir)x64\Texture2DDecoderNative.dll" DestinationFolder="$(PublishDir)x64" ContinueOnError="true" />
</Target>
</Project>
<!-- 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 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>
<!-- No need to publish net472 build of AssetStudioGUI -->
<Target Name="PublishNet472" AfterTargets="Publish" Condition=" '$(TargetFramework)' == 'net472' ">
<Message Text="%0a NOTE: Publishing net472 build of AssetStudioGUI was disabled." Importance="high" />
<Message Text=" Instead, use the binaries created after the build.%0a" Importance="high" />
</Target>
<Target Name="CopyExtraFilesWin86" AfterTargets="AfterBuild" Condition=" $(RuntimeIdentifier.Contains('-x86')) AND '$(TargetFramework)' != 'net472' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\Win32\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\Win32\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\x86\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="CopyExtraFilesWin64" AfterTargets="AfterBuild" Condition=" '$(RuntimeIdentifier)' == 'win-x64' AND '$(TargetFramework)' != 'net472' ">
<Message Text="Copying extra files for $(RuntimeIdentifier)($(TargetFramework))... " Importance="high" />
<Copy SourceFiles="$(SolutionDir)Texture2DDecoderNative\bin\x64\$(Configuration)\Texture2DDecoderNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(SolutionDir)AssetStudioFBXNative\bin\x64\$(Configuration)\AssetStudioFBXNative.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
<Copy SourceFiles="$(ProjectDir)Libraries\x64\fmod.dll" DestinationFolder="$(TargetDir)" ContinueOnError="false" />
</Target>
<Target Name="PublishExtraFilesWin" AfterTargets="Publish" Condition=" $(RuntimeIdentifier.Contains('win')) AND '$(TargetFramework)' != 'net472' ">
<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>
</Project>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -141,6 +141,9 @@ The quick brown fox jumps over the lazy dog. 1234567890</value>
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>432, 17</value>
</metadata>
<metadata name="contextMenuStrip2.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>775, 21</value>
</metadata>
<metadata name="timer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>553, 17</value>
</metadata>

View File

@@ -0,0 +1,93 @@
// AlphanumComparatorFast mod by aelurum
// Original code was developed by Dot Net Perls
// For more detail visit: https://www.dotnetperls.com/alphanumeric-sorting
using System;
using System.Collections.Generic;
namespace AssetStudioGUI
{
internal class AlphanumComparatorFast : IComparer<string>
{
public int Compare(string s1, string s2)
{
int len1 = s1.Length;
int len2 = s2.Length;
int marker1 = 0;
int marker2 = 0;
// Walk through two the strings with two markers.
while (marker1 < len1 && marker2 < len2)
{
char ch1 = s1[marker1];
char ch2 = s2[marker2];
// Some buffers we can build up characters in for each chunk.
char[] space1 = new char[len1];
int loc1 = 0;
char[] space2 = new char[len2];
int loc2 = 0;
// Walk through all following characters that are digits or
// characters in BOTH strings starting at the appropriate marker.
// Collect char arrays.
do
{
space1[loc1++] = ch1;
marker1++;
if (marker1 < len1)
{
ch1 = s1[marker1];
}
else
{
break;
}
} while (char.IsDigit(ch1) == char.IsDigit(space1[0]));
do
{
space2[loc2++] = ch2;
marker2++;
if (marker2 < len2)
{
ch2 = s2[marker2];
}
else
{
break;
}
} while (char.IsDigit(ch2) == char.IsDigit(space2[0]));
// If we have collected numbers, compare them numerically.
// Otherwise, if we have strings, compare them alphabetically.
int result;
if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]))
{
if (long.TryParse(new string(space1), out long thisNumericChunk) &&
long.TryParse(new string(space2), out long thatNumericChunk))
{
result = thisNumericChunk.CompareTo(thatNumericChunk);
}
else
{
result = MemoryExtensions.CompareTo(space1, space2, StringComparison.Ordinal);
}
}
else
{
result = MemoryExtensions.CompareTo(space1, space2, StringComparison.InvariantCultureIgnoreCase);
}
if (result != 0)
{
return result;
}
}
return len1 - len2;
}
}
}

View File

@@ -0,0 +1,99 @@
// AlphanumComparatorFast mod by aelurum
// Original code was developed by Dot Net Perls
// For more detail visit: https://www.dotnetperls.com/alphanumeric-sorting
using System;
using System.Collections.Generic;
#if NET6_0_OR_GREATER
namespace AssetStudioGUI
{
internal class AlphanumComparatorFastNet : IComparer<string>
{
public int Compare(string s1, string s2)
{
const int maxStackSize = 256;
int len1 = s1.Length;
int len2 = s2.Length;
int marker1 = 0;
int marker2 = 0;
// Some buffers we can build up characters in for each chunk.
Span<char> space1 = len1 > maxStackSize ? new char[len1] : stackalloc char[len1];
Span<char> space2 = len2 > maxStackSize ? new char[len2] : stackalloc char[len2];
// Walk through two the strings with two markers.
while (marker1 < len1 && marker2 < len2)
{
char ch1 = s1[marker1];
char ch2 = s2[marker2];
int loc1 = 0;
int loc2 = 0;
space1.Clear();
space2.Clear();
// Walk through all following characters that are digits or
// characters in BOTH strings starting at the appropriate marker.
// Collect char arrays.
do
{
space1[loc1++] = ch1;
marker1++;
if (marker1 < len1)
{
ch1 = s1[marker1];
}
else
{
break;
}
} while (char.IsDigit(ch1) == char.IsDigit(space1[0]));
do
{
space2[loc2++] = ch2;
marker2++;
if (marker2 < len2)
{
ch2 = s2[marker2];
}
else
{
break;
}
} while (char.IsDigit(ch2) == char.IsDigit(space2[0]));
// If we have collected numbers, compare them numerically.
// Otherwise, if we have strings, compare them alphabetically.
int result;
if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]))
{
if (long.TryParse(space1, out long thisNumericChunk) &&
long.TryParse(space2, out long thatNumericChunk))
{
result = thisNumericChunk.CompareTo(thatNumericChunk);
}
else
{
result = MemoryExtensions.CompareTo(space1, space2, StringComparison.Ordinal);
}
}
else
{
result = MemoryExtensions.CompareTo(space1, space2, StringComparison.InvariantCultureIgnoreCase);
}
if (result != 0)
{
return result;
}
}
return len1 - len2;
}
}
}
#endif

View File

@@ -0,0 +1,67 @@
using System;
using System.Runtime.InteropServices;
using AssetStudio;
namespace AssetStudioGUI
{
internal static class ConsoleWindow
{
private enum CtrlSignalType
{
CTRL_C_EVENT,
CTRL_BREAK_EVENT,
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll", SetLastError = true)]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
private delegate bool EventHandler(CtrlSignalType ctrlSignal);
private static EventHandler eventHandler;
private static IntPtr ConsoleWindowHandle;
private static readonly int SW_HIDE = 0;
private static readonly int SW_SHOW = 5;
private static bool CloseEventHandler(CtrlSignalType ctrlSignal)
{
switch (ctrlSignal)
{
case CtrlSignalType.CTRL_C_EVENT:
case CtrlSignalType.CTRL_BREAK_EVENT:
return true;
default:
Logger.Verbose("Closing AssetStudio");
return false;
}
}
public static void RunConsole(bool showConsole)
{
AllocConsole();
ConsoleWindowHandle = GetConsoleWindow();
eventHandler += CloseEventHandler;
SetConsoleCtrlHandler(eventHandler, true);
if (!showConsole)
HideConsoleWindow();
}
public static void ShowConsoleWindow()
{
ShowWindow(ConsoleWindowHandle, SW_SHOW);
}
public static void HideConsoleWindow()
{
ShowWindow(ConsoleWindowHandle, SW_HIDE);
}
}
}

View File

@@ -0,0 +1,164 @@
// Shortcut (.lnk) file reader
// by aelurum
// Based on https://github.com/libyal/liblnk/blob/main/documentation/Windows%20Shortcut%20File%20(LNK)%20format.asciidoc
using AssetStudio;
using System;
using System.IO;
using System.Text;
namespace AssetStudioGUI
{
public static class LnkReader
{
[Flags]
private enum LnkDataFlags
{
//The LNK file contains a link target identifier
HasTargetIDList = 0x00000001,
//The LNK file contains location information
HasLinkInfo = 0x00000002,
}
[Flags]
private enum LnkLocFlags
{
//The linked file is on a volume
//If set the volume information and the local path contain data
VolumeIDAndLocalBasePath = 0x0001,
//The linked file is on a network share
//If set the network share information and common path contain data
CommonNetworkRelativeLinkAndPathSuffix = 0x0002
}
[Flags]
private enum PathTypeFlags
{
IsUnicodeLocalPath = 0x01,
IsUnicodeNetShareName = 0x02,
IsUnicodeCommonPath = 0x04
}
public static string GetLnkTarget(string filePath)
{
var targetPath = string.Empty;
var pathType = (PathTypeFlags)0;
Encoding sysEncoding;
try
{
sysEncoding = GetSysEncoding();
Logger.Debug($"System default text encoding: {sysEncoding.CodePage}");
}
catch (Exception ex)
{
Logger.Error("Text encoding error", ex);
return null;
}
using (var reader = new FileReader(filePath))
{
reader.Endian = EndianType.LittleEndian;
var headerSize = reader.ReadUInt32(); //76 bytes
reader.Position = 20; //skip LNK class identifier (GUID)
var dataFlags = (LnkDataFlags)reader.ReadUInt32();
if ((dataFlags & LnkDataFlags.HasLinkInfo) == 0)
{
Logger.Warning("Unsupported type of .lnk file. Link info was not found.");
return null;
}
reader.Position = headerSize;
//Skip the shell item ID list
if ((dataFlags & LnkDataFlags.HasTargetIDList) != 0)
{
var itemIDListSize = reader.ReadUInt16();
reader.Position += itemIDListSize;
}
//The offsets is relative to the start of the location information block
var locInfoPos = reader.Position;
var locInfoFullSize = reader.ReadUInt32();
if (locInfoFullSize == 0)
{
Logger.Warning("Unsupported type of .lnk file. Link info was not found.");
return null;
}
var locInfoHeaderSize = reader.ReadUInt32();
var locFlags = (LnkLocFlags)reader.ReadUInt32();
//Offset to the volume information block
var offsetVolumeInfo = reader.ReadUInt32();
//Offset to the ANSI local path
var offsetLocalPath = reader.ReadUInt32();
//Offset to the network share information block
var offsetNetInfo = reader.ReadUInt32();
//Offset to the ANSI common path. 0 if not available
var offsetCommonPath = reader.ReadUInt32();
if (locInfoHeaderSize > 28)
{
//Offset to the Unicode local path
offsetLocalPath = reader.ReadUInt32();
pathType |= PathTypeFlags.IsUnicodeLocalPath;
}
if (locInfoHeaderSize > 32)
{
//Offset to the Unicode common path
offsetCommonPath = reader.ReadUInt32();
pathType |= PathTypeFlags.IsUnicodeCommonPath;
}
//Read local path, if exist
if (offsetLocalPath > 0)
{
reader.Position = locInfoPos + offsetLocalPath;
targetPath = (pathType & PathTypeFlags.IsUnicodeLocalPath) != 0
? reader.ReadStringToNull(encoding: Encoding.Unicode)
: reader.ReadStringToNull(encoding: sysEncoding);
}
//Read network path, if exist
if (locFlags == LnkLocFlags.CommonNetworkRelativeLinkAndPathSuffix)
{
reader.Position = locInfoPos + offsetNetInfo;
var netInfoSize = reader.ReadUInt32();
var netInfoFlags = reader.ReadUInt32();
//Offset to the ANSI network share name. The offset is relative to the start of the network share information block
var offsetNetShareName = reader.ReadUInt32();
if (offsetNetShareName > 20)
{
reader.Position = locInfoPos + offsetNetInfo + 20;
//Offset to the Unicode network share name
offsetNetShareName = reader.ReadUInt32();
pathType |= PathTypeFlags.IsUnicodeNetShareName;
}
if (offsetNetShareName > 0)
{
reader.Position = locInfoPos + offsetNetInfo + offsetNetShareName;
targetPath = (pathType & PathTypeFlags.IsUnicodeNetShareName) != 0
? reader.ReadStringToNull(encoding: Encoding.Unicode)
: reader.ReadStringToNull(encoding: sysEncoding);
}
}
//Read common path, if exist
if (offsetCommonPath > 0)
{
reader.Position = locInfoPos + offsetCommonPath;
var commonPath = (pathType & PathTypeFlags.IsUnicodeCommonPath) != 0
? reader.ReadStringToNull(encoding: Encoding.Unicode)
: reader.ReadStringToNull(encoding: sysEncoding);
targetPath = Path.Combine(targetPath, commonPath);
}
}
return targetPath;
}
private static Encoding GetSysEncoding()
{
#if !NETFRAMEWORK
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
#endif
return Encoding.GetEncoding(0);
}
}
}

View File

@@ -1,4 +1,7 @@
using System;
using AssetStudio;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
@@ -7,11 +10,12 @@ namespace AssetStudioGUI
{
public sealed class DirectBitmap : IDisposable
{
public DirectBitmap(byte[] buff, int width, int height)
public DirectBitmap(Image<Bgra32> image)
{
Width = width;
Height = height;
Bits = buff;
Width = image.Width;
Height = image.Height;
Bits = BigArrayPool<byte>.Shared.Rent(Width * Height * 4);
image.CopyPixelDataTo(Bits);
m_handle = GCHandle.Alloc(Bits, GCHandleType.Pinned);
m_bitmap = new Bitmap(Width, Height, Stride, PixelFormat.Format32bppArgb, m_handle.AddrOfPinnedObject());
}
@@ -22,6 +26,7 @@ namespace AssetStudioGUI
{
m_bitmap.Dispose();
m_handle.Free();
BigArrayPool<byte>.Shared.Return(Bits);
}
m_bitmap = null;
}
@@ -38,6 +43,6 @@ namespace AssetStudioGUI
public Bitmap Bitmap => m_bitmap;
private Bitmap m_bitmap;
private readonly GCHandle m_handle;
private GCHandle m_handle;
}
}

View File

@@ -32,17 +32,27 @@
this.OKbutton = new System.Windows.Forms.Button();
this.Cancel = new System.Windows.Forms.Button();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.filenameFormatLabel = new System.Windows.Forms.Label();
this.filenameFormatComboBox = new System.Windows.Forms.ComboBox();
this.exportSpriteWithAlphaMask = new System.Windows.Forms.CheckBox();
this.openAfterExport = new System.Windows.Forms.CheckBox();
this.restoreExtensionName = new System.Windows.Forms.CheckBox();
this.assetGroupOptions = new System.Windows.Forms.ComboBox();
this.label6 = new System.Windows.Forms.Label();
this.convertAudio = new System.Windows.Forms.CheckBox();
this.panel1 = new System.Windows.Forms.Panel();
this.towebp = new System.Windows.Forms.RadioButton();
this.totga = new System.Windows.Forms.RadioButton();
this.tojpg = new System.Windows.Forms.RadioButton();
this.topng = new System.Windows.Forms.RadioButton();
this.tobmp = new System.Windows.Forms.RadioButton();
this.converttexture = new System.Windows.Forms.CheckBox();
this.l2dGroupBox = new System.Windows.Forms.GroupBox();
this.l2dMotionExportMethodPanel = new System.Windows.Forms.Panel();
this.l2dMonoBehaviourRadioButton = new System.Windows.Forms.RadioButton();
this.l2dAnimationClipRadioButton = new System.Windows.Forms.RadioButton();
this.l2dMotionExportMethodLabel = new System.Windows.Forms.Label();
this.l2dForceBezierCheckBox = new System.Windows.Forms.CheckBox();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.exportAllUvsAsDiffuseMaps = new System.Windows.Forms.CheckBox();
this.exportBlendShape = new System.Windows.Forms.CheckBox();
@@ -61,21 +71,27 @@
this.castToBone = new System.Windows.Forms.CheckBox();
this.exportAllNodes = new System.Windows.Forms.CheckBox();
this.eulerFilter = new System.Windows.Forms.CheckBox();
this.exportUvsTooltip = new System.Windows.Forms.ToolTip(this.components);
this.optionTooltip = new System.Windows.Forms.ToolTip(this.components);
this.parallelExportUpDown = new System.Windows.Forms.NumericUpDown();
this.parallelExportCheckBox = new System.Windows.Forms.CheckBox();
this.parallelExportMaxLabel = new System.Windows.Forms.Label();
this.groupBox1.SuspendLayout();
this.panel1.SuspendLayout();
this.l2dGroupBox.SuspendLayout();
this.l2dMotionExportMethodPanel.SuspendLayout();
this.groupBox2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.scaleFactor)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.boneSize)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.filterPrecision)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.parallelExportUpDown)).BeginInit();
this.SuspendLayout();
//
// OKbutton
//
this.OKbutton.Location = new System.Drawing.Point(318, 351);
this.OKbutton.Location = new System.Drawing.Point(381, 380);
this.OKbutton.Name = "OKbutton";
this.OKbutton.Size = new System.Drawing.Size(75, 21);
this.OKbutton.TabIndex = 6;
this.OKbutton.Size = new System.Drawing.Size(75, 23);
this.OKbutton.TabIndex = 4;
this.OKbutton.Text = "OK";
this.OKbutton.UseVisualStyleBackColor = true;
this.OKbutton.Click += new System.EventHandler(this.OKbutton_Click);
@@ -83,10 +99,10 @@
// Cancel
//
this.Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.Cancel.Location = new System.Drawing.Point(399, 351);
this.Cancel.Location = new System.Drawing.Point(462, 380);
this.Cancel.Name = "Cancel";
this.Cancel.Size = new System.Drawing.Size(75, 21);
this.Cancel.TabIndex = 7;
this.Cancel.Size = new System.Drawing.Size(75, 23);
this.Cancel.TabIndex = 5;
this.Cancel.Text = "Cancel";
this.Cancel.UseVisualStyleBackColor = true;
this.Cancel.Click += new System.EventHandler(this.Cancel_Click);
@@ -94,6 +110,12 @@
// groupBox1
//
this.groupBox1.AutoSize = true;
this.groupBox1.Controls.Add(this.parallelExportMaxLabel);
this.groupBox1.Controls.Add(this.parallelExportCheckBox);
this.groupBox1.Controls.Add(this.parallelExportUpDown);
this.groupBox1.Controls.Add(this.filenameFormatLabel);
this.groupBox1.Controls.Add(this.filenameFormatComboBox);
this.groupBox1.Controls.Add(this.exportSpriteWithAlphaMask);
this.groupBox1.Controls.Add(this.openAfterExport);
this.groupBox1.Controls.Add(this.restoreExtensionName);
this.groupBox1.Controls.Add(this.assetGroupOptions);
@@ -101,22 +123,56 @@
this.groupBox1.Controls.Add(this.convertAudio);
this.groupBox1.Controls.Add(this.panel1);
this.groupBox1.Controls.Add(this.converttexture);
this.groupBox1.Location = new System.Drawing.Point(12, 12);
this.groupBox1.Location = new System.Drawing.Point(12, 13);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(232, 334);
this.groupBox1.TabIndex = 9;
this.groupBox1.Size = new System.Drawing.Size(301, 272);
this.groupBox1.TabIndex = 1;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Export";
//
// filenameFormatLabel
//
this.filenameFormatLabel.AutoSize = true;
this.filenameFormatLabel.Location = new System.Drawing.Point(177, 18);
this.filenameFormatLabel.Name = "filenameFormatLabel";
this.filenameFormatLabel.Size = new System.Drawing.Size(84, 13);
this.filenameFormatLabel.TabIndex = 10;
this.filenameFormatLabel.Text = "File name format";
//
// filenameFormatComboBox
//
this.filenameFormatComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.filenameFormatComboBox.FormattingEnabled = true;
this.filenameFormatComboBox.Items.AddRange(new object[] {
"assetName",
"assetName@pathID",
"pathID"});
this.filenameFormatComboBox.Location = new System.Drawing.Point(177, 35);
this.filenameFormatComboBox.Name = "filenameFormatComboBox";
this.filenameFormatComboBox.Size = new System.Drawing.Size(118, 21);
this.filenameFormatComboBox.TabIndex = 9;
//
// exportSpriteWithAlphaMask
//
this.exportSpriteWithAlphaMask.AutoSize = true;
this.exportSpriteWithAlphaMask.Checked = true;
this.exportSpriteWithAlphaMask.CheckState = System.Windows.Forms.CheckState.Checked;
this.exportSpriteWithAlphaMask.Location = new System.Drawing.Point(6, 150);
this.exportSpriteWithAlphaMask.Name = "exportSpriteWithAlphaMask";
this.exportSpriteWithAlphaMask.Size = new System.Drawing.Size(205, 17);
this.exportSpriteWithAlphaMask.TabIndex = 6;
this.exportSpriteWithAlphaMask.Text = "Export sprites with alpha mask applied";
this.exportSpriteWithAlphaMask.UseVisualStyleBackColor = true;
//
// openAfterExport
//
this.openAfterExport.AutoSize = true;
this.openAfterExport.Checked = true;
this.openAfterExport.CheckState = System.Windows.Forms.CheckState.Checked;
this.openAfterExport.Location = new System.Drawing.Point(6, 160);
this.openAfterExport.Location = new System.Drawing.Point(6, 196);
this.openAfterExport.Name = "openAfterExport";
this.openAfterExport.Size = new System.Drawing.Size(168, 16);
this.openAfterExport.TabIndex = 10;
this.openAfterExport.Size = new System.Drawing.Size(137, 17);
this.openAfterExport.TabIndex = 8;
this.openAfterExport.Text = "Open folder after export";
this.openAfterExport.UseVisualStyleBackColor = true;
//
@@ -125,11 +181,12 @@
this.restoreExtensionName.AutoSize = true;
this.restoreExtensionName.Checked = true;
this.restoreExtensionName.CheckState = System.Windows.Forms.CheckState.Checked;
this.restoreExtensionName.Location = new System.Drawing.Point(6, 58);
this.restoreExtensionName.Location = new System.Drawing.Point(6, 63);
this.restoreExtensionName.Name = "restoreExtensionName";
this.restoreExtensionName.Size = new System.Drawing.Size(216, 16);
this.restoreExtensionName.TabIndex = 9;
this.restoreExtensionName.Text = "Restore TextAsset extension name";
this.restoreExtensionName.Size = new System.Drawing.Size(275, 17);
this.restoreExtensionName.TabIndex = 3;
this.restoreExtensionName.Text = "Try to restore/Use original TextAsset extension name";
this.optionTooltip.SetToolTip(this.restoreExtensionName, "If not checked, AssetStudio will export all TextAssets with the \".txt\" extension");
this.restoreExtensionName.UseVisualStyleBackColor = true;
//
// assetGroupOptions
@@ -139,20 +196,22 @@
this.assetGroupOptions.Items.AddRange(new object[] {
"type name",
"container path",
"container path full (with name)",
"source file name",
"scene hierarchy",
"do not group"});
this.assetGroupOptions.Location = new System.Drawing.Point(6, 32);
this.assetGroupOptions.Location = new System.Drawing.Point(6, 35);
this.assetGroupOptions.Name = "assetGroupOptions";
this.assetGroupOptions.Size = new System.Drawing.Size(149, 20);
this.assetGroupOptions.TabIndex = 8;
this.assetGroupOptions.Size = new System.Drawing.Size(165, 21);
this.assetGroupOptions.TabIndex = 2;
//
// label6
//
this.label6.AutoSize = true;
this.label6.Location = new System.Drawing.Point(6, 17);
this.label6.Location = new System.Drawing.Point(6, 18);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(149, 12);
this.label6.TabIndex = 7;
this.label6.Size = new System.Drawing.Size(127, 13);
this.label6.TabIndex = 1;
this.label6.Text = "Group exported assets by";
//
// convertAudio
@@ -160,41 +219,52 @@
this.convertAudio.AutoSize = true;
this.convertAudio.Checked = true;
this.convertAudio.CheckState = System.Windows.Forms.CheckState.Checked;
this.convertAudio.Location = new System.Drawing.Point(6, 138);
this.convertAudio.Location = new System.Drawing.Point(6, 173);
this.convertAudio.Name = "convertAudio";
this.convertAudio.Size = new System.Drawing.Size(198, 16);
this.convertAudio.TabIndex = 6;
this.convertAudio.Text = "Convert AudioClip to WAV(PCM)";
this.convertAudio.Size = new System.Drawing.Size(213, 17);
this.convertAudio.TabIndex = 7;
this.convertAudio.Text = "Convert FMOD AudioClip to WAV(PCM)";
this.convertAudio.UseVisualStyleBackColor = true;
//
// panel1
//
this.panel1.Controls.Add(this.towebp);
this.panel1.Controls.Add(this.totga);
this.panel1.Controls.Add(this.tojpg);
this.panel1.Controls.Add(this.topng);
this.panel1.Controls.Add(this.tobmp);
this.panel1.Location = new System.Drawing.Point(20, 102);
this.panel1.Location = new System.Drawing.Point(18, 111);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(202, 30);
this.panel1.Size = new System.Drawing.Size(260, 33);
this.panel1.TabIndex = 5;
//
// towebp
//
this.towebp.AutoSize = true;
this.towebp.Location = new System.Drawing.Point(201, 7);
this.towebp.Name = "towebp";
this.towebp.Size = new System.Drawing.Size(54, 17);
this.towebp.TabIndex = 4;
this.towebp.Text = "Webp";
this.towebp.UseVisualStyleBackColor = true;
//
// totga
//
this.totga.AutoSize = true;
this.totga.Location = new System.Drawing.Point(150, 6);
this.totga.Location = new System.Drawing.Point(150, 7);
this.totga.Name = "totga";
this.totga.Size = new System.Drawing.Size(41, 16);
this.totga.TabIndex = 2;
this.totga.Size = new System.Drawing.Size(44, 17);
this.totga.TabIndex = 3;
this.totga.Text = "Tga";
this.totga.UseVisualStyleBackColor = true;
//
// tojpg
//
this.tojpg.AutoSize = true;
this.tojpg.Location = new System.Drawing.Point(97, 6);
this.tojpg.Location = new System.Drawing.Point(97, 7);
this.tojpg.Name = "tojpg";
this.tojpg.Size = new System.Drawing.Size(47, 16);
this.tojpg.TabIndex = 4;
this.tojpg.Size = new System.Drawing.Size(48, 17);
this.tojpg.TabIndex = 2;
this.tojpg.Text = "Jpeg";
this.tojpg.UseVisualStyleBackColor = true;
//
@@ -202,10 +272,10 @@
//
this.topng.AutoSize = true;
this.topng.Checked = true;
this.topng.Location = new System.Drawing.Point(50, 6);
this.topng.Location = new System.Drawing.Point(50, 7);
this.topng.Name = "topng";
this.topng.Size = new System.Drawing.Size(41, 16);
this.topng.TabIndex = 3;
this.topng.Size = new System.Drawing.Size(44, 17);
this.topng.TabIndex = 1;
this.topng.TabStop = true;
this.topng.Text = "Png";
this.topng.UseVisualStyleBackColor = true;
@@ -213,10 +283,10 @@
// tobmp
//
this.tobmp.AutoSize = true;
this.tobmp.Location = new System.Drawing.Point(3, 6);
this.tobmp.Location = new System.Drawing.Point(3, 7);
this.tobmp.Name = "tobmp";
this.tobmp.Size = new System.Drawing.Size(41, 16);
this.tobmp.TabIndex = 2;
this.tobmp.Size = new System.Drawing.Size(46, 17);
this.tobmp.TabIndex = 0;
this.tobmp.Text = "Bmp";
this.tobmp.UseVisualStyleBackColor = true;
//
@@ -225,13 +295,79 @@
this.converttexture.AutoSize = true;
this.converttexture.Checked = true;
this.converttexture.CheckState = System.Windows.Forms.CheckState.Checked;
this.converttexture.Location = new System.Drawing.Point(6, 80);
this.converttexture.Location = new System.Drawing.Point(6, 87);
this.converttexture.Name = "converttexture";
this.converttexture.Size = new System.Drawing.Size(126, 16);
this.converttexture.TabIndex = 1;
this.converttexture.Size = new System.Drawing.Size(116, 17);
this.converttexture.TabIndex = 4;
this.converttexture.Text = "Convert Texture2D";
this.converttexture.UseVisualStyleBackColor = true;
//
// l2dGroupBox
//
this.l2dGroupBox.Controls.Add(this.l2dMotionExportMethodPanel);
this.l2dGroupBox.Controls.Add(this.l2dMotionExportMethodLabel);
this.l2dGroupBox.Controls.Add(this.l2dForceBezierCheckBox);
this.l2dGroupBox.Location = new System.Drawing.Point(12, 275);
this.l2dGroupBox.Name = "l2dGroupBox";
this.l2dGroupBox.Size = new System.Drawing.Size(301, 100);
this.l2dGroupBox.TabIndex = 2;
this.l2dGroupBox.TabStop = false;
this.l2dGroupBox.Text = "Cubism Live2D";
//
// l2dMotionExportMethodPanel
//
this.l2dMotionExportMethodPanel.Controls.Add(this.l2dMonoBehaviourRadioButton);
this.l2dMotionExportMethodPanel.Controls.Add(this.l2dAnimationClipRadioButton);
this.l2dMotionExportMethodPanel.Location = new System.Drawing.Point(18, 40);
this.l2dMotionExportMethodPanel.Name = "l2dMotionExportMethodPanel";
this.l2dMotionExportMethodPanel.Size = new System.Drawing.Size(263, 27);
this.l2dMotionExportMethodPanel.TabIndex = 2;
//
// l2dMonoBehaviourRadioButton
//
this.l2dMonoBehaviourRadioButton.AccessibleName = "MonoBehaviour";
this.l2dMonoBehaviourRadioButton.AutoSize = true;
this.l2dMonoBehaviourRadioButton.Checked = true;
this.l2dMonoBehaviourRadioButton.Location = new System.Drawing.Point(3, 5);
this.l2dMonoBehaviourRadioButton.Name = "l2dMonoBehaviourRadioButton";
this.l2dMonoBehaviourRadioButton.Size = new System.Drawing.Size(167, 17);
this.l2dMonoBehaviourRadioButton.TabIndex = 0;
this.l2dMonoBehaviourRadioButton.TabStop = true;
this.l2dMonoBehaviourRadioButton.Text = "MonoBehaviour (Fade motion)";
this.optionTooltip.SetToolTip(this.l2dMonoBehaviourRadioButton, "If no Fade motions are found, the AnimationClip method will be used");
this.l2dMonoBehaviourRadioButton.UseVisualStyleBackColor = true;
//
// l2dAnimationClipRadioButton
//
this.l2dAnimationClipRadioButton.AccessibleName = "AnimationClipV2";
this.l2dAnimationClipRadioButton.AutoSize = true;
this.l2dAnimationClipRadioButton.Location = new System.Drawing.Point(172, 5);
this.l2dAnimationClipRadioButton.Name = "l2dAnimationClipRadioButton";
this.l2dAnimationClipRadioButton.Size = new System.Drawing.Size(88, 17);
this.l2dAnimationClipRadioButton.TabIndex = 1;
this.l2dAnimationClipRadioButton.Text = "AnimationClip";
this.l2dAnimationClipRadioButton.UseVisualStyleBackColor = true;
//
// l2dMotionExportMethodLabel
//
this.l2dMotionExportMethodLabel.AutoSize = true;
this.l2dMotionExportMethodLabel.Location = new System.Drawing.Point(6, 21);
this.l2dMotionExportMethodLabel.Name = "l2dMotionExportMethodLabel";
this.l2dMotionExportMethodLabel.Size = new System.Drawing.Size(109, 13);
this.l2dMotionExportMethodLabel.TabIndex = 1;
this.l2dMotionExportMethodLabel.Text = "Motion export method";
//
// l2dForceBezierCheckBox
//
this.l2dForceBezierCheckBox.AutoSize = true;
this.l2dForceBezierCheckBox.Location = new System.Drawing.Point(6, 77);
this.l2dForceBezierCheckBox.Name = "l2dForceBezierCheckBox";
this.l2dForceBezierCheckBox.Size = new System.Drawing.Size(278, 17);
this.l2dForceBezierCheckBox.TabIndex = 3;
this.l2dForceBezierCheckBox.Text = "Calculate Linear motion segments as Bezier segments";
this.optionTooltip.SetToolTip(this.l2dForceBezierCheckBox, "May help if the exported motions look jerky/not smooth enough");
this.l2dForceBezierCheckBox.UseVisualStyleBackColor = true;
//
// groupBox2
//
this.groupBox2.AutoSize = true;
@@ -252,10 +388,10 @@
this.groupBox2.Controls.Add(this.castToBone);
this.groupBox2.Controls.Add(this.exportAllNodes);
this.groupBox2.Controls.Add(this.eulerFilter);
this.groupBox2.Location = new System.Drawing.Point(250, 12);
this.groupBox2.Location = new System.Drawing.Point(313, 13);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(224, 334);
this.groupBox2.TabIndex = 11;
this.groupBox2.Size = new System.Drawing.Size(224, 362);
this.groupBox2.TabIndex = 3;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Fbx";
//
@@ -263,12 +399,12 @@
//
this.exportAllUvsAsDiffuseMaps.AccessibleDescription = "";
this.exportAllUvsAsDiffuseMaps.AutoSize = true;
this.exportAllUvsAsDiffuseMaps.Location = new System.Drawing.Point(6, 171);
this.exportAllUvsAsDiffuseMaps.Location = new System.Drawing.Point(6, 185);
this.exportAllUvsAsDiffuseMaps.Name = "exportAllUvsAsDiffuseMaps";
this.exportAllUvsAsDiffuseMaps.Size = new System.Drawing.Size(204, 16);
this.exportAllUvsAsDiffuseMaps.TabIndex = 23;
this.exportAllUvsAsDiffuseMaps.Size = new System.Drawing.Size(168, 17);
this.exportAllUvsAsDiffuseMaps.TabIndex = 9;
this.exportAllUvsAsDiffuseMaps.Text = "Export all UVs as diffuse maps";
this.exportUvsTooltip.SetToolTip(this.exportAllUvsAsDiffuseMaps, "Unchecked: UV1 exported as normal map. Check this if your export is missing a UV " +
this.optionTooltip.SetToolTip(this.exportAllUvsAsDiffuseMaps, "Unchecked: UV1 exported as normal map. Check this if your export is missing a UV " +
"map.");
this.exportAllUvsAsDiffuseMaps.UseVisualStyleBackColor = true;
//
@@ -277,10 +413,10 @@
this.exportBlendShape.AutoSize = true;
this.exportBlendShape.Checked = true;
this.exportBlendShape.CheckState = System.Windows.Forms.CheckState.Checked;
this.exportBlendShape.Location = new System.Drawing.Point(6, 127);
this.exportBlendShape.Location = new System.Drawing.Point(6, 138);
this.exportBlendShape.Name = "exportBlendShape";
this.exportBlendShape.Size = new System.Drawing.Size(126, 16);
this.exportBlendShape.TabIndex = 22;
this.exportBlendShape.Size = new System.Drawing.Size(114, 17);
this.exportBlendShape.TabIndex = 7;
this.exportBlendShape.Text = "Export blendshape";
this.exportBlendShape.UseVisualStyleBackColor = true;
//
@@ -289,10 +425,10 @@
this.exportAnimations.AutoSize = true;
this.exportAnimations.Checked = true;
this.exportAnimations.CheckState = System.Windows.Forms.CheckState.Checked;
this.exportAnimations.Location = new System.Drawing.Point(6, 105);
this.exportAnimations.Location = new System.Drawing.Point(6, 114);
this.exportAnimations.Name = "exportAnimations";
this.exportAnimations.Size = new System.Drawing.Size(126, 16);
this.exportAnimations.TabIndex = 21;
this.exportAnimations.Size = new System.Drawing.Size(109, 17);
this.exportAnimations.TabIndex = 6;
this.exportAnimations.Text = "Export animations";
this.exportAnimations.UseVisualStyleBackColor = true;
//
@@ -304,10 +440,10 @@
0,
0,
131072});
this.scaleFactor.Location = new System.Drawing.Point(83, 224);
this.scaleFactor.Location = new System.Drawing.Point(83, 243);
this.scaleFactor.Name = "scaleFactor";
this.scaleFactor.Size = new System.Drawing.Size(60, 21);
this.scaleFactor.TabIndex = 20;
this.scaleFactor.Size = new System.Drawing.Size(60, 20);
this.scaleFactor.TabIndex = 13;
this.scaleFactor.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
this.scaleFactor.Value = new decimal(new int[] {
1,
@@ -318,10 +454,10 @@
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(6, 226);
this.label5.Location = new System.Drawing.Point(6, 245);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(71, 12);
this.label5.TabIndex = 19;
this.label5.Size = new System.Drawing.Size(64, 13);
this.label5.TabIndex = 12;
this.label5.Text = "ScaleFactor";
//
// fbxFormat
@@ -331,18 +467,18 @@
this.fbxFormat.Items.AddRange(new object[] {
"Binary",
"Ascii"});
this.fbxFormat.Location = new System.Drawing.Point(77, 254);
this.fbxFormat.Location = new System.Drawing.Point(77, 275);
this.fbxFormat.Name = "fbxFormat";
this.fbxFormat.Size = new System.Drawing.Size(61, 20);
this.fbxFormat.TabIndex = 18;
this.fbxFormat.Size = new System.Drawing.Size(61, 21);
this.fbxFormat.TabIndex = 15;
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(6, 258);
this.label4.Location = new System.Drawing.Point(6, 280);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(59, 12);
this.label4.TabIndex = 17;
this.label4.Size = new System.Drawing.Size(59, 13);
this.label4.TabIndex = 14;
this.label4.Text = "FBXFormat";
//
// fbxVersion
@@ -356,25 +492,25 @@
"7.3",
"7.4",
"7.5"});
this.fbxVersion.Location = new System.Drawing.Point(77, 284);
this.fbxVersion.Location = new System.Drawing.Point(77, 308);
this.fbxVersion.Name = "fbxVersion";
this.fbxVersion.Size = new System.Drawing.Size(47, 20);
this.fbxVersion.TabIndex = 16;
this.fbxVersion.Size = new System.Drawing.Size(47, 21);
this.fbxVersion.TabIndex = 17;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(6, 287);
this.label3.Location = new System.Drawing.Point(6, 311);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(65, 12);
this.label3.TabIndex = 15;
this.label3.Size = new System.Drawing.Size(62, 13);
this.label3.TabIndex = 16;
this.label3.Text = "FBXVersion";
//
// boneSize
//
this.boneSize.Location = new System.Drawing.Point(65, 197);
this.boneSize.Location = new System.Drawing.Point(65, 213);
this.boneSize.Name = "boneSize";
this.boneSize.Size = new System.Drawing.Size(46, 21);
this.boneSize.Size = new System.Drawing.Size(46, 20);
this.boneSize.TabIndex = 11;
this.boneSize.Value = new decimal(new int[] {
10,
@@ -385,9 +521,9 @@
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(6, 199);
this.label2.Location = new System.Drawing.Point(6, 216);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(53, 12);
this.label2.Size = new System.Drawing.Size(52, 13);
this.label2.TabIndex = 10;
this.label2.Text = "BoneSize";
//
@@ -396,20 +532,20 @@
this.exportSkins.AutoSize = true;
this.exportSkins.Checked = true;
this.exportSkins.CheckState = System.Windows.Forms.CheckState.Checked;
this.exportSkins.Location = new System.Drawing.Point(6, 83);
this.exportSkins.Location = new System.Drawing.Point(6, 90);
this.exportSkins.Name = "exportSkins";
this.exportSkins.Size = new System.Drawing.Size(96, 16);
this.exportSkins.TabIndex = 8;
this.exportSkins.Size = new System.Drawing.Size(83, 17);
this.exportSkins.TabIndex = 5;
this.exportSkins.Text = "Export skins";
this.exportSkins.UseVisualStyleBackColor = true;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(26, 39);
this.label1.Location = new System.Drawing.Point(26, 42);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(95, 12);
this.label1.TabIndex = 7;
this.label1.Size = new System.Drawing.Size(72, 13);
this.label1.TabIndex = 2;
this.label1.Text = "FilterPrecision";
//
// filterPrecision
@@ -420,10 +556,10 @@
0,
0,
131072});
this.filterPrecision.Location = new System.Drawing.Point(127, 37);
this.filterPrecision.Location = new System.Drawing.Point(127, 40);
this.filterPrecision.Name = "filterPrecision";
this.filterPrecision.Size = new System.Drawing.Size(51, 21);
this.filterPrecision.TabIndex = 6;
this.filterPrecision.Size = new System.Drawing.Size(51, 20);
this.filterPrecision.TabIndex = 3;
this.filterPrecision.Value = new decimal(new int[] {
25,
0,
@@ -433,10 +569,10 @@
// castToBone
//
this.castToBone.AutoSize = true;
this.castToBone.Location = new System.Drawing.Point(6, 149);
this.castToBone.Location = new System.Drawing.Point(6, 161);
this.castToBone.Name = "castToBone";
this.castToBone.Size = new System.Drawing.Size(156, 16);
this.castToBone.TabIndex = 5;
this.castToBone.Size = new System.Drawing.Size(131, 17);
this.castToBone.TabIndex = 8;
this.castToBone.Text = "All nodes cast to bone";
this.castToBone.UseVisualStyleBackColor = true;
//
@@ -445,9 +581,9 @@
this.exportAllNodes.AutoSize = true;
this.exportAllNodes.Checked = true;
this.exportAllNodes.CheckState = System.Windows.Forms.CheckState.Checked;
this.exportAllNodes.Location = new System.Drawing.Point(6, 61);
this.exportAllNodes.Location = new System.Drawing.Point(6, 66);
this.exportAllNodes.Name = "exportAllNodes";
this.exportAllNodes.Size = new System.Drawing.Size(120, 16);
this.exportAllNodes.Size = new System.Drawing.Size(101, 17);
this.exportAllNodes.TabIndex = 4;
this.exportAllNodes.Text = "Export all nodes";
this.exportAllNodes.UseVisualStyleBackColor = true;
@@ -457,41 +593,94 @@
this.eulerFilter.AutoSize = true;
this.eulerFilter.Checked = true;
this.eulerFilter.CheckState = System.Windows.Forms.CheckState.Checked;
this.eulerFilter.Location = new System.Drawing.Point(6, 20);
this.eulerFilter.Location = new System.Drawing.Point(6, 22);
this.eulerFilter.Name = "eulerFilter";
this.eulerFilter.Size = new System.Drawing.Size(90, 16);
this.eulerFilter.TabIndex = 3;
this.eulerFilter.Size = new System.Drawing.Size(72, 17);
this.eulerFilter.TabIndex = 1;
this.eulerFilter.Text = "EulerFilter";
this.eulerFilter.UseVisualStyleBackColor = true;
//
// parallelExportUpDown
//
this.parallelExportUpDown.Location = new System.Drawing.Point(209, 218);
this.parallelExportUpDown.Maximum = new decimal(new int[] {
8,
0,
0,
0});
this.parallelExportUpDown.Minimum = new decimal(new int[] {
1,
0,
0,
0});
this.parallelExportUpDown.Name = "parallelExportUpDown";
this.parallelExportUpDown.Size = new System.Drawing.Size(42, 20);
this.parallelExportUpDown.TabIndex = 13;
this.parallelExportUpDown.Value = new decimal(new int[] {
1,
0,
0,
0});
//
// parallelExportCheckBox
//
this.parallelExportCheckBox.AutoSize = true;
this.parallelExportCheckBox.Checked = true;
this.parallelExportCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
this.parallelExportCheckBox.Location = new System.Drawing.Point(6, 219);
this.parallelExportCheckBox.Name = "parallelExportCheckBox";
this.parallelExportCheckBox.Size = new System.Drawing.Size(203, 17);
this.parallelExportCheckBox.TabIndex = 15;
this.parallelExportCheckBox.Text = "Export in parallel with number of tasks";
this.optionTooltip.SetToolTip(this.parallelExportCheckBox, "*Requires slightly more RAM than in single-task mode");
this.parallelExportCheckBox.UseVisualStyleBackColor = true;
this.parallelExportCheckBox.CheckedChanged += new System.EventHandler(this.parallelExportCheckBox_CheckedChanged);
//
// parallelExportMaxLabel
//
this.parallelExportMaxLabel.AutoSize = true;
this.parallelExportMaxLabel.ForeColor = System.Drawing.SystemColors.ControlDark;
this.parallelExportMaxLabel.Location = new System.Drawing.Point(256, 221);
this.parallelExportMaxLabel.Name = "parallelExportMaxLabel";
this.parallelExportMaxLabel.Size = new System.Drawing.Size(33, 13);
this.parallelExportMaxLabel.TabIndex = 16;
this.parallelExportMaxLabel.Text = "Max: ";
this.optionTooltip.SetToolTip(this.parallelExportMaxLabel, "*The maximum number matches the number of CPU cores");
//
// ExportOptions
//
this.AcceptButton = this.OKbutton;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.Cancel;
this.ClientSize = new System.Drawing.Size(486, 384);
this.ClientSize = new System.Drawing.Size(549, 416);
this.Controls.Add(this.l2dGroupBox);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.Cancel);
this.Controls.Add(this.OKbutton);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "ExportOptions";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Export options";
this.TopMost = true;
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.l2dGroupBox.ResumeLayout(false);
this.l2dGroupBox.PerformLayout();
this.l2dMotionExportMethodPanel.ResumeLayout(false);
this.l2dMotionExportMethodPanel.PerformLayout();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.scaleFactor)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.boneSize)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.filterPrecision)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.parallelExportUpDown)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
@@ -530,6 +719,19 @@
private System.Windows.Forms.CheckBox restoreExtensionName;
private System.Windows.Forms.CheckBox openAfterExport;
private System.Windows.Forms.CheckBox exportAllUvsAsDiffuseMaps;
private System.Windows.Forms.ToolTip exportUvsTooltip;
private System.Windows.Forms.ToolTip optionTooltip;
private System.Windows.Forms.CheckBox exportSpriteWithAlphaMask;
private System.Windows.Forms.RadioButton towebp;
private System.Windows.Forms.GroupBox l2dGroupBox;
private System.Windows.Forms.CheckBox l2dForceBezierCheckBox;
private System.Windows.Forms.Label l2dMotionExportMethodLabel;
private System.Windows.Forms.RadioButton l2dAnimationClipRadioButton;
private System.Windows.Forms.RadioButton l2dMonoBehaviourRadioButton;
private System.Windows.Forms.Panel l2dMotionExportMethodPanel;
private System.Windows.Forms.ComboBox filenameFormatComboBox;
private System.Windows.Forms.Label filenameFormatLabel;
private System.Windows.Forms.NumericUpDown parallelExportUpDown;
private System.Windows.Forms.CheckBox parallelExportCheckBox;
private System.Windows.Forms.Label parallelExportMaxLabel;
}
}

View File

@@ -1,5 +1,6 @@
using AssetStudio;
using System;
using System.Linq;
using System.Windows.Forms;
namespace AssetStudioGUI
@@ -12,16 +13,10 @@ namespace AssetStudioGUI
assetGroupOptions.SelectedIndex = Properties.Settings.Default.assetGroupOption;
restoreExtensionName.Checked = Properties.Settings.Default.restoreExtensionName;
converttexture.Checked = Properties.Settings.Default.convertTexture;
exportSpriteWithAlphaMask.Checked = Properties.Settings.Default.exportSpriteWithMask;
convertAudio.Checked = Properties.Settings.Default.convertAudio;
var str = Properties.Settings.Default.convertType.ToString();
foreach (Control c in panel1.Controls)
{
if (c.Text == str)
{
((RadioButton)c).Checked = true;
break;
}
}
var defaultImageType = Properties.Settings.Default.convertType.ToString();
((RadioButton)panel1.Controls.Cast<Control>().First(x => x.Text == defaultImageType)).Checked = true;
openAfterExport.Checked = Properties.Settings.Default.openAfterExport;
eulerFilter.Checked = Properties.Settings.Default.eulerFilter;
filterPrecision.Value = Properties.Settings.Default.filterPrecision;
@@ -35,7 +30,16 @@ namespace AssetStudioGUI
scaleFactor.Value = Properties.Settings.Default.scaleFactor;
fbxVersion.SelectedIndex = Properties.Settings.Default.fbxVersion;
fbxFormat.SelectedIndex = Properties.Settings.Default.fbxFormat;
var defaultMotionMode = Properties.Settings.Default.l2dMotionMode.ToString();
((RadioButton)l2dMotionExportMethodPanel.Controls.Cast<Control>().First(x => x.AccessibleName == defaultMotionMode)).Checked = true;
l2dForceBezierCheckBox.Checked = Properties.Settings.Default.l2dForceBezier;
filenameFormatComboBox.SelectedIndex = Properties.Settings.Default.filenameFormat;
var maxParallelTasks = Environment.ProcessorCount;
var taskCount = Properties.Settings.Default.parallelExportCount;
parallelExportUpDown.Maximum = maxParallelTasks;
parallelExportUpDown.Value = taskCount <= 0 ? maxParallelTasks : Math.Min(taskCount, maxParallelTasks);
parallelExportMaxLabel.Text += maxParallelTasks;
parallelExportCheckBox.Checked = Properties.Settings.Default.parallelExport;
}
private void OKbutton_Click(object sender, EventArgs e)
@@ -43,15 +47,10 @@ namespace AssetStudioGUI
Properties.Settings.Default.assetGroupOption = assetGroupOptions.SelectedIndex;
Properties.Settings.Default.restoreExtensionName = restoreExtensionName.Checked;
Properties.Settings.Default.convertTexture = converttexture.Checked;
Properties.Settings.Default.exportSpriteWithMask = exportSpriteWithAlphaMask.Checked;
Properties.Settings.Default.convertAudio = convertAudio.Checked;
foreach (Control c in panel1.Controls)
{
if (((RadioButton)c).Checked)
{
Properties.Settings.Default.convertType = (ImageFormat)Enum.Parse(typeof(ImageFormat), c.Text);
break;
}
}
var checkedImageType = (RadioButton)panel1.Controls.Cast<Control>().First(x => ((RadioButton)x).Checked);
Properties.Settings.Default.convertType = (ImageFormat)Enum.Parse(typeof(ImageFormat), checkedImageType.Text);
Properties.Settings.Default.openAfterExport = openAfterExport.Checked;
Properties.Settings.Default.eulerFilter = eulerFilter.Checked;
Properties.Settings.Default.filterPrecision = filterPrecision.Value;
@@ -65,6 +64,12 @@ namespace AssetStudioGUI
Properties.Settings.Default.scaleFactor = scaleFactor.Value;
Properties.Settings.Default.fbxVersion = fbxVersion.SelectedIndex;
Properties.Settings.Default.fbxFormat = fbxFormat.SelectedIndex;
var checkedMotionMode = (RadioButton)l2dMotionExportMethodPanel.Controls.Cast<Control>().First(x => ((RadioButton)x).Checked);
Properties.Settings.Default.l2dMotionMode = (CubismLive2DExtractor.Live2DMotionMode)Enum.Parse(typeof(CubismLive2DExtractor.Live2DMotionMode), checkedMotionMode.AccessibleName);
Properties.Settings.Default.l2dForceBezier = l2dForceBezierCheckBox.Checked;
Properties.Settings.Default.filenameFormat = filenameFormatComboBox.SelectedIndex;
Properties.Settings.Default.parallelExport = parallelExportCheckBox.Checked;
Properties.Settings.Default.parallelExportCount = (int)parallelExportUpDown.Value;
Properties.Settings.Default.Save();
DialogResult = DialogResult.OK;
Close();
@@ -76,5 +81,9 @@ namespace AssetStudioGUI
Close();
}
private void parallelExportCheckBox_CheckedChanged(object sender, EventArgs e)
{
parallelExportUpDown.Enabled = parallelExportCheckBox.Checked;
}
}
}

View File

@@ -117,7 +117,13 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="exportUvsTooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<metadata name="optionTooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="optionTooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="optionTooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View File

@@ -9,60 +9,6 @@ namespace AssetStudioGUI
{
internal static class Exporter
{
public static bool ExportTexture2D(AssetItem item, string exportPath)
{
var m_Texture2D = (Texture2D)item.Asset;
if (Properties.Settings.Default.convertTexture)
{
var type = Properties.Settings.Default.convertType;
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
return false;
var image = m_Texture2D.ConvertToImage(true);
if (image == null)
return false;
using (image)
{
using (var file = File.OpenWrite(exportFullPath))
{
image.WriteToStream(file, type);
}
return true;
}
}
else
{
if (!TryExportFile(exportPath, item, ".tex", out var exportFullPath))
return false;
File.WriteAllBytes(exportFullPath, m_Texture2D.image_data.GetData());
return true;
}
}
public static bool ExportAudioClip(AssetItem item, string exportPath)
{
var m_AudioClip = (AudioClip)item.Asset;
var m_AudioData = m_AudioClip.m_AudioData.GetData();
if (m_AudioData == null || m_AudioData.Length == 0)
return false;
var converter = new AudioClipConverter(m_AudioClip);
if (Properties.Settings.Default.convertAudio && converter.IsSupport)
{
if (!TryExportFile(exportPath, item, ".wav", out var exportFullPath))
return false;
var buffer = converter.ConvertToWav();
if (buffer == null)
return false;
File.WriteAllBytes(exportFullPath, buffer);
}
else
{
if (!TryExportFile(exportPath, item, converter.GetExtensionName(), out var exportFullPath))
return false;
File.WriteAllBytes(exportFullPath, m_AudioData);
}
return true;
}
public static bool ExportShader(AssetItem item, string exportPath)
{
if (!TryExportFile(exportPath, item, ".shader", out var exportFullPath))
@@ -75,13 +21,22 @@ namespace AssetStudioGUI
public static bool ExportTextAsset(AssetItem item, string exportPath)
{
var m_TextAsset = (TextAsset)(item.Asset);
var m_TextAsset = (TextAsset)item.Asset;
var extension = ".txt";
var assetExtension = Path.GetExtension(m_TextAsset.m_Name);
if (Properties.Settings.Default.restoreExtensionName)
{
if (!string.IsNullOrEmpty(item.Container))
if (!string.IsNullOrEmpty(assetExtension))
{
extension = Path.GetExtension(item.Container);
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))
@@ -228,49 +183,43 @@ namespace AssetStudioGUI
return true;
}
public static bool ExportSprite(AssetItem item, string exportPath)
{
var type = Properties.Settings.Default.convertType;
if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath))
return false;
var image = ((Sprite)item.Asset).GetImage();
if (image != null)
{
using (image)
{
using (var file = File.OpenWrite(exportFullPath))
{
image.WriteToStream(file, type);
}
return true;
}
}
return false;
}
public static bool ExportRawFile(AssetItem item, string exportPath)
{
if (!TryExportFile(exportPath, item, ".dat", out var exportFullPath))
if (!TryExportFile(exportPath, item, ".dat", out var exportFullPath, mode: "ExportRaw"))
return false;
File.WriteAllBytes(exportFullPath, item.Asset.GetRawData());
return true;
}
private static bool TryExportFile(string dir, AssetItem item, string extension, out string fullPath)
private static bool TryExportFile(string dir, AssetItem item, string extension, out string fullPath, string mode = "Export")
{
var fileName = FixFileName(item.Text);
var filenameFormatIndex = Properties.Settings.Default.filenameFormat;
switch (filenameFormatIndex)
{
case 1: //assetName@pathID
fileName = $"{fileName} @{item.m_PathID}";
break;
case 2: //pathID
fileName = item.m_PathID.ToString();
break;
}
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))
if (filenameFormatIndex == 0) //assetName
{
Directory.CreateDirectory(dir);
return true;
fullPath = Path.Combine(dir, fileName + item.UniqueID + extension);
if (!File.Exists(fullPath))
{
Directory.CreateDirectory(dir);
return true;
}
}
Logger.Warning($"{mode} failed. File \"{fullPath.Color(ColorConsole.BrightYellow)}\" already exist");
return false;
}
@@ -327,7 +276,7 @@ namespace AssetStudioGUI
public static bool ExportDumpFile(AssetItem item, string exportPath)
{
if (!TryExportFile(exportPath, item, ".txt", out var exportFullPath))
if (!TryExportFile(exportPath, item, ".txt", out var exportFullPath, mode: "Dump"))
return false;
var str = item.Asset.Dump();
if (str == null && item.Asset is MonoBehaviour m_MonoBehaviour)
@@ -335,6 +284,10 @@ namespace AssetStudioGUI
var m_Type = Studio.MonoBehaviourToTypeTree(m_MonoBehaviour);
str = m_MonoBehaviour.Dump(m_Type);
}
if (string.IsNullOrEmpty(str))
{
str = item.Asset.DumpObject();
}
if (str != null)
{
File.WriteAllText(exportFullPath, str);
@@ -348,9 +301,11 @@ namespace AssetStudioGUI
switch (item.Type)
{
case ClassIDType.Texture2D:
return ExportTexture2D(item, exportPath);
case ClassIDType.Texture2DArrayImage:
case ClassIDType.Texture2DArray:
case ClassIDType.AudioClip:
return ExportAudioClip(item, exportPath);
case ClassIDType.Sprite:
throw new System.NotImplementedException();
case ClassIDType.Shader:
return ExportShader(item, exportPath);
case ClassIDType.TextAsset:
@@ -365,8 +320,6 @@ namespace AssetStudioGUI
return ExportVideoClip(item, exportPath);
case ClassIDType.MovieTexture:
return ExportMovieTexture(item, exportPath);
case ClassIDType.Sprite:
return ExportSprite(item, exportPath);
case ClassIDType.Animator:
return ExportAnimator(item, exportPath);
case ClassIDType.AnimationClip:
@@ -378,8 +331,9 @@ namespace AssetStudioGUI
public static string FixFileName(string str)
{
if (str.Length >= 260) return Path.GetRandomFileName();
return Path.GetInvalidFileNameChars().Aggregate(str, (current, c) => current.Replace(c, '_'));
return str.Length >= 260
? Path.GetRandomFileName()
: Path.GetInvalidFileNameChars().Aggregate(str, (current, c) => current.Replace(c, '_'));
}
}
}

View File

@@ -1,34 +1,188 @@
using AssetStudio;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AssetStudioGUI
{
class GUILogger : ILogger
{
public bool ShowErrorMessage = true;
public static bool ShowDebugMessage = false;
private bool isFileLoggerRunning = false;
private string loggerInitString;
private string fileLogName;
private string fileLogPath;
private Action<string> action;
private CancellationTokenSource tokenSource;
private BlockingCollection<string> consoleLogMessageCollection = new BlockingCollection<string>();
private BlockingCollection<string> fileLogMessageCollection = new BlockingCollection<string>();
private bool _useFileLogger = false;
public bool UseFileLogger
{
get => _useFileLogger;
set
{
_useFileLogger = value;
if (_useFileLogger && !isFileLoggerRunning)
{
var appAssembly = typeof(Program).Assembly.GetName();
fileLogName = $"{appAssembly.Name}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log";
fileLogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileLogName);
tokenSource = new CancellationTokenSource();
isFileLoggerRunning = true;
ConcurrentFileWriter(tokenSource.Token);
LogToFile(LoggerEvent.Verbose, $"# {loggerInitString} - Logger launched #");
}
else if (!_useFileLogger && isFileLoggerRunning)
{
LogToFile(LoggerEvent.Verbose, "# Logger closed #");
isFileLoggerRunning = false;
tokenSource.Cancel();
tokenSource.Dispose();
}
}
}
public GUILogger(Action<string> action)
{
this.action = action;
var appAssembly = typeof(Program).Assembly.GetName();
var arch = Environment.Is64BitProcess ? "x64" : "x32";
var frameworkName = AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName;
loggerInitString = $"{appAssembly.Name} v{appAssembly.Version} [{arch}] [{frameworkName}]";
try
{
Console.Title = $"Console Logger - {appAssembly.Name} v{appAssembly.Version}";
Console.OutputEncoding = System.Text.Encoding.UTF8;
}
catch
{
// ignored
}
ConcurrentConsoleWriter();
Console.WriteLine($"# {loggerInitString}");
}
public void Log(LoggerEvent loggerEvent, string message)
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 toConsole)
{
message = message.TrimEnd();
var multiLine = message.Contains("\n");
string formattedMessage;
if (toConsole)
{
var colorLogLevel = ColorLogLevel(logMsgLevel);
formattedMessage = $"{colorLogLevel} {message}";
if (multiLine)
{
formattedMessage = formattedMessage.Replace("\n", $"\n{colorLogLevel} ") + $"\n{colorLogLevel}";
}
}
else
{
var curTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
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} | ") + $"\n{curTime} | {logLevel} |";
}
}
return formattedMessage;
}
private void ConcurrentFileWriter(CancellationToken token)
{
Task.Run(() =>
{
using (var sw = new StreamWriter(fileLogPath, append: true, System.Text.Encoding.UTF8))
{
sw.AutoFlush = true;
foreach (var msg in fileLogMessageCollection.GetConsumingEnumerable())
{
sw.WriteLine(msg);
if (token.IsCancellationRequested)
break;
}
}
}, token);
}
private void ConcurrentConsoleWriter()
{
Task.Run(() =>
{
foreach (var msg in consoleLogMessageCollection.GetConsumingEnumerable())
{
Console.WriteLine(msg);
}
});
}
private void LogToFile(LoggerEvent logMsgLevel, string message)
{
fileLogMessageCollection.Add(FormatMessage(logMsgLevel, message, toConsole: false));
}
private void LogToConsole(LoggerEvent logMsgLevel, string message)
{
consoleLogMessageCollection.Add(FormatMessage(logMsgLevel, message, toConsole: true));
}
public void Log(LoggerEvent loggerEvent, string message, bool ignoreLevel)
{
//File logger
if (_useFileLogger)
{
LogToFile(loggerEvent, message);
}
//Console logger
if (!ShowDebugMessage && loggerEvent == LoggerEvent.Debug)
return;
LogToConsole(loggerEvent, message);
//GUI logger
switch (loggerEvent)
{
case LoggerEvent.Error:
if (ShowErrorMessage)
{
MessageBox.Show(message);
}
MessageBox.Show(message, "Error");
break;
case LoggerEvent.Warning:
action("Some warnings occurred. See Console Logger for details.");
break;
case LoggerEvent.Debug:
break;
default:
action(message);
break;
}
}
}
}

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