mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-11-15 17:00:38 +00:00
Compare commits
1351 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3798dbcb47 | ||
|
|
2a70a9a20e | ||
|
|
6f92f85f5d | ||
|
|
b7851601a4 | ||
|
|
cb2a96c413 | ||
|
|
69c0843a0d | ||
|
|
0171ea9da7 | ||
|
|
e493af25e0 | ||
|
|
62427576e9 | ||
|
|
e80ea00872 | ||
|
|
ce1cd15978 | ||
|
|
2a604bd73a | ||
|
|
bcc0a5bd1e | ||
|
|
32d9e8a09d | ||
|
|
d0f5ac322d | ||
|
|
05c86e4c24 | ||
|
|
1d76967a08 | ||
|
|
b9ffd61252 | ||
|
|
b4e1f6b729 | ||
|
|
451fc0b394 | ||
|
|
79ca9f617e | ||
|
|
a602686551 | ||
|
|
4083aed12f | ||
|
|
717de3608c | ||
|
|
88aae0dd26 | ||
|
|
499fc44004 | ||
|
|
07325be930 | ||
|
|
481020bd08 | ||
|
|
5642f601a3 | ||
|
|
b42a12f051 | ||
|
|
a5879b46f1 | ||
|
|
9a14cc2620 | ||
|
|
a777df13db | ||
|
|
78d7bc310f | ||
|
|
ae3978241c | ||
|
|
390e39ef30 | ||
|
|
5ef595e01a | ||
|
|
371623e3fa | ||
|
|
7eb669cef3 | ||
|
|
4eac10c15c | ||
|
|
495368f28d | ||
|
|
a2d15f301b | ||
|
|
d3c9849908 | ||
|
|
249c51933b | ||
|
|
cf586f7af2 | ||
|
|
b805c91cb3 | ||
|
|
2a0e8e2eb0 | ||
|
|
49aa6895b6 | ||
|
|
3f71628600 | ||
|
|
f729eae48a | ||
|
|
f6877d82cd | ||
|
|
9ed671f595 | ||
|
|
a2b9db3afd | ||
|
|
49ea4302b6 | ||
|
|
59f76b1036 | ||
|
|
6fa3631c80 | ||
|
|
b24f96ac0f | ||
|
|
201af08f5f | ||
|
|
b9b1b24f67 | ||
|
|
d28a88e193 | ||
|
|
c02698edcd | ||
|
|
41bfe8edce | ||
|
|
9c66f6ef57 | ||
|
|
3a6ee3a37c | ||
|
|
7227f01ebc | ||
|
|
2bf686675e | ||
|
|
c179cf410b | ||
|
|
ab4bb4cca3 | ||
|
|
2462305e0b | ||
|
|
e383771fb6 | ||
|
|
bba9bfcbd6 | ||
|
|
94f86fe1d1 | ||
|
|
63c13254d7 | ||
|
|
7d6092452f | ||
|
|
6c2098c5d2 | ||
|
|
70bef7d6c9 | ||
|
|
87df32284d | ||
|
|
fda2e39296 | ||
|
|
fe58061a2d | ||
|
|
9a59f7c540 | ||
|
|
25ab9acb57 | ||
|
|
ecd4e7d1df | ||
|
|
024200ebc4 | ||
|
|
ad9828de3c | ||
|
|
d6e974a3ab | ||
|
|
c482160b5b | ||
|
|
d9a50d0c9e | ||
|
|
7acb97e78e | ||
|
|
c409c47ae5 | ||
|
|
2caab2c9cf | ||
|
|
0feb4130fe | ||
|
|
c90dcd7d59 | ||
|
|
2884dcab5b | ||
|
|
ff69b83e05 | ||
|
|
2a2a2a46f9 | ||
|
|
cb8d2885aa | ||
|
|
3e55071d41 | ||
|
|
af568d6a57 | ||
|
|
8f746824d6 | ||
|
|
334ab130da | ||
|
|
b8966f5e37 | ||
|
|
096f9d4b96 | ||
|
|
b6c6296c29 | ||
|
|
8173806537 | ||
|
|
af12310504 | ||
|
|
5694cdf9a9 | ||
|
|
dab850103b | ||
|
|
c646141bdf | ||
|
|
ca02ed6b09 | ||
|
|
5b22902d03 | ||
|
|
4e70a7665c | ||
|
|
650402df39 | ||
|
|
fead0d781c | ||
|
|
3896b75b3b | ||
|
|
ea2a1daadf | ||
|
|
5968a367e8 | ||
|
|
b439cc3e35 | ||
|
|
3675a8ac75 | ||
|
|
e16b485a7b | ||
|
|
b51879bbec | ||
|
|
f26c5bfebc | ||
|
|
4d5e7eb755 | ||
|
|
7d14681fc9 | ||
|
|
0e5380ad1a | ||
|
|
2494b44647 | ||
|
|
3d33f44eca | ||
|
|
5f9eaef9f2 | ||
|
|
b0a9464d2b | ||
|
|
5d4453fb1e | ||
|
|
ca3ebf33a8 | ||
|
|
853e2ff5c0 | ||
|
|
967d9ffe87 | ||
|
|
27a93fa86a | ||
|
|
770f6f75d5 | ||
|
|
2a700dee9c | ||
|
|
c88ea9ce30 | ||
|
|
45956e9128 | ||
|
|
baf68f3f81 | ||
|
|
9d63be5cc7 | ||
|
|
c78bde1fb2 | ||
|
|
574b993985 | ||
|
|
7485a43364 | ||
|
|
0363b1dd96 | ||
|
|
94658ae225 | ||
|
|
76c5dc40e3 | ||
|
|
6ba4a5b3ab | ||
|
|
18b3152a6c | ||
|
|
3412bbf33a | ||
|
|
278bfafae5 | ||
|
|
b4be581879 | ||
|
|
39a31b3a97 | ||
|
|
2d6e9d7875 | ||
|
|
f3d6adbe1d | ||
|
|
0350d8f58f | ||
|
|
2aca55a895 | ||
|
|
4772768c1f | ||
|
|
adfc2cde84 | ||
|
|
6cab41b58e | ||
|
|
adedc502e3 | ||
|
|
388567c9c6 | ||
|
|
c0872cb97b | ||
|
|
e2b2f4d8a5 | ||
|
|
6a7d6e16b7 | ||
|
|
f6f5818767 | ||
|
|
fe0768dad7 | ||
|
|
d6edc33dd2 | ||
|
|
d78c1eb9ed | ||
|
|
15794e07f7 | ||
|
|
d8e4f963c0 | ||
|
|
f93d254a64 | ||
|
|
25f50d19e2 | ||
|
|
752f5d31d4 | ||
|
|
83407133d6 | ||
|
|
742c991deb | ||
|
|
3caf65822f | ||
|
|
0eb5da5fd4 | ||
|
|
337d4c0fed | ||
|
|
5ffc9fef43 | ||
|
|
1a10e10fd6 | ||
|
|
1997346988 | ||
|
|
0ed13167ba | ||
|
|
285f3a267e | ||
|
|
2969324015 | ||
|
|
72dac4d6e2 | ||
|
|
ed72df79a6 | ||
|
|
bf852ef038 | ||
|
|
e31be83832 | ||
|
|
0b99602bc7 | ||
|
|
3b2b4bc525 | ||
|
|
bd8c008610 | ||
|
|
0491133329 | ||
|
|
4b20273d42 | ||
|
|
a4f844cd6a | ||
|
|
8fe2b51d6f | ||
|
|
c819be57a5 | ||
|
|
040ad65a5e | ||
|
|
f9437314cb | ||
|
|
ebf67d39cf | ||
|
|
ca2a289a3f | ||
|
|
cd49a64c2b | ||
|
|
b547792799 | ||
|
|
2f72349e7b | ||
|
|
8f82d3a28d | ||
|
|
7a9f5d1fa1 | ||
|
|
b8d4adf0e7 | ||
|
|
d08427f1ba | ||
|
|
12e1086838 | ||
|
|
274919fd6a | ||
|
|
096f1d9534 | ||
|
|
5ab48845e3 | ||
|
|
4acd2d9fd6 | ||
|
|
0458489a4f | ||
|
|
9c1d2e7b9c | ||
|
|
ba371d971d | ||
|
|
9828119bdd | ||
|
|
d72c7c9cc0 | ||
|
|
eb2f5babd9 | ||
|
|
872fdc2a10 | ||
|
|
845a0cb998 | ||
|
|
3f3f73a56f | ||
|
|
4bb670ac15 | ||
|
|
9e721c5c43 | ||
|
|
9ffa2a5e23 | ||
|
|
e5e279a48d | ||
|
|
f7ac1073b0 | ||
|
|
a8c060ff26 | ||
|
|
2fdecebe66 | ||
|
|
cdc55e721e | ||
|
|
b55a932b2b | ||
|
|
8e2f519e15 | ||
|
|
5b3ff31f85 | ||
|
|
c623c2b1f6 | ||
|
|
97b04b26a3 | ||
|
|
5ccfde54f5 | ||
|
|
9df08780db | ||
|
|
e01537c2f4 | ||
|
|
154be08a53 | ||
|
|
e3927ca3e6 | ||
|
|
fb711001e9 | ||
|
|
52edf9d20c | ||
|
|
62b2fd3325 | ||
|
|
716abed8b8 | ||
|
|
b6ab8a19be | ||
|
|
1d5514b29d | ||
|
|
32dd30adab | ||
|
|
2ed0c1f732 | ||
|
|
4fecf8b498 | ||
|
|
c9c31af8ea | ||
|
|
e604ef314b | ||
|
|
e3d3fe7c1a | ||
|
|
280e65775a | ||
|
|
33958d37fd | ||
|
|
dc773dba5e | ||
|
|
cdb74a9e26 | ||
|
|
600b0a6dbd | ||
|
|
3f82312943 | ||
|
|
08e0aa5b77 | ||
|
|
3c6cfef070 | ||
|
|
11803aa34c | ||
|
|
2b472c6f37 | ||
|
|
a914ee12c3 | ||
|
|
49d3d9c096 | ||
|
|
0df6378713 | ||
|
|
c0fd4d1334 | ||
|
|
c87db265dd | ||
|
|
d3f5dccf5b | ||
|
|
61ea4b2668 | ||
|
|
355aa4b94c | ||
|
|
ca95e96402 | ||
|
|
1e27b5d720 | ||
|
|
11c4097cba | ||
|
|
12ec3678b8 | ||
|
|
7df083c0b2 | ||
|
|
c74942362c | ||
|
|
c61aea7eac | ||
|
|
8356aed5b0 | ||
|
|
3ef7b5860d | ||
|
|
276448a914 | ||
|
|
9993e574a0 | ||
|
|
81c47b297d | ||
|
|
5bb8557555 | ||
|
|
c4524d20f0 | ||
|
|
4c9ff19cf3 | ||
|
|
451e89afff | ||
|
|
826df09a3e | ||
|
|
dfa39947ce | ||
|
|
88d182cfd1 | ||
|
|
d2b8a4aa2c | ||
|
|
5812d197a5 | ||
|
|
6810dce2ed | ||
|
|
3e2bba866d | ||
|
|
4035d9db86 | ||
|
|
1cec6fa918 | ||
|
|
b82d464b12 | ||
|
|
3f5b3d10fd | ||
|
|
af5d161fe4 | ||
|
|
e2e19db294 | ||
|
|
0b055a563e | ||
|
|
4d362bb791 | ||
|
|
0670231606 | ||
|
|
ea2bfbcb1f | ||
|
|
28f4808dbb | ||
|
|
0debcaa8d1 | ||
|
|
f1558587c3 | ||
|
|
d39c2e81d0 | ||
|
|
07fe4415fe | ||
|
|
00ee106f4a | ||
|
|
a2c7588e1c | ||
|
|
5898bec0e9 | ||
|
|
8f71041e7e | ||
|
|
4fd89ced29 | ||
|
|
55522bf59b | ||
|
|
de4566ec26 | ||
|
|
078cd88bcd | ||
|
|
e9902f0907 | ||
|
|
e5679b9152 | ||
|
|
7d29c3d7d9 | ||
|
|
1212603414 | ||
|
|
9929199f31 | ||
|
|
a7a622e118 | ||
|
|
225922e5bc | ||
|
|
465ece5ea3 | ||
|
|
8de1fbc52f | ||
|
|
29620c0c4f | ||
|
|
3a3b20df11 | ||
|
|
85aefc8d90 | ||
|
|
52ad2ff89c | ||
|
|
8356fc5970 | ||
|
|
e61aec89ea | ||
|
|
363ffee738 | ||
|
|
ce2ddf1636 | ||
|
|
dd29c8245a | ||
|
|
fdee2103ad | ||
|
|
b72fcfc60b | ||
|
|
3f6e5c2383 | ||
|
|
2ae3291a30 | ||
|
|
c216129b37 | ||
|
|
2fdc2407dd | ||
|
|
5205fac739 | ||
|
|
8a51f5d8ab | ||
|
|
9c7993f8f1 | ||
|
|
8f49e190d8 | ||
|
|
e11a08b587 | ||
|
|
5f0fdcfd8d | ||
|
|
1053ff7f23 | ||
|
|
de6a08539a | ||
|
|
219392e3a0 | ||
|
|
2ade7495c7 | ||
|
|
1acd448838 | ||
|
|
a8ed7a38f8 | ||
|
|
5208fc3e33 | ||
|
|
3dfbd23867 | ||
|
|
994a366770 | ||
|
|
d1b061fac5 | ||
|
|
6e73c93c89 | ||
|
|
5544ab0b8a | ||
|
|
8bec2087eb | ||
|
|
50e27f926d | ||
|
|
ecc88316bd | ||
|
|
7689c6dd07 | ||
|
|
bec0aeb04c | ||
|
|
2f8ee5545e | ||
|
|
015ad062ac | ||
|
|
ae6608e2a7 | ||
|
|
ca78898e93 | ||
|
|
a6ee92da32 | ||
|
|
b2eb0b5e67 | ||
|
|
685fadbfdf | ||
|
|
638934e6b0 | ||
|
|
cd2e170175 | ||
|
|
68b5436f59 | ||
|
|
b2f074048d | ||
|
|
3147974879 | ||
|
|
1a53c093b8 | ||
|
|
e4d26d4d81 | ||
|
|
b3e25bb6a6 | ||
|
|
37d5b79b96 | ||
|
|
75fef90a14 | ||
|
|
9389797448 | ||
|
|
8b7112d12f | ||
|
|
4add8f3e64 | ||
|
|
9e6c6af985 | ||
|
|
c82b56d084 | ||
|
|
ea4c51b9f2 | ||
|
|
463c3ba50d | ||
|
|
92356903cc | ||
|
|
82ed800a32 | ||
|
|
81cd284a80 | ||
|
|
1eba7403a3 | ||
|
|
b47af580e3 | ||
|
|
cff722ba67 | ||
|
|
810338d011 | ||
|
|
76910047e7 | ||
|
|
a8e53c2d07 | ||
|
|
bb61a1b795 | ||
|
|
1d83d40b9b | ||
|
|
3459c99b29 | ||
|
|
d102e2fc0b | ||
|
|
c7a932c07f | ||
|
|
1c334dfb1b | ||
|
|
a6656fd5e6 | ||
|
|
1e1bf2e27c | ||
|
|
de5794b3ae | ||
|
|
f4192416f7 | ||
|
|
70fe830241 | ||
|
|
e8394d6665 | ||
|
|
2b64d34e7a | ||
|
|
1909851ed4 | ||
|
|
9ce074529a | ||
|
|
e2581fc423 | ||
|
|
f75bc21d54 | ||
|
|
8844632fc7 | ||
|
|
2f3ecb88f0 | ||
|
|
6e93aa65b6 | ||
|
|
ca264ce91e | ||
|
|
b339f213b8 | ||
|
|
93a6926b43 | ||
|
|
5e758d4b38 | ||
|
|
756d5fe03c | ||
|
|
ff67caee25 | ||
|
|
234f865234 | ||
|
|
2d380770f5 | ||
|
|
3f23df868f | ||
|
|
d150bde546 | ||
|
|
d108a815d7 | ||
|
|
7dfb015dd5 | ||
|
|
5c973bac96 | ||
|
|
c6b96d82dc | ||
|
|
9f2765edd0 | ||
|
|
b77e866be4 | ||
|
|
3995017174 | ||
|
|
714c8d4901 | ||
|
|
9e68cf7abe | ||
|
|
1875adfe5f | ||
|
|
ebe4ff7426 | ||
|
|
e9eb3eb4be | ||
|
|
c99f86ee95 | ||
|
|
bb7b1c50a1 | ||
|
|
d56c8a3d83 | ||
|
|
c5f732584b | ||
|
|
3e0a6dbf32 | ||
|
|
b91c060dad | ||
|
|
f0a0fc3137 | ||
|
|
ad3df18b24 | ||
|
|
d089af71a4 | ||
|
|
5f9f037430 | ||
|
|
939a2bab41 | ||
|
|
2d969048e3 | ||
|
|
dc86ed1456 | ||
|
|
a2b392234d | ||
|
|
f56014b8a6 | ||
|
|
3a485d34a1 | ||
|
|
e1da5f192c | ||
|
|
c15809dd72 | ||
|
|
89a67d1bd2 | ||
|
|
c4c6709478 | ||
|
|
5e1f6ac4b3 | ||
|
|
8f8d86d0bb | ||
|
|
ecca11e2f3 | ||
|
|
527b09c6bc | ||
|
|
69b6595434 | ||
|
|
59b918c75f | ||
|
|
344132b50a | ||
|
|
a35e58e9cc | ||
|
|
4300d94858 | ||
|
|
ee85a84d8f | ||
|
|
2959d82231 | ||
|
|
5dfe6aefb0 | ||
|
|
7f7d0f84ef | ||
|
|
4bfaf36e24 | ||
|
|
695fc4b0af | ||
|
|
f16ed9742f | ||
|
|
6bfe68b8cf | ||
|
|
2e5c5b3c79 | ||
|
|
2a6e193756 | ||
|
|
65a86d87f0 | ||
|
|
d7131dfa4c | ||
|
|
a228b16314 | ||
|
|
b8c9048ac6 | ||
|
|
0b54350d59 | ||
|
|
59eddb7129 | ||
|
|
1096232f9d | ||
|
|
4b39ee8d00 | ||
|
|
27a9b12bbf | ||
|
|
11319c3341 | ||
|
|
4de061e0d1 | ||
|
|
258e894c30 | ||
|
|
8658eaf4e0 | ||
|
|
6d341abc75 | ||
|
|
4821d63b4a | ||
|
|
0617bf3170 | ||
|
|
425d13a2ac | ||
|
|
8a1e90f537 | ||
|
|
ddc00948ed | ||
|
|
c96ee81f06 | ||
|
|
f6201e2c35 | ||
|
|
d63fcfcf7f | ||
|
|
dcc50c4c23 | ||
|
|
60e46e580d | ||
|
|
0638998773 | ||
|
|
ad3113987b | ||
|
|
f2f0d0f2e7 | ||
|
|
f7fd2f6f65 | ||
|
|
281c18e21d | ||
|
|
1a9c8b5392 | ||
|
|
831a272f4b | ||
|
|
f47e68b9ba | ||
|
|
eaabd2ee77 | ||
|
|
a269edd6ea | ||
|
|
3bb501ce0c | ||
|
|
d0006a4653 | ||
|
|
c48a1394bc | ||
|
|
fcf7fcfd44 | ||
|
|
cca08e7103 | ||
|
|
b908732f5c | ||
|
|
4ee4a6e85f | ||
|
|
7990f11bab | ||
|
|
427f7c9136 | ||
|
|
f938f1593d | ||
|
|
480a1834ed | ||
|
|
df5acc648c | ||
|
|
10569e6aa1 | ||
|
|
e5103b4830 | ||
|
|
513646fc53 | ||
|
|
27f723bf2f | ||
|
|
6511ecb5ad | ||
|
|
7bf6e6f344 | ||
|
|
d3e340ae39 | ||
|
|
22167a9617 | ||
|
|
48bea55133 | ||
|
|
42239919e5 | ||
|
|
4840d9df6e | ||
|
|
1378b87ea6 | ||
|
|
6b48164b97 | ||
|
|
50b09aa249 | ||
|
|
ecbd147f6d | ||
|
|
c13df79848 | ||
|
|
48bcc1de5b | ||
|
|
d082f5de25 | ||
|
|
c2321669b6 | ||
|
|
e382635b85 | ||
|
|
b4e02d7f8c | ||
|
|
0dc8a0b6c0 | ||
|
|
171834986d | ||
|
|
97af686b73 | ||
|
|
7d627ffc6f | ||
|
|
d924f25eb2 | ||
|
|
abd8ef3b1f | ||
|
|
e9b8dba3a6 | ||
|
|
aaff7e7332 | ||
|
|
2fa00d1a16 | ||
|
|
8a2a2ecd12 | ||
|
|
98517bdd8c | ||
|
|
dfe76f7e7e | ||
|
|
a2e24b1f5f | ||
|
|
6a4e76e245 | ||
|
|
1ebec97e0e | ||
|
|
b9f03b02f0 | ||
|
|
2c06760778 | ||
|
|
3f0ee70b71 | ||
|
|
05b34f158b | ||
|
|
e08db935b4 | ||
|
|
47da068bd3 | ||
|
|
9de669606d | ||
|
|
a02d50e61d | ||
|
|
069dab7fba | ||
|
|
d65edadbbb | ||
|
|
6307a51c0a | ||
|
|
d27c602bdf | ||
|
|
59fe9cb113 | ||
|
|
b4b7ba75cb | ||
|
|
d02e0c16c4 | ||
|
|
4aa0eb54b7 | ||
|
|
c4f73abcb6 | ||
|
|
73dec51a4d | ||
|
|
b7c3c094ae | ||
|
|
97702ab3d5 | ||
|
|
a9b5fe706c | ||
|
|
198d48610c | ||
|
|
c0e0344914 | ||
|
|
d49b2f1b60 | ||
|
|
ec7172466b | ||
|
|
d75a512ead | ||
|
|
af8b6906b6 | ||
|
|
96bc301e6d | ||
|
|
c10749f34d | ||
|
|
0e4235c8ba | ||
|
|
b2d31bb87e | ||
|
|
3ee88793da | ||
|
|
ad0f01c3ef | ||
|
|
63a96a7cf8 | ||
|
|
b8d8ca9a5c | ||
|
|
6b0fca31ae | ||
|
|
c147b12b10 | ||
|
|
7884b0b775 | ||
|
|
9f3c263328 | ||
|
|
b830ce3d73 | ||
|
|
e8b7f33bf0 | ||
|
|
eae75c5943 | ||
|
|
c1f5ebcae1 | ||
|
|
acb8e98fe2 | ||
|
|
a24a92e97c | ||
|
|
f2d2fe97bb | ||
|
|
705252eec7 | ||
|
|
69ae0456f0 | ||
|
|
f07bd07cce | ||
|
|
9db2d2ca05 | ||
|
|
85248b0135 | ||
|
|
8663edc7df | ||
|
|
b9cc9e40fd | ||
|
|
5ce085ad2b | ||
|
|
d24f8a2fce | ||
|
|
a73e830209 | ||
|
|
146fb42fd0 | ||
|
|
5b54ed719e | ||
|
|
02d011228e | ||
|
|
07adfd20a3 | ||
|
|
1d00abd6cc | ||
|
|
36c2cc2f15 | ||
|
|
27cf01ce84 | ||
|
|
1e94163693 | ||
|
|
db1f94775b | ||
|
|
ce394eb4e9 | ||
|
|
15109bed88 | ||
|
|
8a18c8de34 | ||
|
|
2f093a80c7 | ||
|
|
5df1af9c17 | ||
|
|
d5ab37a5a6 | ||
|
|
ed9ecf0da9 | ||
|
|
4fc17197ca | ||
|
|
94a9cf0c3f | ||
|
|
1d9e12183f | ||
|
|
aaa8bbe709 | ||
|
|
805d1b9ad8 | ||
|
|
d24d3e5adb | ||
|
|
d3a7d552c3 | ||
|
|
9f1c5e2bd2 | ||
|
|
1ec4ea8354 | ||
|
|
e4fab52489 | ||
|
|
e029fa4833 | ||
|
|
2c3f229a6a | ||
|
|
3558d3532c | ||
|
|
ad5314dc22 | ||
|
|
eaa5bb5662 | ||
|
|
fcdc703595 | ||
|
|
ef9fa562ab | ||
|
|
ac285c48fe | ||
|
|
e57ad65210 | ||
|
|
0fc3a30422 | ||
|
|
49f6704694 | ||
|
|
6df712c538 | ||
|
|
bda3076a30 | ||
|
|
89e8e7c706 | ||
|
|
c10835d221 | ||
|
|
a6801350ea | ||
|
|
c7a5a62041 | ||
|
|
749b35e5cb | ||
|
|
3c520d33eb | ||
|
|
635170a051 | ||
|
|
9619311d11 | ||
|
|
4bee14835c | ||
|
|
f44059e16a | ||
|
|
bfb206a06d | ||
|
|
6b7b05eb31 | ||
|
|
a9a2a04332 | ||
|
|
50459645dd | ||
|
|
564386038f | ||
|
|
9f229aad47 | ||
|
|
f6f6ea21b4 | ||
|
|
ec416d5631 | ||
|
|
335cbca8fb | ||
|
|
ab8d5ec475 | ||
|
|
ef9f050fbf | ||
|
|
46b1838d72 | ||
|
|
2b9a527743 | ||
|
|
cb41f8a8a6 | ||
|
|
fcf003af7d | ||
|
|
350924545c | ||
|
|
ae6bf0f2da | ||
|
|
7395d9842c | ||
|
|
741a1e13d0 | ||
|
|
447064afc1 | ||
|
|
a443a6833a | ||
|
|
160239eba7 | ||
|
|
04fb3fd363 | ||
|
|
ba60c9b516 | ||
|
|
f8ddfcf5ae | ||
|
|
c379bdcadc | ||
|
|
575caeda92 | ||
|
|
0d4dcf4a60 | ||
|
|
ddb14a69d0 | ||
|
|
f1ace61076 | ||
|
|
0f5f866cd2 | ||
|
|
bd866c5b05 | ||
|
|
07ea1abbf8 | ||
|
|
6853376f60 | ||
|
|
0e67f0bff4 | ||
|
|
6035c19f47 | ||
|
|
592a3d3505 | ||
|
|
2c092ed665 | ||
|
|
2bd4a00df4 | ||
|
|
f0639b28b9 | ||
|
|
775bab6656 | ||
|
|
c82d865cd5 | ||
|
|
0b95b9509a | ||
|
|
d8185e3970 | ||
|
|
b3a3766d77 | ||
|
|
3681682fe8 | ||
|
|
12c292d038 | ||
|
|
5af4de2878 | ||
|
|
2e7f18c407 | ||
|
|
48248b2832 | ||
|
|
c6082fa7c7 | ||
|
|
3af19510a8 | ||
|
|
2a12b8132f | ||
|
|
3daec985ba | ||
|
|
4b79ee103c | ||
|
|
077abd590a | ||
|
|
cab890b1d9 | ||
|
|
d3f0858ec4 | ||
|
|
97896b91d7 | ||
|
|
ca354b3b7f | ||
|
|
d035211a64 | ||
|
|
75728a4d1d | ||
|
|
265789ea84 | ||
|
|
4577311432 | ||
|
|
963c333c2e | ||
|
|
6eff6cf3b6 | ||
|
|
c9794691bb | ||
|
|
9574232719 | ||
|
|
676bf584d6 | ||
|
|
0a4fd01ede | ||
|
|
6cb4023498 | ||
|
|
8f775dbb3b | ||
|
|
91785eab1f | ||
|
|
4f8751667a | ||
|
|
ad7508c464 | ||
|
|
97a9e141ae | ||
|
|
842a0c3daf | ||
|
|
b6acde9145 | ||
|
|
b97dbc2ac5 | ||
|
|
e03852bd7e | ||
|
|
0db5de204e | ||
|
|
5fe3f14419 | ||
|
|
ffe1d9a82d | ||
|
|
11d6560290 | ||
|
|
e29d8e6728 | ||
|
|
918e81a4cb | ||
|
|
ad0ddede38 | ||
|
|
e969b12884 | ||
|
|
7a5475255f | ||
|
|
0ffd436de8 | ||
|
|
4f685187e9 | ||
|
|
2b356b37b1 | ||
|
|
7c56268eb1 | ||
|
|
970a54e6e3 | ||
|
|
e35ddf0780 | ||
|
|
bf35b7c10b | ||
|
|
4026b8ca09 | ||
|
|
6d2e2d8c3b | ||
|
|
a2b08157cc | ||
|
|
0108ecf4c1 | ||
|
|
4921da0bb5 | ||
|
|
0c836bb3b1 | ||
|
|
a8e41c1505 | ||
|
|
f67e1c9d2b | ||
|
|
f0ce58a79e | ||
|
|
b7f782c1b7 | ||
|
|
5e39e169b2 | ||
|
|
104c5ccad4 | ||
|
|
d15b4d7d23 | ||
|
|
60e6a75d5e | ||
|
|
484415d0e5 | ||
|
|
0ef9b447c4 | ||
|
|
8f64e2defd | ||
|
|
fbdadce129 | ||
|
|
d3e7abfaa3 | ||
|
|
b2279e97b2 | ||
|
|
7cf969336f | ||
|
|
f73d48166a | ||
|
|
53af618fe4 | ||
|
|
5d2cf58477 | ||
|
|
664e7dce28 | ||
|
|
14a8f00864 | ||
|
|
0b889fdc06 | ||
|
|
e336efc149 | ||
|
|
4cd52162eb | ||
|
|
eab9fff711 | ||
|
|
d4f3511060 | ||
|
|
ed12bbb35c | ||
|
|
aa4629fe99 | ||
|
|
1950f23cf4 | ||
|
|
ca7c88cef6 | ||
|
|
10848e6c51 | ||
|
|
f5d0f065c1 | ||
|
|
17b0573b0b | ||
|
|
7f1d843d96 | ||
|
|
cc4837c1d1 | ||
|
|
588ee5bfe4 | ||
|
|
e9b1b2750f | ||
|
|
1d6fa06e97 | ||
|
|
2c22924239 | ||
|
|
eb01dd1e25 | ||
|
|
0a3cb79b1c | ||
|
|
da9eace8cc | ||
|
|
526a02b8b6 | ||
|
|
658c7a1c3b | ||
|
|
af84474795 | ||
|
|
42913c6732 | ||
|
|
2cdf544518 | ||
|
|
652ec58238 | ||
|
|
f8531daa5c | ||
|
|
e9e89b0b43 | ||
|
|
55e788a894 | ||
|
|
b28bb93ccb | ||
|
|
367aab0f83 | ||
|
|
9dcf3b9e0a | ||
|
|
3c514110ce | ||
|
|
c9b0c2dace | ||
|
|
d575b6977e | ||
|
|
a00e6a5e2d | ||
|
|
1b9ae83e8c | ||
|
|
8b91eb1caf | ||
|
|
2a6a7b5e9a | ||
|
|
a85943866e | ||
|
|
797fb519c1 | ||
|
|
3ba9d56363 | ||
|
|
04cd4e4056 | ||
|
|
348e170654 | ||
|
|
f5a4ca6276 | ||
|
|
672c010aa7 | ||
|
|
2459d88951 | ||
|
|
350d1c8d31 | ||
|
|
98a3842a3e | ||
|
|
b52a4469ee | ||
|
|
e3143e21ba | ||
|
|
1bf2181fd3 | ||
|
|
1460635aab | ||
|
|
935ec00c86 | ||
|
|
473b6de09b | ||
|
|
ba75f2ac2c | ||
|
|
a230b39fbc | ||
|
|
8e963ac62a | ||
|
|
eaaa89847d | ||
|
|
ef76166978 | ||
|
|
72912586a1 | ||
|
|
fb241a4036 | ||
|
|
368c8b0533 | ||
|
|
4010325e65 | ||
|
|
11dd75ad95 | ||
|
|
d0480a1311 | ||
|
|
2be33b845d | ||
|
|
2ad42e3a0f | ||
|
|
5d1f83800b | ||
|
|
30e89a7943 | ||
|
|
61f5dc4cf2 | ||
|
|
d056c179ed | ||
|
|
b9c4bfc67e | ||
|
|
6ab5ee0ae0 | ||
|
|
94c1a86702 | ||
|
|
af6dd6a7fc | ||
|
|
45d4926d4c | ||
|
|
ce016c5eb0 | ||
|
|
2225c1f2d8 | ||
|
|
2d0c0d5845 | ||
|
|
60f1756cbb | ||
|
|
738a1d250a | ||
|
|
c8e65e1e30 | ||
|
|
ecb09ce6f2 | ||
|
|
72a1484a71 | ||
|
|
de31c10c7c | ||
|
|
84483c229c | ||
|
|
b7e35a8fa8 | ||
|
|
8c190a26a5 | ||
|
|
2e5705125b | ||
|
|
daaa92def0 | ||
|
|
86ec97678e | ||
|
|
d74b82dc55 | ||
|
|
821ade2006 | ||
|
|
3ee999dccd | ||
|
|
f91bda51e1 | ||
|
|
47255fc331 | ||
|
|
f7488bed04 | ||
|
|
a94b3a82c4 | ||
|
|
1c8f64f5e6 | ||
|
|
a015e4df3f | ||
|
|
2489a49924 | ||
|
|
d418fbd60d | ||
|
|
cc2e7704e6 | ||
|
|
a980a56dfa | ||
|
|
da588e3bc0 | ||
|
|
8b6f45d9b7 | ||
|
|
9bdfe33baf | ||
|
|
13d338f615 | ||
|
|
36a8641c8d | ||
|
|
7bde6f99fc | ||
|
|
45428ce991 | ||
|
|
8eb5898ef6 | ||
|
|
556e1c972c | ||
|
|
df8f3e7122 | ||
|
|
da1d575806 | ||
|
|
c4d40a1dde | ||
|
|
912de926d0 | ||
|
|
c60fc1e31f | ||
|
|
386e607887 | ||
|
|
94c9b8d057 | ||
|
|
5f81275b10 | ||
|
|
37c484411b | ||
|
|
ad1f890742 | ||
|
|
2924529fa7 | ||
|
|
ebe675cae1 | ||
|
|
a40c666775 | ||
|
|
2ea060d3b3 | ||
|
|
f8a67839dd | ||
|
|
2773b12c9b | ||
|
|
929e12f976 | ||
|
|
8fbc6c4662 | ||
|
|
797378e9c8 | ||
|
|
113464a7d1 | ||
|
|
a13c837202 | ||
|
|
48765f1ae5 | ||
|
|
091ee56368 | ||
|
|
015a971147 | ||
|
|
7575a01a28 | ||
|
|
7aa14cafea | ||
|
|
db10721386 | ||
|
|
6112cf8d60 | ||
|
|
50caeba322 | ||
|
|
b3499984b3 | ||
|
|
a5609a7a82 | ||
|
|
8626a87860 | ||
|
|
fa31cd0e98 | ||
|
|
6b6b7c6289 | ||
|
|
baa3b272ab | ||
|
|
d7c4f244cc | ||
|
|
e21d226564 | ||
|
|
6ff6a7d4a4 | ||
|
|
daf19a561c | ||
|
|
26226a75ff | ||
|
|
4473edf476 | ||
|
|
54cf0a6470 | ||
|
|
0eb8f7e538 | ||
|
|
23b72c65ba | ||
|
|
db7d89600c | ||
|
|
67d51ad1d6 | ||
|
|
56b71cf7fe | ||
|
|
e0ec2a521f | ||
|
|
e8589ac7b5 | ||
|
|
21e425acee | ||
|
|
ee316052ae | ||
|
|
3463936ae0 | ||
|
|
5ff947b279 | ||
|
|
fdf2ea84ec | ||
|
|
ad9b001b6a | ||
|
|
1308571c10 | ||
|
|
b8cb3419c1 | ||
|
|
02533cf947 | ||
|
|
bf8bd04dc7 | ||
|
|
1894376ab6 | ||
|
|
c789e4df44 | ||
|
|
884685de4a | ||
|
|
7fda7be457 | ||
|
|
5daf759033 | ||
|
|
589ab0896a | ||
|
|
7645ab978f | ||
|
|
e6497289da | ||
|
|
95a9f4769d | ||
|
|
7917601f7a | ||
|
|
3400238fc5 | ||
|
|
cbd335c6aa | ||
|
|
9feaadd32a | ||
|
|
2ec008b3b7 | ||
|
|
c9a462a1c6 | ||
|
|
ab44aa7e68 | ||
|
|
9b9d3176ec | ||
|
|
5dda3e2c81 | ||
|
|
4b8dc12c98 | ||
|
|
044e4c4c75 | ||
|
|
2149630865 | ||
|
|
3d435a1d7e | ||
|
|
f2d993a958 | ||
|
|
7bf75b8dc5 | ||
|
|
dfbdce1dc8 | ||
|
|
63f5566e65 | ||
|
|
4b0f43f6c0 | ||
|
|
98e1ef7362 | ||
|
|
4e0ffcb3c8 | ||
|
|
a93a46d6c0 | ||
|
|
e163302522 | ||
|
|
838afc35c3 | ||
|
|
3a62bfab57 | ||
|
|
560b3a246c | ||
|
|
4360916750 | ||
|
|
3f31edc1c0 | ||
|
|
bd874d436b | ||
|
|
fbc47425f4 | ||
|
|
c9df6ff059 | ||
|
|
ef6ff85e96 | ||
|
|
bb93792f57 | ||
|
|
1f7ab4fccb | ||
|
|
2535e82618 | ||
|
|
66d80c8523 | ||
|
|
834a17fe82 | ||
|
|
ea85167e06 | ||
|
|
e007a06dda | ||
|
|
02f9e1e935 | ||
|
|
21b7d431a7 | ||
|
|
307b932f87 | ||
|
|
6983d1cd3c | ||
|
|
a193c9bb3c | ||
|
|
aa67c7ad6d | ||
|
|
70fc686249 | ||
|
|
c87a456a46 | ||
|
|
456bb1e95b | ||
|
|
b0b334103d | ||
|
|
78851e3b57 | ||
|
|
850a883e14 | ||
|
|
6e0ced5ffe | ||
|
|
64cd8febaa | ||
|
|
6fbe1cffaa | ||
|
|
601e781ef9 | ||
|
|
d26ad35573 | ||
|
|
b70c510734 | ||
|
|
29a7158488 | ||
|
|
1227ca020a | ||
|
|
cd7e6ff98d | ||
|
|
510a0f77f6 | ||
|
|
6d1dabb1db | ||
|
|
2e337891da | ||
|
|
d49e18a83e | ||
|
|
c14560ec18 | ||
|
|
e3e31cd9f2 | ||
|
|
adf9627045 | ||
|
|
c7b03c7765 | ||
|
|
b5f83215d5 | ||
|
|
96e3fc5533 | ||
|
|
828e7dda03 | ||
|
|
8ef50c30d5 | ||
|
|
cbe1d5b608 | ||
|
|
7123a9e986 | ||
|
|
af5996b972 | ||
|
|
15e947784b | ||
|
|
1b382c201e | ||
|
|
8deca4fb0c | ||
|
|
96c4372609 | ||
|
|
ca7a109037 | ||
|
|
c54f5b3c5d | ||
|
|
652942ab9f | ||
|
|
2cc19a9c36 | ||
|
|
6ffb3f4bbd | ||
|
|
bab4278318 | ||
|
|
013d18a426 | ||
|
|
e94e27910e | ||
|
|
34cea6e74a | ||
|
|
640e160863 | ||
|
|
3100963250 | ||
|
|
2086358762 | ||
|
|
ae273c589d | ||
|
|
a229015fcd | ||
|
|
b001680674 | ||
|
|
e684133f72 | ||
|
|
2b020eecc7 | ||
|
|
78f4d6faca | ||
|
|
f07b7a049e | ||
|
|
544637779c | ||
|
|
38b6427e4e | ||
|
|
e36822a19c | ||
|
|
e1a114976f | ||
|
|
5ba9ab3457 | ||
|
|
36edf9ed54 | ||
|
|
e788ffa851 | ||
|
|
e0d819b88d | ||
|
|
e3b216ad09 | ||
|
|
55d0fd452d | ||
|
|
089fd02b2e | ||
|
|
8cfd820dcd | ||
|
|
67ed9860fe | ||
|
|
ee924e3a34 | ||
|
|
9e5085ba79 | ||
|
|
dbfdbbff20 | ||
|
|
21dc72bf11 | ||
|
|
c8295c4724 | ||
|
|
44b4dd3d1b | ||
|
|
7e543c6acb | ||
|
|
63514d149f | ||
|
|
d7e559749d | ||
|
|
a2be40fcbc | ||
|
|
771df5c372 | ||
|
|
2d3d6348ad | ||
|
|
a2bf15f0bc | ||
|
|
0aa25df88f | ||
|
|
36afb1c0ee | ||
|
|
c9d61906fb | ||
|
|
d8440a01d3 | ||
|
|
74d92312ef | ||
|
|
0dc9823d2b | ||
|
|
9c69737073 | ||
|
|
66187dcf04 | ||
|
|
79943560bc | ||
|
|
b3321f9c9a | ||
|
|
428c269a49 | ||
|
|
e84964a321 | ||
|
|
0394ea5356 | ||
|
|
852b086920 | ||
|
|
b7fceee2b7 | ||
|
|
ecf6c957c9 | ||
|
|
5e0b0070ff | ||
|
|
9934bc31a3 | ||
|
|
7b4711f5cf | ||
|
|
844066815d | ||
|
|
3a6293e696 | ||
|
|
3210b8601d | ||
|
|
181788802d | ||
|
|
4b627ce776 | ||
|
|
8b799bb5c8 | ||
|
|
5bc8336fce | ||
|
|
4f9f8ddcec | ||
|
|
29f1f63ef8 | ||
|
|
48ecdff5f7 | ||
|
|
411acd2d5a | ||
|
|
e6742fe889 | ||
|
|
b6561719a7 | ||
|
|
2bf3d6f9a6 | ||
|
|
b2700b5975 | ||
|
|
95b28874da | ||
|
|
4934ee837c | ||
|
|
3cbfd5cd10 | ||
|
|
da28ce310b | ||
|
|
ac6d93a3b9 | ||
|
|
104028204d | ||
|
|
d3e61b42dd | ||
|
|
6351cabb62 | ||
|
|
ddef42126b | ||
|
|
9dd39a7f02 | ||
|
|
ca7f7e97e7 | ||
|
|
8a40349b0c | ||
|
|
6ec6fa4973 | ||
|
|
f0644710e6 | ||
|
|
34048726ab | ||
|
|
60ce6c9370 | ||
|
|
554fccc236 | ||
|
|
7611c043c3 | ||
|
|
976d793474 | ||
|
|
f0f997fadd | ||
|
|
0ce3c9892d | ||
|
|
9743565285 | ||
|
|
fcfe9e4790 | ||
|
|
be36432296 | ||
|
|
fb725bff19 | ||
|
|
2384cf9f9f | ||
|
|
1261930fd9 | ||
|
|
120de4e49f | ||
|
|
354a51769b | ||
|
|
a9f937baa3 | ||
|
|
1790d82a6e | ||
|
|
261c20e95a | ||
|
|
ed6556b1f0 | ||
|
|
a86af8c32a | ||
|
|
1670ab45a0 | ||
|
|
7dc4750f3b | ||
|
|
b5f366680d | ||
|
|
fa9e9a0b2b | ||
|
|
2239b82a4b | ||
|
|
3b631678f5 | ||
|
|
2b111a9688 | ||
|
|
0bda1f4f88 | ||
|
|
7d50e0e1c5 | ||
|
|
224a4caab0 | ||
|
|
b4689da404 | ||
|
|
af66657399 | ||
|
|
0f3e2d8275 | ||
|
|
d664b6defc | ||
|
|
adbf74a6e0 | ||
|
|
7eb401efed | ||
|
|
ba97381b99 | ||
|
|
3de92de225 | ||
|
|
01a195c8aa | ||
|
|
12d43ef68a | ||
|
|
0df806a6d1 | ||
|
|
f8c713b260 | ||
|
|
4d0122f97c | ||
|
|
7874950a7a | ||
|
|
577afc8947 | ||
|
|
5a3e69e8ed | ||
|
|
67ee595eb4 | ||
|
|
992f8eaeb8 | ||
|
|
dde5a57136 | ||
|
|
ae49950ae6 | ||
|
|
b56ea3a479 | ||
|
|
03d4ea046d | ||
|
|
248741b2c0 | ||
|
|
ef307664e2 | ||
|
|
05c497a5a0 | ||
|
|
f8239780b2 | ||
|
|
ff12bf8205 | ||
|
|
0c3adf055c | ||
|
|
d9b1f5d45f | ||
|
|
5ab3dded30 | ||
|
|
991d9ead27 | ||
|
|
25c0d036f2 | ||
|
|
c06d8151e6 | ||
|
|
0e9ba25637 | ||
|
|
bd7ca7f3a7 | ||
|
|
26146554a3 | ||
|
|
884746a5a1 | ||
|
|
ab0e68110c | ||
|
|
e166d18903 | ||
|
|
2152ba43a8 | ||
|
|
f42c5d04ee | ||
|
|
ea535fa393 | ||
|
|
e6d118e8cc | ||
|
|
35a921e436 | ||
|
|
25cc3e42f3 | ||
|
|
14344756da | ||
|
|
cb270d3185 | ||
|
|
d5cef39f1b | ||
|
|
b782af427e | ||
|
|
6b503a0733 | ||
|
|
4a3741312e | ||
|
|
aed140a684 | ||
|
|
5b93df9703 | ||
|
|
5ae162400c | ||
|
|
f828b57159 | ||
|
|
d0cfc67f07 | ||
|
|
2f48c3110c | ||
|
|
6cdc35c82f | ||
|
|
9214a22cc9 | ||
|
|
5ba1156245 | ||
|
|
cb91cdff1d | ||
|
|
4df6a4e79d | ||
|
|
5b82a48267 | ||
|
|
8f70c50a48 | ||
|
|
5fe4d81fa4 | ||
|
|
7d3addbf0a | ||
|
|
b7d5873eb7 | ||
|
|
4e40cc19d5 | ||
|
|
1bc9316bc1 | ||
|
|
c995ec1dca | ||
|
|
4d2fbbae04 | ||
|
|
2776928946 | ||
|
|
8cc87c6540 | ||
|
|
3c212022aa | ||
|
|
511c4d09e5 | ||
|
|
d7eba27dc5 | ||
|
|
09370618ca | ||
|
|
2197167088 | ||
|
|
b527635fe7 | ||
|
|
695309bc32 | ||
|
|
97b2f68ec7 | ||
|
|
593044dbf3 | ||
|
|
1fcf44fb8d | ||
|
|
a2a472baf9 | ||
|
|
b5b4a50d94 | ||
|
|
f1b5464052 | ||
|
|
2c0224db22 | ||
|
|
1e78eecb40 | ||
|
|
3626faea60 | ||
|
|
a0177f1174 | ||
|
|
db5fe4a2cd | ||
|
|
5716143168 | ||
|
|
2a59b23149 | ||
|
|
bdbec4ed02 | ||
|
|
25193f1805 | ||
|
|
4840c816a2 | ||
|
|
d0a8e3770b | ||
|
|
1cf3d50864 | ||
|
|
d1b98f7d6d | ||
|
|
4bc87ff812 | ||
|
|
e1df11b360 | ||
|
|
34606a4f04 | ||
|
|
c4c5fc4bf6 | ||
|
|
cd87ce5373 | ||
|
|
90fc16b888 | ||
|
|
c2d0b71d22 | ||
|
|
e54473682c | ||
|
|
1c8d64d98c | ||
|
|
a19437f42f | ||
|
|
855e2f2c77 | ||
|
|
bd3cf88123 | ||
|
|
e4578ad3fc | ||
|
|
39e56ef864 | ||
|
|
51b77da760 | ||
|
|
4b83219a9b | ||
|
|
3ed07dd299 | ||
|
|
bb7daed7f6 | ||
|
|
0c84c47752 | ||
|
|
c18a185474 | ||
|
|
8ff66b04d8 | ||
|
|
94d6556e04 | ||
|
|
6d960265e4 | ||
|
|
cf4ca76e10 | ||
|
|
c7760e9903 | ||
|
|
d51bedceb6 | ||
|
|
125dc021d5 | ||
|
|
5bce481648 | ||
|
|
20153f62cf | ||
|
|
e302dfccf1 | ||
|
|
594b841490 | ||
|
|
40c354f79f | ||
|
|
b77959f300 | ||
|
|
59d6026a2b | ||
|
|
14226d1270 | ||
|
|
955f4da708 | ||
|
|
700b0359ea | ||
|
|
fe95b894d7 | ||
|
|
38a2712a8f | ||
|
|
d1ea091574 | ||
|
|
6bc812fc2f | ||
|
|
61b89fbd72 | ||
|
|
a2c065bdf2 | ||
|
|
88479f674b | ||
|
|
5edbacde74 | ||
|
|
67fc51224b | ||
|
|
101f3294b4 | ||
|
|
6c5622f732 | ||
|
|
f2a6fe1445 | ||
|
|
b0b593443f | ||
|
|
9b05185add | ||
|
|
17316da536 | ||
|
|
f3ca4dd989 | ||
|
|
e2b7bdac8c | ||
|
|
f86f6dc438 | ||
|
|
2bac0ed505 | ||
|
|
ae4078bb7f | ||
|
|
afaffbd9a2 | ||
|
|
b878e59e2e | ||
|
|
4bb3f625dd | ||
|
|
b7978cafa5 | ||
|
|
17f376c76f | ||
|
|
2774fdf158 | ||
|
|
11081efcb0 | ||
|
|
1b412c3027 | ||
|
|
73ec66e627 | ||
|
|
4ae4cd80b1 | ||
|
|
6eb27c66fc | ||
|
|
f96fd17fd3 | ||
|
|
c255a2494d | ||
|
|
86a9846300 | ||
|
|
db877d253c | ||
|
|
0acf1e3b08 | ||
|
|
362ed3a9b6 | ||
|
|
758878a229 | ||
|
|
ffb6dfc333 | ||
|
|
66da74e00a | ||
|
|
d41a0045cb | ||
|
|
b65629ba0e | ||
|
|
9518e6d1a0 | ||
|
|
4f374ee885 | ||
|
|
afa239056e | ||
|
|
886825af11 | ||
|
|
198de925aa |
167
.editorconfig
Normal file
167
.editorconfig
Normal file
@@ -0,0 +1,167 @@
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# C# files
|
||||
[*.cs]
|
||||
|
||||
# Indentation and spacing
|
||||
charset = utf-8
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
tab_width = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# New line preferences
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
max_line_length = unset
|
||||
|
||||
# using directive preferences
|
||||
csharp_using_directive_placement = outside_namespace
|
||||
dotnet_diagnostic.IDE0005.severity = error
|
||||
|
||||
# Code-block preferences
|
||||
csharp_style_namespace_declarations = block_scoped
|
||||
csharp_style_prefer_method_group_conversion = true
|
||||
csharp_style_prefer_top_level_statements = false
|
||||
|
||||
# Expression-level preferences
|
||||
csharp_prefer_simple_default_expression = true
|
||||
csharp_style_inlined_variable_declaration = true
|
||||
csharp_style_unused_value_assignment_preference = discard_variable
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable
|
||||
dotnet_diagnostic.IDE0001.severity = warning
|
||||
dotnet_diagnostic.IDE0002.severity = warning
|
||||
dotnet_diagnostic.IDE0004.severity = warning
|
||||
dotnet_diagnostic.IDE0010.severity = error
|
||||
dotnet_diagnostic.IDE0051.severity = warning
|
||||
dotnet_diagnostic.IDE0052.severity = warning
|
||||
dotnet_diagnostic.IDE0072.severity = warning
|
||||
dotnet_diagnostic.IDE0080.severity = warning
|
||||
dotnet_diagnostic.IDE0100.severity = error
|
||||
dotnet_diagnostic.IDE0110.severity = error
|
||||
dotnet_diagnostic.IDE0120.severity = warning
|
||||
dotnet_diagnostic.IDE0121.severity = warning
|
||||
dotnet_diagnostic.IDE0240.severity = error
|
||||
dotnet_diagnostic.IDE0241.severity = error
|
||||
dotnet_style_coalesce_expression = true
|
||||
dotnet_style_namespace_match_folder = false
|
||||
dotnet_style_null_propagation = true
|
||||
dotnet_style_prefer_auto_properties = true
|
||||
dotnet_style_prefer_collection_expression = when_types_loosely_match
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
|
||||
dotnet_style_prefer_compound_assignment = true
|
||||
csharp_style_prefer_simple_property_accessors = true
|
||||
dotnet_style_prefer_simplified_interpolation = true
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true
|
||||
csharp_style_prefer_unbound_generic_type_in_nameof = true
|
||||
|
||||
# Field preferences
|
||||
dotnet_diagnostic.IDE0044.severity = warning
|
||||
dotnet_style_readonly_field = true
|
||||
|
||||
# Language keyword vs. framework types preferences
|
||||
dotnet_diagnostic.IDE0049.severity = error
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true
|
||||
dotnet_style_predefined_type_for_member_access = true
|
||||
|
||||
# Modifier preferences
|
||||
csharp_prefer_static_local_function = true
|
||||
csharp_style_prefer_readonly_struct = true
|
||||
dotnet_diagnostic.IDE0036.severity = warning
|
||||
dotnet_diagnostic.IDE0040.severity = error
|
||||
dotnet_diagnostic.IDE0380.severity = error
|
||||
dotnet_style_require_accessibility_modifiers = always
|
||||
|
||||
# New-line preferences
|
||||
dotnet_diagnostic.IDE2000.severity = warning
|
||||
dotnet_diagnostic.IDE2002.severity = warning
|
||||
dotnet_diagnostic.IDE2003.severity = warning
|
||||
dotnet_diagnostic.IDE2004.severity = warning
|
||||
dotnet_diagnostic.IDE2005.severity = warning
|
||||
dotnet_diagnostic.IDE2006.severity = warning
|
||||
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false
|
||||
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = false
|
||||
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = false
|
||||
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false
|
||||
dotnet_style_allow_multiple_blank_lines_experimental = false
|
||||
dotnet_style_allow_statement_immediately_after_block_experimental = false
|
||||
|
||||
# Null-checking preferences
|
||||
csharp_style_conditional_delegate_call = true
|
||||
|
||||
# Parameter preferences
|
||||
dotnet_code_quality_unused_parameters = all
|
||||
dotnet_diagnostic.IDE0280.severity = error
|
||||
|
||||
# Parentheses preferences
|
||||
dotnet_diagnostic.IDE0047.severity = warning
|
||||
dotnet_diagnostic.IDE0048.severity = warning
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_other_operators = always_for_clarity
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
|
||||
|
||||
# Pattern-matching preferences
|
||||
dotnet_diagnostic.IDE0019.severity = warning
|
||||
dotnet_diagnostic.IDE0020.severity = warning
|
||||
dotnet_diagnostic.IDE0038.severity = warning
|
||||
dotnet_diagnostic.IDE0066.severity = none
|
||||
dotnet_diagnostic.IDE0083.severity = warning
|
||||
dotnet_diagnostic.IDE0260.severity = warning
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true
|
||||
csharp_style_prefer_not_pattern = true
|
||||
csharp_style_prefer_pattern_matching = true
|
||||
|
||||
# this. and Me. preferences
|
||||
dotnet_style_qualification_for_event = false
|
||||
dotnet_style_qualification_for_field = false
|
||||
dotnet_style_qualification_for_method = false
|
||||
dotnet_style_qualification_for_property = false
|
||||
|
||||
# var preferences
|
||||
csharp_style_var_for_built_in_types = false
|
||||
csharp_style_var_when_type_is_apparent = true
|
||||
|
||||
# .NET formatting options
|
||||
dotnet_separate_import_directive_groups = false
|
||||
dotnet_sort_system_directives_first = true
|
||||
|
||||
# C# formatting options
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = false
|
||||
csharp_indent_labels = one_less_than_current
|
||||
csharp_indent_switch_labels = true
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = true
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
48
.github/workflows/build_and_test.yml
vendored
Normal file
48
.github/workflows/build_and_test.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: Build and Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: |
|
||||
8.0.x
|
||||
9.0.x
|
||||
10.0.x
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test
|
||||
|
||||
- name: Run publish script
|
||||
run: ./publish-nix.sh -d
|
||||
|
||||
- name: Update rolling tag
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git tag -f rolling
|
||||
git push origin :refs/tags/rolling || true
|
||||
git push origin rolling --force
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.20.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: "*.nupkg,*.snupkg,*.zip"
|
||||
body: "Last built commit: ${{ github.sha }}"
|
||||
name: "Rolling Release"
|
||||
prerelease: True
|
||||
replacesArtifacts: True
|
||||
tag: "rolling"
|
||||
updateOnlyUnreleased: True
|
||||
43
.github/workflows/build_nupkg.yml
vendored
43
.github/workflows/build_nupkg.yml
vendored
@@ -1,43 +0,0 @@
|
||||
name: Nuget Pack
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Pack
|
||||
run: dotnet pack
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'Nuget Package'
|
||||
path: 'SabreTools.Serialization/bin/Release/*.nupkg'
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: 'SabreTools.Serialization/bin/Release/*.nupkg'
|
||||
body: 'Last built commit: ${{ github.sha }}'
|
||||
name: 'Rolling Release'
|
||||
prerelease: True
|
||||
replacesArtifacts: True
|
||||
tag: "rolling"
|
||||
updateOnlyUnreleased: True
|
||||
53
.github/workflows/build_test.yml
vendored
53
.github/workflows/build_test.yml
vendored
@@ -1,53 +0,0 @@
|
||||
name: Build Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
project: [Test]
|
||||
runtime: [win-x86, win-x64, linux-x64, osx-x64] #[win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64]
|
||||
framework: [net8.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0]
|
||||
conf: [Release, Debug]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build
|
||||
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c ${{ matrix.conf == 'Release' && 'Release -p:DebugType=None -p:DebugSymbols=false' || 'Debug'}} --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8')) && '-p:PublishSingleFile=true' || ''}}
|
||||
|
||||
- name: Archive build
|
||||
run: zip -r ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip ${{ matrix.project }}/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}
|
||||
path: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
|
||||
body: 'Last built commit: ${{ github.sha }}'
|
||||
name: 'Rolling Release'
|
||||
prerelease: True
|
||||
replacesArtifacts: True
|
||||
tag: "rolling"
|
||||
updateOnlyUnreleased: True
|
||||
30
.github/workflows/check_pr.yml
vendored
30
.github/workflows/check_pr.yml
vendored
@@ -3,15 +3,21 @@ name: Build PR
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Build
|
||||
run: dotnet build
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: |
|
||||
8.0.x
|
||||
9.0.x
|
||||
10.0.x
|
||||
|
||||
- name: Build
|
||||
run: dotnet build
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test
|
||||
|
||||
329
.gitignore
vendored
329
.gitignore
vendored
@@ -1,22 +1,18 @@
|
||||
*.swp
|
||||
*.*~
|
||||
project.lock.json
|
||||
.DS_Store
|
||||
*.pyc
|
||||
nupkg/
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode
|
||||
|
||||
# Rider
|
||||
.idea
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.lscache
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
@@ -24,15 +20,312 @@ nupkg/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
build/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Oo]ut/
|
||||
msbuild.log
|
||||
msbuild.err
|
||||
msbuild.wrn
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
**/Properties/launchSettings.json
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush
|
||||
.cr/
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
42
.vscode/launch.json
vendored
Normal file
42
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||
// Use hover for the description of the existing attributes
|
||||
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": ".NET Core Launch (ExtractionTool)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/ExtractionTool/bin/Debug/net10.0/ExtractionTool.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false,
|
||||
"justMyCode": false
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Launch (InfoPrint)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/InfoPrint/bin/Debug/net10.0/InfoPrint.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false,
|
||||
"justMyCode": false
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach",
|
||||
"processId": "${command:pickProcess}"
|
||||
}
|
||||
]
|
||||
}
|
||||
24
.vscode/tasks.json
vendored
Normal file
24
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "shell",
|
||||
"args": [
|
||||
"build",
|
||||
// Ask dotnet build to generate full paths for file names.
|
||||
"/property:GenerateFullPaths=true",
|
||||
// Do not generate summary otherwise it leads to duplicate errors in Problems panel
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"reveal": "silent"
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
56
ExtractionTool/ExtractionTool.csproj
Normal file
56
ExtractionTool/ExtractionTool.csproj
Normal file
@@ -0,0 +1,56 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
<OutputType>Exe</OutputType>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>3.2.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Support All Frameworks -->
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`)) OR $(TargetFramework.StartsWith(`net10`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- These are needed for dealing with native Windows DLLs -->
|
||||
<ItemGroup Condition="'$(RuntimeIdentifier)'=='win-x86'">
|
||||
<ContentWithTargetPath Include="..\runtimes\win-x86\native\StormLib.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
|
||||
<TargetPath>StormLib.dll</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(RuntimeIdentifier)'=='win-x64'">
|
||||
<ContentWithTargetPath Include="..\runtimes\win-x64\native\StormLib.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
|
||||
<TargetPath>StormLib.dll</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.Wrappers\SabreTools.Wrappers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.CommandLine" Version="1.5.0" />
|
||||
<PackageReference Include="SabreTools.IO" Version="2.3.0" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.11" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
194
ExtractionTool/Features/MainFeature.cs
Normal file
194
ExtractionTool/Features/MainFeature.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.CommandLine;
|
||||
using SabreTools.CommandLine.Inputs;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
using SabreTools.Wrappers;
|
||||
|
||||
namespace ExtractionTool.Features
|
||||
{
|
||||
internal sealed class MainFeature : Feature
|
||||
{
|
||||
#region Feature Definition
|
||||
|
||||
public const string DisplayName = "main";
|
||||
|
||||
/// <remarks>Flags are unused</remarks>
|
||||
private static readonly string[] _flags = [];
|
||||
|
||||
/// <remarks>Description is unused</remarks>
|
||||
private const string _description = "";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Inputs
|
||||
|
||||
private const string _debugName = "debug";
|
||||
internal readonly FlagInput DebugInput = new(_debugName, ["-d", "--debug"], "Enable debug mode");
|
||||
|
||||
private const string _outputPathName = "output-path";
|
||||
internal readonly StringInput OutputPathInput = new(_outputPathName, ["-o", "--outdir"], "Set output path for extraction (required)");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Enable debug output for relevant operations
|
||||
/// </summary>
|
||||
public bool Debug { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Output path for archive extraction
|
||||
/// </summary>
|
||||
public string OutputPath { get; private set; } = string.Empty;
|
||||
|
||||
#endregion
|
||||
|
||||
public MainFeature()
|
||||
: base(DisplayName, _flags, _description)
|
||||
{
|
||||
RequiresInputs = true;
|
||||
|
||||
Add(DebugInput);
|
||||
Add(OutputPathInput);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Execute()
|
||||
{
|
||||
// Get the options from the arguments
|
||||
Debug = GetBoolean(_debugName);
|
||||
OutputPath = GetString(_outputPathName) ?? string.Empty;
|
||||
|
||||
// Validate the output path
|
||||
if (!ValidateExtractionPath())
|
||||
return false;
|
||||
|
||||
// Loop through the input paths
|
||||
for (int i = 0; i < Inputs.Count; i++)
|
||||
{
|
||||
string arg = Inputs[i];
|
||||
ExtractPath(arg);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => Inputs.Count > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper to extract data for a single path
|
||||
/// </summary>
|
||||
/// <param name="path">File or directory path</param>
|
||||
private void ExtractPath(string path)
|
||||
{
|
||||
// Normalize by getting the full path
|
||||
path = Path.GetFullPath(path);
|
||||
Console.WriteLine($"Checking possible path: {path}");
|
||||
|
||||
// Check if the file or directory exists
|
||||
if (File.Exists(path))
|
||||
{
|
||||
ExtractFile(path);
|
||||
}
|
||||
else if (Directory.Exists(path))
|
||||
{
|
||||
foreach (string file in path.SafeEnumerateFiles("*", SearchOption.AllDirectories))
|
||||
{
|
||||
ExtractFile(file);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{path} does not exist, skipping...");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Print information for a single file, if possible
|
||||
/// </summary>
|
||||
/// <param name="path">File path</param>
|
||||
private void ExtractFile(string file)
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"Attempting to extract all files from {file}");
|
||||
using Stream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
// Read the first 32 bytes
|
||||
byte[] magic = stream.PeekBytes(32);
|
||||
|
||||
// Get the file type
|
||||
string extension = Path.GetExtension(file).TrimStart('.');
|
||||
WrapperType ft = WrapperFactory.GetFileType(magic ?? [], extension);
|
||||
|
||||
// Print out the file format
|
||||
Console.WriteLine($"File format found: {ft}");
|
||||
|
||||
// Setup the wrapper to extract
|
||||
var wrapper = WrapperFactory.CreateWrapper(ft, stream);
|
||||
|
||||
// If we don't have a wrapper
|
||||
if (wrapper is null)
|
||||
{
|
||||
Console.WriteLine($"Either {ft} is not supported or something went wrong during parsing!");
|
||||
Console.WriteLine();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the wrapper is not extractable
|
||||
if (wrapper is not IExtractable extractable)
|
||||
{
|
||||
Console.WriteLine($"{ft} is not supported for extraction!");
|
||||
Console.WriteLine();
|
||||
return;
|
||||
}
|
||||
|
||||
// Print the preamble
|
||||
Console.WriteLine($"Attempting to extract from '{wrapper.Description()}'");
|
||||
Console.WriteLine();
|
||||
|
||||
// Attempt the extraction
|
||||
Directory.CreateDirectory(OutputPath);
|
||||
extractable.Extract(OutputPath, Debug);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(Debug ? ex : "[Exception opening file, please try again]");
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate the extraction path
|
||||
/// </summary>
|
||||
private bool ValidateExtractionPath()
|
||||
{
|
||||
// Null or empty output path
|
||||
if (string.IsNullOrEmpty(OutputPath))
|
||||
{
|
||||
Console.WriteLine("Output directory required for extraction!");
|
||||
Console.WriteLine();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Malformed output path or invalid location
|
||||
try
|
||||
{
|
||||
OutputPath = Path.GetFullPath(OutputPath);
|
||||
Directory.CreateDirectory(OutputPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine("Output directory could not be created!");
|
||||
Console.WriteLine();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
79
ExtractionTool/Program.cs
Normal file
79
ExtractionTool/Program.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExtractionTool.Features;
|
||||
using SabreTools.CommandLine;
|
||||
using SabreTools.CommandLine.Features;
|
||||
|
||||
namespace ExtractionTool
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
// Register the codepages
|
||||
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
|
||||
#endif
|
||||
|
||||
// Create the command set
|
||||
var mainFeature = new MainFeature();
|
||||
var commandSet = CreateCommands(mainFeature);
|
||||
|
||||
// If we have no args, show the help and quit
|
||||
if (args is null || args.Length == 0)
|
||||
{
|
||||
commandSet.OutputAllHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache the first argument and starting index
|
||||
string featureName = args[0];
|
||||
|
||||
// Try processing the standalone arguments
|
||||
var topLevel = commandSet.GetTopLevel(featureName);
|
||||
switch (topLevel)
|
||||
{
|
||||
// Standalone Options
|
||||
case Help help: help.ProcessArgs(args, 0, commandSet); return;
|
||||
|
||||
// Default Behavior
|
||||
default:
|
||||
if (!mainFeature.ProcessArgs(args, 0))
|
||||
{
|
||||
commandSet.OutputAllHelp();
|
||||
return;
|
||||
}
|
||||
else if (!mainFeature.VerifyInputs())
|
||||
{
|
||||
Console.Error.WriteLine("At least one input is required");
|
||||
commandSet.OutputAllHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
mainFeature.Execute();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the command set for the program
|
||||
/// </summary>
|
||||
private static CommandSet CreateCommands(MainFeature mainFeature)
|
||||
{
|
||||
List<string> header = [
|
||||
"Extraction Tool",
|
||||
string.Empty,
|
||||
"ExtractionTool <options> file|directory ...",
|
||||
string.Empty,
|
||||
];
|
||||
|
||||
var commandSet = new CommandSet(header);
|
||||
|
||||
commandSet.Add(new Help(["-?", "-h", "--help"]));
|
||||
commandSet.Add(mainFeature.DebugInput);
|
||||
commandSet.Add(mainFeature.OutputPathInput);
|
||||
|
||||
return commandSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
284
InfoPrint/Features/MainFeature.cs
Normal file
284
InfoPrint/Features/MainFeature.cs
Normal file
@@ -0,0 +1,284 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.CommandLine;
|
||||
using SabreTools.CommandLine.Inputs;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
using SabreTools.Wrappers;
|
||||
|
||||
namespace InfoPrint.Features
|
||||
{
|
||||
internal sealed class MainFeature : Feature
|
||||
{
|
||||
#region Feature Definition
|
||||
|
||||
public const string DisplayName = "main";
|
||||
|
||||
/// <remarks>Flags are unused</remarks>
|
||||
private static readonly string[] _flags = [];
|
||||
|
||||
/// <remarks>Description is unused</remarks>
|
||||
private const string _description = "";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Inputs
|
||||
|
||||
private const string _debugName = "debug";
|
||||
internal readonly FlagInput DebugInput = new(_debugName, ["-d", "--debug"], "Enable debug mode");
|
||||
|
||||
private const string _fileOnlyName = "file-only";
|
||||
internal readonly FlagInput FileOnlyInput = new(_fileOnlyName, ["-f", "--file"], "Print to file only");
|
||||
|
||||
private const string _hashName = "hash";
|
||||
internal readonly FlagInput HashInput = new(_hashName, ["-c", "--hash"], "Output file hashes");
|
||||
|
||||
private const string _jsonName = "json";
|
||||
internal readonly FlagInput JsonInput = new(_jsonName, ["-j", "--json"], "Print info as JSON");
|
||||
|
||||
private const string _recursiveName = "recursive";
|
||||
internal readonly FlagInput RecursiveInput = new(_recursiveName, ["-r", "--recursive"], "Recursively print info from embedded files");
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Enable debug output for relevant operations
|
||||
/// </summary>
|
||||
public bool Debug { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Output information to file only, skip printing to console
|
||||
/// </summary>
|
||||
public bool FileOnly { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Print external file hashes
|
||||
/// </summary>
|
||||
public bool Hash { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable JSON output
|
||||
/// </summary>
|
||||
public bool Json { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable outputting subfile information
|
||||
/// </summary>
|
||||
public bool Recursive { get; private set; }
|
||||
|
||||
public MainFeature()
|
||||
: base(DisplayName, _flags, _description)
|
||||
{
|
||||
RequiresInputs = true;
|
||||
|
||||
Add(DebugInput);
|
||||
Add(HashInput);
|
||||
Add(FileOnlyInput);
|
||||
Add(JsonInput);
|
||||
Add(RecursiveInput);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Execute()
|
||||
{
|
||||
// Get the options from the arguments
|
||||
Debug = GetBoolean(_debugName);
|
||||
Hash = GetBoolean(_hashName);
|
||||
FileOnly = GetBoolean(_fileOnlyName);
|
||||
Json = GetBoolean(_jsonName);
|
||||
Recursive = GetBoolean(_recursiveName);
|
||||
|
||||
// Loop through the input paths
|
||||
for (int i = 0; i < Inputs.Count; i++)
|
||||
{
|
||||
string arg = Inputs[i];
|
||||
PrintPathInfo(arg);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool VerifyInputs() => Inputs.Count > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper to print information for a single path
|
||||
/// </summary>
|
||||
/// <param name="path">File or directory path</param>
|
||||
private void PrintPathInfo(string path)
|
||||
{
|
||||
Console.WriteLine($"Checking possible path: {path}");
|
||||
|
||||
// Check if the file or directory exists
|
||||
if (File.Exists(path))
|
||||
{
|
||||
PrintFileInfo(path);
|
||||
}
|
||||
else if (Directory.Exists(path))
|
||||
{
|
||||
foreach (string file in path.SafeEnumerateFiles("*", SearchOption.AllDirectories))
|
||||
{
|
||||
PrintFileInfo(file);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{path} does not exist, skipping...");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Print information for a single file, if possible
|
||||
/// </summary>
|
||||
/// <param name="file">File path</param>
|
||||
private void PrintFileInfo(string file)
|
||||
{
|
||||
Console.WriteLine($"Attempting to print info for {file}");
|
||||
|
||||
// Get the base info output name
|
||||
string filenameBase = $"info-{DateTime.Now:yyyy-MM-dd_HHmmss.ffff}";
|
||||
|
||||
// If we have the hash flag
|
||||
if (Hash)
|
||||
{
|
||||
var hashBuilder = PrintHashInfo(file);
|
||||
if (hashBuilder is not null)
|
||||
{
|
||||
// Create the output data
|
||||
string hashData = hashBuilder.ToString();
|
||||
|
||||
// Write the output data
|
||||
using var hsw = new StreamWriter(File.OpenWrite($"{filenameBase}.hashes"));
|
||||
hsw.WriteLine(hashData);
|
||||
hsw.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using Stream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
// Read the first 32 bytes
|
||||
byte[] magic = stream.PeekBytes(32);
|
||||
|
||||
// Get the file type
|
||||
string extension = Path.GetExtension(file).TrimStart('.');
|
||||
WrapperType ft = WrapperFactory.GetFileType(magic ?? [], extension);
|
||||
|
||||
// Print out the file format
|
||||
Console.WriteLine($"File format found: {ft}");
|
||||
|
||||
// Setup the wrapper to print
|
||||
var wrapper = WrapperFactory.CreateWrapper(ft, stream);
|
||||
|
||||
// If we don't have a wrapper
|
||||
if (wrapper is null)
|
||||
{
|
||||
Console.WriteLine($"Either {ft} is not supported or something went wrong during parsing!");
|
||||
Console.WriteLine();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the wrapper is not printable
|
||||
if (wrapper is not IPrintable printable)
|
||||
{
|
||||
Console.WriteLine($"{ft} is not supported for printing!");
|
||||
Console.WriteLine();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have the JSON flag
|
||||
if (Json)
|
||||
{
|
||||
// Create the output data
|
||||
string serializedData = printable.ExportJSON(Recursive);
|
||||
|
||||
// Write the output data
|
||||
using var jsw = new StreamWriter(File.OpenWrite($"{filenameBase}.json"));
|
||||
jsw.WriteLine(serializedData);
|
||||
jsw.Flush();
|
||||
}
|
||||
|
||||
// Create the output data
|
||||
var builder = new StringBuilder();
|
||||
printable.PrintInformation(builder, Recursive);
|
||||
|
||||
// Only print to console if enabled
|
||||
if (!FileOnly)
|
||||
Console.WriteLine(builder);
|
||||
|
||||
using var sw = new StreamWriter(File.OpenWrite($"{filenameBase}.txt"));
|
||||
sw.WriteLine(file);
|
||||
sw.WriteLine();
|
||||
sw.WriteLine(builder.ToString());
|
||||
sw.Flush();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(Debug ? ex : "[Exception opening file, please try again]");
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Print hash information for a single file, if possible
|
||||
/// </summary>
|
||||
/// <param name="file">File path</param>
|
||||
/// <returns>StringBuilder representing the hash information, if possible</returns>
|
||||
private StringBuilder? PrintHashInfo(string file)
|
||||
{
|
||||
// Ignore missing files
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
Console.WriteLine($"Attempting to hash {file}, this may take a while...");
|
||||
|
||||
try
|
||||
{
|
||||
// Get all file hashes for flexibility
|
||||
var hashes = HashTool.GetFileHashes(file);
|
||||
if (hashes is null)
|
||||
{
|
||||
if (Debug) Console.WriteLine($"Hashes for {file} could not be retrieved");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Output subset of available hashes
|
||||
var builder = new StringBuilder();
|
||||
if (hashes.TryGetValue(HashType.CRC16, out string? crc16) && crc16 is not null)
|
||||
builder.AppendLine($"CRC-16 checksum: {crc16}");
|
||||
if (hashes.TryGetValue(HashType.CRC32, out string? crc32) && crc32 is not null)
|
||||
builder.AppendLine($"CRC-32 checksum: {crc32}");
|
||||
if (hashes.TryGetValue(HashType.CRC64, out string? crc64) && crc64 is not null)
|
||||
builder.AppendLine($"CRC-64 checksum: {crc64}");
|
||||
if (hashes.TryGetValue(HashType.MD2, out string? md2) && md2 is not null)
|
||||
builder.AppendLine($"MD2 hash: {md2}");
|
||||
if (hashes.TryGetValue(HashType.MD4, out string? md4) && md4 is not null)
|
||||
builder.AppendLine($"MD4 hash: {md4}");
|
||||
if (hashes.TryGetValue(HashType.MD5, out string? md5) && md5 is not null)
|
||||
builder.AppendLine($"MD5 hash: {md5}");
|
||||
if (hashes.TryGetValue(HashType.RIPEMD128, out string? ripemd128) && ripemd128 is not null)
|
||||
builder.AppendLine($"RIPEMD-128 hash: {ripemd128}");
|
||||
if (hashes.TryGetValue(HashType.RIPEMD160, out string? ripemd160) && ripemd160 is not null)
|
||||
builder.AppendLine($"RIPEMD-160 hash: {ripemd160}");
|
||||
if (hashes.TryGetValue(HashType.SHA1, out string? sha1) && sha1 is not null)
|
||||
builder.AppendLine($"SHA-1 hash: {sha1}");
|
||||
if (hashes.TryGetValue(HashType.SHA256, out string? sha256) && sha256 is not null)
|
||||
builder.AppendLine($"SHA-256 hash: {sha256}");
|
||||
if (hashes.TryGetValue(HashType.SHA384, out string? sha384) && sha384 is not null)
|
||||
builder.AppendLine($"SHA-384 hash: {sha384}");
|
||||
if (hashes.TryGetValue(HashType.SHA512, out string? sha512) && sha512 is not null)
|
||||
builder.AppendLine($"SHA-512 hash: {sha512}");
|
||||
|
||||
return builder;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(Debug ? ex : "[Exception opening file, please try again]");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
InfoPrint/InfoPrint.csproj
Normal file
40
InfoPrint/InfoPrint.csproj
Normal file
@@ -0,0 +1,40 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
<OutputType>Exe</OutputType>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>3.2.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Support All Frameworks -->
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`)) OR $(TargetFramework.StartsWith(`net10`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.Wrappers\SabreTools.Wrappers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.CommandLine" Version="1.5.0" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="2.1.0" />
|
||||
<PackageReference Include="SabreTools.IO" Version="2.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
78
InfoPrint/Program.cs
Normal file
78
InfoPrint/Program.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using InfoPrint.Features;
|
||||
using SabreTools.CommandLine;
|
||||
using SabreTools.CommandLine.Features;
|
||||
|
||||
namespace InfoPrint
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Create the command set
|
||||
var mainFeature = new MainFeature();
|
||||
var commandSet = CreateCommands(mainFeature);
|
||||
|
||||
// If we have no args, show the help and quit
|
||||
if (args is null || args.Length == 0)
|
||||
{
|
||||
commandSet.OutputAllHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache the first argument and starting index
|
||||
string featureName = args[0];
|
||||
|
||||
// Try processing the standalone arguments
|
||||
var topLevel = commandSet.GetTopLevel(featureName);
|
||||
switch (topLevel)
|
||||
{
|
||||
// Standalone Options
|
||||
case Help help: help.ProcessArgs(args, 0, commandSet); return;
|
||||
|
||||
// Default Behavior
|
||||
default:
|
||||
if (!mainFeature.ProcessArgs(args, 0))
|
||||
{
|
||||
commandSet.OutputAllHelp();
|
||||
return;
|
||||
}
|
||||
else if (!mainFeature.VerifyInputs())
|
||||
{
|
||||
Console.Error.WriteLine("At least one input is required");
|
||||
commandSet.OutputAllHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
mainFeature.Execute();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the command set for the program
|
||||
/// </summary>
|
||||
private static CommandSet CreateCommands(MainFeature mainFeature)
|
||||
{
|
||||
List<string> header = [
|
||||
"Information Printing Program",
|
||||
string.Empty,
|
||||
"InfoPrint <options> file|directory ...",
|
||||
string.Empty,
|
||||
];
|
||||
|
||||
var commandSet = new CommandSet(header);
|
||||
|
||||
commandSet.Add(new Help(["-?", "-h", "--help"]));
|
||||
commandSet.Add(mainFeature.DebugInput);
|
||||
commandSet.Add(mainFeature.HashInput);
|
||||
commandSet.Add(mainFeature.FileOnlyInput);
|
||||
#if NETCOREAPP
|
||||
commandSet.Add(mainFeature.JsonInput);
|
||||
#endif
|
||||
|
||||
return commandSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
7
LICENSE
Normal file
7
LICENSE
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright (c) 2018-2026 Matt Nadareski
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
106
README.MD
106
README.MD
@@ -1,40 +1,94 @@
|
||||
# SabreTools.Serialization
|
||||
|
||||
This library comprises of serializers that both read and write from files and streams to the dedicated models as well as convert to and from the common internal models. This library is partially used by the current parsing and writing code but none of the internal model serialization is used.
|
||||
[](https://github.com/SabreTools/SabreTools.Serialization/actions/workflows/build_and_test.yml)
|
||||
|
||||
This meta-library comprises of serializers that both read and write from files and streams to the dedicated models as well as convert to and from the common internal models. This library is partially used by the current parsing and writing code but none of the internal model serialization is used.
|
||||
|
||||
Find the link to the Nuget package [here](https://www.nuget.org/packages/SabreTools.Serialization).
|
||||
|
||||
## Interfaces
|
||||
## Releases
|
||||
|
||||
Below is a table representing the various conversion interfaces that are implemented within this library.
|
||||
For the most recent stable build, download the latest release here: [Releases Page](https://github.com/SabreTools/SabreTools.Serialization/releases)
|
||||
|
||||
| Interface Name | Source Type | Destination Type |
|
||||
| --- | --- | --- |
|
||||
| `IByteDeserializer` | `byte[]?` | Model |
|
||||
| `IByteSerializer` | Model | `byte[]?` |
|
||||
| `IFileDeserializer` | `string?` path | Model |
|
||||
| `IFileSerializer` | Model | `string?` path |
|
||||
| `IModelSerializer` | Model | Model |
|
||||
| `IStreamDeserializer` | `Stream?` | Model |
|
||||
| `IStreamSerializer` | Model | `Stream?` |
|
||||
| `IStringDeserializer` | `string?` representation | Model |
|
||||
| `IStringSerializer` | Model | `string?` representation |
|
||||
For the latest WIP build here: [Rolling Release](https://github.com/SabreTools/SabreTools.Serialization/releases/tag/rolling)
|
||||
|
||||
Below is a table representing the various non-conversion interfaces that are implemented within this library.
|
||||
## InfoPrint
|
||||
|
||||
| Interface Name | Purpose |
|
||||
**InfoPrint** is a reference implementation for the deserialization and printing features of the library, packaged as a standalone executable for all supported platforms. It will attempt to detect and display information about many supported file types, optionally both hashing the file and outputting the information to a JSON file.
|
||||
|
||||
```text
|
||||
InfoPrint <options> file|directory ...
|
||||
|
||||
Options:
|
||||
-?, -h, --help Display this help text
|
||||
-d, --debug Enable debug mode
|
||||
-c, --hash Output file hashes
|
||||
-f, --file Print to file only
|
||||
-j, --json Print info as JSON
|
||||
-r, --recursive Recursively print info from embedded files
|
||||
```
|
||||
|
||||
## ExtractionTool
|
||||
|
||||
**ExtractionTool** is a reference implementation for the extraction features of the library, packaged as a standalone executable for all supported platforms. It will attempt to detect and extract many supported file types. See the table below for supported extraction functionality.
|
||||
|
||||
```text
|
||||
ExtractionTool <options> file|directory ...
|
||||
|
||||
Options:
|
||||
-?, -h, --help Display this help text
|
||||
-d, --debug Enable debug mode
|
||||
-o, --outdir [PATH] Set output path for extraction (required)
|
||||
```
|
||||
|
||||
| Format Name | Notes |
|
||||
| --- | --- |
|
||||
| `IPrinter` | Provides a formatted output for a model |
|
||||
| `IWrapper` / `IWrapper<T>` | Wraps a model or set of models to provide additional functionality |
|
||||
| 7-zip archive | .NET Framework 4.6.2 and greater |
|
||||
| Atari 7800 (A78) Cart Image | Header and ROM data |
|
||||
| Atari Lynx (LNX) Cart Image | Header and ROM data |
|
||||
| BFPK custom archive format | |
|
||||
| bzip2 archive | .NET Framework 4.6.2 and greater |
|
||||
| Compound File Binary (CFB) | Only CFB common pieces extractable |
|
||||
| fwNES FDS file | Header and disk data |
|
||||
| gzip archive | |
|
||||
| Half-Life Game Cache File (GCF) | |
|
||||
| Half-Life Level (BSP) | |
|
||||
| Half-Life Package File (PAK) | |
|
||||
| Half-Life Texture Package File (WAD3) | |
|
||||
| Half-Life 2 Level (VBSP) | |
|
||||
| InstallShield Archive V3 (Z) | |
|
||||
| InstallShield CAB | |
|
||||
| ISO9660 | ISO and BIN files |
|
||||
| Microsoft cabinet file | Does not support LZX or Quantum compression |
|
||||
| Microsoft LZ-compressed files | KWAJ, QBasic, and SZDD variants |
|
||||
| MoPaQ game data archive (MPQ) | Windows only |
|
||||
| New Exectuable | Embedded archives and executables in the overlay and Wise installer |
|
||||
| Nintendo Entertainment System (NES) Cart Image | Header, Trainer data, PRG-ROM, CHR-ROM, Unheadered ROM, and PlayChoice-10 INST-ROM |
|
||||
| NovaLogic Game Archive Format (PFF) | |
|
||||
| PKZIP and derived files (ZIP, etc.) | .NET Framework 4.6.2 and greater |
|
||||
| Portable Executable | Embedded archives and executables in the resources and overlay, CExe-packed data, SFX archives (7-zip, PKZIP, and RAR), and Wise installer |
|
||||
| Quantum archive (Q) | Currently not working |
|
||||
| RAR archive (RAR) | .NET Framework 4.6.2 and greater |
|
||||
| SGA game archive | |
|
||||
| Tape archive (TAR) | |
|
||||
| Valve Package File (VPK) | |
|
||||
| XBox Package File (XZP) | |
|
||||
| Xbox DVD Filesystem (XISO) | |
|
||||
| xz archive (XZ) | .NET Framework 4.6.2 and greater |
|
||||
| ZArchive | |
|
||||
|
||||
## Namespaces
|
||||
|
||||
Below is a table of all namespaces within the library and what they represent
|
||||
Below are a list of the included namespaces with links to their README files:
|
||||
|
||||
| Namespace | Description |
|
||||
| --- | --- |
|
||||
| `SabreTools.Serialization.CrossModel` | Convert between models; mainly used for metadata files converting to and from a common, `Dictionary`-based model |
|
||||
| `SabreTools.Serialization.Deserializers` | Convert from external sources to models |
|
||||
| `SabreTools.Serialization.Printers` | Export model information in a formatted manner |
|
||||
| `SabreTools.Serialization.Serializers` | Convert from models to external sources |
|
||||
| `SabreTools.Serialization.Wrappers` | Classes that wrap serialization and models to allow for including extension properties |
|
||||
- [SabreTools.Data.Extensions](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Data.Extensions)
|
||||
- [SabreTools.Data.Models](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Data.Models)
|
||||
- [SabreTools.Metadata.DatFiles](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Metadata.DatFiles)
|
||||
- [SabreTools.Metadata.DatItems](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Metadata.Datitems)
|
||||
- [SabreTools.Metadata.Filter](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Metadata.Filter)
|
||||
- [SabreTools.Metadata.Reports](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Metadata.Reports)
|
||||
- [SabreTools.ObjectIdentifier](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.ObjectIdentifier)
|
||||
- [SabreTools.Serialization.CrossModel](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Serialization.CrossModel)
|
||||
- [SabreTools.Serialization.Readers](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Serialization.Readers)
|
||||
- [SabreTools.Serialization.Writers](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Serialization.Writers)
|
||||
- [SabreTools.Wrappers](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Wrappers)
|
||||
|
||||
141
SabreTools.Data.Extensions.Test/Atari7800CartExtensionsTests.cs
Normal file
141
SabreTools.Data.Extensions.Test/Atari7800CartExtensionsTests.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using SabreTools.Data.Models.Atari7800;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class Atari7800CartExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData((AudioDevice)0, "POKEY - none, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
|
||||
// POKEY
|
||||
[InlineData(AudioDevice.Pokey440, "POKEY - @440, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
|
||||
[InlineData(AudioDevice.Pokey450, "POKEY - @450, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
|
||||
[InlineData(AudioDevice.Pokey450Plus440, "POKEY - @450+@440, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
|
||||
[InlineData(AudioDevice.Pokey800, "POKEY - @800, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
|
||||
[InlineData(AudioDevice.Pokey4000, "POKEY - @4000, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
|
||||
[InlineData((AudioDevice)0x0007, "Unknown 7, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
|
||||
// YM2151
|
||||
[InlineData(AudioDevice.YM2151460, "POKEY - none, YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
|
||||
// COVOX
|
||||
[InlineData(AudioDevice.COVOX430, "POKEY - none, No YM2151 @460, COVOX @430, No ADPCM Audio Stream @420")]
|
||||
// ADPCM
|
||||
[InlineData(AudioDevice.ADPCMAudioStream420, "POKEY - none, No YM2151 @460, No COVOX @430, ADPCM Audio Stream @420")]
|
||||
public void FromAudioDeviceTest(AudioDevice audio, string expected)
|
||||
{
|
||||
string actual = audio.FromAudioDevice();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData((CartType)0, "N/A")]
|
||||
[InlineData(CartType.PokeyAt4000, "pokey at $4000")]
|
||||
[InlineData(CartType.SupergameBankSwitched, "supergame bank switched")]
|
||||
[InlineData(CartType.SupergameRamAt4000, "supergame ram at $4000")]
|
||||
[InlineData(CartType.RomAt4000, "rom at $4000")]
|
||||
[InlineData(CartType.Bank6At4000, "bank 6 at $4000")]
|
||||
[InlineData(CartType.BankedRam, "banked ram")]
|
||||
[InlineData(CartType.PokeyAt450, "pokey at $450")]
|
||||
[InlineData(CartType.MirrorRamAt4000, "mirror ram at $4000")]
|
||||
[InlineData(CartType.ActivisionBanking, "activision banking")]
|
||||
[InlineData(CartType.AbsoluteBanking, "absolute banking")]
|
||||
[InlineData(CartType.PokeyAt440, "pokey at $440")]
|
||||
[InlineData(CartType.Ym2151At460461, "ym2151 at $460/$461")]
|
||||
[InlineData(CartType.Souper, "souper")]
|
||||
[InlineData(CartType.Banksets, "banksets")]
|
||||
[InlineData(CartType.HaltBankedRam, "halt banked ram")]
|
||||
[InlineData(CartType.PokeyAt800, "pokey@800")]
|
||||
public void FromCartTypeTest(CartType type, string expected)
|
||||
{
|
||||
string actual = type.FromCartType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ControllerType.None, "none")]
|
||||
[InlineData(ControllerType.Joystick, "7800 joystick")]
|
||||
[InlineData(ControllerType.Lightgun, "lightgun")]
|
||||
[InlineData(ControllerType.Paddle, "paddle")]
|
||||
[InlineData(ControllerType.Trakball, "trakball")]
|
||||
[InlineData(ControllerType.VcsJoystick, "2600 joystick")]
|
||||
[InlineData(ControllerType.VcsDriving, "2600 driving")]
|
||||
[InlineData(ControllerType.VcsKeypad, "2600 keypad")]
|
||||
[InlineData(ControllerType.STMouse, "ST mouse")]
|
||||
[InlineData(ControllerType.AmigaMouse, "Amiga mouse")]
|
||||
[InlineData(ControllerType.AtariVoxSaveKey, "AtariVox/SaveKey")]
|
||||
[InlineData(ControllerType.SNES2Atari, "SNES2Atari")]
|
||||
[InlineData(ControllerType.Mega7800, "Mega7800")]
|
||||
public void FromControllerTypeTest(ControllerType type, string expected)
|
||||
{
|
||||
string actual = type.FromControllerType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(Interrupt.None, "None")]
|
||||
[InlineData(Interrupt.Pokey1, "POKEY 1 (@450 | @800 | @4000)")]
|
||||
[InlineData(Interrupt.Pokey2, "POKEY 2 (@440)")]
|
||||
[InlineData(Interrupt.YM2151, "YM2151")]
|
||||
public void FromInterruptTest(Interrupt interrupt, string expected)
|
||||
{
|
||||
string actual = interrupt.FromInterrupt();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(Mapper.Linear, "Linear")]
|
||||
[InlineData(Mapper.SuperGame, "SuperGame")]
|
||||
[InlineData(Mapper.Activision, "Activision")]
|
||||
[InlineData(Mapper.Absolute, "Absolute")]
|
||||
[InlineData(Mapper.Souper, "Souper")]
|
||||
public void FromMapperTest(Mapper mapper, string expected)
|
||||
{
|
||||
string actual = mapper.FromMapper();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(MapperOptions.SuperGameNone, "Option at @4000 - none, Standard ROM")]
|
||||
[InlineData(MapperOptions.SuperGame16KRAM, "Option at @4000 - 16K RAM, Standard ROM")]
|
||||
[InlineData(MapperOptions.SuperGame8KEXRAMA8, "Option at @4000 - 8K EXRAM/A8, Standard ROM")]
|
||||
[InlineData(MapperOptions.SuperGame32KEXRAMM2, "Option at @4000 - 32K EXRAM/M2, Standard ROM")]
|
||||
[InlineData(MapperOptions.SuperGameEXROM, "Option at @4000 - EXROM, Standard ROM")]
|
||||
[InlineData(MapperOptions.SuperGameEXFIX, "Option at @4000 - EXFIX, Standard ROM")]
|
||||
[InlineData(MapperOptions.SuperGame32KEXRAMX2, "Option at @4000 - 32k EXRAM/X2, Standard ROM")]
|
||||
[InlineData(MapperOptions.BanksetRom, "Option at @4000 - none, Bankset ROM")]
|
||||
public void FromMapperOptionsTest(MapperOptions options, string expected)
|
||||
{
|
||||
string actual = options.FromMapperOptions();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SaveDevice.None, "None")]
|
||||
[InlineData(SaveDevice.HSC, "HSC")]
|
||||
[InlineData(SaveDevice.SaveKeyAtariVox, "SaveKey/AtariVox")]
|
||||
public void FromSaveDeviceTest(SaveDevice device, string expected)
|
||||
{
|
||||
string actual = device.FromSaveDevice();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SlotPassthroughDevice.None, "None")]
|
||||
[InlineData(SlotPassthroughDevice.XM, "XM")]
|
||||
public void FromSlotPassthroughDeviceTest(SlotPassthroughDevice device, string expected)
|
||||
{
|
||||
string actual = device.FromSlotPassthroughDevice();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData((TVType)0, "NTSC, Component, Single-region")]
|
||||
[InlineData(TVType.PAL, "PAL, Component, Single-region")]
|
||||
[InlineData(TVType.Composite, "NTSC, Composite, Single-region")]
|
||||
[InlineData(TVType.MultiRegion, "NTSC, Component, Multi-region")]
|
||||
public void FromTVTypeTest(TVType type, string expected)
|
||||
{
|
||||
string actual = type.FromTVType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using SabreTools.Data.Models.AtariLynx;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class AtariLynxCartExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(Rotation.NoRotation, "No rotation (horizontal, buttons right)")]
|
||||
[InlineData(Rotation.RotateLeft, "Rotate left (vertical, buttons down)")]
|
||||
[InlineData(Rotation.RotateRight, "Rotate right (vertical, buttons up)")]
|
||||
public void FromRotationTest(Rotation rotation, string expected)
|
||||
{
|
||||
string actual = rotation.FromRotation();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
SabreTools.Data.Extensions.Test/CDROMExtensionsTests.cs
Normal file
65
SabreTools.Data.Extensions.Test/CDROMExtensionsTests.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System.IO;
|
||||
using SabreTools.Data.Models.CDROM;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class CDROMExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(new byte[0], SectorMode.UNKNOWN)]
|
||||
[InlineData(new byte[] { 0x00 }, SectorMode.MODE0)]
|
||||
[InlineData(new byte[] { 0x01 }, SectorMode.MODE1)]
|
||||
[InlineData(new byte[] { 0x02 }, SectorMode.UNKNOWN)]
|
||||
[InlineData(new byte[] { 0x02, 0x00, 0x00, 0x00 }, SectorMode.MODE2_FORM1)]
|
||||
[InlineData(new byte[] { 0x02, 0x00, 0x00, 0x20 }, SectorMode.MODE2_FORM2)]
|
||||
[InlineData(new byte[] { 0x03 }, SectorMode.UNKNOWN)]
|
||||
public void GetSectorModeTest(byte[] bytes, SectorMode expected)
|
||||
{
|
||||
var stream = new MemoryStream(bytes);
|
||||
SectorMode actual = stream.GetSectorMode();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SectorMode.UNKNOWN, 2336)]
|
||||
[InlineData(SectorMode.MODE0, 2336)]
|
||||
[InlineData(SectorMode.MODE1, 2048)]
|
||||
[InlineData(SectorMode.MODE2, 2336)]
|
||||
[InlineData(SectorMode.MODE2_FORM1, 2048)]
|
||||
[InlineData(SectorMode.MODE2_FORM2, 2324)]
|
||||
public void GetUserDataSizeTest(SectorMode mode, long expected)
|
||||
{
|
||||
long actual = mode.GetUserDataSize();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SectorMode.UNKNOWN, 2064)]
|
||||
[InlineData(SectorMode.MODE0, 2064)]
|
||||
[InlineData(SectorMode.MODE1, 2064)]
|
||||
[InlineData(SectorMode.MODE2, 2064)]
|
||||
[InlineData(SectorMode.MODE2_FORM1, 2072)]
|
||||
[InlineData(SectorMode.MODE2_FORM2, 2072)]
|
||||
public void GetUserDataEndTest(SectorMode mode, long expected)
|
||||
{
|
||||
long actual = mode.GetUserDataEnd();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SectorMode.UNKNOWN, 16)]
|
||||
[InlineData(SectorMode.MODE0, 16)]
|
||||
[InlineData(SectorMode.MODE1, 16)]
|
||||
[InlineData(SectorMode.MODE2, 16)]
|
||||
[InlineData(SectorMode.MODE2_FORM1, 24)]
|
||||
[InlineData(SectorMode.MODE2_FORM2, 24)]
|
||||
public void GetUserDataStart(SectorMode mode, long expected)
|
||||
{
|
||||
long actual = mode.GetUserDataStart();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
// TODO: Figure out how to add UserDataStream tests
|
||||
}
|
||||
}
|
||||
17
SabreTools.Data.Extensions.Test/CFBExtensionsTests.cs
Normal file
17
SabreTools.Data.Extensions.Test/CFBExtensionsTests.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class CFBExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(null, null)]
|
||||
[InlineData(new byte[0], "")]
|
||||
// TODO: Create more artifical tests
|
||||
public void DecodeStreamNameTest(byte[]? bytes, string? expected)
|
||||
{
|
||||
string? actual = bytes.DecodeStreamName();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
138
SabreTools.Data.Extensions.Test/ISO9660ExtensionsTests.cs
Normal file
138
SabreTools.Data.Extensions.Test/ISO9660ExtensionsTests.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using SabreTools.Data.Models.ISO9660;
|
||||
using SabreTools.Numerics;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class ISO9660ExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_Generic_SectorLength()
|
||||
{
|
||||
VolumeDescriptor vd = new GenericVolumeDescriptor();
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(sectorLength, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_PVD_ValidBothEndian_ValidBlockSize_BlockSize()
|
||||
{
|
||||
VolumeDescriptor vd = new PrimaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(2048, 2048)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(2048, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_PVD_ValidBothEndian_InvalidBlockSize_SectorLength()
|
||||
{
|
||||
VolumeDescriptor vd = new PrimaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(2352, 2352)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(4096, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_PVD_InvalidBothEndian_ValidLE_LEValue()
|
||||
{
|
||||
VolumeDescriptor vd = new PrimaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(2048, -1)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(2048, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_PVD_InvalidBothEndian_ValidBE_BEValue()
|
||||
{
|
||||
VolumeDescriptor vd = new PrimaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(-1, 2048)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(2048, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_PVD_InvalidBothEndian_BothInvalid_SectorLength()
|
||||
{
|
||||
VolumeDescriptor vd = new PrimaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(-1, -2)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(sectorLength, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_SVD_ValidBothEndian_ValidBlockSize_BlockSize()
|
||||
{
|
||||
VolumeDescriptor vd = new SupplementaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(2048, 2048)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(2048, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_SVD_ValidBothEndian_InvalidBlockSize_SectorLength()
|
||||
{
|
||||
VolumeDescriptor vd = new SupplementaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(2352, 2352)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(4096, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_SVD_InvalidBothEndian_ValidLE_LEValue()
|
||||
{
|
||||
VolumeDescriptor vd = new SupplementaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(2048, -1)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(2048, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_SVD_InvalidBothEndian_ValidBE_BEValue()
|
||||
{
|
||||
VolumeDescriptor vd = new SupplementaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(-1, 2048)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(2048, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_SVD_InvalidBothEndian_BothInvalid_SectorLength()
|
||||
{
|
||||
VolumeDescriptor vd = new SupplementaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(-1, -2)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(sectorLength, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
using SabreTools.Data.Models.InstallShieldCabinet;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class InstallShieldCabinetExtensionsTests
|
||||
{
|
||||
#region IsCompressed
|
||||
|
||||
[Fact]
|
||||
public void IsCompressed_Null_True()
|
||||
{
|
||||
FileDescriptor? fd = null;
|
||||
bool actual = fd.IsCompressed();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsCompressed_Compressed_True()
|
||||
{
|
||||
FileDescriptor? fd = new FileDescriptor { Flags = FileFlags.FILE_COMPRESSED };
|
||||
bool actual = fd.IsCompressed();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsCompressed_NotCompressed_False()
|
||||
{
|
||||
FileDescriptor? fd = new FileDescriptor { Flags = 0 };
|
||||
bool actual = fd.IsCompressed();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsInvalid
|
||||
|
||||
[Fact]
|
||||
public void IsInvalid_Null_True()
|
||||
{
|
||||
FileDescriptor? fd = null;
|
||||
bool actual = fd.IsInvalid();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsInvalid_Invalid_True()
|
||||
{
|
||||
FileDescriptor? fd = new FileDescriptor { Flags = FileFlags.FILE_INVALID };
|
||||
bool actual = fd.IsInvalid();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsInvalid_Valid_False()
|
||||
{
|
||||
FileDescriptor? fd = new FileDescriptor { Flags = 0 };
|
||||
bool actual = fd.IsInvalid();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsObfuscated
|
||||
|
||||
[Fact]
|
||||
public void IsObfuscated_Null_False()
|
||||
{
|
||||
FileDescriptor? fd = null;
|
||||
bool actual = fd.IsObfuscated();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsObfuscated_Obfuscated_True()
|
||||
{
|
||||
FileDescriptor? fd = new FileDescriptor { Flags = FileFlags.FILE_OBFUSCATED };
|
||||
bool actual = fd.IsObfuscated();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsObfuscated_NotObfuscated_False()
|
||||
{
|
||||
FileDescriptor? fd = new FileDescriptor { Flags = 0 };
|
||||
bool actual = fd.IsObfuscated();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsSplit
|
||||
|
||||
[Fact]
|
||||
public void IsSplit_Null_False()
|
||||
{
|
||||
FileDescriptor? fd = null;
|
||||
bool actual = fd.IsSplit();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsSplit_Split_True()
|
||||
{
|
||||
FileDescriptor? fd = new FileDescriptor { Flags = FileFlags.FILE_SPLIT };
|
||||
bool actual = fd.IsSplit();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsSplit_NotSplitFalse()
|
||||
{
|
||||
FileDescriptor? fd = new FileDescriptor { Flags = 0 };
|
||||
bool actual = fd.IsSplit();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetMajorVersion
|
||||
|
||||
[Fact]
|
||||
public void GetMajorVersion_NullCabinet_NegativeOne()
|
||||
{
|
||||
Cabinet? cabinet = null;
|
||||
int actual = cabinet.GetMajorVersion();
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMajorVersion_NullHeader_NegativeOne()
|
||||
{
|
||||
CommonHeader? cabinet = null;
|
||||
int actual = cabinet.GetMajorVersion();
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x00000000, 0)]
|
||||
[InlineData(0x00000004, 4)]
|
||||
[InlineData(0x01000000, 0)]
|
||||
[InlineData(0x01004000, 4)]
|
||||
[InlineData(0x02000000, 0)]
|
||||
[InlineData(0x02000190, 4)]
|
||||
[InlineData(0x04000000, 0)]
|
||||
[InlineData(0x04000190, 4)]
|
||||
public void GetMajorVersionTest(uint version, int expected)
|
||||
{
|
||||
CommonHeader? cabinet = new CommonHeader { Version = version };
|
||||
int actual = cabinet.GetMajorVersion();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
3564
SabreTools.Data.Extensions.Test/MetadataExtensionsTests.cs
Normal file
3564
SabreTools.Data.Extensions.Test/MetadataExtensionsTests.cs
Normal file
File diff suppressed because it is too large
Load Diff
206
SabreTools.Data.Extensions.Test/NESCartExtensionsTests.cs
Normal file
206
SabreTools.Data.Extensions.Test/NESCartExtensionsTests.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
using SabreTools.Data.Models.NES;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class NESCartExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(ConsoleType.StandardSystem, "Nintendo Entertainment System/Family Computer")]
|
||||
[InlineData(ConsoleType.VSUnisystem, "VS Unisystem")]
|
||||
[InlineData(ConsoleType.PlayChoice10, "PlayChoice-10 (8 KB of Hint Screen data stored after CHR data)")]
|
||||
[InlineData(ConsoleType.ExtendedConsoleType, "Extended Console Type")]
|
||||
public void FromConsoleTypeTest(ConsoleType type, string expected)
|
||||
{
|
||||
string actual = type.FromConsoleType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(CPUPPUTiming.RP2C02, "RP2C02 (NTSC NES)")]
|
||||
[InlineData(CPUPPUTiming.RP2C07, "RP2C07 (Licensed PAL NES)")]
|
||||
[InlineData(CPUPPUTiming.MultipleRegion, "Multiple-region")]
|
||||
[InlineData(CPUPPUTiming.UA6538, "UA6538 (Dendy)")]
|
||||
public void FromCPUPPUTimingTest(CPUPPUTiming timing, string expected)
|
||||
{
|
||||
string actual = timing.FromCPUPPUTiming();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(DefaultExpansionDevice.Unspecified, "Unspecified")]
|
||||
[InlineData(DefaultExpansionDevice.StandardControllers, "Standard NES/Famicom controllers")]
|
||||
[InlineData(DefaultExpansionDevice.NESFourScore, "NES Four Score/Satellite with two additional standard controllers")]
|
||||
[InlineData(DefaultExpansionDevice.FamicomFourPlayersAdapter, "Famicom Four Players Adapter with two additional standard controllers using the 'simple' protocol")]
|
||||
[InlineData(DefaultExpansionDevice.VsSystem4016, "Vs. System (1P via $4016)")]
|
||||
[InlineData(DefaultExpansionDevice.VsSystem4017, "Vs. System (1P via $4017)")]
|
||||
[InlineData(DefaultExpansionDevice.Reserved06, "Reserved (0x06)")]
|
||||
[InlineData(DefaultExpansionDevice.VsZapper, "Vs. Zapper")]
|
||||
[InlineData(DefaultExpansionDevice.Zapper4017, "Zapper ($4017)")]
|
||||
[InlineData(DefaultExpansionDevice.TwoZappers, "Two Zappers")]
|
||||
[InlineData(DefaultExpansionDevice.BandaiHyperShotLightgun, "Bandai Hyper Shot Lightgun")]
|
||||
[InlineData(DefaultExpansionDevice.PowerPadSideA, "Power Pad Side A")]
|
||||
[InlineData(DefaultExpansionDevice.PowerPadSideB, "Power Pad Side B")]
|
||||
[InlineData(DefaultExpansionDevice.FamilyTrainerSideA, "Family Trainer Side A")]
|
||||
[InlineData(DefaultExpansionDevice.FamilyTrainerSideB, "Family Trainer Side B")]
|
||||
[InlineData(DefaultExpansionDevice.ArkanoidVausControllerNES, "Arkanoid Vaus Controller (NES)")]
|
||||
[InlineData(DefaultExpansionDevice.ArkanoidVausControllerFamicom, "Arkanoid Vaus Controller (Famicom)")]
|
||||
[InlineData(DefaultExpansionDevice.TwoVausControllersPlusFamicomDataRecorder, "Two Vaus Controllers plus Famicom Data Recorder")]
|
||||
[InlineData(DefaultExpansionDevice.KonamiHyperShotController, "Konami Hyper Shot Controller")]
|
||||
[InlineData(DefaultExpansionDevice.CoconutsPachinkoController, "Coconuts Pachinko Controller")]
|
||||
[InlineData(DefaultExpansionDevice.ExcitingBoxingPunchingBag, "Exciting Boxing Punching Bag (Blowup Doll)")]
|
||||
[InlineData(DefaultExpansionDevice.JissenMahjongController, "Jissen Mahjong Controller")]
|
||||
[InlineData(DefaultExpansionDevice.YonezawaPartyTap, "米澤 (Yonezawa) Party Tap")]
|
||||
[InlineData(DefaultExpansionDevice.OekaKidsTablet, "Oeka Kids Tablet")]
|
||||
[InlineData(DefaultExpansionDevice.SunsoftBarcodeBattler, "Sunsoft Barcode Battler")]
|
||||
[InlineData(DefaultExpansionDevice.MiraclePianoKeyboard, "Miracle Piano Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.PokkunMoguraaTapTapMat, "Pokkun Moguraa Tap-tap Mat (Whack-a-Mole Mat and Mallet)")]
|
||||
[InlineData(DefaultExpansionDevice.TopRider, "Top Rider (Inflatable Bicycle)")]
|
||||
[InlineData(DefaultExpansionDevice.DoubleFisted, "Double-Fisted (Requires or allows use of two controllers by one player)")]
|
||||
[InlineData(DefaultExpansionDevice.Famicom3DSystem, "Famicom 3D System")]
|
||||
[InlineData(DefaultExpansionDevice.DoremikkoKeyboard, "Doremikko Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.ROBGyromite, "R.O.B. Gyromite")]
|
||||
[InlineData(DefaultExpansionDevice.FamicomDataRecorder, "Famicom Data Recorder ('silent' keyboard)")]
|
||||
[InlineData(DefaultExpansionDevice.ASCIITurboFile, "ASCII Turbo File")]
|
||||
[InlineData(DefaultExpansionDevice.IGSStorageBattleBox, "IGS Storage Battle Box")]
|
||||
[InlineData(DefaultExpansionDevice.FamilyBASICKeyboardPlusFamicomDataRecorder, "Family BASIC Keyboard plus Famicom Data Recorder")]
|
||||
[InlineData(DefaultExpansionDevice.DongdaPECKeyboard, "东达 (Dōngdá) PEC Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.BitCorpBit79Keyboard, "普澤 (Pǔzé, a.k.a. Bit Corp.) Bit-79 Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.SuborKeyboard, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusMacroWinnersMouse, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Macro Winners Mouse")]
|
||||
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusSuborMouse4016, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Subor Mouse via $4016")]
|
||||
[InlineData(DefaultExpansionDevice.SNESMouse4016, "SNES Mouse ($4016)")]
|
||||
[InlineData(DefaultExpansionDevice.Multicart, "Multicart")]
|
||||
[InlineData(DefaultExpansionDevice.TwoSNESControllers, "Two SNES controllers replacing the two standard NES controllers")]
|
||||
[InlineData(DefaultExpansionDevice.RacerMateBicycle, "RacerMate Bicycle")]
|
||||
[InlineData(DefaultExpansionDevice.UForce, "U-Force")]
|
||||
[InlineData(DefaultExpansionDevice.ROBStackUp, "R.O.B. Stack-Up")]
|
||||
[InlineData(DefaultExpansionDevice.CityPatrolmanLightgun, "City Patrolman Lightgun")]
|
||||
[InlineData(DefaultExpansionDevice.SharpC1CassetteInterface, "Sharp C1 Cassette Interface")]
|
||||
[InlineData(DefaultExpansionDevice.StandardControllerWithSwappedInputs, "Standard Controller with swapped Left-Right/Up-Down/B-A")]
|
||||
[InlineData(DefaultExpansionDevice.ExcaliburSudokuPad, "Excalibur Sudoku Pad")]
|
||||
[InlineData(DefaultExpansionDevice.ABLPinball, "ABL Pinball")]
|
||||
[InlineData(DefaultExpansionDevice.GoldenNuggetCasinoExtraButtons, "Golden Nugget Casino extra buttons")]
|
||||
[InlineData(DefaultExpansionDevice.KedaKeyboard, "科达 (Kēdá) Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusSuborMouse4017, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Subor Mouse via $4017")]
|
||||
[InlineData(DefaultExpansionDevice.PortTestController, "Port test controller")]
|
||||
[InlineData(DefaultExpansionDevice.BandaiMultiGamePlayerGamepad, "Bandai Multi Game Player Gamepad buttons")]
|
||||
[InlineData(DefaultExpansionDevice.VenomTVDanceMat, "Venom TV Dance Mat")]
|
||||
[InlineData(DefaultExpansionDevice.LGTVRemoteControl, "LG TV Remote Control")]
|
||||
[InlineData(DefaultExpansionDevice.FamicomNetworkController, "Famicom Network Controller")]
|
||||
[InlineData(DefaultExpansionDevice.KingFishingController, "King Fishing Controller")]
|
||||
[InlineData(DefaultExpansionDevice.CroakyKaraokeController, "Croaky Karaoke Controller")]
|
||||
[InlineData(DefaultExpansionDevice.KingwonKeyboard, "科王 (Kēwáng, a.k.a. Kingwon) Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.ZechengKeyboard, "泽诚 (Zéchéng) Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusL90RotatedPS2Mouse4017, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus L90-rotated PS/2 mouse in $4017")]
|
||||
[InlineData(DefaultExpansionDevice.PS2KeyboardInUM6578PS2PortPS2Mouse4017, "PS/2 Keyboard in UM6578 PS/2 port, PS/2 Mouse via $4017")]
|
||||
[InlineData(DefaultExpansionDevice.PS2MouseInUM6578PS2Port, "PS/2 Mouse in UM6578 PS/2 port")]
|
||||
[InlineData(DefaultExpansionDevice.YuxingMouse4016, "裕兴 (Yùxìng) Mouse via $4016")]
|
||||
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusYuxingMouse4016, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus 裕兴 (Yùxìng) Mouse mouse in $4016")]
|
||||
[InlineData(DefaultExpansionDevice.GigggleTVPump, "Gigggle TV Pump")]
|
||||
[InlineData(DefaultExpansionDevice.BBKKeyboardPlusR90RotatedPS2Mouse4017, "步步高 (Bùbùgāo, a.k.a. BBK) Keyboard plus R90-rotated PS/2 mouse in $4017")]
|
||||
[InlineData(DefaultExpansionDevice.MagicalCooking, "Magical Cooking")]
|
||||
[InlineData(DefaultExpansionDevice.SNESMouse4017, "SNES Mouse ($4017)")]
|
||||
[InlineData(DefaultExpansionDevice.Zapper4016, "Zapper ($4016)")]
|
||||
[InlineData(DefaultExpansionDevice.ArkanoidVausControllerPrototype, "Arkanoid Vaus Controller (Prototype)")]
|
||||
[InlineData(DefaultExpansionDevice.TVMahjongGameController, "TV 麻雀 Game (TV Mahjong Game) Controller")]
|
||||
[InlineData(DefaultExpansionDevice.MahjongGekitouDensetsuController, "麻雀激闘伝説 (Mahjong Gekitou Densetsu) Controller")]
|
||||
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusXInvertedPS2Mouse4017, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus X-inverted PS/2 mouse in $4017")]
|
||||
[InlineData(DefaultExpansionDevice.IBMPCXTKeyboard, "IBM PC/XT Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusMegaBookMouse, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Mega Book Mouse")]
|
||||
public void FromDefaultExpansionDeviceTest(DefaultExpansionDevice device, string expected)
|
||||
{
|
||||
string actual = device.FromDefaultExpansionDevice();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ExtendedConsoleType.RegularSystem, "Regular NES/Famicom/Dendy")]
|
||||
[InlineData(ExtendedConsoleType.NintendoVsSystem, "Nintendo Vs. System")]
|
||||
[InlineData(ExtendedConsoleType.Playchoice10, "Playchoice 10")]
|
||||
[InlineData(ExtendedConsoleType.RegularFamicloneDecimalMode, "Regular Famiclone, but with CPU that supports Decimal Mode")]
|
||||
[InlineData(ExtendedConsoleType.RegularNESWithEPSM, "Regular NES/Famicom with EPSM module or plug-through cartridge")]
|
||||
[InlineData(ExtendedConsoleType.VRTechnologyVT01, "V.R. Technology VT01 with red/cyan STN palette")]
|
||||
[InlineData(ExtendedConsoleType.VRTechnologyVT02, "V.R. Technology VT02")]
|
||||
[InlineData(ExtendedConsoleType.VRTechnologyVT03, "V.R. Technology VT03")]
|
||||
[InlineData(ExtendedConsoleType.VRTechnologyVT09, "V.R. Technology VT09")]
|
||||
[InlineData(ExtendedConsoleType.VRTechnologyVT32, "V.R. Technology VT32")]
|
||||
[InlineData(ExtendedConsoleType.VRTechnologyVT369, "V.R. Technology VT369")]
|
||||
[InlineData(ExtendedConsoleType.UMCUM6578, "UMC UM6578")]
|
||||
[InlineData(ExtendedConsoleType.FamicomNetworkSystem, "Famicom Network System")]
|
||||
[InlineData(ExtendedConsoleType.ReservedD, "Reserved (0x0D)")]
|
||||
[InlineData(ExtendedConsoleType.ReservedE, "Reserved (0x0E)")]
|
||||
[InlineData(ExtendedConsoleType.ReservedF, "Reserved (0x0F)")]
|
||||
public void FromExtendedConsoleTypeTest(ExtendedConsoleType type, string expected)
|
||||
{
|
||||
string actual = type.FromExtendedConsoleType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(NametableArrangement.Vertical, "Vertical")]
|
||||
[InlineData(NametableArrangement.Horizontal, "Horizontal")]
|
||||
public void FromNametableArrangementTest(NametableArrangement arrangement, string expected)
|
||||
{
|
||||
string actual = arrangement.FromNametableArrangement();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(TVSystem.NTSC, "NTSC")]
|
||||
[InlineData(TVSystem.PAL, "PAL")]
|
||||
public void FromTVSystemTest(TVSystem system, string expected)
|
||||
{
|
||||
string actual = system.FromTVSystem();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(TVSystemExtended.NTSC, "NTSC")]
|
||||
[InlineData(TVSystemExtended.DualCompatible1, "Dual-compatible (0x01)")]
|
||||
[InlineData(TVSystemExtended.PAL, "PAL")]
|
||||
[InlineData(TVSystemExtended.DualCompatible3, "Dual-compatible (0x03)")]
|
||||
public void FromTVSystemExtendedTest(TVSystemExtended system, string expected)
|
||||
{
|
||||
string actual = system.FromTVSystemExtended();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(VsHardwareType.VsUnisystem, "Vs. Unisystem (normal)")]
|
||||
[InlineData(VsHardwareType.VsUnisystemRBIBaseballProtection, "Vs. Unisystem (RBI Baseball protection)")]
|
||||
[InlineData(VsHardwareType.VsUnisystemTKOBoxingProtection, "Vs. Unisystem (TKO Boxing protection)")]
|
||||
[InlineData(VsHardwareType.VsUnisystemSuperXeviousProtection, "Vs. Unisystem (Super Xevious protection)")]
|
||||
[InlineData(VsHardwareType.VsUnisystemVsIceClimberJapanProtection, "Vs. Unisystem (Vs. Ice Climber Japan protection)")]
|
||||
[InlineData(VsHardwareType.VsDualSystem, "Vs. Dual System (normal)")]
|
||||
[InlineData(VsHardwareType.VsDualSystemRaidOnBungelingBayProtection, "Vs. Dual System (Raid on Bungeling Bay protection)")]
|
||||
public void FromVsHardwareTypeTest(VsHardwareType type, string expected)
|
||||
{
|
||||
string actual = type.FromVsHardwareType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(VsSystemType.AnyRP2C03RC2C03Variant, "Any RP2C03/RC2C03 variant")]
|
||||
[InlineData(VsSystemType.Reserved1, "Reserved (0x01)")]
|
||||
[InlineData(VsSystemType.RP2C040001, "RP2C04-0001")]
|
||||
[InlineData(VsSystemType.RP2C040002, "RP2C04-0002")]
|
||||
[InlineData(VsSystemType.RP2C040003, "RP2C04-0003")]
|
||||
[InlineData(VsSystemType.RP2C040004, "RP2C04-0004")]
|
||||
[InlineData(VsSystemType.Reserved6, "Reserved (0x06)")]
|
||||
[InlineData(VsSystemType.Reserved7, "Reserved (0x07)")]
|
||||
[InlineData(VsSystemType.RC2C0501, "RC2C05-01 (signature unknown)")]
|
||||
[InlineData(VsSystemType.RC2C0502, "RC2C05-02 ($2002 AND $3F =$3D)")]
|
||||
[InlineData(VsSystemType.RC2C0503, "RC2C05-03 ($2002 AND $1F =$1C)")]
|
||||
[InlineData(VsSystemType.RC2C0504, "RC2C05-04 ($2002 AND $1F =$1B)")]
|
||||
[InlineData(VsSystemType.ReservedC, "Reserved (0x0C)")]
|
||||
[InlineData(VsSystemType.ReservedD, "Reserved (0x0D)")]
|
||||
[InlineData(VsSystemType.ReservedE, "Reserved (0x0E)")]
|
||||
[InlineData(VsSystemType.ReservedF, "Reserved (0x0F)")]
|
||||
public void FromVsSystemTypeTest(VsSystemType type, string expected)
|
||||
{
|
||||
string actual = type.FromVsSystemType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using SabreTools.Data.Models.NewExecutable;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class NewExecutableExtensionsTests
|
||||
{
|
||||
#region IsIntegerType
|
||||
|
||||
[Fact]
|
||||
public void IsIntegerType_RTIE_HighBitSet_True()
|
||||
{
|
||||
var entry = new ResourceTypeInformationEntry { TypeID = 0xFFFF };
|
||||
bool actual = entry.IsIntegerType();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsIntegerType_RTIE_HighBitClear_False()
|
||||
{
|
||||
var entry = new ResourceTypeInformationEntry { TypeID = 0x0000 };
|
||||
bool actual = entry.IsIntegerType();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsIntegerType_RTRE_HighBitSet_True()
|
||||
{
|
||||
var entry = new ResourceTypeResourceEntry { ResourceID = 0xFFFF };
|
||||
bool actual = entry.IsIntegerType();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsIntegerType_RTRE_HighBitClear_False()
|
||||
{
|
||||
var entry = new ResourceTypeResourceEntry { ResourceID = 0x0000 };
|
||||
bool actual = entry.IsIntegerType();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetEntryType
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x00, SegmentEntryType.Unused)]
|
||||
[InlineData(0x01, SegmentEntryType.FixedSegment)]
|
||||
[InlineData(0xAA, SegmentEntryType.FixedSegment)]
|
||||
[InlineData(0xFE, SegmentEntryType.FixedSegment)]
|
||||
[InlineData(0xFF, SegmentEntryType.MoveableSegment)]
|
||||
public void GetEntryTypeTest(byte segmentIndicator, SegmentEntryType expected)
|
||||
{
|
||||
var entry = new EntryTableBundle { SegmentIndicator = segmentIndicator };
|
||||
SegmentEntryType actual = entry.GetEntryType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
129
SabreTools.Data.Extensions.Test/NintendoDiscExtensionsTests.cs
Normal file
129
SabreTools.Data.Extensions.Test/NintendoDiscExtensionsTests.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using SabreTools.Data.Models.NintendoDisc;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class NintendoDiscExtensionsTests
|
||||
{
|
||||
#region GetPlatform
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Object_WiiMagic_Wii()
|
||||
{
|
||||
var header = new DiscHeader { WiiMagic = Constants.WiiMagicWord };
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.Wii, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Object_GCMagic_GC()
|
||||
{
|
||||
var header = new DiscHeader { GCMagic = Constants.GCMagicWord };
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.GameCube, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Object_ValidGameID_GC()
|
||||
{
|
||||
var header = new DiscHeader { GameId = "GABC01" };
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.GameCube, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Object_InvalidGameID_Unknown()
|
||||
{
|
||||
var header = new DiscHeader { GameId = "XABC01" };
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.Unknown, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Object_DefaultHeader_Unknown()
|
||||
{
|
||||
var header = new DiscHeader();
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.Unknown, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Bytes_WiiMagic_Wii()
|
||||
{
|
||||
byte[] header =
|
||||
[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00-0x07
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08-0x0F
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10-0x17
|
||||
0x5D, 0x1C, 0x9E, 0xA3, 0x00, 0x00, 0x00, 0x00, // 0x18-0x1F
|
||||
];
|
||||
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.Wii, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Bytes_GCMagic_GC()
|
||||
{
|
||||
byte[] header =
|
||||
[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00-0x07
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08-0x0F
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10-0x17
|
||||
0x00, 0x00, 0x00, 0x00, 0xC2, 0x33, 0x9F, 0x3D, // 0x18-0x1F
|
||||
];
|
||||
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.GameCube, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Bytes_ValidGameID_GC()
|
||||
{
|
||||
byte[] header = [(byte)'G', (byte)'A', (byte)'B', (byte)'C'];
|
||||
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.GameCube, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Bytes_InvalidGameID_Unknown()
|
||||
{
|
||||
byte[] header = [(byte)'G', (byte)'A', (byte)'B', 0x00];
|
||||
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.Unknown, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Bytes_DefaultHeader_Unknown()
|
||||
{
|
||||
byte[] header = [];
|
||||
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.Unknown, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsGameCubeTitleType
|
||||
|
||||
[Theory]
|
||||
[InlineData('\0', false)]
|
||||
[InlineData('a', false)]
|
||||
[InlineData('A', false)]
|
||||
[InlineData('d', false)]
|
||||
[InlineData('D', true)]
|
||||
[InlineData('g', false)]
|
||||
[InlineData('G', true)]
|
||||
[InlineData('r', false)]
|
||||
[InlineData('R', true)]
|
||||
public void IsGameCubeTitleTypeTest(char c, bool expected)
|
||||
{
|
||||
bool actual = c.IsGameCubeTitleType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
26
SabreTools.Data.Extensions.Test/OperaFSExtensionsTests.cs
Normal file
26
SabreTools.Data.Extensions.Test/OperaFSExtensionsTests.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using SabreTools.Data.Models.OperaFS;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class OperaFSExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void DirectoryDescriptor_EqualsExactly_Empty()
|
||||
{
|
||||
var dir1 = new DirectoryDescriptor();
|
||||
var dir2 = new DirectoryDescriptor();
|
||||
bool actual = dir1.EqualsExactly(dir2);
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectoryRecord_EqualsExactly_Empty()
|
||||
{
|
||||
var dr1 = new DirectoryRecord();
|
||||
var dr2 = new DirectoryRecord();
|
||||
bool actual = dr1.EqualsExactly(dr2);
|
||||
Assert.True(actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="10.0.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.Data.Extensions\SabreTools.Data.Extensions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
213
SabreTools.Data.Extensions.Test/TypeLengthValueTests.cs
Normal file
213
SabreTools.Data.Extensions.Test/TypeLengthValueTests.cs
Normal file
@@ -0,0 +1,213 @@
|
||||
using System;
|
||||
using SabreTools.Data.Models.ASN1;
|
||||
using Xunit;
|
||||
|
||||
#pragma warning disable IDE0230 // Use UTF-8 string literal
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class TypeLengthValueTests
|
||||
{
|
||||
#region Formatting
|
||||
|
||||
[Fact]
|
||||
public void Format_EOC()
|
||||
{
|
||||
string expected = "Type: V_ASN1_EOC";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_EOC, Length = 0, Value = null };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_ZeroLength()
|
||||
{
|
||||
string expected = "Type: V_ASN1_NULL, Length: 0";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 0, Value = null };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_InvalidConstructed()
|
||||
{
|
||||
string expected = "Type: V_ASN1_OBJECT, V_ASN1_CONSTRUCTED, Length: 1, Value: [INVALID DATA TYPE]";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 1, Value = false };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_ValidConstructed()
|
||||
{
|
||||
string expected = "Type: V_ASN1_OBJECT, V_ASN1_CONSTRUCTED, Length: 3, Value:\n Type: V_ASN1_BOOLEAN, Length: 1, Value: True";
|
||||
var boolTlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 3, Value = new TypeLengthValue[] { boolTlv } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_InvalidDataType()
|
||||
{
|
||||
string expected = "Type: V_ASN1_OBJECT, Length: 1, Value: [INVALID DATA TYPE]";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT, Length = 1, Value = false };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_InvalidLength()
|
||||
{
|
||||
string expected = "Type: V_ASN1_NULL, Length: 1, Value: [NO DATA]";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 1, Value = Array.Empty<byte>() };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_InvalidBooleanLength()
|
||||
{
|
||||
string expected = "Type: V_ASN1_BOOLEAN, Length: 2 [Expected length of 1], Value: True";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 2, Value = new byte[] { 0x01 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_InvalidBooleanArrayLength()
|
||||
{
|
||||
string expected = "Type: V_ASN1_BOOLEAN, Length: 1 [Expected value length of 1], Value: True";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01, 0x00 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_ValidBoolean()
|
||||
{
|
||||
string expected = "Type: V_ASN1_BOOLEAN, Length: 1, Value: True";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_ValidInteger()
|
||||
{
|
||||
string expected = "Type: V_ASN1_INTEGER, Length: 1, Value: 1";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_INTEGER, Length = 1, Value = new byte[] { 0x01 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_ValidBitString_NoBits()
|
||||
{
|
||||
string expected = "Type: V_ASN1_BIT_STRING, Length: 1, Value with 0 unused bits";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x00 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_ValidBitString_Bits()
|
||||
{
|
||||
string expected = "Type: V_ASN1_BIT_STRING, Length: 1, Value with 1 unused bits: 01";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x01, 0x01 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_ValidOctetString()
|
||||
{
|
||||
string expected = "Type: V_ASN1_OCTET_STRING, Length: 1, Value: 01";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OCTET_STRING, Length = 1, Value = new byte[] { 0x01 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_ValidObject()
|
||||
{
|
||||
string expected = "Type: V_ASN1_OBJECT, Length: 3, Value: 0.1.2.3 (/ITU-T/1/2/3)";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT, Length = 3, Value = new byte[] { 0x01, 0x02, 0x03 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_ValidUTF8String()
|
||||
{
|
||||
string expected = "Type: V_ASN1_UTF8STRING, Length: 3, Value: ABC";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_UTF8STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_ValidPrintableString()
|
||||
{
|
||||
string expected = "Type: V_ASN1_PRINTABLESTRING, Length: 3, Value: ABC";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_PRINTABLESTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_ValidTeletexString()
|
||||
{
|
||||
string expected = "Type: V_ASN1_TELETEXSTRING, Length: 3, Value: ABC";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_TELETEXSTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_ValidIA5String()
|
||||
{
|
||||
string expected = "Type: V_ASN1_IA5STRING, Length: 3, Value: ABC";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_IA5STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_InvalidUTCTime()
|
||||
{
|
||||
string expected = "Type: V_ASN1_UTCTIME, Length: 3, Value: ABC";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_UTCTIME, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_ValidUTCTime()
|
||||
{
|
||||
string expected = "Type: V_ASN1_UTCTIME, Length: 3, Value: 1980-01-01 00:00:00";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_UTCTIME, Length = 3, Value = new byte[] { 0x31, 0x39, 0x38, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31, 0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_ValidBmpString()
|
||||
{
|
||||
string expected = "Type: V_ASN1_BMPSTRING, Length: 6, Value: ABC";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BMPSTRING, Length = 6, Value = new byte[] { 0x41, 0x00, 0x42, 0x00, 0x43, 0x00 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Format_ValidUnformatted()
|
||||
{
|
||||
string expected = "Type: V_ASN1_OBJECT_DESCRIPTOR, Length: 1, Value: 01";
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT_DESCRIPTOR, Length = 1, Value = new byte[] { 0x01 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
59
SabreTools.Data.Extensions.Test/WiseScriptExtensionsTests.cs
Normal file
59
SabreTools.Data.Extensions.Test/WiseScriptExtensionsTests.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class WiseScriptExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
// Defined functions
|
||||
[InlineData("f0", "Add Directory to PATH")]
|
||||
[InlineData("f1", "Add to AUTOEXEC.BAT")]
|
||||
[InlineData("f2", "Add to CONFIG.SYS")]
|
||||
[InlineData("f3", "Add to SYSTEM.INI")]
|
||||
[InlineData("f8", "Read INI Value")]
|
||||
[InlineData("f9", "Get Registry Key Value")]
|
||||
[InlineData("f10", "Register Font")]
|
||||
[InlineData("f11", "Win32 System Directory")]
|
||||
[InlineData("f12", "Check Configuration")]
|
||||
[InlineData("f13", "Search for File")]
|
||||
[InlineData("f15", "Read/Write Binary File")]
|
||||
[InlineData("f16", "Set Variable")]
|
||||
[InlineData("f17", "Get Environment Variable")]
|
||||
[InlineData("f19", "Check if File/Dir Exists")]
|
||||
[InlineData("f20", "Set File Attributes")]
|
||||
[InlineData("f21", "Set Files/Buffers")]
|
||||
[InlineData("f22", "Find File in Path")]
|
||||
[InlineData("f23", "Check Disk Space")]
|
||||
[InlineData("f25", "Insert Line Into Text File")]
|
||||
[InlineData("f27", "Parse String")]
|
||||
[InlineData("f28", "Exit Installation")]
|
||||
[InlineData("f29", "Self-Register OCXs/DLLs")]
|
||||
[InlineData("f30", "Install DirectX Components")]
|
||||
[InlineData("f31", "Wizard Block")]
|
||||
[InlineData("f33", "Read/Update Text File")]
|
||||
[InlineData("f34", "Post to HTTP Server")]
|
||||
[InlineData("f35", "Prompt for Filename")]
|
||||
[InlineData("f36", "Start/Stop Service")]
|
||||
[InlineData("f38", "Check HTTP Connection")]
|
||||
// Undefined functions
|
||||
[InlineData("f4", "UNDEFINED f4")]
|
||||
[InlineData("f5", "UNDEFINED f5")]
|
||||
[InlineData("f6", "UNDEFINED f6")]
|
||||
[InlineData("f7", "UNDEFINED f7")]
|
||||
[InlineData("f14", "UNDEFINED f14")]
|
||||
[InlineData("f18", "UNDEFINED f18")]
|
||||
[InlineData("f24", "UNDEFINED f24")]
|
||||
[InlineData("f26", "UNDEFINED f26")]
|
||||
[InlineData("f32", "UNDEFINED f32")]
|
||||
[InlineData("f37", "UNDEFINED f37")]
|
||||
// External DLL
|
||||
[InlineData(null, null)]
|
||||
[InlineData("f99", "UNDEFINED f99")]
|
||||
[InlineData("func", "External: func")]
|
||||
public void FromWiseFunctionIdTest(string? functionId, string? expected)
|
||||
{
|
||||
string? actual = functionId.FromWiseFunctionId();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
SabreTools.Data.Extensions.Test/XZExtensionsTests.cs
Normal file
20
SabreTools.Data.Extensions.Test/XZExtensionsTests.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class XZExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void VariableLengthIntegerTest()
|
||||
{
|
||||
ulong expected = 123456789;
|
||||
byte[] encoded = expected.EncodeVariableLength();
|
||||
Assert.True(encoded.SequenceEqual<byte>([0x95, 0x9A, 0xEF, 0x3A]));
|
||||
|
||||
ulong actual = encoded.DecodeVariableLength(maxSize: 16, out int length);
|
||||
Assert.Equal(expected, actual);
|
||||
Assert.Equal(4, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class XboxExecutableExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(0x00000000, "0000-000")]
|
||||
[InlineData(0x4142000F, "AB-015")]
|
||||
[InlineData(0x3132F000, "12-61440")]
|
||||
public void ToFormattedXBETitleIDTest(uint value, string expected)
|
||||
{
|
||||
string actual = value.ToFormattedXBETitleID();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
SabreTools.Data.Extensions.Test/ZArchiveExtensionsTests.cs
Normal file
18
SabreTools.Data.Extensions.Test/ZArchiveExtensionsTests.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using SabreTools.Data.Models.ZArchive;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class ZArchiveExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetName_Null()
|
||||
{
|
||||
var de = new DirectoryEntry();
|
||||
NameTable nt = new NameTable();
|
||||
string? expected = null;
|
||||
string? actual = de.GetName(nt);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
327
SabreTools.Data.Extensions/Atari7800CartExtensions.cs
Normal file
327
SabreTools.Data.Extensions/Atari7800CartExtensions.cs
Normal file
@@ -0,0 +1,327 @@
|
||||
using System.Collections.Generic;
|
||||
using SabreTools.Data.Models.Atari7800;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class Atari7800CartExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a <see cref="AudioDevice"/> value to string
|
||||
/// </summary>
|
||||
public static string FromAudioDevice(this AudioDevice audio)
|
||||
{
|
||||
string[] devices = new string[4];
|
||||
|
||||
byte pokey = (byte)((byte)audio & 0x07);
|
||||
devices[0] = pokey switch
|
||||
{
|
||||
0 => "POKEY - none",
|
||||
1 => "POKEY - @440",
|
||||
2 => "POKEY - @450",
|
||||
3 => "POKEY - @450+@440",
|
||||
4 => "POKEY - @800",
|
||||
5 => "POKEY - @4000",
|
||||
_ => $"Unknown {pokey}",
|
||||
};
|
||||
|
||||
byte ym2151 = (byte)(((byte)audio >> 3) & 0x01);
|
||||
devices[1] = ym2151 switch
|
||||
{
|
||||
0 => "No YM2151 @460",
|
||||
1 => "YM2151 @460",
|
||||
_ => $"Unknown {ym2151}",
|
||||
};
|
||||
|
||||
byte covox = (byte)(((byte)audio >> 4) & 0x01);
|
||||
devices[2] = covox switch
|
||||
{
|
||||
0 => "No COVOX @430",
|
||||
1 => "COVOX @430",
|
||||
_ => $"Unknown {covox}",
|
||||
};
|
||||
|
||||
byte adpcm = (byte)(((byte)audio >> 5) & 0x01);
|
||||
devices[3] = adpcm switch
|
||||
{
|
||||
0 => "No ADPCM Audio Stream @420",
|
||||
1 => "ADPCM Audio Stream @420",
|
||||
_ => $"Unknown {adpcm}",
|
||||
};
|
||||
|
||||
return string.Join(", ", [.. devices]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="CartType"/> value to string
|
||||
/// </summary>
|
||||
public static string FromCartType(this CartType type)
|
||||
{
|
||||
List<string> types = [];
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((type & CartType.PokeyAt4000) != 0)
|
||||
#else
|
||||
if (type.HasFlag(CartType.PokeyAt4000))
|
||||
#endif
|
||||
types.Add("pokey at $4000");
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((type & CartType.SupergameBankSwitched) != 0)
|
||||
#else
|
||||
if (type.HasFlag(CartType.SupergameBankSwitched))
|
||||
#endif
|
||||
types.Add("supergame bank switched");
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((type & CartType.SupergameRamAt4000) != 0)
|
||||
#else
|
||||
if (type.HasFlag(CartType.SupergameRamAt4000))
|
||||
#endif
|
||||
types.Add("supergame ram at $4000");
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((type & CartType.RomAt4000) != 0)
|
||||
#else
|
||||
if (type.HasFlag(CartType.RomAt4000))
|
||||
#endif
|
||||
types.Add("rom at $4000");
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((type & CartType.Bank6At4000) != 0)
|
||||
#else
|
||||
if (type.HasFlag(CartType.Bank6At4000))
|
||||
#endif
|
||||
types.Add("bank 6 at $4000");
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((type & CartType.BankedRam) != 0)
|
||||
#else
|
||||
if (type.HasFlag(CartType.BankedRam))
|
||||
#endif
|
||||
types.Add("banked ram");
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((type & CartType.PokeyAt450) != 0)
|
||||
#else
|
||||
if (type.HasFlag(CartType.PokeyAt450))
|
||||
#endif
|
||||
types.Add("pokey at $450");
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((type & CartType.MirrorRamAt4000) != 0)
|
||||
#else
|
||||
if (type.HasFlag(CartType.MirrorRamAt4000))
|
||||
#endif
|
||||
types.Add("mirror ram at $4000");
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((type & CartType.ActivisionBanking) != 0)
|
||||
#else
|
||||
if (type.HasFlag(CartType.ActivisionBanking))
|
||||
#endif
|
||||
types.Add("activision banking");
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((type & CartType.AbsoluteBanking) != 0)
|
||||
#else
|
||||
if (type.HasFlag(CartType.AbsoluteBanking))
|
||||
#endif
|
||||
types.Add("absolute banking");
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((type & CartType.PokeyAt440) != 0)
|
||||
#else
|
||||
if (type.HasFlag(CartType.PokeyAt440))
|
||||
#endif
|
||||
types.Add("pokey at $440");
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((type & CartType.Ym2151At460461) != 0)
|
||||
#else
|
||||
if (type.HasFlag(CartType.Ym2151At460461))
|
||||
#endif
|
||||
types.Add("ym2151 at $460/$461");
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((type & CartType.Souper) != 0)
|
||||
#else
|
||||
if (type.HasFlag(CartType.Souper))
|
||||
#endif
|
||||
types.Add("souper");
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((type & CartType.Banksets) != 0)
|
||||
#else
|
||||
if (type.HasFlag(CartType.Banksets))
|
||||
#endif
|
||||
types.Add("banksets");
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((type & CartType.HaltBankedRam) != 0)
|
||||
#else
|
||||
if (type.HasFlag(CartType.HaltBankedRam))
|
||||
#endif
|
||||
types.Add("halt banked ram");
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((type & CartType.PokeyAt800) != 0)
|
||||
#else
|
||||
if (type.HasFlag(CartType.PokeyAt800))
|
||||
#endif
|
||||
types.Add("pokey@800");
|
||||
|
||||
// If no flags are set
|
||||
if (types.Count == 0)
|
||||
types.Add("N/A");
|
||||
|
||||
return string.Join(", ", [.. types]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="ControllerType"/> value to string
|
||||
/// </summary>
|
||||
public static string FromControllerType(this ControllerType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
ControllerType.None => "none",
|
||||
ControllerType.Joystick => "7800 joystick",
|
||||
ControllerType.Lightgun => "lightgun",
|
||||
ControllerType.Paddle => "paddle",
|
||||
ControllerType.Trakball => "trakball",
|
||||
ControllerType.VcsJoystick => "2600 joystick",
|
||||
ControllerType.VcsDriving => "2600 driving",
|
||||
ControllerType.VcsKeypad => "2600 keypad",
|
||||
ControllerType.STMouse => "ST mouse",
|
||||
ControllerType.AmigaMouse => "Amiga mouse",
|
||||
ControllerType.AtariVoxSaveKey => "AtariVox/SaveKey",
|
||||
ControllerType.SNES2Atari => "SNES2Atari",
|
||||
ControllerType.Mega7800 => "Mega7800",
|
||||
_ => $"Unknown {(byte)type}",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="Interrupt"/> value to string
|
||||
/// </summary>
|
||||
public static string FromInterrupt(this Interrupt interrupt)
|
||||
{
|
||||
return interrupt switch
|
||||
{
|
||||
Interrupt.None => "None",
|
||||
Interrupt.Pokey1 => "POKEY 1 (@450 | @800 | @4000)",
|
||||
Interrupt.Pokey2 => "POKEY 2 (@440)",
|
||||
Interrupt.YM2151 => "YM2151",
|
||||
_ => $"Unknown {(byte)interrupt}",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="Mapper"/> value to string
|
||||
/// </summary>
|
||||
public static string FromMapper(this Mapper mapper)
|
||||
{
|
||||
return mapper switch
|
||||
{
|
||||
Mapper.Linear => "Linear",
|
||||
Mapper.SuperGame => "SuperGame",
|
||||
Mapper.Activision => "Activision",
|
||||
Mapper.Absolute => "Absolute",
|
||||
Mapper.Souper => "Souper",
|
||||
_ => $"Unknown {(byte)mapper}",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="MapperOptions"/> value to string
|
||||
/// </summary>
|
||||
public static string FromMapperOptions(this MapperOptions options)
|
||||
{
|
||||
string[] romOptions = new string[2];
|
||||
|
||||
byte option4000 = (byte)((byte)options & 0x07);
|
||||
romOptions[0] = option4000 switch
|
||||
{
|
||||
0 => "Option at @4000 - none",
|
||||
1 => "Option at @4000 - 16K RAM",
|
||||
2 => "Option at @4000 - 8K EXRAM/A8",
|
||||
3 => "Option at @4000 - 32K EXRAM/M2",
|
||||
4 => "Option at @4000 - EXROM",
|
||||
5 => "Option at @4000 - EXFIX",
|
||||
6 => "Option at @4000 - 32k EXRAM/X2",
|
||||
_ => $"Unknown {option4000}",
|
||||
};
|
||||
|
||||
byte romType = (byte)(((byte)options >> 7) & 0x01);
|
||||
romOptions[1] = romType switch
|
||||
{
|
||||
0 => "Standard ROM",
|
||||
1 => "Bankset ROM",
|
||||
_ => $"Unknown {romType}",
|
||||
};
|
||||
|
||||
return string.Join(", ", [.. romOptions]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="SaveDevice"/> value to string
|
||||
/// </summary>
|
||||
public static string FromSaveDevice(this SaveDevice device)
|
||||
{
|
||||
return device switch
|
||||
{
|
||||
SaveDevice.None => "None",
|
||||
SaveDevice.HSC => "HSC",
|
||||
SaveDevice.SaveKeyAtariVox => "SaveKey/AtariVox",
|
||||
_ => $"Unknown {(byte)device}",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="SlotPassthroughDevice"/> value to string
|
||||
/// </summary>
|
||||
public static string FromSlotPassthroughDevice(this SlotPassthroughDevice device)
|
||||
{
|
||||
return device switch
|
||||
{
|
||||
SlotPassthroughDevice.None => "None",
|
||||
SlotPassthroughDevice.XM => "XM",
|
||||
_ => $"Unknown {(byte)device}",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="TVType"/> value to string
|
||||
/// </summary>
|
||||
public static string FromTVType(this TVType type)
|
||||
{
|
||||
string[] types = new string[3];
|
||||
|
||||
byte standard = (byte)((byte)type & 0x01);
|
||||
types[0] = standard switch
|
||||
{
|
||||
0 => "NTSC",
|
||||
1 => "PAL",
|
||||
_ => $"Unknown {standard}",
|
||||
};
|
||||
|
||||
byte connection = (byte)(((byte)type >> 1) & 0x01);
|
||||
types[1] = connection switch
|
||||
{
|
||||
0 => "Component",
|
||||
1 => "Composite",
|
||||
_ => $"Unknown {standard}",
|
||||
};
|
||||
|
||||
byte region = (byte)(((byte)type >> 2) & 0x01);
|
||||
types[2] = region switch
|
||||
{
|
||||
0 => "Single-region",
|
||||
1 => "Multi-region",
|
||||
_ => $"Unknown {standard}",
|
||||
};
|
||||
|
||||
return string.Join(", ", [.. types]);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
SabreTools.Data.Extensions/AtariLynxCartExtensions.cs
Normal file
21
SabreTools.Data.Extensions/AtariLynxCartExtensions.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using SabreTools.Data.Models.AtariLynx;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class AtariLynxCartExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a <see cref="Rotation"/> value to string
|
||||
/// </summary>
|
||||
public static string FromRotation(this Rotation rotation)
|
||||
{
|
||||
return rotation switch
|
||||
{
|
||||
Rotation.NoRotation => "No rotation (horizontal, buttons right)",
|
||||
Rotation.RotateLeft => "Rotate left (vertical, buttons down)",
|
||||
Rotation.RotateRight => "Rotate right (vertical, buttons up)",
|
||||
_ => $"Unknown {(byte)rotation}",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
33
SabreTools.Data.Extensions/BSPExtensions.cs
Normal file
33
SabreTools.Data.Extensions/BSPExtensions.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using SabreTools.Data.Models.BSP;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class BSPExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a <see cref="BspLumpType"/> value to string
|
||||
/// </summary>
|
||||
public static string FromBspLumpType(this BspLumpType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
BspLumpType.LUMP_ENTITIES => " - LUMP_ENTITIES",
|
||||
BspLumpType.LUMP_PLANES => " - LUMP_PLANES",
|
||||
BspLumpType.LUMP_TEXTURES => " - LUMP_TEXTURES",
|
||||
BspLumpType.LUMP_VERTICES => " - LUMP_VERTICES",
|
||||
BspLumpType.LUMP_VISIBILITY => " - LUMP_VISIBILITY",
|
||||
BspLumpType.LUMP_NODES => " - LUMP_NODES",
|
||||
BspLumpType.LUMP_TEXINFO => " - LUMP_TEXINFO",
|
||||
BspLumpType.LUMP_FACES => " - LUMP_FACES",
|
||||
BspLumpType.LUMP_LIGHTING => " - LUMP_LIGHTING",
|
||||
BspLumpType.LUMP_CLIPNODES => " - LUMP_CLIPNODES",
|
||||
BspLumpType.LUMP_LEAVES => " - LUMP_LEAVES",
|
||||
BspLumpType.LUMP_MARKSURFACES => " - LUMP_MARKSURFACES",
|
||||
BspLumpType.LUMP_EDGES => " - LUMP_EDGES",
|
||||
BspLumpType.LUMP_SURFEDGES => " - LUMP_SURFEDGES",
|
||||
BspLumpType.LUMP_MODELS => " - LUMP_MODELS",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
313
SabreTools.Data.Extensions/CDROMExtensions.cs
Normal file
313
SabreTools.Data.Extensions/CDROMExtensions.cs
Normal file
@@ -0,0 +1,313 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.Data.Models.CDROM;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class CDROMExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the sector mode for a CD-ROM stream
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream to derive the sector mode from</param>
|
||||
/// <returns>Sector mode from the stream on success, <see cref="SectorMode.UNKNOWN"/> on error</returns>
|
||||
public static SectorMode GetSectorMode(this Stream stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte modeByte = stream.ReadByteValue();
|
||||
if (modeByte == 0)
|
||||
{
|
||||
return SectorMode.MODE0;
|
||||
}
|
||||
else if (modeByte == 1)
|
||||
{
|
||||
return SectorMode.MODE1;
|
||||
}
|
||||
else if (modeByte == 2)
|
||||
{
|
||||
stream.SeekIfPossible(2, SeekOrigin.Current);
|
||||
byte submode = stream.ReadByteValue();
|
||||
if ((submode & 0x20) == 0x20)
|
||||
return SectorMode.MODE2_FORM2;
|
||||
else
|
||||
return SectorMode.MODE2_FORM1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SectorMode.UNKNOWN;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore the actual error
|
||||
return SectorMode.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the user data size for a sector mode
|
||||
/// </summary>
|
||||
/// <param name="mode">Sector mode to get a value for</param>
|
||||
/// <returns>User data size, if possible</returns>
|
||||
public static long GetUserDataSize(this SectorMode mode)
|
||||
{
|
||||
return mode switch
|
||||
{
|
||||
SectorMode.UNKNOWN => Constants.Mode0DataSize,
|
||||
SectorMode.MODE0 => Constants.Mode0DataSize,
|
||||
SectorMode.MODE1 => Constants.Mode1DataSize,
|
||||
SectorMode.MODE2 => Constants.Mode0DataSize,
|
||||
SectorMode.MODE2_FORM1 => Constants.Mode2Form1DataSize,
|
||||
SectorMode.MODE2_FORM2 => Constants.Mode2Form2DataSize,
|
||||
_ => Constants.Mode0DataSize,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the user data end offset for a sector mode
|
||||
/// </summary>
|
||||
/// <param name="mode">Sector mode to get a value for</param>
|
||||
/// <returns>User data end offset, if possible</returns>
|
||||
public static long GetUserDataEnd(this SectorMode mode)
|
||||
{
|
||||
return mode switch
|
||||
{
|
||||
SectorMode.UNKNOWN => Constants.Mode0UserDataEnd,
|
||||
SectorMode.MODE0 => Constants.Mode0UserDataEnd, // TODO: Support flexible sector length (2352)
|
||||
SectorMode.MODE1 => Constants.Mode1UserDataEnd,
|
||||
SectorMode.MODE2 => Constants.Mode0UserDataEnd, // TODO: Support flexible sector length (2352)
|
||||
SectorMode.MODE2_FORM1 => Constants.Mode2Form1UserDataEnd,
|
||||
SectorMode.MODE2_FORM2 => Constants.Mode2Form2UserDataEnd, // TODO: Support flexible sector length (2348)
|
||||
_ => Constants.Mode0UserDataEnd,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the user data start offset for a sector mode
|
||||
/// </summary>
|
||||
/// <param name="mode">Sector mode to get a value for</param>
|
||||
/// <returns>User data start offset, if possible</returns>
|
||||
public static long GetUserDataStart(this SectorMode mode)
|
||||
{
|
||||
return mode switch
|
||||
{
|
||||
SectorMode.UNKNOWN => Constants.Mode0UserDataStart,
|
||||
SectorMode.MODE0 => Constants.Mode0UserDataStart,
|
||||
SectorMode.MODE1 => Constants.Mode1UserDataStart,
|
||||
SectorMode.MODE2 => Constants.Mode0UserDataStart,
|
||||
SectorMode.MODE2_FORM1 => Constants.Mode2Form1UserDataStart,
|
||||
SectorMode.MODE2_FORM2 => Constants.Mode2Form2UserDataStart,
|
||||
_ => Constants.Mode0UserDataStart,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a stream that provides only the user data of a CDROM stream
|
||||
/// </summary>
|
||||
public class UserDataStream : Stream
|
||||
{
|
||||
// Base CDROM stream (2352-byte sector)
|
||||
private readonly Stream _baseStream;
|
||||
|
||||
// State variables
|
||||
private long _position = 0;
|
||||
private SectorMode _currentMode = SectorMode.UNKNOWN;
|
||||
private long _userDataStart = Constants.Mode1UserDataStart;
|
||||
private long _userDataEnd = Constants.Mode1UserDataEnd;
|
||||
#pragma warning disable IDE0044
|
||||
private long _isoSectorSize = Constants.Mode1DataSize;
|
||||
#pragma warning restore IDE0044
|
||||
|
||||
public UserDataStream(Stream inputStream)
|
||||
{
|
||||
if (!inputStream.CanSeek || !inputStream.CanRead)
|
||||
throw new ArgumentException("Stream must be readable and seekable.", nameof(inputStream));
|
||||
|
||||
_baseStream = inputStream;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanRead => _baseStream.CanRead;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanSeek => _baseStream.CanSeek;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanWrite => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Flush() => _baseStream.Flush();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override long Length
|
||||
=> _baseStream.Length / Constants.CDROMSectorSize * _isoSectorSize;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SetLength(long value)
|
||||
=> throw new NotSupportedException("Setting the length of this stream is not supported.");
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
=> throw new NotSupportedException("Writing to this stream is not supported.");
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
_baseStream.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override long Position
|
||||
{
|
||||
// Get the position of the underlying ISO9660 stream
|
||||
get
|
||||
{
|
||||
// Get the user data location based on the current sector mode
|
||||
SetState(_position);
|
||||
|
||||
// Get the number of ISO sectors before current position
|
||||
long isoPosition = _position / Constants.CDROMSectorSize * _isoSectorSize;
|
||||
|
||||
// Add the within-sector position
|
||||
long remainder = _position % Constants.CDROMSectorSize;
|
||||
if (remainder > _userDataEnd)
|
||||
isoPosition += _isoSectorSize;
|
||||
else if (remainder > _userDataStart)
|
||||
isoPosition += remainder - _userDataStart;
|
||||
|
||||
return isoPosition;
|
||||
}
|
||||
set
|
||||
{
|
||||
// Seek to the underlying ISO9660 position
|
||||
Seek(value, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int totalRead = 0;
|
||||
int remaining = count;
|
||||
|
||||
while (remaining > 0 && _position < _baseStream.Length)
|
||||
{
|
||||
// Determine location of current sector
|
||||
long baseStreamOffset = _position - (_position % Constants.CDROMSectorSize);
|
||||
|
||||
// Set the current sector's mode and user data location
|
||||
SetState(baseStreamOffset);
|
||||
|
||||
// Deal with case where base position is not in ISO stream
|
||||
long remainder = _position % Constants.CDROMSectorSize;
|
||||
long sectorOffset = remainder - _userDataStart;
|
||||
if (remainder < _userDataStart)
|
||||
{
|
||||
baseStreamOffset += _userDataStart;
|
||||
sectorOffset = 0;
|
||||
_position += _userDataStart;
|
||||
}
|
||||
else if (remainder >= _userDataEnd)
|
||||
{
|
||||
baseStreamOffset += Constants.CDROMSectorSize;
|
||||
sectorOffset = 0;
|
||||
_position += Constants.CDROMSectorSize - _userDataEnd + _userDataStart;
|
||||
}
|
||||
else
|
||||
{
|
||||
baseStreamOffset += remainder;
|
||||
}
|
||||
|
||||
// Sanity check on read location before seeking
|
||||
if (baseStreamOffset < 0 || baseStreamOffset > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(offset), "Attempted to seek outside the stream boundaries.");
|
||||
|
||||
// Seek to target position in base CDROM stream
|
||||
_baseStream.SeekIfPossible(baseStreamOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the remaining bytes, up to max of one ISO sector (2048 bytes)
|
||||
int bytesToRead = (int)Math.Min(remaining, _isoSectorSize - sectorOffset);
|
||||
|
||||
// Don't overshoot end of stream
|
||||
bytesToRead = (int)Math.Min(bytesToRead, _baseStream.Length - _position);
|
||||
|
||||
// Finish reading if no more bytes to be read
|
||||
if (bytesToRead <= 0)
|
||||
break;
|
||||
|
||||
// Read up to 2048 bytes from base CDROM stream
|
||||
int bytesRead = _baseStream.Read(buffer, offset + totalRead, bytesToRead);
|
||||
|
||||
// Update state for base stream
|
||||
_position = _baseStream.Position;
|
||||
if (bytesToRead == (_isoSectorSize - sectorOffset))
|
||||
_position += Constants.CDROMSectorSize - _userDataEnd + _userDataStart;
|
||||
|
||||
// Update state for ISO stream
|
||||
totalRead += bytesRead;
|
||||
remaining -= bytesRead;
|
||||
|
||||
if (bytesRead == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return totalRead;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
// Get the intended position for the ISO9660 stream
|
||||
long targetPosition = origin switch
|
||||
{
|
||||
SeekOrigin.Begin => offset,
|
||||
SeekOrigin.Current => Position + offset,
|
||||
SeekOrigin.End => Length + offset,
|
||||
_ => throw new ArgumentException("Invalid SeekOrigin.", nameof(origin)),
|
||||
};
|
||||
|
||||
// Get the number of ISO sectors before current position
|
||||
long newPosition = targetPosition / _isoSectorSize * Constants.CDROMSectorSize;
|
||||
|
||||
// Set the current sector's mode and user data location
|
||||
SetState(newPosition);
|
||||
|
||||
// Add the within-sector position
|
||||
newPosition += _userDataStart + (targetPosition % _isoSectorSize);
|
||||
if (newPosition < 0 || newPosition > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(offset), "Attempted to seek outside the stream boundaries.");
|
||||
|
||||
_position = _baseStream.SeekIfPossible(newPosition, SeekOrigin.Begin);
|
||||
return Position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the current stream state based on the location
|
||||
/// </summary>
|
||||
/// <param name="sectorLocation">Sector location to update from</param>
|
||||
private void SetState(long sectorLocation)
|
||||
{
|
||||
long current = _baseStream.Position;
|
||||
long modePosition = sectorLocation - (sectorLocation % Constants.CDROMSectorSize) + 15;
|
||||
|
||||
// Get the current sector mode
|
||||
_baseStream.SeekIfPossible(modePosition, SeekOrigin.Begin);
|
||||
_currentMode = _baseStream.GetSectorMode();
|
||||
|
||||
// Set the user data location variables
|
||||
_userDataStart = _currentMode.GetUserDataStart();
|
||||
_userDataEnd = _currentMode.GetUserDataEnd();
|
||||
// _isoSectorSize = _currentMode.GetUserDataSize();
|
||||
|
||||
// Reset the stream position
|
||||
_baseStream.SeekIfPossible(current, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
99
SabreTools.Data.Extensions/CFBExtensions.cs
Normal file
99
SabreTools.Data.Extensions/CFBExtensions.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using System.Text;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class CFBExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Decode a MIME-encoded stream name stored as a byte array
|
||||
/// </summary>
|
||||
/// <param name="input">Byte array to decode</param>
|
||||
/// <returns>Decoded string on success, null otherwise</returns>
|
||||
/// <remarks>Adapted from LibMSI</remarks>
|
||||
public static string? DecodeStreamName(this byte[]? input)
|
||||
{
|
||||
// Ignore invalid inputs
|
||||
if (input is null)
|
||||
return null;
|
||||
else if (input.Length == 0)
|
||||
return string.Empty;
|
||||
|
||||
int count = 0;
|
||||
int p = 0; // inputBytes[0]
|
||||
|
||||
byte[] output = new byte[input.Length + 1];
|
||||
int q = 0; // output[0]
|
||||
while (p < input.Length && input[p] != 0)
|
||||
{
|
||||
byte ch = input[p];
|
||||
if ((ch == 0xe3 && input[p + 1] >= 0xa0) || (ch == 0xe4 && input[p + 1] < 0xa0))
|
||||
{
|
||||
// UTF-8 encoding of 0x3800..0x47ff.
|
||||
output[q++] = (byte)Mime2Utf(input[p + 2] & 0x7f);
|
||||
output[q++] = (byte)Mime2Utf(input[p + 1] ^ 0xa0);
|
||||
p += 3;
|
||||
count += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == 0xe4 && input[p + 1] == 0xa0)
|
||||
{
|
||||
// UTF-8 encoding of 0x4800..0x483f.
|
||||
output[q++] = (byte)Mime2Utf(input[p + 2] & 0x7f);
|
||||
p += 3;
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
output[q++] = input[p++];
|
||||
if (ch >= 0xc1)
|
||||
output[q++] = input[p++];
|
||||
if (ch >= 0xe0)
|
||||
output[q++] = input[p++];
|
||||
if (ch >= 0xf0)
|
||||
output[q++] = input[p++];
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
output[q] = 0;
|
||||
return Encoding.UTF8.GetString(output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decode a MIME-encoded stream name stored as a string
|
||||
/// </summary>
|
||||
/// <param name="input">String to decode</param>
|
||||
/// <returns>Decoded string on success, null otherwise</returns>
|
||||
/// <remarks>Adapted from LibMSI</remarks>
|
||||
public static string? DecodeStreamName(this string? input)
|
||||
{
|
||||
// Ignore invalid inputs
|
||||
if (input is null)
|
||||
return null;
|
||||
else if (input.Length == 0)
|
||||
return string.Empty;
|
||||
|
||||
byte[] inputBytes = Encoding.UTF8.GetBytes(input);
|
||||
return inputBytes.DecodeStreamName();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decode a single MIME-encoded byte
|
||||
/// </summary>
|
||||
/// <param name="b">Byte to decode</param>
|
||||
/// <returns>Decoded byte on success, `_` otherwise</returns>
|
||||
/// <remarks>Adapted from LibMSI</remarks>
|
||||
private static int Mime2Utf(int b)
|
||||
{
|
||||
return b switch
|
||||
{
|
||||
< 10 => b + '0',
|
||||
< 10 + 26 => b - 10 + 'A',
|
||||
< 10 + 26 + 26 => b - 10 - 26 + 'a',
|
||||
10 + 26 + 26 => '.',
|
||||
_ => '_'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
9
SabreTools.Data.Extensions/ExtensionAttribute.cs
Normal file
9
SabreTools.Data.Extensions/ExtensionAttribute.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
#if NET20
|
||||
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)]
|
||||
internal sealed class ExtensionAttribute : Attribute {}
|
||||
}
|
||||
|
||||
#endif
|
||||
54
SabreTools.Data.Extensions/ISO9660Extensions.cs
Normal file
54
SabreTools.Data.Extensions/ISO9660Extensions.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using SabreTools.Data.Models.ISO9660;
|
||||
using SabreTools.Numerics;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class ISO9660Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the logical block size from a sector length
|
||||
/// </summary>
|
||||
/// <param name="vd">Volume descriptor containing block information</param>
|
||||
/// <param name="sectorLength">Defined sector length</param>
|
||||
/// <returns>Size of a logical block</returns>
|
||||
public static short GetLogicalBlockSize(this VolumeDescriptor vd, short sectorLength)
|
||||
{
|
||||
BothInt16 blockSize;
|
||||
if (vd is PrimaryVolumeDescriptor pvd)
|
||||
blockSize = pvd.LogicalBlockSize;
|
||||
else if (vd is SupplementaryVolumeDescriptor svd)
|
||||
blockSize = svd.LogicalBlockSize;
|
||||
else
|
||||
return sectorLength;
|
||||
|
||||
// If the block size is inconsistent
|
||||
if (!blockSize.IsValid)
|
||||
{
|
||||
bool leValid = BlockSizeValid(blockSize.LittleEndian, sectorLength);
|
||||
bool beValid = BlockSizeValid(blockSize.BigEndian, sectorLength);
|
||||
|
||||
if (leValid && !beValid)
|
||||
blockSize = blockSize.LittleEndian;
|
||||
else if (beValid && !leValid)
|
||||
blockSize = blockSize.BigEndian;
|
||||
else
|
||||
return sectorLength;
|
||||
}
|
||||
|
||||
// Validate logical block size
|
||||
if (!BlockSizeValid(blockSize, sectorLength))
|
||||
blockSize = sectorLength;
|
||||
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if a block size is valid
|
||||
/// </summary>
|
||||
/// <param name="blockSize">Block length to check</param>
|
||||
/// <param name="sectorLength">Defined sector length</param>
|
||||
/// <returns>True if the block length is valid, false otherwise</returns>
|
||||
private static bool BlockSizeValid(short blockSize, short sectorLength)
|
||||
=> blockSize >= 512 && blockSize <= sectorLength && (blockSize & (blockSize - 1)) == 0;
|
||||
}
|
||||
}
|
||||
127
SabreTools.Data.Extensions/InstallShieldCabinetExtensions.cs
Normal file
127
SabreTools.Data.Extensions/InstallShieldCabinetExtensions.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using SabreTools.Data.Models.InstallShieldCabinet;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class InstallShieldCabinetExtensions
|
||||
{
|
||||
#region File Descriptors
|
||||
|
||||
/// <summary>
|
||||
/// Indicate if a file descriptor represents a compressed file
|
||||
/// </summary>
|
||||
/// <param name="fileDescriptor">File descriptor to check</param>
|
||||
/// <returns>True if the file is flagged as compressed, false otherwise</returns>
|
||||
public static bool IsCompressed(this FileDescriptor? fileDescriptor)
|
||||
{
|
||||
// Ignore invalid descriptors
|
||||
if (fileDescriptor is null)
|
||||
return true;
|
||||
|
||||
#if NET20 || NET35
|
||||
return (fileDescriptor.Flags & FileFlags.FILE_COMPRESSED) != 0;
|
||||
#else
|
||||
return fileDescriptor.Flags.HasFlag(FileFlags.FILE_COMPRESSED);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicate if a file descriptor represents an invalid file
|
||||
/// </summary>
|
||||
/// <param name="fileDescriptor">File descriptor to check</param>
|
||||
/// <returns>True if the file is flagged as invalid, false otherwise</returns>
|
||||
public static bool IsInvalid(this FileDescriptor? fileDescriptor)
|
||||
{
|
||||
// Ignore invalid descriptors
|
||||
if (fileDescriptor is null)
|
||||
return true;
|
||||
|
||||
#if NET20 || NET35
|
||||
return (fileDescriptor.Flags & FileFlags.FILE_INVALID) != 0;
|
||||
#else
|
||||
return fileDescriptor.Flags.HasFlag(FileFlags.FILE_INVALID);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicate if a file descriptor represents an obfuscated file
|
||||
/// </summary>
|
||||
/// <param name="fileDescriptor">File descriptor to check</param>
|
||||
/// <returns>True if the file is flagged as obfuscated, false otherwise</returns>
|
||||
public static bool IsObfuscated(this FileDescriptor? fileDescriptor)
|
||||
{
|
||||
// Ignore invalid descriptors
|
||||
if (fileDescriptor is null)
|
||||
return false;
|
||||
|
||||
#if NET20 || NET35
|
||||
return (fileDescriptor.Flags & FileFlags.FILE_OBFUSCATED) != 0;
|
||||
#else
|
||||
return fileDescriptor.Flags.HasFlag(FileFlags.FILE_OBFUSCATED);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicate if a file descriptor represents a split file
|
||||
/// </summary>
|
||||
/// <param name="fileDescriptor">File descriptor to check</param>
|
||||
/// <returns>True if the file is flagged as split, false otherwise</returns>
|
||||
public static bool IsSplit(this FileDescriptor? fileDescriptor)
|
||||
{
|
||||
// Ignore invalid descriptors
|
||||
if (fileDescriptor is null)
|
||||
return false;
|
||||
|
||||
#if NET20 || NET35
|
||||
return (fileDescriptor.Flags & FileFlags.FILE_SPLIT) != 0;
|
||||
#else
|
||||
return fileDescriptor.Flags.HasFlag(FileFlags.FILE_SPLIT);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Version
|
||||
|
||||
/// <summary>
|
||||
/// Get the major version of an InstallShield Cabinet
|
||||
/// </summary>
|
||||
/// <param name="cabinet">Cabinet to derive the version from</param>
|
||||
/// <returns>Major version of the cabinet, -1 on error</returns>
|
||||
public static int GetMajorVersion(this Cabinet? cabinet)
|
||||
{
|
||||
// Ignore invalid cabinets
|
||||
if (cabinet is null)
|
||||
return -1;
|
||||
|
||||
return cabinet.CommonHeader.GetMajorVersion();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the major version of an InstallShield Cabinet
|
||||
/// </summary>
|
||||
/// <param name="commonHeader">CommonHeader to derive the version from</param>
|
||||
/// <returns>Major version of the cabinet, -1 on error</returns>
|
||||
public static int GetMajorVersion(this CommonHeader? commonHeader)
|
||||
{
|
||||
// Ignore invalid headers
|
||||
if (commonHeader is null)
|
||||
return -1;
|
||||
|
||||
uint majorVersion = commonHeader.Version;
|
||||
if (majorVersion >> 24 == 1)
|
||||
{
|
||||
majorVersion = (majorVersion >> 12) & 0x0F;
|
||||
}
|
||||
else if (majorVersion >> 24 == 2 || majorVersion >> 24 == 4)
|
||||
{
|
||||
majorVersion &= 0xFFFF;
|
||||
if (majorVersion != 0)
|
||||
majorVersion /= 100;
|
||||
}
|
||||
|
||||
return (int)majorVersion;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1688
SabreTools.Data.Extensions/MetadataExtensions.cs
Normal file
1688
SabreTools.Data.Extensions/MetadataExtensions.cs
Normal file
File diff suppressed because it is too large
Load Diff
241
SabreTools.Data.Extensions/NESCartExtensions.cs
Normal file
241
SabreTools.Data.Extensions/NESCartExtensions.cs
Normal file
@@ -0,0 +1,241 @@
|
||||
using SabreTools.Data.Models.NES;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class NESCartExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a <see cref="ConsoleType"/> value to string
|
||||
/// </summary>
|
||||
public static string FromConsoleType(this ConsoleType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
ConsoleType.StandardSystem => "Nintendo Entertainment System/Family Computer",
|
||||
ConsoleType.VSUnisystem => "VS Unisystem",
|
||||
ConsoleType.PlayChoice10 => "PlayChoice-10 (8 KB of Hint Screen data stored after CHR data)",
|
||||
ConsoleType.ExtendedConsoleType => "Extended Console Type",
|
||||
_ => $"Unknown {(byte)type}",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="CPUPPUTiming"/> value to string
|
||||
/// </summary>
|
||||
public static string FromCPUPPUTiming(this CPUPPUTiming timing)
|
||||
{
|
||||
return timing switch
|
||||
{
|
||||
CPUPPUTiming.RP2C02 => "RP2C02 (NTSC NES)",
|
||||
CPUPPUTiming.RP2C07 => "RP2C07 (Licensed PAL NES)",
|
||||
CPUPPUTiming.MultipleRegion => "Multiple-region",
|
||||
CPUPPUTiming.UA6538 => "UA6538 (Dendy)",
|
||||
_ => $"Unknown {(byte)timing}",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="DefaultExpansionDevice"/> value to string
|
||||
/// </summary>
|
||||
public static string FromDefaultExpansionDevice(this DefaultExpansionDevice device)
|
||||
{
|
||||
return device switch
|
||||
{
|
||||
DefaultExpansionDevice.Unspecified => "Unspecified",
|
||||
DefaultExpansionDevice.StandardControllers => "Standard NES/Famicom controllers",
|
||||
DefaultExpansionDevice.NESFourScore => "NES Four Score/Satellite with two additional standard controllers",
|
||||
DefaultExpansionDevice.FamicomFourPlayersAdapter => "Famicom Four Players Adapter with two additional standard controllers using the 'simple' protocol",
|
||||
DefaultExpansionDevice.VsSystem4016 => "Vs. System (1P via $4016)",
|
||||
DefaultExpansionDevice.VsSystem4017 => "Vs. System (1P via $4017)",
|
||||
DefaultExpansionDevice.Reserved06 => "Reserved (0x06)",
|
||||
DefaultExpansionDevice.VsZapper => "Vs. Zapper",
|
||||
DefaultExpansionDevice.Zapper4017 => "Zapper ($4017)",
|
||||
DefaultExpansionDevice.TwoZappers => "Two Zappers",
|
||||
DefaultExpansionDevice.BandaiHyperShotLightgun => "Bandai Hyper Shot Lightgun",
|
||||
DefaultExpansionDevice.PowerPadSideA => "Power Pad Side A",
|
||||
DefaultExpansionDevice.PowerPadSideB => "Power Pad Side B",
|
||||
DefaultExpansionDevice.FamilyTrainerSideA => "Family Trainer Side A",
|
||||
DefaultExpansionDevice.FamilyTrainerSideB => "Family Trainer Side B",
|
||||
DefaultExpansionDevice.ArkanoidVausControllerNES => "Arkanoid Vaus Controller (NES)",
|
||||
DefaultExpansionDevice.ArkanoidVausControllerFamicom => "Arkanoid Vaus Controller (Famicom)",
|
||||
DefaultExpansionDevice.TwoVausControllersPlusFamicomDataRecorder => "Two Vaus Controllers plus Famicom Data Recorder",
|
||||
DefaultExpansionDevice.KonamiHyperShotController => "Konami Hyper Shot Controller",
|
||||
DefaultExpansionDevice.CoconutsPachinkoController => "Coconuts Pachinko Controller",
|
||||
DefaultExpansionDevice.ExcitingBoxingPunchingBag => "Exciting Boxing Punching Bag (Blowup Doll)",
|
||||
DefaultExpansionDevice.JissenMahjongController => "Jissen Mahjong Controller",
|
||||
DefaultExpansionDevice.YonezawaPartyTap => "米澤 (Yonezawa) Party Tap",
|
||||
DefaultExpansionDevice.OekaKidsTablet => "Oeka Kids Tablet",
|
||||
DefaultExpansionDevice.SunsoftBarcodeBattler => "Sunsoft Barcode Battler",
|
||||
DefaultExpansionDevice.MiraclePianoKeyboard => "Miracle Piano Keyboard",
|
||||
DefaultExpansionDevice.PokkunMoguraaTapTapMat => "Pokkun Moguraa Tap-tap Mat (Whack-a-Mole Mat and Mallet)",
|
||||
DefaultExpansionDevice.TopRider => "Top Rider (Inflatable Bicycle)",
|
||||
DefaultExpansionDevice.DoubleFisted => "Double-Fisted (Requires or allows use of two controllers by one player)",
|
||||
DefaultExpansionDevice.Famicom3DSystem => "Famicom 3D System",
|
||||
DefaultExpansionDevice.DoremikkoKeyboard => "Doremikko Keyboard",
|
||||
DefaultExpansionDevice.ROBGyromite => "R.O.B. Gyromite",
|
||||
DefaultExpansionDevice.FamicomDataRecorder => "Famicom Data Recorder ('silent' keyboard)",
|
||||
DefaultExpansionDevice.ASCIITurboFile => "ASCII Turbo File",
|
||||
DefaultExpansionDevice.IGSStorageBattleBox => "IGS Storage Battle Box",
|
||||
DefaultExpansionDevice.FamilyBASICKeyboardPlusFamicomDataRecorder => "Family BASIC Keyboard plus Famicom Data Recorder",
|
||||
DefaultExpansionDevice.DongdaPECKeyboard => "东达 (Dōngdá) PEC Keyboard",
|
||||
DefaultExpansionDevice.BitCorpBit79Keyboard => "普澤 (Pǔzé, a.k.a. Bit Corp.) Bit-79 Keyboard",
|
||||
DefaultExpansionDevice.SuborKeyboard => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard",
|
||||
DefaultExpansionDevice.SuborKeyboardPlusMacroWinnersMouse => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Macro Winners Mouse",
|
||||
DefaultExpansionDevice.SuborKeyboardPlusSuborMouse4016 => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Subor Mouse via $4016",
|
||||
DefaultExpansionDevice.SNESMouse4016 => "SNES Mouse ($4016)",
|
||||
DefaultExpansionDevice.Multicart => "Multicart",
|
||||
DefaultExpansionDevice.TwoSNESControllers => "Two SNES controllers replacing the two standard NES controllers",
|
||||
DefaultExpansionDevice.RacerMateBicycle => "RacerMate Bicycle",
|
||||
DefaultExpansionDevice.UForce => "U-Force",
|
||||
DefaultExpansionDevice.ROBStackUp => "R.O.B. Stack-Up",
|
||||
DefaultExpansionDevice.CityPatrolmanLightgun => "City Patrolman Lightgun",
|
||||
DefaultExpansionDevice.SharpC1CassetteInterface => "Sharp C1 Cassette Interface",
|
||||
DefaultExpansionDevice.StandardControllerWithSwappedInputs => "Standard Controller with swapped Left-Right/Up-Down/B-A",
|
||||
DefaultExpansionDevice.ExcaliburSudokuPad => "Excalibur Sudoku Pad",
|
||||
DefaultExpansionDevice.ABLPinball => "ABL Pinball",
|
||||
DefaultExpansionDevice.GoldenNuggetCasinoExtraButtons => "Golden Nugget Casino extra buttons",
|
||||
DefaultExpansionDevice.KedaKeyboard => "科达 (Kēdá) Keyboard",
|
||||
DefaultExpansionDevice.SuborKeyboardPlusSuborMouse4017 => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Subor Mouse via $4017",
|
||||
DefaultExpansionDevice.PortTestController => "Port test controller",
|
||||
DefaultExpansionDevice.BandaiMultiGamePlayerGamepad => "Bandai Multi Game Player Gamepad buttons",
|
||||
DefaultExpansionDevice.VenomTVDanceMat => "Venom TV Dance Mat",
|
||||
DefaultExpansionDevice.LGTVRemoteControl => "LG TV Remote Control",
|
||||
DefaultExpansionDevice.FamicomNetworkController => "Famicom Network Controller",
|
||||
DefaultExpansionDevice.KingFishingController => "King Fishing Controller",
|
||||
DefaultExpansionDevice.CroakyKaraokeController => "Croaky Karaoke Controller",
|
||||
DefaultExpansionDevice.KingwonKeyboard => "科王 (Kēwáng, a.k.a. Kingwon) Keyboard",
|
||||
DefaultExpansionDevice.ZechengKeyboard => "泽诚 (Zéchéng) Keyboard",
|
||||
DefaultExpansionDevice.SuborKeyboardPlusL90RotatedPS2Mouse4017 => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus L90-rotated PS/2 mouse in $4017",
|
||||
DefaultExpansionDevice.PS2KeyboardInUM6578PS2PortPS2Mouse4017 => "PS/2 Keyboard in UM6578 PS/2 port, PS/2 Mouse via $4017",
|
||||
DefaultExpansionDevice.PS2MouseInUM6578PS2Port => "PS/2 Mouse in UM6578 PS/2 port",
|
||||
DefaultExpansionDevice.YuxingMouse4016 => "裕兴 (Yùxìng) Mouse via $4016",
|
||||
DefaultExpansionDevice.SuborKeyboardPlusYuxingMouse4016 => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus 裕兴 (Yùxìng) Mouse mouse in $4016",
|
||||
DefaultExpansionDevice.GigggleTVPump => "Gigggle TV Pump",
|
||||
DefaultExpansionDevice.BBKKeyboardPlusR90RotatedPS2Mouse4017 => "步步高 (Bùbùgāo, a.k.a. BBK) Keyboard plus R90-rotated PS/2 mouse in $4017",
|
||||
DefaultExpansionDevice.MagicalCooking => "Magical Cooking",
|
||||
DefaultExpansionDevice.SNESMouse4017 => "SNES Mouse ($4017)",
|
||||
DefaultExpansionDevice.Zapper4016 => "Zapper ($4016)",
|
||||
DefaultExpansionDevice.ArkanoidVausControllerPrototype => "Arkanoid Vaus Controller (Prototype)",
|
||||
DefaultExpansionDevice.TVMahjongGameController => "TV 麻雀 Game (TV Mahjong Game) Controller",
|
||||
DefaultExpansionDevice.MahjongGekitouDensetsuController => "麻雀激闘伝説 (Mahjong Gekitou Densetsu) Controller",
|
||||
DefaultExpansionDevice.SuborKeyboardPlusXInvertedPS2Mouse4017 => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus X-inverted PS/2 mouse in $4017",
|
||||
DefaultExpansionDevice.IBMPCXTKeyboard => "IBM PC/XT Keyboard",
|
||||
DefaultExpansionDevice.SuborKeyboardPlusMegaBookMouse => "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Mega Book Mouse",
|
||||
_ => $"Unknown {(byte)device}",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="ExtendedConsoleType"/> value to string
|
||||
/// </summary>
|
||||
public static string FromExtendedConsoleType(this ExtendedConsoleType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
ExtendedConsoleType.RegularSystem => "Regular NES/Famicom/Dendy",
|
||||
ExtendedConsoleType.NintendoVsSystem => "Nintendo Vs. System",
|
||||
ExtendedConsoleType.Playchoice10 => "Playchoice 10",
|
||||
ExtendedConsoleType.RegularFamicloneDecimalMode => "Regular Famiclone, but with CPU that supports Decimal Mode",
|
||||
ExtendedConsoleType.RegularNESWithEPSM => "Regular NES/Famicom with EPSM module or plug-through cartridge",
|
||||
ExtendedConsoleType.VRTechnologyVT01 => "V.R. Technology VT01 with red/cyan STN palette",
|
||||
ExtendedConsoleType.VRTechnologyVT02 => "V.R. Technology VT02",
|
||||
ExtendedConsoleType.VRTechnologyVT03 => "V.R. Technology VT03",
|
||||
ExtendedConsoleType.VRTechnologyVT09 => "V.R. Technology VT09",
|
||||
ExtendedConsoleType.VRTechnologyVT32 => "V.R. Technology VT32",
|
||||
ExtendedConsoleType.VRTechnologyVT369 => "V.R. Technology VT369",
|
||||
ExtendedConsoleType.UMCUM6578 => "UMC UM6578",
|
||||
ExtendedConsoleType.FamicomNetworkSystem => "Famicom Network System",
|
||||
ExtendedConsoleType.ReservedD => "Reserved (0x0D)",
|
||||
ExtendedConsoleType.ReservedE => "Reserved (0x0E)",
|
||||
ExtendedConsoleType.ReservedF => "Reserved (0x0F)",
|
||||
_ => $"Unknown {(byte)type}",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="NametableArrangement"/> value to string
|
||||
/// </summary>
|
||||
public static string FromNametableArrangement(this NametableArrangement arrangement)
|
||||
{
|
||||
return arrangement switch
|
||||
{
|
||||
NametableArrangement.Vertical => "Vertical",
|
||||
NametableArrangement.Horizontal => "Horizontal",
|
||||
_ => $"Unknown {(byte)arrangement}",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="TVSystem"/> value to string
|
||||
/// </summary>
|
||||
public static string FromTVSystem(this TVSystem system)
|
||||
{
|
||||
return system switch
|
||||
{
|
||||
TVSystem.NTSC => "NTSC",
|
||||
TVSystem.PAL => "PAL",
|
||||
_ => $"Unknown {(byte)system}",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="TVSystemExtended"/> value to string
|
||||
/// </summary>
|
||||
public static string FromTVSystemExtended(this TVSystemExtended system)
|
||||
{
|
||||
return system switch
|
||||
{
|
||||
TVSystemExtended.NTSC => "NTSC",
|
||||
TVSystemExtended.DualCompatible1 => "Dual-compatible (0x01)",
|
||||
TVSystemExtended.PAL => "PAL",
|
||||
TVSystemExtended.DualCompatible3 => "Dual-compatible (0x03)",
|
||||
_ => $"Unknown {(byte)system}",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="VsHardwareType"/> value to string
|
||||
/// </summary>
|
||||
public static string FromVsHardwareType(this VsHardwareType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
VsHardwareType.VsUnisystem => "Vs. Unisystem (normal)",
|
||||
VsHardwareType.VsUnisystemRBIBaseballProtection => "Vs. Unisystem (RBI Baseball protection)",
|
||||
VsHardwareType.VsUnisystemTKOBoxingProtection => "Vs. Unisystem (TKO Boxing protection)",
|
||||
VsHardwareType.VsUnisystemSuperXeviousProtection => "Vs. Unisystem (Super Xevious protection)",
|
||||
VsHardwareType.VsUnisystemVsIceClimberJapanProtection => "Vs. Unisystem (Vs. Ice Climber Japan protection)",
|
||||
VsHardwareType.VsDualSystem => "Vs. Dual System (normal)",
|
||||
VsHardwareType.VsDualSystemRaidOnBungelingBayProtection => "Vs. Dual System (Raid on Bungeling Bay protection)",
|
||||
_ => $"Unknown {(byte)type}",
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="VsSystemType"/> value to string
|
||||
/// </summary>
|
||||
public static string FromVsSystemType(this VsSystemType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
VsSystemType.AnyRP2C03RC2C03Variant => "Any RP2C03/RC2C03 variant",
|
||||
VsSystemType.Reserved1 => "Reserved (0x01)",
|
||||
VsSystemType.RP2C040001 => "RP2C04-0001",
|
||||
VsSystemType.RP2C040002 => "RP2C04-0002",
|
||||
VsSystemType.RP2C040003 => "RP2C04-0003",
|
||||
VsSystemType.RP2C040004 => "RP2C04-0004",
|
||||
VsSystemType.Reserved6 => "Reserved (0x06)",
|
||||
VsSystemType.Reserved7 => "Reserved (0x07)",
|
||||
VsSystemType.RC2C0501 => "RC2C05-01 (signature unknown)",
|
||||
VsSystemType.RC2C0502 => "RC2C05-02 ($2002 AND $3F =$3D)",
|
||||
VsSystemType.RC2C0503 => "RC2C05-03 ($2002 AND $1F =$1C)",
|
||||
VsSystemType.RC2C0504 => "RC2C05-04 ($2002 AND $1F =$1B)",
|
||||
VsSystemType.ReservedC => "Reserved (0x0C)",
|
||||
VsSystemType.ReservedD => "Reserved (0x0D)",
|
||||
VsSystemType.ReservedE => "Reserved (0x0E)",
|
||||
VsSystemType.ReservedF => "Reserved (0x0F)",
|
||||
_ => $"Unknown {(byte)type}",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,24 @@
|
||||
using SabreTools.Models.NewExecutable;
|
||||
using SabreTools.Data.Models.NewExecutable;
|
||||
|
||||
namespace SabreTools.Serialization
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static partial class Extensions
|
||||
public static class NewExecutableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Determine if a resource type information entry is an integer or offset
|
||||
/// </summary>
|
||||
/// <param name="entry">Resource type information entry to check</param>
|
||||
/// <returns>True if the entry is an integer type, false if an offset, null on error</returns>
|
||||
public static bool? IsIntegerType(this ResourceTypeInformationEntry entry)
|
||||
{
|
||||
// We can't do anything with an invalid entry
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
// If the highest order bit is set, it's an integer type
|
||||
return (entry.TypeID & 0x8000) != 0;
|
||||
}
|
||||
public static bool IsIntegerType(this ResourceTypeInformationEntry entry)
|
||||
=> (entry.TypeID & 0x8000) != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a resource type resource entry is an integer or offset
|
||||
/// </summary>
|
||||
/// <param name="entry">Resource type resource entry to check</param>
|
||||
/// <returns>True if the entry is an integer type, false if an offset, null on error</returns>
|
||||
public static bool? IsIntegerType(this ResourceTypeResourceEntry entry)
|
||||
{
|
||||
// We can't do anything with an invalid entry
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
// If the highest order bit is set, it's an integer type
|
||||
return (entry.ResourceID & 0x8000) != 0;
|
||||
}
|
||||
public static bool IsIntegerType(this ResourceTypeResourceEntry entry)
|
||||
=> (entry.ResourceID & 0x8000) != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Get the segment entry type for an entry table bundle
|
||||
@@ -41,10 +27,6 @@ namespace SabreTools.Serialization
|
||||
/// <returns>SegmentEntryType corresponding to the type</returns>
|
||||
public static SegmentEntryType GetEntryType(this EntryTableBundle entry)
|
||||
{
|
||||
// We can't do anything with an invalid entry
|
||||
if (entry == null)
|
||||
return SegmentEntryType.Unused;
|
||||
|
||||
// Determine the entry type based on segment indicator
|
||||
if (entry.SegmentIndicator == 0x00)
|
||||
return SegmentEntryType.Unused;
|
||||
@@ -57,4 +39,4 @@ namespace SabreTools.Serialization
|
||||
return SegmentEntryType.Unused;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
77
SabreTools.Data.Extensions/NintendoDiscExtensions.cs
Normal file
77
SabreTools.Data.Extensions/NintendoDiscExtensions.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using SabreTools.Data.Models.NintendoDisc;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class NintendoDiscExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the platform associated with a disc header
|
||||
/// </summary>
|
||||
public static Platform GetPlatform(this DiscHeader header)
|
||||
{
|
||||
if (header.WiiMagic == Constants.WiiMagicWord)
|
||||
return Platform.Wii;
|
||||
else if (header.GCMagic == Constants.GCMagicWord)
|
||||
return Platform.GameCube;
|
||||
else if (header.GameId.Length >= 1 && IsGameCubeTitleType(header.GameId[0]))
|
||||
return Platform.GameCube;
|
||||
else
|
||||
return Platform.Unknown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the platform associated with a disc header
|
||||
/// </summary>
|
||||
public static Platform GetPlatform(this byte[] header)
|
||||
{
|
||||
// Check for Wii magic bytes
|
||||
if (header.Length >= 0x1C)
|
||||
{
|
||||
int offset = 0x18;
|
||||
uint magic = header.ReadUInt32BigEndian(ref offset);
|
||||
if (magic == Constants.WiiMagicWord)
|
||||
return Platform.Wii;
|
||||
}
|
||||
|
||||
// Check for GameCube magic bytes
|
||||
if (header.Length >= 0x20)
|
||||
{
|
||||
int offset = 0x1C;
|
||||
uint magic = header.ReadUInt32BigEndian(ref offset);
|
||||
if (magic == Constants.GCMagicWord)
|
||||
return Platform.GameCube;
|
||||
}
|
||||
|
||||
// Check for a valid game ID
|
||||
if (header.Length >= 4)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
byte c = header[i];
|
||||
if (!((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')))
|
||||
return Platform.Unknown;
|
||||
}
|
||||
|
||||
return Platform.GameCube;
|
||||
}
|
||||
|
||||
return Platform.Unknown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the GameId first character is a known GameCube title type prefix.
|
||||
/// Used as a fallback when the GC magic word is absent from the disc image.
|
||||
/// </summary>
|
||||
public static bool IsGameCubeTitleType(this char c)
|
||||
=> c == 'G' || c == 'D' || c == 'R';
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the byte is a known Nintendo disc title type code
|
||||
/// (first byte of the 6-char GameId, e.g. 'G'=GameCube, 'R'=GameCube,
|
||||
/// 'D'=GameCube demo, 'S'=Wii, 'F'=Wii channel)
|
||||
/// </summary>
|
||||
public static bool IsNintendoDiscTitleType(this byte b)
|
||||
=> b == 'G' || b == 'D' || b == 'R' || b == 'S' || b == 'F';
|
||||
}
|
||||
}
|
||||
78
SabreTools.Data.Extensions/OperaFSExtensions.cs
Normal file
78
SabreTools.Data.Extensions/OperaFSExtensions.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using SabreTools.Data.Models.OperaFS;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class OperaFSExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Compare two DirectoryDescriptor objects
|
||||
/// </summary>
|
||||
/// <param name="dir1">The provided DirectoryDescriptor</param>
|
||||
/// <param name="dir2">The DirectoryDescriptor to compare against</param>
|
||||
/// <returns>True if all fields are equal, otherwise false</returns>
|
||||
public static bool EqualsExactly(this DirectoryDescriptor dir1, DirectoryDescriptor dir2)
|
||||
{
|
||||
if (dir1.NextBlock != dir2.NextBlock)
|
||||
return false;
|
||||
if (dir1.PreviousBlock != dir2.PreviousBlock)
|
||||
return false;
|
||||
if (dir1.Flags != dir2.Flags)
|
||||
return false;
|
||||
if (dir1.FirstFreeByte != dir2.FirstFreeByte)
|
||||
return false;
|
||||
if (dir1.FirstEntryOffset != dir2.FirstEntryOffset)
|
||||
return false;
|
||||
if (dir1.DirectoryRecords.Length != dir2.DirectoryRecords.Length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < dir1.DirectoryRecords.Length; i++)
|
||||
{
|
||||
if (!dir1.DirectoryRecords[i].EqualsExactly(dir2.DirectoryRecords[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two DirectoryRecord objects
|
||||
/// </summary>
|
||||
/// <param name="dir1">The provided DirectoryRecord</param>
|
||||
/// <param name="dir2">The DirectoryRecord to compare against</param>
|
||||
/// <returns>True if all fields are equal, otherwise false</returns>
|
||||
public static bool EqualsExactly(this DirectoryRecord dr1, DirectoryRecord dr2)
|
||||
{
|
||||
if ((byte)dr1.DirectoryRecordFlags != (byte)dr2.DirectoryRecordFlags)
|
||||
return false;
|
||||
if (!dr1.UniqueIdentifier.EqualsExactly(dr2.UniqueIdentifier))
|
||||
return false;
|
||||
if (!dr1.Type.EqualsExactly(dr2.Type))
|
||||
return false;
|
||||
if (dr1.BlockSize != dr2.BlockSize)
|
||||
return false;
|
||||
if (dr1.ByteCount != dr2.ByteCount)
|
||||
return false;
|
||||
if (dr1.BlockCount != dr2.BlockCount)
|
||||
return false;
|
||||
if (dr1.Burst != dr2.Burst)
|
||||
return false;
|
||||
if (dr1.Gap != dr2.Gap)
|
||||
return false;
|
||||
if (!dr1.Filename.EqualsExactly(dr2.Filename))
|
||||
return false;
|
||||
if (dr1.LastAvatarIndex != dr2.LastAvatarIndex)
|
||||
return false;
|
||||
if (dr1.AvatarList.Length != dr2.AvatarList.Length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < dr1.AvatarList.Length; i++)
|
||||
{
|
||||
if (dr1.AvatarList[i] != dr2.AvatarList[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
98
SabreTools.Data.Extensions/PortableExecutableExtensions.cs
Normal file
98
SabreTools.Data.Extensions/PortableExecutableExtensions.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using SabreTools.Data.Models.COFF;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
// TODO: Add tests
|
||||
public static class PortableExecutableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a relative virtual address to a physical one
|
||||
/// </summary>
|
||||
/// <param name="rva">Relative virtual address to convert</param>
|
||||
/// <param name="sections">Array of sections to check against</param>
|
||||
/// <returns>Physical address, 0 on error</returns>
|
||||
public static uint ConvertVirtualAddress(this uint rva, SectionHeader[] sections)
|
||||
{
|
||||
// If we have an invalid section table, we can't do anything
|
||||
if (sections.Length == 0)
|
||||
return 0;
|
||||
|
||||
// If the RVA is 0, we just return 0 because it's invalid
|
||||
if (rva == 0)
|
||||
return 0;
|
||||
|
||||
// If the RVA matches a section start exactly, use that
|
||||
var matchingSection = Array.Find(sections, s => s.VirtualAddress == rva);
|
||||
if (matchingSection is not null)
|
||||
return rva - matchingSection.VirtualAddress + matchingSection.PointerToRawData;
|
||||
|
||||
// Loop through all of the sections
|
||||
uint maxVirtualAddress = 0, maxRawPointer = 0;
|
||||
for (int i = 0; i < sections.Length; i++)
|
||||
{
|
||||
// If the section "starts" at 0, just skip it
|
||||
var section = sections[i];
|
||||
if (section.PointerToRawData == 0)
|
||||
continue;
|
||||
|
||||
// If the virtual address is greater than the RVA
|
||||
if (rva < section.VirtualAddress)
|
||||
continue;
|
||||
|
||||
// Cache the maximum matching section data, in case of a miss
|
||||
if (rva >= section.VirtualAddress)
|
||||
{
|
||||
maxVirtualAddress = section.VirtualAddress;
|
||||
maxRawPointer = section.PointerToRawData;
|
||||
}
|
||||
|
||||
// Attempt to derive the physical address from the current section
|
||||
if (section.VirtualSize != 0 && rva <= section.VirtualAddress + section.VirtualSize)
|
||||
return rva - section.VirtualAddress + section.PointerToRawData;
|
||||
else if (section.SizeOfRawData != 0 && rva <= section.VirtualAddress + section.SizeOfRawData)
|
||||
return rva - section.VirtualAddress + section.PointerToRawData;
|
||||
}
|
||||
|
||||
return maxRawPointer != 0 ? rva - maxVirtualAddress + maxRawPointer : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the section a revlative virtual address lives in
|
||||
/// </summary>
|
||||
/// <param name="rva">Relative virtual address to convert</param>
|
||||
/// <param name="sections">Array of sections to check against</param>
|
||||
/// <returns>Section index, null on error</returns>
|
||||
public static int ContainingSectionIndex(this uint rva, SectionHeader[] sections)
|
||||
{
|
||||
// If we have an invalid section table, we can't do anything
|
||||
if (sections is null || sections.Length == 0)
|
||||
return -1;
|
||||
|
||||
// If the RVA is 0, we just return -1 because it's invalid
|
||||
if (rva == 0)
|
||||
return -1;
|
||||
|
||||
// Loop through all of the sections
|
||||
for (int i = 0; i < sections.Length; i++)
|
||||
{
|
||||
// If the section "starts" at 0, just skip it
|
||||
var section = sections[i];
|
||||
if (section.PointerToRawData == 0)
|
||||
continue;
|
||||
|
||||
// If the virtual address is greater than the RVA
|
||||
if (rva < section.VirtualAddress)
|
||||
continue;
|
||||
|
||||
// Attempt to derive the physical address from the current section
|
||||
if (section.VirtualSize != 0 && rva <= section.VirtualAddress + section.VirtualSize)
|
||||
return i;
|
||||
else if (section.SizeOfRawData != 0 && rva <= section.VirtualAddress + section.SizeOfRawData)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
SabreTools.Data.Extensions/README.MD
Normal file
24
SabreTools.Data.Extensions/README.MD
Normal file
@@ -0,0 +1,24 @@
|
||||
# SabreTools.Data.Extensions
|
||||
|
||||
This library contains extension methods related to models. These extensions are used in both reader and wrapper code.
|
||||
|
||||
## Provided Extension Classes
|
||||
|
||||
| Class Name | Applicable Namespace |
|
||||
| --- | --- |
|
||||
| `Atari7800CartExtensions` | `SabreTools.Data.Models.Atari7800` |
|
||||
| `AtariLynxCartExtensions` | `SabreTools.Data.Models.AtariLynx` |
|
||||
| `BSPExtensions` | `SabreTools.Data.Models.BSP` |
|
||||
| `CDROMExtensions` | `SabreTools.Data.Models.CDROM` |
|
||||
| `CFBExtensions` | `SabreTools.Data.Models.CFB` |
|
||||
| `InstallShieldCabinetExtensions` | `SabreTools.Data.Models.InstallShieldCabinet` |
|
||||
| `ISO9660Extensions` | `SabreTools.Data.Models.ISO9660` |
|
||||
| `MetadataExtensions` | `SabreTools.Data.Models.Metadata` |
|
||||
| `NESCartExtensions` | `SabreTools.Data.Models.NES` |
|
||||
| `NewExecutableExtensions` | `SabreTools.Data.Models.NewExecutable` |
|
||||
| `PortableExecutableExtensions` | `SabreTools.Data.Models.COFF` |
|
||||
| `TypeLengthValueExtensions` | `SabreTools.Data.Models.ASN1` and `SabreTools.ObjectIdentifier` |
|
||||
| `VBSPExtensions` | `SabreTools.Data.Models.BSP` |
|
||||
| `WiseScriptExtensions` | `SabreTools.Data.Models.WiseInstaller` |
|
||||
| `XboxExecutableExtensions` | `SabreTools.Data.Models.XboxExecutable` |
|
||||
| `XZExtensions` | `SabreTools.Data.Models.XZ` |
|
||||
43
SabreTools.Data.Extensions/SabreTools.Data.Extensions.csproj
Normal file
43
SabreTools.Data.Extensions/SabreTools.Data.Extensions.csproj
Normal file
@@ -0,0 +1,43 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>3.2.0</Version>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Description>Extension methods related to models</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2026</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<RepositoryUrl>https://github.com/SabreTools/SabreTools.Serialization</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageTags>models extensions conversion</PackageTags>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="README.md" Pack="true" PackagePath="" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NetLegacySupport.Numerics" Version="1.0.1" Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`))" />
|
||||
<PackageReference Include="SabreTools.IO" Version="2.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.Data.Models\SabreTools.Data.Models.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.ObjectIdentifier\SabreTools.ObjectIdentifier.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
181
SabreTools.Data.Extensions/TypeLengthValueExtensions.cs
Normal file
181
SabreTools.Data.Extensions/TypeLengthValueExtensions.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using SabreTools.Data.Models.ASN1;
|
||||
using SabreTools.ObjectIdentifier;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class TypeLengthValueExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Format a TypeLengthValue as a string
|
||||
/// </summary>
|
||||
/// <param name="paddingLevel">Padding level of the item when formatting</param>
|
||||
/// <returns>String representing the TypeLengthValue, if possible</returns>
|
||||
public static string Format(this TypeLengthValue tlv, int paddingLevel = 0)
|
||||
{
|
||||
// Create the left-padding string
|
||||
string padding = new(' ', paddingLevel);
|
||||
|
||||
// Create the string builder
|
||||
var formatBuilder = new StringBuilder();
|
||||
|
||||
// Append the type
|
||||
formatBuilder.Append($"{padding}Type: {tlv.Type}");
|
||||
if (tlv.Type == ASN1Type.V_ASN1_EOC)
|
||||
return formatBuilder.ToString();
|
||||
|
||||
// Append the length
|
||||
formatBuilder.Append($", Length: {tlv.Length}");
|
||||
if (tlv.Length == 0)
|
||||
return formatBuilder.ToString();
|
||||
|
||||
// If we have a constructed type
|
||||
#if NET20 || NET35
|
||||
if ((tlv.Type & ASN1Type.V_ASN1_CONSTRUCTED) != 0)
|
||||
#else
|
||||
if (tlv.Type.HasFlag(ASN1Type.V_ASN1_CONSTRUCTED))
|
||||
#endif
|
||||
{
|
||||
if (tlv.Value is not TypeLengthValue[] valueAsObjectArray)
|
||||
{
|
||||
formatBuilder.Append(", Value: [INVALID DATA TYPE]");
|
||||
return formatBuilder.ToString();
|
||||
}
|
||||
|
||||
formatBuilder.Append(", Value:\n");
|
||||
for (int i = 0; i < valueAsObjectArray.Length; i++)
|
||||
{
|
||||
var child = valueAsObjectArray[i];
|
||||
string childString = child.Format(paddingLevel + 1);
|
||||
formatBuilder.Append($"{childString}\n");
|
||||
}
|
||||
|
||||
return formatBuilder.ToString().TrimEnd('\n');
|
||||
}
|
||||
|
||||
// Get the value as a byte array
|
||||
if (tlv.Value is not byte[] valueAsByteArray)
|
||||
{
|
||||
formatBuilder.Append(", Value: [INVALID DATA TYPE]");
|
||||
return formatBuilder.ToString();
|
||||
}
|
||||
else if (valueAsByteArray.Length == 0)
|
||||
{
|
||||
formatBuilder.Append(", Value: [NO DATA]");
|
||||
return formatBuilder.ToString();
|
||||
}
|
||||
|
||||
// If we have a primitive type
|
||||
switch (tlv.Type)
|
||||
{
|
||||
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-boolean"/>
|
||||
case ASN1Type.V_ASN1_BOOLEAN:
|
||||
if (tlv.Length > 1)
|
||||
formatBuilder.Append($" [Expected length of 1]");
|
||||
else if (valueAsByteArray.Length > 1)
|
||||
formatBuilder.Append($" [Expected value length of 1]");
|
||||
|
||||
bool booleanValue = valueAsByteArray[0] != 0x00;
|
||||
formatBuilder.Append($", Value: {booleanValue}");
|
||||
break;
|
||||
|
||||
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-integer"/>
|
||||
case ASN1Type.V_ASN1_INTEGER:
|
||||
Array.Reverse(valueAsByteArray);
|
||||
var integerValue = new BigInteger(valueAsByteArray);
|
||||
formatBuilder.Append($", Value: {integerValue}");
|
||||
break;
|
||||
|
||||
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-bit-string"/>
|
||||
case ASN1Type.V_ASN1_BIT_STRING:
|
||||
// TODO: Read into a BitArray and print that out instead?
|
||||
int unusedBits = valueAsByteArray[0];
|
||||
if (unusedBits == 0)
|
||||
formatBuilder.Append($", Value with {unusedBits} unused bits");
|
||||
else
|
||||
formatBuilder.Append($", Value with {unusedBits} unused bits: {BitConverter.ToString(valueAsByteArray, 1).Replace('-', ' ')}");
|
||||
break;
|
||||
|
||||
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-octet-string"/>
|
||||
case ASN1Type.V_ASN1_OCTET_STRING:
|
||||
formatBuilder.Append($", Value: {BitConverter.ToString(valueAsByteArray).Replace('-', ' ')}");
|
||||
break;
|
||||
|
||||
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-object-identifier"/>
|
||||
/// <see cref="http://snmpsharpnet.com/index.php/2009/03/02/ber-encoding-and-decoding-oid-values/"/>
|
||||
case ASN1Type.V_ASN1_OBJECT:
|
||||
// Derive array of values
|
||||
ulong[] objectNodes = Parser.ParseDERIntoArray(valueAsByteArray, tlv.Length);
|
||||
|
||||
// Append the dot and modified OID-IRI notations
|
||||
string? dotNotationString = Parser.ParseOIDToDotNotation(objectNodes);
|
||||
string? oidIriString = Parser.ParseOIDToOIDIRINotation(objectNodes);
|
||||
formatBuilder.Append($", Value: {dotNotationString} ({oidIriString})");
|
||||
break;
|
||||
|
||||
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-utf8string"/>
|
||||
case ASN1Type.V_ASN1_UTF8STRING:
|
||||
formatBuilder.Append($", Value: {Encoding.UTF8.GetString(valueAsByteArray)}");
|
||||
break;
|
||||
|
||||
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-printablestring"/>
|
||||
case ASN1Type.V_ASN1_PRINTABLESTRING:
|
||||
formatBuilder.Append($", Value: {Encoding.ASCII.GetString(valueAsByteArray)}");
|
||||
break;
|
||||
|
||||
//case ASN1Type.V_ASN1_T61STRING:
|
||||
case ASN1Type.V_ASN1_TELETEXSTRING:
|
||||
formatBuilder.Append($", Value: {Encoding.ASCII.GetString(valueAsByteArray)}");
|
||||
break;
|
||||
|
||||
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-ia5string"/>
|
||||
case ASN1Type.V_ASN1_IA5STRING:
|
||||
formatBuilder.Append($", Value: {Encoding.ASCII.GetString(valueAsByteArray)}");
|
||||
break;
|
||||
|
||||
case ASN1Type.V_ASN1_UTCTIME:
|
||||
string utctimeString = Encoding.ASCII.GetString(valueAsByteArray);
|
||||
if (DateTime.TryParse(utctimeString, out DateTime utctimeDateTime))
|
||||
formatBuilder.Append($", Value: {utctimeDateTime:yyyy-MM-dd HH:mm:ss}");
|
||||
else
|
||||
formatBuilder.Append($", Value: {utctimeString}");
|
||||
break;
|
||||
|
||||
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-bmpstring"/>
|
||||
case ASN1Type.V_ASN1_BMPSTRING:
|
||||
formatBuilder.Append($", Value: {Encoding.Unicode.GetString(valueAsByteArray)}");
|
||||
break;
|
||||
|
||||
// Unimplemented
|
||||
case ASN1Type.V_ASN1_EOC:
|
||||
case ASN1Type.V_ASN1_NULL:
|
||||
case ASN1Type.V_ASN1_OBJECT_DESCRIPTOR:
|
||||
case ASN1Type.V_ASN1_EXTERNAL:
|
||||
case ASN1Type.V_ASN1_REAL:
|
||||
case ASN1Type.V_ASN1_ENUMERATED:
|
||||
case ASN1Type.V_ASN1_SEQUENCE:
|
||||
case ASN1Type.V_ASN1_SET:
|
||||
case ASN1Type.V_ASN1_NUMERICSTRING:
|
||||
case ASN1Type.V_ASN1_VIDEOTEXSTRING:
|
||||
case ASN1Type.V_ASN1_GENERALIZEDTIME:
|
||||
case ASN1Type.V_ASN1_GRAPHICSTRING:
|
||||
case ASN1Type.V_ASN1_ISO64STRING:
|
||||
case ASN1Type.V_ASN1_GENERALSTRING:
|
||||
case ASN1Type.V_ASN1_UNIVERSALSTRING:
|
||||
case ASN1Type.V_ASN1_PRIMITIVE_TAG:
|
||||
case ASN1Type.V_ASN1_CONSTRUCTED:
|
||||
case ASN1Type.V_ASN1_APPLICATION:
|
||||
case ASN1Type.V_ASN1_CONTEXT_SPECIFIC:
|
||||
case ASN1Type.V_ASN1_PRIVATE:
|
||||
default:
|
||||
formatBuilder.Append($", Value: {BitConverter.ToString(valueAsByteArray).Replace('-', ' ')}");
|
||||
break;
|
||||
}
|
||||
|
||||
// Return the formatted string
|
||||
return formatBuilder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
82
SabreTools.Data.Extensions/VBSPExtensions.cs
Normal file
82
SabreTools.Data.Extensions/VBSPExtensions.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using SabreTools.Data.Models.BSP;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class VBSPExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a <see cref="VbspLumpType"/> value to string
|
||||
/// </summary>
|
||||
public static string FromVbspLumpType(this VbspLumpType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
VbspLumpType.LUMP_ENTITIES => " - LUMP_ENTITIES",
|
||||
VbspLumpType.LUMP_PLANES => " - LUMP_PLANES",
|
||||
VbspLumpType.LUMP_TEXTURES => " - LUMP_TEXDATA",
|
||||
VbspLumpType.LUMP_VERTICES => " - LUMP_VERTEXES",
|
||||
VbspLumpType.LUMP_VISIBILITY => " - LUMP_VISIBILITY",
|
||||
VbspLumpType.LUMP_NODES => " - LUMP_NODES",
|
||||
VbspLumpType.LUMP_TEXINFO => " - LUMP_TEXINFO",
|
||||
VbspLumpType.LUMP_FACES => " - LUMP_FACES",
|
||||
VbspLumpType.LUMP_LIGHTING => " - LUMP_LIGHTING",
|
||||
VbspLumpType.LUMP_CLIPNODES => " - LUMP_OCCLUSION",
|
||||
VbspLumpType.LUMP_LEAVES => " - LUMP_LEAVES",
|
||||
VbspLumpType.LUMP_MARKSURFACES => " - LUMP_FACEIDS",
|
||||
VbspLumpType.LUMP_EDGES => " - LUMP_EDGES",
|
||||
VbspLumpType.LUMP_SURFEDGES => " - LUMP_SURFEDGES",
|
||||
VbspLumpType.LUMP_MODELS => " - LUMP_MODELS",
|
||||
VbspLumpType.LUMP_WORLDLIGHTS => " - LUMP_WORLDLIGHTS",
|
||||
VbspLumpType.LUMP_LEAFFACES => " - LUMP_LEAFFACES",
|
||||
VbspLumpType.LUMP_LEAFBRUSHES => " - LUMP_LEAFBRUSHES",
|
||||
VbspLumpType.LUMP_BRUSHES => " - LUMP_BRUSHES",
|
||||
VbspLumpType.LUMP_BRUSHSIDES => " - LUMP_BRUSHSIDES",
|
||||
VbspLumpType.LUMP_AREAS => " - LUMP_AREAS",
|
||||
VbspLumpType.LUMP_AREAPORTALS => " - LUMP_AREAPORTALS",
|
||||
VbspLumpType.LUMP_PORTALS => " - LUMP_PORTALS / LUMP_UNUSED0 / LUMP_PROPCOLLISION",
|
||||
VbspLumpType.LUMP_CLUSTERS => " - LUMP_CLUSTERS / LUMP_UNUSED1 / LUMP_PROPHULLS",
|
||||
VbspLumpType.LUMP_PORTALVERTS => " - LUMP_PORTALVERTS / LUMP_UNUSED2 / LUMP_FAKEENTITIES / LUMP_PROPHULLVERTS",
|
||||
VbspLumpType.LUMP_CLUSTERPORTALS => " - LUMP_CLUSTERPORTALS / LUMP_UNUSED3 / LUMP_PROPTRIS",
|
||||
VbspLumpType.LUMP_DISPINFO => " - LUMP_DISPINFO",
|
||||
VbspLumpType.LUMP_ORIGINALFACES => " - LUMP_ORIGINALFACES",
|
||||
VbspLumpType.LUMP_PHYSDISP => " - LUMP_PHYSDISP",
|
||||
VbspLumpType.LUMP_PHYSCOLLIDE => " - LUMP_PHYSCOLLIDE",
|
||||
VbspLumpType.LUMP_VERTNORMALS => " - LUMP_VERTNORMALS",
|
||||
VbspLumpType.LUMP_VERTNORMALINDICES => " - LUMP_VERTNORMALINDICES",
|
||||
VbspLumpType.LUMP_DISP_LIGHTMAP_ALPHAS => " - LUMP_DISP_LIGHTMAP_ALPHAS",
|
||||
VbspLumpType.LUMP_DISP_VERTS => " - LUMP_DISP_VERTS",
|
||||
VbspLumpType.LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS => " - LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS",
|
||||
VbspLumpType.LUMP_GAME_LUMP => " - LUMP_GAME_LUMP",
|
||||
VbspLumpType.LUMP_LEAFWATERDATA => " - LUMP_LEAFWATERDATA",
|
||||
VbspLumpType.LUMP_PRIMITIVES => " - LUMP_PRIMITIVES",
|
||||
VbspLumpType.LUMP_PRIMVERTS => " - LUMP_PRIMVERTS",
|
||||
VbspLumpType.LUMP_PRIMINDICES => " - LUMP_PRIMINDICES",
|
||||
VbspLumpType.LUMP_PAKFILE => " - LUMP_PAKFILE",
|
||||
VbspLumpType.LUMP_CLIPPORTALVERTS => " - LUMP_CLIPPORTALVERTS",
|
||||
VbspLumpType.LUMP_CUBEMAPS => " - LUMP_CUBEMAPS",
|
||||
VbspLumpType.LUMP_TEXDATA_STRING_DATA => " - LUMP_TEXDATA_STRING_DATA",
|
||||
VbspLumpType.LUMP_TEXDATA_STRING_TABLE => " - LUMP_TEXDATA_STRING_TABLE",
|
||||
VbspLumpType.LUMP_OVERLAYS => " - LUMP_OVERLAYS",
|
||||
VbspLumpType.LUMP_LEAFMINDISTTOWATER => " - LUMP_LEAFMINDISTTOWATER",
|
||||
VbspLumpType.LUMP_FACE_MACRO_TEXTURE_INFO => " - LUMP_FACE_MACRO_TEXTURE_INFO",
|
||||
VbspLumpType.LUMP_DISP_TRIS => " - LUMP_DISP_TRIS",
|
||||
VbspLumpType.LUMP_PHYSCOLLIDESURFACE => " - LUMP_PHYSCOLLIDESURFACE / LUMP_PROP_BLOB",
|
||||
VbspLumpType.LUMP_WATEROVERLAYS => " - LUMP_WATEROVERLAYS",
|
||||
VbspLumpType.LUMP_LIGHTMAPPAGES => " - LUMP_LIGHTMAPPAGES / LUMP_LEAF_AMBIENT_INDEX_HDR",
|
||||
VbspLumpType.LUMP_LIGHTMAPPAGEINFOS => " - LUMP_LIGHTMAPPAGEINFOS / LUMP_LEAF_AMBIENT_INDEX",
|
||||
VbspLumpType.LUMP_LIGHTING_HDR => " - LUMP_LIGHTING_HDR",
|
||||
VbspLumpType.LUMP_WORLDLIGHTS_HDR => " - LUMP_WORLDLIGHTS_HDR",
|
||||
VbspLumpType.LUMP_LEAF_AMBIENT_LIGHTING_HDR => " - LUMP_LEAF_AMBIENT_LIGHTING_HDR",
|
||||
VbspLumpType.LUMP_LEAF_AMBIENT_LIGHTING => " - LUMP_LEAF_AMBIENT_LIGHTING",
|
||||
VbspLumpType.LUMP_XZIPPAKFILE => " - LUMP_XZIPPAKFILE",
|
||||
VbspLumpType.LUMP_FACES_HDR => " - LUMP_FACES_HDR",
|
||||
VbspLumpType.LUMP_MAP_FLAGS => " - LUMP_MAP_FLAGS",
|
||||
VbspLumpType.LUMP_OVERLAY_FADES => " - LUMP_OVERLAY_FADES",
|
||||
VbspLumpType.LUMP_OVERLAY_SYSTEM_LEVELS => " - LUMP_OVERLAY_SYSTEM_LEVELS",
|
||||
VbspLumpType.LUMP_PHYSLEVEL => " - LUMP_PHYSLEVEL",
|
||||
VbspLumpType.LUMP_DISP_MULTIBLEND => " - LUMP_DISP_MULTIBLEND",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
66
SabreTools.Data.Extensions/WiseScriptExtensions.cs
Normal file
66
SabreTools.Data.Extensions/WiseScriptExtensions.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class WiseScriptExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a Wise function ID to the formal action name
|
||||
/// </summary>
|
||||
/// <param name="functionId">Function ID to convert</param>
|
||||
/// <returns>The formal action name on success, null otherwise</returns>
|
||||
public static string? FromWiseFunctionId(this string? functionId)
|
||||
{
|
||||
return functionId switch
|
||||
{
|
||||
"f0" => "Add Directory to PATH",
|
||||
"f1" => "Add to AUTOEXEC.BAT",
|
||||
"f2" => "Add to CONFIG.SYS",
|
||||
"f3" => "Add to SYSTEM.INI",
|
||||
"f8" => "Read INI Value",
|
||||
"f9" => "Get Registry Key Value",
|
||||
"f10" => "Register Font",
|
||||
"f11" => "Win32 System Directory",
|
||||
"f12" => "Check Configuration",
|
||||
"f13" => "Search for File",
|
||||
"f15" => "Read/Write Binary File",
|
||||
"f16" => "Set Variable",
|
||||
"f17" => "Get Environment Variable",
|
||||
"f19" => "Check if File/Dir Exists",
|
||||
"f20" => "Set File Attributes",
|
||||
"f21" => "Set Files/Buffers",
|
||||
"f22" => "Find File in Path",
|
||||
"f23" => "Check Disk Space",
|
||||
"f25" => "Insert Line Into Text File",
|
||||
"f27" => "Parse String",
|
||||
"f28" => "Exit Installation",
|
||||
"f29" => "Self-Register OCXs/DLLs",
|
||||
"f30" => "Install DirectX Components",
|
||||
"f31" => "Wizard Block",
|
||||
"f33" => "Read/Update Text File",
|
||||
"f34" => "Post to HTTP Server",
|
||||
"f35" => "Prompt for Filename",
|
||||
"f36" => "Start/Stop Service",
|
||||
"f38" => "Check HTTP Connection",
|
||||
|
||||
// Undefined function IDs
|
||||
"f4" => $"UNDEFINED {functionId}",
|
||||
"f5" => $"UNDEFINED {functionId}",
|
||||
"f6" => $"UNDEFINED {functionId}",
|
||||
"f7" => $"UNDEFINED {functionId}",
|
||||
"f14" => $"UNDEFINED {functionId}",
|
||||
"f18" => $"UNDEFINED {functionId}",
|
||||
"f24" => $"UNDEFINED {functionId}",
|
||||
"f26" => $"UNDEFINED {functionId}",
|
||||
"f32" => $"UNDEFINED {functionId}",
|
||||
"f37" => $"UNDEFINED {functionId}",
|
||||
|
||||
// External DLL
|
||||
null => null,
|
||||
#pragma warning disable SYSLIB1045 // Convert to 'GeneratedRegexAttribute'
|
||||
_ => Regex.IsMatch(functionId, @"^f[0-9]{1,2}$") ? $"UNDEFINED {functionId}" : $"External: {functionId}",
|
||||
#pragma warning restore SYSLIB1045
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
62
SabreTools.Data.Extensions/XZExtensions.cs
Normal file
62
SabreTools.Data.Extensions/XZExtensions.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class XZExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Decode a value from a variable-length integer
|
||||
/// </summary>
|
||||
/// <param name="value">Value to decode</param>
|
||||
/// <param name="maxSize">Maximum number of bytes to parse</param>
|
||||
/// <param name="length">Number of bytes parsed</param>
|
||||
/// <returns>UInt64 representing the decoded integer</returns>
|
||||
/// <see href="https://tukaani.org/xz/xz-file-format.txt"/>
|
||||
public static ulong DecodeVariableLength(this byte[] value, int maxSize, out int length)
|
||||
{
|
||||
length = 0;
|
||||
if (maxSize <= 0)
|
||||
return 0;
|
||||
|
||||
if (maxSize > 9)
|
||||
maxSize = 9;
|
||||
|
||||
ulong output = (ulong)(value[0] & 0x7F);
|
||||
int i = 0;
|
||||
|
||||
while ((value[i++] & 0x80) != 0)
|
||||
{
|
||||
if (i >= maxSize || value[i] == 0x00)
|
||||
return 0;
|
||||
|
||||
output |= (ulong)(value[i] & 0x7F) << (i * 7);
|
||||
}
|
||||
|
||||
length = i;
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encode a value to a variable-length integer
|
||||
/// </summary>
|
||||
/// <param name="value">Value to encode</param>
|
||||
/// <returns>Byte array representing the encoded integer</returns>
|
||||
/// <see href="https://tukaani.org/xz/xz-file-format.txt"/>
|
||||
public static byte[] EncodeVariableLength(this ulong value)
|
||||
{
|
||||
if (value > long.MaxValue / 2)
|
||||
return [];
|
||||
|
||||
var output = new List<byte>();
|
||||
|
||||
while (value >= 0x80)
|
||||
{
|
||||
output.Add((byte)(value | 0x80));
|
||||
value >>= 7;
|
||||
}
|
||||
|
||||
output.Add((byte)value);
|
||||
return [.. output];
|
||||
}
|
||||
}
|
||||
}
|
||||
32
SabreTools.Data.Extensions/XboxExecutableExtensions.cs
Normal file
32
SabreTools.Data.Extensions/XboxExecutableExtensions.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class XboxExecutableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a UInt32 to a formatted XBE title ID
|
||||
/// </summary>
|
||||
public static string ToFormattedXBETitleID(this uint value)
|
||||
{
|
||||
// Convert to a byte array
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
|
||||
// Create the prefix
|
||||
string prefix1 = (0x41 <= data[3] && data[3] <= 0x5A) || (0x30 <= data[3] && data[3] <= 0x39)
|
||||
? $"{(char)data[3]}"
|
||||
: $"{data[3]:X2}";
|
||||
string prefix2 = (0x41 <= data[2] && data[2] <= 0x5A) || (0x30 <= data[2] && data[2] <= 0x39)
|
||||
? $"{(char)data[2]}"
|
||||
: $"{data[2]:X2}";
|
||||
|
||||
// Create the serial
|
||||
int offset = 0;
|
||||
ushort serial = data.ReadUInt16LittleEndian(ref offset);
|
||||
|
||||
// Assemble and return
|
||||
return $"{prefix1}{prefix2}-{serial:D3}";
|
||||
}
|
||||
}
|
||||
}
|
||||
36
SabreTools.Data.Extensions/ZArchiveExtensions.cs
Normal file
36
SabreTools.Data.Extensions/ZArchiveExtensions.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using SabreTools.Data.Models.ZArchive;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class ZArchiveExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the name of the specified node from the NameTable
|
||||
/// </summary>
|
||||
/// <param name="node">Node in the file tree</param>
|
||||
/// <param name="nameTable">ZArchive NameTable</param>
|
||||
/// <returns>UTF-8 string representing node's name</returns>
|
||||
public static string? GetName(this FileDirectoryEntry node, NameTable nameTable)
|
||||
{
|
||||
// Check for a valid offset into the NameTable
|
||||
uint nameOffset = node.NameOffsetAndTypeFlag & Constants.RootNode;
|
||||
if (nameOffset == Constants.RootNode)
|
||||
return null;
|
||||
|
||||
// Get the index into the name table
|
||||
var index = Array.IndexOf(nameTable.NameTableOffsets, nameOffset);
|
||||
if (index < 0)
|
||||
return null;
|
||||
|
||||
// Get the name entry for the requested index
|
||||
var nameEntry = nameTable.NameEntries[index];
|
||||
if (nameEntry is null)
|
||||
return null;
|
||||
|
||||
// Decode name to UTF-8
|
||||
return Encoding.UTF8.GetString(nameEntry.NodeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
SabreTools.Data.Models/AACS/CopyrightRecord.cs
Normal file
16
SabreTools.Data.Models/AACS/CopyrightRecord.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <summary>
|
||||
/// This record type is undocumented but found in real media key blocks
|
||||
/// </summary>
|
||||
public sealed class CopyrightRecord : Record
|
||||
{
|
||||
/// <summary>
|
||||
/// Null-terminated ASCII string representing the copyright
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
public string Copyright = string.Empty;
|
||||
}
|
||||
}
|
||||
25
SabreTools.Data.Models/AACS/DriveRevocationListEntry.cs
Normal file
25
SabreTools.Data.Models/AACS/DriveRevocationListEntry.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <see href="https://aacsla.com/wp-content/uploads/2019/02/AACS_Spec_Common_Final_0953.pdf"/>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public sealed class DriveRevocationListEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// A 2-byte Range value indicates the range of revoked ID’s starting
|
||||
/// from the ID contained in the record. A value of zero in the Range
|
||||
/// field indicates that only one ID is being revoked, a value of one
|
||||
/// in the Range field indicates two ID’s are being revoked, and so on.
|
||||
/// </summary>
|
||||
public ushort Range;
|
||||
|
||||
/// <summary>
|
||||
/// A 6-byte Drive ID value identifying the Licensed Drive being revoked
|
||||
/// (or the first in a range of Licensed Drives being revoked, in the
|
||||
/// case of a non-zero Range value).
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
|
||||
public byte[] DriveID = new byte[6];
|
||||
}
|
||||
}
|
||||
26
SabreTools.Data.Models/AACS/DriveRevocationListRecord.cs
Normal file
26
SabreTools.Data.Models/AACS/DriveRevocationListRecord.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <summary>
|
||||
/// A properly formatted type 3 or type 4 Media Key Block contains exactly
|
||||
/// one Drive Revocation List Record. It follows the Host Revocation List
|
||||
/// Record, although it may not immediately follow it.
|
||||
///
|
||||
/// The Drive Revocation List Record is identical to the Host Revocation
|
||||
/// List Record, except it has type 2016, and it contains Drive Revocation
|
||||
/// List Entries, not Host Revocation List Entries. The Drive Revocation List
|
||||
/// Entries refer to Drive IDs in the Drive Certificates.
|
||||
/// </summary>
|
||||
/// <see href="https://aacsla.com/wp-content/uploads/2019/02/AACS_Spec_Common_Final_0953.pdf"/>
|
||||
public sealed class DriveRevocationListRecord : Record
|
||||
{
|
||||
/// <summary>
|
||||
/// The total number of Drive Revocation List Entry fields that follow.
|
||||
/// </summary>
|
||||
public uint TotalNumberOfEntries { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Revocation list entries
|
||||
/// </summary>
|
||||
public DriveRevocationSignatureBlock[] SignatureBlocks { get; set; } = [];
|
||||
}
|
||||
}
|
||||
17
SabreTools.Data.Models/AACS/DriveRevocationSignatureBlock.cs
Normal file
17
SabreTools.Data.Models/AACS/DriveRevocationSignatureBlock.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <see href="https://aacsla.com/wp-content/uploads/2019/02/AACS_Spec_Common_Final_0953.pdf"/>
|
||||
public sealed class DriveRevocationSignatureBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of Drive Revocation List Entry fields in the signature block.
|
||||
/// </summary>
|
||||
public uint NumberOfEntries { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of 8-byte Host Drive List Entry fields, the length of this
|
||||
/// list being equal to the number in the signature block.
|
||||
/// </summary>
|
||||
public DriveRevocationListEntry[] EntryFields { get; set; } = [];
|
||||
}
|
||||
}
|
||||
24
SabreTools.Data.Models/AACS/EndOfMediaKeyBlockRecord.cs
Normal file
24
SabreTools.Data.Models/AACS/EndOfMediaKeyBlockRecord.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <summary>
|
||||
/// A properly formatted MKB shall contain an End of Media Key Block Record.
|
||||
/// When a device encounters this Record it stops processing the MKB, using
|
||||
/// whatever Km value it has calculated up to that point as the final Km for
|
||||
/// that MKB (pending possible checks for correctness of the key, as
|
||||
/// described previously).
|
||||
/// </summary>
|
||||
/// <see href="https://aacsla.com/wp-content/uploads/2019/02/AACS_Spec_Common_Final_0953.pdf"/>
|
||||
public sealed class EndOfMediaKeyBlockRecord : Record
|
||||
{
|
||||
/// <summary>
|
||||
/// AACS LA’s signature on the data in the Media Key Block up to,
|
||||
/// but not including, this record. Devices depending on the Version
|
||||
/// Number in the Type and Version Record must verify the signature.
|
||||
/// Other devices may ignore the signature data. If any device
|
||||
/// determines that the signature does not verify or is omitted, it
|
||||
/// must refuse to use the Media Key.
|
||||
/// </summary>
|
||||
/// <remarks><see cref="RecordLength"/>-4 bytes</remarks>
|
||||
public byte[] SignatureData { get; set; } = [];
|
||||
}
|
||||
}
|
||||
69
SabreTools.Data.Models/AACS/Enums.cs
Normal file
69
SabreTools.Data.Models/AACS/Enums.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
public enum MediaKeyBlockType : uint
|
||||
{
|
||||
/// <summary>
|
||||
/// (Type 3). This is a normal Media Key Block suitable for being recorded
|
||||
/// on a AACS Recordable Media. Both Class I and Class II Licensed Products
|
||||
/// use it to directly calculate the Media Key.
|
||||
/// </summary>
|
||||
Type3 = 0x00031003,
|
||||
|
||||
/// <summary>
|
||||
/// (Type 4). This is a Media Key Block that has been designed to use Key
|
||||
/// Conversion Data (KCD). Thus, it is suitable only for pre-recorded media
|
||||
/// from which the KCD is derived. Both Class I and Class II Licensed Products
|
||||
/// use it to directly calculate the Media Key.
|
||||
/// </summary>
|
||||
Type4 = 0x00041003,
|
||||
|
||||
/// <summary>
|
||||
/// (Type 10). This is a Class II Media Key Block (one that has the functionality
|
||||
/// of a Sequence Key Block). This can only be processed by Class II Licensed
|
||||
/// Products; Class I Licensed Products are revoked in Type 10 Media Key Blocks
|
||||
/// and cannot process them. This type does not contain the Host Revocation List
|
||||
/// Record, the Drive Revocation List Record, and the Media Key Data Record, as
|
||||
/// described in the following sections. It does contain the records shown in
|
||||
/// Section 3.2.5.2, which are only processed by Class II Licensed Products.
|
||||
/// </summary>
|
||||
Type10 = 0x000A1003,
|
||||
|
||||
/// <summary>
|
||||
/// Type 2.0 Category C.
|
||||
/// This is the Media Key Block Type found on "UHD" media (AACS v2.0)
|
||||
/// </summary>
|
||||
/// <see href="https://code.videolan.org/videolan/libaacs/-/blob/master/src/libaacs/mkb.h"/>
|
||||
Type20 = 0x48141003,
|
||||
|
||||
/// <summary>
|
||||
/// Type 2.1 Category C.
|
||||
/// This is the Media Key Block Type found on "UHD" media (AACS v2.1)
|
||||
/// </summary>
|
||||
/// <see href="https://code.videolan.org/videolan/libaacs/-/blob/master/src/libaacs/mkb.h"/>
|
||||
Type21 = 0x48151003,
|
||||
}
|
||||
|
||||
public enum RecordType : byte
|
||||
{
|
||||
EndOfMediaKeyBlock = 0x02,
|
||||
ExplicitSubsetDifference = 0x04,
|
||||
MediaKeyData = 0x05,
|
||||
SubsetDifferenceIndex = 0x07,
|
||||
MediaKeyVariantData = 0x0C,
|
||||
TypeAndVersion = 0x10,
|
||||
DriveRevocationList = 0x20,
|
||||
HostRevocationList = 0x21,
|
||||
VerifyMediaKey = 0x81,
|
||||
|
||||
// Not documented
|
||||
Copyright = 0x7F,
|
||||
|
||||
// Record types only found in UHD media (AACS v2)
|
||||
// <see href="https://code.videolan.org/videolan/libaacs/-/blob/master/src/devtools/mkb_dump.c"/>
|
||||
Unknown0x28_AACS2 = 0x28,
|
||||
DriveRevocationList_AACS2 = 0x30,
|
||||
HostRevocationList_AACS2 = 0x31,
|
||||
VerifyMediaKey_AACS2 = 0x86,
|
||||
EmptyRecord0xF8_AACS2 = 0xF8,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <see href="https://aacsla.com/wp-content/uploads/2019/02/AACS_Spec_Common_Final_0953.pdf"/>
|
||||
public sealed class ExplicitSubsetDifferenceRecord : Record
|
||||
{
|
||||
/// <summary>
|
||||
/// In this record, each subset-difference is encoded with 5 bytes.
|
||||
/// </summary>
|
||||
public SubsetDifference[] SubsetDifferences { get; set; } = [];
|
||||
}
|
||||
}
|
||||
13
SabreTools.Data.Models/AACS/GenericRecord.cs
Normal file
13
SabreTools.Data.Models/AACS/GenericRecord.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <summary>
|
||||
/// This represents any record that does not have a concrete model yet
|
||||
/// </summary>
|
||||
public sealed class GenericRecord : Record
|
||||
{
|
||||
/// <summary>
|
||||
/// Unparsed data comprising the record after the header
|
||||
/// </summary>
|
||||
public byte[] Data { get; set; } = [];
|
||||
}
|
||||
}
|
||||
25
SabreTools.Data.Models/AACS/HostRevocationListEntry.cs
Normal file
25
SabreTools.Data.Models/AACS/HostRevocationListEntry.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <see href="https://aacsla.com/wp-content/uploads/2019/02/AACS_Spec_Common_Final_0953.pdf"/>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public sealed class HostRevocationListEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// A 2-byte Range value indicates the range of revoked ID’s starting
|
||||
/// from the ID contained in the record. A value of zero in the Range
|
||||
/// field indicates that only one ID is being revoked, a value of one
|
||||
/// in the Range field indicates two ID’s are being revoked, and so on.
|
||||
/// </summary>
|
||||
public ushort Range;
|
||||
|
||||
/// <summary>
|
||||
/// A 6-byte Host ID value identifying the host being revoked (or the
|
||||
/// first in a range of hosts being revoked, in the case of a non-zero
|
||||
/// Range value).
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
|
||||
public byte[] HostID = new byte[6];
|
||||
}
|
||||
}
|
||||
29
SabreTools.Data.Models/AACS/HostRevocationListRecord.cs
Normal file
29
SabreTools.Data.Models/AACS/HostRevocationListRecord.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <summary>
|
||||
/// A properly formatted type 3 or type 4 Media Key Block shall have exactly
|
||||
/// one Host Revocation List Record as its second record. This record provides
|
||||
/// a list of hosts that have been revoked by the AACS LA. The AACS specification
|
||||
/// is applicable to PC-based system where a Licensed Drive and PC Host act
|
||||
/// together as the Recording Device and/or Playback Device for AACS Content.
|
||||
/// AACS uses a drive-host authentication protocol for the host to verify the
|
||||
/// integrity of the data received from the Licensed Drive, and for the Licensed
|
||||
/// Drive to check the validity of the host application. The Type and Version
|
||||
/// Record and the Host Revocation List Record are guaranteed to be the first two
|
||||
/// records of a Media Key Block, to make it easier for Licensed Drives to extract
|
||||
/// this data from an arbitrary Media Key Block.
|
||||
/// </summary>
|
||||
/// <see href="https://aacsla.com/wp-content/uploads/2019/02/AACS_Spec_Common_Final_0953.pdf"/>
|
||||
public sealed class HostRevocationListRecord : Record
|
||||
{
|
||||
/// <summary>
|
||||
/// The total number of Host Revocation List Entry fields that follow.
|
||||
/// </summary>
|
||||
public uint TotalNumberOfEntries { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Revocation list entries
|
||||
/// </summary>
|
||||
public HostRevocationSignatureBlock[] SignatureBlocks { get; set; } = [];
|
||||
}
|
||||
}
|
||||
17
SabreTools.Data.Models/AACS/HostRevocationSignatureBlock.cs
Normal file
17
SabreTools.Data.Models/AACS/HostRevocationSignatureBlock.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <see href="https://aacsla.com/wp-content/uploads/2019/02/AACS_Spec_Common_Final_0953.pdf"/>
|
||||
public sealed class HostRevocationSignatureBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of Host Revocation List Entry fields in the signature block.
|
||||
/// </summary>
|
||||
public uint NumberOfEntries { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of 8-byte Host Revocation List Entry fields, the length of this
|
||||
/// list being equal to the number in the signature block.
|
||||
/// </summary>
|
||||
public HostRevocationListEntry[] EntryFields { get; set; } = [];
|
||||
}
|
||||
}
|
||||
14
SabreTools.Data.Models/AACS/MediaKeyBlock.cs
Normal file
14
SabreTools.Data.Models/AACS/MediaKeyBlock.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <summary>
|
||||
/// A Media Key Block is formatted as a sequence of contiguous Records.
|
||||
/// </summary>
|
||||
/// <see href="https://aacsla.com/wp-content/uploads/2019/02/AACS_Spec_Common_Final_0953.pdf"/>
|
||||
public sealed class MediaKeyBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// Records
|
||||
/// </summary>
|
||||
public Record[] Records { get; set; } = [];
|
||||
}
|
||||
}
|
||||
18
SabreTools.Data.Models/AACS/MediaKeyDataRecord.cs
Normal file
18
SabreTools.Data.Models/AACS/MediaKeyDataRecord.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <summary>
|
||||
/// This record gives the associated encrypted media key data for the
|
||||
/// subset-differences identified in the Explicit Subset-Difference Record.
|
||||
/// </summary>
|
||||
/// <see href="https://aacsla.com/wp-content/uploads/2019/02/AACS_Spec_Common_Final_0953.pdf"/>
|
||||
public sealed class MediaKeyDataRecord : Record
|
||||
{
|
||||
/// <summary>
|
||||
/// Each subset difference has its associated 16 bytes in this
|
||||
/// record, in the same order it is encountered in the subset-difference
|
||||
/// record. This 16 bytes is the ciphertext value C in the media
|
||||
/// key calculation.
|
||||
/// </summary>
|
||||
public byte[][] MediaKeyData { get; set; } = [];
|
||||
}
|
||||
}
|
||||
30
SabreTools.Data.Models/AACS/Record.cs
Normal file
30
SabreTools.Data.Models/AACS/Record.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using SabreTools.Numerics;
|
||||
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <summary>
|
||||
/// Each Record begins with a one-byte Record Type field, followed by a
|
||||
/// three-byte Record Length field.
|
||||
///
|
||||
/// The following subsections describe the currently defined Record types,
|
||||
/// and how a device processes each. All multi-byte integers, including
|
||||
/// the length field, are “Big Endian”; in other words, the most significant
|
||||
/// byte comes first in the record.
|
||||
/// </summary>
|
||||
/// <see href="https://aacsla.com/wp-content/uploads/2019/02/AACS_Spec_Common_Final_0953.pdf"/>
|
||||
public abstract class Record
|
||||
{
|
||||
/// <summary>
|
||||
/// The Record Type field value indicates the type of the Record.
|
||||
/// </summary>
|
||||
public RecordType RecordType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Record Length field value indicates the number of bytes in
|
||||
/// the Record, including the Record Type and the Record Length
|
||||
/// fields themselves. Record lengths are always multiples of 4 bytes.
|
||||
/// </summary>
|
||||
// <remarks>UInt24 not UInt32</remarks>
|
||||
public UInt24 RecordLength { get; set; } = new();
|
||||
}
|
||||
}
|
||||
23
SabreTools.Data.Models/AACS/SubsetDifference.cs
Normal file
23
SabreTools.Data.Models/AACS/SubsetDifference.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <see href="https://aacsla.com/wp-content/uploads/2019/02/AACS_Spec_Common_Final_0953.pdf"/>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public sealed class SubsetDifference
|
||||
{
|
||||
/// <summary>
|
||||
/// The mask for u is given by the first byte. That byte is
|
||||
/// treated as a number, the number of low-order 0-bits in
|
||||
/// the mask. For example, the value 0x01 denotes a mask of
|
||||
/// 0xFFFFFFFE; value 0x0A denotes a mask of 0xFFFFFC00.
|
||||
/// </summary>
|
||||
public byte Mask;
|
||||
|
||||
/// <summary>
|
||||
/// The last 4 bytes are the uv number, most significant
|
||||
/// byte first.
|
||||
/// </summary>
|
||||
public uint Number;
|
||||
}
|
||||
}
|
||||
26
SabreTools.Data.Models/AACS/SubsetDifferenceIndexRecord.cs
Normal file
26
SabreTools.Data.Models/AACS/SubsetDifferenceIndexRecord.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a speed-up record which can be ignored by devices not wishing to
|
||||
/// take advantage of it. It is a lookup table which allows devices to quickly
|
||||
/// find their subset-difference in the Explicit Subset-Difference record,
|
||||
/// without processing the entire record. This Subset-Difference Index record
|
||||
/// is always present, and always precedes the Explicit Subset-Difference record
|
||||
/// in the MKB, although it does not necessarily immediately precede it.
|
||||
/// </summary>
|
||||
/// <see href="https://aacsla.com/wp-content/uploads/2019/02/AACS_Spec_Common_Final_0953.pdf"/>
|
||||
public sealed class SubsetDifferenceIndexRecord : Record
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of devices per index offset.
|
||||
/// </summary>
|
||||
public uint Span { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// These offsets refer to the offset within the following Explicit
|
||||
/// Subset-Difference record, with 0 being the start of the record.
|
||||
/// </summary>
|
||||
// <remarks>UInt24 not UInt32</remarks>
|
||||
public uint[] Offsets { get; set; } = [];
|
||||
}
|
||||
}
|
||||
32
SabreTools.Data.Models/AACS/TypeAndVersionRecord.cs
Normal file
32
SabreTools.Data.Models/AACS/TypeAndVersionRecord.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <summary>
|
||||
/// Devices, except for recording devices which are writing Media Key Block
|
||||
/// Extensions, may ignore this record. Recording devices shall verify the
|
||||
/// signature (see End of Media Key Block record) and use the Version Number
|
||||
/// in this record to determine if a new Media Key BLock Extension is, in
|
||||
/// fact, more recent than the Media Key Block Extension that is currently
|
||||
/// on the media.
|
||||
/// </summary>
|
||||
/// <see href="https://aacsla.com/wp-content/uploads/2019/02/AACS_Spec_Common_Final_0953.pdf"/>
|
||||
public sealed class TypeAndVersionRecord : Record
|
||||
{
|
||||
/// <summary>
|
||||
/// For AACS applications, the MKBType field is one of three values.
|
||||
/// It is not an error for a Type 3 Media Key Block to be used for
|
||||
/// controlling access to AACS Content on pre- recorded media. In
|
||||
/// this case, the device shall not use the KCD.
|
||||
/// </summary>
|
||||
public MediaKeyBlockType MediaKeyBlockType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Version Number is a 32-bit unsigned integer. Each time the
|
||||
/// licensing agency changes the revocation, it increments the version
|
||||
/// number and inserts the new value in subsequent Media Key Blocks.
|
||||
/// Thus, larger values indicate more recent Media Key Blocks. The
|
||||
/// Version Numbers begin at 1; 0 is a special value used for test
|
||||
/// Media Key Blocks.
|
||||
/// </summary>
|
||||
public uint VersionNumber { get; set; }
|
||||
}
|
||||
}
|
||||
25
SabreTools.Data.Models/AACS/VerifyMediaKeyRecord.cs
Normal file
25
SabreTools.Data.Models/AACS/VerifyMediaKeyRecord.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <summary>
|
||||
/// A properly formatted MKB shall have exactly one Verify Media Key Record
|
||||
/// as its first record. The presence of the Verify Media Key Record in an MKB
|
||||
/// is mandatory, but the use of the Record by a device is not mandatory. The
|
||||
/// device may use the Verify Media Key Record to verify the correctness of a
|
||||
/// given MKB, or of its processing of it. If everything is correct, the device
|
||||
/// should observe the condition:
|
||||
/// [AES_128D(vKm, C]msb_64 == 0x0123456789ABCDEF)]
|
||||
/// where Km is the Media Key value.
|
||||
/// </summary>
|
||||
/// <see href="https://aacsla.com/wp-content/uploads/2019/02/AACS_Spec_Common_Final_0953.pdf"/>
|
||||
public sealed class VerifyMediaKeyRecord : Record
|
||||
{
|
||||
/// <summary>
|
||||
/// Bytes 4 through 19 of the Record contain the ciphertext value
|
||||
/// Cv = AES-128E (Km, 0x0123456789ABCDEF || 0xXXXXXXXXXXXXXXXX)
|
||||
/// where 0xXXXXXXXXXXXXXXXX is an arbitrary 8-byte value, and Km is
|
||||
/// the correct final Media Key value.
|
||||
/// </summary>
|
||||
/// <remarks>0x10 bytes</remarks>
|
||||
public byte[] CiphertextValue { get; set; } = new byte[0x10];
|
||||
}
|
||||
}
|
||||
59
SabreTools.Data.Models/ASN1/ASN1Type.cs
Normal file
59
SabreTools.Data.Models/ASN1/ASN1Type.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
|
||||
#pragma warning disable CA1069 // Enums values should not be duplicated
|
||||
namespace SabreTools.Data.Models.ASN1
|
||||
{
|
||||
/// <summary>
|
||||
/// ASN.1 type indicators
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ASN1Type : byte
|
||||
{
|
||||
#region Types
|
||||
|
||||
V_ASN1_EOC = 0x00,
|
||||
V_ASN1_BOOLEAN = 0x01,
|
||||
V_ASN1_INTEGER = 0x02,
|
||||
V_ASN1_BIT_STRING = 0x03,
|
||||
V_ASN1_OCTET_STRING = 0x04,
|
||||
V_ASN1_NULL = 0x05,
|
||||
V_ASN1_OBJECT = 0x06,
|
||||
V_ASN1_OBJECT_DESCRIPTOR = 0x07,
|
||||
V_ASN1_EXTERNAL = 0x08,
|
||||
V_ASN1_REAL = 0x09,
|
||||
V_ASN1_ENUMERATED = 0x0A,
|
||||
V_ASN1_UTF8STRING = 0x0C,
|
||||
V_ASN1_SEQUENCE = 0x10,
|
||||
V_ASN1_SET = 0x11,
|
||||
V_ASN1_NUMERICSTRING = 0x12,
|
||||
V_ASN1_PRINTABLESTRING = 0x13,
|
||||
V_ASN1_T61STRING = 0x14,
|
||||
V_ASN1_TELETEXSTRING = 0x14,
|
||||
V_ASN1_VIDEOTEXSTRING = 0x15,
|
||||
V_ASN1_IA5STRING = 0x16,
|
||||
V_ASN1_UTCTIME = 0x17,
|
||||
V_ASN1_GENERALIZEDTIME = 0x18,
|
||||
V_ASN1_GRAPHICSTRING = 0x19,
|
||||
V_ASN1_ISO64STRING = 0x1A,
|
||||
V_ASN1_VISIBLESTRING = 0x1A,
|
||||
V_ASN1_GENERALSTRING = 0x1B,
|
||||
V_ASN1_UNIVERSALSTRING = 0x1C,
|
||||
V_ASN1_BMPSTRING = 0x1E,
|
||||
|
||||
#endregion
|
||||
|
||||
#region Modifiers
|
||||
|
||||
// Commented out because it is the default
|
||||
// and can interfere with V_ASN1_EOC
|
||||
// V_ASN1_UNIVERSAL = 0x00,
|
||||
|
||||
V_ASN1_PRIMITIVE_TAG = 0x1F,
|
||||
V_ASN1_CONSTRUCTED = 0x20,
|
||||
V_ASN1_APPLICATION = 0x40,
|
||||
V_ASN1_CONTEXT_SPECIFIC = 0x80,
|
||||
V_ASN1_PRIVATE = 0xC0,
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
23
SabreTools.Data.Models/ASN1/TypeLengthValue.cs
Normal file
23
SabreTools.Data.Models/ASN1/TypeLengthValue.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace SabreTools.Data.Models.ASN1
|
||||
{
|
||||
/// <summary>
|
||||
/// ASN.1 type/length/value class that all types are based on
|
||||
/// </summary>
|
||||
public class TypeLengthValue
|
||||
{
|
||||
/// <summary>
|
||||
/// The ASN.1 type
|
||||
/// </summary>
|
||||
public ASN1Type Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length of the value
|
||||
/// </summary>
|
||||
public ulong Length { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Generic value associated with <see cref="Type"/>
|
||||
/// </summary>
|
||||
public object? Value { get; set; }
|
||||
}
|
||||
}
|
||||
9
SabreTools.Data.Models/AdvancedInstaller/Constants.cs
Normal file
9
SabreTools.Data.Models/AdvancedInstaller/Constants.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace SabreTools.Data.Models.AdvancedInstaller
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
public static readonly byte[] SignatureBytes = [0x41, 0x44, 0x56, 0x49, 0x4E, 0x53, 0x54, 0x53, 0x46, 0x58];
|
||||
|
||||
public const string SignatureString = "ADVINSTSFX";
|
||||
}
|
||||
}
|
||||
61
SabreTools.Data.Models/AdvancedInstaller/FileEntry.cs
Normal file
61
SabreTools.Data.Models/AdvancedInstaller/FileEntry.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
namespace SabreTools.Data.Models.AdvancedInstaller
|
||||
{
|
||||
/// <summary>
|
||||
/// Single entry in the file table
|
||||
/// </summary>
|
||||
public class FileEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Unknown
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Observed values:
|
||||
/// - 00 00 00 00 (INI)
|
||||
/// - 01 00 00 00 (MSI, CAB)
|
||||
/// - 05 00 00 00 (DLL)
|
||||
/// </remarks>
|
||||
public uint Unknown0 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unknown
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Observed values:
|
||||
/// - 00 00 00 00 (MSI)
|
||||
/// - 01 00 00 00 (CAB)
|
||||
/// - 03 00 00 00 (INI)
|
||||
/// - 0C 00 00 00 (DLL)
|
||||
/// </remarks>
|
||||
public uint? Unknown1 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unknown, always 0?
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Observed values:
|
||||
/// - 00 00 00 00 (DLL, MSI, CAB, INI)
|
||||
/// </remarks>
|
||||
public uint? Unknown2 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Size of the file
|
||||
/// </summary>
|
||||
public uint FileSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Offset of the file relative to the start
|
||||
/// of the SFX stub
|
||||
/// </summary>
|
||||
public uint FileOffset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Size of the file name in characters
|
||||
/// </summary>
|
||||
public uint NameSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unicode-encoded file name
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
109
SabreTools.Data.Models/AdvancedInstaller/Footer.cs
Normal file
109
SabreTools.Data.Models/AdvancedInstaller/Footer.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
namespace SabreTools.Data.Models.AdvancedInstaller
|
||||
{
|
||||
/// <summary>
|
||||
/// Structure similar to the end of central directory
|
||||
/// header in PKZIP files
|
||||
/// </summary>
|
||||
public class Footer
|
||||
{
|
||||
/// <summary>
|
||||
/// Unknown
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Observed values:
|
||||
/// - 00 00 00 00
|
||||
/// </remarks>
|
||||
public uint Unknown0 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Size of the original filename?
|
||||
/// </summary>
|
||||
/// <remarks>Doesn't exist in some cases?</remarks>
|
||||
public uint? OriginalFilenameSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unicode-encoded original filename?
|
||||
/// </summary>
|
||||
/// <remarks>Doesn't exist in some cases?</remarks>
|
||||
public string? OriginalFilename { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unknown, possibly a string count?
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only seen when the preceeding two fields exist
|
||||
///
|
||||
/// Observed values:
|
||||
/// - 01 00 00 00
|
||||
/// </remarks>
|
||||
public uint? Unknown1 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Pointer to <see cref="Unknown0"/>?
|
||||
/// </summary>
|
||||
public uint FooterOffset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of entries that preceed the footer
|
||||
/// </summary>
|
||||
public uint EntryCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unknown
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Observed values:
|
||||
/// - 64 00 00 00
|
||||
/// </remarks>
|
||||
public uint Unknown2 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Unknown offset
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Points to <see cref="Unknown0"/> if no original filename.
|
||||
/// Points to <see cref="EntryCount"/> if contains an original filename.
|
||||
/// </remarks>
|
||||
public uint UnknownOffset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Offset of the start of the file table
|
||||
/// </summary>
|
||||
public uint TablePointer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Offset to the start of the file data
|
||||
/// </summary>
|
||||
public uint FileDataStart { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Hex string that looks like a key or other identifier
|
||||
/// </summary>
|
||||
/// <remarks>32 bytes</remarks>
|
||||
public string HexString { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Unknown
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Offset pointer to <see cref="FileDataStart"/>
|
||||
/// relative to the end of the signature if no filename
|
||||
/// exists.
|
||||
///
|
||||
/// Observed values:
|
||||
/// - 32 00 00 00 (No original filename)
|
||||
/// - 13 02 00 00 (Original filename)
|
||||
/// </remarks>
|
||||
public uint Unknown3 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// "ADVINSTSFX"
|
||||
/// </summary>
|
||||
public string Signature { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Unknown, always 0? Padding?
|
||||
/// </summary>
|
||||
public ushort? Unknown4 { get; set; }
|
||||
}
|
||||
}
|
||||
29
SabreTools.Data.Models/AdvancedInstaller/SFX.cs
Normal file
29
SabreTools.Data.Models/AdvancedInstaller/SFX.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
namespace SabreTools.Data.Models.AdvancedInstaller
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the structure at the end of a Caphyon
|
||||
/// Advanced Installer SFX file. These SFX files store
|
||||
/// all files uncompressed sequentially in the overlay
|
||||
/// of an executable.
|
||||
///
|
||||
/// The design is similar to the end of central directory
|
||||
/// in a PKZIP file. The footer needs to be read before
|
||||
/// the entry table as both the pointer to the start of
|
||||
/// the table as well as the entry count are included there.
|
||||
///
|
||||
/// The layout of this is derived from the layout in the
|
||||
/// physical file.
|
||||
/// </summary>
|
||||
public class SFX
|
||||
{
|
||||
/// <summary>
|
||||
/// Set of file entries
|
||||
/// </summary>
|
||||
public FileEntry[] Entries { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Footer representing the central directory
|
||||
/// </summary>
|
||||
public Footer Footer { get; set; } = new();
|
||||
}
|
||||
}
|
||||
206
SabreTools.Data.Models/ArchiveDotOrg/File.cs
Normal file
206
SabreTools.Data.Models/ArchiveDotOrg/File.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
namespace SabreTools.Data.Models.ArchiveDotOrg
|
||||
{
|
||||
[XmlRoot("file")]
|
||||
public class File
|
||||
{
|
||||
[XmlAttribute("name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <remarks>Is this a set of defined values?</remarks>
|
||||
[XmlAttribute("source")]
|
||||
public string? Source { get; set; }
|
||||
|
||||
[XmlElement("btih")]
|
||||
public string? BitTorrentMagnetHash { get; set; }
|
||||
|
||||
[XmlElement("mtime")]
|
||||
public string? LastModifiedTime { get; set; }
|
||||
|
||||
[XmlElement("size")]
|
||||
public string? Size { get; set; }
|
||||
|
||||
[XmlElement("md5")]
|
||||
public string? MD5 { get; set; }
|
||||
|
||||
[XmlElement("crc32")]
|
||||
public string? CRC32 { get; set; }
|
||||
|
||||
[XmlElement("sha1")]
|
||||
public string? SHA1 { get; set; }
|
||||
|
||||
[XmlElement("filecount")]
|
||||
public long? FileCount { get; set; }
|
||||
|
||||
/// <remarks>Is this a set of defined values?</remarks>
|
||||
[XmlElement("format")]
|
||||
public string? Format { get; set; }
|
||||
|
||||
[XmlElement("original")]
|
||||
public string? Original { get; set; }
|
||||
|
||||
/// <remarks>Is this a set of defined values?</remarks>
|
||||
[XmlElement("summation")]
|
||||
public string? Summation { get; set; }
|
||||
|
||||
[XmlElement("matrix_number")]
|
||||
public string? MatrixNumber { get; set; }
|
||||
|
||||
[XmlElement("collection-catalog-number")]
|
||||
public string? CollectionCatalogNumber { get; set; }
|
||||
|
||||
[XmlElement("publisher")]
|
||||
public string? Publisher { get; set; }
|
||||
|
||||
[XmlElement("comment")]
|
||||
public string? Comment { get; set; }
|
||||
|
||||
#region ASR-Related
|
||||
|
||||
[XmlElement("asr_detected_lang")]
|
||||
public string? ASRDetectedLang { get; set; }
|
||||
|
||||
[XmlElement("asr_detected_lang_conf")]
|
||||
public string? ASRDetectedLangConf { get; set; }
|
||||
|
||||
[XmlElement("asr_transcribed_lang")]
|
||||
public string? ASRTranscribedLang { get; set; }
|
||||
|
||||
[XmlElement("whisper_asr_module_version")]
|
||||
public string? WhisperASRModuleVersion { get; set; }
|
||||
|
||||
[XmlElement("whisper_model_hash")]
|
||||
public string? WhisperModelHash { get; set; }
|
||||
|
||||
[XmlElement("whisper_model_name")]
|
||||
public string? WhisperModelName { get; set; }
|
||||
|
||||
[XmlElement("whisper_version")]
|
||||
public string? WhisperVersion { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region OCR-Related
|
||||
|
||||
[XmlElement("cloth_cover_detection_module_version")]
|
||||
public string? ClothCoverDetectionModuleVersion { get; set; }
|
||||
|
||||
[XmlElement("hocr_char_to_word_hocr_version")]
|
||||
public string? hOCRCharToWordhOCRVersion { get; set; }
|
||||
|
||||
[XmlElement("hocr_char_to_word_module_version")]
|
||||
public string? hOCRCharToWordModuleVersion { get; set; }
|
||||
|
||||
[XmlElement("hocr_fts_text_hocr_version")]
|
||||
public string? hOCRFtsTexthOCRVersion { get; set; }
|
||||
|
||||
[XmlElement("hocr_fts_text_module_version")]
|
||||
public string? hOCRFtsTextModuleVersion { get; set; }
|
||||
|
||||
[XmlElement("hocr_pageindex_hocr_version")]
|
||||
public string? hOCRPageIndexhOCRVersion { get; set; }
|
||||
|
||||
[XmlElement("hocr_pageindex_module_version")]
|
||||
public string? hOCRPageIndexModuleVersion { get; set; }
|
||||
|
||||
[XmlElement("ocr")]
|
||||
public string? TesseractOCR { get; set; }
|
||||
|
||||
[XmlElement("ocr_converted")]
|
||||
public string? TesseractOCRConverted { get; set; }
|
||||
|
||||
[XmlElement("ocr_detected_lang")]
|
||||
public string? TesseractOCRDetectedLang { get; set; }
|
||||
|
||||
[XmlElement("ocr_detected_lang_conf")]
|
||||
public string? TesseractOCRDetectedLangConf { get; set; }
|
||||
|
||||
[XmlElement("ocr_detected_script")]
|
||||
public string? TesseractOCRDetectedScript { get; set; }
|
||||
|
||||
[XmlElement("ocr_detected_script_conf")]
|
||||
public string? TesseractOCRDetectedScriptConf { get; set; }
|
||||
|
||||
[XmlElement("ocr_module_version")]
|
||||
public string? TesseractOCRModuleVersion { get; set; }
|
||||
|
||||
[XmlElement("ocr_parameters")]
|
||||
public string? TesseractOCRParameters { get; set; }
|
||||
|
||||
[XmlElement("pdf_module_version")]
|
||||
public string? PDFModuleVersion { get; set; }
|
||||
|
||||
[XmlElement("word_conf_0_10")]
|
||||
public string? WordConfidenceInterval0To10 { get; set; }
|
||||
|
||||
[XmlElement("word_conf_11_20")]
|
||||
public string? WordConfidenceInterval11To20 { get; set; }
|
||||
|
||||
[XmlElement("word_conf_21_30")]
|
||||
public string? WordConfidenceInterval21To30 { get; set; }
|
||||
|
||||
[XmlElement("word_conf_31_40")]
|
||||
public string? WordConfidenceInterval31To40 { get; set; }
|
||||
|
||||
[XmlElement("word_conf_41_50")]
|
||||
public string? WordConfidenceInterval41To50 { get; set; }
|
||||
|
||||
[XmlElement("word_conf_51_60")]
|
||||
public string? WordConfidenceInterval51To60 { get; set; }
|
||||
|
||||
[XmlElement("word_conf_61_70")]
|
||||
public string? WordConfidenceInterval61To70 { get; set; }
|
||||
|
||||
[XmlElement("word_conf_71_80")]
|
||||
public string? WordConfidenceInterval71To80 { get; set; }
|
||||
|
||||
[XmlElement("word_conf_81_90")]
|
||||
public string? WordConfidenceInterval81To90 { get; set; }
|
||||
|
||||
[XmlElement("word_conf_91_100")]
|
||||
public string? WordConfidenceInterval91To100 { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Media-Related
|
||||
|
||||
[XmlElement("album")]
|
||||
public string? Album { get; set; }
|
||||
|
||||
[XmlElement("artist")]
|
||||
public string? Artist { get; set; }
|
||||
|
||||
[XmlElement("bitrate")]
|
||||
public string? Bitrate { get; set; }
|
||||
|
||||
[XmlElement("creator")]
|
||||
public string? Creator { get; set; }
|
||||
|
||||
[XmlElement("height")]
|
||||
public string? Height { get; set; }
|
||||
|
||||
[XmlElement("length")]
|
||||
public string? Length { get; set; }
|
||||
|
||||
[XmlElement("preview-image")]
|
||||
public string? PreviewImage { get; set; }
|
||||
|
||||
/// <remarks>Is this a set of defined values?</remarks>
|
||||
[XmlElement("rotation")]
|
||||
public string? Rotation { get; set; }
|
||||
|
||||
[XmlElement("title")]
|
||||
public string? Title { get; set; }
|
||||
|
||||
[XmlElement("track")]
|
||||
public string? Track { get; set; }
|
||||
|
||||
[XmlElement("width")]
|
||||
public string? Width { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
12
SabreTools.Data.Models/ArchiveDotOrg/Files.cs
Normal file
12
SabreTools.Data.Models/ArchiveDotOrg/Files.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace SabreTools.Data.Models.ArchiveDotOrg
|
||||
{
|
||||
[XmlRoot("files")]
|
||||
public class Files
|
||||
{
|
||||
[XmlElement("file")]
|
||||
public File[]? File { get; set; }
|
||||
}
|
||||
}
|
||||
20
SabreTools.Data.Models/Atari7800/Cart.cs
Normal file
20
SabreTools.Data.Models/Atari7800/Cart.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace SabreTools.Data.Models.Atari7800
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari 7800 headered cart
|
||||
/// </summary>
|
||||
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
|
||||
public class Cart
|
||||
{
|
||||
/// <summary>
|
||||
/// A78 header
|
||||
/// </summary>
|
||||
/// <remarks>If omitted, format is technically BIN</remarks>
|
||||
public Header? Header { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cartridge data
|
||||
/// </summary>
|
||||
public byte[] Data { get; set; } = [];
|
||||
}
|
||||
}
|
||||
49
SabreTools.Data.Models/Atari7800/Constants.cs
Normal file
49
SabreTools.Data.Models/Atari7800/Constants.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
namespace SabreTools.Data.Models.Atari7800
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// A78 magic string (null-padded)
|
||||
/// </summary>
|
||||
public static readonly byte[] MagicBytesWithNull =
|
||||
[
|
||||
0x41, 0x54, 0x41, 0x52, 0x49, 0x37, 0x38, 0x30,
|
||||
0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// A78 magic string (null-padded)
|
||||
/// </summary>
|
||||
public const string MagicStringWithNull = "ATARI7800\0\0\0\0\0\0\0";
|
||||
|
||||
/// <summary>
|
||||
/// A78 magic string (space-padded)
|
||||
/// </summary>
|
||||
public static readonly byte[] MagicBytesWithSpace =
|
||||
[
|
||||
0x41, 0x54, 0x41, 0x52, 0x49, 0x37, 0x38, 0x30,
|
||||
0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// A78 magic string (space-padded)
|
||||
/// </summary>
|
||||
public const string MagicStringWithSpace = "ATARI7800 ";
|
||||
|
||||
/// <summary>
|
||||
/// A78 header end magic text
|
||||
/// </summary>
|
||||
public static readonly byte[] HeaderEndMagicBytes =
|
||||
[
|
||||
0x41, 0x43, 0x54, 0x55, 0x41, 0x4C, 0x20, 0x43,
|
||||
0x41, 0x52, 0x54, 0x20, 0x44, 0x41, 0x54, 0x41,
|
||||
0x20, 0x53, 0x54, 0x41, 0x52, 0x54, 0x53, 0x20,
|
||||
0x48, 0x45, 0x52, 0x45,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// A78 header end magic text
|
||||
/// </summary>
|
||||
public const string HeaderEndMagicString = "ACTUAL CART DATA STARTS HERE";
|
||||
}
|
||||
}
|
||||
380
SabreTools.Data.Models/Atari7800/Enums.cs
Normal file
380
SabreTools.Data.Models/Atari7800/Enums.cs
Normal file
@@ -0,0 +1,380 @@
|
||||
using System;
|
||||
|
||||
namespace SabreTools.Data.Models.Atari7800
|
||||
{
|
||||
/// <summary>
|
||||
/// Audio device (v4+ only)
|
||||
/// </summary>
|
||||
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
|
||||
[Flags]
|
||||
public enum AudioDevice : ushort
|
||||
{
|
||||
#region POKEY location
|
||||
|
||||
/// <summary>
|
||||
/// POKEY - none
|
||||
/// </summary>
|
||||
PokeyNone = 0b00000000,
|
||||
|
||||
/// <summary>
|
||||
/// POKEY - @440
|
||||
/// </summary>
|
||||
Pokey440 = 0b00000001,
|
||||
|
||||
/// <summary>
|
||||
/// POKEY - @450
|
||||
/// </summary>
|
||||
Pokey450 = 0b00000010,
|
||||
|
||||
/// <summary>
|
||||
/// POKEY - @450+@440
|
||||
/// </summary>
|
||||
Pokey450Plus440 = 0b00000011,
|
||||
|
||||
/// <summary>
|
||||
/// POKEY - @800
|
||||
/// </summary>
|
||||
Pokey800 = 0b00000100,
|
||||
|
||||
/// <summary>
|
||||
/// POKEY - @4000
|
||||
/// </summary>
|
||||
Pokey4000 = 0b00000101,
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// YM2151 @460
|
||||
/// </summary>
|
||||
YM2151460 = 0b00001000,
|
||||
|
||||
/// <summary>
|
||||
/// COVOX @430
|
||||
/// </summary>
|
||||
COVOX430 = 0b00010000,
|
||||
|
||||
/// <summary>
|
||||
/// ADPCM Audio Stream @420
|
||||
/// </summary>
|
||||
ADPCMAudioStream420 = 0b00100000,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cart type specifications
|
||||
/// </summary>
|
||||
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
|
||||
[Flags]
|
||||
public enum CartType : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// pokey at $4000
|
||||
/// </summary>
|
||||
PokeyAt4000 = 0b00000000_00000001,
|
||||
|
||||
/// <summary>
|
||||
/// supergame bank switched
|
||||
/// </summary>
|
||||
SupergameBankSwitched = 0b00000000_00000010,
|
||||
|
||||
/// <summary>
|
||||
/// supergame ram at $4000
|
||||
/// </summary>
|
||||
SupergameRamAt4000 = 0b00000000_00000100,
|
||||
|
||||
/// <summary>
|
||||
/// rom at $4000
|
||||
/// </summary>
|
||||
RomAt4000 = 0b00000000_00001000,
|
||||
|
||||
/// <summary>
|
||||
/// bank 6 at $4000
|
||||
/// </summary>
|
||||
Bank6At4000 = 0b00000000_00010000,
|
||||
|
||||
/// <summary>
|
||||
/// banked ram
|
||||
/// </summary>
|
||||
BankedRam = 0b00000000_00100000,
|
||||
|
||||
/// <summary>
|
||||
/// pokey at $450
|
||||
/// </summary>
|
||||
PokeyAt450 = 0b00000000_01000000,
|
||||
|
||||
/// <summary>
|
||||
/// mirror ram at $4000
|
||||
/// </summary>
|
||||
MirrorRamAt4000 = 0b00000000_10000000,
|
||||
|
||||
/// <summary>
|
||||
/// activision banking
|
||||
/// </summary>
|
||||
ActivisionBanking = 0b00000001_00000000,
|
||||
|
||||
/// <summary>
|
||||
/// absolute banking
|
||||
/// </summary>
|
||||
AbsoluteBanking = 0b00000010_00000000,
|
||||
|
||||
/// <summary>
|
||||
/// pokey at $440
|
||||
/// </summary>
|
||||
PokeyAt440 = 0b00000100_00000000,
|
||||
|
||||
/// <summary>
|
||||
/// ym2151 at $460/$461
|
||||
/// </summary>
|
||||
Ym2151At460461 = 0b00001000_00000000,
|
||||
|
||||
/// <summary>
|
||||
/// souper
|
||||
/// </summary>
|
||||
Souper = 0b00010000_00000000,
|
||||
|
||||
/// <summary>
|
||||
/// banksets
|
||||
/// </summary>
|
||||
Banksets = 0b00100000_00000000,
|
||||
|
||||
/// <summary>
|
||||
/// halt banked ram
|
||||
/// </summary>
|
||||
HaltBankedRam = 0b01000000_00000000,
|
||||
|
||||
/// <summary>
|
||||
/// pokey@800
|
||||
/// </summary>
|
||||
PokeyAt800 = 0b10000000_00000000,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Controller type
|
||||
/// </summary>
|
||||
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
|
||||
public enum ControllerType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// none
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 7800 joystick
|
||||
/// </summary>
|
||||
Joystick = 1,
|
||||
|
||||
/// <summary>
|
||||
/// lightgun
|
||||
/// </summary>
|
||||
Lightgun = 2,
|
||||
|
||||
/// <summary>
|
||||
/// paddle
|
||||
/// </summary>
|
||||
Paddle = 3,
|
||||
|
||||
/// <summary>
|
||||
/// trakball
|
||||
/// </summary>
|
||||
Trakball = 4,
|
||||
|
||||
/// <summary>
|
||||
/// 2600 joystick
|
||||
/// </summary>
|
||||
VcsJoystick = 5,
|
||||
|
||||
/// <summary>
|
||||
/// 2600 driving
|
||||
/// </summary>
|
||||
VcsDriving = 6,
|
||||
|
||||
/// <summary>
|
||||
/// 2600 keypad
|
||||
/// </summary>
|
||||
VcsKeypad = 7,
|
||||
|
||||
/// <summary>
|
||||
/// ST mouse
|
||||
/// </summary>
|
||||
STMouse = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Amiga mouse
|
||||
/// </summary>
|
||||
AmigaMouse = 9,
|
||||
|
||||
/// <summary>
|
||||
/// AtariVox/SaveKey
|
||||
/// </summary>
|
||||
AtariVoxSaveKey = 10,
|
||||
|
||||
/// <summary>
|
||||
/// SNES2Atari
|
||||
/// </summary>
|
||||
SNES2Atari = 11,
|
||||
|
||||
/// <summary>
|
||||
/// Mega7800
|
||||
/// </summary>
|
||||
Mega7800 = 12,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interrupt (v4+ only)
|
||||
/// </summary>
|
||||
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
|
||||
[Flags]
|
||||
public enum Interrupt : ushort
|
||||
{
|
||||
None = 0b00000000,
|
||||
|
||||
/// <summary>
|
||||
/// POKEY 1 (@450 | @800 | @4000)
|
||||
/// </summary>
|
||||
Pokey1 = 0b00000001,
|
||||
|
||||
/// <summary>
|
||||
/// POKEY 2 (@440)
|
||||
/// </summary>
|
||||
Pokey2 = 0b00000010,
|
||||
|
||||
/// <summary>
|
||||
/// YM2151
|
||||
/// </summary>
|
||||
YM2151 = 0b00000100,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mapper (v4+ only)
|
||||
/// </summary>
|
||||
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
|
||||
public enum Mapper : byte
|
||||
{
|
||||
Linear = 0,
|
||||
SuperGame = 1,
|
||||
Activision = 2,
|
||||
Absolute = 3,
|
||||
Souper = 4,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mapper options/details (v4+ only)
|
||||
/// </summary>
|
||||
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
|
||||
[Flags]
|
||||
public enum MapperOptions : byte
|
||||
{
|
||||
#region Linear
|
||||
|
||||
/// <summary>
|
||||
/// Option at @4000 - none
|
||||
/// </summary>
|
||||
LinearNone = 0b00000000,
|
||||
|
||||
/// <summary>
|
||||
/// Option at @4000 - 16K RAM
|
||||
/// </summary>
|
||||
Linear16KRAM = 0b00000001,
|
||||
|
||||
/// <summary>
|
||||
/// Option at @4000 - 8K EXRAM/A8
|
||||
/// </summary>
|
||||
Linear8KEXRAMA8 = 0b00000010,
|
||||
|
||||
/// <summary>
|
||||
/// Option at @4000 - 32K EXRAM/M2
|
||||
/// </summary>
|
||||
Linear32KEXRAMM2 = 0b00000011,
|
||||
|
||||
#endregion
|
||||
|
||||
#region SuperGame
|
||||
|
||||
/// <summary>
|
||||
/// Option at @4000 - none
|
||||
/// </summary>
|
||||
SuperGameNone = 0b00000000,
|
||||
|
||||
/// <summary>
|
||||
/// Option at @4000 - 16K RAM
|
||||
/// </summary>
|
||||
SuperGame16KRAM = 0b00000001,
|
||||
|
||||
/// <summary>
|
||||
/// Option at @4000 - 8K EXRAM/A8
|
||||
/// </summary>
|
||||
SuperGame8KEXRAMA8 = 0b00000010,
|
||||
|
||||
/// <summary>
|
||||
/// Option at @4000 - 32K EXRAM/M2
|
||||
/// </summary>
|
||||
SuperGame32KEXRAMM2 = 0b00000011,
|
||||
|
||||
/// <summary>
|
||||
/// Option at @4000 - EXROM
|
||||
/// </summary>
|
||||
SuperGameEXROM = 0b00000100,
|
||||
|
||||
/// <summary>
|
||||
/// Option at @4000 - EXFIX
|
||||
/// </summary>
|
||||
SuperGameEXFIX = 0b00000101,
|
||||
|
||||
/// <summary>
|
||||
/// Option at @4000 - 32k EXRAM/X2
|
||||
/// </summary>
|
||||
SuperGame32KEXRAMX2 = 0b00000110,
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Bankset ROM
|
||||
/// </summary>
|
||||
BanksetRom = 0b10000000,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save device
|
||||
/// </summary>
|
||||
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
|
||||
public enum SaveDevice : byte
|
||||
{
|
||||
None = 0b00000000,
|
||||
|
||||
/// <summary>
|
||||
/// HSC
|
||||
/// </summary>
|
||||
HSC = 0b00000001,
|
||||
|
||||
/// <summary>
|
||||
/// SaveKey/AtariVox
|
||||
/// </summary>
|
||||
SaveKeyAtariVox = 0b00000010,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slot passthrough device
|
||||
/// </summary>
|
||||
public enum SlotPassthroughDevice : byte
|
||||
{
|
||||
None = 0b00000000,
|
||||
XM = 0b00000001,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TV type
|
||||
/// </summary>
|
||||
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
|
||||
[Flags]
|
||||
public enum TVType : byte
|
||||
{
|
||||
NTSC = 0b00000000,
|
||||
PAL = 0b00000001,
|
||||
|
||||
Component = 0b00000000,
|
||||
Composite = 0b00000010,
|
||||
|
||||
SingleRegion = 0b00000000,
|
||||
MultiRegion = 0b00000100,
|
||||
}
|
||||
}
|
||||
115
SabreTools.Data.Models/Atari7800/Header.cs
Normal file
115
SabreTools.Data.Models/Atari7800/Header.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
namespace SabreTools.Data.Models.Atari7800
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari 7800 emulator header
|
||||
/// </summary>
|
||||
/// <see href="https://7800.8bitdev.org/index.php/A78_Header_Specification"/>
|
||||
public class Header
|
||||
{
|
||||
/// <summary>
|
||||
/// Header version
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Current header version is 4.
|
||||
/// If no V4 features are used, this should be set to 3.
|
||||
/// </remarks>
|
||||
public byte HeaderVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// "ATARI7800" with 7 null bytes following
|
||||
/// </summary>
|
||||
public byte[] MagicText { get; set; } = new byte[16];
|
||||
|
||||
/// <summary>
|
||||
/// Cart title, encoding not specified
|
||||
/// </summary>
|
||||
public byte[] CartTitle { get; set; } = new byte[32];
|
||||
|
||||
/// <summary>
|
||||
/// ROM size without header data
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Endianness possibly varies by version?
|
||||
/// Version 1 - Big-endian
|
||||
/// Version 2 - ?????
|
||||
/// Version 3 - Documented as little-endian (unconfirmed)
|
||||
/// Version 4 - Documented as little-endian (unconfirmed)
|
||||
/// </remarks>
|
||||
public uint RomSizeWithoutHeader { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cart type flags
|
||||
/// </summary>
|
||||
public CartType CartType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controller 1 type
|
||||
/// </summary>
|
||||
public ControllerType Controller1Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controller 2 type
|
||||
/// </summary>
|
||||
public ControllerType Controller2Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// TV type
|
||||
/// </summary>
|
||||
public TVType TVType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Save device
|
||||
/// </summary>
|
||||
public SaveDevice SaveDevice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reserved
|
||||
/// </summary>
|
||||
public byte[] Reserved { get; set; } = new byte[4];
|
||||
|
||||
/// <summary>
|
||||
/// Slot passthrough device
|
||||
/// </summary>
|
||||
public SlotPassthroughDevice SlotPassthroughDevice { get; set; }
|
||||
|
||||
#region Version 4+
|
||||
|
||||
/// <summary>
|
||||
/// Mapper
|
||||
/// </summary>
|
||||
/// <remarks>v4+ only</remarks>
|
||||
public Mapper Mapper { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Mapper options
|
||||
/// </summary>
|
||||
/// <remarks>v4+ only</remarks>
|
||||
public MapperOptions MapperOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Audio device(s)
|
||||
/// </summary>
|
||||
public AudioDevice AudioDevice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Interrupt
|
||||
/// </summary>
|
||||
public Interrupt Interrupt { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Padding from end of header data to end magic text
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 36 bytes in versions lower than 4.
|
||||
/// 30 bytes in versions 4 and above.
|
||||
/// </remarks>
|
||||
public byte[] Padding { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// "ACTUAL CART DATA STARTS HERE"
|
||||
/// </summary>
|
||||
public byte[] EndMagicText { get; set; } = new byte[28];
|
||||
}
|
||||
}
|
||||
20
SabreTools.Data.Models/AtariLynx/Cart.cs
Normal file
20
SabreTools.Data.Models/AtariLynx/Cart.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace SabreTools.Data.Models.AtariLynx
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari Lynx headered cart (LNX)
|
||||
/// </summary>
|
||||
/// <see href="https://github.com/mozzwald/handy-sdl/blob/master/src/handy-0.95/cart.h"/>
|
||||
public class Cart
|
||||
{
|
||||
/// <summary>
|
||||
/// LNX header
|
||||
/// </summary>
|
||||
/// <remarks>If omitted, format is technically LYX</remarks>
|
||||
public Header? Header { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cartridge data
|
||||
/// </summary>
|
||||
public byte[] Data { get; set; } = [];
|
||||
}
|
||||
}
|
||||
15
SabreTools.Data.Models/AtariLynx/Constants.cs
Normal file
15
SabreTools.Data.Models/AtariLynx/Constants.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace SabreTools.Data.Models.AtariLynx
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// LNX magic string
|
||||
/// </summary>
|
||||
public static readonly byte[] MagicBytes = [0x4C, 0x59, 0x4E, 0x58];
|
||||
|
||||
/// <summary>
|
||||
/// LNX magic string
|
||||
/// </summary>
|
||||
public const string MagicString = "LYNX";
|
||||
}
|
||||
}
|
||||
24
SabreTools.Data.Models/AtariLynx/Enums.cs
Normal file
24
SabreTools.Data.Models/AtariLynx/Enums.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace SabreTools.Data.Models.AtariLynx
|
||||
{
|
||||
/// <summary>
|
||||
/// Screen rotation
|
||||
/// </summary>
|
||||
/// <see href="https://github.com/mozzwald/handy-sdl/blob/master/src/handy-0.95/cart.h"/>
|
||||
public enum Rotation : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// No rotation (horizontal, buttons right)
|
||||
/// </summary>
|
||||
NoRotation = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Rotate left (vertical, buttons down)
|
||||
/// </summary>
|
||||
RotateLeft = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Rotate right (vertical, buttons up)
|
||||
/// </summary>
|
||||
RotateRight = 2,
|
||||
}
|
||||
}
|
||||
54
SabreTools.Data.Models/AtariLynx/Header.cs
Normal file
54
SabreTools.Data.Models/AtariLynx/Header.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
namespace SabreTools.Data.Models.AtariLynx
|
||||
{
|
||||
/// <summary>
|
||||
/// Atari Lynx emulator header
|
||||
/// </summary>
|
||||
/// <see href="https://github.com/mozzwald/handy-sdl/blob/master/src/handy-0.95/cart.h"/>
|
||||
public class Header
|
||||
{
|
||||
/// <summary>
|
||||
/// "LYNX"
|
||||
/// </summary>
|
||||
/// <remarks>4 bytes</remarks>
|
||||
public byte[] Magic { get; set; } = new byte[4];
|
||||
|
||||
/// <summary>
|
||||
/// Page size for bank 0
|
||||
/// </summary>
|
||||
public ushort Bank0PageSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Page size for bank 1
|
||||
/// </summary>
|
||||
public ushort Bank1PageSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Header version
|
||||
/// </summary>
|
||||
/// <remarks>Must be 0x0001</remarks>
|
||||
public ushort Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Game title (Null-padded ASCII)
|
||||
/// </summary>
|
||||
/// <remarks>32 bytes</remarks>
|
||||
public byte[] CartName { get; set; } = new byte[32];
|
||||
|
||||
/// <summary>
|
||||
/// Publisher name
|
||||
/// </summary>
|
||||
/// <remarks>16 bytes</remarks>
|
||||
public byte[] Manufacturer { get; set; } = new byte[16];
|
||||
|
||||
/// <summary>
|
||||
/// Screen rotation
|
||||
/// </summary>
|
||||
public Rotation Rotation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Padding
|
||||
/// </summary>
|
||||
/// <remarks>5 bytes</remarks>
|
||||
public byte[] Spare { get; set; } = new byte[5];
|
||||
}
|
||||
}
|
||||
13
SabreTools.Data.Models/AttractMode/MetadataFile.cs
Normal file
13
SabreTools.Data.Models/AttractMode/MetadataFile.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace SabreTools.Data.Models.AttractMode
|
||||
{
|
||||
/// <summary>
|
||||
/// #Name;Title;Emulator;CloneOf;Year;Manufacturer;Category;Players;Rotation;Control;Status;DisplayCount;DisplayType;AltRomname;AltTitle;Extra;Buttons /// </summary>
|
||||
/// </summary>
|
||||
public class MetadataFile
|
||||
{
|
||||
[Required]
|
||||
public string[]? Header { get; set; }
|
||||
|
||||
public Row[]? Row { get; set; }
|
||||
}
|
||||
}
|
||||
51
SabreTools.Data.Models/AttractMode/Row.cs
Normal file
51
SabreTools.Data.Models/AttractMode/Row.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
namespace SabreTools.Data.Models.AttractMode
|
||||
{
|
||||
public class Row
|
||||
{
|
||||
/// <remarks>Also called Romname</remarks>
|
||||
[Required]
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string? Title { get; set; }
|
||||
|
||||
public string? Emulator { get; set; }
|
||||
|
||||
public string? CloneOf { get; set; }
|
||||
|
||||
public string? Year { get; set; }
|
||||
|
||||
public string? Manufacturer { get; set; }
|
||||
|
||||
public string? Category { get; set; }
|
||||
|
||||
public string? Players { get; set; }
|
||||
|
||||
public string? Rotation { get; set; }
|
||||
|
||||
public string? Control { get; set; }
|
||||
|
||||
public string? Status { get; set; }
|
||||
|
||||
public string? DisplayCount { get; set; }
|
||||
|
||||
public string? DisplayType { get; set; }
|
||||
|
||||
public string? AltRomname { get; set; }
|
||||
|
||||
public string? AltTitle { get; set; }
|
||||
|
||||
public string? Extra { get; set; }
|
||||
|
||||
public string? Buttons { get; set; }
|
||||
|
||||
public string? Favorite { get; set; }
|
||||
|
||||
public string? Tags { get; set; }
|
||||
|
||||
public string? PlayedCount { get; set; }
|
||||
|
||||
public string? PlayedTime { get; set; }
|
||||
|
||||
public bool? FileIsAvailable { get; set; }
|
||||
}
|
||||
}
|
||||
9
SabreTools.Data.Models/BDPlus/Constants.cs
Normal file
9
SabreTools.Data.Models/BDPlus/Constants.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace SabreTools.Data.Models.BDPlus
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
public static readonly byte[] SignatureBytes = [0x42, 0x44, 0x53, 0x56, 0x4D, 0x5F, 0x43, 0x43];
|
||||
|
||||
public const string SignatureString = "BDSVM_CC";
|
||||
}
|
||||
}
|
||||
50
SabreTools.Data.Models/BDPlus/SVM.cs
Normal file
50
SabreTools.Data.Models/BDPlus/SVM.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SabreTools.Data.Models.BDPlus
|
||||
{
|
||||
/// <see href="https://github.com/mwgoldsmith/bdplus/blob/master/src/libbdplus/bdsvm/loader.c"/>
|
||||
public sealed class SVM
|
||||
{
|
||||
/// <summary>
|
||||
/// "BDSVM_CC"
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
|
||||
public string Signature = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Unknown data
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
|
||||
public byte[] Unknown1 = new byte[5];
|
||||
|
||||
/// <summary>
|
||||
/// Version year
|
||||
/// </summary>
|
||||
public ushort Year;
|
||||
|
||||
/// <summary>
|
||||
/// Version month
|
||||
/// </summary>
|
||||
public byte Month;
|
||||
|
||||
/// <summary>
|
||||
/// Version day
|
||||
/// </summary>
|
||||
public byte Day;
|
||||
|
||||
/// <summary>
|
||||
/// Unknown data
|
||||
/// </summary>
|
||||
public uint Unknown2;
|
||||
|
||||
/// <summary>
|
||||
/// Length
|
||||
/// </summary>
|
||||
public uint Length;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="Length"> bytes of data
|
||||
/// </summary>
|
||||
public byte[] Data { get; set; } = [];
|
||||
}
|
||||
}
|
||||
19
SabreTools.Data.Models/BFPK/Archive.cs
Normal file
19
SabreTools.Data.Models/BFPK/Archive.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace SabreTools.Data.Models.BFPK
|
||||
{
|
||||
/// <summary>
|
||||
/// BFPK custom archive format
|
||||
/// </summary>
|
||||
/// <see cref="https://forum.xentax.com/viewtopic.php?t=5102"/>
|
||||
public sealed class Archive
|
||||
{
|
||||
/// <summary>
|
||||
/// Header
|
||||
/// </summary>
|
||||
public Header Header { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Files
|
||||
/// </summary>
|
||||
public FileEntry[] Files { get; set; } = [];
|
||||
}
|
||||
}
|
||||
11
SabreTools.Data.Models/BFPK/Constants.cs
Normal file
11
SabreTools.Data.Models/BFPK/Constants.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace SabreTools.Data.Models.BFPK
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
public static readonly byte[] SignatureBytes = [0x42, 0x46, 0x50, 0x4b];
|
||||
|
||||
public const string SignatureString = "BFPK";
|
||||
|
||||
public const uint SignatureUInt32 = 0x4b504642;
|
||||
}
|
||||
}
|
||||
34
SabreTools.Data.Models/BFPK/FileEntry.cs
Normal file
34
SabreTools.Data.Models/BFPK/FileEntry.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
namespace SabreTools.Data.Models.BFPK
|
||||
{
|
||||
/// <summary>
|
||||
/// File entry
|
||||
/// </summary>
|
||||
/// <see cref="https://forum.xentax.com/viewtopic.php?t=5102"/>
|
||||
public sealed class FileEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Name size
|
||||
/// </summary>
|
||||
public int NameSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Uncompressed size
|
||||
/// </summary>
|
||||
public int UncompressedSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Offset
|
||||
/// </summary>
|
||||
public int Offset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Compressed size
|
||||
/// </summary>
|
||||
public int CompressedSize { get; set; }
|
||||
}
|
||||
}
|
||||
28
SabreTools.Data.Models/BFPK/Header.cs
Normal file
28
SabreTools.Data.Models/BFPK/Header.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SabreTools.Data.Models.BFPK
|
||||
{
|
||||
/// <summary>
|
||||
/// Header
|
||||
/// </summary>
|
||||
/// <see cref="https://forum.xentax.com/viewtopic.php?t=5102"/>
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
public sealed class Header
|
||||
{
|
||||
/// <summary>
|
||||
/// "BFPK"
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4)]
|
||||
public string Magic = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Version
|
||||
/// </summary>
|
||||
public int Version;
|
||||
|
||||
/// <summary>
|
||||
/// Files
|
||||
/// </summary>
|
||||
public int Files;
|
||||
}
|
||||
}
|
||||
11
SabreTools.Data.Models/BSP/AmbientIndexLump.cs
Normal file
11
SabreTools.Data.Models/BSP/AmbientIndexLump.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace SabreTools.Data.Models.BSP
|
||||
{
|
||||
/// <see href="https://developer.valvesoftware.com/wiki/BSP_(Source)"/>
|
||||
public sealed class AmbientIndexLump : Lump
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicies
|
||||
/// </summary>
|
||||
public LeafAmbientIndex[] Indicies { get; set; } = [];
|
||||
}
|
||||
}
|
||||
11
SabreTools.Data.Models/BSP/AmbientLightingLump.cs
Normal file
11
SabreTools.Data.Models/BSP/AmbientLightingLump.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace SabreTools.Data.Models.BSP
|
||||
{
|
||||
/// <see href="https://developer.valvesoftware.com/wiki/BSP_(Source)"/>
|
||||
public sealed class AmbientLightingLump : Lump
|
||||
{
|
||||
/// <summary>
|
||||
/// Lightings
|
||||
/// </summary>
|
||||
public LeafAmbientLighting[] Lightings { get; set; } = [];
|
||||
}
|
||||
}
|
||||
33
SabreTools.Data.Models/BSP/Brush.cs
Normal file
33
SabreTools.Data.Models/BSP/Brush.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SabreTools.Data.Models.BSP
|
||||
{
|
||||
/// <summary>
|
||||
/// The brush lump (Lump 18) contains all brushes that were
|
||||
/// present in the original VMF file before compiling.
|
||||
/// Unlike faces, brushes are constructive solid geometry (CSG)
|
||||
/// defined by planes instead of edges and vertices. It is the
|
||||
/// presence of the brush and brushside lumps in Source BSP
|
||||
/// files that makes decompiling them a much easier job than
|
||||
/// for GoldSrc files, which lacked this info.
|
||||
/// </summary>
|
||||
/// <see href="https://developer.valvesoftware.com/wiki/BSP_(Source)"/>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public sealed class Brush
|
||||
{
|
||||
/// <summary>
|
||||
/// First brushside
|
||||
/// </summary>
|
||||
public int FirstSide;
|
||||
|
||||
/// <summary>
|
||||
/// Number of brushsides
|
||||
/// </summary>
|
||||
public int NumSides;
|
||||
|
||||
/// <summary>
|
||||
/// Contents flags
|
||||
/// </summary>
|
||||
public VbspContents Contents;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user