Compare commits

...

251 Commits

Author SHA1 Message Date
Connor Olding d261b299db move files 2019-03-28 16:45:40 +01:00
Connor Olding 52956fd397 Merge remote-tracking branch 'coin_or_build/master' into pensive 2019-03-28 16:45:40 +01:00
Connor Olding 83b73c0930 move files 2019-03-11 06:50:29 +01:00
Connor Olding f94f5382de Merge remote-tracking branch 'rng64/master' 2019-03-11 06:50:29 +01:00
Connor Olding bddb5c32ea move files 2019-03-11 06:50:28 +01:00
Connor Olding 8ce28c4bf4 Merge remote-tracking branch 'response/master' 2019-03-11 06:50:28 +01:00
Connor Olding 6e1544f860 move files 2019-03-11 06:50:28 +01:00
Connor Olding df441b445c Merge remote-tracking branch 'resnet/master' 2019-03-11 06:50:28 +01:00
Connor Olding 9f94bd2f45 move files 2019-03-11 06:50:27 +01:00
Connor Olding b3d8d55997 Merge remote-tracking branch 'gs/master' 2019-03-11 06:50:27 +01:00
Connor Olding 7188ba47c5 move files 2019-03-11 06:50:27 +01:00
Connor Olding 7739895224 Merge remote-tracking branch 'debug_saves/master' 2019-03-11 06:50:26 +01:00
Connor Olding 3aaee2c09f move files 2019-03-11 06:50:26 +01:00
Connor Olding 61188f00da Merge remote-tracking branch 'atttt/master' 2019-03-11 06:50:26 +01:00
Connor Olding 02f586f1cd update readme 2019-03-11 06:34:51 +01:00
Connor Olding a492e20819 move files 2019-03-11 06:23:24 +01:00
Connor Olding fc6c9faeb2 Merge remote-tracking branch 'dnm/master' 2019-03-11 06:23:23 +01:00
Connor Olding 591d5bb5ca move files 2019-03-11 06:20:06 +01:00
Connor Olding 67d6447acf Merge remote-tracking branch 'ds1/master' 2019-03-11 06:17:32 +01:00
Connor 591b496479 2019-03-10 22:15:22 -07:00
Connor Olding ca0e5c7fca move files 2019-03-11 06:09:07 +01:00
Connor Olding 7220056b79 Merge remote-tracking branch 'lsca/master' 2019-03-11 06:07:26 +01:00
Connor 1d216288db 2019-01-19 23:41:58 -08:00
Connor fe84c73459 2019-01-19 23:21:58 -08:00
Connor b87651c549 2018-12-05 03:49:48 -08:00
Connor Olding f8c15bc5b7 add missing period 2018-11-10 05:29:48 +01:00
Connor Olding 70b59d455d fix shit
gitea's markdown renderer is bad
2018-11-10 05:27:43 +01:00
Connor Olding bc2365daff write readme 2018-11-10 05:17:46 +01:00
Connor 04ccfec87b 2018-10-20 05:13:20 -07:00
Connor f66ec0a8a6 2018-10-20 04:59:45 -07:00
Connor Olding ffeef6ee6d move files 2018-10-11 16:45:40 +02:00
Connor Olding 57461ace0b Merge remote-tracking branch 'warcraft_hashes/master' 2018-10-11 16:45:40 +02:00
Connor Olding 18fbf59cb7 move files 2018-10-11 16:45:40 +02:00
Connor Olding 96a45a4ac4 Merge remote-tracking branch 'warcraft_hash/master' 2018-10-11 16:45:40 +02:00
Connor Olding 53a13fb30b move files 2018-10-11 16:45:39 +02:00
Connor Olding f8ffcc851a Merge remote-tracking branch 'twitch_following/master' 2018-10-11 16:45:39 +02:00
Connor Olding 3d0fb91f5d move files 2018-10-11 16:45:39 +02:00
Connor Olding bd110dca48 Merge remote-tracking branch 'trackmogrify/master' 2018-10-11 16:45:39 +02:00
Connor Olding f9bd047c58 move files 2018-10-11 16:45:38 +02:00
Connor Olding 93f7ce654d Merge remote-tracking branch 'tiny_crc32/master' 2018-10-11 16:45:38 +02:00
Connor Olding c626d93521 move files 2018-10-11 16:45:38 +02:00
Connor Olding 88bde0c705 Merge remote-tracking branch 'thps1/master' 2018-10-11 16:45:37 +02:00
Connor Olding 47b5fcc31c move files 2018-10-11 16:45:37 +02:00
Connor Olding 07c587287a Merge remote-tracking branch 'string_tensions/master' 2018-10-11 16:45:37 +02:00
Connor Olding 0527b538ed move files 2018-10-11 16:45:36 +02:00
Connor Olding 9ec61f013f Merge remote-tracking branch 'starcraft_maps/master' 2018-10-11 16:45:36 +02:00
Connor Olding 71da8551ab move files 2018-10-11 16:45:36 +02:00
Connor Olding cddb62a3a0 Merge remote-tracking branch 'starcraft_cdkey/master' 2018-10-11 16:45:36 +02:00
Connor Olding fc9ff7a8b4 move files 2018-10-11 16:45:35 +02:00
Connor Olding 376ab80b03 Merge remote-tracking branch 'speedrun_comparison/master' 2018-10-11 16:45:35 +02:00
Connor Olding 67a6535439 move files 2018-10-11 16:45:35 +02:00
Connor Olding 2c226e0ec8 Merge remote-tracking branch 'psnip_clock/master' 2018-10-11 16:45:35 +02:00
Connor Olding 6c7a9be5df move files 2018-10-11 16:45:34 +02:00
Connor Olding bc4506b3bd Merge remote-tracking branch 'print_tables/master' 2018-10-11 16:45:34 +02:00
Connor Olding e1996520c2 move files 2018-10-11 16:45:34 +02:00
Connor Olding 498cdba5a2 Merge remote-tracking branch 'polyphase_halfband/master' 2018-10-11 16:45:34 +02:00
Connor Olding d5c51aae94 move files 2018-10-11 16:45:33 +02:00
Connor Olding 999da5ac53 Merge remote-tracking branch 'n64_models/master' 2018-10-11 16:45:33 +02:00
Connor Olding ffbdf32f13 move files 2018-10-11 16:45:33 +02:00
Connor Olding cf507ec580 Merge remote-tracking branch 'music_sync/master' 2018-10-11 16:45:33 +02:00
Connor Olding 4f5375a378 move files 2018-10-11 16:45:33 +02:00
Connor Olding b79aed3f66 Merge remote-tracking branch 'mips_disassembler/master' 2018-10-11 16:45:33 +02:00
Connor Olding 71705de4ac move files 2018-10-11 16:45:32 +02:00
Connor Olding e1c1844fb2 Merge remote-tracking branch 'mario_tennis/master' 2018-10-11 16:45:32 +02:00
Connor Olding 4ce6e4ae96 move files 2018-10-11 16:45:32 +02:00
Connor Olding f2c1354637 Merge remote-tracking branch 'lsf/master' 2018-10-11 16:45:32 +02:00
Connor Olding c088d79dec move files 2018-10-11 16:45:32 +02:00
Connor Olding 213b5d0ccb Merge remote-tracking branch 'love_plotting/master' 2018-10-11 16:45:32 +02:00
Connor Olding 9f39f4aa14 move files 2018-10-11 16:45:32 +02:00
Connor Olding b9960ee594 Merge remote-tracking branch 'kyaa/master' 2018-10-11 16:45:32 +02:00
Connor Olding 6b81464a74 move files 2018-10-11 16:45:31 +02:00
Connor Olding d0efbb3886 Merge remote-tracking branch 'kill_reboot/master' 2018-10-11 16:45:31 +02:00
Connor Olding 35921704e3 move files 2018-10-11 16:45:31 +02:00
Connor Olding 703f4b7039 Merge remote-tracking branch 'image_deduplication/master' 2018-10-11 16:45:31 +02:00
Connor Olding 8f42809229 move files 2018-10-11 16:45:31 +02:00
Connor Olding e0160e220c Merge remote-tracking branch 'filter_tutorial/master' 2018-10-11 16:45:31 +02:00
Connor Olding 41488120af move files 2018-10-11 16:45:31 +02:00
Connor Olding b7924de0c4 Merge remote-tracking branch 'explicit_globals/master' 2018-10-11 16:45:31 +02:00
Connor Olding 130989789a move files 2018-10-11 16:45:31 +02:00
Connor Olding 7cb3341158 Merge remote-tracking branch 'dwarf_font/master' 2018-10-11 16:45:30 +02:00
Connor Olding 965064ef90 move files 2018-10-11 16:45:30 +02:00
Connor Olding bec9cc266a Merge remote-tracking branch 'dictionary_attack/master' 2018-10-11 16:45:30 +02:00
Connor Olding 7a7597ce83 move files 2018-10-11 16:45:30 +02:00
Connor Olding 8f568e07ff Merge remote-tracking branch 'desmos/master' 2018-10-11 16:45:30 +02:00
Connor Olding e12cde76c0 move files 2018-10-11 16:45:30 +02:00
Connor Olding 52ee141c09 Merge remote-tracking branch 'danbooru_scrape/master' 2018-10-11 16:45:30 +02:00
Connor Olding 7de1254516 move files 2018-10-11 16:45:30 +02:00
Connor Olding 31de1416b1 Merge remote-tracking branch 'danbooru_atomizer/master' 2018-10-11 16:45:29 +02:00
Connor Olding 1576082e43 move files 2018-10-11 16:45:29 +02:00
Connor Olding 221362d19b Merge remote-tracking branch 'batch_font_render/master' 2018-10-11 16:45:29 +02:00
Connor Olding 3a378d5e56 move files 2018-10-11 16:45:29 +02:00
Connor Olding 130725b759 Merge remote-tracking branch 'batch_encoding/master' 2018-10-11 16:45:29 +02:00
Connor Olding 6247148a85 move files 2018-10-11 16:45:29 +02:00
Connor Olding e9c0c0b245 Merge remote-tracking branch 'arithmetic_coding/master' 2018-10-11 16:45:29 +02:00
Connor Olding 145ccd8f82 move files 2018-10-11 16:45:29 +02:00
Connor Olding 6dbace24f9 Merge remote-tracking branch '360_wasd/master' 2018-10-11 16:43:20 +02:00
Connor Olding 54de7512c9 init 2018-10-11 16:33:25 +02:00
Connor cf893660eb 2018-10-11 06:53:08 -07:00
Connor eeed9ea275 2018-10-09 22:27:51 -07:00
Connor 4031c2dbda 2018-10-09 22:21:38 -07:00
Connor 17040c6b77 2018-09-20 13:59:38 -07:00
Connor e8e11c3ad3 2018-09-20 13:47:17 -07:00
Connor f6c8945b8c 2018-09-20 13:47:10 -07:00
Connor dfe69c396f 2018-09-11 14:03:16 -07:00
Connor 248a9c0305 2018-09-10 13:33:41 -07:00
Connor 7c5834de20 2018-09-10 13:30:55 -07:00
Connor a904f76704 2018-09-10 12:24:24 -07:00
Connor 207603e462 2018-09-10 12:23:05 -07:00
Connor 74cd548adb 2018-09-10 12:08:36 -07:00
Connor 7321c3c9e2 2018-09-10 12:08:23 -07:00
Connor 5b83a72513 2018-08-21 19:17:05 -07:00
Connor 50b1e9a48e 2018-06-04 11:14:04 -07:00
Connor cd0b5a8b4b 2018-06-04 11:07:29 -07:00
Connor 22e3eceb5d 2018-06-03 13:00:46 -07:00
Connor f4c682bbea 2018-06-03 03:36:00 -07:00
Connor aab2829c9c 2018-06-03 02:59:27 -07:00
Connor db487fe6c3 2018-04-20 19:15:40 -07:00
Connor 40b0a332a5 2018-04-13 16:32:14 -07:00
Connor d31b7b580e 2018-03-10 17:30:15 -08:00
Connor dd81f51280 2018-03-03 15:49:11 -08:00
Connor 29e33e832e 2017-11-08 00:53:23 -08:00
Connor Olding 614fa18ea6 . 2017-10-01 09:57:40 +00:00
Connor 14d49ba33c 2017-10-01 02:57:09 -07:00
Connor 1ec021fc8a 2017-09-14 10:20:32 -07:00
Connor 5bd84506a0 2017-09-14 10:19:16 -07:00
Connor daafe529f3 2017-09-14 10:19:02 -07:00
Connor 8ebad01de8 . 2017-08-31 04:48:12 +00:00
Connor f8cc5a50db 2017-08-24 22:57:48 -07:00
Connor 4c0887edfb 2017-08-21 04:22:57 -07:00
Connor 401732d4be 2017-08-10 12:18:11 -07:00
Connor a8cc797d0f 2017-08-10 12:17:25 -07:00
Connor cebd8b94aa 2017-08-10 12:16:18 -07:00
Connor f055ff2497 2017-08-10 12:15:39 -07:00
Connor 12a9b1640d 2017-08-10 03:28:25 -07:00
Connor 7ccb616fee 2017-08-10 03:14:16 -07:00
Connor bd2496fa0e 2017-07-30 19:42:17 -07:00
Connor 0243eb33dd 2017-07-30 19:41:51 -07:00
Connor 075418af21 2017-07-30 18:08:20 -07:00
Connor 1815a0b996 2017-07-30 17:52:04 -07:00
Connor Olding add6801a23 fix calloc of functions
what was i thinking?
2017-07-05 02:33:44 +00:00
Connor Olding eb7ead0b78 use stdint.h for types 2017-07-05 02:30:12 +00:00
Connor Olding 56e4333bab fix base of shift instructions 2017-07-05 02:18:50 +00:00
Connor 273abe1ee4 2017-07-03 20:16:09 -07:00
Connor 89a434c5e8 2017-07-03 20:03:50 -07:00
Connor f8fbea1e92 2017-07-03 19:56:44 -07:00
Connor 309749f0db 2017-05-16 21:02:45 -07:00
Connor 1ee01e95ef 2017-05-15 21:07:31 -07:00
Connor e8e5428400 2017-05-15 20:50:15 -07:00
Connor ef338fea75 2017-05-15 18:45:05 -07:00
Connor 44a3945bf2 2017-04-25 11:57:05 -07:00
Connor 8093266ec7 2017-04-25 11:52:21 -07:00
Connor caf9a2aab7 2017-04-25 11:44:45 -07:00
Connor Olding 327b8ef28c . 2017-04-02 02:24:19 +00:00
Connor Olding 2f145eb879 . 2017-04-02 02:23:01 +00:00
Connor Olding f361a02852 . 2017-03-31 06:39:03 +00:00
Connor Olding f7e3cfceb1 . 2017-01-05 04:56:35 -08:00
Connor Olding 15d053789e . 2017-01-05 04:55:10 -08:00
Connor Olding 2c824ad30b _G 2016-12-11 05:23:17 -08:00
Connor Olding 959be16350 . 2016-12-11 05:22:03 -08:00
Connor Olding af75dcc7c5 . 2016-12-11 05:20:08 -08:00
Connor Olding c76f172975 . 2016-12-11 04:02:36 -08:00
Connor Olding 6fb6d3ba62 . 2016-12-11 03:54:59 -08:00
Connor Olding 03860a8c7f . 2016-11-06 18:07:41 -08:00
Connor 82010d49d8 2016-08-19 02:11:10 -07:00
Connor 259475bfe6 2016-08-18 20:04:48 -07:00
Connor 7f5dd0453e 2016-08-18 19:56:33 -07:00
Connor 121dd44429 2016-08-18 19:55:00 -07:00
Connor 8c8b5d313a 2016-08-18 19:50:10 -07:00
Connor fd56c5124a 2016-08-18 19:46:44 -07:00
Connor Olding e02bca097c . 2016-08-09 01:11:05 -07:00
Connor b61ecf090a 2016-08-08 23:57:20 -07:00
Connor Olding 98f669262c . 2016-06-01 09:23:25 -07:00
Connor d88fe0dde7 2016-06-01 09:20:24 -07:00
Connor Olding b028ee53d9 . 2016-05-25 11:38:44 -07:00
Connor Olding e61a32c615 . 2016-05-25 11:30:07 -07:00
Connor Olding eeb5d2941e . 2016-05-25 07:31:48 -07:00
Connor Olding 28edd29072 . 2016-05-24 20:15:26 -07:00
Connor db3171ac29 2016-05-24 20:10:24 -07:00
Connor 14af124e09 2016-04-02 08:45:27 -07:00
Connor Olding 00c1942cba . 2016-04-01 00:41:08 -07:00
Connor Olding 828c74cb72 . 2016-04-01 00:40:44 -07:00
Connor 82917de18f 2016-04-01 00:22:24 -07:00
Connor bc1eceaaf7 2016-03-29 07:16:25 -07:00
Connor Olding 6a2eb1ffae . 2016-02-14 09:43:52 -08:00
Connor Olding 26904c640f add Makefile 2016-02-09 00:11:56 -08:00
Connor Olding ec865923a5 dumb changes 2016-02-09 00:11:03 -08:00
Connor Olding a111b309cb fix relative path 2016-02-09 00:08:28 -08:00
Connor Olding 814cd636c0 clone spinout182.com/mipsdis/actordis/ 2016-02-09 00:08:06 -08:00
Connor b4e1ec0a59 2016-02-09 00:05:39 -08:00
Connor Olding 176b8b32b0 . 2015-11-21 20:01:40 -08:00
Connor 8d44432e0f 2015-05-25 18:56:30 -07:00
Connor Olding 11b0a802f3 sync by track and disc numbers too 2015-03-24 20:25:59 -07:00
Connor Olding a6aed4fcc5 a whole heap of crap 2014-11-03 12:57:21 -08:00
Connor Olding 62f12d031a more cleanup 2014-11-02 13:49:07 -08:00
Connor Olding 2bbe301cdc switch to mutagenx and upgrade to python 3
mutagenx is about 13 times faster than mutagen!
2014-11-02 12:31:39 -08:00
Connor Olding d4fbd7a829 cleanup 2014-11-02 10:12:52 -08:00
Connor Olding 34db3bf060 lol windows batch files 2014-10-15 21:28:48 -07:00
Connor e824a1f8ba 2014-10-15 21:23:15 -07:00
Connor Olding f937547991 . 2014-10-02 01:33:47 -07:00
Connor Olding 9ef423de71 . 2014-10-02 01:26:46 -07:00
Connor Olding 83f1fbe6fc . 2014-10-02 01:21:52 -07:00
Connor Olding 3aae7d6c1d . 2014-10-02 00:54:55 -07:00
Connor Olding 115a30cc5b . 2014-09-30 14:26:11 -07:00
Connor Olding 1fb3df1403 . 2014-09-29 21:47:58 -07:00
Connor Olding bb5eabbd56 . 2014-09-29 21:20:44 -07:00
Connor cf0d3cf896 2014-09-29 21:14:37 -07:00
Connor Olding 051265ea3a smarter string quoting 2014-08-25 22:51:45 -07:00
Connor Olding 124ec51da3 basic testing table 2014-08-20 17:59:36 -07:00
Connor Olding 62866ee36c dear pesky plumbers 2014-08-20 17:48:45 -07:00
Connor ab2b4a4dff 2014-08-20 17:35:32 -07:00
Connor Olding a61ff7660c . 2014-06-18 05:48:19 -07:00
Connor 5cb1407dfa 2014-06-18 05:46:12 -07:00
Connor Olding c61c00dfd6 i gave up 2014-05-25 16:34:58 -07:00
Connor Olding fef8ae685d beginning filesync syncing 2014-05-03 19:33:38 -07:00
Connor Olding 9d475fdba7 OOP bloat 2014-04-30 17:04:02 -07:00
Connor Olding 4bbf061085 unbreak it 2014-04-29 08:52:55 -07:00
Connor Olding f7ac8f0704 is this wrong, feels so wrong 2014-04-29 03:32:23 -07:00
Connor Olding b4eeededf8 it's workingggg better 2014-04-28 19:31:24 -07:00
Connor Olding a236ef5a51 it's workinggggg 2014-04-27 15:34:46 -07:00
Connor Olding dbed8bb926 in the shape of an L on her forehead 2014-04-27 13:47:08 -07:00
Connor Olding 14014e2e93 with her finger and her thumb 2014-04-27 12:03:52 -07:00
Connor Olding 6ae05ac68d she was looking kinda dumb 2014-04-27 09:36:14 -07:00
Connor Olding d1ea330243 but i ain't the sharpest tool in the shed 2014-04-27 07:17:32 -07:00
Connor Olding 1a2c9ae1e6 the world is gonna roll me 2014-04-27 07:05:51 -07:00
Connor Olding 6a1562c5f5 BODY once told me 2014-04-27 06:46:24 -07:00
Connor Olding e5b036d9b8 some 2014-04-26 14:34:13 -07:00
Connor c716e824dd 2014-04-26 14:22:38 -07:00
Connor Olding 9c69160563 quit finding after one match 2013-12-08 06:21:28 -08:00
Connor Olding e4732d88cd whatever 2013-11-01 20:08:34 -07:00
Connor Olding 20b8930ee6 no such thing 2013-07-29 00:49:47 -07:00
notwa 98b94536f7 2013-07-29 00:45:45 -07:00
notwa 290370630e 2013-06-27 11:54:53 -07:00
Connor Olding dceb0894b3 f that s 2013-06-21 20:38:51 -07:00
notwa 654b8ce3f7 2013-05-31 21:06:00 -07:00
Connor Olding c8fc1d462e dumbest bug 2013-05-20 20:29:40 -07:00
Connor Olding 2edb454851 strip path option 2013-05-20 20:10:49 -07:00
Connor Olding 2ed08389c9 oops 2013-05-20 17:09:40 -07:00
Connor Olding 5865c56fd8 refactor 2013-05-20 16:58:54 -07:00
Connor Olding 31ebe0b649 lint 2013-05-20 12:54:10 -07:00
Connor Olding ea6bf420b2 forgot 2013-05-14 19:08:38 -07:00
Connor Olding 6e286aec91 trunc len 2013-05-14 19:07:50 -07:00
notwa 85da14dad0 2013-05-14 18:55:04 -07:00
notwa b26f4074ec 2013-04-12 11:02:02 +00:00
notwa b739c4b6c0 2013-04-12 08:39:43 +00:00
notwa 097b5e519b 2013-04-12 08:37:55 +00:00
notwa 39c1899843 2013-03-13 11:44:09 +00:00
notwa 5456e17c8b 2013-03-11 15:09:01 +00:00
notwa 0c131cf1ea 2013-02-26 11:39:15 +00:00
notwa 9da2f9fc2e 2013-02-26 10:41:48 +00:00
notwa 54aff32db3 2013-02-26 08:39:01 +00:00
notwa 15b7cb75e5 2013-02-25 15:04:51 +00:00
72 changed files with 11936 additions and 0 deletions

64
360_wasd/360-stick.ahk Normal file
View File

@ -0,0 +1,64 @@
#Persistent
#SingleInstance Force
Send {w up}
Send {a up}
Send {s up}
Send {d up}
Deadzone := 0.6
SetTimer Wax, 5
return
Wax:
GetKeyState JoyX, JoyX
GetKeyState JoyY, JoyY
JoyX := (JoyX - 50)/50
JoyY := (JoyY - 50)/50
Angle := ATan(JoyX/JoyY)*2/3.14159
Angle := (JoyY > 0) ? Angle + 3: Angle + 1
Length := Sqrt(JoyX**2 + JoyY**2)
; 1 1 0
; 2 - 0
; 2 3 3
OldX = %HoldX%
OldY = %HoldY%
if Length > %Deadzone%
{
if % Angle >= 1.2 && Angle < 2.7
HoldX = a
else if % Angle >= 3.2 || Angle < 0.7
HoldX = d
else
HoldX =
if % Angle >= 0.3 && Angle < 1.7
HoldY = w
else if % Angle >= 2.3 && Angle < 3.7
HoldY = s
else
HoldY =
} else
{
HoldX =
HoldY =
}
SetKeyDelay -1
if OldX != %HoldX%
{
if HoldX
Send {%HoldX% down}
if OldX
Send {%OldX% up}
}
if OldY != %HoldY%
{
if HoldY
Send {%HoldY% down}
if OldY
Send {%OldY% up}
}
return
F10::ExitApp

268
README.md Normal file
View File

@ -0,0 +1,268 @@
# gists
all files are [Unlicensed,](http://unlicense.org/) unless otherwise noted:
```
This is free and unencumbered software released into the public domain.
For more information, please refer to <http://unlicense.org/>
```
the Python scripts here only work with Python 3.
### 360\_wasd
an ancient AutoHotKey script i wrote to convert the left control stick
of a 360 controller to WASD inputs on a keyboard.
i used it to play an equally ancient version of [The Binding of Isaac,](https://store.steampowered.com/app/113200/The_Binding_of_Isaac/)
among a couple other games.
it should still work fine even today, but
you can press F10 to kill it if it starts acting up.
### arithmetic\_coding
a GPL-licensed arithmetic coding compressor and decompressor for binary strings.
originally by [David MacKay,](http://www.inference.org.uk/mackay/python/compress/)
this has been cleaned up (passes pycodestyle) and ported to Python 3.
### batch\_encoding
another old script. this time it's a Windows batch file for re-encoding
videos to be suitable for online distribution.
i don't really use this anymore, but
it looks like i once used it for N64 videos recorded with Bizhawk.
it contains some advanced `compand` nonsense from before `acompressor` was a thing.
### batch\_font\_render
a zsh script to render fonts — all the otf & ttf's in a directory —
to png's, with some good ol' Lorem Ipsum.
it uses pango and fontconfig, so i've only tested it on \*nix.
i recently repurposed this script
to render characters to bitmaps for use as textures in games,
but i haven't uploaded that version yet.
### danbooru\_atomizer
a Python script that scrapes atom feeds on [danbooru (nsfw)](https://danbooru.donmai.us/)
and merges them into one mega-feed.
### danbooru\_scrape
another danbooru scraper, this time for batch-downloading all the images
for a given search query. requires zsh and xml2.
i don't think this version works anymore,
so i'll have to update it with my local changes sometime.
### desmos
[stuff i've plotted in desmos.](/desmos/desmos.md)
just click the link; it's another markdown file.
### dictionary\_attack
i went looking for a dead-simple tool to
construct random, fixed-length strings from a list of shorter strings,
but i realized in the time i'd find something i liked,
i could just write my own in Python.
so that's exactly what i did!
beware, this is highly unoptimized, favoring simplicity over speed.
### dnm
stuff related to Doubutsu no Mori,
the original Japan-only release of Animal Crossing for the N64.
right now, this just contains a patch to make the game boot
directly into the NES emulator for experimenting with.
since this game is built on the Zelda 64 engine (go figure),
you can extract files and reconstruct ROMs
by using the `z64_dump.py` script, found elsewhere in my `mm` repo.
### ds1
stuff related to Dark Souls 1.
contains scripts to extract raw dialog and weapon/armor/npc/etc data.
you can extract the necessary files from the game's archives
using this tool:
[PC](https://github.com/HotPocketRemix/UnpackDarkSoulsForModding)
or [PS3](https://github.com/notwa/UnpackDarkSoulsForModding)
### dwarf\_font
the 8x12 bitmap font included with [Dwarf Fortress](http://www.bay12games.com/dwarves),
alongside a [love2d](https://love2d.org/) Lua script for loading it
with all the appropriate character mappings.
the code is Unlicensed, and i believe the font itself is public domain.
### explicit\_globals
hmm, this probably shouldn't be here...
### filter\_tutorial
a single Python file that walks you through
designing and plotting an IIR filter.
requires numpy, scipy, and matplotlib.
### image\_deduplication
a Python script to find duplicate images given a hamming distance threshold.
it employs dhash to do the heavy lifting. requires pillow and dhash.
it doesn't recurse into `./_duplicate/` so you can dump things there if you wish.
### kill\_reboot
does nasty stuff to Windows 10 to
prevent updates from automatically triggering reboots.
the C# portion is based on [NSudo.](https://github.com/M2Team/NSudo)
the batch script exploits [a UAC bypass found by Tyranid.](https://tyranidslair.blogspot.ca/2017/05/exploiting-environment-variables-in.html)
### kyaa
really *really* hacky argument-parsing macros for C programs.
contains [some ad-hoc documentation.](/kyaa/kyaa.md)
This is free and unencumbered software released into the public domain.
### lsca
a brief cellular automata experiment.
inspired by [Loren Schmidt.](https://twitter.com/lorenschmidt)
### love\_plotting
plotting equations with [love2d](https://love2d.org/).
it probably doesn't work on the latest version.
it's nothing special, and i only included it here for archival sake.
i have a newer version locally, but it needs to be cleaned up before it can be committed.
### lsf
an awful bash script i started writing just for fun,
left here for archival sake.
heaven forbid you actually enjoy this program,
please instead consider using [ls--](https://github.com/trapd00r/ls--)
or the like.
### mario\_tennis
this C program validates, brute-forces, and generates codes
for the defunct Ring Tournament mode in Mario Tennis (N64),
once used to hold competitions in magazines and the like.
### mips\_disassembler
an old disassembler for MIPS III binaries by spinout.
the license is ambiguous.
i've since tweaked and cleaned the code. a little. not a lot.
### music\_sync
a Python script for syncing and re-encoding audio files
for use on music players that support ogg vorbis.
if you actually want to use this for some reason,
be aware that you will need to know how to write Python
to get it to work the way you want. and also, it's *really* slow,
and even runs out of memory if too many files need updating (thanks mutagenx).
### n64\_models
a hastily-written Python script for outlining the F3DEX model format
used in some N64 games.
### polyphase\_halfband
a C port (from C++) of the polyphase halfband filter coefficient generator
by [Laurent de Soras.](http://ldesoras.free.fr/index.html)
the original is licensed under the [WTFPL,](http://www.wtfpl.net/)
so i'm re-licensing this under [Unlicense,](http://unlicense.org/) because i can.
don't read into the code too much, it's not meant to be understood (sadly).
### print\_tables
provides the `pt` Lua module for recursively dumping table contents
into a semi-human-readable format.
since the resulting format cannot be deserialized,
this is primarily intended for debugging purposes,
and i'd like to think it excels at that.
### psnip\_clock
a fork of the clock utility header file
from [Portable Snippets](https://github.com/nemequ/portable-snippets)
by Evan Nemerson.
this fork removes the dependency on `windows.h` for the Windows backend
by using some gross hacks,
albeit not as gross as actually having to include `windows.h`.
this is licensed under [CC0 1.0.](https://creativecommons.org/publicdomain/zero/1.0/)
### speedrun\_comparison
an avisynth script for creating
[speedrun comparison videos like this.](https://youtu.be/Y2wW5TmOzFw)
### starcraft\_cdkey
a C program to validate Starcraft 1 CD-keys.
the original Starcraft is free now,
so this program doesn't serve much purpose other than
to demonstrate how *not* to write a CD-key validation routine —
you can brute-force any key by hand just by tweaking the final digit!
### starcraft\_maps
a Python script for reading and re-writing Starcraft 1 maps,
i.e. deprotection, or what-have-you.
### string\_tensions
Python scripts for determining optimal guitar string tensions
for a given tuning, and similar tasks,
backed by a database of manufacturers' strings.
the data here is probably obsolete by now.
### thps1
a C program for exploring the "TYR" glitch in Tony Hawk's Pro Skater 1 (N64).
### tiny\_crc32
a very small C program for computing CRC-32s.
this is a rewrite of [the code by Karl Malbrain.](http://www.geocities.ws/malbrain/crc_c.html)
the license is ambiguous?
i've written [a similar routine in MIPS assembly.](https://eaguru.guru/git/notwa/mm/src/branch/master/asm/crc32.asm)
### trackmogrify
just a list of strings recognized by [Distance's](https://store.steampowered.com/app/233610/Distance/)
trackmogrify feature. this was extracted directly from the game's memory,
and was last updated for version 1.0.
### warcraft\_hash
a C program for the unique hashing function found in Warcraft III.
### warcraft\_hashes
a list of hashes and the source strings
found in the game that use the aforementioned hash function.
### README.md
you're lookin' at it!

View File

@ -0,0 +1,251 @@
# Arithmetic coding compressor and decompressor for binary strings.
# via: http://www.inference.org.uk/mackay/python/compress/ac/ac_encode.py
# main page: http://www.inference.org.uk/mackay/python/compress/
# this has been cleaned up (passes pycodestyle) and ported to python 3.
# default prior distribution
BETA0 = 1
BETA1 = 1
M = 30
ONE = 1 << M
HALF = 1 << (M - 1)
QUARTER = 1 << (M - 2)
THREEQU = HALF + QUARTER
def clear(c, charstack):
# print out character c, and other queued characters
a = repr(c) + repr(1 - c) * charstack[0]
charstack[0] = 0
return a
def encode(string, c0=BETA0, c1=BETA1, adaptive=True):
assert c0 > 0
assert c1 > 0
b = ONE
a = 0
if not adaptive:
p0 = c0 / (c0 + c1)
ans = ""
charstack = [0] # how many undecided characters remain to print
for c in string:
w = b - a
if adaptive:
cT = c0 + c1
p0 = c0 / cT
boundary = a + int(p0 * w)
# these warnings mean that some of the probabilities
# requested by the probabilistic model are so small
# (compared to our integers) that we had to round them up
# to bigger values.
if boundary == a:
boundary += 1
print("warningA")
if boundary == b:
boundary -= 1
print("warningB")
if c == '1':
a = boundary
if adaptive:
c1 += 1
elif c == '0':
b = boundary
if adaptive:
c0 += 1
# ignore other characters
while a >= HALF or b <= HALF: # output bits
if a >= HALF:
ans += clear(1, charstack)
a -= HALF
b -= HALF
else:
ans += clear(0, charstack)
a *= 2
b *= 2
assert a <= HALF
assert b >= HALF
assert a >= 0
assert b <= ONE
# if the gap a-b is getting small, rescale it
while a > QUARTER and b < THREEQU:
charstack[0] += 1
a *= 2
b *= 2
a -= HALF
b -= HALF
assert a <= HALF
assert b >= HALF
assert a >= 0
assert b <= ONE
# terminate
if HALF - a > b - HALF:
w = HALF - a
ans += clear(0, charstack)
while w < HALF:
ans += clear(1, charstack)
w *= 2
else:
w = b - HALF
ans += clear(1, charstack)
while w < HALF:
ans += clear(0, charstack)
w *= 2
return ans
def decode(string, N, c0=BETA0, c1=BETA1, adaptive=True):
# must supply N, the number of source characters remaining.
assert c0 > 0
assert c1 > 0
b = ONE
a = 0
model_needs_updating = True
if not adaptive:
p0 = c0 / (c0 + c1)
ans = ""
u = 0
v = ONE
for c in string:
if N <= 0:
break # out of the string-reading loop
assert N > 0
# (u,v) is the current "encoded alphabet" binary interval,
# and halfway is its midpoint.
# (a,b) is the current "source alphabet" interval,
# and boundary is the "midpoint"
assert u >= 0
assert v <= ONE
halfway = u + (v - u) / 2
if c == '1':
u = halfway
elif c == '0':
v = halfway
# Read bits until we can decide what the source symbol was.
# Then emulate the encoder's computations,
# and tie (u,v) to tag along for the ride.
while 1: # do-while
if model_needs_updating:
w = b - a
if adaptive:
cT = c0 + c1
p0 = c0 / cT
boundary = a + int(p0 * w)
if boundary == a:
boundary += 1
print("warningA")
if boundary == b:
boundary -= 1
print("warningB")
model_needs_updating = False
if boundary <= u:
ans += "1"
if adaptive:
c1 += 1
a = boundary
model_needs_updating = True
N -= 1
elif boundary >= v:
ans += "0"
if adaptive:
c0 += 1
b = boundary
model_needs_updating = True
N -= 1
else:
# not enough bits have yet been read to know the decision.
pass
# emulate outputting of bits by the encoder,
# and tie (u,v) to tag along for the ride.
while a >= HALF or b <= HALF:
if a >= HALF:
a -= HALF
b -= HALF
u -= HALF
v -= HALF
a *= 2
b *= 2
u *= 2
v *= 2
model_needs_updating = True
assert a <= HALF
assert b >= HALF
assert a >= 0
assert b <= ONE
# if the gap a-b is getting small, rescale it
while a > QUARTER and b < THREEQU:
a *= 2
b *= 2
u *= 2
v *= 2
a -= HALF
b -= HALF
u -= HALF
v -= HALF
# this is the condition for this do-while loop
if not (N > 0 and model_needs_updating):
break
return ans
def test():
tests = [
"1010",
"111",
"00001000000000000000",
"1",
"10",
"01",
"0",
"0000000",
"""
00000000000000010000000000000000
00000000000000001000000000000000
00011000000
""",
]
for s in tests:
# an ugly way to remove whitespace and newlines from the test strings:
s = "".join(s.split())
N = len(s) # required for decoding later.
print("original:", s)
e = encode(s, c0=10, c1=1)
print("encoded: ", e)
ds = decode(e, N, c0=10, c1=1)
print("decoded: ", ds)
if ds != s:
print("FAIL")
else:
print("PASS")
print()
if __name__ == '__main__':
test()

280
atttt/atttt.py Executable file
View File

@ -0,0 +1,280 @@
#!/usr/bin/env python3
import sys
import numpy as np
from misc import *
from basic import Brain
def align(x, alignment):
return (x + alignment // 2) // alignment * alignment
def uniq_rows(a, return_index=False, return_inverse=False, return_counts=False):
# via http://stackoverflow.com/a/16973510
# black magic wrapper around np.unique
return_any = return_index or return_inverse or return_counts
if not return_any:
np.unique(a.view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))).view(a.dtype).reshape(-1, a.shape[1])
else:
void_dtype = np.dtype((np.void, a.dtype.itemsize * a.shape[1]))
ret = np.unique(a.view(void_dtype), return_index, return_inverse, return_counts)
return (ret[0].view(a.dtype).reshape(-1, a.shape[1]),) + ret[1:]
class ATTTT():
def __init__(self, brain):
self.brain = brain
self.score = self._score
def _score(self, reply, maxn):
if len(reply) > maxn:
return -999999999
#return len(reply)
return 1
def reply(self, item=None, maxn=1000, include_scores=False, attempts=None):
if attempts == None:
# just guess some value that'll take roughly the same amount of time
attempts = int(2**12 / self.brain.order)
lament('attempts:', attempts)
replies = []
for i in range(attempts):
reply = "".join(self.brain.reply(item=item, maxn=maxn+1))
replies += [(reply, self.score(reply, maxn))]
result = sorted(replies, key=lambda t: t[1], reverse=True)[0]
if include_scores:
return result
else:
return result[0]
class PatternBrain(Brain):
def __init__(self, *args, **kwargs):
super().__init__(*args, padding='~', **kwargs)
self.tokens = []
def helper(self, v):
return (v,)
def resolve_tokens(self, tokens):
# positive values are just unicode characters
if isinstance(tokens, int) or isinstance(tokens, np.int32):
return tokens < 0 and self.tokens[tokens] or chr(tokens)
else:
return [o < 0 and self.tokens[o] or chr(o) for o in tokens]
def new_token(self, value):
new_id = -1 - len(self.tokens)
self.tokens[new_id] = value
return new_id
@staticmethod
def prepare_items(items, pad=True):
new_items = []
for item in items:
item = item.strip('\n')
# assert that the number of sequences is a multiple of 2
# otherwise we can't .reshape() it to be two-dimensional later on
next_biggest = align(len(item) + 1, 2)
# initialize with padding (-1)
new_item = -np.ones(next_biggest, dtype=np.int32)
for i, c in enumerate(item):
new_item[i] = ord(c)
new_items.append(new_item)
# add an extra padding item to the head and tail
# to make it easier to convert from sequences back to items later on
if pad:
pad = -np.ones(1, dtype=np.int32)
new_items.insert(0, pad)
new_items.append(pad)
return np.concatenate(new_items)
def stat_tokens(self, all_items, skip_normal=False):
unique, counts = np.unique(all_items, return_counts=True)
count_order = np.argsort(counts)[::-1]
counts_descending = counts[count_order]
unique_descending = unique[count_order]
for i, token_id in enumerate(unique_descending):
if token_id == -1:
continue
if skip_normal and token_id >= 0:
continue
token = self.resolve_tokens(token_id)
lament("token id {:5} occurs {:8} times: \"{}\"".format(
token_id, counts_descending[i], token))
lament("total tokens: {:5}".format(i + 1))
def merge_all(self, all_items, merges, min_count=2):
# set up a 2d array to step through at half the row length;
# this means double redundancy; to acquire all the sequences.
# we could instead .roll it later to get the other half.
# that would require less memory, but memory isn't really a concern.
sequences = all_items.repeat(2)[1:-1].reshape(-1, 2).copy()
for i in range(merges):
invalid = np.any(sequences == -1, axis=1)
valid_sequences = np.delete(sequences, np.where(invalid), axis=0)
unique, counts = uniq_rows(valid_sequences, return_counts=True)
count = counts.max()
most_common = (None, 1)
if count > most_common[1]:
seq = unique[counts == count][0]
most_common = (seq, count)
if most_common[0] is None or most_common[1] <= 1 or most_common[1] < min_count:
lament('no more valid sequences')
break
token_value = "".join(self.resolve_tokens(most_common[0]))
new_id = self.new_token(token_value)
# replace the most common two-token sequence
# with one token to represent both
found = np.all(sequences == most_common[0], axis=1)
before = np.roll(found, -1)
after = np.roll(found, 1)
# don't wrap around truth values
before[-1] = False
after[0] = False
# remove the "found" sequences
# and update the previous/next,
# not unlike a doubly-linked list.
befores = sequences[before].T.copy()
befores[1] = new_id
sequences[before] = befores.T
afters = sequences[after].T.copy()
afters[0] = new_id
sequences[after] = afters.T
here = np.where(found)
sequences = np.delete(sequences, here, axis=0)
lament("new token id {:5} occurs {:8} times: \"{}\"".format(
new_id, len(here[0]), self.tokens[new_id]))
# reconstruct all_items out of the sequences
all_items = sequences.reshape(-1)[::2][1:].copy()
return all_items
def learn_all(self, items, merges=0, stat=True):
min_count = 2 # minimum number of occurences to stop creating tokens at
if merges < 0:
min_count = -merges
merges = 65536 # arbitrary sanity value
# we'll use numpy matrices so this isn't nearly as disgustingly slow
self.tokens = {-1: ''} # default with an empty padding token
all_items = self.prepare_items(items)
if merges > 0:
all_items = self.merge_all(all_items, merges, min_count)
# begin the actual learning
self.reset()
np_item = []
for i in all_items:
if i == -1:
if len(np_item) == 0:
continue
item = tuple()
for i in np_item:
if i < 0:
assert(i != -1)
item += self.helper(self.tokens[i])
else:
item += self.helper(chr(i))
#die(np_item, item)
self.learn(item)
np_item = []
else:
np_item.append(i)
self.update()
if merges != 0 and stat:
self.stat_tokens(all_items)
def run(pname, args, env):
if not 1 <= len(args) <= 2:
lament("usage: {} {{input file}} [savestate file]".format(pname))
return 1
args = dict(enumerate(args)) # just for the .get() method
fn = args[0]
state_fn = args.get(1, None)
# the number of lines to output.
count = int(env.get('COUNT', '8'))
# learn and sample using this number of sequential tokens.
order = int(env.get('ORDER', '2'))
# how experimental to be with sampling.
# probably doesn't work properly.
temperature = float(env.get('TEMPERATURE', '0.5'))
# the max character length of output. (not guaranteed)
maxn = int(env.get('MAXN', '240'))
# attempts to maximize scoring
attempts = int(env.get('ATTEMPTS', '-1'))
# if positive, maximum number of tokens to merge.
# if negative, minimum number of occurences to stop at.
merges = int(env.get('MERGES', '0'))
if attempts <= 0:
attempts = None
brain = PatternBrain(order=order, temperature=temperature)
tool = ATTTT(brain)
if state_fn:
lament('# loading')
try:
brain.load(state_fn, raw=False)
except FileNotFoundError:
lament('# no file to load. skipping')
pass
if brain and brain.new:
lament('# learning')
lines = open(fn).readlines()
brain.learn_all(lines, merges)
if brain and brain.new and state_fn:
lament('# saving')
brain.save(state_fn, raw=False)
lament('# replying')
for i in range(count):
#reply = tool.reply(maxn=maxn, raw=True, attempts=attempts)
#print('{:6.1f}\t{}'.format(reply[1], reply[0]))
print(tool.reply(maxn=maxn, attempts=attempts))
return 0
if __name__ == '__main__':
import sys
import os
pname = len(sys.argv) > 0 and sys.argv[0] or ''
args = len(sys.argv) > 1 and sys.argv[1:] or []
sys.exit(run(pname, args, os.environ))

189
atttt/basic.py Executable file
View File

@ -0,0 +1,189 @@
import math
import numpy as np
from misc import *
def normalize(counter):
v = counter.values()
s = float(sum(v))
m = float(max(v))
del v
return [(c, cnt/s, cnt/m) for c, cnt in counter.items()]
def normalize_sorted(counter):
# if the elements were unsorted,
# we couldn't use our lazy method (subtraction) of selecting tokens
# and temperature would correspond to arbitrary tokens
# instead of more/less common tokens.
return sorted(normalize(counter), key=lambda t: t[1], reverse=True)
# http://nbviewer.jupyter.org/gist/yoavg/d76121dfde2618422139
class Brain:
def __init__(self, padding, order=1, temperature=0.5):
self.order = order
self.temperature = temperature
self.padding = padding
self.reset()
def reset(self):
import collections as cool
# unnormalized
self._machine = cool.defaultdict(cool.Counter)
# normalized
self.machine = None
self.type = None
self.dirty = False
self.new = True
@property
def temperature(self):
return self._temperature
@temperature.setter
def temperature(self, value):
assert(0 < value < 1)
self._temperature = value
a = 1 - value * 2
# http://www.mathopenref.com/graphfunctions.html?fx=(a*x-x)/(2*a*x-a-1)&sg=f&sh=f&xh=1&xl=0&yh=1&yl=0&ah=1&al=-1&a=0.5
tweak = lambda x: (a * x - x) / (2 * a * x - a - 1)
self.random = lambda n: 1 - tweak(np.random.random(n))
def learn_all(self, items):
for item in items:
self.learn(item)
self.update()
def learn(self, item):
assert(self.padding)
if self.type is None and item is not None:
self.type = type(item)
if type(item) is not self.type:
raise Exception("that's no good")
if self.type == type("string"):
item = item.strip()
if len(item) == 0:
return
pad = self.helper(self.padding) * self.order
item = pad + item + pad
stop = len(item) - self.order
if stop > 0:
for i in range(stop):
history, newitem = item[i:i+self.order], item[i+self.order]
self._machine[history][newitem] += 1
self.dirty = True
def update(self):
if self.dirty and self._machine:
self.machine = {hist: normalize_sorted(items)
for hist, items in self._machine.items()}
self.dirty = False
def next(self, history):
history = history[-self.order:]
dist = self.machine.get(history, None)
if dist == None:
lament('warning: no value: {}'.format(history))
return None
x = self.random(1)
for c, cs, cm in dist:
x = x - cs
if x <= 0:
return c
# for overriding in subclasses
# in case the input tokens aren't strings (e.g. tuples)
def helper(self, v):
return v
def reply(self, item=None, maxn=1000):
assert(self.padding)
self.update()
history = self.helper(self.padding) * self.order
out = []
for i in range(maxn):
c = self.next(history)
if c.find(self.padding) != -1:
out.append(c.replace(self.padding, ''))
break
history = history[-self.order:] + self.helper(c)
out.append(c)
return out
def load(self, fn, raw=True):
import pickle
if type(fn) == type(''):
f = open(fn, 'rb')
else:
f = fn
d = pickle.load(f)
if d['order'] != self.order:
lament('warning: order mismatch. cancelling load.')
return
self.order = d['order']
if raw:
if not d.get('_machine'):
lament('warning: no _machine. cancelling load.')
return
self._machine = d['_machine']
self.dirty = True
self.update()
else:
if not d.get('machine'):
lament('warning: no machine. cancelling load.')
return
self.machine = d['machine']
self.new = False
if f != fn:
f.close()
def save(self, fn, raw=True):
import pickle
if type(fn) == type(''):
f = open(fn, 'wb')
else:
f = fn
d = {}
d['order'] = self.order
if raw:
d['_machine'] = self._machine
else:
d['machine'] = self.machine
pickle.dump(d, f)
if f != fn:
f.close()

11
atttt/misc.py Executable file
View File

@ -0,0 +1,11 @@
import sys
lament = lambda *args, **kwargs: print(*args, file=sys.stderr, **kwargs)
def die(*args, **kwargs):
# just for ad-hoc debugging really
lament(*args, **kwargs)
sys.exit(1)
__all__ = [o for o in locals() if type(o) != 'module' and not o.startswith('_')]

40
batch_encoding/enc.bat Executable file
View File

@ -0,0 +1,40 @@
@ECHO OFF
set input=%1
set output="%~p1%~n1.fdk.mp4"
REM any of these can be commented out
set trim=start=0:duration=41
set scale=640:480
set aspect=4:3
set volume=24dB
REM -b:v 1280k + -vbr 5 ~= 10MiB/s
set video_bitrate=-crf 23 -maxrate 2560k -bufsize 5120k
set x264=-profile:v high -preset slow
set fdk=-profile:a aac_he_v2 -vbr 5 -cutoff 18000
set general=-y -sn -dn -metadata = -map_chapters -1 -movflags +faststart
REM attack|right :release|right:inL/out|./...:knee:g:start:delay
set "limA=compand=0.0050|0.0050:0.0500|0.0500:-14/-14|0/-14:0.01:0:-14:0.0052"
set "limB=compand=0.0010|0.0010:0.0100|0.0100:-13/-13|0/-13:0.01:0:-13:0.0010"
set "limC=compand=0.0005|0.0005:0.0100|0.0100:-12/-12|0/-12:0.01:0:-12:0.0015"
set "limD=compand=0.0000|0.0000:0.0100|0.0100:-12/-12|0/-12"
set "louderizer=volume=0.25,aformat=channel_layouts=stereo,asetrate=705600,%limA%,%limB%,%limC%,asetrate=44100,%limD%,volume=3.98"
set sanity=lowpass=18000:4,highpass=40:4
set vf=
set af=
if NOT "%scale%"=="" set "vf=%vf%,scale=%scale%"
if NOT "%trim%"=="" set "vf=%vf%,trim=%trim%,setpts=PTS-STARTPTS"
if NOT "%trim%"=="" set "af=%af%,atrim=%trim%,asetpts=PTS-STARTPTS"
if NOT "%volume%"=="" set "af=%af%,asetrate=44100,volume=%volume%,%sanity%,%louderizer%"
if NOT "%vf%"=="" set vf=-vf "%vf:~1%"
if NOT "%af%"=="" set af=-af "%af:~1%"
if NOT "%aspect%"=="" set "aspect=-aspect %aspect%"
set video=%video_bitrate% -c:v libx264 %x264% -threads 0 %aspect% %vf%
set audio=-c:a libfdk_aac %fdk% -ar 44100 -ac 2 %af%
start /low /wait /b ffmpeg -i %input% %general% %video% %audio% %output%

View File

@ -0,0 +1,54 @@
#!/bin/zsh
in=(**/*.(otf|ttf))
size=16
outdir='rendered-again'
lipsum='Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est.'
mkdir -p $outdir
flags=(-q --font $size --pixels --justify --hinting=auto)
flags+=(--width=$((size*32)) --margin=$((size*4))\ $((size*5)))
cleanup() {
rm -f $tf $out
rm -rf $td
exit
}
trap cleanup INT
tf=$(mktemp)
td=$(mktemp -d)
<<<"$lipsum">$tf
export FONTCONFIG_PATH=$td
<<<'<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<dir>'$td'</dir>
<cachedir>'$td'</cachedir>
<include ignore_missing="yes">/etc/fonts/infinality/infinality.conf</include>
<config></config>
</fontconfig>
'>>$td/fonts.conf
for font in ${in[@]}; do
filename=${font:t}
stripped=${filename:r}
out=$outdir/$stripped.png
[ -s $out ] && continue
ok=1
cp $font $td
pango-view ${flags[@]} -o $out $tf 2>&1 | while read -r; do
<<<$REPLY >&2
ok=0
done
[ $ok -eq 1 ] && <<<$stripped || {
<<<$'\e[7m'$stripped$'\e[0m'
rm -f $out
}
rm -f $td/$filename $td/*.cache-4
done
cleanup

19
danbooru_atomizer/get.py Normal file
View File

@ -0,0 +1,19 @@
from retry import retry
import requests, requests.exceptions
class StatusCodeError(Exception):
def __init__(self, code, url):
self.code = code
self.url = url
def __str__(self):
return 'request for {} returned status code {}'.format(self.url, self.code)
@retry((requests.exceptions.ConnectionError, StatusCodeError, ValueError), tries=6, wait=300)
def get(uri, json=False):
r = requests.get(uri)
if r.status_code != 200:
raise StatusCodeError(r.status_code, uri)
if json:
return r.json()
return r

View File

@ -0,0 +1,17 @@
# so damn useful it deserved its own file
import time
def retry(Exceptions, tries=10, wait=1):
if type(Exceptions) == Exception:
Exceptions = (Exceptions,)
def retryer(f):
def deco(*args, **kwargs):
for i in range(tries - 1):
try:
return f(*args, **kwargs)
except Exceptions:
time.sleep(wait)
return f(*args, **kwargs)
return deco
return retryer

124
danbooru_atomizer/run.py Executable file
View File

@ -0,0 +1,124 @@
#!/usr/bin/env python3
import sys
from urllib.parse import quote_plus
from xml.dom.minidom import parseString as parseXML
import datetime
from get import get
lament = lambda *args, **kwargs: print(*args, file=sys.stderr, **kwargs)
parseDate = lambda s: datetime.datetime.strptime(s+'+0000', '%Y-%m-%dT%H:%M:%SZ%z')
formatDate = lambda dt: dt.strftime('%FT%TZ')
# we only need a handful of mime types so we may as well inline them
mimes = {
'png': 'image/png',
'jpg': 'image/jpeg',
'gif': 'image/gif',
'swf': 'application/x-shockwave-flash',
}
class Untitled:
template = """
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xmlns="http://www.w3.org/2005/Atom">
</feed>
""".strip()
def __init__(self, urls, max_entries=512):
self.urls = urls
self.max_entries = max_entries
self.title = 'Danbooru - Personalized Feed'
self.items = []
def parse(self, q):
url = self.urls['atom'] + quote_plus(q)
xml = get(url).text
dom = parseXML(xml)
entries = dom.getElementsByTagName('entry')
for entry in entries:
getText = lambda tn: entry.getElementsByTagName(tn)[0].firstChild.nodeValue
item = {
'title': getText('title'),
'id': getText('id').split('/')[-1],
'updated': getText('updated'),
'summary': getText('summary'),
'img': entry.getElementsByTagName('img')[0].getAttribute('src'),
'query': q,
}
item['updated_unix'] = parseDate(item['updated']).timestamp()
self.items.append(item)
def generate(self):
self.items = sorted(self.items, key=lambda d: d['updated_unix'], reverse=True)
self.items = self.items[:self.max_entries]
now = formatDate(datetime.datetime.utcnow())
dom = parseXML(self.template)
feed = dom.firstChild
def newText(entity_name, text):
e = dom.createElement(entity_name)
e.appendChild(dom.createTextNode(text))
return e
def newLink(**kwargs):
link = dom.createElement('link')
for k, v in kwargs.items():
link.setAttribute(k, v)
return link
feed.appendChild(newText('title', self.title))
feed.appendChild(newLink(href=self.urls['feed'], rel='self'))
feed.appendChild(newText('id', self.urls['feed']))
feed.appendChild(newText('updated', now))
for item in self.items:
ext = item['img'].split('.')[-1]
mime = mimes[ext]
alt = self.urls['post'] + item['id']
query_quote = quote_plus(item['query'])
entry = dom.createElement('entry')
entry.appendChild(newText('title', item['title']))
entry.appendChild(newLink(href=alt, rel="alternate"))
entry.appendChild(newText('id', alt))
entry.appendChild(newText('published', item['updated']))
entry.appendChild(newText('updated', item['updated']))
entry.appendChild(newLink(rel="enclosure", type=mime, href=item['img']))
entry.appendChild(newText('summary', item['summary']))
author = dom.createElement('author')
author.appendChild(newText('name', item['query']))
author.appendChild(newText('uri', self.urls['query'] + query_quote))
entry.appendChild(author)
feed.appendChild(entry)
return dom.toxml()
urls = {
'atom': 'https://danbooru.donmai.us/posts.atom?limit=48&tags=',
'post': 'https://danbooru.donmai.us/posts/',
'img': 'https://danbooru.donmai.us/ssd/data/preview/',
'query': 'https://danbooru.donmai.us/posts?tags=',
}
if __name__ == '__main__':
urls['feed'] = sys.argv[1]
untitled = Untitled(urls)
queries = sys.stdin.read()
for q in queries.splitlines():
lament(q)
untitled.parse(q)
print(untitled.generate())

44
danbooru_scrape/boorufind Executable file
View File

@ -0,0 +1,44 @@
#!/bin/zsh
q=$1
url='http://danbooru.donmai.us/posts.xml'
de=--data-urlencode
curl=(curl -sS -m 32 --connect-timeout 8 --retry 3 --retry-delay 1)
die() {
echo -E "$@" >&2
exit 1
}
push() {
if [ -z $md5 ] || [ -z $ext ] || [ -z $size ]; then
print missing value >&2
return
fi
local fn=$md5.$ext
local s="$(find . -name $fn -print -quit 2>/dev/null)"
[ -n "$s" ] && print $s || print \#$fn
md5= ext= size=
}
page=1
while; do
once=0
${curl[@]} -G -d limit=100 -d page=$page $de tags=$q $url \
| xml2 2>/dev/null \
| while IFS== read -r tree val; do
[ $tree = /posts/post ] && push && continue
[ ${tree:0:12} = /posts/post/ ] && tree=${tree:12} || continue
once=1
case $tree in
(md5) md5=$val ;;
(file-ext) ext=$val ;;
(file-size) size=$val ;;
esac
done
[[ ${PIPESTATUS[1]} -eq 0 ]] || die "curl failed on page $page"
[[ $once -eq 0 ]] && exit 0 # no posts left, well done
push
let page++
done

40
danbooru_scrape/boorugrab Executable file
View File

@ -0,0 +1,40 @@
#!/bin/zsh
dir=$1
url='http://danbooru.donmai.us/data/'
curl=(curl -sS -m 900 --connect-timeout 8 --retry 3 --retry-delay 1)
cleanup() {
[ -e $dir/$md5e ] && {
rm -f $dir/$md5e
echo "\e[F\e[K$md5e - canceled"
}
exit 1
}
trap cleanup INT
mkdir -p $dir
while read -r; do
if [[ ${REPLY[1]} = "#" ]]; then
md5e=${REPLY:1}
echo "$md5e - downloading..."
${curl[@]} $url$md5e > $dir/$md5e || {
rm -f $dir/$md5e
echo "\e[F\e[K$md5e - failed"
}
echo "\e[F\e[K$md5e - downloaded!"
else
md5e=${REPLY##*/}
[ $REPLY -ef $dir/$md5e ] && {
echo "$md5e - skipping (ef)"
continue
}
[ -s $dir/$md5e ] && {
echo "$md5e - skipping (s)"
continue
}
echo "$md5e - copying..."
cp $REPLY $dir
echo "\e[F\e[K$md5e - copied!"
fi
done

View File

@ -0,0 +1,26 @@
### how to use:
these gameshark cheats will override the default save file with the debug save file.
create a new file on the file select screen and it should create a debug save.
note: this is probably the same as using map select.
### MM (J) 1.0
```
D1142850 0C05
D1142852 053D
81142852 0573
```
### MM (J) 1.1
```
D11428B0 0C05
D11428B2 0555
811428B2 058B
```
### MM (U)
```
D1146AC8 0C05
D1146ACA 1224
81146ACA 125A
```

117
desmos/desmos.md Normal file
View File

@ -0,0 +1,117 @@
## stuff i've plotted in desmos
in some order idk
### [circumcircle](https://www.desmos.com/calculator/zaf29i2xrk)
drag around the three points to see how their circumcircle is affected.
sorry, i never finished simplifying the math here.
### [parabola arc length](https://www.desmos.com/calculator/hrokhmphrf)
not closed form, sorry. drag the points around like it's a jumprope.
### ["diode"](https://www.desmos.com/calculator/vappug98ky)
i didn't name this function; i have no idea if or how it relates to a diode.
the red line (the line of interest) is actually just the 12th order smoothstep.
btw, you can find smoothstep coefficients for any order
by the triangular table of this integer sequence:
[OEIS A091811](http://oeis.org/A091811)
[punching it into wolfram][smoothie] (try changing n)
[smoothie]: https://www.wolframalpha.com/input/?i=ReplaceAll%5BBinomial%5Bn+%2B+k+-+2,+k+-+1%5D+Binomial%5B2+n+-+1,+n+-+k%5D,+%7Bk+-%3E+Range%5B1,n%5D%7D%5D+where+n+%3D+12
### [smoothstep alternatives](https://www.desmos.com/calculator/ibigyiumzb)
refer to shadertoy:
* [Inverse Smoothstep](https://www.shadertoy.com/view/MsSBRh)
* [gain()](https://www.shadertoy.com/view/ldBfR1)
* [Alt smoothstep](https://www.shadertoy.com/view/lsBfz1)
### [tanh approximation](https://www.desmos.com/calculator/k3jzkhmoid)
approximating tanh by taking its continued fraction form and (externally) optimizing the constants.
probably not useful.
### [TanhTest lolremez approx](https://www.desmos.com/calculator/carcltkgem)
a polynomial approximation of a scaled tanh function
made with [the excellent lolremez](https://github.com/samhocevar/lolremez).
### [e^(-2/x) approximations](https://www.desmos.com/calculator/q8jtxgnmlh)
i forget where i read about this but basically you can
approximate `e^(-2/x)` by `(x-1)/(x+1)` when x is large.
the black line shows the absolute error.
### [compressor gain](https://www.desmos.com/calculator/rizrdxsrbg)
trying to design a cheap-to-compute gain curve for a dynamic range compressor.
this thing uses just one division.
iirc it didn't wind up being all that useful.
both axes are in power decibels. note that it acts as a gate under -70 dB.
### [compressor approx stuff](https://www.desmos.com/calculator/5wbup88lev)
trying to compute abs(x) by polynomials, or something. weird stuff.
### [doom turnspeeds](https://www.desmos.com/calculator/zfscpmsdbk)
i used this to help design turnspeed smoothing in [my zdoom patch.](https://eaguru.guru/t/zdoom_smoothstep.diff)
*note to self: i think i have a more recent version of that patch lying around somewhere.*
### [Biweight Funtimes](https://www.desmos.com/calculator/mlnqmf6ogp)
messing around with a variations of Tukey's Biweight?
i think the only reason i kept this around was 'cause it's fun to watch animated.
### [sincmod](https://www.desmos.com/calculator/lqgl4o0naz)
something something windowing functions i dunno.
### [SGDR](https://www.desmos.com/calculator/hlgqmyswy2)
simple case of a [SGDR](https://arxiv.org/abs/1608.03983) curve.
i just kept this around 'cause the logarithmic plot complements
the linear plot in a kinda cool way.
### [sqr/abs generalization](https://www.desmos.com/calculator/fagjg9vuz7)
self-explanitory.
### [SELU](https://www.desmos.com/calculator/bqu264oprw)
[it's SELU!](https://arxiv.org/abs/1706.02515)
### [GELU](https://www.desmos.com/calculator/ydzgtccsld)
[GELU activation](https://arxiv.org/abs/1606.08415)
alongside an approximation i lifted off reddit or someplace.
### [erf cdf approximations](https://www.desmos.com/calculator/22r29cude2)
approximate gaussian stuff, with inversions and integrals.
### [squish](https://www.desmos.com/calculator/1qdx4ovpb6)
possibly useful functions for squishing values.
mostly based on sigmoidal functions.
### [sin LUT approx 2](https://www.desmos.com/calculator/zdbgkxadns)
approximating a quadrant of sin(x) with a polynomial and a LUT.
### [circle calculus](https://www.desmos.com/calculator/yd5hyagaoj)
some fun curves relating to unit circles.
### [archimedes spiral](https://www.desmos.com/calculator/63vi3wnqug)
it's just Archimedes' Spiral, plus some related functions.

View File

@ -0,0 +1,82 @@
# dead simple dictionary attack for fixed-length passwords.
# this is far from optimal.
from collections import defaultdict
import argparse
import sys
import random
program = sys.argv[0]
args = sys.argv[1:]
parser = argparse.ArgumentParser(
description="attack: produce fixed-length strings from dictionaries")
parser.add_argument(
'length', type=int,
help="the length of the strings to output")
parser.add_argument(
'path', metavar='text-file', nargs='+',
help="a dictionary file containing one string per line")
parser.add_argument(
'-n', metavar='limit', type=float, default='inf',
help="output this many strings and exit")
parser.add_argument(
'-s', metavar='seed', type=int, default=None,
help="use as a seed for the random number generator")
a = parser.parse_args(args)
seed = a.s
limit = a.n
paths = a.path
fixed_length = a.length
del a
lines = []
for path in paths:
with open(path, "r") as f:
for line in f:
lines.append(line.strip('\r\n'))
nlines = defaultdict(lambda: [])
ncount = defaultdict(lambda: 0)
for line in lines:
length = len(line)
if length == 0 or length > fixed_length:
continue
nlines[length].append(line)
ncount[length] += 1
del lines
if seed is not None:
random.seed(seed)
lengths = list(ncount.keys())
length_weights = list(ncount.values())
i = 0
while i < limit:
s = ''
new_weights = length_weights.copy()
while len(s) < fixed_length:
if len(s) > 0:
for j, length in enumerate(lengths):
if len(s) + length > fixed_length:
new_weights[j] = 0
if sum(new_weights) == 0:
s = ''
new_weights = length_weights.copy()
continue
chosen_length = random.choices(lengths, new_weights)[0]
s += random.choice(nlines[chosen_length])
try:
print(s)
except OSError:
# pipe closed.
break
i += 1

View File

@ -0,0 +1,4 @@
40c40
< 00000270: a040 009f 3c0e 8080 25ce 32f8 240f 2410 .@..<...%.2.$.$.
---
> 00000270: a040 009f 3c0e 8083 25ce df6c 240f 00e0 .@..<...%..l$...

View File

@ -0,0 +1,10 @@
1011,1012c1011,1012
< 00003f20: 910f 09f8 a100 09f9 3c01 8085 a02f 49ce ........<..../I.
< 00003f30: 8d18 07a0 8fa7 0048 3c01 8085 0018 c82b .......H<......+
---
> 00003f20: 340f 0002 a100 09f9 3c01 8085 a02f 49ce 4.......<..../I.
> 00003f30: 3418 0014 8fa7 0048 3c01 8085 0018 c82b 4......H<......+
1057c1057
< 00004200: 0000 0000 0c03 483d 0000 0000 1040 fff9 ......H=.....@..
---
> 00004200: 0000 0000 0c03 483d 0000 0000 0000 0000 ......H=........

10
dnm/cmd.sh Normal file
View File

@ -0,0 +1,10 @@
# the patch for file 0017 (N64 logo overlay) tells it to load the Famicom overlay next
# instead of the normal gameplay overlay. it's specified by its VRAM address and context size.
# these values were taken from the table loaded at 80106E20 (each element is 0x30 long).
diff <(xxd e106dff*/0017*) <(xxd dnm-instant-famicom/0017*) > '0017 V00744020.xxd.patch'
# the patch for file 0023 (Famicom emulator overlay) tells it to load some constant values
# instead of loading them from a setup we never did.
# change the "340f 0002" instruction to select a game. (0001 to 0007)
# it also NOPs out of a loop that expects a flag to be set.
diff <(xxd e106dff*/0023*) <(xxd dnm-instant-famicom/0023*) > '0023 V007492E0.xxd.patch'

238
ds1/extract_params.py Normal file
View File

@ -0,0 +1,238 @@
#!/usr/bin/env python3
import struct
import csv
import sys
from collections import namedtuple
Def = namedtuple("Def", "name desc notes typedef ctype pytype length bits default min max step".split(" "))
typemap = dict(
s8="b",
u8="B",
s16="h",
u16="H",
s32="i",
u32="I",
s64="q",
u64="Q",
f32="f",
f64="d",
dummy8="B", # note the array length typically alongside them.
)
def U(fmt, *args, **kwargs):
return struct.unpack(E + fmt, *args, **kwargs)
def trunc(s):
if b"\0" in s:
return s[:s.find(b"\0")]
return s
def readcstr(f, offset=None):
if offset is not None:
here = f.tell()
f.seek(offset)
raw = b""
while True:
buf = f.read(16)
if len(buf) == 0:
break
if b"\0" in buf:
raw += buf[:buf.find(b"\0")]
break
else:
raw += buf
if offset is not None:
f.seek(here)
return raw
def read_paramdef(f):
defs = []
filesize, unk1, unk2, count, unk3 = U("IHHHH", f.read(12))
paramdef_title = f.read(32)
unk4, unk5 = U("HH", f.read(4))
for i in range(count):
# TODO: rename a lot of the variables here.
desc = f.read(64)
typename = f.read(8)
printformat = f.read(8)
default, min_, max_, step = U("ffff", f.read(16))
unk6, unk7, notes_offset = U("IIi", f.read(12))
full_typename = f.read(32)
name = f.read(32)
# ID? it seems to increase by 100 sometimes.
(unk8,) = U("I", f.read(4))
desc_str = trunc(desc).decode("shift-jis", errors="replace")
type_str = trunc(full_typename).decode("shift-jis")
name_str = trunc(name).decode("shift-jis")
length = None
if "[" in name_str and "]" in name_str:
length = int(name_str.split("[")[1].split("]")[0])
bits = None
if ":" in name_str:
bits = int(name_str.split(":")[1])
if type_str in typemap:
type_ = typemap[type_str]
else:
underlying_type = trunc(typename).decode()
type_ = typemap[underlying_type]
if notes_offset in (0, -1):
notes_str = ""
else:
notes = readcstr(f, notes_offset)
notes_str = notes.decode("shift-jis", errors="replace")
d = Def(name_str, desc_str, notes_str,
type_str, trunc(typename).decode(), type_, length, bits,
default, min_, max_, step)
defs.append(d)
return paramdef_title, defs
def read_param(f, paramdef_title=None):
entries = []
filesize, unk1, unk2, unk3, count = U("IHHHH", f.read(12))
param_title = f.read(32)
if paramdef_title is not None:
if trunc(paramdef_title) != trunc(param_title):
raise Exception(
"that's the wrong paramdef for this param file!" +
f"\nexpected: {paramedef_title}\nretrieved: {param_title}")
unk4, unk5 = U("HH", f.read(4))
here = f.tell()
for i in range(count):
f.seek(here)
entry_id, param_offset, notes_offset = U("iii", f.read(12))
here = f.tell()
f.seek(param_offset)
entry = [entry_id]
prev_type = None
for d in defs:
is_simple = d.length is None and d.bits is None
if d.pytype != prev_type or d.bits is None:
buf, bufbits = 0, 0
# print(f"{d.pytype:24} {f.tell():X}")
size = struct.calcsize(d.pytype)
if is_simple:
(datum,) = U(d.pytype, f.read(size))
elif d.length is not None:
# this only seems to be used for padding, so we can skip it.
assert d.ctype == "dummy8" # let's assert that though.
datum = f.read(d.length * size)
elif d.bits is not None:
if bufbits == 0 or bufbits < d.bits:
assert d.pytype not in ("f", "d")
(buf,) = U(d.pytype.upper(), f.read(size))
bufbits = size * 8
mask = (1 << d.bits) - 1
if big_endian:
datum = (buf >> (size * 8 - d.bits)) & mask
buf <<= d.bits
else:
datum = buf & mask
buf >>= d.bits
bufbits -= d.bits
else:
raise Exception("unhandled definition: " + name)
if d.ctype != "dummy8":
entry.append(datum)
prev_type = d.pytype
if notes_offset in (0, -1):
notes_str = ""
else:
notes = readcstr(f, notes_offset)
notes_str = notes.decode("shift-jis", errors="replace")
entry.append(notes_str)
entries.append(entry)
return param_title, entries
if __name__ == "__main__":
if len(sys.argv) == 6:
fp1 = sys.argv[1]
fp2 = sys.argv[2]
fpo = sys.argv[3]
fph = sys.argv[4]
# ew, nasty global:
big_endian = sys.argv[5] == "big"
elif len(sys.argv) == 4:
fp1 = sys.argv[1]
fp2 = None
fpo = None
fph = sys.argv[2]
# ew, nasty global:
big_endian = sys.argv[3] == "big"
else:
print("usage:")
print(" python3 extract_params.py {paramdef in} {param in} {param out} {paramdef out} [big]")
print(" python3 extract_params.py {paramdef in} {paramdef out} [big]")
sys.exit(1)
# ew, nasty global:
E = ">" if big_endian else "<"
with open(fp1, "rb") as f:
paramdef_title, defs = read_paramdef(f)
if fp2 is not None and fph is not None:
header = ["entryId"]
for d in defs:
name = d.name
if ":" in name:
name = name.split(":")[0]
if "[" in name:
name = name.split("[")[0]
if d.ctype == "dummy8":
# print("skipping", name)
continue
header.append(name)
header.append("notes")
with open(fp2, "rb") as f:
param_title, entries = read_param(f, paramdef_title)
with open(fpo, "w", newline="", encoding="utf-8") as f:
cw = csv.writer(f, dialect="excel-tab")
cw.writerow(header)
for entry in entries:
cw.writerow(entry)
with open(fph, "w", newline="", encoding="utf-8") as f:
cw = csv.writer(f, dialect="excel-tab")
cw.writerow(Def._fields)
for d in defs:
cw.writerow(d)

115
ds1/fmg_flatten.py Normal file
View File

@ -0,0 +1,115 @@
from struct import unpack as U
import csv
import sys
big_endian = False
def readint(f):
if big_endian:
return U(">i", f.read(4))[0]
else:
return U("<i", f.read(4))[0]
def dumpy(f, mapping):
f.seek(0, 2)
fsize = f.tell()
f.seek(0, 0)
something = readint(f)
assert something == 0x10000, something
size = readint(f)
assert size == fsize, size
unk = readint(f)
if big_endian:
assert unk == 0x01FF0000, unk
else:
assert unk == 1, unk
count = readint(f)
offset_count = readint(f)
somecount1 = readint(f) # still unknown!
something = readint(f) # still unknown!
starts = {}
lengths = {}
ids = []
cumulative_length = 0
previous_end = None
for i in range(count):
if big_endian:
a, b, c = U(">iii", f.read(4 * 3))
else:
a, b, c = U("<iii", f.read(4 * 3))
#print(f"{a:10}: {b:10} to {c:10}")
length = c - b + 1
assert a not in starts
if previous_end is not None:
assert a == previous_end
starts[a] = b
lengths[a] = length
for i in range(length):
ids.append(b + i)
cumulative_length += length
previous_end = a + length
assert offset_count == cumulative_length
offsets = []
for i in range(offset_count):
offsets.append(readint(f))
for id, offset in zip(ids, offsets):
if offset == 0:
#mapping[id] = ""
continue
f.seek(offset)
string = ""
while True:
char = f.read(2)
if char == b"\0\0":
break
if big_endian:
string += char.decode("utf-16be")
else:
string += char.decode("utf-16le")
mapping[id] = string
fp = sys.argv[1]
fpo = sys.argv[2]
if len(sys.argv) > 3:
big_endian = sys.argv[3] == "big"
en_mapping = {}
jp_mapping = {}
with open(fp, "rb") as f:
dumpy(f, en_mapping)
with open(fp.replace("ENGLISH", "JAPANESE"), "rb") as f:
dumpy(f, jp_mapping)
from collections import defaultdict
mappings = defaultdict(lambda: ["", ""])
for k, v in en_mapping.items():
mappings[k][0] = v
for k, v in jp_mapping.items():
mappings[k][1] = v
with open(fpo, "w", newline="", encoding="utf-8") as f:
cw = csv.writer(f, dialect="excel-tab")
for k in sorted(mappings.keys()):
en_v, jp_v = mappings[k]
cw.writerow([k, en_v, jp_v])

27
ds1/param_notes.py Normal file
View File

@ -0,0 +1,27 @@
from sys import argv
from struct import unpack as U
big_endian = False
if len(argv) > 2:
big_endian = argv[2] == "big"
with open(argv[1], "rb") as f:
f.seek(0xA)
if big_endian:
count = U(">h", f.read(2))[0]
else:
count = U("<h", f.read(2))[0]
for i in range(count):
f.seek(0x30 + i * 3 * 4)
if big_endian:
entryID, paramAddr, infoAddr = U(">iii", f.read(3 * 4))
else:
entryID, paramAddr, infoAddr = U("<iii", f.read(3 * 4))
if infoAddr not in (0, -1):
f.seek(infoAddr)
string = f.read()
string = string[:string.index(b"\0")]
print(entryID, string.decode("shift-jis", errors="replace"), sep="\t")

22
dwarf_font/dwarf.lua Normal file
View File

@ -0,0 +1,22 @@
-- This is free and unencumbered software released into the public domain.
-- For more information, please refer to <http://unlicense.org/>
local charset = ("\
\x00\
§\
!\"#$%&'()*+,-./\
0123456789:;<=>?\
@ABCDEFGHIJKLMNO\
PQRSTUVWXYZ[\\]^_\
`abcdefghijklmno\
pqrstuvwxyz{|}~\
ÇüéâäàåçêëèïîìÄÅ\
ÉæÆôöòûùÿÖÜ¢£¥ƒ\
áíóúñѪº¿¬½¼¡«»\
\
\
\
αßΓπΣσµτΦΘΩδφε\
±÷°·²<EFBFBD>"):gsub('\n', '')
return love.graphics.newImageFont("dwarf.png", charset)

BIN
dwarf_font/dwarf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,25 @@
local mt = getmetatable(_G)
if mt == nil then
mt = {}
setmetatable(_G, mt)
end
mt.__declared = {}
function mt.__newindex(t, n, v)
if not mt.__declared[n] then
local info = debug.getinfo(2, "S")
if info and info.what ~= "main" and info.what ~= "C" then
error("cannot assign undeclared global '" .. tostring(n) .. "'", 2)
end
mt.__declared[n] = true
end
rawset(t, n, v)
end
function mt.__index(t, n)
if not mt.__declared[n] then
local info = debug.getinfo(2, "S")
if info and info.what ~= "main" and info.what ~= "C" then
error("cannot use undeclared global '" .. tostring(n) .. "'", 2)
end
end
end

View File

@ -0,0 +1,239 @@
import numpy as np
import matplotlib.pyplot as plt
import scipy.signal as sig
# optionally, use an alternate style for plotting.
# this reconfigures colors, fonts, padding, etc.
# i personally prefer the look of ggplot, but the contrast is generally worse.
plt.style.use('ggplot')
# create log-spaced points from 20 to 20480 Hz.
# we'll compute our y values using these later.
# increasing precision will result in a smoother plot.
precision = 4096
xs = np.arange(0, precision) / precision
xs = 20 * 1024**xs
# decide on a sample-rate and a center-frequency (the -3dB point).
fs = 44100
fc = 1000
def response_setup(ax, ymin=-24, ymax=24, major_ticks_every=6, minor_ticks_split=2):
"""configure axes for magnitude plotting within the range of human hearing."""
ax.set_xlim(20, 20000)
ax.set_ylim(ymin, ymax)
ax.grid(True, 'both') # enable major and minor gridlines on both axes
ax.set_xlabel('frequency (Hz)')
ax.set_ylabel('magnitude (dB)')
# manually set y tick positions.
# this is usually better than relying on matplotlib to choose good values.
from matplotlib import ticker
ax.set_yticks(tuple(range(ymin, ymax + 1, major_ticks_every)))
minor_ticker = ticker.AutoMinorLocator(minor_ticks_split)
ax.yaxis.set_minor_locator(minor_ticker)
def modified_bilinear(b, a, w0):
"""
the modified bilinear transform defers specifying the center frequency
of an analog filter to the conversion to a digital filter:
this s-plane to z-plane transformation.
it also compensates for the frequency-warping effect.
b and a are the normalized numerator and denominator coefficients (respectively)
of the polynomial in the s-plane, and w0 is the angular center frequency.
effectively, w0 = 2 * pi * fc / fs.
the modified bilinear transform is specified by RBJ [1] as:
s := (1 - z^-1) / (1 + z^-1) / tan(w0 / 2)
as opposed to the traditional bilinear transform:
s := (1 - z^-1) / (1 + z^-1)
[1]: http://musicdsp.org/files/Audio-EQ-Cookbook.txt
"""
# in my opinion, this is a more convenient way of creating digital filters.
# scipy doesn't implement the modified bilinear transform,
# so i'll just write the expanded form for second-order filters here.
# TODO: include the equation that expanded/simplified to this
# use padding to allow for first-order filters.
# untested
if len(b) == 2:
b = (b[0], b[1], 0)
if len(a) == 2:
a = (a[0], a[1], 0)
assert len(b) == 3 and len(a) == 3, "unsupported order of filter"
cw = np.cos(w0)
sw = np.sin(w0)
zb = np.array((
b[0]*(1 - cw) + b[2]*(1 + cw) - b[1]*sw,
2*(b[0]*(1 - cw) - b[2]*(1 + cw)),
b[0]*(1 - cw) + b[2]*(1 + cw) + b[1]*sw,
))
za = np.array((
a[0]*(1 - cw) + a[2]*(1 + cw) - a[1]*sw,
2*(a[0]*(1 - cw) - a[2]*(1 + cw)),
a[0]*(1 - cw) + a[2]*(1 + cw) + a[1]*sw,
))
return zb, za
def digital_magnitude(xs, cascade, fc, fs, decibels=True):
"""
compute the digital magnitude response
of an analog SOS filter (cascade)
at the points given at xs
and the center frequency fc
for the sampling rate of fs.
"""
to_magnitude_decibels = lambda x: np.real(np.log10(np.abs(x)**2) * 10)
# compute angular frequencies
ws = 2 * np.pi * xs / fs
w0 = 2 * np.pi * fc / fs
digital_cascade = [modified_bilinear(*ba, w0=w0) for ba in cascade]
# we don't need the first result freqz returns
# because it'll just be the worN we're passing.
responses = [sig.freqz(*ba, worN=ws)[1] for ba in digital_cascade]
if decibels:
mags = [to_magnitude_decibels(response) for response in responses]
mags = np.array(mags)
composite = np.sum(mags, axis=0)
else:
# untested
mags = [np.abs(response) for response in responses]
mags = np.array(mags)
composite = np.prod(mags, axis=0)
# we could also compute phase using (untested):
# -np.arctan2(np.imag(response), np.real(response))
return composite, mags
# compute the butterworth coefficients for later.
# we'll request them in SOS format: second-order sections.
# that means we'll get an array of second-order filters
# that combine to produce one higher-order filter.
# note that we specify 1 as the frequency; we'll give the actual frequency
# later, when we transform it to a digital filter.
butterworth_2 = sig.butter(N=2, Wn=1, analog=True, output='sos')
butterworth_6 = sig.butter(N=6, Wn=1, analog=True, output='sos')
# scipy returns the b and a coefficients in a flat array,
# but we need them separate.
butterworth_2 = butterworth_2.reshape(-1, 2, 3)
butterworth_6 = butterworth_6.reshape(-1, 2, 3)
butterworth_2_times3 = butterworth_2.repeat(3, axis=0)
# set up our first plot.
fig, ax = plt.subplots()
response_setup(ax, -30, 3)
ax.set_title("comparison of digital butterworth filters (fc=1kHz, fs=44.1kHz)")
# compute the magnitude of the filters at our x positions.
# we don't want the individual magnitudes here,
# so assign them to the dummy variable _.
ys1, _ = digital_magnitude(xs, butterworth_2, fc, fs)
ys2, _ = digital_magnitude(xs, butterworth_2_times3, fc, fs)
ys3, _ = digital_magnitude(xs, butterworth_6, fc, fs)
# our x axis is frequency, so it should be spaced logarithmically.
# our y axis is decibels, which is already a logarithmic unit.
# thus, semilogx is the plotting function to use here.
ax.semilogx(xs, ys1, label="second order")
ax.semilogx(xs, ys2, label="second order, three times")
ax.semilogx(xs, ys3, label="sixth order")
ax.legend() # display the legend, given by labels when plotting.
fig.show()
fig.savefig('butterworth_comparison.png')
# set up our second plot.
fig, ax = plt.subplots()
response_setup(ax, -12, 12)
ax.set_title("digital butterworth decomposition (fc=1kHz, fs=44.1kHz)")
cascade = butterworth_6
composite, mags = digital_magnitude(xs, cascade, fc, fs)
for i, mag in enumerate(mags):
# Q has an inverse relation to the 1st-degree coefficient
# of the analog filter's denominator, so we can compute it that way.
Q = 1 / cascade[i][1][1]
ax.semilogx(xs, mag, label="second order (Q = {:.6f})".format(Q))
ax.semilogx(xs, composite, label="composite")
ax.legend()
fig.show()
fig.savefig('butterworth_composite.png')
# as a bonus, here's how the modified bilinear transform is especially useful:
# we can specify all the basic second-order analog filter types
# with minimal code!
lowpass2 = lambda A, Q: ((1, 0, 0),
(1, 1/Q, 1))
highpass2 = lambda A, Q: ((0, 0, 1),
(1, 1/Q, 1))
allpass2 = lambda A, Q: ((1, 1/Q, 1),
(1, 1/Q, 1))
# peaking filters are also (perhaps for the better) known as bell filters.
peaking2 = lambda A, Q: ((1, A/Q, 1),
(1, 1/A/Q, 1))
# TODO: add classical "analog" peaking filter, where bandwidth and amplitude are interlinked
# there are two common bandpass variants:
# one where the bandwidth and amplitude are interlinked,
bandpass2a = lambda A, Q: ((0, -A/Q, 0),
(1, 1/A/Q, 1))
# and one where they aren't.
bandpass2b = lambda A, Q: ((0,-A*A/Q, 0),
(1, 1/Q, 1))
notch2 = lambda A, Q: ((1, 0, 1),
(1, 1/Q, 1))
lowshelf2 = lambda A, Q: (( A, np.sqrt(A)/Q, 1),
(1/A, 1/np.sqrt(A)/Q, 1))
highshelf2 = lambda A, Q: ((1, np.sqrt(A)/Q, A),
(1, 1/np.sqrt(A)/Q, 1/A))
# here's an example of how to plot these.
# we specify the gain in decibels and bandwidth in octaves,
# then convert these to A and Q.
fig, ax = plt.subplots()
response_setup(ax, -30, 30)
invsqrt2 = 1 / np.sqrt(2) # for bandwidth <-> Q calculation
# note that A is squared, so the decibel math here is a little different.
designer = lambda f, decibels, bandwidth: f(10**(decibels / 40.), invsqrt2 / bandwidth)
ax.set_title("example of interlinked bandpass amplitude/bandwidth")
cascade = [
designer(bandpass2a, 0, 2),
designer(bandpass2a, -18, 2),
designer(bandpass2a, 18, 2),
]
composite, mags = digital_magnitude(xs, cascade, fc, fs)
for mag in mags:
ax.semilogx(xs, mag)
#ax.semilogx(xs, composite)
#ax.legend()
fig.show()
fig.savefig('filter_example.png')

32
gs/gs.lua Normal file
View File

@ -0,0 +1,32 @@
package.path = package.path..";./?/init.lua"
local lips = require "lips"
function make_gameshark_writer()
local buff = {}
local max = -1
return function(pos, b)
if pos then
pos = pos % 0x80000000
buff[pos] = b
if pos > max then
max = pos
end
elseif max >= 0 then
for i=0, max, 2 do
local a = buff[i+0]
local b = buff[i+1]
if a and b then
print(('81%06X %s'):format(i, a..b))
elseif a then
print(('80%06X 00%s'):format(i, a))
elseif b then
print(('80%06X 00%s'):format(i + 1, b))
end
end
end
end
end
local gs = make_gameshark_writer()
lips('test.asm', gs, {unsafe=true, offset=0x80000000})
gs()

76
gs/test.asm Normal file
View File

@ -0,0 +1,76 @@
;Free Inventory Movement
;This will make it so that you can place the cursor on empty
;Cursor Movement:
;LEFT
.org 0x803D8B88
NOP
;RIGHT
.org 0x803D8C68
NOP
;FROM L
.org 0x803D8DD0
.skip 8 0 ;NOP the next 2 commands
;FROM R
.org 0x803D8EA8
.skip 8 0 ;NOP the next 2 commands
;UP
.org 0x803D8FAC
NOP
;DOWN
.org 0x803D9004
NOP
;Hide Item 0xFF name (on select)
.org 0x803E2168
lhu a1, 0x023C(v0) ;
lhu v1, 0x002A(sp) ;Already there
slti at, a1, 0x00FF ;
beql at, zero, 0x803E2338
;Hide Item 0xFF name (loop)
.org 0x803E1370
slti at, t7, 0x00FF ;Set at to 1 if you don't have item 0xFF or a blank slot selected
.org 0x803E1378
beql at, zero, 0x803E17E8 ;Branch if you have item 0xFF or a blank slot selected
;Set Cursor to Default on 0xFF
.org 0x803D9058
slti t5, a0, 0x00FF ;t5 = 1 if you have selected an item
sll t5, t5, 0x0002 ;t5 = t5<<0x2
NOP ;
;Make Item 0xFF unequippable (2.0)
.org 0x803D9140
addiu at, zero, 0x00FF ;at = 0xFF (Blank)
lw t6, 0x0048(sp) ;t6 = Item ID
beq at, t6, 0x803D92FC ;Branch past error sound if item is 0xFF (Blank)
addiu at, zero, 0x0009 ;at = 0x0009 (Usable by both)
beq v0, at, + ;Branch if usable by child and adult
lw t5, 0x0004(s4) ;t5 = Age
bne v0, t5, 0x803D92D0 ;Branch to error sound if wrong age
+:
NOP ;Remove command
;Hide text when on empty slot
.org 0x803E1B80
lhu v0, 0x023E(t0) ;v0 = Selected Item ID
slti v0, v0, 0x00FF ;v0 = 1 if Selected Item ID is less than 0xFF
beq v0, zero, 0x803E1D0C ;Branch if item ID is 0xFF or higher
;Optimized code below
lbu t6, 0x1409(t2)
lui t7, 0x8016
lw t9, 0xFA90(t7)
sll t8, t6, 0x1
addu v0, t9, t8
lw t9, 0x0154(t0)
lh v0, 0x0DF6(v0)
sh v0, 0x0120(t9)
lui a1, 0x803F ;This command need a offset relocation fix. 0x1470 must be added. 0x0000F7FC
addiu t3, a1, 0x9968

136
image_deduplication/idup.py Normal file
View File

@ -0,0 +1,136 @@
#!/usr/bin/env python3
# find duplicate images given a hamming distance threshold.
# employs dhash to do the heavy lifting.
# doesn't recurse into "./_duplicate/" so you can dump things there if you wish.
# dependencies: pillow, dhash
import sys, os, os.path, pickle
from PIL import Image
import dhash
def lament(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def result(diff, p1, p2): # TODO: rename
print("{}\t{}\t{}".format(diff, p1, p2))
dbname = "idup.db"
exts = ".jpeg .jpg .png".split()
rootpath = "."
ignore_dir = os.path.join(rootpath, "_duplicate")
"""verbosity:
-1: only unrecoverable errors.
0: include failures.
1: include image opening/hashing.
2: the kitchen sink.
"""
verbosity = 1
pname = sys.argv[0]
if len(sys.argv) <= 1:
print("usage: {} {{threshold}}".format(pname))
print(" utilizes {} in the current working directory".format(dbname))
sys.exit(1)
args = sys.argv[1:]
threshold = int(args[0])
paths = {} # path to hash mapping.
if os.path.exists(dbname) and os.path.getsize(dbname) > 0:
with open(dbname, "rb") as f:
paths = pickle.load(f)
#lament("loaded", len(paths.keys()), "hashes")
else:
if verbosity >= 0:
lament("warning: no database found. starting from scratch.")
existing = dict((path, h) for path, h in paths.items() if os.path.exists(path))
for path in paths.keys():
if path not in existing:
if verbosity >= 0:
lament("#d", path)
paths = existing
def compare_hash(h1, h2):
# hashes are in byte strings, so we have to convert them to integers.
i1 = int.from_bytes(h1, byteorder="big")
i2 = int.from_bytes(h2, byteorder="big")
# return the hamming distance.
return bin(i1 ^ i2).count('1')
def run():
for dn, _, fns in os.walk(rootpath):
if dn == ignore_dir:
continue
for fn in fns:
name, ext = os.path.splitext(fn)
path = os.path.join(dn, fn)
if ext not in exts:
continue
if path in paths:
if verbosity >= 2:
lament("#s", path)
continue
try:
image = Image.open(path)
except OSError:
if verbosity >= 0:
lament("#f", path)
else:
try:
row, col = dhash.dhash_row_col(image)
except OSError:
if verbosity >= 0:
lament("#f", path)
else:
if verbosity >= 1:
lament("#o", path)
h = dhash.format_bytes(row, col)
paths[path] = h
finally:
image.close()
# first pass: exact hash matching.
hashes = dict((v, k) for k, v in paths.items())
for p1, h in paths.items():
p2 = hashes[h]
if p1 != p2:
result(-1, p1, p2)
# second pass: fuzzy hash matching.
if threshold <= 0:
return
seen = set()
for p1, h1 in paths.items():
if verbosity >= 2:
lament("#c", p1)
seen.add(p1)
for p2, h2 in paths.items():
if p2 in seen:
continue
if h1 == h2:
continue
diff = compare_hash(h1, h2)
if diff <= threshold:
result(diff, p1, p2)
try:
run()
except KeyboardInterrupt:
if verbosity >= 0:
lament("# interrupted")
finally:
if os.path.exists(dbname):
backup = dbname+".bak"
if os.path.exists(backup):
os.remove(backup)
os.rename(dbname, dbname+".bak")
with open(dbname, "wb") as f:
pickle.dump(paths, f)

314
kill_reboot/elevate.ps1 Normal file
View File

@ -0,0 +1,314 @@
# this is a very stripped-down C# port of NSudo:
# https://github.com/M2Team/NSudo
# you should already be running this script as an administrator.
param([string]$command="cmd")
function Execute-Elevated {
param([string]$command="cmd")
$Definition = @"
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
public class Dummy {
const int CREATE_NO_WINDOW = 0x8000000;
const int CREATE_UNICODE_ENVIRONMENT = 0x400;
const int MAXIMUM_ALLOWED = 0x2000000;
const int PROCESS_QUERY_INFORMATION = 0x400;
const int SE_PRIVILEGE_ENABLED = 0x2;
const int SecurityImpersonation = 2;
const int TOKEN_QUERY = 0x8;
const int TokenPrimary = 1;
const int TokenImpersonation = 2;
const int TokenUser = 1;
[StructLayout(LayoutKind.Sequential)]
public struct SID_AND_ATTRIBUTES {
public IntPtr Sid;
public int Attributes;
}
public struct TOKEN_USER {
public SID_AND_ATTRIBUTES User;
}
[StructLayout(LayoutKind.Sequential)]
public struct LUID {
public UInt32 LowPart;
public Int32 HighPart;
}
// hack to make a simple TOKEN_PRIVILEGES struct when Count=1.
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct TokenPrivilegeSingle {
public int Count;
public LUID Luid;
public uint Attr;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct STARTUPINFOW {
public int cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public ushort wShowWindow;
public ushort cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION {
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[DllImport("kernel32.dll")]
static extern IntPtr LocalFree(
IntPtr hMem);
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool CloseHandle(
IntPtr hHandle);
[DllImport("userenv.dll", SetLastError=true)]
static extern bool CreateEnvironmentBlock(
out IntPtr lpEnvironment,
IntPtr hToken, bool bInherit);
[DllImport("userenv.dll", SetLastError=true)]
static extern bool DestroyEnvironmentBlock(
IntPtr lpEnvironment);
[DllImport("advapi32", CharSet=CharSet.Auto, SetLastError=true)]
static extern bool ConvertSidToStringSid(
IntPtr pSID,
out IntPtr ptrSid);
[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr OpenProcess(
int dwDesiredAccess,
bool bInheritHandle,
long dwProcessId);
[DllImport("advapi32.dll", ExactSpelling=true, SetLastError=true)]
static extern bool OpenProcessToken(
IntPtr h,
int acc,
out IntPtr phtok);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool SetThreadToken(
IntPtr pHandle,
IntPtr hToken);
[DllImport("advapi32.dll", SetLastError=true)]
static extern bool GetTokenInformation(
IntPtr TokenHandle,
int TokenInformationClass,
IntPtr TokenInformation,
int TokenInformationLength,
out int ReturnLength);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
static extern bool DuplicateTokenEx(
IntPtr hExistingToken,
uint dwDesiredAccess,
IntPtr lpTokenAttributes,
int ImpersonationLevel,
int TokenType,
out IntPtr phNewToken);
[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
static extern bool CreateProcessAsUserW(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFOW lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool AdjustTokenPrivileges(
IntPtr TokenHandle,
bool DisableAllPrivileges,
ref TokenPrivilegeSingle NewState,
uint Zero,
IntPtr Null1,
IntPtr Null2);
public static string ConvertSidToStringSid(IntPtr sid) {
IntPtr pstr = IntPtr.Zero;
bool ok = ConvertSidToStringSid(sid, out pstr);
if (!ok) {
LocalFree(pstr);
throw new Exception("ConvertSidToStringSid: not ok");
}
string sidstr = Marshal.PtrToStringAuto(pstr);
LocalFree(pstr);
return sidstr;
}
public static bool HasPrivileges(int pid) {
bool ok = true;
IntPtr hProc = OpenProcess(PROCESS_QUERY_INFORMATION, false, pid);
if (hProc == IntPtr.Zero) throw new Exception("OpenProcess: null");
IntPtr hToken = IntPtr.Zero;
ok = OpenProcessToken(hProc, TOKEN_QUERY, out hToken);
if (!ok || hToken == IntPtr.Zero) throw new Exception("OpenProcessToken: not ok or null");
int tiLength = -1;
GetTokenInformation(hToken, TokenUser, IntPtr.Zero, 0, out tiLength);
IntPtr ti = Marshal.AllocHGlobal(tiLength);
ok = GetTokenInformation(hToken, TokenUser, ti, tiLength, out tiLength);
if (!ok) {
Marshal.FreeHGlobal(ti);
throw new Exception("GetTokenInformation: not ok");
}
TOKEN_USER tokenUser = (TOKEN_USER)Marshal.PtrToStructure(ti, typeof(TOKEN_USER));
string sidstr = ConvertSidToStringSid(tokenUser.User.Sid);
return sidstr == "S-1-5-18"; // described as "Local System"
}
public static int FindSystemPid(string processName) {
Process currentProc = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName(processName);
int pid = -1;
foreach (Process proc in processes) {
if (currentProc.SessionId == proc.SessionId && HasPrivileges(proc.Id)) {
pid = proc.Id;
break;
}
}
if (pid == -1) throw new Exception("FindSystemPid: couldn't find " + processName);
return pid;
}
public static IntPtr DuplicateProcessToken(int pid, int impLevel, int tokenType) {
// desiredAccess is always MAXIMUM_ALLOWED
// lpTokenAttributes is always null (IntPtr.Zero)
bool ok = true;
IntPtr hProc = OpenProcess(MAXIMUM_ALLOWED, false, pid);
if (hProc == IntPtr.Zero) throw new Exception("OpenProcess: null");
IntPtr hToken = IntPtr.Zero;
ok = OpenProcessToken(hProc, MAXIMUM_ALLOWED, out hToken);
if (!ok || hToken == IntPtr.Zero) throw new Exception("OpenProcessToken: not ok or null");
IntPtr phToken = IntPtr.Zero;
ok = DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, IntPtr.Zero,
impLevel, tokenType,
out phToken);
if (!ok || phToken == IntPtr.Zero) throw new Exception("DuplicateTokenEx: not ok or null");
return phToken;
}
public static void CreateProcessW(IntPtr hToken, IntPtr environment, string directory,
string comSpec, string command) {
bool ok = true;
STARTUPINFOW startupInfo = new STARTUPINFOW();
PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();
startupInfo.cb = Marshal.SizeOf(startupInfo);
startupInfo.lpDesktop = "WinSta0\\Default";
ok = CreateProcessAsUserW(hToken, comSpec, command,
IntPtr.Zero, IntPtr.Zero, false,
CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
environment, directory,
ref startupInfo, out processInfo);
if (!ok) {
int err = Marshal.GetLastWin32Error();
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
throw new Exception("CreateProcessAsUser failure: " + err);
}
}
public static IntPtr CreateEnvironment(IntPtr hToken) {
bool ok = true;
IntPtr environment;
ok = CreateEnvironmentBlock(out environment, hToken, true);
if (!ok || environment == IntPtr.Zero) throw new Exception("CreateEnvironmentBlock: not ok or null");
return environment;
}
public static void SetTokenAllPrivileges(IntPtr hToken, bool enable) {
// turns out the only one we need is 3: SE_ASSIGNPRIMARYTOKEN_PRIVILEGE
bool ok = true;
TokenPrivilegeSingle tp = new TokenPrivilegeSingle();
tp.Count = 1;
tp.Attr = (uint)(enable ? SE_PRIVILEGE_ENABLED : 0);
tp.Luid.LowPart = 3;
ok = AdjustTokenPrivileges(hToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
if (!ok) throw new Exception("AdjustTokenPrivileges: not ok");
int err = Marshal.GetLastWin32Error();
if (err != 0) throw new Exception("AdjustTokenPrivileges failure: " + err);
}
public static void Execute(string comSpec, string command) {
bool ok = true;
IntPtr hTokenSelf;
ok = OpenProcessToken((IntPtr)(-1), MAXIMUM_ALLOWED, out hTokenSelf);
if (!ok || hTokenSelf == IntPtr.Zero) throw new Exception("OpenProcessToken: not ok or null");
IntPtr environment = CreateEnvironment(hTokenSelf);
int pid = FindSystemPid("winlogon");
Console.WriteLine("winlogon PID: " + pid);
IntPtr hTokenSystemImpersonate = DuplicateProcessToken(pid, SecurityImpersonation, TokenImpersonation);
SetTokenAllPrivileges(hTokenSystemImpersonate, true);
ok = SetThreadToken(IntPtr.Zero, hTokenSystemImpersonate);
if (!ok) throw new Exception("SetThreadToken: not ok");
IntPtr hTokenSystemPrimary = DuplicateProcessToken(pid, SecurityImpersonation, TokenPrimary);
string fullCommand = "/c start \"" + comSpec + "\" " + command;
Console.WriteLine(comSpec + " " + fullCommand);
CreateProcessW(hTokenSystemPrimary, environment, null, comSpec, fullCommand);
// TODO: always clean up regardless of exceptions.
DestroyEnvironmentBlock(environment);
}
}
"@
$type = Add-Type $definition -PassThru
$type[0]::Execute((get-item env:"ComSpec").Value, $command)
}
Execute-Elevated $command
exit

View File

@ -0,0 +1,32 @@
@echo off
set debug=0
set ps_reg="HKCU\Software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell"
rem check if the key already exists.
reg query %ps_reg% /v ExecutionPolicy >nul 2>&1
if NOT %errorLevel% == 0 (
rem set it just high enough so we can run our script.
reg add %ps_reg% /t REG_SZ /v ExecutionPolicy /d RemoteSigned
)
rem check if we're at least running as an admin.
net session >nul 2>&1
if NOT %errorLevel% == 0 (
rem re-run this script as system.
rem that's more than administrator, but less than trusted installer.
if "%debug%"=="1" (
powershell -Command "Start-Process powershell {%~dp0\elevate.ps1 %0} -Verb RunAs"
) else (
rem this version bypasses UAC. please refer to the following post:
rem https://tyranidslair.blogspot.ca/2017/05/exploiting-environment-variables-in.html
reg add hkcu\Environment /v windir /d "cmd /K reg delete hkcu\Environment /v windir /f && powershell -WindowStyle Hidden -NonInteractive %~dp0\elevate.ps1 %0 && REM "
)
schtasks /Run /TN \Microsoft\Windows\DiskCleanup\SilentCleanup /I
exit
)
schtasks /change /tn \Microsoft\Windows\UpdateOrchestrator\Reboot /DISABLE
icacls "%WINDIR%\System32\Tasks\Microsoft\Windows\UpdateOrchestrator\Reboot" /inheritance:r /deny "Everyone:F" /deny "SYSTEM:F" /deny "Local Service:F" /deny "Administrators:F"
if "%debug%"=="1" ( pause )
exit

113
kyaa/kyaa.h Normal file
View File

@ -0,0 +1,113 @@
/* kyaa.h - macro hacks for handling main() arguments
This is free and unencumbered software released into the public domain.
Refer to kyaa.md for documentation.
*/
#pragma once
#ifndef KYAA_OKAY
#define KYAA_OKAY 0
#endif
#ifndef KYAA_ERROR
#define KYAA_ERROR 1
#endif
#ifndef KYAA_ITER
#define KYAA_ITER i
#endif
#define KYAA_SETUP \
/* sanity checks */ \
if (argc <= 0 || argv == NULL || argv[0] == NULL) { \
fprintf(stderr, "You've met with a terrible fate.\n"); \
return KYAA_ERROR; \
} \
char *kyaa_name = argv[0]; \
bool kyaa_read_stdin = false; \
char kyaa_flag = '\0'; \
bool kyaa_keep_parsing = true; \
bool kyaa_parse_next = false; \
#define KYAA_LOOP \
KYAA_SETUP \
for (int KYAA_ITER = 1; KYAA_ITER < argc; KYAA_ITER++) \
#define KYAA_BEGIN \
char *kyaa_arg = argv[KYAA_ITER]; \
if (kyaa_keep_parsing && (kyaa_parse_next || kyaa_arg[0] == '-')) { \
if (!kyaa_parse_next && kyaa_arg[1] == '-' && kyaa_arg[2] == '\0') { \
kyaa_keep_parsing = false; \
continue; \
} \
if (!kyaa_parse_next && kyaa_arg[1] == '\0') { \
kyaa_read_stdin = true; \
} else { \
/* case: kyaa_parse_next: arg is at least 1 char initialized. */ \
/* case: !kyaa_parse_next: arg is at least 3 chars initialized. */ \
char *kyaa_etc = kyaa_parse_next ? kyaa_arg : kyaa_arg + 2; \
bool kyaa_no_more = false; \
bool kyaa_helping = false; \
bool kyaa_any = false; \
if (!kyaa_parse_next && kyaa_arg[1] != '-') { \
kyaa_flag = kyaa_arg[1]; \
kyaa_no_more = kyaa_arg[2] == '\0'; \
} \
if (kyaa_flag == 'h' || !strcmp(kyaa_arg, "--help")) { \
printf("usage:\n"); \
kyaa_helping = true; \
} \
if (0) { \
#define KYAA_END \
} \
if (!kyaa_any && !kyaa_helping) { \
if (kyaa_flag) { \
fprintf(stderr, "unknown flag: -%c\n", kyaa_flag); \
} else { \
fprintf(stderr, "unknown flag: %s\n", kyaa_arg); \
} \
return KYAA_ERROR; \
} \
if (kyaa_helping) { \
return KYAA_OKAY; \
} \
kyaa_parse_next = false; \
kyaa_flag = '\0'; \
continue; \
} \
} \
#define KYAA_DESCRIBE(c, name, description) \
printf(" -%c --%s\n%s\n", c, name, description) \
#define KYAA_FLAG(c, name, description) \
} \
if (kyaa_helping) { \
KYAA_DESCRIBE(c, name, description); \
} else if (kyaa_flag == c || !strcmp(kyaa_arg, "--"name)) { \
kyaa_flag = c; \
kyaa_any = true; \
#define KYAA_FLAG_ARG(c, name, description) \
} \
if (kyaa_helping) { \
KYAA_DESCRIBE(c, name, description); \
} else if (kyaa_flag == c || !strcmp(kyaa_arg, "--"name)) { \
if (kyaa_no_more || kyaa_flag != c) { \
kyaa_parse_next = true; \
if (KYAA_ITER + 1 == argc || argv[KYAA_ITER + 1][0] == '\0') { \
fprintf(stderr, "expected an argument for --%s (-%c)\n", name, c); \
return KYAA_ERROR; \
} \
kyaa_flag = c; \
continue; \
} \
kyaa_flag = c; \
kyaa_any = true; \
#define KYAA_HELP(description) \
} \
if (kyaa_helping) { \
if (description != NULL) { \
printf("%s\n", description); \
} \

160
kyaa/kyaa.md Normal file
View File

@ -0,0 +1,160 @@
# kyaa
super hacky macro hacks for parsing arguments in C.
## prerequisites
C99 or greater.
standard library headers:
* `stdbool.h`
* `stdio.h`
* `string.h`
in addition, `kyaa_extra.h` requires `limits.h`,
or at least `LONG_MIN` to be defined.
## tutorial
ensure `argc` and `argv` are defined.
kyaa doesn't actually care if it's in `main` or not.
iterate over the arguments with `KYAA_LOOP`:
```c
int main(int argc, char *argv[]) {
KYAA_LOOP {
// KYAA_ITER, kyaa_name, kyaa_read_stdin, and kyaa_flag are exposed here.
// [other code goes here]
}
return 0;
}
```
use `KYAA_BEGIN` and `KYAA_END` to begin parsing arguments.
put unrelated code above `KYAA_BEGIN` or below `KYAA_END`, but not within:
```c
KYAA_LOOP {
// [other code goes here]
KYAA_BEGIN
// kyaa_arg and kyaa_etc are exposed here.
// [kyaa code goes here]
KYAA_END
// [other code goes here]
}
```
use `KYAA_FLAG` for defining flags that don't take an argument,
and `KYAA_FLAG_ARG` or `KYAA_FLAG_LONG` for flags that do:
```c
bool use_feature = false;
char *log_fn = "logs.txt";
long my_var = 0;
KYAA_LOOP {
// [other code goes here]
KYAA_BEGIN
// arguments: short flag, long flag, help description
KYAA_FLAG("-x", "--enable-feature",
" enable some feature")
use_feature = true;
// same arguments, but kyaa_etc contains the relevant string.
KYAA_FLAG_ARG("-l", "--log-file",
" use a given filename for the log file")
log_fn = kyaa_etc;
// same arguments, kyaa_etc is set, but kyaa_flag_arg is also set.
KYAA_FLAG_LONG("-v", "--var",
" set an integer variable\n"
" default: 0")
my_var = kyaa_flag_arg;
KYAA_END
// [other code goes here]
}
```
kyaa secretly wraps flag handling in if/else statements with {} blocks.
do not confuse it for a switch/case method.
kyaa handles `-h` and `--help` for printing help text.
additional help text may be defined using `KYAA_HELP`:
```c
KYAA_LOOP {
KYAA_BEGIN
KYAA_HELP(
" {files...}\n"
" do things with files.")
KYAA_END
do_stuff(kyaa_arg);
}
```
kyaa interprets an argument of `-` as a request to enable reading from stdin:
`kyaa_read_stdin` is set to true.
arguments may be passed in three ways, consider:
* `-v42`
* `-v 42`
* `--var 42`
kyaa returns `KYAA_OKAY` when `-h` or `--help` is given,
and `KYAA_ERROR` in the event of invalid flags, missing arguments,
or invalid numbers (`KYAA_FLAG_LONG`).
kyaa uses `continue` for handling arguments.
kyaa prints error messages to `stderr`, and help text to `stdout`.
`KYAA_ITER` may be redefined to avoid name collisions.
## TODO
* support `--var=42` argument style
* fix overlapping names, e.g. `KYAA_FLAG` vs `kyaa_flag`, `KYAA_FLAG_ARG` vs `kyaa_flag_arg`, etc.
* maybe pass `argc`/`argv` manually?
## API
### kyaa.h
return type or type | name | arguments or default value
--- | --- | ---
define | KYAA\_OKAY | 0
define | KYAA\_ERROR | 1
define | KYAA\_ITER | i
||
macro | KYAA\_SETUP | *none*
macro | KYAA\_LOOP | *none*
macro | KYAA\_BEGIN | *none*
macro | KYAA\_END | *none*
macro | KYAA\_DESCRIBE | c, name, description
macro | KYAA\_FLAG | c, name, description
macro | KYAA\_FLAG\_ARG | c, name, description
macro | KYAA\_HELP | description
||
`char *` | kyaa\_name | *n/a*
`bool` | kyaa\_read\_stdin | *n/a*
`char *` | kyaa\_arg | *n/a*
|| **internal use** ||
`char` | kyaa\_flag | *n/a*
`bool` | kyaa\_keep\_parsing | *n/a*
`bool` | kyaa\_parse\_next | *n/a*
`bool` | kyaa\_no\_more | *n/a*
`bool` | kyaa\_helping | *n/a*
`bool` | kyaa\_any | *n/a*
### kyaa\_extra.h
return value or type | name | arguments or default value
--- | --- | ---
macro | KYAA\_FLAG\_LONG | c, name, description
||
`char *` | kyaa\_flag\_arg | *n/a*
||
`char *` | kyaa\_skip\_spaces | `char *` str
`const char *` | kyaa\_str\_to\_long | `char *` str, `long *` p\_out

211
kyaa/kyaa_extra.h Normal file
View File

@ -0,0 +1,211 @@
/* kyaa_extra.h - extensions to kyaa for parsing arguments.
This is free and unencumbered software released into the public domain.
Refer to kyaa.md for documentation.
*/
static char *kyaa_skip_spaces(char *str) {
/* iterates str to first non-space character according to the C locale */
while (*str != '\0') {
switch (*str) {
case ' ':
case '\f':
case '\n':
case '\r':
case '\t':
case '\v': { str++; } break;
default: return str;
}
}
return str;
}
static const char *kyaa__base_2(char **p_str, long *p_out) {
char *str = *p_str;
long out = *p_out;
for (char c; (c = *str) != '\0'; str++) {
switch (c) {
case '0': case '1': {
long digit = (long)(c - '0');
if (out < (LONG_MIN + digit) / 2) {
return "out of range for long integer";
}
out = out * 2 - digit;
} break;
case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9':
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case '.': return "invalid character for base 2 integer";
default: goto exit;
}
}
exit:
*p_str = str;
*p_out = out;
return NULL;
}
static const char *kyaa__base_8(char **p_str, long *p_out) {
char *str = *p_str;
long out = *p_out;
for (char c; (c = *str) != '\0'; str++) {
switch (c) {
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7': {
long digit = (long)(c - '0');
if (out < (LONG_MIN + digit) / 8) {
return "out of range for long integer";
}
out = out * 8 - digit;
} break;
case '8': case '9':
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case '.': return "invalid character for base 8 integer";
default: goto exit;
}
}
exit:
*p_str = str;
*p_out = out;
return NULL;
}
static const char *kyaa__base_10(char **p_str, long *p_out) {
char *str = *p_str;
long out = *p_out;
for (char c; (c = *str) != '\0'; str++) {
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': {
long digit = (long)(c - '0');
if (out < (LONG_MIN + digit) / 10) {
return "out of range for long integer";
}
out = out * 10 - digit;
} break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case '.': return "invalid character for base 10 integer";
default: goto exit;
}
}
exit:
*p_str = str;
*p_out = out;
return NULL;
}
static const char *kyaa__base_16(char **p_str, long *p_out) {
char *str = *p_str;
long out = *p_out;
for (char c; (c = *str) != '\0'; str++) {
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': {
long digit = (long)(c - '0');
if (out < (LONG_MIN + digit) / 16) {
return "out of range for long integer";
}
out = out * 16 - digit;
} break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': {
long digit = (long)(c - 'A') + 10;
if (out < (LONG_MIN + digit) / 16) {
return "out of range for long integer";
}
out = out * 16 - digit;
} break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': {
long digit = (long)(c - 'a') + 10;
if (out < (LONG_MIN + digit) / 16) {
return "out of range for long integer";
}
out = out * 16 - digit;
} break;
case '.': return "invalid character for base 16 integer";
default: goto exit;
}
}
exit:
*p_str = str;
*p_out = out;
return NULL;
}
static const char *kyaa_str_to_long(char *str, long *p_out) {
/* returns error message or NULL */
long out = 0;
int base = 10;
bool negated = false;
str = kyaa_skip_spaces(str);
switch (*str) {
case '-': { negated = true; str++; } break;
case '+': { str++; } break;
default: break;
}
switch (*str) {
case '#': { base = 10; str++; } break;
case '$': { base = 8; str++; } break;
case '%': { base = 2; str++; } break;
case '0': { base = -1; str++; } break;
default: break;
}
if (base == -1) {
switch (*str) {
case '\0': { *p_out = 0; } return NULL;
case 'b': { base = 2; str++; } break;
case 'h': { base = 16; str++; } break;
case 'o': { base = 8; str++; } break;
case 'x': { base = 16; str++; } break;
default: { base = 8; } break;
}
}
if (*str == '\0') return "no number given";
// NOTE: we actually subtract each digit from the result instead of summing.
// this lets us represent LONG_MIN without overflowing.
const char *err;
switch (base) {
case 2: { err = kyaa__base_2( &str, &out); } break;
case 8: { err = kyaa__base_8( &str, &out); } break;
case 10: { err = kyaa__base_10(&str, &out); } break;
case 16: { err = kyaa__base_16(&str, &out); } break;
default: return "internal error";
}
if (err != NULL) return err;
str = kyaa_skip_spaces(str);
if (*str != '\0') return "unexpected character";
// NOTE: out is negative here; see above comment.
// assuming two's complement
if (!negated && out == LONG_MIN) return "out of range for long integer";
*p_out = negated ? out : -out;
return NULL;
}
#define KYAA_FLAG_LONG(c, name, description) \
KYAA_FLAG_ARG(c, name, description) \
long kyaa_flag_arg; \
const char *err = kyaa_str_to_long(kyaa_etc, &kyaa_flag_arg); \
if (err) { \
fprintf(stderr, "%s\n", err); \
return KYAA_ERROR; \
} \

165
love_plotting/main.lua Normal file
View File

@ -0,0 +1,165 @@
function lerp(x0, x1, t)
return x0*(1-t) + x1*t
end
function blendExp(x0, x1, t)
return x0^(1-t)*x1^t
end
local sw = 640
local sh = 480
local font
local fs = 12
local coordsA = {}
local coordsB = {}
local coordsAL = {}
local coordsBL = {}
local input = {
[0]=0.9013,
[60]=1.3685,
[120]=1.1046,
[180]=1.1633,
[240]=0.7332,
[300]=0.8603,
[360]=0.9013,
}
local xPrecision = 60/20
local win = {
T = 1.5,
B = -0.5,
L = 0,
R = 360,
stepX = 30,
stepY = 0.25,
}
win.scaleX = 1/(win.L - win.R)
win.scaleY = 1/(win.T - win.B)
function screenX(x)
return sw*(win.L-x)*win.scaleX
end
function screenY(y)
return sh*(win.T-y)*win.scaleY
end
function addPoint(coords, x, y)
coords[#coords+1] = screenX(x)
coords[#coords+1] = screenY(y)
end
function index(t, x)
if type(x) ~= type(0) then
return nil
end
local x0
local x1
for tx, ty in pairs(t) do
-- we can't guarantee the order the key/values come in
if tx ~= "interpolator" then
-- x0 = largest tx that doesn't go over x
if tx <= x and (x0 == nil or tx > x0) then
x0 = tx
end
-- x1 = smallest tx that doesn't go under x
if tx > x and (x1 == nil or tx < x1) then
x1 = tx
end
end
end
x1 = x1 or x0
local y0 = rawget(t, x0)
local y1 = rawget(t, x1)
local f = rawget(t, "interpolator")
local y = f(y0, y1, (x - x0)/(x1 - x0))
return y
end
function love.load()
if not love.graphics.setMode(sw, sh) then
error("couldn't setMode ("..sw.." by "..sh..")")
end
love.graphics.setCaption("hey it's a graph")
--font = love.graphics.newFont("DejaVuSansMono.ttf", fs)
--love.graphics.setFont(font)
setmetatable(input, {__index = index})
input.interpolator = lerp
for x=win.L, win.R, xPrecision do
local y = input[x]
addPoint(coordsA, x, y)
addPoint(coordsAL, x, math.log(y))
end
input.interpolator = blendExp
for x=win.L, win.R, xPrecision do
local y = input[x]
addPoint(coordsB, x, y)
addPoint(coordsBL, x, math.log(y))
end
end
function love.keypressed(key)
if key == "escape" then
love.event.push("quit")
end
end
local drawCoords = love.graphics.line
local drawText = love.graphics.print
function color(hex)
local r = tonumber(string.sub(hex, 1,2), 16)
local g = tonumber(string.sub(hex, 3,4), 16)
local b = tonumber(string.sub(hex, 5,6), 16)
love.graphics.setColor(r,g,b)
end
function drawVert(x)
drawCoords(x, 0, x, sh)
end
function drawHoriz(y)
drawCoords(0, y, sw, y)
end
function love.draw()
love.graphics.setBackgroundColor(255,255,255)
love.graphics.clear()
for x=win.L, win.R, win.stepX do
color("d3e2e7")
local rx = screenX(x)
drawVert(rx)
color("808080")
drawText(tostring(x), rx, sh - fs)
end
for y=win.B, win.T, win.stepY do
color("c6cce3")
local ry = screenY(y)
drawHoriz(ry)
color("808080")
drawText(tostring(y), 0, ry)
end
color("404040")
drawVert(screenX(0))
drawHoriz(screenY(0))
color("57C1BB")
drawCoords(coordsA)
drawCoords(coordsAL)
color("9F65C5")
drawCoords(coordsB)
drawCoords(coordsBL)
end

83
lsca/lsca.uni.py Normal file
View File

@ -0,0 +1,83 @@
#!/usr/bin/env python3
import sys
import numpy as np
def lament(*args, **kwargs):
return print(*args, file=sys.stderr, **kwargs)
def colorize(x):
return x * 17 % 256
def render(row, scale):
grays = colorize(row)
if scale > 1:
grays = np.repeat(grays, scale)
line = " ".join(str(x) for x in grays)
for y in range(scale):
print(line)
def limit(row): # in-place
row[row < 0] = 0
row[row > 15] = 15
return row
def initialize(width):
row = np.zeros(width, int)
value = 0
for i in range(width):
if np.random.randint(8) == 0: # only change values occasionally
value = np.random.randint(16)
row[i] = value
limit(row)
return row
name, args = sys.argv[0], sys.argv[1:]
if len(args) == 0:
rule_lut = None
elif len(args) == 16:
rule_lut = [int(x.strip(",")) for x in args]
else:
lament(f"usage: {name}")
lament(f"usage: {name} {{rules 1 through 16}}")
sys.exit(1)
scale = 3
width, height = 960 // scale, 960 // scale
if rule_lut is None:
rule_lut = np.random.randint(-2, 2 + 1, size=(4, 4))
lament(" ".join(f"{x:+2}" for x in rule_lut.flat))
else:
rule_lut = np.array(rule_lut).reshape(4, 4)
print("P2") # magic code for an ascii Portable GrayMap (PGM) file
print(width * scale, height * scale)
print(255) # maximum color value
row = initialize(width)
for i in range(height):
# left, center, right:
L = np.roll(row, 1)
C = row.copy()
R = np.roll(row, -1)
diffusion = (L + C + C + R + 2) // 4
# v = [0,1,2,3,1,0,3,2,2,3,0,1,3,2,1,0][V]
y = (L ^ (L >> 2)) % 4
x = (R ^ (R >> 2)) % 4
delta = rule_lut[y, x]
row = diffusion + delta
limit(row)
render(row, scale)

172
lsf/lsf.sh Executable file
View File

@ -0,0 +1,172 @@
#!/bin/bash
# ultra-fancy ultra-pointless `ls -l` alternative
# to be sourced by bash or zsh
# similar project: https://github.com/trapd00r/ls--
# to maintain zsh compatibility:
# $() is surrounded in double quotes
# echo -E is used to avoid \033 expanding
# bare asterisks are escaped to prevent globbing
# arrays are used instead of relying on word expansion
# for loops over "${array[@]}" as it works the same in both
# TODO: set better defaults for quick glances at filesize
# TODO: include sorting somehow
# TODO: handle symlinks nicely
# TODO: append WHI / clr to dir names
_lsf_begin(){
local begin='
me='$UID'
'"$(
awk -F: '{print "unames["$3"]=\""$1"\""}' < /etc/passwd
awk -F: '{print "gnames["$3"]=\""$1"\""}' < /etc/group
for x in $(groups); do echo "us[\"$x\"]=1"; done
i=0;for x in bla red gre yel blu pur cya whi; do
echo -E $x'="\033[3'${i}'m"'
echo -n "$x" | tr a-z A-Z
echo -E '="\033[1;3'${i}'m"'
let i++
done
)"'
clr="\033[0m"
BLD="\033[1m"
s=1
m=60*s
h=60*m
d=24*h
y=365*d
B=1
K=1024*B
M=1024*K
G=1024*M
T=1024*G
ff["time"]="%3d%s " clr
ff["size"]="%4d" clr "%s "
'"$(
for x in s m h d y;do echo "u[\"time,$x\"]=$x";done
for x in B K M G T;do echo "u[\"size,$x\"]=$x";done
ft=(-1 m m\*10 h h\*12 d d\*7 30\*d y y\*2 y\*10)
fut=(s s s m m h h d d d y y)
fct=(RED PUR pur YEL yel GRE gre CYA cya BLU blu BLA)
fs=( 0 K K\*8 M M\*8 G G\*8 T T\*8)
fus=(B B B K K M M G G T)
fcs=(BLA cya CYA CYA yel yel pur pur red red)
pc=(BLA WHI yel YEL blu BLU gre GRE)
i=0;for x in "${ft[@]}"; do echo "f[\"time,$i\"]=$x"; let i++;done
echo "f[\"times\"]=$i"
i=0;for x in "${fs[@]}"; do echo "f[\"size,$i\"]=$x"; let i++;done
echo "f[\"sizes\"]=$i"
i=0;for x in "${fut[@]}";do echo "fu[\"time,$i\"]=\"$x\"";let i++;done
i=0;for x in "${fus[@]}";do echo "fu[\"size,$i\"]=\"$x\"";let i++;done
i=0;for x in "${fct[@]}";do echo "fc[\"time,$i\"]=$x"; let i++;done
i=0;for x in "${fcs[@]}";do echo "fc[\"size,$i\"]=$x"; let i++;done
i=0;for x in "${pc[@]}"; do echo "pc[$i]=$x"; let i++;done
)"
echo -E "$begin"
}
_lsf_cached=
_lsf_program='
function printff(id, n) {
len=f[id "s"]
for(i=0;i<=len;i++) {
idi=id "," i
if(i!=len && n>f[idi]) continue
unit=fu[idi]
printf(fc[idi] ff[id], n/u[id "," unit], unit)
break
}
}
function trunc(str, len) {
e=length(str)>len?"…":""
return substr(str,0,len-(e?1:0)) e
}
function fixlen(str, len) {
return trunc(sprintf("%" len "s", str), len)
}
{
printff("size", $(NF-14))
uid=$(NF-11)
gid=$(NF-10)
is_me=(uid==me)
is_us=(gnames[gid] in us)
bits=("0x" $(NF-12))+0
# note: we ignore the set and sticky bits... for now
type=rshift(and(bits, 0170000), 12)
operm=and(bits, 07)
gperm=rshift(and(bits, 070), 3)
uperm=rshift(and(bits, 0700), 6)
our_perm=or(or((is_me)?uperm:0, (is_us)?gperm:0), operm)
printf(pc[our_perm] "%o " clr, our_perm)
if(OSP) {
printf(pc[uperm] "%o" clr, uperm)
printf(pc[gperm] "%o" clr, gperm)
printf(pc[operm] "%o " clr, operm)
}
if(OSU) {
name=fixlen((uid in unames)?unames[uid]:uid, 6)
if(is_me) name=WHI name clr
printf("%s ", name)
}
if(OSG) {
name=fixlen((gid in gnames)?gnames[gid]:gid, 6)
if(is_us) name=WHI name clr
printf("%s ", name)
}
da=$(NF-4)
dc=$(NF-3)
dm=$(NF-2)
if(OMR) {
max=(da>dm)?da:dm
max=(max>dc)?max:dc
printff("time", now-max)
} else {
printff("time", now-da)
printff("time", now-dm)
printff("time", now-dc)
}
# acquire filename by killing all fields not part of it
NF-=15
fn=$0
if (!OSPA) {
if (NR!=1) fn=substr(fn,firstlen)
else firstlen=length(fn)+2-(fn ~ /\/$/)
}
print fn
}'
lsf(){
local o_showallperm=1 o_showuser=1 o_showgroup=1
local o_mostrecent=0 o_showpath=1 opts=mgupfs opt
while getopts $opts'h' opt; do
case $opt in
f) _lsf_cached=;;
p) o_showallperm=0;;
u) o_showuser=0;;
g) o_showgroup=0;;
m) o_mostrecent=1;;
s) o_showpath=0;;
?) echo "usage: $0 [-$opts] [dir]"
return 1;;
esac
done
[ "$_lsf_cached" ] || _lsf_cached="$(_lsf_begin)"
shift $((OPTIND-1))
find "${1:-.}" -maxdepth 1 -exec stat -t {} + | awk --non-decimal-data \
-v"now=$(date +%s)" -v"OMR=$o_mostrecent" -v"OSU=$o_showuser" \
-v"OSG=$o_showgroup" -v"OSP=$o_showallperm" -v"OSPA=$o_showpath" \
"BEGIN{$_lsf_cached}$_lsf_program"
}

328
mario_tennis/tennis.c Normal file
View File

@ -0,0 +1,328 @@
// arbitrary terminology i made up on the spot:
// "entry" = code as exposed to user, in ASCII.
// "code" = code as indices into lut, used internally.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef uint32_t u32;
typedef uint64_t u64;
#define lament(...) fprintf(stderr, __VA_ARGS__)
#define sizeofel(a) (sizeof(*(a)))
#define LEN(a) (sizeof(a) / sizeofel(a))
const char *lut[] = {
// characters within the same string are considered the same.
// that means the number of choices for a single character
// goes down from 36 to 28.
"N", "P", "Q", "R",
"T", "W", "X", "Y",
"17I", "2Z", "5S", "8B",
"0OD", "UV", "3", "4",
"6", "9", "A", "C",
"E", "F", "G", "H",
"J", "K", "L", "M",
};
enum {lutlen = LEN(lut)}; // should be 28.
const u32 bases[] = {
64,
11,
21,
2,
21,
8,
6, // dummy value.
};
enum {
MT_CHOICE = -1,
};
typedef enum {
MT_YOSHI,
MT_PEACH,
MT_MARIO,
MT_BOWSER,
MT_BOO,
MT_DK,
MT_BABY_MARIO,
MT_TOAD,
MT_WALUIGI,
MT_WARIO,
MT_LUIGI,
MT_DAISY,
MT_BIRDO,
MT_SHYGUY,
MT_DK_JR,
MT_PARATROOPA,
} character_t;
typedef enum {
MT_SINGLES_GAME,
MT_SINGLES_TIME_3,
MT_SINGLES_TIME_5,
MT_SINGLES_BALL_1,
MT_SINGLES_BALL_3,
MT_SINGLES_POINT_3,
MT_SINGLES_POINT_5,
MT_DOUBLES_GAME_1,
MT_DOUBLES_TIME_3,
MT_DOUBLES_BALL_1,
MT_DOUBLES_POINT_3,
} gamemode_t;
typedef enum {
MT_HARD,
MT_CLAY,
MT_GRASS,
MT_COMPOSITION,
MT_OPEN,
} court_t;
typedef struct {
char code[9];
// data[0]: cup name
// data[1]: gamemode
// data[2]: player character (-1 for player's choice)
// data[3]: unknown (boolean)
// data[4]: opponent character (-1 for player's choice)
// data[5]: unknown
// data[6]: court (-1 for player's choice)
u32 data[7];
} code_t;
code_t translate(const char *entry) {
code_t code = {0};
for (int i = 0; i < 9; i++) {
char e = entry[i];
if (e == '\0') return code;
for (int j = 0; j < lutlen; j++) {
const char *c = lut[j];
while (*c != '\0') {
if (e == *c) {
code.code[i] = j;
goto nextchar;
}
c++;
}
}
nextchar:
;
}
return code;
}
int check_ranges(const code_t *code) {
const u32 choice = (u32)MT_CHOICE;
const u32 *data = code->data;
return
(data[0] < bases[0]) &&
(data[1] < bases[1]) &&
(data[2] + 1 < bases[2] - 4) &&
(data[3] < bases[3]) &&
(data[4] + 1 < bases[4] - 4) &&
(data[5] < bases[5]) &&
(data[6] + 1 < bases[6]) &&
(data[2] == choice || data[2] != data[4]) &&
(data[1] < 7 || (data[2] != choice && data[4] != choice));
}
int decode_data(code_t *code) {
u32 part0 = 0, part1 = 0;
for (int i = 5; i >= 0; i--) part0 = part0 * lutlen + code->code[i];
for (int i = 8; i >= 6; i--) part1 = part1 * lutlen + code->code[i];
u32 checksum = (part0 + 0x2AE0) % (lutlen * lutlen * lutlen);
#if !NDEBUG
lament("merged data: %08X\n", part0);
lament("expected checksum: %08X\n", part1);
lament("actual checksum: %08X\n", checksum);
#endif
u32 leftover = part0 ^ 0x02AAAAAA;
code->data[0] = leftover % bases[0]; leftover /= bases[0];
code->data[1] = leftover % bases[1]; leftover /= bases[1];
code->data[2] = leftover % bases[2]; leftover /= bases[2];
code->data[3] = leftover % bases[3]; leftover /= bases[3];
code->data[4] = leftover % bases[4]; leftover /= bases[4];
code->data[5] = leftover % bases[5]; leftover /= bases[5];
code->data[6] = leftover;
code->data[2]--;
code->data[4]--;
code->data[6]--;
#if !NDEBUG
lament("stored data:");
for (int i = 0; i < 7; i++) lament(" %i", code->data[i]);
lament("\n");
#endif
if (checksum != part1) return 0;
if (!check_ranges(code)) return 0;
return 1;
}
int encode_data(code_t *code) {
if (!check_ranges(code)) return 0;
u32 combined = 0;
combined = code->data[6] + 1;
combined = combined * bases[5] + code->data[5];
combined = combined * bases[4] + code->data[4] + 1;
combined = combined * bases[3] + code->data[3];
combined = combined * bases[2] + code->data[2] + 1;
combined = combined * bases[1] + code->data[1];
combined = combined * bases[0] + code->data[0];
combined = combined ^ 0x02AAAAAA;
u32 checksum = (combined + 0x2AE0) % (lutlen * lutlen * lutlen);
#if !NDEBUG
lament("merged data: %08X\n", combined);
lament("checksum: %08X\n", checksum);
#endif
u32 part0 = combined, part1 = checksum;
for (int i = 0; i <= 5; i++) {
code->code[i] = part0 % lutlen; part0 /= lutlen;
}
for (int i = 6; i <= 8; i++) {
code->code[i] = part1 % lutlen; part1 /= lutlen;
}
return 1;
}
int validate_entry(const char *entry) {
code_t code = translate(entry);
#if !NDEBUG
lament("character mapping:");
for (int i = 0; i < 9; i++) {
lament(" %c->%i,", entry[i], code.code[i]);
}
lament("\n");
#endif
return decode_data(&code);
}
static void print_code(const code_t *code) {
for (int j = 0; j < 9; j++) {
int index = code->code[j];
printf("%c", lut[index][0]);
}
printf("\n");
}
static u64 prng_state = 1;
static u32 prng() {
prng_state = 3935559000370003845 * prng_state + 1;
return prng_state ^ (prng_state >> 32);
}
int bruteforce(int count) {
/* with a fixed seed, this is useful for testing.
the first 10 results should be:
8RPEHR8R4
MNXKWQMNE
CE922RCER
MQ1KMQMQG
2N9GGR2NR
25L53R250
15R09R159
9R9LMQ9RR
L312MQL3G
P93M6QP9N
*/
int found = 0;
code_t code = {0};
while (found < count) {
for (int j = 0; j < 9; j++) {
code.code[j] = prng() % lutlen;
}
if (decode_data(&code)) {
found++;
print_code(&code);
}
}
return 1;
}
#ifndef TENNIS_NO_MAIN
int main(int argc, char **argv) {
if (argc == 1) {
// self-test with codes known before this program was written.
#define TEST(entry) if (!validate_entry(entry)) return -1
TEST("48HWOR482");
TEST("5G3LTQ5GN");
TEST("A3W5KQA3C");
TEST("ARM6JQARU");
TEST("E880MPE8K");
TEST("E8ULJRE8M");
TEST("EPJEGREP5");
TEST("GH4KNQGHP");
TEST("H6L3MPH60");
TEST("HH4KNQHHP");
TEST("J6M9PQJ6U");
TEST("JEP8YQJE4");
TEST("LA98JRLAR");
TEST("LQM1MPLQU");
TEST("LTHWYQLT2");
TEST("M1C2YQM1W");
TEST("MM55MQMMJ");
TEST("N24K8QN2P");
TEST("OF9XFQOFR");
TEST("P4K6GRP48");
TEST("TE6WARTEQ");
TEST("TQJEGRTQ5");
TEST("UOUFMPUOM");
TEST("V2UFMPUZM");
TEST("W2HEGRW22");
TEST("WQJEGRWQ5");
TEST("WRWQARWRC");
TEST("YQJEGRYQ5");
#undef TEST
return 0;
}
code_t code = {0};
int code_i = 0;
int invalid_count = 0;
for (int i = 1; i < argc; i++) {
const char *arg = argv[i];
if (strlen(arg) == 9) {
//if (validate_entry(arg)) printf("%s\n", arg);
invalid_count += !validate_entry(arg);
} else if (!strcmp(arg, "bruteforce")) {
invalid_count += !bruteforce(10);
} else {
u32 datum = strtoul(arg, NULL, 0);
code.data[code_i] = datum;
code_i++;
if (code_i >= 7) {
code_i = 0;
if (encode_data(&code)) {
print_code(&code);
} else {
invalid_count++;
}
}
}
}
return invalid_count;
}
#endif

11
mips_disassembler/Makefile Executable file
View File

@ -0,0 +1,11 @@
#CFLAGS = -ggdb -Wall
CFLAGS = -Ofast -Wall
install: all
cp zadis /usr/bin/zadis
all:
$(CC) $(CFLAGS) -o zadis adis.c
clean:
rm -vf adis

1634
mips_disassembler/adis.c Executable file

File diff suppressed because it is too large Load Diff

10
music_sync/convert.py Normal file
View File

@ -0,0 +1,10 @@
import subprocess as sp
import os
def ogg(fin, fout):
p1 = sp.Popen(["ffmpeg", "-loglevel", "error", "-i", fin, "-f", "flac", "-"], stdout=sp.PIPE)
p2 = sp.Popen(["oggenc", "-Q", "-q", "5", "-", "-o", fout], stdin=p1.stdout, stdout=sp.PIPE)
p1.stdout.close()
p2.communicate()
ret = p1.poll() or p2.poll()
return ret

117
music_sync/mutaext.py Normal file
View File

@ -0,0 +1,117 @@
from collections import MutableMapping
import mutagenx
import mutagenx.id3
from mutagenx.easyid3 import EasyID3
def popms(id3):
for k, v in id3.items():
if k.startswith('POPM'):
yield k, v
def byte2rating(b):
if b >= 224: return 5
if b >= 160: return 4
if b >= 96: return 3
if b >= 32: return 2
if b >= 1: return 1
return 0
def rating2byte(r):
if r == 5: return 256
if r == 4: return 192
if r == 3: return 128
if r == 2: return 64
if r == 1: return 1
return 0
def rating_get(id3, key):
if 'TXXX:RATING' in id3:
rating = id3['TXXX:RATING']
return list(rating.text)
else:
try:
_, popm = next(popms(id3))
except StopIteration:
return []
else:
return [str(byte2rating(popm.rating))]
def _canconv(r):
try:
ir = int(r)
if ir != str(ir):
return False
return ir >= 1 and ir <= 5
except (ValueError, TypeError):
return False
def rating_set(id3, key, val):
rating_delete(id3, key)
if _canconv(val):
popm = mutagenx.id3.POPM()
popm.email = "Windows Media Player 9 Series"
popm.count = 0
popm.rating = rating2byte(int(val))
id3.add(popm)
else:
if 'TXXX:RATING' in id3:
del(id3['TXXX:RATING'])
id3.add(mutagenx.id3.TXXX(encoding=3, desc='RATING', text=str(val)))
def rating_delete(id3, key):
for k, v in popms(id3):
del(id3[k])
if 'TXXX:RATING' in id3:
del(id3['TXXX:RATING'])
replaygain_tags = ('replaygain_album_gain', 'replaygain_album_peak', \
'replaygain_track_gain', 'replaygain_track_peak')
for tag in replaygain_tags:
EasyID3.RegisterTXXXKey(tag, tag)
extra_tags = ('sync', 'totaltracks', 'totaldiscs')
for tag in extra_tags:
EasyID3.RegisterTXXXKey(tag, tag.upper())
EasyID3.RegisterTextKey('albumartist', 'TPE2')
EasyID3.RegisterKey('rating', rating_get, rating_set, rating_delete)
class SyncFile(MutableMapping):
def __init__(self, path):
self.md = mutagenx.File(path, easy=True)
self.path = path
self.seen = False
def __getitem__(self, key):
if self.md == None:
print(self.path)
d = self.md[key]
try:
return d[0]
except IndexError:
raise KeyError(key)
def __setitem__(self, key, value):
#if type(value) != str:
#raise ValueError
if type(value) is type(None):
raise ValueError
self.md[key] = [value]
def __delitem__(self, key):
del(self.md[key])
def __iter__(self):
for k in self.md:
try:
self.__getitem__(k)
except KeyError:
pass
else:
yield k
def __len__(self):
return len([k for k in self.__iter__()])
def save(self):
return self.md.save()

172
music_sync/unsync.py Executable file
View File

@ -0,0 +1,172 @@
#!/bin/python
import os, os.path
import sys
from shutil import copy2
from tempfile import mkstemp
from zlib import crc32
import mutaext
import convert
from mutaext import SyncFile
goodexts = ('.mp3', '.flac', '.ogg')
matchtags = ['artist', 'album', 'title', 'tracknumber', 'discnumber']
alltags = [
'albumartist', 'composer', 'comment',
'genre', 'date',
]
alltags.extend(mutaext.replaygain_tags)
alltags.extend(mutaext.extra_tags)
alltags.extend(matchtags)
lament = lambda *args, **kwargs: print(*args, file=sys.stderr, **kwargs)
walkfiles = lambda w: (os.path.join(r, f) for r, _, fs in w for f in fs)
extof = lambda p: os.path.splitext(p)[1].lower()
filterext = lambda ps, es: (p for p in ps if extof(p) in es)
def shouldsync(md):
rating = md.get('rating', '')
sync = md.get('sync', '')
if rating.isnumeric():
rating = int(rating)
if sync:
sync = sync.lower()
return sync == 'yes' or type(rating) == int and rating >= 3 and sync != 'no' and sync != 'space'
import re
re_digits = re.compile(r'\d+')
def tonumber(crap):
if crap is None or len(crap) == 0:
return 0
nums = re_digits.findall(crap)
if len(nums) == 0:
return 0
return int(nums[0])
def fixmetadata(md):
md['artist'] = md.get('artist', "Unknown Artist")
md['album'] = md.get('album', "Unknown Album")
md['discnumber'] = str(tonumber(md.get('discnumber', '0')))
md['tracknumber'] = str(tonumber(md.get('tracknumber', '0')))
if 'title' not in md:
fn = os.path.basename(md.path)
fn = os.path.splitext(fn)[0]
md['title'] = str(fn)
def findmatching(haystack, needle):
#matchme = [needle[t].lower() for t in matchtags]
#ismatch = lambda hay: [hay[t].lower() for t in matchtags] == matchme
matchme = [needle[t] for t in matchtags]
ismatch = lambda hay: [hay[t] for t in matchtags] == matchme
for match in (hay for hay in haystack if ismatch(hay)):
if match.seen:
# TODO: check other tags too?
lament("Duplicate")
return None
match.seen = needle.path
return match
def updatemetadata(mdold, mdnew):
modified = False
for tag in alltags:
if tag in mdnew:
if tag not in mdold or mdnew[tag] != mdold[tag]:
mdold[tag] = mdnew[tag]
modified = True
elif tag in mdold:
del mdold[tag]
modified = True
return modified
def makefilename(md):
title = md['title']
artist = md['artist']
album = md['album']
track = md['tracknumber']
disc = md['discnumber']
fn = "%(disc)s-%(track)s - %(artist)s - %(album)s - %(title)s" % locals()
# FAT is a pain to deal with so just use nondescript filenames
crc = crc32(fn.encode('utf-8')) & 0xFFFFFFFF
fn = '{:08X}.ogg'.format(crc)
return fn
def run(args):
if not len(args) in (2, 3):
lament("I need a path or two!")
return 1
inonly = len(args) == 2
tosync = []
indir = args[1]
paths = lambda dir: filterext(walkfiles(os.walk(dir)), goodexts)
for p in paths(indir):
md = SyncFile(p)
if shouldsync(md):
if inonly:
print(p)
else:
fixmetadata(md)
tosync.append(md)
if inonly:
return 0
lament("Matching tags...")
outdir = args[2]
for p in paths(outdir):
md = SyncFile(p)
fixmetadata(md)
match = findmatching(tosync, md)
if match == None:
print("DEL", p)
print('was', md['title'], 'by', md['artist'])
os.remove(p)
elif updatemetadata(md, match):
print("UPD", p)
md.save()
lament("Syncing files...")
for md in tosync:
fn = makefilename(md)
fout = os.path.join(outdir, fn)
if md.seen:
_from = md.seen
_to = fout
if _from != _to:
print("MOV", _from)
os.rename(_from, _to)
continue
print("ADD", md.path)
_, ftemp = mkstemp()
try:
convert.ogg(md.path, ftemp)
mdnew = SyncFile(ftemp)
for tag in alltags:
if tag in md:
mdnew[tag] = md[tag]
fixmetadata(mdnew) # redundant?
mdnew.save()
copy2(ftemp, fout)
finally:
os.remove(ftemp)
return 0
if __name__ == '__main__':
ret = 0
try:
ret = run(sys.argv)
except KeyboardInterrupt:
sys.exit(1)
sys.exit(ret)

139
n64_models/pot3d.py Normal file
View File

@ -0,0 +1,139 @@
# hi this is super hacky and only converts a tiny segment of display lists.
# it's really just to get a feel for how the formats work.
# after extracting/decompressing OoT, run this script on object_tsubo.
# the top of the pot is missing, i know. see the comment above.
import array
import io
import struct
import sys
name = "pot"
tw, th = 32, 64 # hardcoded texture width and height
viscale = 256 # inverted scale, this is arbitrary
v_base = 0x1838 # offset to first G_VTX command
with open(sys.argv[1], "rb") as f:
data = f.read()
f = io.BytesIO(data)
# hardcoded for a rga5a1 texture at the start of the file:
pix = array.array('H', f.read(tw * th * 2))
pix.byteswap()
rgbs = []
for p in pix:
r = (p & 0xF800) >> 11
g = (p & 0x07C0) >> 6
b = (p & 0x003E) >> 1
a = p & 1
# TODO: round color calculations. or not? i don't imagine the N64 bothers.
rgb = (r * 255 // 31, g * 255 // 31, b * 255 // 31)
rgbs.append(rgb)
verts = []
texes = []
norms = [] # unimplemented
polys = []
vi = 0 # vertex index to offset by (incremented after each chunk)
f.seek(v_base)
opcode = ord(f.read(1))
while opcode == 0x01: # G_VTX
counts = f.read(3)
numv = ((counts[0] & 0xF) << 4) | ((counts[1] & 0xF0) >> 4)
vbidx = counts[2] // 2 - numv
vaddr = struct.unpack(">i", f.read(4))[0]
back = f.tell()
f.seek(vaddr & 0xFFFFFF)
for i in range(numv):
if 0:
# colored vertices
vertex = struct.unpack(">hhhHhhBBBB", f.read(16))
x, y, z, w, tx, ty, r, g, b, a = vertex
else:
# lit vertices
vertex = struct.unpack(">hhhHhhbbbB", f.read(16))
x, y, z, w, tx, ty, n, p, q, a = vertex
pos = (x / viscale, y / viscale, z / viscale)
# FIXME: texture coordinates are slightly off
tpos = ((tx / 32 / tw), 1 - (ty / 32 / th))
verts.append(pos)
texes.append(tpos)
f.seek(back)
while 1:
opcode = ord(f.read(1))
if opcode not in (6, 5):
break
if opcode == 5:
indices = struct.unpack('>bbbbbbb', f.read(7))
a0, a1, a2, _, _, _, _ = indices
atri = a0 // 2 + 1 + vi, a1 // 2 + 1 + vi, a2 // 2 + 1 + vi
polys.append(atri)
elif opcode == 6:
indices = struct.unpack('>bbbbbbb', f.read(7))
a0, a1, a2, _, b0, b1, b2 = indices
# TODO: assert all indices are in range(32)
atri = a0 // 2 + 1 + vi, a1 // 2 + 1 + vi, a2 // 2 + 1 + vi
btri = b0 // 2 + 1 + vi, b1 // 2 + 1 + vi, b2 // 2 + 1 + vi
polys.append(atri)
polys.append(btri)
vi = len(verts)
# write the model file
with open("{}.obj".format(name), "w") as f:
fprint = lambda *args, **kwargs: print(*args, file=f, **kwargs)
fprint("mtllib {}.mtl".format(name))
fprint("o {}".format(name))
for vert in verts:
fprint("v", *("{:.8f}".format(v) for v in vert))
for tex in texes:
fprint("vt", *("{:.8f}".format(v) for v in tex))
#fprint("g {}".format(name))
fprint("usemtl {}".format(name))
fprint("s off")
for poly in polys:
fprint("f", *("{}/{}".format(i, i) for i in poly))
# write the material file
with open("{}.mtl".format(name), "w") as f:
fprint = lambda *args, **kwargs: print(*args, file=f, **kwargs)
fprint("newmtl {}".format(name))
fprint("Ns 0.0")
# i don't know what any of these do but they all look terrible
fprint("Ka 1.0 1.0 1.0")
fprint("Kd 0.8 0.8 0.8")
fprint("Ks 0.0 0.0 0.0")
fprint("Ke 0.0 0.0 0.0")
fprint("d 1.0")
fprint("illum 0")
fprint("map_Kd {}.bmp".format(name))
#fprint("map_Ka {}.bmp".format(name))
# write the texture file
with open("{}.bmp".format(name), "wb") as f:
f.write(b'BM')
# format: 32-bit BGRA
# everything else: sane default
f.write(struct.pack("<ihhi", len(rgbs) * 4 + 14, 0, 0, 14 + 40))
f.write(struct.pack("<iiihhiiiiii", 40, tw, th, 1, 32, 0, 0, 0, 0, 0, 0))
for rgb in reversed(rgbs):
r, g, b = rgb
a = 0xFF
f.write(struct.pack("<BBBB", b, g, r, a))

View File

@ -0,0 +1,98 @@
// C port of http://ldesoras.free.fr/prod.html#src_hiir
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <errno.h>
const double PI = 3.1415926535897932384626433832795;
void
compute_transition_param(double *kp, double *qp, double transition)
{
double k = *kp;
double q = *qp;
k = tan((1 - transition*2)*PI/4);
k *= k;
double kksqrt = pow(1 - k*k, 0.25);
double e = 0.5*(1 - kksqrt)/(1 + kksqrt);
double e4 = e*e*e*e;
q = e*(1 + e4*(2 + e4*(15 + 150*e4)));
*kp = k;
*qp = q;
}
double
compute_acc_numden(double q, int order, int c, int den)
{
den = den == 1;
int i = den;
int sign = den ? -1 : 1;
double sum = 0;
double q_ii;
do {
int i2 = i + 1 - den;
q_ii = pow(q, i*i2);
q_ii *= sin((i + i2)*c*PI/order + den*PI/2);
q_ii *= sign;
sum += q_ii;
sign = -sign;
++i;
} while (fabs(q_ii) > 1e-100);
return sum;
}
double
compute_coef(int c, double k, double q, int order)
{
double num = compute_acc_numden(q, order, c, 0)*pow(q, 0.25);
double den = compute_acc_numden(q, order, c, 1) + 0.5;
double ww = num/den;
ww *= ww;
double x = sqrt((1 - ww*k)*(1 - ww/k))/(1 + ww);
double coef = (1 - x)/(1 + x);
return coef;
}
void
compute_coefs_spec_order_tbw(double coef_arr[], int n, double transition)
{
double k;
double q;
compute_transition_param(&k, &q, transition);
const int order = n*2 + 1;
/*printf("k: %.18f\nq: %.18f\n", k, q);*/
for (int i = 0; i < n; i++)
coef_arr[i] = compute_coef(i + 1, k, q, order);
}
int
main(int argc, char **argv)
{
if (argc != 3) {
fputs("usage: halfband COUNT TRANSITION\n", stderr);
return 1;
}
double tv[2];
for (int i = 1; i <= 2; i++) {
tv[i - 1]=strtold(argv[i], NULL);
if (errno) {
fprintf(stderr, "arg #%i failed to convert to double\n", i);
return 1;
}
}
int count = (int) tv[0];
double *coefs = malloc(count*sizeof(double));
compute_coefs_spec_order_tbw(coefs, count, tv[1]);
for (int i = 0; i < count; i++)
printf("%.18f\n", coefs[i]);
free(coefs);
return 0;
}

246
print_tables/_G.yml Normal file
View File

@ -0,0 +1,246 @@
__root: &__root_t0x4004f960
_G: *__root_t0x4004f960
_VERSION: Lua 5.1
arg: &arg_t0x40055cc8
"-1": luajit
0: run.lua
assert: fbuiltin#2
bit: &bit_t0x40054d58
arshift: fbuiltin#70
band: fbuiltin#73
bnot: fbuiltin#66
bor: fbuiltin#74
bswap: fbuiltin#67
bxor: fbuiltin#75
lshift: fbuiltin#68
rol: fbuiltin#71
ror: fbuiltin#72
rshift: fbuiltin#69
tobit: fbuiltin#65
tohex: fbuiltin#76
collectgarbage: fbuiltin#27
coroutine: &coroutine_t0x40051730
create: fbuiltin#32
resume: fbuiltin#34
running: fbuiltin#31
status: fbuiltin#30
wrap: fbuiltin#36
yield: fbuiltin#33
debug: &debug_t0x40054740
debug: fbuiltin#145
getfenv: fbuiltin#134
gethook: fbuiltin#144
getinfo: fbuiltin#136
getlocal: fbuiltin#137
getmetatable: fbuiltin#132
getregistry: fbuiltin#131
getupvalue: fbuiltin#139
setfenv: fbuiltin#135
sethook: fbuiltin#143
setlocal: fbuiltin#138
setmetatable: fbuiltin#133
setupvalue: fbuiltin#140
traceback: fbuiltin#146
upvalueid: fbuiltin#141
upvaluejoin: fbuiltin#142
dofile: fbuiltin#25
dump: f0x4005dd40
error: fbuiltin#19
gcinfo: fbuiltin#26
getfenv: fbuiltin#10
getmetatable: fbuiltin#8
io: &io_t0x40052a90
close: fbuiltin#112
flush: fbuiltin#115
input: fbuiltin#116
lines: fbuiltin#118
open: fbuiltin#109
output: fbuiltin#117
popen: fbuiltin#110
read: fbuiltin#113
stderr: file (0x7ff9dd21f500)
stdin: file (0x7ff9dd21e8a0)
stdout: file (0x7ff9dd21f5e0)
tmpfile: fbuiltin#111
type: fbuiltin#119
write: fbuiltin#114
ipairs: fbuiltin#7
jit: &jit_t0x40055318
arch: x64
attach: fbuiltin#151
flush: fbuiltin#149
off: fbuiltin#148
on: fbuiltin#147
opt: &opt_t0x40055bd8
start: fbuiltin#163
os: Linux
status: fbuiltin#150
util: &util_t0x400556a0
funcbc: fbuiltin#153
funcinfo: fbuiltin#152
funck: fbuiltin#154
funcuvname: fbuiltin#155
ircalladdr: fbuiltin#162
traceexitstub: fbuiltin#161
traceinfo: fbuiltin#156
traceir: fbuiltin#157
tracek: fbuiltin#158
tracemc: fbuiltin#160
tracesnap: fbuiltin#159
version: LuaJIT 2.0.4
version_num: 20004
load: fbuiltin#23
loadfile: fbuiltin#22
loadstring: fbuiltin#24
math: &math_t0x40053b60
abs: fbuiltin#37
acos: fbuiltin#47
asin: fbuiltin#46
atan: fbuiltin#48
atan2: fbuiltin#57
ceil: fbuiltin#39
cos: fbuiltin#44
cosh: fbuiltin#50
deg: fbuiltin#54
exp: fbuiltin#42
floor: fbuiltin#38
fmod: fbuiltin#59
frexp: fbuiltin#52
huge: inf
ldexp: fbuiltin#60
log: fbuiltin#56
log10: fbuiltin#41
max: fbuiltin#62
min: fbuiltin#61
mod: fbuiltin#59
modf: fbuiltin#53
pi: 3.1415926535898
pow: fbuiltin#58
rad: fbuiltin#55
random: fbuiltin#63
randomseed: fbuiltin#64
sin: fbuiltin#43
sinh: fbuiltin#49
sqrt: fbuiltin#40
tan: fbuiltin#45
tanh: fbuiltin#51
module: f0x40051ee0
newproxy: fbuiltin#28
next: fbuiltin#4
os: &os_t0x40052fd0
clock: fbuiltin#126
date: fbuiltin#127
difftime: fbuiltin#129
execute: fbuiltin#120
exit: fbuiltin#125
getenv: fbuiltin#124
remove: fbuiltin#121
rename: fbuiltin#122
setlocale: fbuiltin#130
time: fbuiltin#128
tmpname: fbuiltin#123
package: &package_t0x40051ac8
config:
/
;
?
!
-
cpath: /home/notwa/opt/local/lib/?.so;/home/notwa/.luarocks/lib/lua/5.1/?.so;/home/notwa/opt/local/lib/lua/5.1/?.so;./?.so;/usr/local/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/loadall.so
loaded: &loaded_t0x40050b38
_G: *__root_t0x4004f960
bit: *bit_t0x40054d58
coroutine: *coroutine_t0x40051730
debug: *debug_t0x40054740
extra: &extra_t0x4005cd30
add_zeros: f0x4005db58
mixed_sorter: f0x4005db98
opairs: f0x4005cca8
order_keys: f0x4005b2f8
strpad: f0x4004f8b8
traverse: f0x4005cce8
io: *io_t0x40052a90
jit: *jit_t0x40055318
"jit.opt": *opt_t0x40055bd8
"jit.util": *util_t0x400556a0
math: *math_t0x40053b60
os: *os_t0x40052fd0
package: *package_t0x40051ac8
pt: &pt_t0x4005ce20
__metatable: *pt_t0x4005ce20
__call: f0x4005dc98
__index: *pt_t0x4005ce20
inner: f0x4005dde0
outer: f0x4005dd18
outer_old: f0x4005dc00
safecanon: f0x4005dd60
safekey: f0x4005dd80
safeval: f0x4005ddc0
write: f0x4005dcd8
string: *string_t0x400534b0
table: *table_t0x400522f8
loaders: &loaders_t0x40051c38
1: f0x40051c88
2: f0x40051cb0
3: f0x40051cd8
4: f0x40051d00
loadlib: f0x40051b58
path: /home/notwa/.luarocks/share/lua/5.1/?.lua;/home/notwa/.luarocks/share/lua/5.1/?/init.lua;/home/notwa/opt/local/share/lua/5.1/?.lua;/home/notwa/opt/local/share/lua/5.1/?/init.lua;./?.lua;/home/notwa/opt/local/share/luajit-2.1.0-beta1/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua
preload: &preload_t0x400520c0
ffi: f0x40055c80
searchpath: f0x40051ba0
seeall: f0x40051bf0
pairs: fbuiltin#5
pcall: fbuiltin#20
print: fbuiltin#29
rawequal: fbuiltin#14
rawget: fbuiltin#12
rawset: fbuiltin#13
require: f0x40051f28
select: fbuiltin#16
setfenv: fbuiltin#11
setmetatable: fbuiltin#9
string: &string_t0x400534b0
byte: fbuiltin#78
char: fbuiltin#79
dump: fbuiltin#85
find: fbuiltin#86
format: fbuiltin#91
gfind: fbuiltin#89
gmatch: fbuiltin#89
gsub: fbuiltin#90
len: fbuiltin#77
lower: fbuiltin#83
match: fbuiltin#87
rep: fbuiltin#81
reverse: fbuiltin#82
sub: fbuiltin#80
upper: fbuiltin#84
t: &t_t0x4005e298
A: hello
B: &B_t0x4005e328
a: beep
b: boop
c: burp
d: *d_t0x4005e550
C: &d_t0x4005e550
a: nude
b: dude
c: lewd
d: *B_t0x4005e328
D: goodbye
E: *t_t0x4005e298
table: &table_t0x400522f8
concat: fbuiltin#98
foreach: fbuiltin#93
foreachi: fbuiltin#92
getn: fbuiltin#94
insert: fbuiltin#96
maxn: fbuiltin#95
remove: fbuiltin#97
sort: fbuiltin#99
tonumber: fbuiltin#17
tostring: fbuiltin#18
type: fbuiltin#3
unpack: fbuiltin#15
xpcall: fbuiltin#21

69
print_tables/extra.lua Executable file
View File

@ -0,0 +1,69 @@
local insert = table.insert
local pairs = pairs
local rawget = rawget
local sort = table.sort
local tostring = tostring
local type = type
local function strpad(num, count, pad)
num = tostring(num)
return (pad:rep(count)..num):sub(#num)
end
local function add_zeros(num, count)
return strpad(num, count - 1, '0')
end
local function mixed_sorter(a, b)
a = type(a) == 'number' and add_zeros(a, 16) or tostring(a)
b = type(b) == 'number' and add_zeros(b, 16) or tostring(b)
return a < b
end
-- loosely based on http://lua-users.org/wiki/SortedIteration
-- the original didn't make use of closures for who knows why
local function order_keys(t)
local oi = {}
for key in pairs(t) do
insert(oi, key)
end
sort(oi, mixed_sorter)
return oi
end
local function opairs(t, cache)
local oi = cache and cache[t] or order_keys(t)
if cache then
cache[t] = oi
end
local i = 0
return function()
i = i + 1
local key = oi[i]
if key ~= nil then return key, t[key] end
end
end
local function traverse(path)
if not path then return end
local parent = _G
local key
for w in path:gfind("[%w_]+") do
if key then
parent = rawget(parent, key)
if type(parent) ~= 'table' then return end
end
key = w
end
if not key then return end
return {parent=parent, key=key}
end
return {
strpad = strpad,
add_zeros = add_zeros,
mixed_sorter = mixed_sorter,
order_keys = order_keys,
opairs = opairs,
traverse = traverse,
}

2
print_tables/init.lua Normal file
View File

@ -0,0 +1,2 @@
local path = string.gsub(..., "[^.]+$", "")
return require(path.."pt")

203
print_tables/pt.lua Executable file
View File

@ -0,0 +1,203 @@
local path = string.gsub(..., "[^.]+$", "")
local extra = require(path.."extra")
local opairs = extra.opairs
local pt = {}
pt.__index = pt
setmetatable(pt, pt)
local function rawstr(v)
if v == nil then return 'nil' end
local mt = getmetatable(v)
local ts = mt and rawget(mt, '__tostring')
if not ts then return tostring(v) end
mt.__tostring = nil
local s = tostring(v)
mt.__tostring = ts
return s
end
local function getaddr(t)
return rawstr(t):sub(#type(t) + 3)
end
local function copy(t)
-- shallow copy
if type(t) ~= 'table' then return end
local new = {}
for k,v in pairs(t) do
new[k] = v
end
return new
end
function pt.__call(pt, args)
-- print a table as semi-valid YAML
-- with references to prevent recursion/duplication
local t = args.table or args[1]
local self = {}
setmetatable(self, pt)
self.seen = copy(args.seen) or {}
self.skipped = copy(args.skipped) or {}
self.seen_elsewhere = args.seen or {}
self.depth = args.depth or 16
self.writer = args.writer or io.write
self.skeleton = args.skeleton or false
self.outer = args.alt_order and self.outer_old or self.outer
self.noncanon = args.noncanon or false
self.indent = args.indent or ' '
self.queued = {}
self.cache = {}
self.canonicalized = {}
self:inner('__root', t, '')
return self.seen
end
function pt:write(...)
self.writer(...)
end
function pt:safecanon(k)
local s = tostring(k)
return s:gsub('[^%w_]', '_')
end
function pt:safekey(k)
if type(k) == 'table' then
return 't'..getaddr(k)
end
local s = tostring(k)
s = s:gsub('[\r\n]', '')
return s:find('[^%w_]') and ('%q'):format(s) or s
end
function pt:safeval(v, indentation)
if type(v) == 'function' then
return 'f'..getaddr(v)
end
local s = tostring(v)
if type(v) == 'number' then
return s
end
-- TODO: move newline/indentation handling to another function?
if s:find('[\r\n]') then
s = ('\n'..s):gsub('[\r\n]', '\n'..indentation..self.indent)
end
--local safe = ('%q'):format(s)
--return s == safe:sub(2, -2) and s or safe
-- TODO: finish matching valid characters
return s:find('[^%w_()[]{}.]') and ('%q'):format(s) or s
end
function pt:inner(k, v, indentation)
if type(v) ~= 'table' then
if self.skeleton then return end
self:write(indentation, self:safekey(k), ': ')
self:write(self:safeval(v, indentation), '\n')
return
end
local addr = getaddr(v)
self:write(indentation, self:safekey(k))
local canon
if not self.noncanon and type(k) ~= 'table' then
canon = self.canonicalized[addr]
if canon == nil then
canon = self:safecanon(k)..'_t'..addr
self.canonicalized[addr] = canon
end
else
canon = 't'..addr
end
if #indentation > self.depth or self.skipped[addr] then
--self.skipped[addr] = true -- TODO: extra logics
self:write(': #', canon, '\n')
return
end
if self.seen[addr] or self.queued[addr] then
self:write(': *', canon, self.seen_elsewhere[addr] and ' #\n' or '\n')
return
end
self.seen[addr] = true
self:write(': &', canon, '\n')
self:outer(v, indentation..self.indent)
end
function pt:outer_old(t, indentation)
if type(t) ~= "table" then
local s = self:safeval(t, indentation)
self:write(indentation, s, '\n')
return
end
local ours = {}
local not_ours = {}
for k,v in opairs(t) do
if type(v) == 'table' then
local addr = getaddr(v)
if not (self.queued[addr] or self.seen[addr] or self.skipped[addr]) then
self.queued[addr] = true
ours[k] = v
else
not_ours[k] = v
end
else
self:inner(k, v, indentation)
end
end
for k,v in opairs(not_ours) do
self:inner(k, v, indentation)
end
for k,v in opairs(ours) do
self.queued[getaddr(v)] = nil
self:inner(k, v, indentation)
end
local mt = getmetatable(t)
if mt ~= nil then
self:inner('__metatable', mt, indentation)
end
end
function pt:outer(t, indentation)
if type(t) ~= "table" then
local s = self:safeval(t, indentation)
self:write(indentation, s, '\n')
return
end
local ours = {}
for k,v in opairs(t, self.cache) do
if type(v) == 'table' then
local addr = getaddr(v)
if not (self.queued[addr] or self.seen[addr] or self.skipped[addr]) then
self.queued[addr] = true
ours[k] = addr
end
end
end
local mt = getmetatable(t)
if mt ~= nil then
self:inner('__metatable', mt, indentation)
end
for k,v in opairs(t, self.cache) do
local addr = ours[k]
if addr then
self.queued[addr] = nil
end
self:inner(k, v, indentation)
end
end
return pt

41
print_tables/run.lua Executable file
View File

@ -0,0 +1,41 @@
#!/usr/bin/lua
local pt = require('pt')
t = {
A = 'hello',
B = {
a = 'beep',
b = 'boop',
c = 'burp',
},
C = {
a = 'nude',
b = 'dude',
c = 'lewd',
},
D = 'goodbye',
}
t.B.d = t.C
t.C.d = t.B
t.E = t
function dump(t, fn, seen)
if t == nil then return end
local file = io.open(fn, "w")
if not file then
io.write("Failed opening ", fn, "\n")
return
end
local writer = function(...)
file:write(...)
end
seen = pt{t, writer=writer, seen=seen}
file:close()
return seen
end
pt{t}
dump(_G, '_G.yml')

478
psnip_clock/clock.h Normal file
View File

@ -0,0 +1,478 @@
/* Clocks (v1)
* Portable Snippets - https://github.com/nemequ/portable-snippets
* Created by Evan Nemerson <evan@nemerson.com>
*
* To the extent possible under law, the authors have waived all
* copyright and related or neighboring rights to this code. For
* details, see the Creative Commons Zero 1.0 Universal license at
* https://creativecommons.org/publicdomain/zero/1.0/
*
* Modified by Connor Olding, 2017
*/
#if !defined(PSNIP_CLOCK_H)
#define PSNIP_CLOCK_H
#if !defined(PSNIP_CLOCK_STATIC_INLINE)
# if defined(__GNUC__)
# define PSNIP_CLOCK__COMPILER_ATTRIBUTES __attribute__((__unused__))
# else
# define PSNIP_CLOCK__COMPILER_ATTRIBUTES
# endif
# define PSNIP_CLOCK__FUNCTION PSNIP_CLOCK__COMPILER_ATTRIBUTES static
#endif
enum PsnipClockType {
/* This clock provides the current time, in units since 1970-01-01
* 00:00:00 UTC not including leap seconds. In other words, UNIX
* time. Keep in mind that this clock doesn't account for leap
* seconds, and can go backwards (think NTP adjustments). */
PSNIP_CLOCK_TYPE_WALL = 1,
/* The CPU time is a clock which increases only when the current
* process is active (i.e., it doesn't increment while blocking on
* I/O). */
PSNIP_CLOCK_TYPE_CPU = 2,
/* Monotonic time is always running (unlike CPU time), but it only
ever moves forward unless you reboot the system. Things like NTP
adjustments have no effect on this clock. */
PSNIP_CLOCK_TYPE_MONOTONIC = 3
};
struct PsnipClockTimespec {
uint64_t seconds;
uint64_t nanoseconds;
};
/* Methods we support: */
#define PSNIP_CLOCK_METHOD_CLOCK_GETTIME 1
#define PSNIP_CLOCK_METHOD_TIME 2
#define PSNIP_CLOCK_METHOD_GETTIMEOFDAY 3
#define PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER 4
#define PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME 5
#define PSNIP_CLOCK_METHOD_CLOCK 6
#define PSNIP_CLOCK_METHOD_GETPROCESSTIMES 7
#define PSNIP_CLOCK_METHOD_GETRUSAGE 8
#define PSNIP_CLOCK_METHOD_GETSYSTEMTIMEPRECISEASFILETIME 9
#define PSNIP_CLOCK_METHOD_GETTICKCOUNT64 10
#include <assert.h>
#if defined(HEDLEY_UNREACHABLE)
# define PSNIP_CLOCK_UNREACHABLE() HEDLEY_UNREACHABLE()
#else
# define PSNIP_CLOCK_UNREACHABLE() assert(0)
#endif
/* Choose an implementation */
/* #undef PSNIP_CLOCK_WALL_METHOD */
/* #undef PSNIP_CLOCK_CPU_METHOD */
/* #undef PSNIP_CLOCK_MONOTONIC_METHOD */
/* We want to be able to detect the libc implementation, so we include
<limits.h> (<features.h> isn't available everywhere). */
#if defined(__unix__) || defined(__unix) || defined(__linux__)
# include <limits.h>
# include <unistd.h>
#endif
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
/* These are known to work without librt. If you know of others
* please let us know so we can add them. */
# if \
(defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17))) || \
(defined(__FreeBSD__))
# define PSNIP_CLOCK_HAVE_CLOCK_GETTIME
# elif !defined(PSNIP_CLOCK_NO_LIBRT)
# define PSNIP_CLOCK_HAVE_CLOCK_GETTIME
# endif
#endif
#if defined(_WIN32)
# if !defined(PSNIP_CLOCK_CPU_METHOD)
# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_GETPROCESSTIMES
# endif
# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER
# endif
#endif
#if defined(__MACH__)
# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME
# endif
#endif
#if defined(PSNIP_CLOCK_HAVE_CLOCK_GETTIME)
# include <time.h>
# if !defined(PSNIP_CLOCK_WALL_METHOD)
# if defined(CLOCK_REALTIME_PRECISE)
# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME_PRECISE
# elif !defined(__sun)
# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_WALL CLOCK_REALTIME
# endif
# endif
# if !defined(PSNIP_CLOCK_CPU_METHOD)
# if defined(_POSIX_CPUTIME) || defined(CLOCK_PROCESS_CPUTIME_ID)
# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_PROCESS_CPUTIME_ID
# elif defined(CLOCK_VIRTUAL)
# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_CPU CLOCK_VIRTUAL
# endif
# endif
# if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
# if defined(CLOCK_MONOTONIC_RAW) && !defined(__EMSCRIPTEN__)
# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC_RAW
# elif defined(CLOCK_MONOTONIC_PRECISE)
# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC_PRECISE
# elif defined(_POSIX_MONOTONIC_CLOCK) || defined(CLOCK_MONOTONIC)
# define PSNIP_CLOCK_MONOTONIC_METHOD PSNIP_CLOCK_METHOD_CLOCK_GETTIME
# define PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC CLOCK_MONOTONIC
# endif
# endif
#endif
#if defined(_POSIX_VERSION) && (_POSIX_VERSION >= 200112L)
# if !defined(PSNIP_CLOCK_WALL_METHOD)
# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_GETTIMEOFDAY
# endif
#endif
#if !defined(PSNIP_CLOCK_WALL_METHOD)
# define PSNIP_CLOCK_WALL_METHOD PSNIP_CLOCK_METHOD_TIME
#endif
#if !defined(PSNIP_CLOCK_CPU_METHOD)
# define PSNIP_CLOCK_CPU_METHOD PSNIP_CLOCK_METHOD_CLOCK
#endif
/* Primarily here for testing. */
#if !defined(PSNIP_CLOCK_MONOTONIC_METHOD) && defined(PSNIP_CLOCK_REQUIRE_MONOTONIC)
# error No monotonic clock found.
#endif
/* Implementations */
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK)) || \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_TIME))
# include <time.h>
#endif
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY))
# include <sys/time.h>
#endif
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES)) || \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64))
typedef struct _FILETIME {
uint32_t dwLowDateTime;
uint32_t dwHighDateTime;
} FILETIME;
typedef union _LARGE_INTEGER {
struct {
uint32_t LowPart;
int32_t HighPart;
};
struct {
uint32_t LowPart;
int32_t HighPart;
} u;
int64_t QuadPart;
} LARGE_INTEGER;
#ifdef __cplusplus
#define PSNIP_EXTERN extern "C"
#else
#define PSNIP_EXTERN
#endif
PSNIP_EXTERN void * __stdcall GetCurrentProcess(void);
PSNIP_EXTERN int __stdcall QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
PSNIP_EXTERN int __stdcall QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount);
PSNIP_EXTERN int __stdcall GetProcessTimes(
void *hProcess,
FILETIME *lpCreationTime,
FILETIME *lpExitTime,
FILETIME *lpKernelTime,
FILETIME *lpUserTime
);
#endif
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE))
# include <sys/time.h>
# include <sys/resource.h>
#endif
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME))
# include <CoreServices/CoreServices.h>
# include <mach/mach.h>
# include <mach/mach_time.h>
#endif
/*** Implementations ***/
#define PSNIP_CLOCK_NSEC_PER_SEC ((uint32_t) (1000000000ULL))
#if \
(defined(PSNIP_CLOCK_CPU_METHOD) && (PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
(defined(PSNIP_CLOCK_WALL_METHOD) && (PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME)) || \
(defined(PSNIP_CLOCK_MONOTONIC_METHOD) && (PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME))
PSNIP_CLOCK__FUNCTION uint32_t
psnip_clock__clock_getres (clockid_t clk_id) {
struct timespec res;
int r;
r = clock_getres(clk_id, &res);
if (r != 0)
return 0;
return (uint32_t) (PSNIP_CLOCK_NSEC_PER_SEC / res.tv_nsec);
}
PSNIP_CLOCK__FUNCTION int
psnip_clock__clock_gettime (clockid_t clk_id, struct PsnipClockTimespec* res) {
struct timespec ts;
if (clock_gettime(clk_id, &ts) != 0)
return -10;
res->seconds = (uint64_t) (ts.tv_sec);
res->nanoseconds = (uint64_t) (ts.tv_nsec);
return 0;
}
#endif
PSNIP_CLOCK__FUNCTION uint32_t
psnip_clock_wall_get_precision (void) {
#if !defined(PSNIP_CLOCK_WALL_METHOD)
return 0;
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_WALL);
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY
return 1000000;
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME
return 1;
#else
return 0;
#endif
}
PSNIP_CLOCK__FUNCTION int
psnip_clock_wall_get_time (struct PsnipClockTimespec* res) {
(void) res;
#if !defined(PSNIP_CLOCK_WALL_METHOD)
return -2;
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_WALL, res);
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_TIME
res->seconds = time(NULL);
res->nanoseconds = 0;
#elif defined(PSNIP_CLOCK_WALL_METHOD) && PSNIP_CLOCK_WALL_METHOD == PSNIP_CLOCK_METHOD_GETTIMEOFDAY
struct timeval tv;
if (gettimeofday(&tv, NULL) != 0)
return -6;
res->seconds = tv.tv_sec;
res->nanoseconds = tv.tv_usec * 1000;
#else
return -2;
#endif
return 0;
}
PSNIP_CLOCK__FUNCTION uint32_t
psnip_clock_cpu_get_precision (void) {
#if !defined(PSNIP_CLOCK_CPU_METHOD)
return 0;
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_CPU);
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK
return CLOCKS_PER_SEC;
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES
return PSNIP_CLOCK_NSEC_PER_SEC / 100;
#else
return 0;
#endif
}
PSNIP_CLOCK__FUNCTION int
psnip_clock_cpu_get_time (struct PsnipClockTimespec* res) {
#if !defined(PSNIP_CLOCK_CPU_METHOD)
(void) res;
return -2;
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_CPU, res);
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_CLOCK
clock_t t = clock();
if (t == ((clock_t) -1))
return -5;
res->seconds = t / CLOCKS_PER_SEC;
res->nanoseconds = (t % CLOCKS_PER_SEC) * (PSNIP_CLOCK_NSEC_PER_SEC / CLOCKS_PER_SEC);
#elif defined(PSNIP_CLOCK_CPU_METHOD) && PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETPROCESSTIMES
FILETIME CreationTime, ExitTime, KernelTime, UserTime;
LARGE_INTEGER date, adjust;
if (!GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime))
return -7;
/* http://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/ */
date.HighPart = UserTime.dwHighDateTime;
date.LowPart = UserTime.dwLowDateTime;
adjust.QuadPart = 11644473600000 * 10000;
date.QuadPart -= adjust.QuadPart;
res->seconds = date.QuadPart / 10000000;
res->nanoseconds = (date.QuadPart % 10000000) * (PSNIP_CLOCK_NSEC_PER_SEC / 100);
#elif PSNIP_CLOCK_CPU_METHOD == PSNIP_CLOCK_METHOD_GETRUSAGE
struct rusage usage;
if (getrusage(RUSAGE_SELF, &usage) != 0)
return -8;
res->seconds = usage.ru_utime.tv_sec;
res->nanoseconds = tv.tv_usec * 1000;
#else
(void) res;
return -2;
#endif
return 0;
}
PSNIP_CLOCK__FUNCTION uint32_t
psnip_clock_monotonic_get_precision (void) {
#if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
return 0;
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_getres(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC);
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME
static mach_timebase_info_data_t tbi = { 0, };
if (tbi.denom == 0)
mach_timebase_info(&tbi);
return (uint32_t) (tbi.numer / tbi.denom);
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64
return 1000;
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER
LARGE_INTEGER Frequency;
QueryPerformanceFrequency(&Frequency);
return (uint32_t) ((Frequency.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC) ? PSNIP_CLOCK_NSEC_PER_SEC : Frequency.QuadPart);
#else
return 0;
#endif
}
PSNIP_CLOCK__FUNCTION int
psnip_clock_monotonic_get_time (struct PsnipClockTimespec* res) {
#if !defined(PSNIP_CLOCK_MONOTONIC_METHOD)
(void) res;
return -2;
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_CLOCK_GETTIME
return psnip_clock__clock_gettime(PSNIP_CLOCK_CLOCK_GETTIME_MONOTONIC, res);
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_MACH_ABSOLUTE_TIME
uint64_t nsec = mach_absolute_time();
static mach_timebase_info_data_t tbi = { 0, };
if (tbi.denom == 0)
mach_timebase_info(&tbi);
nsec *= ((uint64_t) tbi.numer) / ((uint64_t) tbi.denom);
res->seconds = nsec / PSNIP_CLOCK_NSEC_PER_SEC;
res->nanoseconds = nsec % PSNIP_CLOCK_NSEC_PER_SEC;
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_QUERYPERFORMANCECOUNTER
LARGE_INTEGER t, f;
if (QueryPerformanceCounter(&t) == 0)
return -12;
QueryPerformanceFrequency(&f);
res->seconds = t.QuadPart / f.QuadPart;
res->nanoseconds = t.QuadPart % f.QuadPart;
if (f.QuadPart > PSNIP_CLOCK_NSEC_PER_SEC)
res->nanoseconds /= f.QuadPart / PSNIP_CLOCK_NSEC_PER_SEC;
else
res->nanoseconds *= PSNIP_CLOCK_NSEC_PER_SEC / f.QuadPart;
#elif defined(PSNIP_CLOCK_MONOTONIC_METHOD) && PSNIP_CLOCK_MONOTONIC_METHOD == PSNIP_CLOCK_METHOD_GETTICKCOUNT64
const ULONGLONG msec = GetTickCount64();
res->seconds = msec / 1000;
res->nanoseconds = sec % 1000;
#else
return -2;
#endif
return 0;
}
/* Returns the number of ticks per second for the specified clock.
* For example, a clock with millisecond precision would return 1000,
* and a clock with 1 second (such as the time() function) would
* return 1.
*
* If the requested clock isn't available, it will return 0.
* Hopefully this will be rare, but if it happens to you please let us
* know so we can work on finding a way to support your system.
*
* Note that different clocks on the same system often have a
* different precisions.
*/
PSNIP_CLOCK__FUNCTION uint32_t
psnip_clock_get_precision (enum PsnipClockType clock_type) {
switch (clock_type) {
case PSNIP_CLOCK_TYPE_MONOTONIC:
return psnip_clock_monotonic_get_precision ();
case PSNIP_CLOCK_TYPE_CPU:
return psnip_clock_cpu_get_precision ();
case PSNIP_CLOCK_TYPE_WALL:
return psnip_clock_wall_get_precision ();
}
PSNIP_CLOCK_UNREACHABLE();
return 0;
}
/* Set the provided timespec to the requested time. Returns 0 on
* success, or a negative value on failure. */
PSNIP_CLOCK__FUNCTION int
psnip_clock_get_time (enum PsnipClockType clock_type, struct PsnipClockTimespec* res) {
assert(res != NULL);
switch (clock_type) {
case PSNIP_CLOCK_TYPE_MONOTONIC:
return psnip_clock_monotonic_get_time (res);
case PSNIP_CLOCK_TYPE_CPU:
return psnip_clock_cpu_get_time (res);
case PSNIP_CLOCK_TYPE_WALL:
return psnip_clock_wall_get_time (res);
}
return -1;
}
#endif /* !defined(PSNIP_CLOCK_H) */

Binary file not shown.

184
resnet/resnet.py Executable file
View File

@ -0,0 +1,184 @@
#!/usr/bin/env python3
import keras.backend as K
assert K.image_dim_ordering() == 'th'
import pickle, time
import sys
import numpy as np
from keras.callbacks import LearningRateScheduler
from keras.datasets import mnist
from keras.layers import BatchNormalization
from keras.layers import Convolution2D, MaxPooling2D
from keras.layers import Flatten, Reshape
from keras.layers import Input, merge, Dense, Activation
from keras.models import Model
from keras.utils.np_utils import to_categorical
nb_classes = 10
width = 28
height = 28
loss='categorical_crossentropy'
name = 'resnet-{:.0f}'.format(time.time())
args = dict(enumerate(sys.argv))
restore_fn = args.get(1)
if restore_fn == '.': # TODO: accept any directory
# just use most recent resnet-*.pkl file in directory
import os
is_valid = lambda fn: fn.startswith('resnet-') and fn.endswith('.pkl')
files = sorted([fn for fn in os.listdir(restore_fn) if is_valid(fn)])
if len(files) == 0:
raise Exception("couldn't find any appropriate .pkl files in the CWD")
restore_fn = files[-1]
dont_train = False
verbose_summary = False
reslayers = 4
size = 8
batch_size = 128
epochs = 24
convolutional = True
resnet_enabled = True
original_resnet = False
LR = 1e-2
LRprod = 0.1**(1/20.) # will use a tenth of the learning rate after 20 epochs
use_image_generator = True
def prepare(X, y):
X = X.reshape(X.shape[0], 1, width, height).astype('float32') / 255
# convert class vectors to binary class matrices
Y = to_categorical(y, nb_classes)
return X, Y
# the data, shuffled and split between train and test sets
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train, Y_train = prepare(X_train, y_train)
X_test, Y_test = prepare(X_test, y_test)
if use_image_generator:
from keras.preprocessing.image import ImageDataGenerator
idg = ImageDataGenerator(rotation_range=5.,
width_shift_range=.10,
height_shift_range=.10,
shear_range=5 / 180 * np.pi,
zoom_range=0.1,
fill_mode='constant',
cval=0.)
# ReLU activation is supposed to be the best with he_normal
if convolutional:
layer = lambda x: Convolution2D(x, 3, 3, init='he_normal', border_mode='same')
else:
layer = lambda x: Dense(x, init='he_normal')
# start construting the model
x = Input(shape=(1, width, height))
y = x
if convolutional:
# it might be worth trying other sizes here
y = Convolution2D(size, 7, 7, subsample=(2, 2), border_mode='same')(y)
y = MaxPooling2D()(y)
else:
y = Flatten()(y)
y = Dense(dense_size)(y)
for i in range(reslayers):
skip = y
if original_resnet:
y = layer(size)(y)
y = BatchNormalization(axis=1)(y)
y = Activation('relu')(y)
y = layer(size)(y)
y = BatchNormalization(axis=1)(y)
if resnet_enabled: y = merge([skip, y], mode='sum')
y = Activation('relu')(y)
else:
y = BatchNormalization(axis=1)(y)
y = Activation('relu')(y)
y = layer(size)(y)
y = BatchNormalization(axis=1)(y)
y = Activation('relu')(y)
y = layer(size)(y)
if resnet_enabled: y = merge([skip, y], mode='sum')
if convolutional:
from keras.layers import AveragePooling1D
y = Reshape((size, int(width * height / 2**2 / 2**2)))(y)
y = AveragePooling1D(size)(y)
y = Flatten()(y)
y = Dense(nb_classes)(y)
y = Activation('softmax')(y)
model = Model(input=x, output=y)
if verbose_summary:
model.summary()
else:
total_params = 0
for layer in model.layers:
total_params += layer.count_params()
print("Total params: {}".format(total_params))
if restore_fn:
with open(restore_fn, 'rb') as f:
W = pickle.loads(f.read())
if not dont_train:
# sparsify an existing model
for i, w in enumerate(W):
if w.shape == (size, size, 3, 3):
middle = np.median(np.abs(w.flat))
where = np.abs(w) < middle
total = np.prod(w.shape)
fmt = 'W[{}]: zeroing {} params of {}'
print(fmt.format(i, int(np.count_nonzero(where)), int(total)))
W[i] = np.where(where, 0, w)
model.set_weights(W)
LR /= 10
model.compile(loss=loss, optimizer='adam', metrics=['accuracy'])
if not dont_train:
callbacks = [LearningRateScheduler(lambda e: LR * LRprod**e)]
kwargs = dict(
nb_epoch=epochs,
validation_data=(X_test, Y_test),
callbacks=callbacks,
verbose=1
)
if use_image_generator:
history = model.fit_generator(idg.flow(X_train, Y_train, batch_size=batch_size),
samples_per_epoch=len(X_train), **kwargs)
else:
history = model.fit(X_train, Y_train, batch_size=batch_size,
**kwargs)
def evaluate(X, Y):
score = model.evaluate(X, Y, verbose=0)
for name, score in zip(model.metrics_names, score):
if name == "acc":
print("{:7} {:6.2f}%".format(name, score * 100))
else:
print("{:7} {:7.5f}".format(name, score))
print('TRAIN')
evaluate(X_train, Y_train)
print('TEST')
evaluate(X_test, Y_test)
print('ALL')
evaluate(np.vstack((X_train, X_test)), np.vstack((Y_train, Y_test)))
if not dont_train:
open(name+'.json', 'w').write(model.to_json())
with open(name+'.pkl', 'wb') as f:
f.write(pickle.dumps(model.get_weights()))

26
response/response.txt Normal file
View File

@ -0,0 +1,26 @@
if you have a transfer function like,
b2·s² + b1·s + b0
H(s) = ———————————————————
a2·s² + a1·s + a0
whereas s would be (1 - z⁻¹)(1 + z⁻¹)e^(j·ω) in the bilinear transform,
you can find its magnitude response with this equation:
(b2·x)² - (2·b2·b0 - b1²)·W·x·y + (b0·W·y)²
|H(j·ω)|² = —————————————————————————————————————————————
(a2·x)² - (2·a2·a0 - a1²)·W·x·y + (a0·W·y)²
(analog) x = ω²
y = 1
W = ω0²
(digital) x = sin(ω2)²
y = cos(ω2)²
W = tan(ω02)²
whereas ω is the physical frequency in rads/sec
ω0 is the center frequency in rads/sec
and the phase? maybe some other time
note: I'm no math genius and there's probably an error in here

121
rng64/rng.md Normal file
View File

@ -0,0 +1,121 @@
# info dump on the RNG in OoT and MM
if you find any discrepancies, please
leave a comment or tweet at [@antiformant][twitter].
[twitter]: https://twitter.com/antiformant
## the function itself
the random number generator in both games is an LCG:
https://en.wikipedia.org/wiki/Linear_congruential_generator
specifically, it uses the constants from Numerical Recipes in C.
```c
/* the C code looks something like this: */
static uint32_t rng_value = 1;
uint32_t prng() {
/* note: intentional unsigned overflow. */
rng_value = rng_value * 1664525 + 1013904223;
return rng_value;
/* note: in the game, there's some code to reinterpret the value
as a floating point number, which I've omitted here. */
}
```
if you're interested, you can find the function(s) in any version by searching
for F35F in RAM. you should see a 660D nearby, usually a bit before.
you can find the rng_value variable
by looking at the disassembly of the RNG function.
note that there are a few different variations of the function
that are not commonly invoked; you can find these the same way.
here are some known addresses:
game, version | RNG value | RNG function | variants
------------- | --------- | ------------ | --------
OoT 1.0 | 80105440 | 800CDC90 | ?
OoT 1.2 | 80105A80 | 800CE4D0 | ?
MM (J) 1.1 | 8009E890 | 8008797C | 80087940
MM (U): | 80097530 | 80086FDC | ?
## the quirks
when a new scene is loaded,
the RNG value is set directly to the CPU's cycle count.
that means, depending on all the code that has been run
up to that point, the RNG value is set to a
pseudo-unpredictable value when you enter a new area.
to clarify what a "new scene" being loaded is:
* loading the title screen
* loading a save file
* entering a new area (TODO: do rooms within a scene count?)
* **not** the N64 logo being shown
* **not** the file select screen
### other invocations
creating a new file will invoke the RNG
to determine the Bomber's Code, etc.
obviously, this does not apply to OoT.
although the GameCube versions don't have an N64 logo,
they still do similar initializations before the title screen is shown.
### emulators
as far as i know, the cycle counter in emulators
(including Virtual Console, excluding CEN64)
is very roughly approximated.
it's not a critical component to emulating most games,
so emulators can skimp on it to achieve better performance.
this means RNG should be fairly consistent, given identical inputs.
*this does not mean RNG will be consistent across emulators.*
the plugins you use might affect this as well.
## exploiting the RNG
### in Ocarina of Time
because the title screen sequence starts on Hyrule Field
with a ton of actors loaded,
the RNG value is updated many times each frame.
as far as i know, you'd have to be frame perfect to reach the file select screen
with a consistent RNG, but i haven't done a lot of testing.
maybe there's a part of the title sequence
with wider gaps between RNG invocations,
giving you a wider window to enter the inputs to reach the file select screen.
this might not be exploitable anyway due to
the reset that occurs when loading scenes, as described earlier.
### in Majora's Mask
the first title screen in this game is very simple.
only a handful of actors are loaded and executing.
as a result, the RNG is only invoked
something like every 75 frames *on average.*
this is a big window to reach the file select screen.
*in theory,* you should be able mash A and Start to reach the
file select to create a file, and that file will have the same
Bomber's code, lottery codes, and Spider House mask order.
remember that "the same" is specific to each emulator,
as described earlier.
### in practice
this needs testing.
i can somewhat consistently reach the file select screen
with the same RNG value in Project64 2.2 and Bizhawk 1.12.1.
Virtual Console and N64 have not been tested.
this might be useful for the new MM 100% ruleset
if it's found to be consistent, but don't hold your breath.

View File

@ -0,0 +1,171 @@
global fontsize = 26
function DiffClip(clip c, int diff)
{ BlankClip(c, diff).Subtitle(String(-diff), align=5, size=c.Height / 8) }
function PadWithDiff(clip c, int len)
{ c.FrameCount < len ? c + DiffClip(c, len - c.FrameCount) : c }
function DualMonoToStereo(clip left, clip right)
{ MergeChannels(ConvertToMono(left), ConvertToMono(right)) }
function MergeHoriz(clip left, clip right, int audio_index)
{
v = Overlay(AddBorders(left, 0, 0, right.Width, 0), right, left.Width, 0)
return audio_index == 0 \
? AudioDub(v, DualMonoToStereo(left, right)) \
: audio_index == 1 ? AudioDub(v, left) \
: audio_index == 2 ? AudioDub(v, right) \
: v
}
function MergeVert(clip top, clip bottom, int audio_index)
{
v = Overlay(AddBorders(top, 0, 0, 0, bottom.Height), bottom, 0, top.Height)
return audio_index == 0 \
? AudioDub(v, DualMonoToStereo(top, bottom)) \
: audio_index == 1 ? AudioDub(v, top) \
: audio_index == 2 ? AudioDub(v, bottom) \
: v
}
function Cmp(clip left, clip right)
{
frames = max(left.FrameCount, right.FrameCount)
left = PadWithDiff(left, frames)
right = PadWithDiff(right, frames)
return MergeHoriz(left, right, 0)
}
function TextHoriz(clip c, int frames, string left, string right)
{
size = fontsize
tleft = BlankClip(c, frames, height=size).Subtitle(left, align=2, size=size)
tright = BlankClip(c, frames, height=size).Subtitle(right, align=2, size=size)
MergeHoriz(tleft, tright, -1)
AddBorders(0, 2, 0, 2)
}
function MakeFrameNumberClip(clip c, int frames)
{
width = fontsize * 5 / 2 + 1
halved = (c.Width - width) / 2
ShowFrameNumber(BlankClip(c, frames, width=width, height=fontsize + 1), size=fontsize)
Crop(0, 5, -0, -0)
AddBorders(halved, 0, halved, 4)
AddBorders(0, 2, 0, 2)
}
function AdjustClip(clip c, int offset)
{
# set up your padding and resizing here
PointResize(c, c.Width * 2, c.Height * 2)
AddBorders(64 + offset, 0, 64 - offset, 0)
MergeVert(MakeFrameNumberClip(last.FrameCount), last, 2)
AddBorders(0, 16 - 2, 0, 16)
}
# http://tasvideos.org/485M.html
global old_name = "phil"
global old_date = "2006-02-25"
global old_frames = 32248
global old_time = "08:57.47"
global old_comment = "http://tasvideos.org/985S.html"
AviSource("phil5-ddragon-again.avi")+\
AviSource("phil5-ddragon-again_part2.avi")+\
AviSource("phil5-ddragon-again_part3.avi")
Trim(0, old_frames + 1636)
#ShowFrameNumber(scroll=true)
AdjustClip(0)#-48)
global old = last
# http://tasvideos.org/3211M.html
global new_name = "alyosha"
global new_date = "2016-08-28"
global new_frames = 31869
global new_time = "08:50.28"
global new_comment = "http://tasvideos.org/5207S.html"
AviSource("alyosha-doubledragon-again.avi")+\
AviSource("alyosha-doubledragon-again_part2.avi")+\
AviSource("alyosha-doubledragon-again_part3.avi")
Trim(0, new_frames + 1636)
#ShowFrameNumber(scroll=true)
AdjustClip(0)#48)
global new = last
# compare by slice
function x(int oldStart, int oldEnd, int newStart, int newEnd, string title)
{
left = Trim(old, oldStart, oldEnd)
right = Trim(new, newStart, newEnd)
frames = max(left.FrameCount, right.FrameCount)
fc = MakeFrameNumberClip(left, frames)
TextHoriz(left, frames, old_name + " (" + old_date + ")", new_name + " (" + new_date + ")")
MergeVert(last, TextHoriz(left, frames, String(old_frames) + " frames (" + old_time + ")", string(new_frames) + " frames (" + new_time + ")"), -1)
MergeVert(last, TextHoriz(left, frames, old_comment, new_comment), -1)
MergeVert(last, Cmp(left, right), 2)
MergeVert(last, TextHoriz(left, frames, title, title), 1)
MergeVert(last, TextHoriz(left, frames, "in " + String(left.FrameCount) + " frames", "in " + String(Right.FrameCount) + " frames"), 1)
MergeVert(last, MergeHoriz(fc, fc, -1), 1)
}
# compare by split (start is last split)
global prevOldSplit = 0
global prevNewSplit = 0
function s(int oldSplit, int newSplit, string title)
{
ot = prevOldSplit
nt = prevNewSplit
global prevOldSplit = oldSplit + 1
global prevNewSplit = newSplit + 1
return x(ot, oldSplit, nt, newSplit, title)
}
# TODO: frames gained/lost in corner per file
s( 1557, 1547, "first screen scroll")+\
s( 1869, 1862, "first door opening")+\
s( 2123, 2143, "second screen scroll")+\
s( 3041, 3067, "third screen scroll")+\
s( 3973, 4017, "door entered")+\
s( 4729, 4817, "mission completed")+\
s( 5943, 6059, "first screen scroll")+\
s( 6489, 6601, "background visible again")+\
s( 7118, 7229, "second screen scroll")+\
s( 7604, 7713, "top of first ladder")+\
s( 7988, 8094, "top of second ladder")+\
s( 8316, 8416, "top of third ladder")+\
s( 8649, 8749, "first door opening")+\
s( 9291, 9393, "mission completed")+\
s(10515,10611, "first screen scroll")+\
s(11695,11747, "second screen scroll")+\
s(12514,12581, "third screen scroll")+\
s(13412,13543, "fourth screen scroll")+\
s(14588,14773, "door entered")+\
s(15369,15548, "platformer begin")+\
s(15764,15933, "platformer finished")+\
s(16087,16256, "first door opening")+\
s(16941,17111, "door entered")+\
s(17008,17179, "loaded")+\
s(17794,17984, "first screen scroll")+\
s(18793,18983, "black screen")+\
s(19398,19608, "boss defeated")+\
s(20776,20994, "mission completed")+\
s(22572,22804, "first screen scroll")+\
s(23183,23400, "climbed wall")+\
s(24076,24398, "black screen")+\
s(24326,24642, "top of first ladder")+\
s(24788,25108, "top of second ladder")+\
s(25606,25917, "top of third ladder")+\
s(26147,26446, "top of fourth ladder")+\
s(26283,26642, "black screen")+\
s(27623,27788, "first screen scroll")+\
s(27951,28118, "first door opening")+\
s(28800,28585, "second door opening")+\
s(29492,29152, "third door opening")+\
s(29660,29320, "fourth door opening")+\
s(29944,29545, "fifth door opening")+\
s(30683,30290, "sixth door opening")+\
s(old_frames,new_frames, "end of input")+\
s( 0, 0, "end of game")
AddBorders(0, 16, 0, 16)

34
starcraft_cdkey/sckey.c Normal file
View File

@ -0,0 +1,34 @@
int validate(const char *key) {
int magic = 3;
int count = 0;
for (char c; (c = *key); key++) {
if (c < '0' || c > '9') continue;
int v = c - '0';
if (++count == 13) {
// final character.
return magic % 10 == v;
} else {
v ^= magic * 2;
magic += v;
}
}
return 0;
}
int main(int argc, char **argv) {
if (argc == 1) {
// self-test.
if (!validate("0000-00000-0003")) return -1;
if (!validate("1234-56789-0123")) return -1;
if (!validate("1337-42069-0008")) return -1;
if (!validate("1998-00000-1997")) return -1;
if (!validate("3333-33333-3333")) return -1;
return 0;
}
int ret = 0;
for (int i = 1; i < argc; i++) {
if (!validate(argv[i])) ret++;
}
return ret;
}

507
starcraft_maps/bwmap.py Normal file
View File

@ -0,0 +1,507 @@
import sys
import struct
# sorry not sorry
P = struct.pack
U = struct.unpack
# notes:
# SCMDraft 2 requires, at bare minimum:
scmdraft_req = b"ERA ~DIM ~TILE~UNIT~PUNI~IOWN~THG2~SPRP~WAV ~MASK~STR ~OWNR~SIDE~FORC".split(b"~")
# Starcraft 1.18+ Melee requires, at bare minimum:
# absolutely required:
# VER TYPE OWNR SIDE COLR ERA DIM MTXM UNIT THG2 SPRP FORC
# not needed at all:
# IVER IOWN ISOM TILE DD2
# not required for Melee mode (non-UMS):
# PUPx UPGx PUNI MASK MRGN WAV PTEx UNIx TECx TRIG MBRF UPRP UPUS SWNM
# special cases:
# VCOD partly: 34 19 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1F 29 7D A6 D7 B0 00 BB CC 31 24 ED 17 4C 13 0B
# STR kinda (can be empty for melee)
melee_req = b"VER ~TYPE~VCOD~OWNR~SIDE~COLR~ERA ~DIM ~MTXM~UNIT~THG2~STR ~SPRP~FORC".split(b"~")
# TODO: try more things to add to this list.
ums_req = melee_req + b"MRGN~PTEx~PUPx~TECx~TRIG~UNIx~UPGx~UPRP".split(b"~")
both_req = set(melee_req + scmdraft_req)
known_keys = [ # in the order we want to write them.
# boring filetype stuff
b"TYPE",
b"VER ",
b"IVER",
b"IVE2",
b"VCOD",
b"SPRP", # scenario name and description
b"COLR", # player colors
b"FORC", # player forces and settings
b"SIDE", # player races
b"IOWN", # editor player types
b"OWNR", # ingame player types
b"DIM ", # map dimensions
b"ERA ", # tileset
b"MTXM", # ingame tiles
b"TILE", # editor tiles
b"ISOM", # editor isometric tiles
b"MASK", # fog of war per tile
b"DD2 ", # editor doodads
b"UNIT", # placed units
b"THG2", # placed sprites (e.g. doodad)
b"MRGN", # locations
b"UNIS", # unit settings (a bunch of stuff, including names)
b"UNIx", # broodwar override
b"PUNI", # build unit restrictions
b"PTEC", # tech restrictions
b"PTEx", # broodwar override
b"UPGR", # upgrade restrictions (and defaults, max levels...)
b"PUPx", # broodwar override
b"TECS", # tech settings (costs and times)
b"TECx", # broodwar override
b"UPGS", # upgrade settings (costs and times)
b"UPGx", # broodwar override
b"UPRP", # properties of spawn-with-properties
b"UPUS", # booleans of which properties are used
b"MBRF", # mission briefings (triggers without conditions)
b"TRIG", # triggers
b"SWNM", # switch names
b"WAV ", # wav paths (editor only)
b"STR ", # string data
]
skippable_keys = set(known_keys).symmetric_difference(both_req)
vcod_valid = b"\
\x34\x19\xCA\x77\x99\xDC\x68\x71\x0A\x60\xBF\xC3\xA7\xE7\x75\xA7\
\x1F\x29\x7D\xA6\xD7\xB0\x3A\xBB\xCC\x31\x24\xED\x17\x4C\x13\x0B\
\x65\x20\xA2\xB7\x91\xBD\x18\x6B\x8D\xC3\x5D\xDD\xE2\x7A\xD5\x37\
\xF6\x59\x64\xD4\x63\x9A\x12\x0F\x43\x5C\x2E\x46\xE3\x74\xF8\x2A\
\x08\x6A\x37\x06\x37\xF6\xD6\x3B\x0E\x94\x63\x16\x45\x67\x5C\xEC\
\xD7\x7B\xF7\xB7\x1A\xFC\xD4\x9E\x73\xFA\x3F\x8C\x2E\xC0\xE1\x0F\
\xD1\x74\x09\x07\x95\xE3\x64\xD7\x75\x16\x68\x74\x99\xA7\x4F\xDA\
\xD5\x20\x18\x1F\xE7\xE6\xA0\xBE\xA6\xB6\xE3\x1F\xCA\x0C\xEF\x70\
\x31\xD5\x1A\x31\x4D\xB8\x24\x35\xE3\xF8\xC7\x7D\xE1\x1A\x58\xDE\
\xF4\x05\x27\x43\xBA\xAC\xDB\x07\xDC\x69\xBE\x0A\xA8\x8F\xEC\x49\
\xD7\x58\x16\x3F\xE5\xDB\xC1\x8A\x41\xCF\xC0\x05\x9D\xCA\x1C\x72\
\xA2\xB1\x5F\xA5\xC4\x23\x70\x9B\x84\x04\xE1\x14\x80\x7B\x90\xDA\
\xFA\xDB\x69\x06\xA3\xF3\x0F\x40\xBE\xF3\xCE\xD4\xE3\xC9\xCB\xD7\
\x5A\x40\x01\x34\xF2\x68\x14\xF8\x38\x8E\xC5\x1A\xFE\xD6\x3D\x4B\
\x53\x05\x05\xFA\x34\x10\x45\x8E\xDD\x91\x69\xFE\xAF\xE0\xEE\xF0\
\xF3\x48\x7E\xDD\x9F\xAD\xDC\x75\x62\x7A\xAC\xE5\x31\x1B\x62\x67\
\x20\xCD\x36\x4D\xE0\x98\x21\x74\xFB\x09\x79\x71\x36\x67\xCD\x7F\
\x77\x5F\xD6\x3C\xA2\xA2\xA6\xC6\x1A\xE3\xCE\x6A\x4E\xCD\xA9\x6C\
\x86\xBA\x9D\x3B\xB5\xF4\x76\xFD\xF8\x44\xF0\xBC\x2E\xE9\x6E\x29\
\x23\x25\x2F\x6B\x08\xAB\x27\x44\x7A\x12\xCC\x99\xED\xDC\xF2\x75\
\xC5\x3C\x38\x7E\xF7\x1C\x1B\xC5\xD1\x2D\x94\x65\x06\xC9\x48\xDD\
\xBE\x32\x2D\xAC\xB5\xC9\x32\x81\x66\x4A\xD8\x34\x35\x3F\x15\xDF\
\xB2\xEE\xEB\xB6\x04\xF6\x4D\x96\x35\x42\x94\x9C\x62\x8A\xD3\x61\
\x52\xA8\x7B\x6F\xDC\x61\xFC\xF4\x6C\x14\x2D\xFE\x99\xEA\xA4\x0A\
\xE8\xD9\xFE\x13\xD0\x48\x44\x59\x80\x66\xF3\xE3\x34\xD9\x8D\x19\
\x16\xD7\x63\xFE\x30\x18\x7E\x3A\x9B\x8D\x0F\xB1\x12\xF0\xF5\x8C\
\x0A\x78\x58\xDB\x3E\x63\xB8\x8C\x3A\xAA\xF3\x8E\x37\x8A\x1A\x2E\
\x5C\x31\xF9\xEF\xE3\x6D\xE3\x7E\x9B\xBD\x3E\x13\xC6\x44\xC0\xB9\
\xBC\x3A\xDA\x90\xA4\xAD\xB0\x74\xF8\x57\x27\x89\x47\xE6\x3F\x37\
\xE4\x42\x79\x5A\xDF\x43\x8D\xEE\xB4\x0A\x49\xE8\x3C\xC3\x88\x1A\
\x88\x01\x6B\x76\x8A\xC3\xFD\xA3\x16\x7A\x4E\x56\xA7\x7F\xCB\xBA\
\x02\x5E\x1C\xEC\xB0\xB9\xC9\x76\x1E\x82\xB1\x39\x3E\xC9\x57\xC5\
\x19\x24\x38\x4C\x5D\x2F\x54\xB8\x6F\x5D\x57\x8E\x30\xA1\x0A\x52\
\x6D\x18\x71\x5E\x13\x06\xC3\x59\x1F\xDC\x3E\x62\xDC\xDA\xB5\xEB\
\x1B\x91\x95\xF9\xA7\x91\xD5\xDA\x33\x53\xCE\x6B\xF5\x00\x70\x01\
\x7F\xD8\xEE\xE8\xC0\x0A\xF1\xCE\x63\xEB\xB6\xD3\x78\xEF\xCC\xA5\
\xAA\x5D\xBC\xA4\x96\xAB\xF2\xD2\x61\xFF\xEA\x9A\xA8\x6A\xED\xA2\
\xBD\x3E\xED\x61\x39\xC1\x82\x92\x16\x36\x23\xB1\xB0\xA0\x24\xE5\
\x05\x9B\xA7\xAA\x0D\x12\x9B\x33\x83\x92\x20\xDA\x25\xB0\xEC\xFC\
\x24\xD0\x38\x23\xFC\x95\xF2\x74\x80\x73\xE5\x19\x97\x50\x7D\x44\
\x45\x93\x44\xDB\xA2\xAD\x1D\x69\x44\x14\xEE\xE7\x2C\x7F\x87\xFF\
\x38\x9E\x32\xF1\x4D\xBC\x29\xDA\x42\x27\x26\xFE\xC1\xD2\x2B\xA9\
\xF6\x42\x7A\x0E\xCB\xE8\x7C\xD1\x0F\x5B\xEC\x56\x69\xB7\x61\x31\
\xB4\x6D\xF9\x25\x40\x34\x79\x6D\xFA\x53\xA7\x0B\xFA\xA4\x82\xCE\
\xC3\x45\x49\x61\x0D\x45\x2C\x8F\x28\x49\x60\xF7\xF3\x7D\xC9\x1E\
\x0F\xD0\x89\xC1\x26\x52\xF8\xD3\x4D\x8F\x35\x14\xBA\x9D\x5F\x0B\
\x07\xA9\x4A\x00\xF7\x22\x26\x2F\x3E\x67\xFB\x1F\xA1\x9C\x11\xC6\
\x69\x4F\x5D\x66\x58\x34\x15\x90\x6C\xE5\x54\x46\xAF\x5F\x63\xD6\
\x8A\x0C\x95\xDF\xBD\x0D\xE4\xAF\xBF\x40\x40\x4C\xA3\xF6\x51\x71\
\x29\xED\x26\xF8\x85\x28\x22\xD5\xBF\xBE\xCF\xFA\x28\xC5\x7F\x51\
\xB8\x06\x63\x07\xEC\xBD\x8F\x29\xFA\x55\x7E\x71\x1A\x40\x32\x66\
\xE8\xD4\xDE\x9D\xD4\x5E\xFC\x93\x7A\x3D\xD5\x3B\xCD\x75\x2E\x80\
\x0A\x4F\x74\x87\x1B\xCC\x8F\xEA\x9A\xA9\xDB\x7C\x16\x53\xE5\xEF\
\xAB\x78\xC1\x6E\xA4\x72\x89\x5A\x98\x2C\x70\x50\xFB\xA1\xDF\x1F\
\x6B\xB7\xD9\x44\x07\x80\x82\x56\xFD\xBF\xC0\x83\x0E\x49\xD0\x5B\
\x1E\x68\x6A\x0E\x9A\xC2\x0B\x2F\x8E\x43\xA0\xE1\x99\x0C\xF6\xB2\
\xE0\x7A\x1C\x5E\x2C\xC8\xA0\x45\x3C\x0B\xE9\x88\xAC\xB9\x96\xC6\
\x74\xAE\x83\x2A\xBB\x13\xFA\x65\xEB\x4F\x1F\xA6\xB0\x8A\x8A\xE1\
\x81\xE9\xB8\xB9\xD5\x55\x15\x4E\x45\xF2\xAD\x9B\x3E\xC2\x35\x7E\
\x5F\x92\x2E\x72\xB6\x5B\x68\x23\x6E\xC6\x45\x0E\xE9\x3B\x87\xD4\
\xF4\x41\xC0\xE3\xA8\x05\x44\xBE\xE4\x0F\x8A\x13\x1A\xC4\x37\xF4\
\x5A\x40\x55\xEF\x9D\x79\x1D\x4B\x4A\x79\x3A\x9C\x76\x85\x37\xCC\
\x82\x3D\x0F\xB6\x60\xA6\x93\x7E\xBD\x5C\xC2\xC4\x72\xC7\x7F\x90\
\x4D\x1B\x96\x10\x13\x05\x68\x68\x35\xC0\x7B\xFF\x46\x85\x43\x2A\
\x01\x04\x05\x06\x02\x01\x05\x02\x00\x03\x07\x07\x05\x04\x06\x03\
"
mrgn_default = bytearray(b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" * 255)
# set up the "Anywhere" location correctly. this is always location 64.
mrgn_default[63*20 + 9] = 0x10
mrgn_default[63*20 + 13] = 0x10
mrgn_default[63*20 + 16] = 0x03
mrgn_default = bytes(mrgn_default)
def readit(f):
f.seek(0, 2)
fs = f.tell()
f.seek(0)
src = {}
while f.tell() < fs:
k = f.read(4)
try:
name = k.decode('ascii')
except UnicodeDecodeError:
print("! skipping invalid section name:", k)
size = U("<l", f.read(4))[0]
if size > 0:
f.read(size)
continue
size = U("<l", f.read(4))[0]
if size < 0:
print("gotcha!", size)
return
print(name, size)
section = f.read(size)
if len(section) != size:
print("bad section size:", len(section))
print("expected:", size)
return
#if k not in b"ERA ~DIM ~MTXM~THG2~UNIT".split(b"~"): continue
if k not in src:
src[k] = []
src[k].append(section)
return src
def makestringtable(strings):
count = len(strings)
if len(strings) > 0 and type(strings[0]) != bytes:
strings = [str(s).encode("CP1252") for s in strings]
flat = b"\0" + b"\0".join(strings)
offsets = []
o = count * 2 + 3
for s in strings:
offsets += [o]
o += len(s) + 1
out = P("<H", count)
for o in offsets:
out += P("<H", o)
out += flat
return out
def rewrite(src):
if b"STR " not in src:
return
ok = b"MRGN~MBRF~TRIG~SPRP~FORC~WAV ~UNIx~SWNM".split(b"~")
refs = []
def mark(k, v, i, ki=0, extra=None):
num = v[i] + v[i+1]*256
if num != 0:
print("ref {:04X} found at {}[{}]".format(num, k, i))
ref = [k, ki, i, num, extra]
refs.append(ref)
for k, a in src.items():
if k not in ok:
continue
if k == b"MRGN":
for ki, v in enumerate(a):
for i in range(0, len(v), 20):
mark(k, v, i+16, ki)
if k == b"SPRP":
for ki, v in enumerate(a):
mark(k, v, 0, ki)
mark(k, v, 2, ki)
if k == b"FORC":
for ki, v in enumerate(a):
# TODO: handle truncated FORC.
mark(k, v, 8, ki)
mark(k, v, 10, ki)
mark(k, v, 12, ki)
mark(k, v, 14, ki)
if k == b"WAV ":
for ki, v in enumerate(a):
for i in range(0, 512, 2):
if v[i] != 0 and v[i+1] != 0:
mark(k, v, i, ki)
if k == b"UNIx":
for ki, v in enumerate(a):
uc = 228
stringstart = uc + uc*4 + uc*2 + uc + uc*2 + uc*2 + uc*2
for i in range(stringstart, stringstart + uc*2, 2):
if v[i] != 0 and v[i+1] != 0:
mark(k, v, i, ki)
if k == b"SWNM":
for ki, v in enumerate(a):
for i in range(0, 1024, 4):
if v[i] != 0 and v[i+1] != 0:
mark(k, v, i, ki)
if k == b"TRIG" or k == b"MBRF":
for ki, v in enumerate(a):
# trigger size: (20)*16 + (32)*64 + (4+28) = 2400
for i in range(0, len(v), 2400):
# iterate through each of the 64 actions per trigger.
for j in range(20*16, 2400, 32):
atype = v[i+j+26]
if atype == 0:
continue
#full = U("<l", v[i+j+4:i+j+4+4])[0]
#if full != 0: print("DEBUG:", full, atype)
#full = U("<l", v[i+j+8:i+j+8+4])[0]
#if full != 0: print("DEBUG:", full, atype)
mark(k, v, i+j+4, ki)
mark(k, v, i+j+8, ki, "wav")
strings = []
wavstrings = []
# dereference.
STR = src[b"STR "][0] # TODO: rename
for ref in refs:
k, ki, i, num, extra = ref
offset = STR[num*2] + STR[num*2+1]*256
null = STR.find(b'\0', offset)
string = STR[offset:null]
#print("found string at {}[{}] + {:04X} (ref {:04X}):".format(k, ki, offset, num))
#print(string.decode('CP949', errors='replace'))
if string not in strings: # FIXME: potentially slow.
strings.append(string)
if extra == "wav":
wavstrings.append(string)
# create new table.
src[b"STR "][0] = makestringtable(strings)
# update references to point at the new table.
for ref in refs:
k, ki, i, num, extra = ref
# TODO: don't repeat ourselves.
offset = STR[num*2] + STR[num*2+1]*256
null = STR.find(b'\0', offset)
string = STR[offset:null]
assert string in strings # FIXME: potentially slow.
si = strings.index(string) # FIXME: potentially slow.
ba = bytearray(src[k][ki]) # FIXME: potentially slow.
ba[i+0] = (1 + si) % 256
ba[i+1] = (1 + si) // 256
src[k][ki] = bytes(ba) # FIXME: potentially slow.
# fix wav table.
# NOTE: since we're dealing with raw .chk and not .mpq,
# you still need to reimport the wavs with an MPQ editor!
wavs = bytearray(b'\0\0\0\0' * 512)
for i, string in enumerate(wavstrings):
assert string in strings # FIXME: potentially slow.
si = strings.index(string) # FIXME: potentially slow.
wavs[i*4+0] = (1 + si) % 256
wavs[i*4+1] = (1 + si) // 256
src[b"WAV "] = [bytes(wavs)]
def section(k, v):
assert type(k) == bytes, type(k)
assert type(v) == bytes, type(v)
assert len(k) == 4, k
return k + P("<L", len(v)) + v
def check_dim(v):
w, h = U("<HH", v)
if w not in (64, 96, 128, 192, 256):
return False
if h not in (64, 96, 128, 192, 256):
return False
return True
def writeit(f, src):
wroteonce = {}
def W(k, v):
wroteonce[k] = True
f.write(section(k, v))
# set some essentials that there's no need to change.
# (unless you're writing expansionless maps for some reason)
src[b"TYPE"] = [b"RAWB"]
src[b"VER "] = [b"\315\0"]
src[b"IVER"] = [b"\12\0"]
src[b"IVE2"] = [b"\13\0"]
src[b"VCOD"] = [vcod_valid]
if b"TILE" not in src:
src[b"TILE"] = src[b"MTXM"]
w, h = 0, 0
for k, a in src.items():
# TODO: consider removing this since we check in the write func anyway.
assert type(k) == bytes
assert type(a) == list
assert len(k) == 4
if k == b"DIM ": # TODO: move out of loop.
a = [v for v in a if check_dim(v)]
w, h = U("<HH", a[0])
placed_starts = [False] * 12
k = b"UNIT"
if k in src:
for ki, v in enumerate(src[k]):
for i in range(0, len(v), 36):
unit = v[i:i+36]
uid = unit[8] + unit[9]*256
if uid != 214:
continue
pi = unit[16]
if pi >= 12:
print("# ignoring start location for player", pi)
placed_starts[pi] = True
k = b"ERA "
if k in src:
for ki, v in enumerate(src[k]):
era = U("<H", v)[0]
era &= 7
src[k][ki] = P("<H", era)
assert b"DIM " in src # TODO: try to guess map dimensions.
if b"COLR" not in src:
src[b"COLR"] = [b"\0\1\2\3\4\5\6\7"]
# SCMDraft 2 seems to require this to be the correct length.
if b"MASK" not in src or len(src[b"MASK"][0]) != w * h:
src[b"MASK"] = [b"\xFF" * (w * h)]
if b"STR " not in src:
src[b"STR "] = [b""]
# SCMDraft 2 calls this "player settings" in its errors.
if b"OWNR" not in src:
b = b""
for i in range(12):
b += b"\6" if placed_starts[i] else b"\0"
src[b"OWNR"] = [b]
assert len(b) == 12, b
# SCMDraft 2 calls this "player settings" in its errors.
if b"SIDE" not in src:
src[b"SIDE"] = [b"\5\5\5\5\5\5\5\5\7\7\7\4"]
if b"FORC" not in src:
b = P("<BBBBBBBBHHHHBBBB",
0,0,0,0,0,0,0,0, # player->force mapping
0,0,0,0, # strings for forces
1,1,1,1, # bitmasks: randstart, ally, allyvic, vis, unused...
)
src[b"FORC"] = [b]
assert len(b) == 20, b
if b"IOWN" not in src:
# FIXME: possibly invalid.
b = b""
for i in range(8):
b += b"\6" if placed_starts[i] else b"\0"
b += b"\0\0\0\7"
src[b"IOWN"] = [b]
assert len(b) == 12, b
if b"MRGN" not in src:
src[b"MRGN"] = [mrgn_default]
if b"THG2" not in src:
src[b"THG2"] = [b""]
if b"SPRP" not in src:
src[b"SPRP"] = [b"\0\0\0\0"]
if b"WAV " not in src:
src[b"WAV "] = [b"\0\0\0\0" * 512]
#if b"ISOM" not in src:
# size = (w // 2 + 1) * (h + 1) * 4
# src[b"ISOM"] = [b'\0' * size]
if b"PUNI" not in src:
src[b"PUNI"] = [b"\1" * 5700]
done = {}
for k in known_keys:
if k in src:
for v in src[k]:
W(k, v)
done[k] = True
for k, v in src.items():
if not done[k]:
print("# deferred write:", k)
W(k, v)
for k in known_keys:
if k not in skippable_keys and k not in wroteonce:
print("# never wrote section:", k)
for k in scmdraft_req:
if k not in wroteonce:
print("# map might not open in SCMDraft 2. missing", k)
for k in melee_req:
if k not in wroteonce:
print("# map might not open in StarCraft (Melee). missing", k)
for k in ums_req:
if k not in wroteonce:
print("# map might not open in StarCraft (Use Map Settings). missing", k)
def run(args):
for fn in args:
with open(fn, 'rb') as f:
src = readit(f)
if src is None:
print("! failed to rewrite", fn) # FIXME: reuse of terminology.
ofn = fn.replace('.chk', '') + '.x.chk'
assert fn != ofn, ofn
rewrite(src)
with open(ofn, 'wb') as f:
writeit(f, src)
if __name__ == '__main__':
ret = run(sys.argv[1:])
sys.exit(ret)

156
string_tensions/data.py Executable file
View File

@ -0,0 +1,156 @@
# (product name, unit weight)
# D'Addario stock via http://www.daddario.com/upload/tension_chart_13934.pdf
# Circle K String stock http://circlekstrings.com/CKSIMAGES/UnitWeightChart130105.pdf
# string names don't necessarily match up with their actual product names
daddario_plain_steel = (
('DAPL007' , .00001085),
('DAPL008' , .00001418),
('DAPL0085', .00001601),
('DAPL009' , .00001794),
('DAPL0095', .00001999),
('DAPL010' , .00002215),
('DAPL0105', .00002442),
('DAPL011' , .00002680),
('DAPL0115', .00002930),
('DAPL012' , .00003190),
('DAPL013' , .00003744),
('DAPL0135', .00004037),
('DAPL014' , .00004342),
('DAPL015' , .00004984),
('DAPL016' , .00005671),
('DAPL017' , .00006402),
('DAPL018' , .00007177),
('DAPL019' , .00007997),
('DAPL020' , .00008861),
('DAPL022' , .00010722),
('DAPL024' , .00012760),
('DAPL027' , .00014975),
)
daddario_nickle_wound = ( # XL
('DANW017' , .00005524),
('DANW018' , .00006215),
('DANW019' , .00006947),
('DANW020' , .00007495),
('DANW021' , .00008293),
('DANW022' , .00009184),
('DANW024' , .00010857),
('DANW025*', .00011875), # from the EXL110BT, an approximation
('DANW026' , .00012671),
('DANW028' , .00014666),
('DANW030' , .00017236),
('DANW032' , .00019347),
('DANW034' , .00021590),
('DANW036' , .00023964),
('DANW037*', .00024872), # from the EXL115BT, an approximation
('DANW038' , .00026471),
('DANW039' , .00027932),
('DANW040*', .00029570), # from the EXL120BT, an approximation
('DANW042' , .00032279),
('DANW044' , .00035182),
('DANW046' , .00038216),
('DANW048' , .00041382),
('DANW049' , .00043014),
('DANW050*', .00044720), # from the EXL115BT, an approximation
('DANW052' , .00048109),
('DANW054' , .00053838),
('DANW056' , .00057598),
('DANW059' , .00064191),
('DANW060' , .00066542),
('DANW062' , .00070697),
('DANW064' , .00074984),
('DANW066' , .00079889),
('DANW068' , .00084614),
('DANW070' , .00089304),
('DANW072' , .00094124),
('DANW074' , .00098869),
('DANW080' , .00115011),
)
kalium_plain = (
('CKPL008 ',.0000142401458191),
('CKPL0085',.00001607510288),
('CKPL009', .0000180219146483),
('CKPL0095',.00002008032129),
('CKPL010', .0000222518914107),
('CKPL0105',.00002453144932),
('CKPL011', .0000269251480883),
('CKPL0115',.00002942561205),
('CKPL012', .0000320389593746),
('CKPL0125',.00003476567932),
('CKPL013', .0000376052948255),
('CKPL0135',.00004055150041),
('CKPL014', .000043607),
('CKPL015', .000050050),
('CKPL016', .000056961),
('CKPL017', .000064300),
('CKPL018', .000072088),
('CKPL019', .000080360),
('CKPL020', .000089031),
('CKPL021', .000098155),
('CKPL022', .000107666),
('CKPL023', .000117702),
)
kalium_hybrid_wound = (
('CKHW021', .000093873),
('CKHW022', .000103500),
('CKHW023', .000113985),
('CKHW024', .000124963),
('CKHW025', .000136054),
('CKHW026', .000144691),
('CKHW027', .000153146),
('CKHW028', .000161203),
('CKHW029', .000178551),
('CKHW031', .000198902),
('CKHW033', .000223217),
('CKHW035', .000249034),
('CKHW037', .000276237),
('CKHW039', .000304788),
('CKHW041', .000334965),
('CKHW043', .000366357),
('CKHW045', .000404956),
('CKHW047', .000447408),
('CKHW049', .000475438),
('CKHW051', .000512645),
('CKHW053', .000551898),
('CKHW055', .000584407),
('CKHW057', .000625704),
('CKHW059', .000679149),
('CKHW061', .000720293),
('CKHW063', .000765973),
('CKHW065', .000821116),
('CKHW067', .000870707),
('CKHW070', .000939851),
('CKHW073', .001021518),
('CKHW076', .001110192),
('CKHW079', .001188974),
('CKHW082', .001293598),
('CKHW086', .001416131),
('CKHW090', .001544107),
('CKHW094', .001677765),
('CKHW098', .001831487),
('CKHW102', .001986524),
('CKHW106', .002127413),
('CKHW112', .002367064),
('CKHW118', .002616406),
('CKHW124', .002880915),
('CKHW130', .003154996),
('CKHW136', .003441822),
('CKHW142', .003741715),
('CKHW150', .004051506),
('CKHW158', .004375389),
('CKHW166', .005078724),
('CKHW174', .005469937),
('CKHW182', .006071822),
('CKHW190', .006605072),
('CKHW200', .007311717),
('CKHW210', .008037439),
('CKHW222', .009091287),
('CKHW232', .009888443),
('CKHW244', .010907182),
('CKHW254', .011787319),
)

50
string_tensions/notes.py Executable file
View File

@ -0,0 +1,50 @@
#!/usr/bin/python
A = 440
notes = {
'C' : 0,
'D' : 2,
'E' : 4,
'F' : 5,
'G' : 7,
'A' : 9,
'B' : 11,
}
rel = (2**(1/12.))
def note2freq(name):
sharp = name[1] == '#'
flat = name[1] == 'b'
if sharp or flat:
note = name[0:1]
octave = int(name[2])
else:
note = name[0]
octave = int(name[1])
num = notes[note]
if sharp:
num += 1
if flat:
num -= 1
fullnum = num + 12*(octave - 5)
return A*rel**(fullnum + 3)
if __name__ == '__main__':
test_fmt = '{:3} gave {: 9.2f} Hz\nexpected {: 9.2f} Hz'
def test(name, expected):
print(test_fmt.format(name, note2freq(name), expected))
test('C2' , 65.41)
test('Ab4', 415.30)
test('A4' , 440.00)
test('B4' , 493.88)
test('B#4', 523.25)
test('Cb5', 493.88)
test('C5' , 523.25)
print()
test('E4' , 329.63)
test('B3' , 246.94)
test('G3' , 196.00)
test('D3' , 146.83)
test('A2' , 110.00)
test('E2' , 82.41)

81
string_tensions/run.py Executable file
View File

@ -0,0 +1,81 @@
#!/usr/bin/python
from strings import print_ideal_stock
in_mm = 25.4
default_scale_length = 648/in_mm
sets = """
regular light
E4 16 PD
B3 16 PD
G3 16 PD
D3 16 WD
A2 16 WD
E2 16 WD
jazz medium
E4 24 PD
B3 24 PD
G3 24 WD
D3 24 WD
A2 24 WD
E2 24 WD
regular light (DADGAD)
D4 16 PD
A3 16 PD
G3 16 PD
D3 16 WD
A2 16 WD
D2 16 WD
fanned-fret bass
G2 35 WC 34.00
D2 35 WC 34.75
A1 35 WC 35.50
E1 35 WC 36.25
B0 35 WC 37.00
regular light (Open G 7-string)
D4 16 PD
B3 16 PD
G3 16 PD
D3 16 WD
B2 16 WD
G2 16 WD
D2 16 WD
"""
string_sets = {}
sets = sets+'\n\n'
title = None
for line in sets.splitlines():
if not line:
title = None
continue
if not title:
title = line
string_sets[title] = []
else:
fields = line.split()
note, tension = fields[0:2]
tension = int(tension)
req = ''
if len(fields) > 2:
req = fields[2]
length = None
if len(fields) > 3:
length = float(fields[3])
string = (note, tension, req, length)
string_sets[title].append(string)
print('for a scale length of {:>5.2f} inches'.format(default_scale_length))
for name, strings in sorted(string_sets.items()):
print()
print('"{}"'.format(name))
print_ideal_stock(strings, default_scale_length)

84
string_tensions/run2.py Executable file
View File

@ -0,0 +1,84 @@
#!/usr/bin/python
from strings import print_tensions
in_mm = 25.4
default_scale_length = 648/in_mm
sets = """
E standard (super light balanced)
E4 DAPL009
B3 DAPL012
G3 DAPL015
D3 DANW022
A2 DANW030
E2 DANW040
E standard (medium balanced)
E4 DAPL011
B3 DAPL015
G3 DAPL019
D3 DANW028
A2 DANW037
E2 DANW050
C standard (medium balanced)
C4 DAPL011
G3 DAPL015
D#3 DAPL019
A#2 DANW028
F2 DANW037
C2 DANW050
C standard (jazz medium)
C4 DAPL013
G3 DAPL017
D#3 DANW026
A#2 DANW036
F2 DANW046
C2 DANW056
C standard (CKS-G6-14-59mb)
C4 CKPL014
G3 CKPL019
D#3 CKHW025
A#2 CKHW033
F2 CKHW045
C2 CKHW059
B standard (CKS-G6-14-59mb)
B3 CKPL014
F#3 CKPL019
D3 CKHW025
A2 CKHW033
E2 CKHW045
B1 CKHW059
D standard drop C (medium balanced)
D4 DAPL011
A3 DAPL015
F3 DAPL019
C3 DANW028
G2 DANW037
C2 DANW050
"""
string_sets = {}
sets = sets+'\n\n'
title = None
for line in sets.splitlines():
if not line:
title = None
continue
if not title:
title = line
string_sets[title] = []
else:
note, string = line.split()
string_sets[title].append((note, string))
print('for a scale length of {:>5.2f} inches'.format(default_scale_length))
for name, strings in sorted(string_sets.items()):
print()
print('"{}"'.format(name))
print_tensions(strings, default_scale_length)

117
string_tensions/strings.py Executable file
View File

@ -0,0 +1,117 @@
#!/usr/bin/python
from data import *
from notes import note2freq
stock = []
stock += daddario_plain_steel
stock += daddario_nickle_wound
stock += kalium_plain
stock += kalium_hybrid_wound
uw_const = 386.4
def uw2tension(uw, freq, SL):
return (uw*(2*SL*freq)**2)/uw_const
def tension2uw(t, freq, SL):
return (t*uw_const)/(2*SL*freq)**2
outfmt = '\
{:<8} at {:>6.2f} lb ({:>+4.2f})'
finalfmt = '\
average: {:>7.2f} lb ({:>+5.2f})\n\
total: {:>7.2f} lb ({:>+5.2f})'
tension_outfmt = '\
{:<3} {:<8} at {:>6.2f} lb'
tension_finalfmt = '\
average: {:>7.2f} lb\n\
total: {:>7.2f} lb'
def print_ideal_stock(strings, scale_length=25.512):
SL = scale_length
total_tension = 0
total_desired_tension = 0
for note, tension, req, length in strings:
freq = note2freq(note)
if length:
SL = length
else:
SL = scale_length
uw = tension2uw(tension, freq, SL)
kind = len(req) > 0 and req[0]
brand = len(req) > 1 and req[1]
closest = ('n/a', 0)
for name, stock_uw in stock:
if kind and kind == 'P' and name[2] != 'P':
continue
if kind and kind == 'W' and name[3] != 'W':
continue
if brand and brand[0] != name[0]:
continue
if abs(stock_uw - uw) < abs(closest[1] - uw):
closest = (name, stock_uw)
closest_tension = uw2tension(closest[1], freq, SL)
diff = closest_tension - tension
print(outfmt.format(closest[0], closest_tension, diff))
total_tension += closest_tension
total_desired_tension += tension
error = total_tension - total_desired_tension
average_tension = total_tension/len(strings)
average_error = error/len(strings)
print(finalfmt.format(average_tension, average_error, total_tension, error))
def print_tensions(strings, scale_length=25.512):
SL = scale_length
total_tension = 0
for note, name in strings:
freq = note2freq(note)
uw = 0
for stock_name, stock_uw in stock:
if name == stock_name or name + '*' == stock_name:
uw = stock_uw
break
if uw:
tension = uw2tension(uw, freq, SL)
else:
tension = 0
print(tension_outfmt.format(note, name, tension))
total_tension += tension
print(tension_finalfmt.format(total_tension/len(strings), total_tension))
if __name__ == '__main__':
# DAd's data is all screwy so we use change scale lengths for a best-fit
SL = 25.4825
D3 = note2freq('D3')
A2 = note2freq('A2')
E2 = note2freq('E2')
test_fmt = '{:8} {:10.8f} == {:10}'
def test(name, unit, tension, note):
print(test_fmt.format(name, tension2uw(tension, note, SL), unit))
test('NW024' , '0.00010857', 15.73, D3)
test('NW025*', '?' , 17.21, D3)
test('NW026' , '0.00012671', 18.38, D3)
print()
test('NW036' , '0.00023964', 19.04, A2)
test('NW037*', '? ', 20.23, A2)
test('NW038' , '0.00026471', 20.96, A2)
print()
SL = 25.18
test('NW039' , '0.00027932', 12.46, E2)
test('NW040*', '? ', 13.18, E2)
test('NW042' , '0.00032279', 14.37, E2)
print()
SL = 25.02
test('NW049' , '0.00043014', 18.97, E2)
test('NW050*', '? ', 19.68, E2)
test('NW052' , '0.00048109', 21.15, E2)

2170
thps1/names.txt Normal file

File diff suppressed because it is too large Load Diff

168
thps1/thps1.c Normal file
View File

@ -0,0 +1,168 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef uint8_t u8;
typedef int32_t s32;
typedef uint32_t u32;
typedef int64_t s64;
typedef uint64_t u64;
static u64 prng_state = 1;
static u32 prng() {
prng_state = 3935559000370003845 * prng_state + 1;
return prng_state ^ (prng_state >> 32);
}
typedef struct {
u8 name[13];
u8 payload[11];
u32 residual;
} code_t;
void translate(u8 *name) {
// in-place translation of ascii to alphabetical indices.
// the input argument must be able to hold at least 13 characters.
// the resulting translation is no longer null-terminated (if the input even was).
int i;
for (i = 0; i < 13; i++) {
u8 c = name[i];
if (c == '\0') break;
if (c >= 'a' && c <= 'z') {
name[i] = c - 'a';
} else if (c >= 'A' && c <= 'Z') {
name[i] = c - 'A';
} else {
name[i] = 26;
}
}
// remaining space gets filled with a null-like character:
for (; i < 13; i++) {
name[i] = 26;
}
}
u32 multu_hi(u32 a, u32 b) {
// returns the upper 32-bits of a * b (unsigned).
return (u32)(((u64)a * (u64)b) >> 32);
}
s32 mult_lo(s32 a, s32 b) {
// returns the lower 32-bits of a * b (signed).
return (s32)(((s64)a * (s64)b) & 0xFFFFFFFF);
}
void decode(code_t *code) {
u32 r = 0;
u32 a3 = 0x98534637;
for (int i = 0; i < 11; i++) {
u32 a1 = code->name[i];
u32 temp = 0x10000 - (a3 & 0xFFFF);
u32 a0 = a1 + 20 + temp;
u32 v1 = a0 % 27;
code->payload[i] = v1;
r += a1 + mult_lo(r, 0x75344393) + 0x92438937;
a3 = mult_lo(a3 + v1, 0x34985343) + 0x64358931;
}
code->residual = r;
}
int validate(const code_t *code) {
u32 r = code->residual;
if (code->name[11] != r % 27) return 0;
r /= 27;
if (code->name[12] != r % 27) return 0;
return 1;
}
void print_code(const code_t *code) {
for (int i = 0; i < 13; i++) {
char c = code->name[i];
c = c == 26 ? ' ' : c + 'A';
printf("%c", c);
}
printf(" -> ");
for (int i = 0; i < 11; i++) {
char c = code->payload[i];
c = c == 26 ? ' ' : c + 'A';
printf("%c", c);
}
printf("\n");
fflush(stdout);
}
int validate_entry(const char *entry) {
code_t code = {0};
for (int i = 0; i < 13; i++) {
code.name[i] = entry[i];
if (entry[i] == '\0') break;
}
translate(code.name);
decode(&code);
int result = validate(&code);
if (result) print_code(&code);
// DEBUG:
//if (result) printf("%s\n", entry);
//for (i = 0; i < 13; i++) printf(" %02X", code.name[i]); printf("\n");
//for (i = 0; i < 11; i++) printf(" %02X", code.payload[i]); printf("\n");
return result;
}
int bruteforce(int count) {
/* with a fixed seed, this is useful for testing.
the first 10 results should be:
AQPIEOJSFIMJE
JHVBASITTCRQR
RXCUCODXHPAIS
GEXEPVFQBBWZN
EZRTHUSHOWHYG
LBSXAAOTMSUXV
WCDKPNSEWDKAF
NJVIYIYNRWZJQ
RVGLKPYDWYCIA
CEIQQHURYAJAV
*/
int found = 0;
code_t code = {0};
while (found < count) {
for (int j = 0; j < 13; j++) code.name[j] = prng() % 27;
//for (int j = 3; j < 13; j++) code.name[j] = 26;
decode(&code);
if (validate(&code)) {
found++;
print_code(&code);
}
}
return 1;
}
int main(int argc, char **argv) {
if (argc == 1) {
// self-test. only one code was known prior, so...
if (!validate_entry("TYR")) return 1;
return 0;
}
int invalid_count = 0;
for (int i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!strcmp(arg, "bruteforce")) {
invalid_count += !bruteforce(10);
} else {
invalid_count += !validate_entry(arg);
}
}
return !!invalid_count;
}

60
tiny_crc32/crc32.c Normal file
View File

@ -0,0 +1,60 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef BUFFER
#define BUFFER 4096
#endif
uint8_t msg[BUFFER];
/* CRC-32, eg. ethernet */
const uint32_t crc32_tbl[] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
};
/* the idea is to act on nybbles instead of bytes to require less CPU cache */
uint32_t crc32_calc(uint8_t *ptr, int cnt, uint32_t crc)
{
while (cnt--) {
crc = (crc >> 4) ^ crc32_tbl[(crc & 0xf) ^ (*ptr & 0xf)];
crc = (crc >> 4) ^ crc32_tbl[(crc & 0xf) ^ (*(ptr++) >> 4)];
}
return crc;
}
int main(int argc, char **argv)
{
int len;
uint32_t v32 = ~0;
FILE *f;
if (argc == 1) {
freopen(NULL, "rb", stdin);
f = stdin;
} else if (argc == 2) {
f = fopen(argv[1], "rb");
if (f == NULL) {
perror(argv[1]);
exit(1);
}
} else {
fputs("usage: crc32 [filename]", stderr);
exit(1);
}
do {
len = fread(msg, 1, BUFFER, f);
if (ferror(f)) {
perror(NULL);
exit(1);
}
v32 = crc32_calc(msg, len, v32);
} while (!feof(f));
fclose(f);
printf("%08lX\n", (unsigned long) ~v32);
}

139
trackmogrify/colors.txt Normal file
View File

@ -0,0 +1,139 @@
indianRed
lightCoral
salmon
darkSalmon
lightSalmon
crimson
red
fireBrick
darkRed
pink
lightPink
hotPink
deepPink
mediumVioletRed
paleVioletRed
coral
tomato
orangeRed
darkOrange
orange
yellow
lightYellow
lemonChiffon
lightGoldenrodYellow
papayaWhip
moccasin
peachPuff
paleGoldenrod
khaki
darkKhaki
lavender
thistle
plum
violet
orchid
fuchsia
magenta
mediumOrchid
mediumPurple
amethyst
blueViolet
darkViolet
darkOrchid
darkMagenta
purple
indigo
slateBlue
darkSlateBlue
mediumSlateBlue
greenYellow
chartreuse
lawnGreen
lime
limeGreen
paleGreen
lightGreen
mediumSpringGreen
springGreen
mediumSeaGreen
seaGreen
forestGreen
green
darkGreen
yellowGreen
oliveDrab
olive
darkOliveGreen
mediumAquamarine
darkSeaGreen
lightSeaGreen
darkCyan
teal
aqua
cyan
lightCyan
paleTurquoise
aquamarine
turquoise
mediumTurquoise
darkTurquoise
cadetBlue
steelBlue
lightSteelBlue
powderBlue
lightBlue
skyBlue
lightSkyBlue
deepSkyBlue
dodgerBlue
cornflowerBlue
royalBlue
blue
mediumBlue
darkBlue
navy
midnightBlue
cornsilk
blanchedAlmond
bisque
navajoWhite
wheat
burlyWood
tan
rosyBrown
sandyBrown
goldenrod
darkGoldenrod
peru
chocolate
saddleBrown
sienna
brown
maroon
white
snow
honeydew
mintCream
azure
aliceBlue
ghostWhite
whiteSmoke
seashell
beige
oldLace
floralWhite
ivory
antiqueWhite
linen
lavenderBlush
mistyRose
gainsboro
lightGrey
darkGray
gray
dimGray
lightSlateGray
slateGray
darkSlateGray
black

161
trackmogrify/modifiers.txt Normal file
View File

@ -0,0 +1,161 @@
america
rainbow
neon
zebra
minecraft
distance
infected
old
toon
spooky
graveyard
spike
ball
tube
hex
donut
pill
clear
party
disco
dance
plant
industrial
forest
alliance
logo
sponsored
pop-up
thunder
lantern
downhill
fall
lower
uphill
climb
ascent
rise
elevate
above
under
below
chill
boring
angry
never
not
anti
barely
slightly
kinda
so
very
double
super
triple
mega
ultra
insanely
long
marathon
short
momentary
quick
fleeting
dash
monster
colossus
big
giant
small
mini
tiny
shrunk
micro
vast
space
tight
uniform
iridescent
pearl
tron
metallic
metal
copper
gold
silver
platinum
brass
iron
steel
aluminum
bronze
titanium
road
casual
trivial
realm
safe
easy
medium
advanced
hard
expert
hazardous
nightmare
doom
desaturated
mute
saturated
bright
dim
stone
light
dark
shadow
flame
fire
ice
ooze
slime
radioactive
radiation
biohazard
sea
ocean
water
marine
storm
stop
morning
sunrise
noon
day
afternoon
evening
sunset
night
midnight
moon
fog
foggy
mist
misty
gas
shiny
calm
rollercoaster
twisted
crazy
snakey
snaky
zigzag
winding
bumpy
bouncy
corkscrew
helix
upsidedown
inverse
inverted
ceiling

View File

@ -0,0 +1,61 @@
#!/usr/bin/env bash
set -e
# super quickly hacked together script
# for listing your twitch following from the command-line.
# REQUIREMENTS:
# editing this script.
# downloading a python script.
# python 2 or 3.
# a web browser.
# curl.
# GNU sort.
# GNU awk.
# TODO:
# fix known issues.
# automatically extract relevant values from a cURL string.
# add toggle for colors.
# optional CSV output.
# automatically download json2?
# rewrite in python?
# fill these in with values extracted from your browser's
# "copy as cURL" feature.
# 1. open a new tab in Chromium or Firefox; these instructions apply to either.
# 2. open the web inspector (ctrl+shift+I) and click on the network tab.
# 3. go to https://www.twitch.tv/directory/following/live
# make sure this is with the network tab already open!
# if it isn't, open it up and just refresh the page.
# 4. type "followed" in the filter of the network tab.
# 5. right click on the first result and click copy as cURL.
# in Chromium, it's in a submenu as "copy as cURL (bash)"
# 6. paste into a text editor and extract the following header values:
# (each header is specified by a -H flag)
authorization="Authorization: OAuth abcdefghijklmnopqrstuvwxyz1234"
json2() {
# download this from https://github.com/vi/json2/blob/master/json2
# and edit this line accordingly.
python3 ~/src/json2/json2 "$@"
}
curl -LsS 'https://gql.twitch.tv/gql' \
-H "$authorization" \
--data-binary '[{"variables":{"limit":100},"extensions":{},"operationName":"FollowingLive_CurrentUser","query":"query FollowingLive_CurrentUser($limit: Int, $cursor: Cursor) {\n currentUser {\n follows {\n totalCount\n }\n followedLiveUsers(first: $limit, after: $cursor) {\n edges {\n node {\n login\n displayName\n stream {\n game {\n name\n }\n viewersCount\n title\n type\n }\n }\n }\n pageInfo {\n hasNextPage\n }\n }\n }\n}\n"}]' \
| json2 | tr -d '\r' | awk '
function trunc(s,L) {
e = length(s) > L ? "…" : "";
return (substr(s, 0, L -(e ? 1 : 0)) e);
}
$1~/\/stream\/game\/name$/{game=substr($2,2)}
$1~/\/stream\/title$/{title=substr($2,2)}
$1~/\/login$/{name=substr($2,2)}
$1~/\/viewersCount$/{viewers=substr($2,2)}
$1~/\/stream\/type$/{
printf "\x1b[90m%5s\x1b[0m \x1b[97m%-25s\x1b[0m %s\n \x1b[36m%s\x1b[0m\n",
viewers, name, trunc(game, 48), title;
}
' FPAT='(^[^=]+)|(=.*)'

81
warcraft_hash/wc3hash.c Normal file
View File

@ -0,0 +1,81 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
typedef uint8_t u8;
typedef uint32_t u32;
typedef uint64_t u64;
const u64 table[16] = {
// magic numbers for each possible value of a nybble.
0x486E26EEDCAA16B3, 0xE1918EEF202DAFDB,
0x341C7DC71C365303, 0x40EF2D3765FD5E49,
0xD6057177904ECE93, 0x1C38024F98FD323B,
0xE3061AE7A39B0FA1, 0x9797F25FE4444563,
0xCD2EC20C8DC1B898, 0x31759633799A306D,
0x8C2063852E6E9627, 0x79237D9973922C66,
0x8728628D28628824, 0x8F1F7E9625887795,
0x296E3281389C0D60, 0x6F4893CA61636542
};
u64 wc3hash(const char *key, size_t len, u64 hash, bool isPath) {
// technically there doesn't seem to be a
// length argument in the original function,
// I'm just adding it so you can hash strings with '\0' in them.
if (key == NULL)
return 0;
if (hash == 0)
hash = 0x7FED7FED7FED7FED;
u64 state = 0xEEEEEEEEEEEEEEEE;
for (size_t i = 0; i < len; i++) {
u8 v;
if (isPath) {
char c = key[i];
if (c >= 'a' && c <= 'z')
c -= 'a' - 'A'; // uppercase
if (c == '/')
c = '\\'; // DOS-style paths
v = (u8)c;
} else {
v = (u8)key[i];
}
hash += state;
hash ^= table[v >> 4] + table[v & 0xF];
state += state << 5;
state += hash + v + 3;
}
if (hash == 0)
return 1;
return hash;
}
u64 wc3hashC(const char *key) {
// simple interface that only takes a null-terminated string.
return wc3hash(key, strlen(key), 0, true);
}
int main(int argc, char **argv) {
if (argc == 1) {
const char str[] = "IseeDeadPeople";
u64 hash = wc3hashC(str);
fprintf(stderr, "%" PRIX64 " should equal 701EA16D47F385FC\n", hash);
return hash != 0x701EA16D47F385FC;
}
for (int i = 1; i < argc; i++) {
u64 hash = wc3hashC(argv[i]);
u32 hash32 = (hash >> 32) ^ hash;
printf("%" PRIX64 "\t%08X\n", hash, hash32);
}
return 0;
}

View File

@ -0,0 +1,207 @@
# found hashes in Warcraft III (The Frozen Throne) 1.28c
## known cheats
### Synergy
`F9CC37EF6A43F87B 938FCF94 0AD0048C 0AD008A8 0AD00854 0AD00428 00000000`
### RiseAndShine
`3A307C034564F51C 7F54891F 0AB801E8 0AD00908 0AD00404 0AD00458 00000000`
### LightsOut
`AB05DD8482042FDD 2901F259 0AB801A0 0AD00878 0AD00434 0AD00488 00000000`
### DayLightSavings
`274C1EE817B2556C 30FE4B84 0AB801C4 0AD003F8 0AD00464 0AD004B8 00000000`
### SharpAndShiny
`DEEE313DDE8F4C46 00617D7B 0AD004EC 0AD00848 0AD00494 0AD004E8 00000000`
### SomebodySetUpUsTheBomb
`559E5996EE428C15 BBDCD583 0AB801B8 0AD004B8 0AD004C4 0AD00C98 00000000`
### TenthLevelTaurenChieftain
`831ED0A2DC6523A9 5F7BF30B 0AD004BC 0AD008D8 0AD00884 0AD003F8 00000000`
### IseeDeadPeople
`701EA16D47F385FC 37ED2491 0AD0045C 0AD00B18 0AD008B4 0AD00848 00000000`
### GreedIsGood
`20ACC89F1CC95E1B 3C659684 0AD003FC 0AD00B48 0AD008E4 0AD00878 00000000`
### WhoIsJohnGalt
`AF7D59528BF4CA21 24899373 0AD0084C 0AD00A88 0AD00914 0AD008A8 00000000`
### TheDudeAbides
`EEE62EB91EB79CA6 F051B21F 0AD0042C 0AD00968 0AD00944 0AD008D8 00000000`
### ItVexesMe
`CA29470C3EB71379 F49E5475 0AB801D0 0AD00AE8 0AD00974 0AD00908 00000000`
### StrengthAndHonor
`762B2E78DB40C1E7 AD6BEF9F 0AD0090C 0AD00218 0AD009A4 0AD00938 00000000`
### ThereIsNoSpoon
`9131AC63D1E29109 40D33D6A 0AD0099C 0AD00A58 0AD00A04 0AD00998 00000000`
### LeafitToMe
`F3E36048DD3DADB8 2EDECDF0 0AB80194 0AD00A28 0AD00A34 0AD009C8 00000000`
### Motherland
`AA5CEFDDC541C195 6F1D2E48 0AD009FC 0AD00188 0AD00A64 0AD009F8 00000000`
### KeyserSoze
`90DAFFD11E7A8FC3 8EA07012 0AD009CC 0AD00AB8 0AD00A94 0AD00A28 00000000`
### WhosYourDaddy
`F3176D0490330CF7 632461F3 0AD008DC 0AD00128 0AD00AC4 0AD00A58 00000000`
### PointBreak
`0B03F57AA3F85088 A8FBA5F2 0AD00A5C 0AD00248 0AD00AF4 0AD00A88 00000000`
### IocainePowder
`D225C8F453D8E4C9 81FD2C3D 0AD0093C 0AD001B8 0AD00B24 0AD00AB8 00000000`
### WarpTen
`9F4929AF30929AE6 AFDBB349 0AD0087C 0AD001E8 0AD00B54 0AD00AE8 00000000`
### AllYourBaseAreBelongToUs
`F1DACF199149F9EF 609336F6 0AB801DC 0AD00278 0AD004F4 F548FF4F 00000000`
## known hashes
### SMReload
`5C103D4172ECF421 2EFCC960 0AD000CC F547FE6B 0AB700B0 0AD000C8 52333D20`
### ps
`BCE67317FD3989EF 41DFFAF8 0AD0018C 0AD00098 0AD000A4 0AD000F8 52333D2C`
### ptm
`8D06F935F3B3CBBF 7EB5328A 0AD0024C F547FE53 0AD000D4 0AD00128 52333D30`
### SMLoadCustomSLK
`4D86DCAB95A991D8 D82F4D73 0AD00A8C F547FE47 0AD00104 0AD00158 52333D34`
### SMUnloadCustomSLK
`6B8E333B3BB2AA14 503C992F 0AD0021C F547FE17 0AD00134 0AD00188 52333D44`
### SMEnable
`558C46F5E6B0AB35 B33CEDC0 0AD00A2C 0AD000C8 0AD00164 0AD001B8 52333D58`
### SMDisable
`B2F0229471D85C61 C3287EF5 0AD00AEC F547FE2F 0AD00194 0AD001E8 52333D64`
### ChannelVolume
`BB507AD60538D687 BE68AC51 0AD00B1C F547FE5F 0AD001C4 0AD00218 52333D70`
### SMEnvironment
`656682F14532D42E 205456DF 0AD0096C 0AD00158 0AD001F4 0AD00248 52333D80`
### SMVerbose
`FF46DE59CD796AC3 323FB49A 0AD00ABC 0AD000F8 0AD00224 0AD00278 52333D90`
### SM3DVolumeScale
`F77E958F2DD2CF09 DAAC5A86 0AD00C9C F547FE23 0AD00254 0AD00B48 52333D9C`
### wenodel
`FF2E83780E1BF8A1 F1357BD9 0AD0030C F547FEFF 0AB700F8 0AD002D8 523FE874`
### mmhires
`D938517FCD2C1095 141441EA 0AD0036C F547FEF3 0AD002B4 0AD00308 523FE8A4`
### mmfilter
`55E017D0FA9D51C5 AF7D4615 0AD003CC 0AD002A8 0AD002E4 0AD00338 523FE8D0`
### wenoanim
`B62FE0AA562D4C62 E002ACC8 0AD0039C F547FF0B 0AD00314 0AD00368 523FE8F4`
### debugimageload
`DACCB9B21890F58C C25C4C3E 0AB8010C 0AD002D8 0AD00344 0AD00398 523FE920`
### combinecliffs
`9043F7C49F06AC08 0F455BCC 0AB800F4 0AD00338 0AD00374 0AD003C8 523FE950`
### desthm
`DF5D5BD9F36D078C 2C305C55 0AB80100 0AD00308 0AD003A4 F548FF07 523FE984`
### terstats
`CAF538359FE3B43B 55168C0E 0AD0060C F547FE8B 0AB70140 0AD00548 52402584`
### showgridnorms
`095FF91A9C8ECFDE 95D136C4 0AD006CC F547FEA3 0AD00524 0AD00578 524025AC`
### showcliffnorms
`A5E6D9C3C53B8004 60DD59C7 0AD007EC F547FE7F 0AD00554 0AD005A8 524025D0`
### showvertcolor
`A9E4099B6A680CBE C38C0525 0AD0069C F547FE97 0AD00584 0AD005D8 524025F8`
### togcliffmeld
`880E433F593BEFA6 D135AC99 0AD0078C F547FEC7 0AD005B4 0AD00608 52402628`
### testfog
`7FC2CDAE434EE518 3C8C28B6 0AD00C3C 0AD00518 0AD005E4 0AD00638 52402650`
### tetris
`C0DBC99B9AED0083 5A36C918 0AD0066C F547FED3 0AD00614 0AD00668 52402670`
### debugcliffadjust
`5EBE4C01F65F44C1 A8E108C0 0AD00B7C 0AD00638 0AD00644 0AD00698 52402694`
### puffstats
`4868E581E70D2E84 AF65CB05 0AD006FC 0AD005A8 0AD00674 0AD006C8 524026C8`
### markwater
`C5F3A6A3AD116C47 68E2CAE4 0AD0075C 0AD00548 0AD006A4 0AD006F8 524026EC`
### debugdoodsearch
`71DB3C34684345F9 199879CD 0AB80168 0AD00698 0AD006D4 0AD00728 5240270C`
### showocc
`E7A83F207AF3FFAA 9D5BC08A 0AD00C6C F547FEBB 0AD00704 0AD00758 52402738`
### occalpha
`870461D7A64988D3 214DE904 0AD007BC 0AD006C8 0AD00734 0AD00788 52402760`
### gcstats
`FC7C3275F856BDBC 042A8FC9 0AD0081C 0AD005D8 0AD00764 0AD007B8 52402788`
### fcstats
`AF5C1C16C20A72D2 6D566EC4 0AD00C0C 0AD00758 0AD00794 0AD007E8 524027A8`
### tersort
`1490D7F9D53D3986 C1ADEE7F 0AB80180 0AD00578 0AD007C4 0AD00818 524027C8`
### shownaval
`F2605BB3FF968B32 0DF6D081 0AB80138 0AD00788 0AD007F4 0AD00C68 524027E8`
### maxfps
`F3C623DE6158790C 929E5AD2 0AB801AC 0AD009C8 0AD009D4 0AD00968 522FA6C0`
### gridhl
`3C01956D04BC7671 38BDE31C 0AD008AC F547FE3B 0AD00284 0AD00B18 52402F10`
### occstats
`A45FA7037D01759B D95ED298 0AB8012C 0AD00668 0AD00BB4 F548FEBF 52402838`
### termesh
`4E933DDD69E6A867 277595BA 0AB80144 0AD00C68 0AD00BE4 0AD00B78 52402830`
### termemf
`84ADED3C39A35A2A BD0EB716 0AB80174 0AD00C38 0AD00C14 0AD00BA8 52402828`
### termemd
`91AC838C2AD71938 BB7B9AB4 0AB8015C 0AD007B8 0AD00C44 0AD00BD8 52402820`
### termem
`71B5C4882E2E089E 5F9BCC16 0AD00BDC 0AD00608 0AD00C74 0AD00C08 52402818`
### revwarnings
`C2205630D42AC772 160A9142 0AD00BAC 0AD00728 0AD00824 0AD00C38 5240280C`
## raw data
```
00000030 6F6D0AD0 2EFCC960 0AD000CC F547FE6B 0AB700B0 0AD000C8 00000000 72ECF421 5C103D41 52333D20 00000000
00000030 6F6D0AD0 41DFFAF8 0AD0018C 0AD00098 0AD000A4 0AD000F8 00000000 FD3989EF BCE67317 52333D2C 00000000
00000030 6F6D0AD0 7EB5328A 0AD0024C F547FE53 0AD000D4 0AD00128 00000000 F3B3CBBF 8D06F935 52333D30 00000000
00000030 6F6D0AD0 D82F4D73 0AD00A8C F547FE47 0AD00104 0AD00158 00000000 95A991D8 4D86DCAB 52333D34 00000000
00000030 6F6D0AD0 503C992F 0AD0021C F547FE17 0AD00134 0AD00188 00000000 3BB2AA14 6B8E333B 52333D44 00000000
00000030 6F6D0AD0 B33CEDC0 0AD00A2C 0AD000C8 0AD00164 0AD001B8 00000000 E6B0AB35 558C46F5 52333D58 00000000
00000030 6F6D0AD0 C3287EF5 0AD00AEC F547FE2F 0AD00194 0AD001E8 00000000 71D85C61 B2F02294 52333D64 00000000
00000030 6F6D0AD0 BE68AC51 0AD00B1C F547FE5F 0AD001C4 0AD00218 00000000 0538D687 BB507AD6 52333D70 00000000
00000030 6F6D0AD0 205456DF 0AD0096C 0AD00158 0AD001F4 0AD00248 00000000 4532D42E 656682F1 52333D80 00000000
00000030 6F6D0AD0 323FB49A 0AD00ABC 0AD000F8 0AD00224 0AD00278 00000000 CD796AC3 FF46DE59 52333D90 00000000
00000030 6F6D0AD0 DAAC5A86 0AD00C9C F547FE23 0AD00254 0AD00B48 00000000 2DD2CF09 F77E958F 52333D9C 00000000
00000030 6F6D0AD0 F1357BD9 0AD0030C F547FEFF 0AB700F8 0AD002D8 00000000 0E1BF8A1 FF2E8378 523FE874 00000000
00000030 6F6D0AD0 141441EA 0AD0036C F547FEF3 0AD002B4 0AD00308 00000000 CD2C1095 D938517F 523FE8A4 00000000
00000030 6F6D0AD0 AF7D4615 0AD003CC 0AD002A8 0AD002E4 0AD00338 00000000 FA9D51C5 55E017D0 523FE8D0 00000000
00000030 6F6D0AD0 E002ACC8 0AD0039C F547FF0B 0AD00314 0AD00368 00000000 562D4C62 B62FE0AA 523FE8F4 00000000
00000030 6F6D0AD0 C25C4C3E 0AB8010C 0AD002D8 0AD00344 0AD00398 00000000 1890F58C DACCB9B2 523FE920 00000000
00000030 6F6D0AD0 0F455BCC 0AB800F4 0AD00338 0AD00374 0AD003C8 00000000 9F06AC08 9043F7C4 523FE950 00000000
00000030 6F6D0AD0 2C305C55 0AB80100 0AD00308 0AD003A4 F548FF07 00000000 F36D078C DF5D5BD9 523FE984 00000000
00000030 6F6D0AD0 938FCF94 0AD0048C 0AD008A8 0AD00854 0AD00428 00000000 6A43F87B F9CC37EF 00000000 00000000
00000030 6F6D0AD0 7F54891F 0AB801E8 0AD00908 0AD00404 0AD00458 00000000 4564F51C 3A307C03 00000000 00000000
00000030 6F6D0AD0 2901F259 0AB801A0 0AD00878 0AD00434 0AD00488 00000000 82042FDD AB05DD84 00000000 00000000
00000030 6F6D0AD0 30FE4B84 0AB801C4 0AD003F8 0AD00464 0AD004B8 00000000 17B2556C 274C1EE8 00000000 00000000
00000030 6F6D0AD0 00617D7B 0AD004EC 0AD00848 0AD00494 0AD004E8 00000000 DE8F4C46 DEEE313D 00000000 00000000
00000030 6F6D0AD0 BBDCD583 0AB801B8 0AD004B8 0AD004C4 0AD00C98 00000000 EE428C15 559E5996 00000000 00000000
00000030 6F6D0AD0 55168C0E 0AD0060C F547FE8B 0AB70140 0AD00548 00000000 9FE3B43B CAF53835 52402584 00000000
00000030 6F6D0AD0 95D136C4 0AD006CC F547FEA3 0AD00524 0AD00578 00000000 9C8ECFDE 095FF91A 524025AC 00000000
00000030 6F6D0AD0 60DD59C7 0AD007EC F547FE7F 0AD00554 0AD005A8 00000000 C53B8004 A5E6D9C3 524025D0 00000000
00000030 6F6D0AD0 C38C0525 0AD0069C F547FE97 0AD00584 0AD005D8 00000000 6A680CBE A9E4099B 524025F8 00000000
00000030 6F6D0AD0 D135AC99 0AD0078C F547FEC7 0AD005B4 0AD00608 00000000 593BEFA6 880E433F 52402628 00000000
00000030 6F6D0AD0 3C8C28B6 0AD00C3C 0AD00518 0AD005E4 0AD00638 00000000 434EE518 7FC2CDAE 52402650 00000000
00000030 6F6D0AD0 5A36C918 0AD0066C F547FED3 0AD00614 0AD00668 00000000 9AED0083 C0DBC99B 52402670 00000000
00000030 6F6D0AD0 A8E108C0 0AD00B7C 0AD00638 0AD00644 0AD00698 00000000 F65F44C1 5EBE4C01 52402694 00000000
00000030 6F6D0AD0 AF65CB05 0AD006FC 0AD005A8 0AD00674 0AD006C8 00000000 E70D2E84 4868E581 524026C8 00000000
00000030 6F6D0AD0 68E2CAE4 0AD0075C 0AD00548 0AD006A4 0AD006F8 00000000 AD116C47 C5F3A6A3 524026EC 00000000
00000030 6F6D0AD0 199879CD 0AB80168 0AD00698 0AD006D4 0AD00728 00000000 684345F9 71DB3C34 5240270C 00000000
00000030 6F6D0AD0 9D5BC08A 0AD00C6C F547FEBB 0AD00704 0AD00758 00000000 7AF3FFAA E7A83F20 52402738 00000000
00000030 6F6D0AD0 214DE904 0AD007BC 0AD006C8 0AD00734 0AD00788 00000000 A64988D3 870461D7 52402760 00000000
00000030 6F6D0AD0 042A8FC9 0AD0081C 0AD005D8 0AD00764 0AD007B8 00000000 F856BDBC FC7C3275 52402788 00000000
00000030 6F6D0AD0 6D566EC4 0AD00C0C 0AD00758 0AD00794 0AD007E8 00000000 C20A72D2 AF5C1C16 524027A8 00000000
00000030 6F6D0AD0 C1ADEE7F 0AB80180 0AD00578 0AD007C4 0AD00818 00000000 D53D3986 1490D7F9 524027C8 00000000
00000030 6F6D0AD0 0DF6D081 0AB80138 0AD00788 0AD007F4 0AD00C68 00000000 FF968B32 F2605BB3 524027E8 00000000
00000030 6F6D0AD0 5F7BF30B 0AD004BC 0AD008D8 0AD00884 0AD003F8 00000000 DC6523A9 831ED0A2 00000000 00000000
00000030 6F6D0AD0 37ED2491 0AD0045C 0AD00B18 0AD008B4 0AD00848 00000000 47F385FC 701EA16D 00000000 00000000
00000030 6F6D0AD0 3C659684 0AD003FC 0AD00B48 0AD008E4 0AD00878 00000000 1CC95E1B 20ACC89F 00000000 00000000
00000030 6F6D0AD0 24899373 0AD0084C 0AD00A88 0AD00914 0AD008A8 00000000 8BF4CA21 AF7D5952 00000000 00000000
00000030 6F6D0AD0 F051B21F 0AD0042C 0AD00968 0AD00944 0AD008D8 00000000 1EB79CA6 EEE62EB9 00000000 00000000
00000030 6F6D0AD0 F49E5475 0AB801D0 0AD00AE8 0AD00974 0AD00908 00000000 3EB71379 CA29470C 00000000 00000000
00000030 6F6D0AD0 AD6BEF9F 0AD0090C 0AD00218 0AD009A4 0AD00938 00000000 DB40C1E7 762B2E78 00000000 00000000
00000030 6F6D0AD0 929E5AD2 0AB801AC 0AD009C8 0AD009D4 0AD00968 00000000 6158790C F3C623DE 522FA6C0 00000000
00000030 6F6D0AD0 40D33D6A 0AD0099C 0AD00A58 0AD00A04 0AD00998 00000000 D1E29109 9131AC63 00000000 00000000
00000030 6F6D0AD0 2EDECDF0 0AB80194 0AD00A28 0AD00A34 0AD009C8 00000000 DD3DADB8 F3E36048 00000000 00000000
00000030 6F6D0AD0 6F1D2E48 0AD009FC 0AD00188 0AD00A64 0AD009F8 00000000 C541C195 AA5CEFDD 00000000 00000000
00000030 6F6D0AD0 8EA07012 0AD009CC 0AD00AB8 0AD00A94 0AD00A28 00000000 1E7A8FC3 90DAFFD1 00000000 00000000
00000030 6F6D0AD0 632461F3 0AD008DC 0AD00128 0AD00AC4 0AD00A58 00000000 90330CF7 F3176D04 00000000 00000000
00000030 6F6D0AD0 A8FBA5F2 0AD00A5C 0AD00248 0AD00AF4 0AD00A88 00000000 A3F85088 0B03F57A 00000000 00000000
00000030 6F6D0AD0 81FD2C3D 0AD0093C 0AD001B8 0AD00B24 0AD00AB8 00000000 53D8E4C9 D225C8F4 00000000 00000000
00000030 6F6D0AD0 AFDBB349 0AD0087C 0AD001E8 0AD00B54 0AD00AE8 00000000 30929AE6 9F4929AF 00000000 00000000
00000030 6F6D0AD0 38BDE31C 0AD008AC F547FE3B 0AD00284 0AD00B18 00000000 04BC7671 3C01956D 52402F10 00000000
00000030 6F6D0AD0 D95ED298 0AB8012C 0AD00668 0AD00BB4 F548FEBF 00000000 7D01759B A45FA703 52402838 00000000
00000030 6F6D0AD0 277595BA 0AB80144 0AD00C68 0AD00BE4 0AD00B78 00000000 69E6A867 4E933DDD 52402830 00000000
00000030 6F6D0AD0 BD0EB716 0AB80174 0AD00C38 0AD00C14 0AD00BA8 00000000 39A35A2A 84ADED3C 52402828 00000000
00000030 6F6D0AD0 BB7B9AB4 0AB8015C 0AD007B8 0AD00C44 0AD00BD8 00000000 2AD71938 91AC838C 52402820 00000000
00000030 6F6D0AD0 5F9BCC16 0AD00BDC 0AD00608 0AD00C74 0AD00C08 00000000 2E2E089E 71B5C488 52402818 00000000
00000030 6F6D0AD0 160A9142 0AD00BAC 0AD00728 0AD00824 0AD00C38 00000000 D42AC772 C2205630 5240280C 00000000
00000030 6F6D0AD0 609336F6 0AB801DC 0AD00278 0AD004F4 F548FF4F 00000000 9149F9EF F1DACF19 00000000 00000000
```