Compare commits

...

15 Commits

12 changed files with 885 additions and 251 deletions

View File

@ -1,5 +1,17 @@
# Changelog # Changelog
## [0.4.1]
### Fixed
- Improved material parsing, big thanks to [bevity](https://github.com/gamedolphin/bevity).
## [0.4.0]
### Added
- New flag `--get_materials_from_prefabs` for updating model textures based on prefabs and materials.
## [0.3.0] ## [0.3.0]
### Added ### Added

553
Cargo.lock generated
View File

@ -1,84 +1,88 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 4
[[package]] [[package]]
name = "adler" name = "adler2"
version = "1.0.2" version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]] [[package]]
name = "ahash" name = "aho-corasick"
version = "0.8.6" version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [ dependencies = [
"cfg-if", "memchr",
"once_cell",
"version_check",
"zerocopy",
] ]
[[package]]
name = "allocator-api2"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.5" version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"anstyle-parse", "anstyle-parse",
"anstyle-query", "anstyle-query",
"anstyle-wincon", "anstyle-wincon",
"colorchoice", "colorchoice",
"is_terminal_polyfill",
"utf8parse", "utf8parse",
] ]
[[package]] [[package]]
name = "anstyle" name = "anstyle"
version = "1.0.4" version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]] [[package]]
name = "anstyle-parse" name = "anstyle-parse"
version = "0.2.3" version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [ dependencies = [
"utf8parse", "utf8parse",
] ]
[[package]] [[package]]
name = "anstyle-query" name = "anstyle-query"
version = "1.0.2" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]] [[package]]
name = "anstyle-wincon" name = "anstyle-wincon"
version = "3.0.2" version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"windows-sys", "windows-sys",
] ]
[[package]] [[package]]
name = "autocfg" name = "anyhow"
version = "1.1.0" version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
@ -88,9 +92,27 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.4.1" version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bytemuck"
version = "1.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "byteorder-lite"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -100,9 +122,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.4.11" version = "4.5.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -110,9 +132,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.4.11" version = "4.5.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -122,9 +144,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.4.7" version = "4.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@ -134,182 +156,301 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.6.0" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.0" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.3.2" version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]] [[package]]
name = "crossbeam-deque" name = "crossbeam-deque"
version = "0.8.4" version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [ dependencies = [
"cfg-if",
"crossbeam-epoch", "crossbeam-epoch",
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]] [[package]]
name = "crossbeam-epoch" name = "crossbeam-epoch"
version = "0.9.16" version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [ dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils", "crossbeam-utils",
"memoffset",
] ]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.17" version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "either" name = "either"
version = "1.9.0" version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.8" version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys", "windows-sys",
] ]
[[package]] [[package]]
name = "filetime" name = "fdeflate"
version = "0.2.23" version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
dependencies = [
"simd-adler32",
]
[[package]]
name = "filetime"
version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"redox_syscall", "libredox",
"windows-sys", "windows-sys",
] ]
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.28" version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
dependencies = [ dependencies = [
"crc32fast", "crc32fast",
"miniz_oxide", "miniz_oxide",
] ]
[[package]] [[package]]
name = "hashbrown" name = "gltf"
version = "0.14.3" version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" checksum = "e3ce1918195723ce6ac74e80542c5a96a40c2b26162c1957a5cd70799b8cacf7"
dependencies = [ dependencies = [
"ahash", "base64",
"allocator-api2", "byteorder",
"rayon", "gltf-json",
"image",
"lazy_static",
"serde_json",
"urlencoding",
] ]
[[package]] [[package]]
name = "heck" name = "gltf-derive"
version = "0.4.1" version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" checksum = "14070e711538afba5d6c807edb74bcb84e5dbb9211a3bf5dea0dfab5b24f4c51"
dependencies = [
"inflections",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "gltf-json"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6176f9d60a7eab0a877e8e96548605dedbde9190a7ae1e80bbcc1c9af03ab14"
dependencies = [
"gltf-derive",
"serde",
"serde_derive",
"serde_json",
]
[[package]]
name = "hashbrown"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "image"
version = "0.25.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b"
dependencies = [
"bytemuck",
"byteorder-lite",
"num-traits",
"png",
"zune-core",
"zune-jpeg",
]
[[package]]
name = "indexmap"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "inflections"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itoa"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.151" version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.6.0",
"libc",
"redox_syscall",
]
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.4.12" version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]] [[package]]
name = "lwa_unity_unpack" name = "lwa_unity_unpack"
version = "0.2.1" version = "0.4.1"
dependencies = [ dependencies = [
"anyhow",
"clap", "clap",
"flate2", "flate2",
"hashbrown", "gltf",
"rayon", "rayon",
"regex",
"serde",
"serde_yaml",
"tar", "tar",
] ]
[[package]] [[package]]
name = "memoffset" name = "memchr"
version = "0.9.0" version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [ dependencies = [
"autocfg", "autocfg",
] ]
[[package]] [[package]]
name = "miniz_oxide" name = "png"
version = "0.7.1" version = "0.17.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
dependencies = [ dependencies = [
"adler", "bitflags 1.3.2",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
] ]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.70" version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.33" version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]] [[package]]
name = "rayon" name = "rayon"
version = "1.8.0" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [ dependencies = [
"either", "either",
"rayon-core", "rayon-core",
@ -317,9 +458,9 @@ dependencies = [
[[package]] [[package]]
name = "rayon-core" name = "rayon-core"
version = "1.12.0" version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [ dependencies = [
"crossbeam-deque", "crossbeam-deque",
"crossbeam-utils", "crossbeam-utils",
@ -327,20 +468,49 @@ dependencies = [
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.4.1" version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 2.6.0",
] ]
[[package]] [[package]]
name = "rustix" name = "regex"
version = "0.38.28" version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [ dependencies = [
"bitflags 2.4.1", "aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "rustix"
version = "0.38.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
dependencies = [
"bitflags 2.6.0",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
@ -348,16 +518,73 @@ dependencies = [
] ]
[[package]] [[package]]
name = "strsim" name = "ryu"
version = "0.10.0" version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "serde"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.134"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.41" version = "2.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -366,9 +593,9 @@ dependencies = [
[[package]] [[package]]
name = "tar" name = "tar"
version = "0.4.40" version = "0.4.43"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6"
dependencies = [ dependencies = [
"filetime", "filetime",
"libc", "libc",
@ -377,40 +604,47 @@ dependencies = [
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.12" version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "urlencoding"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.1" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.52.0" version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [ dependencies = [
"windows-targets", "windows-targets",
] ]
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.52.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm", "windows_aarch64_gnullvm",
"windows_aarch64_msvc", "windows_aarch64_msvc",
"windows_i686_gnu", "windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc", "windows_i686_msvc",
"windows_x86_64_gnu", "windows_x86_64_gnu",
"windows_x86_64_gnullvm", "windows_x86_64_gnullvm",
@ -419,51 +653,57 @@ dependencies = [
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.52.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.52.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.52.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.52.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.52.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.52.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.52.0" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]] [[package]]
name = "xattr" name = "xattr"
version = "1.1.3" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7dae5072fe1f8db8f8d29059189ac175196e410e40ba42d5d4684ae2f750995" checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909"
dependencies = [ dependencies = [
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
@ -471,21 +711,16 @@ dependencies = [
] ]
[[package]] [[package]]
name = "zerocopy" name = "zune-core"
version = "0.7.32" version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
dependencies = [
"zerocopy-derive",
]
[[package]] [[package]]
name = "zerocopy-derive" name = "zune-jpeg"
version = "0.7.32" version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028"
dependencies = [ dependencies = [
"proc-macro2", "zune-core",
"quote",
"syn",
] ]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "lwa_unity_unpack" name = "lwa_unity_unpack"
version = "0.3.0" version = "0.4.1"
edition = "2021" edition = "2021"
repository = "https://github.com/Leinnan/lwa_unity_unpack" repository = "https://github.com/Leinnan/lwa_unity_unpack"
homepage = "https://github.com/Leinnan/lwa_unity_unpack" homepage = "https://github.com/Leinnan/lwa_unity_unpack"
@ -20,8 +20,12 @@ lto = true
opt-level = 2 opt-level = 2
[dependencies] [dependencies]
clap = { version = "4.4", features = ["derive"] } clap = { version = "4.5", features = ["derive"] }
flate2 = "1.0" flate2 = "1"
hashbrown = { version ="0.14.3", features = ["ahash","allocator-api2","inline-more","rayon"] } gltf = "1"
rayon = "1.8.0" rayon = "1"
regex = "1"
tar = "0.4" tar = "0.4"
serde = { version = "1", features = ["derive"] }
serde_yaml = "0.9"
anyhow = "1"

View File

@ -16,6 +16,8 @@ Options:
-i, --input <INPUT> .unitypackage file to extract -i, --input <INPUT> .unitypackage file to extract
-o, --output <OUTPUT> target directory -o, --output <OUTPUT> target directory
-f, --fbx-to-gltf <FBX_TO_GLTF> optional- path to the tool that will auto convert fbx files to gltf during unpacking -f, --fbx-to-gltf <FBX_TO_GLTF> optional- path to the tool that will auto convert fbx files to gltf during unpacking
--get-materials-from-prefabs
checks if material base texture in prefabs differ from the one specified in fbx model that is converted to GLTF and overrides it with the one from prefab and copy texture to models folder
--ignore-extensions <IGNORE_EXTENSIONS> --ignore-extensions <IGNORE_EXTENSIONS>
optional- extensions that will be ignored during unpacking optional- extensions that will be ignored during unpacking
--copy-meta-files --copy-meta-files

View File

@ -13,9 +13,14 @@ pub struct Args {
pub output: PathBuf, pub output: PathBuf,
/// optional- path to the tool that will auto convert fbx files to gltf during unpacking /// optional- path to the tool that will auto convert fbx files to gltf during unpacking
#[arg(short, long)] #[arg(long)]
pub fbx_to_gltf: Option<PathBuf>, pub fbx_to_gltf: Option<PathBuf>,
/// checks if material base texture in prefabs differ from the one specified in fbx model
/// that is converted to GLTF and overrides it with the one from prefab and copy texture to models folder
#[arg(long, default_value = "false", default_missing_value = "true")]
pub get_materials_from_prefabs: bool,
/// optional- extensions that will be ignored during unpacking /// optional- extensions that will be ignored during unpacking
#[arg(long, action = clap::ArgAction::Append)] #[arg(long, action = clap::ArgAction::Append)]
pub ignore_extensions: Option<Vec<String>>, pub ignore_extensions: Option<Vec<String>>,
@ -24,3 +29,34 @@ pub struct Args {
#[arg(long, default_value = "false", default_missing_value = "true")] #[arg(long, default_value = "false", default_missing_value = "true")]
pub copy_meta_files: bool, pub copy_meta_files: bool,
} }
impl Args {
pub fn check(&self) {
if let Some(path) = &self.fbx_to_gltf {
assert!(
is_executable(&path),
"fbx_to_gltf require a path to executable"
)
}
}
}
#[cfg(target_os = "windows")]
fn is_executable(path: &PathBuf) -> bool {
if let Some(extension) = path.extension() {
extension.to_str().unwrap().ends_with("exe")
} else {
false
}
}
#[cfg(not(target_os = "windows"))]
fn is_executable(path: &PathBuf) -> bool {
use std::os::unix::fs::PermissionsExt;
if let Ok(metadata) = std::fs::metadata(path) {
let permissions = metadata.permissions();
permissions.mode() & 0o111 != 0
} else {
false
}
}

View File

@ -1,26 +1,75 @@
use crate::primitives::materials::read_single_material;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fs; use std::fs;
use std::fs::DirEntry; use std::fs::DirEntry;
use std::fs::File; use std::fs::File;
use std::io::BufRead; use std::io::BufRead;
use std::io::BufReader; use std::io::BufReader;
use std::path::{Path, PathBuf}; use std::path::Path;
#[derive(Clone)] #[derive(Clone)]
pub struct Asset { pub struct Asset {
pub extension: Option<String>, pub extension: Option<String>,
pub hash: String, pub guid: String,
pub path_name: String, pub path: String,
pub has_meta: bool, pub has_meta: bool,
pub asset_type: AssetType,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub enum AssetType {
FbxModel,
Material,
Prefab,
Scene,
Other(String),
} }
impl Asset { impl Asset {
pub fn from_path(entry: &DirEntry) -> Option<Asset> { pub fn try_get_mat_texture_guid(&self) -> Option<String> {
match &self.asset_type {
AssetType::Material => {}
_ => return None,
}
let content = fs::read_to_string(&self.path).unwrap();
let material = read_single_material(&content);
if let Ok(mat) = material {
return mat
.properties
.tex_envs
.iter()
.find_map(|tex| tex.get("_MainTex"))
.and_then(|t| t.texture.guid.clone());
}
None
}
pub fn prepare_directory(&self) {
println!("{}: {:?}", self.guid, self.path);
let base_path = Path::new(&self.path);
let result_dir = base_path.parent();
if result_dir.is_none() {
eprintln!("{} is none", &self.path);
}
let result_dir = result_dir.unwrap();
if !result_dir.exists() {
let result = fs::create_dir_all(result_dir);
if result.is_err() {
eprintln!(
"Error {}: {}",
result_dir.to_str().unwrap(),
result.err().unwrap()
);
}
}
}
pub fn from_path(entry: &DirEntry, output_dir: &Path) -> Option<Asset> {
let root_file = entry.path(); let root_file = entry.path();
if !root_file.is_dir() { if !root_file.is_dir() {
return None; return None;
} }
let asset = entry.file_name().into_string().unwrap(); let guid = entry.file_name().into_string().unwrap();
let mut real_path = String::new(); let mut real_path = String::new();
let mut extension = None; let mut extension = None;
let mut has_asset = false; let mut has_asset = false;
@ -36,7 +85,7 @@ impl Asset {
let line = buf_reader.lines().next(); let line = buf_reader.lines().next();
match line { match line {
Some(Ok(path)) => { Some(Ok(path)) => {
real_path = path; real_path = output_dir.join(path).to_str().unwrap().to_string();
if let Some(e) = if let Some(e) =
Path::new(&real_path).extension().and_then(OsStr::to_str) Path::new(&real_path).extension().and_then(OsStr::to_str)
{ {
@ -52,11 +101,22 @@ impl Asset {
} }
} }
if has_asset { if has_asset {
let asset_type = match &extension {
Some(str) => match str.as_str() {
"fbx" => AssetType::FbxModel,
"prefab" => AssetType::Prefab,
"unity" => AssetType::Scene,
"mat" => AssetType::Material,
_ => AssetType::Other(str.clone()),
},
_ => AssetType::Other(String::new()),
};
Some(Asset { Some(Asset {
extension, extension,
hash: asset, guid,
path_name: real_path, path: real_path,
has_meta, has_meta,
asset_type,
}) })
} else { } else {
None None

View File

@ -1,13 +1,19 @@
mod args; mod args;
pub mod asset; pub mod asset;
pub mod primitives;
mod unpacker; mod unpacker;
mod yaml_helpers;
use clap::Parser; use clap::Parser;
fn main() { fn main() {
let args = crate::args::Args::parse(); let args = crate::args::Args::parse();
let unpacker = crate::unpacker::Unpacker { args }; let mut unpacker = crate::unpacker::Unpacker {
args,
assets: vec![],
};
unpacker.prepare_environment(); unpacker.prepare_environment();
unpacker.extract();
unpacker.process_data(); unpacker.process_data();
unpacker.update_gltf_materials();
} }

101
src/primitives/materials.rs Normal file
View File

@ -0,0 +1,101 @@
use crate::primitives::reference::FileReference;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
// use bevy::prelude::*;
use crate::yaml_helpers::parse_unity_yaml;
use anyhow::{bail, Context, Result};
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct UnityMaterial {
#[serde(alias = "m_Name")]
pub name: String,
#[serde(alias = "m_Shader")]
pub shader: FileReference,
#[serde(alias = "m_SavedProperties")]
pub properties: SavedProperties,
#[serde(default, alias = "stringTagMap")]
pub string_tags: HashMap<String, String>,
}
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct SavedProperties {
#[serde(alias = "serializedVersion")]
pub serialized_version: u64,
#[serde(alias = "m_TexEnvs")]
pub tex_envs: Vec<HashMap<String, TextureInfo>>,
#[serde(alias = "m_Floats")]
pub floats: Vec<HashMap<String, f32>>,
#[serde(alias = "m_Colors")]
pub colors: Vec<HashMap<String, UnityColor>>,
}
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct TextureInfo {
#[serde(alias = "m_Texture")]
pub texture: FileReference,
#[serde(alias = "m_Scale")]
pub scale: UnityVector2,
#[serde(alias = "m_Offset")]
pub offset: UnityVector2,
}
#[derive(Serialize, Deserialize, Debug, Default, Copy, Clone)]
pub struct UnityVector2 {
pub x: f32,
pub y: f32,
}
#[derive(Serialize, Deserialize, Debug, Default, Copy, Clone)]
pub struct UnityColor {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
// impl From<UnityColor> for Color {
// fn from(value: UnityColor) -> Self {
// Color::Rgba {
// red: value.r,
// green: value.g,
// blue: value.b,
// alpha: value.a,
// }
// }
// }
//
// impl From<&UnityColor> for Color {
// fn from(value: &UnityColor) -> Self {
// Color::Rgba {
// red: value.r,
// green: value.g,
// blue: value.b,
// alpha: value.a,
// }
// }
// }
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "object_type")]
enum MaterialContainer {
Material(UnityMaterial),
#[serde(other)]
DontCare,
}
pub fn read_single_material(contents: &str) -> Result<UnityMaterial> {
let map = parse_unity_yaml(contents)?;
let (_, output) = map.into_iter().next().context("0 items in material file")?;
let MaterialContainer::Material(mat) = output else {
bail!("invalid material file");
};
Ok(mat)
}

2
src/primitives/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod materials;
pub mod reference;

View File

@ -0,0 +1,53 @@
use serde::{
de::{self, Visitor},
Deserialize, Deserializer, Serialize,
};
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct FileReference {
#[serde(alias = "fileID")]
pub file_id: i64,
#[serde(default, deserialize_with = "deserialize_option_string_or_float")]
pub guid: Option<String>,
}
fn deserialize_option_string_or_float<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
struct StringOrFloat;
impl<'de> Visitor<'de> for StringOrFloat {
type Value = Option<String>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a string or a float")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
Ok(Some(value.to_owned()))
}
fn visit_string<E: de::Error>(self, value: String) -> Result<Self::Value, E> {
Ok(Some(value))
}
fn visit_f64<E: de::Error>(self, _: f64) -> Result<Self::Value, E> {
Ok(None)
}
fn visit_f32<E: de::Error>(self, _: f32) -> Result<Self::Value, E> {
Ok(None)
}
fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
Ok(None)
}
fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
Ok(None)
}
}
deserializer.deserialize_any(StringOrFloat)
}

View File

@ -1,12 +1,11 @@
use crate::asset::Asset; use crate::asset::{Asset, AssetType};
use flate2::read::GzDecoder; use flate2::read::GzDecoder;
use hashbrown::HashMap; use gltf::{json, Document};
use rayon::prelude::*; use rayon::prelude::*;
use std::ffi::OsStr; use std::borrow::Cow;
use std::fs::File; use std::fs::File;
use std::io::BufRead; use std::path::Path;
use std::io::BufReader;
use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
use std::sync::Arc; use std::sync::Arc;
@ -16,10 +15,12 @@ use tar::Archive;
#[derive(Clone)] #[derive(Clone)]
pub struct Unpacker { pub struct Unpacker {
pub args: crate::args::Args, pub args: crate::args::Args,
pub assets: Vec<Asset>,
} }
impl Unpacker { impl Unpacker {
pub fn prepare_environment(&self) { pub fn prepare_environment(&self) {
self.args.check();
let archive_path = Path::new(&self.args.input); let archive_path = Path::new(&self.args.input);
let output_dir = Path::new(&self.args.output); let output_dir = Path::new(&self.args.output);
let tmp_path = Path::new("./tmp_dir"); let tmp_path = Path::new("./tmp_dir");
@ -36,11 +37,11 @@ impl Unpacker {
} }
} }
pub fn process_data(&self) { pub fn extract(&mut self) {
let archive_path = Path::new(&self.args.input); let archive_path = Path::new(&self.args.input);
let output_dir = Path::new(&self.args.output);
let copy_meta_files = self.args.copy_meta_files;
let tmp_path = Path::new("./tmp_dir"); let tmp_path = Path::new("./tmp_dir");
let output_dir = Path::new(&self.args.output);
if let Err(e) = Unpacker::extract_archive(archive_path, tmp_path) { if let Err(e) = Unpacker::extract_archive(archive_path, tmp_path) {
println!("Failed to extract archive: {}", e); println!("Failed to extract archive: {}", e);
} }
@ -53,7 +54,7 @@ impl Unpacker {
.par_bridge() .par_bridge()
.for_each_with(sender, |s, entry| { .for_each_with(sender, |s, entry| {
let entry = entry.unwrap(); let entry = entry.unwrap();
let asset = crate::asset::Asset::from_path(&entry); let asset = crate::asset::Asset::from_path(&entry, output_dir);
if let Some(asset) = asset { if let Some(asset) = asset {
let extension = &asset.extension.clone().unwrap_or_default(); let extension = &asset.extension.clone().unwrap_or_default();
if !ignored_extensions.contains(extension) { if !ignored_extensions.contains(extension) {
@ -61,71 +62,155 @@ impl Unpacker {
} }
} }
}); });
self.assets = receiver.iter().collect();
let tmp_dir = Arc::new(tmp_path);
fs::create_dir(output_dir).unwrap();
let output_dir = Arc::new(output_dir);
let mapping: Vec<Asset> = receiver.iter().collect();
let mapping_arc = Arc::new(mapping);
mapping_arc.par_iter().for_each(|asset| {
let asset_hash = &asset.hash;
let path = Path::new(&asset.path_name);
let source_asset = Path::new(&*tmp_dir).join(asset_hash).join("asset");
let result_path = output_dir.join(path);
process_directory(asset_hash, &asset.path_name, &result_path);
if copy_meta_files && asset.has_meta {
let source_meta = Path::new(&*tmp_dir).join(asset_hash).join("asset.meta");
let mut meta_path = asset.path_name.clone();
meta_path.push_str(".meta");
let result_path = output_dir.join(meta_path);
fs::rename(source_meta, result_path).unwrap();
} }
check_source_asset_exists(&source_asset);
if self.args.fbx_to_gltf.is_some() { pub fn assets_of_type(&self, asset_type: AssetType) -> Vec<Asset> {
if let Some("fbx") = path.extension().and_then(OsStr::to_str) { self.assets
process_fbx_file( .clone()
&source_asset, .into_iter()
&result_path, .filter(|a| a.asset_type == asset_type)
&self.args.fbx_to_gltf.clone().unwrap(), .collect()
}
pub fn update_gltf_materials(&self) {
if self.args.fbx_to_gltf.is_none() || !self.args.get_materials_from_prefabs {
return;
}
let fbx_models = self.assets_of_type(AssetType::FbxModel);
let prefabs = self.assets_of_type(AssetType::Prefab);
let materials = self.assets_of_type(AssetType::Material);
println!(
"There are {} models, {} prefabs and {} materials",
fbx_models.len(),
prefabs.len(),
materials.len()
); );
prefabs.par_iter().for_each(|prefab| {
let path = Path::new(&prefab.path);
let prefab_content = fs::read_to_string(path).unwrap();
let matching_materials: Vec<Asset> = materials
.clone()
.into_iter()
.filter(|a| prefab_content.contains(&a.guid))
.collect();
let matching_models: Vec<Asset> = fbx_models
.clone()
.into_iter()
.filter(|a| prefab_content.contains(&a.guid))
.collect();
if matching_materials.len() != 1 || 1 != matching_models.len() {
return;
}
let material = matching_materials.first().unwrap();
let model: &Asset = matching_models.first().unwrap();
let texture_guid: Option<String> = material.try_get_mat_texture_guid();
let texture_asset: &Asset = match &texture_guid {
Some(guid) => self.assets.iter().find(|a| guid.eq(&a.guid)).unwrap(),
None => return,
};
// here we should read gltf file and replace material texture with Uri based on texture_asset
let model_path = Path::new(&model.path).with_extension("glb");
Self::update_material(&model_path, Path::new(&texture_asset.path));
});
}
fn align_to_multiple_of_four(n: &mut usize) {
*n = (*n + 3) & !3;
}
fn update_material(gltf_path: &Path, texture_asset: &Path) {
let file = fs::File::open(gltf_path).unwrap();
let reader = io::BufReader::new(file);
let mut gltf = gltf::Gltf::from_reader(reader).unwrap();
let mut json = gltf.document.into_json();
for image in json.images.iter_mut() {
let result = texture_asset
.file_name()
.unwrap()
.to_str()
.unwrap()
.to_string();
let required_file = gltf_path.with_file_name(&result);
if !required_file.exists() {
fs::copy(texture_asset, gltf_path.with_file_name(&result)).unwrap();
}
if let Some(old_path) = &image.uri {
if old_path.eq(&result) {
return; return;
} }
} }
println!(
process_non_fbx_file(&source_asset, &result_path); "Image{:?}: {:?} to be replaced with: {}",
}); image.name, image.uri, &result
);
fs::remove_dir_all(Path::new(&*tmp_dir)).unwrap(); image.uri = Some(result);
}
fn process_directory(asset_hash: &str, asset_path: &str, result_path: &Path) {
println!("{}: {:?}", asset_hash, asset_path); gltf.document = Document::from_json(json.clone()).unwrap();
let result_dir = result_path.parent().unwrap(); // Save the modified glTF
if !result_dir.exists() { let json_string = json::serialize::to_string(&json).expect("Serialization error");
fs::create_dir_all(result_dir).unwrap(); let mut json_offset = json_string.len();
} Self::align_to_multiple_of_four(&mut json_offset);
let blob = gltf.blob.clone().unwrap_or_default();
let buffer_length = blob.len();
let glb = gltf::binary::Glb {
header: gltf::binary::Header {
magic: *b"glTF",
version: 2,
// N.B., the size of binary glTF file is limited to range of `u32`.
length: (json_offset + buffer_length)
.try_into()
.expect("file size exceeds binary glTF limit"),
},
bin: Some(Cow::Owned(gltf.blob.unwrap_or_default())),
json: Cow::Owned(json_string.into_bytes()),
};
let writer = std::fs::File::create(gltf_path).expect("I/O error");
glb.to_writer(writer).expect("glTF binary output error");
}
pub fn process_data(&self) {
let output_dir = Path::new(&self.args.output);
let copy_meta_files = self.args.copy_meta_files;
let tmp_path = Path::new("./tmp_dir");
let tmp_dir = Arc::new(tmp_path);
fs::create_dir(output_dir).unwrap();
self.assets.par_iter().for_each(|asset| {
let asset_hash = &asset.guid;
let path = Path::new(&asset.path);
let source_asset = Path::new(&*tmp_dir).join(asset_hash).join("asset");
asset.prepare_directory();
if copy_meta_files && asset.has_meta {
let source_meta = Path::new(&*tmp_dir).join(asset_hash).join("asset.meta");
let mut meta_path = asset.path.clone();
meta_path.push_str(".meta");
fs::rename(source_meta, meta_path).unwrap();
} }
fn check_source_asset_exists(source_asset: &Path) {
if !source_asset.exists() { if !source_asset.exists() {
panic!("SOURCE ASSET DOES NOT EXIST: {}", source_asset.display()); panic!("SOURCE ASSET DOES NOT EXIST: {}", source_asset.display());
} }
if self.args.fbx_to_gltf.is_some() && asset.asset_type == AssetType::FbxModel {
self.process_fbx_file(&source_asset, path);
} else {
fs::rename(source_asset, path).unwrap();
}
});
fs::remove_dir_all(Path::new(&*tmp_dir)).unwrap();
} }
fn process_fbx_file(source_asset: &Path, result_path: &Path, tool: &PathBuf) { fn process_fbx_file(&self, source_asset: &Path, result_path: &Path) {
let tool = self.args.fbx_to_gltf.clone().unwrap();
let out_path = result_path.with_extension(""); let out_path = result_path.with_extension("");
println!( let _output = Command::new(tool)
"{:?}",
&[
"--input",
source_asset.to_str().unwrap(),
"--output",
out_path.to_str().unwrap()
]
);
let output = Command::new(tool)
.args([ .args([
"--input", "--input",
source_asset.to_str().unwrap(), source_asset.to_str().unwrap(),
@ -135,13 +220,10 @@ impl Unpacker {
]) ])
.output() .output()
.unwrap(); .unwrap();
let output_result = String::from_utf8_lossy(&output.stdout); println!(
println!("output: {}", output_result); "Fbx converted to GLTF: {}",
} out_path.with_extension("glb").to_str().unwrap()
);
fn process_non_fbx_file(source_asset: &Path, result_path: &Path) {
fs::rename(source_asset, result_path).unwrap();
}
} }
fn extract_archive(archive_path: &Path, extract_to: &Path) -> io::Result<()> { fn extract_archive(archive_path: &Path, extract_to: &Path) -> io::Result<()> {

41
src/yaml_helpers.rs Normal file
View File

@ -0,0 +1,41 @@
use anyhow::Result;
use serde::de::DeserializeOwned;
use std::collections::HashMap;
pub fn parse_unity_yaml<T: DeserializeOwned>(file: &str) -> Result<HashMap<i64, T>> {
let file = cleanup_unity_yaml(file)?;
let parse: HashMap<i64, T> = serde_yaml::from_str(&file)?;
Ok(parse)
}
fn cleanup_unity_yaml(yaml: &str) -> Result<String> {
let lines: Vec<String> = yaml
.lines()
.filter_map(|line| {
if line.starts_with("%YAML") || line.starts_with("%TAG") {
// unity specific headers. SKIP!
None
} else if line.starts_with("--- !u!") {
// unity object id declared on this line
// --- !u!104 &2 => 104 is object type and 2 is object id
let mut splits = line.split_whitespace();
let object_id: i64 = splits
.find(|&part| part.starts_with('&'))
.and_then(|num| num[1..].parse().ok())?;
Some(format!("{}:", object_id))
} else if line.starts_with(' ') {
Some(line.to_string())
} else {
Some(format!(" object_type: {}", line.replace(':', "")))
}
})
.collect();
let mut lines = lines.join("\n");
lines.push('\n'); // insert new line at the end
Ok(lines)
}