mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-22 05:59:15 +01:00
progress
This commit is contained in:
parent
19159b094e
commit
e83c6feeb0
402
sw/deployer/Cargo.lock
generated
402
sw/deployer/Cargo.lock
generated
@ -23,6 +23,12 @@ dependencies = [
|
||||
"mach",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.20"
|
||||
@ -58,6 +64,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@ -70,6 +82,18 @@ version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
@ -153,6 +177,12 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.0.0"
|
||||
@ -179,6 +209,55 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
version = "3.2.5"
|
||||
@ -233,6 +312,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.32"
|
||||
@ -263,6 +348,89 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exr"
|
||||
version = "1.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8af5ef47e2ed89d23d0ecbc1b681b30390069de70260937877514377fc24feb"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"flume",
|
||||
"half",
|
||||
"lebe",
|
||||
"miniz_oxide",
|
||||
"smallvec",
|
||||
"threadpool",
|
||||
"zune-inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.10.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"nanorand",
|
||||
"pin-project",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06"
|
||||
dependencies = [
|
||||
"color_quant",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
@ -278,6 +446,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.1"
|
||||
@ -308,6 +485,25 @@ dependencies = [
|
||||
"cxx-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.24.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"exr",
|
||||
"gif",
|
||||
"jpeg-decoder",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"png",
|
||||
"scoped_threadpool",
|
||||
"tiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.5"
|
||||
@ -330,6 +526,15 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
|
||||
dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.61"
|
||||
@ -345,6 +550,12 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.139"
|
||||
@ -386,6 +597,16 @@ version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
@ -419,6 +640,33 @@ version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanorand"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.26.2"
|
||||
@ -441,6 +689,17 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
@ -450,6 +709,16 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||
dependencies = [
|
||||
"hermit-abi 0.2.6",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
@ -468,12 +737,44 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384e52fd8fbd4cbe3c317e8216260c21a0f9134de108cea8a4dd4e7e152c472d"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crc32fast",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
@ -516,6 +817,28 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.7.1"
|
||||
@ -548,7 +871,7 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sc64loader"
|
||||
name = "sc64deployer"
|
||||
version = "2.12.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
@ -558,10 +881,23 @@ dependencies = [
|
||||
"crc32fast",
|
||||
"ctrlc",
|
||||
"encoding_rs",
|
||||
"image",
|
||||
"panic-message",
|
||||
"serialport",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped_threadpool"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.3"
|
||||
@ -584,6 +920,27 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14a5df39617d7c8558154693a1bb8157a4aab8179209540cc0b10e5dc24e0b18"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dccf47db1b41fa1573ed27ccf5e08e3ca771cb994f776668c5ebda893b248fc"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
@ -616,6 +973,26 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "threadpool"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
|
||||
dependencies = [
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"jpeg-decoder",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.45"
|
||||
@ -623,7 +1000,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@ -651,6 +1028,12 @@ version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.84"
|
||||
@ -705,6 +1088,12 @@ version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@ -801,3 +1190,12 @@ name = "windows_x86_64_msvc"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||
|
||||
[[package]]
|
||||
name = "zune-inflate"
|
||||
version = "0.2.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589245df6230839c305984dcc0a8385cc72af1fd223f360ffd5d65efa4216d40"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
@ -14,5 +14,6 @@ colored = "2.0.0"
|
||||
crc32fast = "1.3.2"
|
||||
ctrlc = "3.2.5"
|
||||
encoding_rs = "0.8.32"
|
||||
image = "0.24.5"
|
||||
panic-message = "0.3.0"
|
||||
serialport = { git = "https://github.com/serialport/serialport-rs", branch = "main" }
|
||||
|
@ -1,27 +1,206 @@
|
||||
use crate::sc64::DebugPacket;
|
||||
use crate::sc64;
|
||||
use chrono::Local;
|
||||
use std::{
|
||||
io::{ErrorKind, Read, Write},
|
||||
net::{TcpListener, TcpStream},
|
||||
sync::mpsc::{channel, Receiver, Sender},
|
||||
thread::{sleep, spawn},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
pub fn handle_debug_packet(debug_packet: DebugPacket) {
|
||||
let DebugPacket { datatype, data } = debug_packet;
|
||||
match datatype {
|
||||
0x01 => handle_datatype_text(&data),
|
||||
// 0x02 => handle_datatype_raw_binary(&data),
|
||||
// 0x03 => handle_datatype_header(&data),
|
||||
// 0x04 => handle_datatype_screenshot(&data),
|
||||
// 0xDB => handle_datatype_gdb(&data),
|
||||
_ => {}
|
||||
pub struct Handler {
|
||||
header: Option<Vec<u8>>,
|
||||
gdb_tx: Sender<Vec<u8>>,
|
||||
gdb_rx: Receiver<Vec<u8>>,
|
||||
}
|
||||
|
||||
enum DataType {
|
||||
Text,
|
||||
RawBinary,
|
||||
Header,
|
||||
Screenshot,
|
||||
GDB,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl From<u8> for DataType {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
0x01 => Self::Text,
|
||||
0x02 => Self::RawBinary,
|
||||
0x03 => Self::Header,
|
||||
0x04 => Self::Screenshot,
|
||||
0xDB => Self::GDB,
|
||||
_ => Self::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_datatype_text(data: &[u8]) {
|
||||
if let Ok(message) = std::str::from_utf8(data) {
|
||||
print!("{message}");
|
||||
impl From<DataType> for u8 {
|
||||
fn from(value: DataType) -> Self {
|
||||
match value {
|
||||
DataType::Text => 0x01,
|
||||
DataType::RawBinary => 0x02,
|
||||
DataType::Header => 0x03,
|
||||
DataType::Screenshot => 0x04,
|
||||
DataType::GDB => 0xDB,
|
||||
DataType::Unknown => 0xFF,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fn handle_datatype_raw_binary(data: &[u8]) {}
|
||||
impl Handler {
|
||||
pub fn handle_debug_packet(
|
||||
&mut self,
|
||||
debug_packet: sc64::DebugPacket,
|
||||
) -> Result<(), sc64::Error> {
|
||||
let sc64::DebugPacket { datatype, data } = debug_packet;
|
||||
match datatype.into() {
|
||||
DataType::Text => self.handle_datatype_text(&data),
|
||||
DataType::RawBinary => self.handle_datatype_raw_binary(&data),
|
||||
DataType::Header => self.handle_datatype_header(&data),
|
||||
DataType::Screenshot => self.handle_datatype_screenshot(&data),
|
||||
DataType::GDB => self.handle_datatype_gdb(&data),
|
||||
_ => {
|
||||
println!("Unknown debug packet datatype: 0x{datatype:02X}");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fn handle_datatype_header(data: &[u8]) {}
|
||||
fn handle_datatype_text(&self, data: &[u8]) -> Result<(), sc64::Error> {
|
||||
print!("{}", String::from_utf8_lossy(data));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// fn handle_datatype_screenshot(data: &[u8]) {}
|
||||
fn handle_datatype_raw_binary(&self, data: &[u8]) -> Result<(), sc64::Error> {
|
||||
let filename = &self.generate_filename("binaryout", "bin");
|
||||
let mut file = std::fs::File::create(filename)?;
|
||||
file.write_all(data)?;
|
||||
println!("Wrote {} bytes to {}", data.len(), filename);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// fn handle_datatype_gdb(data: &[u8]) {}
|
||||
fn handle_datatype_header(&mut self, data: &[u8]) -> Result<(), sc64::Error> {
|
||||
self.header = Some(data.to_vec());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_datatype_screenshot(&mut self, _data: &[u8]) -> Result<(), sc64::Error> {
|
||||
if let Some(header) = self.header.take() {
|
||||
// TODO: support screenshot datatype
|
||||
println!("Screenshot datatype not supported yet {:?}", header);
|
||||
} else {
|
||||
println!("Got screenshot packet without header data");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_datatype_gdb(&self, data: &[u8]) -> Result<(), sc64::Error> {
|
||||
self.gdb_tx.send(data.to_vec()).ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_filename(&self, prefix: &str, extension: &str) -> String {
|
||||
format!(
|
||||
"{prefix}-{}.{extension}",
|
||||
Local::now().format("%y%m%d%H%M%S.%f")
|
||||
)
|
||||
}
|
||||
|
||||
pub fn receive_gdb_packet(&self) -> Option<sc64::DebugPacket> {
|
||||
if let Some(data) = self.gdb_rx.try_recv().ok() {
|
||||
Some(sc64::DebugPacket {
|
||||
datatype: DataType::GDB.into(),
|
||||
data,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(gdb_port: Option<u16>) -> Result<Handler, sc64::Error> {
|
||||
let (gdb_tx, gdb_loop_rx) = channel::<Vec<u8>>();
|
||||
let (gdb_loop_tx, gdb_rx) = channel::<Vec<u8>>();
|
||||
|
||||
if let Some(port) = gdb_port {
|
||||
let listener = TcpListener::bind(format!("0.0.0.0:{port}"))
|
||||
.map_err(|_| sc64::Error::new("Couldn't open GDB TCP socket port"))?;
|
||||
listener.set_nonblocking(true).map_err(|_| {
|
||||
sc64::Error::new("Couldn't set GDB TCP socket listener as non-blocking")
|
||||
})?;
|
||||
spawn(move || gdb_loop(listener, gdb_loop_tx, gdb_loop_rx));
|
||||
}
|
||||
|
||||
Ok(Handler {
|
||||
header: None,
|
||||
gdb_tx,
|
||||
gdb_rx,
|
||||
})
|
||||
}
|
||||
|
||||
fn gdb_loop(listener: TcpListener, gdb_tx: Sender<Vec<u8>>, gdb_rx: Receiver<Vec<u8>>) {
|
||||
for tcp_stream in listener.incoming() {
|
||||
match tcp_stream {
|
||||
Ok(mut stream) => {
|
||||
handle_gdb_connection(&mut stream, &gdb_tx, &gdb_rx);
|
||||
}
|
||||
Err(error) => {
|
||||
if error.kind() == ErrorKind::WouldBlock {
|
||||
sleep(Duration::from_millis(1));
|
||||
} else {
|
||||
panic!("{error}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_gdb_connection(
|
||||
stream: &mut TcpStream,
|
||||
gdb_tx: &Sender<Vec<u8>>,
|
||||
gdb_rx: &Receiver<Vec<u8>>,
|
||||
) {
|
||||
const GDB_DATA_BUFFER: usize = 8 * 1024;
|
||||
|
||||
let mut buffer = vec![0u8; GDB_DATA_BUFFER];
|
||||
|
||||
let peer = stream.peer_addr().unwrap();
|
||||
|
||||
println!("[GDB]: New connection ({peer})");
|
||||
|
||||
loop {
|
||||
match stream.read(&mut buffer) {
|
||||
Ok(length) => {
|
||||
if length > 0 {
|
||||
gdb_tx.send(buffer[0..length].to_vec()).ok();
|
||||
continue;
|
||||
} else {
|
||||
println!("[GDB]: Connection closed ({peer})");
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() != ErrorKind::WouldBlock {
|
||||
println!("[GDB]: Connection closed ({peer}), read IO error: {e}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(data) = gdb_rx.try_recv() {
|
||||
match stream.write_all(&data) {
|
||||
Ok(()) => {
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
println!("[GDB]: Connection closed ({peer}), write IO error: {e}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(1));
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,23 @@
|
||||
mod debug;
|
||||
mod n64;
|
||||
mod sc64;
|
||||
pub mod sc64; // TODO: delete pub
|
||||
|
||||
use chrono::Local;
|
||||
use clap::{Args, Parser, Subcommand, ValueEnum};
|
||||
use clap_num::maybe_hex_range;
|
||||
use colored::Colorize;
|
||||
use debug::handle_debug_packet;
|
||||
use panic_message::panic_message;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{panic, process, thread};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, Read, Write},
|
||||
path::PathBuf,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
time::Duration,
|
||||
{panic, process, thread},
|
||||
};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
@ -167,6 +171,32 @@ enum SaveType {
|
||||
Flashram,
|
||||
}
|
||||
|
||||
impl From<n64::SaveType> for SaveType {
|
||||
fn from(value: n64::SaveType) -> Self {
|
||||
match value {
|
||||
n64::SaveType::None => Self::None,
|
||||
n64::SaveType::Eeprom4k => Self::Eeprom4k,
|
||||
n64::SaveType::Eeprom16k => Self::Eeprom16k,
|
||||
n64::SaveType::Sram => Self::Sram,
|
||||
n64::SaveType::SramBanked => Self::SramBanked,
|
||||
n64::SaveType::Flashram => Self::Flashram,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SaveType> for sc64::SaveType {
|
||||
fn from(value: SaveType) -> Self {
|
||||
match value {
|
||||
SaveType::None => Self::None,
|
||||
SaveType::Eeprom4k => Self::Eeprom4k,
|
||||
SaveType::Eeprom16k => Self::Eeprom16k,
|
||||
SaveType::Sram => Self::Sram,
|
||||
SaveType::SramBanked => Self::SramBanked,
|
||||
SaveType::Flashram => Self::Flashram,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, ValueEnum)]
|
||||
enum TvType {
|
||||
PAL,
|
||||
@ -174,6 +204,16 @@ enum TvType {
|
||||
MPAL,
|
||||
}
|
||||
|
||||
impl From<TvType> for sc64::TvType {
|
||||
fn from(value: TvType) -> Self {
|
||||
match value {
|
||||
TvType::PAL => Self::PAL,
|
||||
TvType::NTSC => Self::NTSC,
|
||||
TvType::MPAL => Self::MPAL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let cli = Cli::parse();
|
||||
|
||||
@ -188,28 +228,6 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
fn init_sc64(sn: Option<String>, check_firmware: bool) -> Result<sc64::SC64, sc64::Error> {
|
||||
let mut sc64 = sc64::new(sn)?;
|
||||
|
||||
if check_firmware {
|
||||
sc64.check_firmware_version()?;
|
||||
}
|
||||
|
||||
Ok(sc64)
|
||||
}
|
||||
|
||||
fn setup_exit_flag() -> Arc<AtomicBool> {
|
||||
let exit_flag = Arc::new(AtomicBool::new(false));
|
||||
let handler_exit_flag = exit_flag.clone();
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
handler_exit_flag.store(true, Ordering::Relaxed);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
exit_flag
|
||||
}
|
||||
|
||||
fn handle_command(command: &Commands, sn: Option<String>) {
|
||||
let result = match command {
|
||||
Commands::Upload(args) => handle_upload_command(sn, args),
|
||||
@ -232,62 +250,45 @@ fn handle_upload_command(sn: Option<String>, args: &UploadArgs) -> Result<(), sc
|
||||
|
||||
sc64.reset_state()?;
|
||||
|
||||
let rom_path = args.rom.to_str().unwrap();
|
||||
let (mut rom_file, rom_name, rom_length) = open_file(&args.rom)?;
|
||||
|
||||
print!(
|
||||
"Uploading ROM [{}]... ",
|
||||
args.rom.file_name().unwrap().to_str().unwrap()
|
||||
);
|
||||
std::io::stdout().flush().unwrap();
|
||||
sc64.upload_rom(rom_path, args.no_shadow)?;
|
||||
println!("done");
|
||||
log_wait(format!("Uploading ROM [{rom_name}]"), || {
|
||||
sc64.upload_rom(&mut rom_file, rom_length, args.no_shadow)
|
||||
})?;
|
||||
|
||||
// TODO: autodetect save
|
||||
|
||||
let args_save_type = args.save_type.as_ref().unwrap_or(&SaveType::None);
|
||||
let save_type = match args_save_type {
|
||||
SaveType::None => sc64::SaveType::None,
|
||||
SaveType::Eeprom4k => sc64::SaveType::Eeprom4k,
|
||||
SaveType::Eeprom16k => sc64::SaveType::Eeprom16k,
|
||||
SaveType::Sram => sc64::SaveType::Sram,
|
||||
SaveType::SramBanked => sc64::SaveType::SramBanked,
|
||||
SaveType::Flashram => sc64::SaveType::Flashram,
|
||||
let save: SaveType = if let Some(save_type) = args.save_type.clone() {
|
||||
save_type
|
||||
} else {
|
||||
n64::guess_save_type(&mut rom_file)?.into()
|
||||
};
|
||||
|
||||
let save_type: sc64::SaveType = save.into();
|
||||
println!("Save type set to [{save_type}]");
|
||||
sc64.set_save_type(save_type)?;
|
||||
|
||||
if args.save.is_some() {
|
||||
let save = args.save.as_ref().unwrap();
|
||||
let (mut save_file, save_name, save_length) = open_file(&args.save.as_ref().unwrap())?;
|
||||
|
||||
print!(
|
||||
"Uploading save [{}]... ",
|
||||
save.file_name().unwrap().to_str().unwrap()
|
||||
);
|
||||
std::io::stdout().flush().unwrap();
|
||||
sc64.upload_save(save.to_str().unwrap())?;
|
||||
println!("done");
|
||||
log_wait(format!("Uploading save [{save_name}]"), || {
|
||||
sc64.upload_save(&mut save_file, save_length)
|
||||
})?;
|
||||
}
|
||||
|
||||
println!("Save type set to [{args_save_type:?}]");
|
||||
|
||||
let boot_mode = if args.direct {
|
||||
sc64::BootMode::DirectRom
|
||||
} else {
|
||||
sc64::BootMode::Rom
|
||||
};
|
||||
|
||||
println!("Boot mode set to [{boot_mode}]");
|
||||
sc64.set_boot_mode(boot_mode)?;
|
||||
println!("Boot mode set to [{:?}]", boot_mode);
|
||||
|
||||
if let Some(tv) = args.tv.as_ref() {
|
||||
if let Some(tv) = args.tv.clone() {
|
||||
if args.direct {
|
||||
println!("TV type ignored because direct boot mode is enabled");
|
||||
println!("TV type ignored due to direct boot mode being enabled");
|
||||
} else {
|
||||
sc64.set_tv_type(match tv {
|
||||
TvType::PAL => sc64::TvType::PAL,
|
||||
TvType::NTSC => sc64::TvType::NTSC,
|
||||
TvType::MPAL => sc64::TvType::MPAL,
|
||||
})?;
|
||||
println!("TV type set to [{tv:?}]");
|
||||
let tv_type: sc64::TvType = tv.into();
|
||||
println!("TV type set to [{tv_type}]");
|
||||
sc64.set_tv_type(tv_type)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,73 +298,47 @@ fn handle_upload_command(sn: Option<String>, args: &UploadArgs) -> Result<(), sc
|
||||
}
|
||||
|
||||
fn handle_64dd_command(sn: Option<String>, args: &_64DDArgs) -> Result<(), sc64::Error> {
|
||||
let mut sc64 = init_sc64(sn, true)?;
|
||||
let _ = (sn, args);
|
||||
|
||||
// TODO: parse 64DD disk files
|
||||
// TODO: handle 64DD stuff
|
||||
|
||||
sc64.reset_state()?;
|
||||
|
||||
let ddipl_path = args.ddipl.to_str().unwrap();
|
||||
|
||||
print!(
|
||||
"Uploading DDIPL [{}]... ",
|
||||
args.ddipl.file_name().unwrap().to_str().unwrap()
|
||||
);
|
||||
std::io::stdout().flush().unwrap();
|
||||
sc64.upload_ddipl(ddipl_path)?;
|
||||
println!("done");
|
||||
|
||||
// TODO: upload other stuff
|
||||
|
||||
// TODO: set boot mode
|
||||
|
||||
sc64.calculate_cic_parameters()?;
|
||||
|
||||
let exit = setup_exit_flag();
|
||||
while exit.load(Ordering::Relaxed) {
|
||||
if let Some(data_packet) = sc64.receive_data_packet()? {
|
||||
match data_packet {
|
||||
sc64::DataPacket::Disk(_disk_packet) => {
|
||||
// TODO: handle 64DD packet
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
}
|
||||
println!("{}", "Sorry nothing".yellow());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_dump_command(sn: Option<String>, args: &DumpArgs) -> Result<(), sc64::Error> {
|
||||
let dump_path = &args.path;
|
||||
|
||||
let mut file = std::fs::File::create(dump_path)?;
|
||||
|
||||
let mut sc64 = init_sc64(sn, true)?;
|
||||
|
||||
print!(
|
||||
"Dumping from [0x{:08X}] length [0x{:X}] to [{}]... ",
|
||||
args.address,
|
||||
args.length,
|
||||
dump_path.file_name().unwrap().to_str().unwrap()
|
||||
);
|
||||
std::io::stdout().flush().unwrap();
|
||||
let data = sc64.dump_memory(args.address, args.length)?;
|
||||
let (mut dump_file, dump_name) = create_file(&args.path)?;
|
||||
|
||||
file.write(&data)?;
|
||||
let data = log_wait(
|
||||
format!(
|
||||
"Dumping from [0x{:08X}] length [0x{:X}] to [{dump_name}]",
|
||||
args.address, args.length
|
||||
),
|
||||
|| sc64.dump_memory(args.address, args.length),
|
||||
)?;
|
||||
|
||||
println!("done");
|
||||
dump_file.write(&data)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_debug_command(sn: Option<String>, args: &DebugArgs) -> Result<(), sc64::Error> {
|
||||
let mut debug_handler = debug::new(args.gdb)?;
|
||||
if let Some(port) = args.gdb {
|
||||
println!("GDB TCP socket listening at [0.0.0.0:{port}]");
|
||||
}
|
||||
|
||||
let mut sc64 = init_sc64(sn, true)?;
|
||||
|
||||
if args.isv.is_some() {
|
||||
sc64.configure_isviewer64(args.isv)?;
|
||||
sc64.configure_is_viewer_64(args.isv)?;
|
||||
println!(
|
||||
"IS-Viewer configured and listening at offset [0x{:08X}]",
|
||||
args.isv.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
println!("{}", "Debug mode started".bold());
|
||||
@ -372,10 +347,16 @@ fn handle_debug_command(sn: Option<String>, args: &DebugArgs) -> Result<(), sc64
|
||||
while !exit.load(Ordering::Relaxed) {
|
||||
if let Some(data_packet) = sc64.receive_data_packet()? {
|
||||
match data_packet {
|
||||
sc64::DataPacket::IsViewer(message) => print!("{}", message),
|
||||
sc64::DataPacket::Debug(debug_packet) => handle_debug_packet(debug_packet),
|
||||
sc64::DataPacket::IsViewer(message) => {
|
||||
print!("{message}")
|
||||
}
|
||||
sc64::DataPacket::Debug(debug_packet) => {
|
||||
debug_handler.handle_debug_packet(debug_packet)?
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else if let Some(gdb_packet) = debug_handler.receive_gdb_packet() {
|
||||
sc64.send_debug_packet(gdb_packet)?;
|
||||
} else {
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
@ -384,7 +365,7 @@ fn handle_debug_command(sn: Option<String>, args: &DebugArgs) -> Result<(), sc64
|
||||
println!("{}", "Debug mode ended".bold());
|
||||
|
||||
if args.isv.is_some() {
|
||||
sc64.configure_isviewer64(None)?;
|
||||
sc64.configure_is_viewer_64(None)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -398,24 +379,24 @@ fn handle_info_command(sn: Option<String>) -> Result<(), sc64::Error> {
|
||||
let datetime = state.datetime.format("%Y-%m-%d %H:%M:%S %Z");
|
||||
|
||||
println!("{}", "SC64 information and current state:".bold());
|
||||
println!(" Firmware version: {major}.{minor}");
|
||||
println!(" Firmware version: {}.{}", major, minor);
|
||||
println!(" RTC datetime: {}", datetime);
|
||||
println!(" LED blink enabled: {}", state.led_enable);
|
||||
println!(" Boot mode: {}", state.boot_mode);
|
||||
println!(" Save type: {}", state.save_type);
|
||||
println!(" CIC seed: {}", state.cic_seed);
|
||||
println!(" TV type: {}", state.tv_type);
|
||||
println!(" Bootloader switch: {}", state.bootloader_switch);
|
||||
println!(" ROM write enabled: {}", state.rom_write_enable);
|
||||
println!(" ROM shadow enabled: {}", state.rom_shadow_enable);
|
||||
println!(" ROM extended enabled: {}", state.rom_extended_enable);
|
||||
println!(" Boot mode: {:?}", state.boot_mode);
|
||||
println!(" Save type: {:?}", state.save_type);
|
||||
println!(" CIC seed: {:?}", state.cic_seed);
|
||||
println!(" TV type: {:?}", state.tv_type);
|
||||
println!(" 64DD mode: {:?}", state.dd_mode);
|
||||
println!(" 64DD SD card mode: {}", state.dd_sd_enable);
|
||||
println!(" 64DD drive type: {:?}", state.dd_drive_type);
|
||||
println!(" 64DD disk state: {:?}", state.dd_disk_state);
|
||||
println!(" Button mode: {:?}", state.button_mode);
|
||||
println!(" Button state: {}", state.button_state);
|
||||
println!(" ROM write: {}", state.rom_write_enable);
|
||||
println!(" ROM shadow: {}", state.rom_shadow_enable);
|
||||
println!(" ROM extended: {}", state.rom_extended_enable);
|
||||
println!(" IS-Viewer 64 offset: 0x{:08X}", state.isv_address);
|
||||
println!(" 64DD mode: {}", state.dd_mode);
|
||||
println!(" 64DD SD card mode: {}", state.dd_sd_enable);
|
||||
println!(" 64DD drive type: {}", state.dd_drive_type);
|
||||
println!(" 64DD disk state: {}", state.dd_disk_state);
|
||||
println!(" Button mode: {}", state.button_mode);
|
||||
println!(" Button state: {}", state.button_state);
|
||||
println!(" LED blink: {}", state.led_enable);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -456,56 +437,49 @@ fn handle_firmware_command(
|
||||
) -> Result<(), sc64::Error> {
|
||||
match command {
|
||||
FirmwareCommands::Info(args) => {
|
||||
let firmware_path = &args.firmware;
|
||||
let (mut firmware_file, _, firmware_length) = open_file(&args.firmware)?;
|
||||
|
||||
let mut file = std::fs::File::open(firmware_path)?;
|
||||
let length = file.metadata()?.len();
|
||||
let mut buffer = vec![0u8; length as usize];
|
||||
file.read_exact(&mut buffer)?;
|
||||
let mut firmware = vec![0u8; firmware_length as usize];
|
||||
firmware_file.read_exact(&mut firmware)?;
|
||||
|
||||
// TODO: print firmware metadata
|
||||
|
||||
println!("{}", "Sorry nothing".yellow());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
FirmwareCommands::Backup(args) => {
|
||||
let backup_path = &args.firmware;
|
||||
|
||||
let mut file = std::fs::File::create(backup_path)?;
|
||||
|
||||
let mut sc64 = init_sc64(sn, false)?;
|
||||
|
||||
print!(
|
||||
"Generating firmware backup, this might take a while [{}]... ",
|
||||
backup_path.file_name().unwrap().to_str().unwrap()
|
||||
);
|
||||
std::io::stdout().flush().unwrap();
|
||||
let data = sc64.backup_firmware()?;
|
||||
file.write(&data)?;
|
||||
println!("done");
|
||||
let (mut backup_file, backup_name) = create_file(&args.firmware)?;
|
||||
|
||||
let firmware = log_wait(
|
||||
format!("Generating firmware backup, this might take a while [{backup_name}]"),
|
||||
|| sc64.backup_firmware(),
|
||||
)?;
|
||||
|
||||
// TODO: print firmware metadata
|
||||
|
||||
backup_file.write(&firmware)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
FirmwareCommands::Update(args) => {
|
||||
let update_path = &args.firmware;
|
||||
let mut sc64 = init_sc64(sn, false)?;
|
||||
|
||||
let mut file = std::fs::File::open(update_path)?;
|
||||
let length = file.metadata()?.len();
|
||||
let mut buffer = vec![0u8; length as usize];
|
||||
file.read_exact(&mut buffer)?;
|
||||
let (mut update_file, update_name, update_length) = open_file(&args.firmware)?;
|
||||
|
||||
let mut fimware = vec![0u8; update_length as usize];
|
||||
update_file.read_exact(&mut fimware)?;
|
||||
|
||||
// TODO: print firmware metadata
|
||||
|
||||
let mut sc64 = init_sc64(sn, false)?;
|
||||
|
||||
print!(
|
||||
"Updating firmware, this might take a while [{}]... ",
|
||||
update_path.file_name().unwrap().to_str().unwrap()
|
||||
);
|
||||
std::io::stdout().flush().unwrap();
|
||||
sc64.update_firmware(&buffer)?;
|
||||
println!("done");
|
||||
log_wait(
|
||||
format!("Updating firmware, this might take a while [{update_name}]"),
|
||||
|| sc64.update_firmware(&fimware),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -522,3 +496,46 @@ fn handle_list_command() -> Result<(), sc64::Error> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_sc64(sn: Option<String>, check_firmware: bool) -> Result<sc64::SC64, sc64::Error> {
|
||||
let mut sc64 = sc64::new(sn)?;
|
||||
|
||||
if check_firmware {
|
||||
sc64.check_firmware_version()?;
|
||||
}
|
||||
|
||||
Ok(sc64)
|
||||
}
|
||||
|
||||
fn log_wait<F: FnOnce() -> Result<T, E>, T, E>(message: String, operation: F) -> Result<T, E> {
|
||||
print!("{}... ", message);
|
||||
io::stdout().flush().unwrap();
|
||||
let result = operation();
|
||||
println!("done");
|
||||
result
|
||||
}
|
||||
|
||||
fn open_file(path: &PathBuf) -> Result<(File, String, usize), sc64::Error> {
|
||||
let name: String = path.file_name().unwrap().to_string_lossy().to_string();
|
||||
let file = File::open(path)?;
|
||||
let length = file.metadata()?.len() as usize;
|
||||
Ok((file, name, length))
|
||||
}
|
||||
|
||||
fn create_file(path: &PathBuf) -> Result<(File, String), sc64::Error> {
|
||||
let name: String = path.file_name().unwrap().to_string_lossy().to_string();
|
||||
let file = File::create(path)?;
|
||||
Ok((file, name))
|
||||
}
|
||||
|
||||
fn setup_exit_flag() -> Arc<AtomicBool> {
|
||||
let exit_flag = Arc::new(AtomicBool::new(false));
|
||||
let handler_exit_flag = exit_flag.clone();
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
handler_exit_flag.store(true, Ordering::Relaxed);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
exit_flag
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
use std::io::{Error, Read, Seek};
|
||||
|
||||
pub enum SaveType {
|
||||
None,
|
||||
Eeprom4k,
|
||||
Eeprom16k,
|
||||
Sram,
|
||||
SramBanked,
|
||||
Flashram,
|
||||
}
|
||||
|
||||
pub fn guess_save_type<T: Read + Seek>(reader: &mut T) -> Result<SaveType, Error> {
|
||||
let mut header = vec![0u8; 0x40];
|
||||
|
||||
reader.rewind()?;
|
||||
reader.read_exact(&mut header)?;
|
||||
|
||||
let pi_config = &header[0..4];
|
||||
match pi_config {
|
||||
[0x37, 0x80, 0x40, 0x12] => {
|
||||
header.chunks_exact_mut(2).for_each(|c| c.swap(0, 1));
|
||||
}
|
||||
[0x40, 0x12, 0x37, 0x80] => {
|
||||
header.chunks_exact_mut(4).for_each(|c| {
|
||||
c.swap(0, 3);
|
||||
c.swap(1, 2);
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let rom_id = &header[0x3B..0x3E];
|
||||
// let region = header[0x3E];
|
||||
// let revision = header[0x3F];
|
||||
|
||||
// TODO: fix this mess
|
||||
|
||||
if let Some(save_type) = match rom_id {
|
||||
b"NTW" | b"NHF" | b"NOS" | b"NTC" | b"NER" | b"NAG" | b"NAB" | b"NS3" | b"NTN" | b"NBN"
|
||||
| b"NBK" | b"NFH" | b"NMU" | b"NBC" | b"NBH" | b"NHA" | b"NBM" | b"NBV" | b"NBD"
|
||||
| b"NCT" | b"NCH" | b"NCG" | b"NP2" | b"NXO" | b"NCU" | b"NCX" | b"NDY" | b"NDQ"
|
||||
| b"NDR" | b"NN6" | b"NDU" | b"NJM" | b"NFW" | b"NF2" | b"NKA" | b"NFG" | b"NGL"
|
||||
| b"NGV" | b"NGE" | b"NHP" | b"NPG" | b"NIJ" | b"NIC" | b"NFY" | b"NKI" | b"NLL"
|
||||
| b"NLR" | b"NKT" | b"CLB" | b"NLB" | b"NMW" | b"NML" | b"NTM" | b"NMI" | b"NMG"
|
||||
| b"NMO" | b"NMS" | b"NMR" | b"NCR" | b"NEA" | b"NPW" | b"NPY" | b"NPT" | b"NRA"
|
||||
| b"NWQ" | b"NSU" | b"NSN" | b"NK2" | b"NSV" | b"NFX" | b"NFP" | b"NS6" | b"NNA"
|
||||
| b"NRS" | b"NSW" | b"NSC" | b"NSA" | b"NB6" | b"NSS" | b"NTX" | b"NT6" | b"NTP"
|
||||
| b"NTJ" | b"NRC" | b"NTR" | b"NTB" | b"NGU" | b"NIR" | b"NVL" | b"NVY" | b"NWC"
|
||||
| b"NAD" | b"NWU" | b"NYK" | b"NMZ" | b"NSM" | b"NWR" => Some(SaveType::Eeprom4k),
|
||||
b"NB7" | b"NGT" | b"NFU" | b"NCW" | b"NCZ" | b"ND6" | b"NDO" | b"ND2" | b"N3D" | b"NMX"
|
||||
| b"NGC" | b"NIM" | b"NNB" | b"NMV" | b"NM8" | b"NEV" | b"NPP" | b"NUB" | b"NPD"
|
||||
| b"NRZ" | b"NR7" | b"NEP" | b"NYS" => Some(SaveType::Eeprom16k),
|
||||
b"NTE" | b"NVB" | b"NB5" | b"CFZ" | b"NFZ" | b"NSI" | b"NG6" | b"NGP" | b"NYW" | b"NHY"
|
||||
| b"NIB" | b"NPS" | b"NPA" | b"NP4" | b"NJ5" | b"NP6" | b"NPE" | b"NJG" | b"CZL"
|
||||
| b"NZL" | b"NKG" | b"NMF" | b"NRI" | b"NUT" | b"NUM" | b"NOB" | b"CPS" | b"NPM"
|
||||
| b"NRE" | b"NAL" | b"NT3" | b"NS4" | b"NA2" | b"NVP" | b"NWL" | b"NW2" | b"NWX" => {
|
||||
Some(SaveType::Sram)
|
||||
}
|
||||
b"CDZ" => Some(SaveType::SramBanked),
|
||||
b"NCC" | b"NDA" | b"NAF" | b"NJF" | b"NKJ" | b"NZS" | b"NM6" | b"NCK" | b"NMQ" | b"NPN"
|
||||
| b"NPF" | b"NPO" | b"CP2" | b"NP3" | b"NRH" | b"NSQ" | b"NT9" | b"NW4" | b"NDP" => {
|
||||
Some(SaveType::Flashram)
|
||||
}
|
||||
_ => None,
|
||||
} {
|
||||
return Ok(save_type);
|
||||
}
|
||||
|
||||
Ok(SaveType::None)
|
||||
}
|
@ -33,10 +33,10 @@ enum DataType {
|
||||
}
|
||||
|
||||
pub trait Link {
|
||||
fn execute_command(&mut self, command: &Command) -> Result<Vec<u8>, Error>;
|
||||
fn execute_command(&mut self, command: &mut Command) -> Result<Vec<u8>, Error>;
|
||||
fn execute_command_raw(
|
||||
&mut self,
|
||||
command: &Command,
|
||||
command: &mut Command,
|
||||
timeout: Duration,
|
||||
no_response: bool,
|
||||
ignore_error: bool,
|
||||
@ -83,15 +83,17 @@ impl SerialLink {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serial_send_command(&mut self, command: &Command, timeout: Duration) -> Result<(), Error> {
|
||||
let Command { id, args, mut data } = command.clone();
|
||||
|
||||
fn serial_send_command(
|
||||
&mut self,
|
||||
command: &mut Command,
|
||||
timeout: Duration,
|
||||
) -> Result<(), Error> {
|
||||
let mut packet: Vec<u8> = Vec::new();
|
||||
packet.append(&mut b"CMD".to_vec());
|
||||
packet.append(&mut [id].to_vec());
|
||||
packet.append(&mut args[0].to_be_bytes().to_vec());
|
||||
packet.append(&mut args[1].to_be_bytes().to_vec());
|
||||
packet.append(&mut data);
|
||||
packet.append(&mut [command.id].to_vec());
|
||||
packet.append(&mut command.args[0].to_be_bytes().to_vec());
|
||||
packet.append(&mut command.args[1].to_be_bytes().to_vec());
|
||||
packet.append(&mut command.data);
|
||||
|
||||
self.serial.set_timeout(timeout)?;
|
||||
self.serial.write_all(&packet)?;
|
||||
@ -140,7 +142,7 @@ impl SerialLink {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn send_command(&mut self, command: &Command) -> Result<(), Error> {
|
||||
fn send_command(&mut self, command: &mut Command) -> Result<(), Error> {
|
||||
self.serial_send_command(command, COMMAND_TIMEOUT)
|
||||
}
|
||||
|
||||
@ -153,13 +155,13 @@ impl SerialLink {
|
||||
}
|
||||
|
||||
impl Link for SerialLink {
|
||||
fn execute_command(&mut self, command: &Command) -> Result<Vec<u8>, Error> {
|
||||
fn execute_command(&mut self, command: &mut Command) -> Result<Vec<u8>, Error> {
|
||||
self.execute_command_raw(command, COMMAND_TIMEOUT, false, false)
|
||||
}
|
||||
|
||||
fn execute_command_raw(
|
||||
&mut self,
|
||||
command: &Command,
|
||||
command: &mut Command,
|
||||
timeout: Duration,
|
||||
no_response: bool,
|
||||
ignore_error: bool,
|
||||
@ -169,7 +171,9 @@ impl Link for SerialLink {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
let response = self.receive_response(timeout)?;
|
||||
compare_id(&command, &response)?;
|
||||
if command.id != response.id {
|
||||
return Err(Error::new("Command response ID didn't match"));
|
||||
}
|
||||
if !ignore_error && response.error {
|
||||
return Err(Error::new("Command response error"));
|
||||
}
|
||||
@ -184,13 +188,6 @@ impl Link for SerialLink {
|
||||
}
|
||||
}
|
||||
|
||||
fn compare_id(command: &Command, response: &Response) -> Result<(), Error> {
|
||||
if command.id != response.id {
|
||||
return Err(Error::new("Command response ID didn't match"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn list_serial_devices() -> Result<Vec<Device>, Error> {
|
||||
const SC64_VID: u16 = 0x0403;
|
||||
const SC64_PID: u16 = 0x6014;
|
||||
@ -219,9 +216,7 @@ pub fn list_serial_devices() -> Result<Vec<Device>, Error> {
|
||||
|
||||
pub fn new_serial(port: &str) -> Result<Box<dyn Link>, Error> {
|
||||
let mut link = SerialLink {
|
||||
serial: serialport::new(port, 115_200)
|
||||
.timeout(COMMAND_TIMEOUT)
|
||||
.open()?,
|
||||
serial: serialport::new(port, 115_200).open()?,
|
||||
packets: VecDeque::new(),
|
||||
};
|
||||
|
||||
|
@ -4,54 +4,57 @@ mod link;
|
||||
mod types;
|
||||
mod utils;
|
||||
|
||||
use crate::sc64::cic::IPL3_OFFSET;
|
||||
|
||||
use self::cic::{calculate_ipl3_checksum, guess_ipl3_seed, IPL3_LENGTH};
|
||||
pub use self::link::list_serial_devices;
|
||||
pub use self::types::{
|
||||
BootMode, DataPacket, DdDiskState, DdDriveType, DdMode, DebugPacket, DiskPacket,
|
||||
FirmwareStatus, SaveType, TvType,
|
||||
};
|
||||
use self::types::{ButtonMode, CicSeed, UpdateStatus};
|
||||
use self::{
|
||||
link::{Command, Link},
|
||||
types::{get_config, get_setting, Config, ConfigId, Setting, SettingId},
|
||||
utils::{
|
||||
args_from_vec, datetime_from_vec, file_open_and_check_length, u32_from_vec,
|
||||
vec_from_datetime,
|
||||
pub use self::{
|
||||
error::Error,
|
||||
link::list_serial_devices,
|
||||
types::{
|
||||
BootMode, DataPacket, DdDiskState, DdDriveType, DdMode, DebugPacket, DiskPacket, SaveType,
|
||||
TvType,
|
||||
},
|
||||
};
|
||||
|
||||
use self::{
|
||||
cic::{calculate_ipl3_checksum, guess_ipl3_seed, IPL3_LENGTH, IPL3_OFFSET},
|
||||
link::{Command, Link},
|
||||
types::{
|
||||
get_config, get_setting, ButtonMode, CicSeed, Config, ConfigId, FirmwareStatus, Setting,
|
||||
SettingId, UpdateStatus, Switch, ButtonState,
|
||||
},
|
||||
utils::{args_from_vec, datetime_from_vec, u32_from_vec, vec_from_datetime},
|
||||
};
|
||||
use chrono::{DateTime, Local};
|
||||
pub use error::Error;
|
||||
use std::io::{Read, Seek};
|
||||
use std::time::Instant;
|
||||
use std::{cmp::min, time::Duration};
|
||||
use std::{
|
||||
io::{Read, Seek},
|
||||
time::Instant,
|
||||
{cmp::min, time::Duration},
|
||||
};
|
||||
|
||||
pub struct SC64 {
|
||||
link: Box<dyn Link>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DeviceState {
|
||||
pub bootloader_switch: bool,
|
||||
pub rom_write_enable: bool,
|
||||
pub rom_shadow_enable: bool,
|
||||
pub bootloader_switch: Switch,
|
||||
pub rom_write_enable: Switch,
|
||||
pub rom_shadow_enable: Switch,
|
||||
pub dd_mode: DdMode,
|
||||
pub isv_address: u32,
|
||||
pub boot_mode: BootMode,
|
||||
pub save_type: SaveType,
|
||||
pub cic_seed: CicSeed,
|
||||
pub tv_type: TvType,
|
||||
pub dd_sd_enable: bool,
|
||||
pub dd_sd_enable: Switch,
|
||||
pub dd_drive_type: DdDriveType,
|
||||
pub dd_disk_state: DdDiskState,
|
||||
pub button_state: bool,
|
||||
pub button_state: ButtonState,
|
||||
pub button_mode: ButtonMode,
|
||||
pub rom_extended_enable: bool,
|
||||
pub led_enable: bool,
|
||||
pub rom_extended_enable: Switch,
|
||||
pub led_enable: Switch,
|
||||
pub datetime: DateTime<Local>,
|
||||
}
|
||||
|
||||
const SC64_V2_IDENTIFIER: &[u8; 4] = b"SCv2";
|
||||
|
||||
const SUPPORTED_MAJOR_VERSION: u16 = 2;
|
||||
const SUPPORTED_MINOR_VERSION: u16 = 12;
|
||||
|
||||
@ -91,9 +94,11 @@ const ISV_BUFFER_LENGTH: usize = 64 * 1024;
|
||||
|
||||
pub const MEMORY_LENGTH: usize = 0x0500_2980;
|
||||
|
||||
const MEMORY_WRITE_CHUNK_SIZE: usize = 1 * 1024 * 1024;
|
||||
|
||||
impl SC64 {
|
||||
fn command_identifier_get(&mut self) -> Result<Vec<u8>, Error> {
|
||||
let identifier = self.link.execute_command(&Command {
|
||||
let identifier = self.link.execute_command(&mut Command {
|
||||
id: b'v',
|
||||
args: [0, 0],
|
||||
data: vec![],
|
||||
@ -102,7 +107,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_version_get(&mut self) -> Result<(u16, u16), Error> {
|
||||
let version = self.link.execute_command(&Command {
|
||||
let version = self.link.execute_command(&mut Command {
|
||||
id: b'V',
|
||||
args: [0, 0],
|
||||
data: vec![],
|
||||
@ -113,7 +118,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_state_reset(&mut self) -> Result<(), Error> {
|
||||
self.link.execute_command(&Command {
|
||||
self.link.execute_command(&mut Command {
|
||||
id: b'R',
|
||||
args: [0, 0],
|
||||
data: vec![],
|
||||
@ -130,7 +135,7 @@ impl SC64 {
|
||||
let mut params: Vec<u8> = vec![];
|
||||
params.append(&mut [(disable as u8) << 0, seed].to_vec());
|
||||
params.append(&mut checksum.to_vec());
|
||||
self.link.execute_command(&Command {
|
||||
self.link.execute_command(&mut Command {
|
||||
id: b'B',
|
||||
args: args_from_vec(¶ms[0..8])?,
|
||||
data: vec![],
|
||||
@ -139,7 +144,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_config_get(&mut self, config_id: ConfigId) -> Result<Config, Error> {
|
||||
let data = self.link.execute_command(&Command {
|
||||
let data = self.link.execute_command(&mut Command {
|
||||
id: b'c',
|
||||
args: [config_id.into(), 0],
|
||||
data: vec![],
|
||||
@ -149,7 +154,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_config_set(&mut self, config: Config) -> Result<(), Error> {
|
||||
self.link.execute_command(&Command {
|
||||
self.link.execute_command(&mut Command {
|
||||
id: b'C',
|
||||
args: config.into(),
|
||||
data: vec![],
|
||||
@ -158,7 +163,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_setting_get(&mut self, setting_id: SettingId) -> Result<Setting, Error> {
|
||||
let data = self.link.execute_command(&Command {
|
||||
let data = self.link.execute_command(&mut Command {
|
||||
id: b'a',
|
||||
args: [setting_id.into(), 0],
|
||||
data: vec![],
|
||||
@ -168,7 +173,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_setting_set(&mut self, setting: Setting) -> Result<(), Error> {
|
||||
self.link.execute_command(&Command {
|
||||
self.link.execute_command(&mut Command {
|
||||
id: b'A',
|
||||
args: setting.into(),
|
||||
data: vec![],
|
||||
@ -177,7 +182,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_time_get(&mut self) -> Result<DateTime<Local>, Error> {
|
||||
let data = self.link.execute_command(&Command {
|
||||
let data = self.link.execute_command(&mut Command {
|
||||
id: b't',
|
||||
args: [0, 0],
|
||||
data: vec![],
|
||||
@ -186,7 +191,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_time_set(&mut self, datetime: DateTime<Local>) -> Result<(), Error> {
|
||||
self.link.execute_command(&Command {
|
||||
self.link.execute_command(&mut Command {
|
||||
id: b'T',
|
||||
args: args_from_vec(&vec_from_datetime(datetime)?[0..8])?,
|
||||
data: vec![],
|
||||
@ -195,7 +200,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_memory_read(&mut self, address: u32, length: usize) -> Result<Vec<u8>, Error> {
|
||||
let data = self.link.execute_command(&Command {
|
||||
let data = self.link.execute_command(&mut Command {
|
||||
id: b'm',
|
||||
args: [address, length as u32],
|
||||
data: vec![],
|
||||
@ -204,7 +209,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_memory_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
|
||||
self.link.execute_command(&Command {
|
||||
self.link.execute_command(&mut Command {
|
||||
id: b'M',
|
||||
args: [address, data.len() as u32],
|
||||
data: data.to_vec(),
|
||||
@ -214,7 +219,7 @@ impl SC64 {
|
||||
|
||||
fn command_usb_write(&mut self, datatype: u8, data: &[u8]) -> Result<(), Error> {
|
||||
self.link.execute_command_raw(
|
||||
&Command {
|
||||
&mut Command {
|
||||
id: b'U',
|
||||
args: [datatype as u32, data.len() as u32],
|
||||
data: data.to_vec(),
|
||||
@ -227,7 +232,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_dd_set_block_ready(&mut self, error: bool) -> Result<(), Error> {
|
||||
self.link.execute_command(&Command {
|
||||
self.link.execute_command(&mut Command {
|
||||
id: b'D',
|
||||
args: [error as u32, 0],
|
||||
data: vec![],
|
||||
@ -236,7 +241,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_flash_wait_busy(&mut self, wait: bool) -> Result<u32, Error> {
|
||||
let erase_block_size = self.link.execute_command(&Command {
|
||||
let erase_block_size = self.link.execute_command(&mut Command {
|
||||
id: b'p',
|
||||
args: [wait as u32, 0],
|
||||
data: vec![],
|
||||
@ -245,7 +250,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
fn command_flash_erase_block(&mut self, address: u32) -> Result<(), Error> {
|
||||
self.link.execute_command(&Command {
|
||||
self.link.execute_command(&mut Command {
|
||||
id: b'P',
|
||||
args: [address, 0],
|
||||
data: vec![],
|
||||
@ -255,7 +260,7 @@ impl SC64 {
|
||||
|
||||
fn command_firmware_backup(&mut self, address: u32) -> Result<(FirmwareStatus, u32), Error> {
|
||||
let data = self.link.execute_command_raw(
|
||||
&Command {
|
||||
&mut Command {
|
||||
id: b'f',
|
||||
args: [address, 0],
|
||||
data: vec![],
|
||||
@ -275,7 +280,7 @@ impl SC64 {
|
||||
length: usize,
|
||||
) -> Result<FirmwareStatus, Error> {
|
||||
let data = self.link.execute_command_raw(
|
||||
&Command {
|
||||
&mut Command {
|
||||
id: b'F',
|
||||
args: [address, length as u32],
|
||||
data: vec![],
|
||||
@ -286,91 +291,34 @@ impl SC64 {
|
||||
)?;
|
||||
Ok(FirmwareStatus::try_from(utils::u32_from_vec(&data[0..4])?)?)
|
||||
}
|
||||
|
||||
fn flash_erase(&mut self, address: u32, length: usize) -> Result<(), Error> {
|
||||
let erase_block_size = self.command_flash_wait_busy(false)?;
|
||||
for offset in (0..length as u32).step_by(erase_block_size as usize) {
|
||||
self.command_flash_erase_block(address + offset)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flash_program(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
|
||||
let current_data = self.command_memory_read(address, data.len())?;
|
||||
if data == current_data {
|
||||
return Ok(());
|
||||
}
|
||||
self.flash_erase(address, data.len())?;
|
||||
self.command_memory_write(address, data)?;
|
||||
self.command_flash_wait_busy(true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flash_program_shadow(&mut self, data: &[u8]) -> Result<(), Error> {
|
||||
if data.len() > ROM_SHADOW_LENGTH {
|
||||
return Err(Error::new(
|
||||
"Invalid data length for program ROM shadow operation",
|
||||
));
|
||||
}
|
||||
self.flash_program(ROM_SHADOW_ADDRESS, data)
|
||||
}
|
||||
|
||||
fn flash_program_extended(&mut self, data: &[u8]) -> Result<(), Error> {
|
||||
if data.len() > ROM_EXTENDED_LENGTH {
|
||||
return Err(Error::new(
|
||||
"Invalid data length for program ROM extended operation",
|
||||
));
|
||||
}
|
||||
self.flash_program(ROM_EXTENDED_ADDRESS, data)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn flash_program_bootloader(&mut self, data: &[u8]) -> Result<(), Error> {
|
||||
if data.len() > BOOTLOADER_LENGTH {
|
||||
return Err(Error::new(
|
||||
"Invalid data length for program bootloader operation",
|
||||
));
|
||||
}
|
||||
self.flash_program(BOOTLOADER_ADDRESS, data)
|
||||
}
|
||||
}
|
||||
|
||||
impl SC64 {
|
||||
pub fn check_firmware_version(&mut self) -> Result<(u16, u16), Error> {
|
||||
let (major, minor) = self
|
||||
.command_version_get()
|
||||
.map_err(|_| Error::new("Outdated SC64 firmware version, please update firmware"))?;
|
||||
if major != SUPPORTED_MAJOR_VERSION || minor < SUPPORTED_MINOR_VERSION {
|
||||
return Err(Error::new(
|
||||
"Unsupported SC64 firmware version, please update firmware",
|
||||
));
|
||||
}
|
||||
Ok((major, minor))
|
||||
pub fn upload_rom<T: Read + Seek>(
|
||||
&mut self,
|
||||
reader: &mut T,
|
||||
length: usize,
|
||||
no_shadow: bool,
|
||||
) -> Result<(), Error> {
|
||||
if length > MAX_ROM_LENGTH {
|
||||
return Err(Error::new("ROM length too big"));
|
||||
}
|
||||
|
||||
pub fn reset_state(&mut self) -> Result<(), Error> {
|
||||
self.command_state_reset()?;
|
||||
Ok(())
|
||||
let mut pi_config = vec![0u8; 4];
|
||||
reader.read(&mut pi_config)?;
|
||||
reader.rewind()?;
|
||||
|
||||
let endian_swapper = match &pi_config[0..4] {
|
||||
[0x37, 0x80, 0x40, 0x12] => {
|
||||
|b: &mut [u8]| b.chunks_exact_mut(2).for_each(|c| c.swap(0, 1))
|
||||
}
|
||||
|
||||
pub fn upload_rom(&mut self, path: &str, no_shadow: bool) -> Result<(), Error> {
|
||||
const BUFFER_SIZE: usize = 1 * 1024 * 1024;
|
||||
|
||||
let (mut file, length) = file_open_and_check_length(path, MAX_ROM_LENGTH)?;
|
||||
|
||||
let mut endian_check = vec![0u8; 4];
|
||||
file.read(&mut endian_check)?;
|
||||
file.rewind()?;
|
||||
|
||||
let endian_swapper = match u32_from_vec(&endian_check[0..4])? {
|
||||
0x37804012 => |b: &mut [u8]| b.chunks_exact_mut(2).for_each(|c| c.swap(0, 1)),
|
||||
0x40123780 => |b: &mut [u8]| {
|
||||
[0x40, 0x12, 0x37, 0x80] => |b: &mut [u8]| {
|
||||
b.chunks_exact_mut(4).for_each(|c| {
|
||||
c.swap(0, 3);
|
||||
c.swap(1, 2)
|
||||
})
|
||||
},
|
||||
_ => |_b: &mut [u8]| {},
|
||||
_ => |_: &mut [u8]| {},
|
||||
};
|
||||
|
||||
let rom_shadow_enabled = !no_shadow && length > (SDRAM_LENGTH - ROM_SHADOW_LENGTH);
|
||||
@ -382,79 +330,62 @@ impl SC64 {
|
||||
min(length, SDRAM_LENGTH)
|
||||
};
|
||||
|
||||
let mut buffer = vec![0u8; BUFFER_SIZE];
|
||||
self.memory_write_chunked(reader, SDRAM_ADDRESS, sdram_length, Some(endian_swapper))?;
|
||||
|
||||
for offset in (0..sdram_length as u32).step_by(buffer.len()) {
|
||||
let chunk = file.read(&mut buffer)?;
|
||||
endian_swapper(&mut buffer);
|
||||
self.command_memory_write(SDRAM_ADDRESS + offset, &buffer[0..chunk])?;
|
||||
}
|
||||
|
||||
self.command_config_set(Config::RomShadowEnable(rom_shadow_enabled))?;
|
||||
self.command_config_set(Config::RomShadowEnable(rom_shadow_enabled.into()))?;
|
||||
if rom_shadow_enabled {
|
||||
let mut buffer = vec![0u8; ROM_SHADOW_LENGTH];
|
||||
let chunk = file.read(&mut buffer)?;
|
||||
endian_swapper(&mut buffer);
|
||||
self.flash_program_shadow(&buffer[0..chunk])?;
|
||||
let rom_shadow_length = min(length - sdram_length, ROM_SHADOW_LENGTH);
|
||||
self.flash_program(
|
||||
reader,
|
||||
ROM_SHADOW_ADDRESS,
|
||||
rom_shadow_length,
|
||||
Some(endian_swapper),
|
||||
)?;
|
||||
}
|
||||
|
||||
self.command_config_set(Config::RomExtendedEnable(rom_extended_enabled))?;
|
||||
self.command_config_set(Config::RomExtendedEnable(rom_extended_enabled.into()))?;
|
||||
if rom_extended_enabled {
|
||||
let mut buffer = vec![0u8; ROM_EXTENDED_LENGTH];
|
||||
let chunk = file.read(&mut buffer)?;
|
||||
endian_swapper(&mut buffer);
|
||||
self.flash_program_extended(&buffer[0..chunk])?;
|
||||
let rom_extended_length = min(length - SDRAM_LENGTH, ROM_EXTENDED_LENGTH);
|
||||
self.flash_program(
|
||||
reader,
|
||||
ROM_EXTENDED_ADDRESS,
|
||||
rom_extended_length,
|
||||
Some(endian_swapper),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn upload_ddipl(&mut self, path: &str) -> Result<(), Error> {
|
||||
let (mut file, length) = file_open_and_check_length(path, DDIPL_LENGTH)?;
|
||||
|
||||
let mut buffer = vec![0u8; length];
|
||||
let chunk = file.read(&mut buffer)?;
|
||||
|
||||
self.command_memory_write(DDIPL_ADDRESS, &buffer[0..chunk])
|
||||
pub fn upload_ddipl<T: Read>(&mut self, reader: &mut T, length: usize) -> Result<(), Error> {
|
||||
if length > DDIPL_LENGTH {
|
||||
return Err(Error::new("DDIPL length too big"));
|
||||
}
|
||||
|
||||
pub fn upload_save(&mut self, path: &str) -> Result<(), Error> {
|
||||
self.memory_write_chunked(reader, DDIPL_ADDRESS, length, None)
|
||||
}
|
||||
|
||||
pub fn upload_save<T: Read>(&mut self, reader: &mut T, length: usize) -> Result<(), Error> {
|
||||
let save_type = get_config!(self, SaveType)?;
|
||||
|
||||
if matches!(save_type, SaveType::None) {
|
||||
let (address, save_length) = match save_type {
|
||||
SaveType::None => {
|
||||
return Err(Error::new("No save type is enabled"));
|
||||
}
|
||||
|
||||
let address = match save_type {
|
||||
SaveType::None => 0,
|
||||
SaveType::Eeprom4k => EEPROM_ADDRESS,
|
||||
SaveType::Eeprom16k => EEPROM_ADDRESS,
|
||||
SaveType::Sram => SAVE_ADDRESS,
|
||||
SaveType::SramBanked => SAVE_ADDRESS,
|
||||
SaveType::Flashram => SAVE_ADDRESS,
|
||||
SaveType::Eeprom4k => (EEPROM_ADDRESS, EEPROM_4K_LENGTH),
|
||||
SaveType::Eeprom16k => (EEPROM_ADDRESS, EEPROM_16K_LENGTH),
|
||||
SaveType::Sram => (SAVE_ADDRESS, SRAM_LENGTH),
|
||||
SaveType::SramBanked => (SAVE_ADDRESS, SRAM_BANKED_LENGTH),
|
||||
SaveType::Flashram => (SAVE_ADDRESS, FLASHRAM_LENGTH),
|
||||
};
|
||||
|
||||
let save_length = match save_type {
|
||||
SaveType::None => 0,
|
||||
SaveType::Eeprom4k => EEPROM_4K_LENGTH,
|
||||
SaveType::Eeprom16k => EEPROM_16K_LENGTH,
|
||||
SaveType::Sram => SRAM_LENGTH,
|
||||
SaveType::SramBanked => SRAM_BANKED_LENGTH,
|
||||
SaveType::Flashram => FLASHRAM_LENGTH,
|
||||
};
|
||||
|
||||
let (mut file, length) = file_open_and_check_length(path, save_length)?;
|
||||
|
||||
if length != save_length {
|
||||
return Err(Error::new(
|
||||
"Save file size did not match currently enabled save type",
|
||||
));
|
||||
}
|
||||
|
||||
let mut buffer = vec![0u8; length];
|
||||
file.read(&mut buffer)?;
|
||||
|
||||
self.command_memory_write(address, &buffer)
|
||||
self.memory_write_chunked(reader, address, save_length, None)
|
||||
}
|
||||
|
||||
pub fn dump_memory(&mut self, address: u32, length: usize) -> Result<Vec<u8>, Error> {
|
||||
@ -498,7 +429,7 @@ impl SC64 {
|
||||
}
|
||||
|
||||
pub fn set_led_blink(&mut self, enabled: bool) -> Result<(), Error> {
|
||||
self.command_setting_set(Setting::LedEnable(enabled))
|
||||
self.command_setting_set(Setting::LedEnable(enabled.into()))
|
||||
}
|
||||
|
||||
pub fn get_device_state(&mut self) -> Result<DeviceState, Error> {
|
||||
@ -529,7 +460,7 @@ impl SC64 {
|
||||
drive_type: DdDriveType,
|
||||
) -> Result<(), Error> {
|
||||
self.command_config_set(Config::DdMode(dd_mode))?;
|
||||
self.command_config_set(Config::DdSdEnable(false))?;
|
||||
self.command_config_set(Config::DdSdEnable(Switch::Off))?;
|
||||
self.command_config_set(Config::DdDriveType(drive_type))?;
|
||||
self.command_config_set(Config::DdDiskState(DdDiskState::Ejected))?;
|
||||
Ok(())
|
||||
@ -539,22 +470,22 @@ impl SC64 {
|
||||
self.command_config_set(Config::DdDiskState(disk_state))
|
||||
}
|
||||
|
||||
pub fn configure_isviewer64(&mut self, offset: Option<u32>) -> Result<(), Error> {
|
||||
if let Some(off) = offset {
|
||||
if get_config!(self, RomShadowEnable)? {
|
||||
if off > (SAVE_ADDRESS - ISV_BUFFER_LENGTH as u32) {
|
||||
pub fn configure_is_viewer_64(&mut self, offset: Option<u32>) -> Result<(), Error> {
|
||||
if let Some(offset) = offset {
|
||||
if get_config!(self, RomShadowEnable)?.into() {
|
||||
if offset > (SAVE_ADDRESS - ISV_BUFFER_LENGTH as u32) {
|
||||
return Err(Error::new(
|
||||
format!(
|
||||
"ROM shadow is enabled, IS-Viewer 64 at offset 0x{off:08X} won't work"
|
||||
"ROM shadow is enabled, IS-Viewer 64 at offset 0x{offset:08X} won't work"
|
||||
)
|
||||
.as_str(),
|
||||
));
|
||||
}
|
||||
}
|
||||
self.command_config_set(Config::RomWriteEnable(true))?;
|
||||
self.command_config_set(Config::IsvAddress(off))?;
|
||||
self.command_config_set(Config::RomWriteEnable(Switch::On))?;
|
||||
self.command_config_set(Config::IsvAddress(offset))?;
|
||||
} else {
|
||||
self.command_config_set(Config::RomWriteEnable(false))?;
|
||||
self.command_config_set(Config::RomWriteEnable(Switch::Off))?;
|
||||
self.command_config_set(Config::IsvAddress(0))?;
|
||||
}
|
||||
Ok(())
|
||||
@ -586,12 +517,29 @@ impl SC64 {
|
||||
self.command_usb_write(debug_packet.datatype, &debug_packet.data)
|
||||
}
|
||||
|
||||
pub fn check_firmware_version(&mut self) -> Result<(u16, u16), Error> {
|
||||
let (major, minor) = self
|
||||
.command_version_get()
|
||||
.map_err(|_| Error::new("Outdated SC64 firmware version, please update firmware"))?;
|
||||
if major != SUPPORTED_MAJOR_VERSION || minor < SUPPORTED_MINOR_VERSION {
|
||||
return Err(Error::new(
|
||||
"Unsupported SC64 firmware version, please update firmware",
|
||||
));
|
||||
}
|
||||
Ok((major, minor))
|
||||
}
|
||||
|
||||
pub fn reset_state(&mut self) -> Result<(), Error> {
|
||||
self.command_state_reset()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn backup_firmware(&mut self) -> Result<Vec<u8>, Error> {
|
||||
self.command_state_reset()?;
|
||||
let (status, length) = self.command_firmware_backup(FIRMWARE_ADDRESS)?;
|
||||
if !matches!(status, FirmwareStatus::Ok) {
|
||||
return Err(Error::new(
|
||||
format!("Firmware backup error: {:?}", status).as_str(),
|
||||
format!("Firmware backup error: {}", status).as_str(),
|
||||
));
|
||||
}
|
||||
self.command_memory_read(FIRMWARE_ADDRESS, length as usize)
|
||||
@ -603,7 +551,7 @@ impl SC64 {
|
||||
let status = self.command_firmware_update(FIRMWARE_ADDRESS, data.len())?;
|
||||
if !matches!(status, FirmwareStatus::Ok) {
|
||||
return Err(Error::new(
|
||||
format!("Firmware update verify error: {:?}", status).as_str(),
|
||||
format!("Firmware update verify error: {}", status).as_str(),
|
||||
));
|
||||
}
|
||||
let timeout = Instant::now();
|
||||
@ -619,7 +567,7 @@ impl SC64 {
|
||||
UpdateStatus::Err => {
|
||||
return Err(Error::new(
|
||||
format!(
|
||||
"Firmware update error on step {:?}, device is most likely bricked",
|
||||
"Firmware update error on step {}, device is most likely bricked",
|
||||
last_update_status
|
||||
)
|
||||
.as_str(),
|
||||
@ -641,6 +589,52 @@ impl SC64 {
|
||||
std::thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_bootloader(&mut self, reader: &mut dyn Read, length: usize) -> Result<(), Error> {
|
||||
if length > BOOTLOADER_LENGTH {
|
||||
return Err(Error::new("Bootloader length too big"));
|
||||
}
|
||||
self.flash_program(reader, BOOTLOADER_ADDRESS, length, None)
|
||||
}
|
||||
|
||||
fn memory_write_chunked(
|
||||
&mut self,
|
||||
reader: &mut dyn Read,
|
||||
address: u32,
|
||||
length: usize,
|
||||
transform: Option<fn(&mut [u8])>,
|
||||
) -> Result<(), Error> {
|
||||
let mut data: Vec<u8> = vec![0u8; MEMORY_WRITE_CHUNK_SIZE];
|
||||
for offset in (0..length).step_by(MEMORY_WRITE_CHUNK_SIZE) {
|
||||
let chunk = reader.read(&mut data)?;
|
||||
if let Some(transform) = transform {
|
||||
transform(&mut data);
|
||||
}
|
||||
self.command_memory_write(address + offset as u32, &data[0..chunk])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flash_erase(&mut self, address: u32, length: usize) -> Result<(), Error> {
|
||||
let erase_block_size = self.command_flash_wait_busy(false)?;
|
||||
for offset in (0..length as u32).step_by(erase_block_size as usize) {
|
||||
self.command_flash_erase_block(address + offset)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flash_program(
|
||||
&mut self,
|
||||
reader: &mut dyn Read,
|
||||
address: u32,
|
||||
length: usize,
|
||||
transform: Option<fn(&mut [u8])>,
|
||||
) -> Result<(), Error> {
|
||||
self.flash_erase(address, length)?;
|
||||
self.memory_write_chunked(reader, address, length, transform)?;
|
||||
self.command_flash_wait_busy(true)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(sn: Option<String>) -> Result<SC64, Error> {
|
||||
@ -664,7 +658,7 @@ pub fn new(sn: Option<String>) -> Result<SC64, Error> {
|
||||
.command_identifier_get()
|
||||
.map_err(|_| Error::new("Couldn't get SC64 device identifier"))?;
|
||||
|
||||
if identifier != b"SCv2" {
|
||||
if identifier != SC64_V2_IDENTIFIER {
|
||||
return Err(Error::new("Unknown identifier received, not a SC64 device"));
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::{link::Packet, utils::u32_from_vec, Error};
|
||||
use encoding_rs::EUC_JP;
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ConfigId {
|
||||
@ -21,21 +22,21 @@ pub enum ConfigId {
|
||||
}
|
||||
|
||||
pub enum Config {
|
||||
BootloaderSwitch(bool),
|
||||
RomWriteEnable(bool),
|
||||
RomShadowEnable(bool),
|
||||
BootloaderSwitch(Switch),
|
||||
RomWriteEnable(Switch),
|
||||
RomShadowEnable(Switch),
|
||||
DdMode(DdMode),
|
||||
IsvAddress(u32),
|
||||
BootMode(BootMode),
|
||||
SaveType(SaveType),
|
||||
CicSeed(CicSeed),
|
||||
TvType(TvType),
|
||||
DdSdEnable(bool),
|
||||
DdSdEnable(Switch),
|
||||
DdDriveType(DdDriveType),
|
||||
DdDiskState(DdDiskState),
|
||||
ButtonState(bool),
|
||||
ButtonState(ButtonState),
|
||||
ButtonMode(ButtonMode),
|
||||
RomExtendedEnable(bool),
|
||||
RomExtendedEnable(Switch),
|
||||
}
|
||||
|
||||
impl From<ConfigId> for u32 {
|
||||
@ -65,21 +66,21 @@ impl TryFrom<(ConfigId, u32)> for Config {
|
||||
fn try_from(value: (ConfigId, u32)) -> Result<Self, Self::Error> {
|
||||
let (id, config) = value;
|
||||
Ok(match id {
|
||||
ConfigId::BootloaderSwitch => Config::BootloaderSwitch(config != 0),
|
||||
ConfigId::RomWriteEnable => Config::RomWriteEnable(config != 0),
|
||||
ConfigId::RomShadowEnable => Config::RomShadowEnable(config != 0),
|
||||
ConfigId::DdMode => Config::DdMode(config.try_into()?),
|
||||
ConfigId::IsvAddress => Config::IsvAddress(config),
|
||||
ConfigId::BootMode => Config::BootMode(config.try_into()?),
|
||||
ConfigId::SaveType => Config::SaveType(config.try_into()?),
|
||||
ConfigId::CicSeed => Config::CicSeed(config.try_into()?),
|
||||
ConfigId::TvType => Config::TvType(config.try_into()?),
|
||||
ConfigId::DdSdEnable => Config::DdSdEnable(config != 0),
|
||||
ConfigId::DdDriveType => Config::DdDriveType(config.try_into()?),
|
||||
ConfigId::DdDiskState => Config::DdDiskState(config.try_into()?),
|
||||
ConfigId::ButtonState => Config::ButtonState(config != 0),
|
||||
ConfigId::ButtonMode => Config::ButtonMode(config.try_into()?),
|
||||
ConfigId::RomExtendedEnable => Config::RomExtendedEnable(config != 0),
|
||||
ConfigId::BootloaderSwitch => Self::BootloaderSwitch(config.try_into()?),
|
||||
ConfigId::RomWriteEnable => Self::RomWriteEnable(config.try_into()?),
|
||||
ConfigId::RomShadowEnable => Self::RomShadowEnable(config.try_into()?),
|
||||
ConfigId::DdMode => Self::DdMode(config.try_into()?),
|
||||
ConfigId::IsvAddress => Self::IsvAddress(config),
|
||||
ConfigId::BootMode => Self::BootMode(config.try_into()?),
|
||||
ConfigId::SaveType => Self::SaveType(config.try_into()?),
|
||||
ConfigId::CicSeed => Self::CicSeed(config.try_into()?),
|
||||
ConfigId::TvType => Self::TvType(config.try_into()?),
|
||||
ConfigId::DdSdEnable => Self::DdSdEnable(config.try_into()?),
|
||||
ConfigId::DdDriveType => Self::DdDriveType(config.try_into()?),
|
||||
ConfigId::DdDiskState => Self::DdDiskState(config.try_into()?),
|
||||
ConfigId::ButtonState => Self::ButtonState(config.try_into()?),
|
||||
ConfigId::ButtonMode => Self::ButtonMode(config.try_into()?),
|
||||
ConfigId::RomExtendedEnable => Self::RomExtendedEnable(config.try_into()?),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -106,7 +107,58 @@ impl From<Config> for [u32; 2] {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Switch {
|
||||
Off,
|
||||
On,
|
||||
}
|
||||
|
||||
impl Display for Switch {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
Switch::Off => "Disabled",
|
||||
Switch::On => "Enabled",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for Switch {
|
||||
type Error = Error;
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
0 => Self::Off,
|
||||
_ => Self::On,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Switch> for u32 {
|
||||
fn from(value: Switch) -> Self {
|
||||
match value {
|
||||
Switch::Off => 0,
|
||||
Switch::On => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Switch {
|
||||
fn from(value: bool) -> Self {
|
||||
match value {
|
||||
false => Self::Off,
|
||||
true => Self::On,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Switch> for bool {
|
||||
fn from(value: Switch) -> Self {
|
||||
match value {
|
||||
Switch::Off => false,
|
||||
Switch::On => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum DdMode {
|
||||
None,
|
||||
Regs,
|
||||
@ -114,14 +166,25 @@ pub enum DdMode {
|
||||
Full,
|
||||
}
|
||||
|
||||
impl Display for DdMode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
DdMode::None => "Disabled",
|
||||
DdMode::Regs => "Only registers",
|
||||
DdMode::DdIpl => "Only 64DD IPL",
|
||||
DdMode::Full => "Registers + 64DD IPL",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for DdMode {
|
||||
type Error = Error;
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
0 => DdMode::None,
|
||||
1 => DdMode::Regs,
|
||||
2 => DdMode::DdIpl,
|
||||
3 => DdMode::Full,
|
||||
0 => Self::None,
|
||||
1 => Self::Regs,
|
||||
2 => Self::DdIpl,
|
||||
3 => Self::Full,
|
||||
_ => return Err(Error::new("Unknown 64DD mode code")),
|
||||
})
|
||||
}
|
||||
@ -138,7 +201,6 @@ impl From<DdMode> for u32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum BootMode {
|
||||
Menu,
|
||||
Rom,
|
||||
@ -147,15 +209,27 @@ pub enum BootMode {
|
||||
DirectDdIpl,
|
||||
}
|
||||
|
||||
impl Display for BootMode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
Self::Menu => "Menu",
|
||||
Self::Rom => "Bootloader -> ROM",
|
||||
Self::DdIpl => "Bootloader -> 64DD IPL",
|
||||
Self::DirectRom => "ROM (direct)",
|
||||
Self::DirectDdIpl => "64DD IPL (direct)",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for BootMode {
|
||||
type Error = Error;
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
0 => BootMode::Menu,
|
||||
1 => BootMode::Rom,
|
||||
2 => BootMode::DdIpl,
|
||||
3 => BootMode::DirectRom,
|
||||
4 => BootMode::DirectDdIpl,
|
||||
0 => Self::Menu,
|
||||
1 => Self::Rom,
|
||||
2 => Self::DdIpl,
|
||||
3 => Self::DirectRom,
|
||||
4 => Self::DirectDdIpl,
|
||||
_ => return Err(Error::new("Unknown boot mode code")),
|
||||
})
|
||||
}
|
||||
@ -173,7 +247,6 @@ impl From<BootMode> for u32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SaveType {
|
||||
None,
|
||||
Eeprom4k,
|
||||
@ -183,16 +256,29 @@ pub enum SaveType {
|
||||
SramBanked,
|
||||
}
|
||||
|
||||
impl Display for SaveType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
Self::None => "None",
|
||||
Self::Eeprom4k => "EEPROM 4k",
|
||||
Self::Eeprom16k => "EEPROM 16k",
|
||||
Self::Sram => "SRAM",
|
||||
Self::SramBanked => "SRAM banked",
|
||||
Self::Flashram => "FlashRAM",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for SaveType {
|
||||
type Error = Error;
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
0 => SaveType::None,
|
||||
1 => SaveType::Eeprom4k,
|
||||
2 => SaveType::Eeprom16k,
|
||||
3 => SaveType::Sram,
|
||||
4 => SaveType::Flashram,
|
||||
5 => SaveType::SramBanked,
|
||||
0 => Self::None,
|
||||
1 => Self::Eeprom4k,
|
||||
2 => Self::Eeprom16k,
|
||||
3 => Self::Sram,
|
||||
4 => Self::Flashram,
|
||||
5 => Self::SramBanked,
|
||||
_ => return Err(Error::new("Unknown save type code")),
|
||||
})
|
||||
}
|
||||
@ -211,19 +297,28 @@ impl From<SaveType> for u32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CicSeed {
|
||||
Seed(u8),
|
||||
Auto,
|
||||
}
|
||||
|
||||
impl Display for CicSeed {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if let CicSeed::Seed(seed) = self {
|
||||
f.write_fmt(format_args!("0x{seed:02X}"))
|
||||
} else {
|
||||
f.write_str("Auto")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for CicSeed {
|
||||
type Error = Error;
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
Ok(if value <= 0xFF {
|
||||
CicSeed::Seed(value as u8)
|
||||
Self::Seed(value as u8)
|
||||
} else if value == 0xFFFF {
|
||||
CicSeed::Auto
|
||||
Self::Auto
|
||||
} else {
|
||||
return Err(Error::new("Unknown CIC seed code"));
|
||||
})
|
||||
@ -239,7 +334,6 @@ impl From<CicSeed> for u32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TvType {
|
||||
PAL,
|
||||
NTSC,
|
||||
@ -247,14 +341,25 @@ pub enum TvType {
|
||||
Auto,
|
||||
}
|
||||
|
||||
impl Display for TvType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
Self::PAL => "PAL",
|
||||
Self::NTSC => "NTSC",
|
||||
Self::MPAL => "MPAL",
|
||||
Self::Auto => "Auto",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for TvType {
|
||||
type Error = Error;
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
0 => TvType::PAL,
|
||||
1 => TvType::NTSC,
|
||||
2 => TvType::MPAL,
|
||||
3 => TvType::Auto,
|
||||
0 => Self::PAL,
|
||||
1 => Self::NTSC,
|
||||
2 => Self::MPAL,
|
||||
3 => Self::Auto,
|
||||
_ => return Err(Error::new("Unknown TV type code")),
|
||||
})
|
||||
}
|
||||
@ -271,18 +376,26 @@ impl From<TvType> for u32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DdDriveType {
|
||||
Retail,
|
||||
Development,
|
||||
}
|
||||
|
||||
impl Display for DdDriveType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
DdDriveType::Retail => "Retail",
|
||||
DdDriveType::Development => "Development",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for DdDriveType {
|
||||
type Error = Error;
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
0 => DdDriveType::Retail,
|
||||
1 => DdDriveType::Development,
|
||||
0 => Self::Retail,
|
||||
1 => Self::Development,
|
||||
_ => return Err(Error::new("Unknown 64DD drive type code")),
|
||||
})
|
||||
}
|
||||
@ -297,20 +410,29 @@ impl From<DdDriveType> for u32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DdDiskState {
|
||||
Ejected,
|
||||
Inserted,
|
||||
Changed,
|
||||
}
|
||||
|
||||
impl Display for DdDiskState {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
DdDiskState::Ejected => "Ejected",
|
||||
DdDiskState::Inserted => "Inserted",
|
||||
DdDiskState::Changed => "Changed",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for DdDiskState {
|
||||
type Error = Error;
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
0 => DdDiskState::Ejected,
|
||||
1 => DdDiskState::Inserted,
|
||||
2 => DdDiskState::Changed,
|
||||
0 => Self::Ejected,
|
||||
1 => Self::Inserted,
|
||||
2 => Self::Changed,
|
||||
_ => return Err(Error::new("Unknown 64DD disk state code")),
|
||||
})
|
||||
}
|
||||
@ -326,7 +448,57 @@ impl From<DdDiskState> for u32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ButtonState {
|
||||
NotPressed,
|
||||
Pressed,
|
||||
}
|
||||
|
||||
impl Display for ButtonState {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
ButtonState::NotPressed => "Not pressed",
|
||||
ButtonState::Pressed => "Pressed",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for ButtonState {
|
||||
type Error = Error;
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
0 => Self::NotPressed,
|
||||
_ => Self::Pressed,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ButtonState> for u32 {
|
||||
fn from(value: ButtonState) -> Self {
|
||||
match value {
|
||||
ButtonState::NotPressed => 0,
|
||||
ButtonState::Pressed => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for ButtonState {
|
||||
fn from(value: bool) -> Self {
|
||||
match value {
|
||||
false => Self::NotPressed,
|
||||
true => Self::Pressed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ButtonState> for bool {
|
||||
fn from(value: ButtonState) -> Self {
|
||||
match value {
|
||||
ButtonState::NotPressed => false,
|
||||
ButtonState::Pressed => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ButtonMode {
|
||||
None,
|
||||
N64Irq,
|
||||
@ -334,14 +506,25 @@ pub enum ButtonMode {
|
||||
DdDiskSwap,
|
||||
}
|
||||
|
||||
impl Display for ButtonMode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
ButtonMode::None => "User",
|
||||
ButtonMode::N64Irq => "N64 IRQ trigger",
|
||||
ButtonMode::UsbPacket => "Send USB packet",
|
||||
ButtonMode::DdDiskSwap => "Swap 64DD disk",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for ButtonMode {
|
||||
type Error = Error;
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
0 => ButtonMode::None,
|
||||
1 => ButtonMode::N64Irq,
|
||||
2 => ButtonMode::UsbPacket,
|
||||
3 => ButtonMode::DdDiskSwap,
|
||||
0 => Self::None,
|
||||
1 => Self::N64Irq,
|
||||
2 => Self::UsbPacket,
|
||||
3 => Self::DdDiskSwap,
|
||||
_ => return Err(Error::new("Unknown button mode code")),
|
||||
})
|
||||
}
|
||||
@ -364,7 +547,7 @@ pub enum SettingId {
|
||||
}
|
||||
|
||||
pub enum Setting {
|
||||
LedEnable(bool),
|
||||
LedEnable(Switch),
|
||||
}
|
||||
|
||||
impl From<SettingId> for u32 {
|
||||
@ -380,7 +563,7 @@ impl TryFrom<(SettingId, u32)> for Setting {
|
||||
fn try_from(value: (SettingId, u32)) -> Result<Self, Self::Error> {
|
||||
let (id, setting) = value;
|
||||
Ok(match id {
|
||||
SettingId::LedEnable => Setting::LedEnable(setting != 0),
|
||||
SettingId::LedEnable => Self::LedEnable(setting.try_into()?),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -480,7 +663,6 @@ impl DiskBlock {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FirmwareStatus {
|
||||
Ok,
|
||||
ErrToken,
|
||||
@ -490,22 +672,34 @@ pub enum FirmwareStatus {
|
||||
ErrRead,
|
||||
}
|
||||
|
||||
impl Display for FirmwareStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
FirmwareStatus::Ok => "OK",
|
||||
FirmwareStatus::ErrToken => "Invalid firmware header",
|
||||
FirmwareStatus::ErrChecksum => "Invalid chunk checksum",
|
||||
FirmwareStatus::ErrSize => "Invalid firmware size",
|
||||
FirmwareStatus::ErrUnknownChunk => "Unknown chunk in firmware",
|
||||
FirmwareStatus::ErrRead => "Firmware read error",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for FirmwareStatus {
|
||||
type Error = Error;
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
0 => FirmwareStatus::Ok,
|
||||
1 => FirmwareStatus::ErrToken,
|
||||
2 => FirmwareStatus::ErrChecksum,
|
||||
3 => FirmwareStatus::ErrSize,
|
||||
4 => FirmwareStatus::ErrUnknownChunk,
|
||||
5 => FirmwareStatus::ErrRead,
|
||||
0 => Self::Ok,
|
||||
1 => Self::ErrToken,
|
||||
2 => Self::ErrChecksum,
|
||||
3 => Self::ErrSize,
|
||||
4 => Self::ErrUnknownChunk,
|
||||
5 => Self::ErrRead,
|
||||
_ => return Err(Error::new("Unknown firmware status code")),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum UpdateStatus {
|
||||
MCU,
|
||||
FPGA,
|
||||
@ -514,15 +708,27 @@ pub enum UpdateStatus {
|
||||
Err,
|
||||
}
|
||||
|
||||
impl Display for UpdateStatus {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
UpdateStatus::MCU => "Microcontroller",
|
||||
UpdateStatus::FPGA => "FPGA",
|
||||
UpdateStatus::Bootloader => "Bootloader",
|
||||
UpdateStatus::Done => "Done",
|
||||
UpdateStatus::Err => "Error",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for UpdateStatus {
|
||||
type Error = Error;
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
1 => UpdateStatus::MCU,
|
||||
2 => UpdateStatus::FPGA,
|
||||
3 => UpdateStatus::Bootloader,
|
||||
0x80 => UpdateStatus::Done,
|
||||
0xFF => UpdateStatus::Err,
|
||||
1 => Self::MCU,
|
||||
2 => Self::FPGA,
|
||||
3 => Self::Bootloader,
|
||||
0x80 => Self::Done,
|
||||
0xFF => Self::Err,
|
||||
_ => return Err(Error::new("Unknown update status code")),
|
||||
})
|
||||
}
|
||||
@ -540,7 +746,8 @@ macro_rules! get_config {
|
||||
|
||||
macro_rules! get_setting {
|
||||
($sc64:ident, $setting:ident) => {{
|
||||
#[allow(irrefutable_let_patterns)] // TODO: is there another way to ignore this warning?
|
||||
// Note: remove 'allow(irrefutable_let_patterns)' below when more settings are added
|
||||
#[allow(irrefutable_let_patterns)]
|
||||
if let Setting::$setting(value) = $sc64.command_setting_get(SettingId::$setting)? {
|
||||
Ok(value)
|
||||
} else {
|
||||
|
@ -61,15 +61,3 @@ pub fn vec_from_datetime(datetime: DateTime<Local>) -> Result<Vec<u8>, Error> {
|
||||
let day = bcd_from_u8(datetime.day() as u8);
|
||||
Ok(vec![weekday, hour, minute, second, 0, year, month, day])
|
||||
}
|
||||
|
||||
pub fn file_open_and_check_length(
|
||||
path: &str,
|
||||
max_length: usize,
|
||||
) -> Result<(std::fs::File, usize), Error> {
|
||||
let file = std::fs::File::open(path)?;
|
||||
let length = file.metadata()?.len() as usize;
|
||||
if length > max_length {
|
||||
return Err(Error::new("File size is too big"));
|
||||
}
|
||||
Ok((file, length))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user