Compare commits
24 commits
Author | SHA1 | Date | |
---|---|---|---|
d261b299db | |||
52956fd397 | |||
83b73c0930 | |||
f94f5382de | |||
bddb5c32ea | |||
8ce28c4bf4 | |||
6e1544f860 | |||
df441b445c | |||
9f94bd2f45 | |||
b3d8d55997 | |||
7188ba47c5 | |||
7739895224 | |||
3aaee2c09f | |||
61188f00da | |||
f7e3cfceb1 | |||
15d053789e | |||
e02bca097c | |||
b61ecf090a | |||
b028ee53d9 | |||
e61a32c615 | |||
eeb5d2941e | |||
28edd29072 | |||
db3171ac29 | |||
14af124e09 |
15 changed files with 1776 additions and 150 deletions
42
README.md
42
README.md
|
@ -44,11 +44,6 @@ i recently repurposed this script
|
||||||
to render characters to bitmaps for use as textures in games,
|
to render characters to bitmaps for use as textures in games,
|
||||||
but i haven't uploaded that version yet.
|
but i haven't uploaded that version yet.
|
||||||
|
|
||||||
### coin\_or\_build
|
|
||||||
|
|
||||||
a shell script that builds Bonmin and its many, *many* dependencies
|
|
||||||
on other coin\_or projects.
|
|
||||||
|
|
||||||
### danbooru\_atomizer
|
### danbooru\_atomizer
|
||||||
|
|
||||||
a Python script that scrapes atom feeds on [danbooru (nsfw)](https://danbooru.donmai.us/)
|
a Python script that scrapes atom feeds on [danbooru (nsfw)](https://danbooru.donmai.us/)
|
||||||
|
@ -62,14 +57,6 @@ for a given search query. requires zsh and xml2.
|
||||||
i don't think this version works anymore,
|
i don't think this version works anymore,
|
||||||
so i'll have to update it with my local changes sometime.
|
so i'll have to update it with my local changes sometime.
|
||||||
|
|
||||||
### debug\_saves
|
|
||||||
|
|
||||||
for Majora's Mask:
|
|
||||||
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.
|
|
||||||
|
|
||||||
### desmos
|
### desmos
|
||||||
|
|
||||||
[stuff i've plotted in desmos.](/desmos/desmos.md)
|
[stuff i've plotted in desmos.](/desmos/desmos.md)
|
||||||
|
@ -115,6 +102,10 @@ with all the appropriate character mappings.
|
||||||
|
|
||||||
the code is Unlicensed, and i believe the font itself is public domain.
|
the code is Unlicensed, and i believe the font itself is public domain.
|
||||||
|
|
||||||
|
### explicit\_globals
|
||||||
|
|
||||||
|
hmm, this probably shouldn't be here...
|
||||||
|
|
||||||
### filter\_tutorial
|
### filter\_tutorial
|
||||||
|
|
||||||
a single Python file that walks you through
|
a single Python file that walks you through
|
||||||
|
@ -138,7 +129,11 @@ the batch script exploits [a UAC bypass found by Tyranid.](https://tyranidslair.
|
||||||
|
|
||||||
### kyaa
|
### kyaa
|
||||||
|
|
||||||
[*moved to its own repo*](/git/notwa/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
|
### lsca
|
||||||
|
|
||||||
|
@ -188,10 +183,6 @@ and even runs out of memory if too many files need updating (thanks mutagenx).
|
||||||
a hastily-written Python script for outlining the F3DEX model format
|
a hastily-written Python script for outlining the F3DEX model format
|
||||||
used in some N64 games.
|
used in some N64 games.
|
||||||
|
|
||||||
### phasmophobia
|
|
||||||
|
|
||||||
an AutoHotKey script for quickly adding and removing equipment to games.
|
|
||||||
|
|
||||||
### polyphase\_halfband
|
### polyphase\_halfband
|
||||||
|
|
||||||
a C port (from C++) of the polyphase halfband filter coefficient generator
|
a C port (from C++) of the polyphase halfband filter coefficient generator
|
||||||
|
@ -210,14 +201,15 @@ since the resulting format cannot be deserialized,
|
||||||
this is primarily intended for debugging purposes,
|
this is primarily intended for debugging purposes,
|
||||||
and i'd like to think it excels at that.
|
and i'd like to think it excels at that.
|
||||||
|
|
||||||
### response
|
### psnip\_clock
|
||||||
|
|
||||||
some old, brief notes on biquads.
|
a fork of the clock utility header file
|
||||||
|
from [Portable Snippets](https://github.com/nemequ/portable-snippets)
|
||||||
### rng64
|
by Evan Nemerson.
|
||||||
|
this fork removes the dependency on `windows.h` for the Windows backend
|
||||||
some notes on the random number generator that Ocarina of Time
|
by using some gross hacks,
|
||||||
and Majora's Mask use. don't get your hopes up about the manipulation stuff.
|
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
|
### speedrun\_comparison
|
||||||
|
|
||||||
|
|
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('_')]
|
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
|
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
|
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; \
|
||||||
|
} \
|
||||||
|
|
125
phasmo.ahk
125
phasmo.ahk
|
@ -1,125 +0,0 @@
|
||||||
#SingleInstance Force
|
|
||||||
#IfWinActive ahk_exe Phasmophobia.exe
|
|
||||||
Return
|
|
||||||
|
|
||||||
; 1 = EMF Reader
|
|
||||||
; 2 = Flashlight
|
|
||||||
; 3 = Photo Camera
|
|
||||||
; 4 = Lighter
|
|
||||||
; 5 = Candle
|
|
||||||
; 6 = UV Light
|
|
||||||
; 7 = Crucifix
|
|
||||||
; 8 = Video Camera
|
|
||||||
; 9 = Spirit Box
|
|
||||||
; 10 = Salt
|
|
||||||
; 11 = Smudge Sticks
|
|
||||||
; 12 = Tripod
|
|
||||||
; 13 = Strong Flashlight
|
|
||||||
; 14 = Motion Sensor
|
|
||||||
; 15 = Sound Sensor
|
|
||||||
; 16 = Thermometer
|
|
||||||
; 17 = Sanity Pills
|
|
||||||
; 18 = Ghost Writing Book
|
|
||||||
; 19 = Infrared Light Sensor
|
|
||||||
; 20 = Parabolic Microphone
|
|
||||||
; 21 = Glowstick
|
|
||||||
; 22 = Head Mounted Camera
|
|
||||||
|
|
||||||
*,::
|
|
||||||
; add just the stuff you need.
|
|
||||||
Prologue("ahk_class UnityWndClass", mouseX, mouseY)
|
|
||||||
PhasmoAdd(2) ; Flashlight
|
|
||||||
PhasmoAdd(3) ; Photo Camera
|
|
||||||
PhasmoAdd(4) ; Lighter
|
|
||||||
PhasmoAdd(7) ; Crucifix
|
|
||||||
PhasmoAdd(8) ; Video Camera
|
|
||||||
PhasmoAdd(10) ; Salt
|
|
||||||
PhasmoAdd(11) ; Smudge Sticks
|
|
||||||
PhasmoAdd(12,2) ; Tripod (x2)
|
|
||||||
PhasmoAdd(13,2) ; Strong Flashlight (x2)
|
|
||||||
PhasmoAdd(14) ; Motion Sensor
|
|
||||||
PhasmoAdd(16) ; Thermometer
|
|
||||||
PhasmoAdd(17,4) ; Sanity Pills (x4)
|
|
||||||
PhasmoAdd(21) ; Glowstick
|
|
||||||
PhasmoAdd(22) ; Head Mounted Camera
|
|
||||||
Epilogue(mouseX, mouseY)
|
|
||||||
Return
|
|
||||||
|
|
||||||
*;::
|
|
||||||
; add one of everything.
|
|
||||||
Prologue("ahk_class UnityWndClass", mouseX, mouseY)
|
|
||||||
Loop 22
|
|
||||||
PhasmoAdd(A_Index)
|
|
||||||
Epilogue(mouseX, mouseY)
|
|
||||||
Return
|
|
||||||
|
|
||||||
*'::
|
|
||||||
; remove one of everything.
|
|
||||||
Prologue("ahk_class UnityWndClass", mouseX, mouseY)
|
|
||||||
Loop 22
|
|
||||||
PhasmoRemove(A_Index)
|
|
||||||
Epilogue(mouseX, mouseY)
|
|
||||||
Return
|
|
||||||
|
|
||||||
; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
|
|
||||||
; internal stuff from hereon! ;
|
|
||||||
; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
|
|
||||||
|
|
||||||
GetClientSize(hwnd, ByRef w, ByRef h)
|
|
||||||
{
|
|
||||||
VarSetCapacity(rc, 16)
|
|
||||||
DllCall("GetClientRect", "uint", hwnd, "uint", &rc)
|
|
||||||
w := NumGet(rc, 8, "int")
|
|
||||||
h := NumGet(rc, 12, "int")
|
|
||||||
}
|
|
||||||
|
|
||||||
Prologue(hint, ByRef mouseX, ByRef mouseY)
|
|
||||||
{
|
|
||||||
global winW, winH
|
|
||||||
hwnd := WinExist(hint)
|
|
||||||
GetClientSize(hwnd, winW, winH)
|
|
||||||
SetDefaultMouseSpeed 0
|
|
||||||
SetMouseDelay 0
|
|
||||||
CoordMode Mouse, Screen
|
|
||||||
MouseGetPos mouseX, mouseY ; store mouse position to restore later
|
|
||||||
CoordMode Mouse, Client
|
|
||||||
}
|
|
||||||
|
|
||||||
Epilogue(mouseX, mouseY)
|
|
||||||
{
|
|
||||||
CoordMode Mouse, Screen
|
|
||||||
MouseMove mouseX, mouseY ; restore original mouse position
|
|
||||||
Sleep 100 ; prevent accidental repetition of macro
|
|
||||||
}
|
|
||||||
|
|
||||||
PhasmoClick(relX, relY, n:=1)
|
|
||||||
{
|
|
||||||
global winW, winH
|
|
||||||
fakeW := 1.6 * winH
|
|
||||||
off := (winW - fakeW) / 2
|
|
||||||
MouseMove % off + fakeW * relX, winH * relY
|
|
||||||
Sleep 10
|
|
||||||
Click
|
|
||||||
Loop % n - 1
|
|
||||||
{
|
|
||||||
Sleep 60
|
|
||||||
Click
|
|
||||||
}
|
|
||||||
Sleep 10
|
|
||||||
}
|
|
||||||
|
|
||||||
PhasmoAdd(id, n:=1)
|
|
||||||
{
|
|
||||||
If id between 1 and 15
|
|
||||||
PhasmoClick(0.41, (id + 11.5) / 37.1, n)
|
|
||||||
If id between 16 and 22
|
|
||||||
PhasmoClick(0.72, (id - 3.5) / 37.1, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
PhasmoRemove(id, n:=1)
|
|
||||||
{
|
|
||||||
If id between 1 and 15
|
|
||||||
PhasmoClick(0.435, (id + 11.5) / 37.1, n)
|
|
||||||
If id between 16 and 22
|
|
||||||
PhasmoClick(0.745, (id - 3.5) / 37.1, n)
|
|
||||||
}
|
|
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()))
|
Loading…
Add table
Reference in a new issue