Compare commits
251 commits
Author | SHA1 | Date | |
---|---|---|---|
d261b299db | |||
52956fd397 | |||
83b73c0930 | |||
f94f5382de | |||
bddb5c32ea | |||
8ce28c4bf4 | |||
6e1544f860 | |||
df441b445c | |||
9f94bd2f45 | |||
b3d8d55997 | |||
7188ba47c5 | |||
7739895224 | |||
3aaee2c09f | |||
61188f00da | |||
02f586f1cd | |||
a492e20819 | |||
fc6c9faeb2 | |||
591d5bb5ca | |||
67d6447acf | |||
591b496479 | |||
ca0e5c7fca | |||
7220056b79 | |||
1d216288db | |||
fe84c73459 | |||
b87651c549 | |||
f8c15bc5b7 | |||
70b59d455d | |||
bc2365daff | |||
04ccfec87b | |||
f66ec0a8a6 | |||
ffeef6ee6d | |||
57461ace0b | |||
18fbf59cb7 | |||
96a45a4ac4 | |||
53a13fb30b | |||
f8ffcc851a | |||
3d0fb91f5d | |||
bd110dca48 | |||
f9bd047c58 | |||
93f7ce654d | |||
c626d93521 | |||
88bde0c705 | |||
47b5fcc31c | |||
07c587287a | |||
0527b538ed | |||
9ec61f013f | |||
71da8551ab | |||
cddb62a3a0 | |||
fc9ff7a8b4 | |||
376ab80b03 | |||
67a6535439 | |||
2c226e0ec8 | |||
6c7a9be5df | |||
bc4506b3bd | |||
e1996520c2 | |||
498cdba5a2 | |||
d5c51aae94 | |||
999da5ac53 | |||
ffbdf32f13 | |||
cf507ec580 | |||
4f5375a378 | |||
b79aed3f66 | |||
71705de4ac | |||
e1c1844fb2 | |||
4ce6e4ae96 | |||
f2c1354637 | |||
c088d79dec | |||
213b5d0ccb | |||
9f39f4aa14 | |||
b9960ee594 | |||
6b81464a74 | |||
d0efbb3886 | |||
35921704e3 | |||
703f4b7039 | |||
8f42809229 | |||
e0160e220c | |||
41488120af | |||
b7924de0c4 | |||
130989789a | |||
7cb3341158 | |||
965064ef90 | |||
bec9cc266a | |||
7a7597ce83 | |||
8f568e07ff | |||
e12cde76c0 | |||
52ee141c09 | |||
7de1254516 | |||
31de1416b1 | |||
1576082e43 | |||
221362d19b | |||
3a378d5e56 | |||
130725b759 | |||
6247148a85 | |||
e9c0c0b245 | |||
145ccd8f82 | |||
6dbace24f9 | |||
54de7512c9 | |||
cf893660eb | |||
eeed9ea275 | |||
4031c2dbda | |||
17040c6b77 | |||
e8e11c3ad3 | |||
f6c8945b8c | |||
dfe69c396f | |||
248a9c0305 | |||
7c5834de20 | |||
a904f76704 | |||
207603e462 | |||
74cd548adb | |||
7321c3c9e2 | |||
5b83a72513 | |||
50b1e9a48e | |||
cd0b5a8b4b | |||
22e3eceb5d | |||
f4c682bbea | |||
aab2829c9c | |||
db487fe6c3 | |||
40b0a332a5 | |||
d31b7b580e | |||
dd81f51280 | |||
29e33e832e | |||
614fa18ea6 | |||
14d49ba33c | |||
1ec021fc8a | |||
5bd84506a0 | |||
daafe529f3 | |||
8ebad01de8 | |||
f8cc5a50db | |||
4c0887edfb | |||
401732d4be | |||
a8cc797d0f | |||
cebd8b94aa | |||
f055ff2497 | |||
12a9b1640d | |||
7ccb616fee | |||
bd2496fa0e | |||
0243eb33dd | |||
075418af21 | |||
1815a0b996 | |||
add6801a23 | |||
eb7ead0b78 | |||
56e4333bab | |||
273abe1ee4 | |||
89a434c5e8 | |||
f8fbea1e92 | |||
309749f0db | |||
1ee01e95ef | |||
e8e5428400 | |||
ef338fea75 | |||
44a3945bf2 | |||
8093266ec7 | |||
caf9a2aab7 | |||
327b8ef28c | |||
2f145eb879 | |||
f361a02852 | |||
f7e3cfceb1 | |||
15d053789e | |||
2c824ad30b | |||
959be16350 | |||
af75dcc7c5 | |||
c76f172975 | |||
6fb6d3ba62 | |||
03860a8c7f | |||
82010d49d8 | |||
259475bfe6 | |||
7f5dd0453e | |||
121dd44429 | |||
8c8b5d313a | |||
fd56c5124a | |||
e02bca097c | |||
b61ecf090a | |||
98f669262c | |||
d88fe0dde7 | |||
b028ee53d9 | |||
e61a32c615 | |||
eeb5d2941e | |||
28edd29072 | |||
db3171ac29 | |||
14af124e09 | |||
00c1942cba | |||
828c74cb72 | |||
82917de18f | |||
bc1eceaaf7 | |||
6a2eb1ffae | |||
26904c640f | |||
ec865923a5 | |||
a111b309cb | |||
814cd636c0 | |||
b4e1ec0a59 | |||
176b8b32b0 | |||
8d44432e0f | |||
11b0a802f3 | |||
a6aed4fcc5 | |||
62f12d031a | |||
2bbe301cdc | |||
d4fbd7a829 | |||
34db3bf060 | |||
e824a1f8ba | |||
f937547991 | |||
9ef423de71 | |||
83f1fbe6fc | |||
3aae7d6c1d | |||
115a30cc5b | |||
1fb3df1403 | |||
bb5eabbd56 | |||
cf0d3cf896 | |||
051265ea3a | |||
124ec51da3 | |||
62866ee36c | |||
ab2b4a4dff | |||
a61ff7660c | |||
5cb1407dfa | |||
c61c00dfd6 | |||
fef8ae685d | |||
9d475fdba7 | |||
4bbf061085 | |||
f7ac8f0704 | |||
b4eeededf8 | |||
a236ef5a51 | |||
dbed8bb926 | |||
14014e2e93 | |||
6ae05ac68d | |||
d1ea330243 | |||
1a2c9ae1e6 | |||
6a1562c5f5 | |||
e5b036d9b8 | |||
c716e824dd | |||
9c69160563 | |||
e4732d88cd | |||
20b8930ee6 | |||
98b94536f7 | |||
290370630e | |||
dceb0894b3 | |||
654b8ce3f7 | |||
c8fc1d462e | |||
2edb454851 | |||
2ed08389c9 | |||
5865c56fd8 | |||
31ebe0b649 | |||
ea6bf420b2 | |||
6e286aec91 | |||
85da14dad0 | |||
b26f4074ec | |||
b739c4b6c0 | |||
097b5e519b | |||
39c1899843 | |||
5456e17c8b | |||
0c131cf1ea | |||
9da2f9fc2e | |||
54aff32db3 | |||
15b7cb75e5 |
72 changed files with 11936 additions and 0 deletions
64
360_wasd/360-stick.ahk
Normal file
64
360_wasd/360-stick.ahk
Normal 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
268
README.md
Normal 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!
|
251
arithmetic_coding/ac_encode.py
Normal file
251
arithmetic_coding/ac_encode.py
Normal 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
280
atttt/atttt.py
Executable 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
189
atttt/basic.py
Executable 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
11
atttt/misc.py
Executable 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
40
batch_encoding/enc.bat
Executable 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%
|
54
batch_font_render/render.zsh
Normal file
54
batch_font_render/render.zsh
Normal 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
19
danbooru_atomizer/get.py
Normal 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
|
||||
|
17
danbooru_atomizer/retry.py
Normal file
17
danbooru_atomizer/retry.py
Normal 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
124
danbooru_atomizer/run.py
Executable 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
44
danbooru_scrape/boorufind
Executable 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
40
danbooru_scrape/boorugrab
Executable 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
|
26
debug_saves/use-debug-saves.md
Normal file
26
debug_saves/use-debug-saves.md
Normal 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
117
desmos/desmos.md
Normal 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.
|
82
dictionary_attack/attack.py
Normal file
82
dictionary_attack/attack.py
Normal 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
|
4
dnm/0017 V00744020.xxd.patch
Normal file
4
dnm/0017 V00744020.xxd.patch
Normal 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$...
|
10
dnm/0023 V007492E0.xxd.patch
Normal file
10
dnm/0023 V007492E0.xxd.patch
Normal 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
10
dnm/cmd.sh
Normal 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
238
ds1/extract_params.py
Normal 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
115
ds1/fmg_flatten.py
Normal 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
27
ds1/param_notes.py
Normal 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
22
dwarf_font/dwarf.lua
Normal 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
BIN
dwarf_font/dwarf.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
25
explicit_globals/corepatchlua.lua
Normal file
25
explicit_globals/corepatchlua.lua
Normal 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
|
239
filter_tutorial/filter_example.py
Normal file
239
filter_tutorial/filter_example.py
Normal 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
32
gs/gs.lua
Normal 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
76
gs/test.asm
Normal 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
136
image_deduplication/idup.py
Normal 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
314
kill_reboot/elevate.ps1
Normal 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
|
32
kill_reboot/kill-reboot.cmd
Normal file
32
kill_reboot/kill-reboot.cmd
Normal 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
113
kyaa/kyaa.h
Normal 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
160
kyaa/kyaa.md
Normal 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
211
kyaa/kyaa_extra.h
Normal 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
165
love_plotting/main.lua
Normal 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
83
lsca/lsca.uni.py
Normal 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
172
lsf/lsf.sh
Executable 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
328
mario_tennis/tennis.c
Normal 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
11
mips_disassembler/Makefile
Executable 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
1634
mips_disassembler/adis.c
Executable file
File diff suppressed because it is too large
Load diff
10
music_sync/convert.py
Normal file
10
music_sync/convert.py
Normal 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
117
music_sync/mutaext.py
Normal 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
172
music_sync/unsync.py
Executable 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
139
n64_models/pot3d.py
Normal 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))
|
98
polyphase_halfband/halfband.c
Normal file
98
polyphase_halfband/halfband.c
Normal 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
246
print_tables/_G.yml
Normal 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
69
print_tables/extra.lua
Executable 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
2
print_tables/init.lua
Normal file
|
@ -0,0 +1,2 @@
|
|||
local path = string.gsub(..., "[^.]+$", "")
|
||||
return require(path.."pt")
|
203
print_tables/pt.lua
Executable file
203
print_tables/pt.lua
Executable 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
41
print_tables/run.lua
Executable 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
478
psnip_clock/clock.h
Normal 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) */
|
BIN
resnet/resnet-1470729826.pkl
Normal file
BIN
resnet/resnet-1470729826.pkl
Normal file
Binary file not shown.
184
resnet/resnet.py
Executable file
184
resnet/resnet.py
Executable 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
26
response/response.txt
Normal 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(ω0∕2)²
|
||||
|
||||
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
121
rng64/rng.md
Normal 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.
|
171
speedrun_comparison/spddiff.avs
Normal file
171
speedrun_comparison/spddiff.avs
Normal 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
34
starcraft_cdkey/sckey.c
Normal 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
507
starcraft_maps/bwmap.py
Normal 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
156
string_tensions/data.py
Executable 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
50
string_tensions/notes.py
Executable 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
81
string_tensions/run.py
Executable 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
84
string_tensions/run2.py
Executable 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
117
string_tensions/strings.py
Executable 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
2170
thps1/names.txt
Normal file
File diff suppressed because it is too large
Load diff
168
thps1/thps1.c
Normal file
168
thps1/thps1.c
Normal 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
60
tiny_crc32/crc32.c
Normal 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
139
trackmogrify/colors.txt
Normal 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
161
trackmogrify/modifiers.txt
Normal 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
|
61
twitch_following/following
Normal file
61
twitch_following/following
Normal 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
81
warcraft_hash/wc3hash.c
Normal 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;
|
||||
}
|
207
warcraft_hashes/wc3hashes.md
Normal file
207
warcraft_hashes/wc3hashes.md
Normal 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
|
||||
```
|
Loading…
Add table
Reference in a new issue