initial commit

This commit is contained in:
minisbett 2021-04-23 13:31:43 +02:00
commit 491faab301
46 changed files with 20338 additions and 0 deletions

23
.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
tags
*.log
*.tar.xz
*.tar.gz
*.zip
/oppai
*.json
*.obj
*.exe
*.swp
*.so
*.dll
*.lib
*.exp
/test/test_suite
/test/oppai_test
/swig/**/*.c
/swig/*/*.i
/swig/python/oppai.egg-info
/swig/python/build
/swig/python/oppai.py
*.whl
*.pyc

29
.travis.yml Normal file
View File

@ -0,0 +1,29 @@
language: c
matrix:
include:
- os: linux
dist: precise
- os: linux
dist: trusty
- os: osx
install: true
cache:
directories:
- test/test_suite
script:
- ./build
- ./libbuild
- cd test
- ./download_suite
- ./build
- DYLD_PRINT_LIBRARIES=1 DYLD_PRINT_LIBRARIES_POST_LAUNCH=1
DYLD_PRINT_RPATHS=1 LD_DEBUG=libs ./oppai_test
- wc -c ./oppai_test
- LDFLAGS="-loppai" ./build -L..
- LD_LIBRARY_PATH=.. DYLD_LIBRARY_PATH=..
DYLD_PRINT_LIBRARIES=1 DYLD_PRINT_LIBRARIES_POST_LAUNCH=1
DYLD_PRINT_RPATHS=1 LD_DEBUG=libs ./oppai_test
- wc -c ./oppai_test

6
Dockerfile Normal file
View File

@ -0,0 +1,6 @@
ARG PREFIX=
FROM ${PREFIX}ubuntu:bionic
RUN apt-get update && apt-get install -y \
gcc musl musl-tools musl-dev git-core file
WORKDIR /tmp
CMD setarch $arch ./release

67
appveyor.yml Normal file
View File

@ -0,0 +1,67 @@
version: b{build}
image: Visual Studio 2017
cache: test/test_suite -> test/suite_url
build_script:
- ps: >-
function VcVars {
param ([string]$VcPath, [string]$BatName)
Push-Location $VcPath
cmd /c ($BatName + "&set") |
ForEach-Object {
if ($_ -match "=") {
$v = $_.split("="); set-item -force -path "ENV:\$($v[0])" -value "$($v[1])"
}
}
Pop-Location
}
$VcPath = "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build"
VcVars $VcPath vcvars32.bat
.\release.ps1; if (-not $?) { exit $LastExitCode }
VcVars $VcPath vcvars64.bat
.\release.ps1; if (-not $?) { exit $LastExitCode }
cd .\test
.\download_suite.ps1
.\build.bat; if (-not $?) { exit $LastExitCode }
.\oppai_test.exe; if (-not $?) { exit $LastExitCode }
Write-Host "bin size: " + (Get-Item '.\oppai_test.exe').length
dumpbin /dependents .\oppai_test.exe
.\build.bat ..\oppai.lib; if (-not $?) { exit $LastExitCode }
cp ..\oppai.dll .
.\oppai_test.exe; if (-not $?) { exit $LastExitCode }
Write-Host "bin size: " + (Get-Item '.\oppai_test.exe').length
dumpbin /dependents .\oppai_test.exe
test: off
artifacts:
- path: oppai-*-windows-*.zip
name: windows-binaries
deploy:
- provider: GitHub
tag: $(appveyor_repo_tag_name)
release: oppai $(appveyor_repo_tag_name)-$(appveyor_build_version)
description: linux binaries are manually uploaded shortly after the windows release and statically linked against musl libc\n\nwindows binaries should not require the c runtime\n\nx64 and x86_64 mean 64-bit i586 and x86 mean 32-bit\n\nthe binary packages include the source code inside the src directory
auth_token:
secure: k73tV2NZTFp4thujp/KiohNwRwIpWC12gU/qsnfCqlctcC+rqWiDWet3sSAz34gT
artifact: windows-binaries
force_update: true
on:
APPVEYOR_REPO_TAG: true

6
build Normal file
View File

@ -0,0 +1,6 @@
#!/bin/sh
dir="$(dirname "$0")"
. "$dir"/cflags
$cc $cflags "$@" -DOPPAI_IMPLEMENTATION main.c oppai.c $ldflags -o oppai

12
build.bat Normal file
View File

@ -0,0 +1,12 @@
@echo off
del oppai.exe >nul 2>&1
del main.obj >nul 2>&1
cl -D_CRT_SECURE_NO_WARNINGS=1 ^
-DNOMINMAX=1 ^
-O2 -nologo -MT -Gm- -GR- -EHsc -W4 ^
-DOPPAI_IMPLEMENTATION ^
-DOPPAI_STATIC_HEADER ^
main.c oppai.c ^
-Feoppai.exe ^
|| EXIT /B 1

45
cflags Normal file
View File

@ -0,0 +1,45 @@
#!/bin/sh
cflags="-std=c89 -pedantic"
cflags="$cflags -Os"
cflags="$cflags -fno-strict-aliasing"
cflags="$cflags -Wall"
cflags="$cflags -ffunction-sections -fdata-sections"
if [ -z $DBGINFO ]; then
cflags="$cflags -g0 -fno-unwind-tables -s"
cflags="$cflags -fno-asynchronous-unwind-tables"
cflags="$cflags -fno-stack-protector"
else
cflags="$cflags -g -fsanitize=address -fsanitize=leak "
cflags="$cflags -fsanitize=signed-integer-overflow -fsanitize=undefined -static-libasan"
fi
if [ $(uname) = "Darwin" ]; then
cflags="$cflags -Wl,-dead_strip"
else
cflags="$cflags -Wl,--gc-sections,--build-id=none"
fi
ldflags="-lm"
cflags="$cflags $CFLAGS"
ldflags="$ldflags $LDFLAGS"
cc="$CC"
if [ $(uname) = "Darwin" ]; then
cc=${cc:-clang}
else
cc=${cc:-gcc}
fi
uname -a > flags.log
echo $cc >> flags.log
echo $cflags >> flags.log
echo $ldflags >> flags.log
$cc --version >> flags.log
$cc -dumpmachine >> flags.log
export cflags="$cflags"
export ldflags="$ldflags"
export cc="$cc"

26
examples/FFI.cs Normal file
View File

@ -0,0 +1,26 @@
// csc FFI.cs
// FFI.exe /path/to/file.osu
// make sure oppai.dll is in the same directory as FFI.exe
// see oppai.c for a full list of functions
using System;
using System.Runtime.InteropServices;
public class Program
{
[DllImport(@"oppai.dll")]
public static extern IntPtr ezpp_new();
[DllImport(@"oppai.dll")]
public static extern IntPtr ezpp(IntPtr ez, char[] map);
[DllImport(@"oppai.dll")]
public static extern float ezpp_pp(IntPtr ez);
static void Main(string[] args)
{
IntPtr ez = ezpp_new();
ezpp(ez, args[0].ToCharArray());
Console.WriteLine($"{ezpp_pp(ez)} pp");
}
}

133
examples/binary.c Normal file
View File

@ -0,0 +1,133 @@
/*
* example of parsing oppai's binary output
*
* gcc binary.c
* oppai /path/to/file.osu -obinary | ./a.out
*/
#include <stdio.h>
#include <string.h>
/*
* these are only necessary to ensure endian-ness, if you don't
* care about that you can read values like v = *(int*)p
*/
int read2(char** c) {
unsigned char* p = (unsigned char*)*c;
*c += 2;
return p[0] | (p[1] << 8);
}
int read4(char** c) {
unsigned char* p = (unsigned char*)*c;
*c += 4;
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
}
float read_flt(char** p) {
int v = read4(p);
float* pf = (float*)&v;
return *pf;
}
char* read_str(char** p, int* len) {
char* res;
*len = read2(p);
res = *p;
*p += *len + 1;
return res;
}
#define MODS_NF (1<<0)
#define MODS_EZ (1<<1)
#define MODS_HD (1<<3)
#define MODS_HR (1<<4)
#define MODS_DT (1<<6)
#define MODS_HT (1<<8)
#define MODS_NC (1<<9)
#define MODS_FL (1<<10)
#define MODS_SO (1<<12)
int main() {
char buf[8192];
char* p = buf;
int len;
int result;
int mods;
memset(buf, 0, sizeof(buf));
/* read stdin in binary mode */
if (!freopen(0, "rb", stdin)) {
perror("freopen");
return 1;
}
if (!fread(buf, 1, sizeof(buf), stdin)) {
perror("fread");
return 1;
}
if (strncmp((char const*)p, "binoppai", 8)) {
puts("invalid input");
return 1;
}
p += 8;
printf("oppai %d.%d.%d\n", p[0], p[1], p[2]);
p += 3;
puts("");
/* error code */
result = read4(&p);
if (result < 0) {
printf("error %d\n", result);
return 1;
}
printf("artist: %s\n", read_str(&p, &len));
printf("artist_unicode: %s\n", read_str(&p, &len));
printf("title: %s\n", read_str(&p, &len));
printf("title_unicode: %s\n", read_str(&p, &len));
printf("version: %s\n", read_str(&p, &len));
printf("creator: %s\n", read_str(&p, &len));
mods = read4(&p);
puts("");
printf("mods: ");
if (mods & MODS_NF) printf("NF");
if (mods & MODS_EZ) printf("EZ");
if (mods & MODS_HD) printf("HD");
if (mods & MODS_HR) printf("HR");
if (mods & MODS_DT) printf("DT");
if (mods & MODS_HT) printf("HT");
if (mods & MODS_NC) printf("NC");
if (mods & MODS_FL) printf("FL");
if (mods & MODS_SO) printf("SO");
puts("");
printf("OD%g ", read_flt(&p));
printf("AR%g ", read_flt(&p));
printf("CS%g ", read_flt(&p));
printf("HP%g\n", read_flt(&p));
printf("%d/%dx\n", read4(&p), read4(&p));
printf("%d circles ", read2(&p));
printf("%d sliders ", read2(&p));
printf("%d spinners\n", read2(&p));
printf("scorev%d\n", read4(&p));
puts("");
printf("%g stars ", read_flt(&p));
printf("(%g speed, ", read_flt(&p));
printf("%g aim)\n", read_flt(&p));
read2(&p); /* legacy */
read2(&p); /* legacy */
puts("");
printf("%g aim pp\n", read_flt(&p));
printf("%g speed pp\n", read_flt(&p));
printf("%g acc pp\n", read_flt(&p));
puts("");
printf("%g pp\n", read_flt(&p));
return 0;
}

16
examples/min.c Normal file
View File

@ -0,0 +1,16 @@
/*
* gcc min.c -lm
* cat /path/to/file.osu | ./a.out
*/
#define OPPAI_IMPLEMENTATION
#include "../oppai.c"
int main() {
ezpp_t ez = ezpp_new();
ezpp_set_mods(ez, MODS_HD | MODS_DT);
ezpp(ez, "-");
printf("%gpp\n", ezpp_pp(ez));
return 0;
}

61
examples/reuse.c Normal file
View File

@ -0,0 +1,61 @@
/*
* gcc reuse.c -lm
* ./a.out /path/to/file.osu
*/
#define OPPAI_IMPLEMENTATION
#include "../oppai.c"
/*
* for better performance, the same instance can be reused
* settings are remembered and map is only reparsed if mods or cs change
*/
int main(int argc, char* argv[]) {
ezpp_t ez = ezpp_new();
ezpp_set_autocalc(ez, 1); /* autorecalc pp when changing any parameter */
ezpp(ez, argv[1]);
puts("---");
puts("nomod fc");
printf("%gpp\n", ezpp_pp(ez));
puts("---");
puts("nomod 95% fc");
ezpp_set_accuracy_percent(ez, 95);
printf("%gpp\n", ezpp_pp(ez));
puts("---");
puts("nomod 1x100 fc");
ezpp_set_accuracy(ez, 1, 0);
printf("%gpp\n", ezpp_pp(ez));
puts("---");
puts("HD 1x100 1miss 300x");
ezpp_set_mods(ez, MODS_HD);
ezpp_set_nmiss(ez, 1);
ezpp_set_combo(ez, 300);
printf("%gpp\n", ezpp_pp(ez));
puts("---");
puts("HDDT 1x100 1xmiss 300x");
ezpp_set_mods(ez, MODS_HD | MODS_DT);
printf("%gpp\n", ezpp_pp(ez));
puts("---");
puts("HDDT 1x100 1xmiss 300x ends at object 300");
ezpp_set_end(ez, 300);
printf("%gpp\n", ezpp_pp(ez));
puts("---");
puts("HDDT fc");
ezpp_set_end(ez, 0);
ezpp_set_combo(ez, -1);
ezpp_set_accuracy(ez, 0, 0);
ezpp_set_nmiss(ez, 0);
printf("%gpp\n", ezpp_pp(ez));
puts("---");
ezpp_free(ez);
return 0;
}

42
examples/reuse_mem.c Normal file
View File

@ -0,0 +1,42 @@
#define OPPAI_IMPLEMENTATION
#include "oppai.c"
char buf[1000000];
int mods[] = { 0, MODS_HR, MODS_HD | MODS_HR, MODS_DT, MODS_HD | MODS_DT };
#define N_MODS (sizeof(mods) / sizeof(mods[0]))
void print_mods(int mods) {
putchar('+');
if (!mods) puts("nomod");
else {
if (mods & MODS_HD) printf("hd");
if (mods & MODS_HR) printf("hr");
if (mods & MODS_DT) printf("dt");
puts("");
}
}
int main(int argc, char* argv[]) {
int i, j, n, acc;
ezpp_t ez = ezpp_new();
ezpp_set_autocalc(ez, 1);
for (i = 1; i < argc; ++i) {
FILE* f = fopen(argv[i], "r");
n = fread(buf, 1, sizeof(buf), f);
fclose(f);
ezpp_data(ez, buf, n);
printf("%s - %s [%s]\n", ezpp_artist(ez), ezpp_title(ez),
ezpp_version(ez));
for (j = 0; j < N_MODS; ++j) {
print_mods(mods[j]);
ezpp_set_mods(ez, mods[j]);
for (acc = 95; acc <= 100; ++acc) {
ezpp_set_accuracy_percent(ez, acc);
printf("%d%% -> %gpp\n", acc, ezpp_pp(ez));
}
}
puts("");
}
ezpp_free(ez);
return 0;
}

37
libbuild Normal file
View File

@ -0,0 +1,37 @@
#!/bin/sh
dir="$(dirname "$0")"
. "$dir"/cflags
tmp=$(mktemp -d)
hide_unnecessary_symbols() {
[ ! -d "$tmp" ] && echo "W: couldn't find tmp dir" && return
gcc_syms="$tmp/gcc_exports.sym"
clang_syms="$tmp/clang_exports.list"
code="$tmp/main.c"
echo "int main() { return 0; }" >> "$code"
exports='ezpp ezpp_ errstr oppai_'
( printf '{global:'
for e in $exports; do
printf '%s;' "$e"
done
printf 'local:*;};' ) | sed 's/_;/_*;/g' >"$gcc_syms"
echo "$exports" | tr ' ' '\n' | sed s/_$/_*/g > "$clang_syms"
for flags in "-Wl,--version-script=$gcc_syms" \
"-Wl,-exported_symbols_list,$clang_syms"
do
if "$cc" $flags "$code" -o /dev/null >/dev/null 2>&1; then
ldflags="$ldflags $flags"
return
fi
done
echo "W: can't figure out how to hide unnecessary symbols"
}
hide_unnecessary_symbols
$cc -shared $cflags -DOPPAI_EXPORT \
"$@" oppai.c $ldflags -fpic -o liboppai.so
[ -d "$tmp" ] && rm -rf "$tmp"

14
libbuild.bat Normal file
View File

@ -0,0 +1,14 @@
@echo off
del oppai.dll >nul 2>&1
del oppai.obj >nul 2>&1
del oppai.exp >nul 2>&1
echo compiling
cl ^
/c /O2 /nologo /Gm- /GR- /EHsc /W4 /Gz ^
/D_CRT_SECURE_NO_WARNINGS=1 /DNOMINMAX=1 ^
/DOPPAI_EXPORT /D_WINDLL /D_USRDLL ^
oppai.c ^
|| EXIT /B 1
echo making dll
link /OUT:oppai.dll /IMPLIB:oppai.lib /NOLOGO /DLL oppai.obj

966
main.c Normal file
View File

@ -0,0 +1,966 @@
/*
* this is free and unencumbered software released into the
* public domain.
*
* refer to the attached UNLICENSE or http://unlicense.org/
* ----------------------------------------------------------------
* command line interface for oppai
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#undef OPPAI_EXPORT
#undef OPPAI_IMPLEMENTATION
#include "oppai.c"
char* me = "oppai";
#define al_round(x) (float)floor((x) + 0.5f)
#define al_min(a, b) ((a) < (b) ? (a) : (b))
#define al_max(a, b) ((a) > (b) ? (a) : (b))
#define twodec(x) (al_round((x) * 100.0f) / 100.0f)
#define array_len(x) (sizeof(x) / sizeof((x)[0]))
static
float get_inf() {
static unsigned raw = 0x7F800000;
float* p = (float*)&raw;
return *p;
}
static
int is_nan(float b) {
int* p = (int*)&b;
return (
(*p > 0x7F800000 && *p < 0x80000000) ||
(*p > 0x7FBFFFFF && *p <= 0xFFFFFFFF)
);
}
static
int info(char* fmt, ...) {
int res;
va_list va;
va_start(va, fmt);
res = vfprintf(stderr, fmt, va);
va_end(va);
return res;
}
void usage() {
/* logo by flesnuk https://github.com/Francesco149/oppai-ng/issues/10 */
info(
" /\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb"
"\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb/ /\xe2\x8e\xbb\xe2"
"\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb"
"\xe2\x8e\xbb/ /\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb\xe2"
"\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb/ /\xe2\x8e"
"\xbb\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb\xe2"
"\x8e\xbb\xe2\x8e\xbb/ /\xe2\x8e\xbb/ /\xe2\x8e\xbb"
"\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb\\ /\xe2\x8e\xbb"
"\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e"
"\xbb\xe2\x8e\xbb/\n / /\xe2\x8e\xbb\xe2\x8e\xbb\xe2"
"\x8e\xbb/ / / /\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb/ / "
);
info(
"/ /\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb/ / \xe2\x8e\xbb"
"\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb/ / / / "
"___ / /\xe2\x8e\xbb\xe2\x8e\xbb\\ \\ / /\xe2\x8e\xbb"
"\xe2\x8e\xbb\xe2\x8e\xbb/ /\n / / / / / / / / / /"
" / / /\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb\xe2\x8e\xbb"
"\xe2\x8e\xbb/ / / / /__/ / / / / / / / /\n / /___/ "
"/ / /___/ / / /___/ / / /___/ / / / / / / / / /__"
"_/ /\n /_______/ / ______/ / ______/ /_______/ /_/ "
"/_/ /_/ /_____ /\n / / / / "
" / / \n / / /"
" / /\xe2\x8e\xbb/___/"
" / \n /_/ /_/ "
" /_______/"
);
info("\n\n");
info("usage: %s /path/to/file.osu parameters\n\n", me);
info(
"set filename to '-' to read from standard input\n"
"all parameters are case insensitive\n"
"\n"
"-o[output_module]\n"
" output module. pass ? to list modules (oppai - -o?)\n"
" default: text\n"
" example: -ojson\n"
"\n"
"[accuracy]%%\n"
" accuracy percentage\n"
" default: 100%%\n"
" example: 95%%\n"
"\n"
"[n]x100\n"
" amount of 100s\n"
" default: 0\n"
" example: 2x100\n"
"\n"
);
info(
"[n]x50\n"
" amount of 50s\n"
" default: 0\n"
" example: 2x50\n"
"\n"
"[n]xm\n"
"[n]xmiss\n"
"[n]m\n"
" amount of misses\n"
" default: 0\n"
" example: 1m\n"
"\n"
"[combo]x\n"
" highest combo achieved\n"
" default: full combo (calculated from map data)\n"
" example: 500x\n"
"\n"
"scorev[n]\n"
" scoring system\n"
" default: 1\n"
" example: scorev2\n"
"\n"
);
info(
"ar[n]\n"
" base approach rate override\n"
" default: map's base approach rate\n"
" example: AR5\n"
"\n"
"od[n]\n"
" base overall difficulty override\n"
" default: map's base overall difficulty\n"
" example: OD10\n"
"\n"
"cs[n]\n"
" base circle size override\n"
" default: map's base circle size\n"
" example: CS6.5\n"
"\n"
);
info(
"-m[n]\n"
" gamemode id override for converted maps\n"
" default: uses the map's gamemode\n"
" example: -m1\n"
"\n"
"-taiko\n"
" forces gamemode to taiko for converted maps\n"
" default: disabled\n"
"\n"
"-touch\n"
" calculates pp for touchscreen / touch devices. can \n"
" also be specified as mod TD\n"
"\n"
"[n]speed\n"
" override speed stars. "
"useful for maps with incorrect star rating\n"
" default: uses computed speed stars\n"
" example: 3.5speed\n"
"\n"
);
info(
"[n]aim\n"
" override aim stars. "
"useful for maps with incorrect star rating\n"
" default: uses computed aim stars\n"
" example: 2.4aim\n"
"\n"
"-end[n]\n"
" cuts map to a certain number of objects\n"
);
}
#define output_sig(name) void name(int result, ezpp_t ez, char* mods_str)
typedef output_sig(fnoutput);
/* null output --------------------------------------------------------- */
/* stdout must be left alone, outputting to stderr is fine tho */
output_sig(output_null) { (void)result; (void)ez; (void)mods_str; }
/* text output --------------------------------------------------------- */
#define ASCIIPLT_W 51
void asciiplt(float (* getvalue)(void* data, int i), int n, void* data) {
static char* charset[] = {
#ifdef OPPAI_UTF8GRAPH
"\xe2\x96\x81",
"\xe2\x96\x82",
"\xe2\x96\x83",
"\xe2\x96\x84",
"\xe2\x96\x85",
"\xe2\x96\x86",
"\xe2\x96\x87",
"\xe2\x96\x88"
#else
" ", "_", ".", "-", "^"
#endif
};
static int charsetsize = array_len(charset);
float values[ASCIIPLT_W];
float minval = (float)get_inf();
float maxval = (float)-get_inf();
float range;
int i;
int chunksize;
int w = al_min(ASCIIPLT_W, n);
memset(values, 0, sizeof(values));
chunksize = (int)ceil((float)n / w);
for (i = 0; i < n; ++i) {
int chunki = i / chunksize;
values[chunki] = al_max(values[chunki], getvalue(data, i));
}
for (i = 0; i < n; ++i) {
int chunki = i / chunksize;
maxval = al_max(maxval, values[chunki]);
minval = al_min(minval, values[chunki]);
}
range = al_max(0.00001f, maxval - minval);
for (i = 0; i < w; ++i) {
int chari = (int)(((values[i] - minval) / range) * charsetsize);
chari = al_max(0, al_min(chari, charsetsize - 1));
printf("%s", charset[chari]);
}
puts("");
}
float getaim(void* data, int i) {
ezpp_t ez = data;
return ezpp_strain_at(ez, i, DIFF_AIM);
}
float getspeed(void* data, int i) {
ezpp_t ez = data;
return ezpp_strain_at(ez, i, DIFF_SPEED);
}
output_sig(output_text) {
float ar, od, cs, hp, stars, aim_stars, speed_stars, accuracy_percent;
float pp, aim_pp, speed_pp, acc_pp;
if (result < 0) {
puts(errstr(result));
return;
}
printf("%s - %s ", ezpp_artist(ez), ezpp_title(ez));
if (strcmp(ezpp_artist(ez), ezpp_artist_unicode(ez)) ||
strcmp(ezpp_title(ez), ezpp_title_unicode(ez)))
{
printf("(%s - %s) ", ezpp_artist_unicode(ez), ezpp_title_unicode(ez));
}
printf("[%s] mapped by %s ", ezpp_version(ez), ezpp_creator(ez));
puts("\n");
ar = twodec(ezpp_ar(ez));
od = twodec(ezpp_od(ez));
cs = twodec(ezpp_cs(ez));
hp = twodec(ezpp_hp(ez));
stars = twodec(ezpp_stars(ez));
aim_stars = twodec(ezpp_aim_stars(ez));
speed_stars = twodec(ezpp_speed_stars(ez));
accuracy_percent = twodec(ezpp_accuracy_percent(ez));
pp = twodec(ezpp_pp(ez));
aim_pp = twodec(ezpp_aim_pp(ez));
speed_pp = twodec(ezpp_speed_pp(ez));
acc_pp = twodec(ezpp_acc_pp(ez));
printf("AR%g OD%g ", ar, od);
if (ezpp_mode(ez) == MODE_STD) {
printf("CS%g ", cs);
}
printf("HP%g\n", hp);
printf("300 hitwindow: %g ms\n", ezpp_odms(ez));
printf("%d circles, %d sliders, %d spinners\n",
ezpp_ncircles(ez), ezpp_nsliders(ez), ezpp_nspinners(ez));
if (ezpp_mode(ez) == MODE_STD) {
printf("%g stars (%g aim, %g speed)\n", stars, aim_stars, speed_stars);
printf("\nspeed strain: ");
asciiplt(getspeed, ezpp_nobjects(ez), ez);
printf(" aim strain: ");
asciiplt(getaim, ezpp_nobjects(ez), ez);
} else {
printf("%g stars\n", ezpp_stars(ez));
}
printf("\n");
if (mods_str) {
printf("+%s ", mods_str);
}
printf("%d/%dx ", ezpp_combo(ez), ezpp_max_combo(ez));
printf("%g%%\n", accuracy_percent);
printf("%g pp (", pp);
if (ezpp_mode(ez) == MODE_STD) {
printf("%g aim, ", aim_pp);
}
printf("%g speed, ", speed_pp);
printf("%g acc)\n\n", acc_pp);
}
/* json output --------------------------------------------------------- */
void print_escaped_json_string_ex(char* str, int quotes) {
char* chars_to_escape = "\\\"";
char* p;
if (quotes) {
putchar('"');
}
for (; *str; ++str) {
/* escape all characters in chars_to_escape */
for (p = chars_to_escape; *p; ++p) {
if (*p == *str) {
putchar('\\');
}
}
putchar(*str);
}
if (quotes) {
putchar('"');
}
}
#define print_escaped_json_string(x) \
print_escaped_json_string_ex(x, 1)
/* https://www.doc.ic.ac.uk/%7Eeedwards/compsys/float/nan.html */
static int is_inf(float b) {
int* p = (int*)&b;
return *p == 0x7F800000 || *p == 0xFF800000;
}
/*
* json is mentally challenged and can't handle inf and nan so
* we're gonna be mathematically incorrect
*/
void fix_json_flt(float* v) {
if (is_inf(*v)) {
*v = -1;
} else if (is_nan(*v)) {
*v = 0;
}
}
output_sig(output_json) {
float pp, aim_pp, speed_pp, acc_pp, stars, aim_stars, speed_stars;
printf("{\"oppai_version\":\"%s\",", oppai_version_str());
if (result < 0) {
printf("\"code\":%d,", result);
printf("\"errstr\":");
print_escaped_json_string(errstr(result));
printf("}");
return;
}
pp = ezpp_pp(ez);
aim_pp = ezpp_aim_pp(ez);
speed_pp = ezpp_speed_pp(ez);
acc_pp = ezpp_acc_pp(ez);
stars = ezpp_stars(ez);
aim_stars = ezpp_aim_stars(ez);
speed_stars = ezpp_speed_stars(ez);
fix_json_flt(&pp);
fix_json_flt(&aim_pp);
fix_json_flt(&speed_pp);
fix_json_flt(&acc_pp);
fix_json_flt(&stars);
fix_json_flt(&aim_stars);
fix_json_flt(&speed_stars);
printf("\"code\":200,\"errstr\":\"no error\",");
printf("\"artist\":");
print_escaped_json_string(ezpp_artist(ez));
if (strcmp(ezpp_artist(ez), ezpp_artist_unicode(ez))) {
printf(",\"artist_unicode\":");
print_escaped_json_string(ezpp_artist_unicode(ez));
}
printf(",\"title\":");
print_escaped_json_string(ezpp_title(ez));
if (strcmp(ezpp_title(ez), ezpp_title_unicode(ez))) {
printf(",\"title_unicode\":");
print_escaped_json_string(ezpp_title_unicode(ez));
}
printf(",\"creator\":");
print_escaped_json_string(ezpp_creator(ez));
printf(",\"version\":");
print_escaped_json_string(ezpp_version(ez));
printf(",");
if (!mods_str) {
mods_str = "";
}
printf(
"\"mods_str\":\"%s\",\"mods\":%d,"
"\"od\":%g,\"ar\":%g,\"cs\":%g,\"hp\":%g,"
"\"combo\":%d,\"max_combo\":%d,"
"\"num_circles\":%d,\"num_sliders\":%d,"
"\"num_spinners\":%d,\"misses\":%d,"
"\"score_version\":%d,\"stars\":%.17g,"
"\"speed_stars\":%.17g,\"aim_stars\":%.17g,"
"\"aim_pp\":%.17g,\"speed_pp\":%.17g,\"acc_pp\":%.17g,"
"\"pp\":%.17g}",
mods_str, ezpp_mods(ez), ezpp_od(ez), ezpp_ar(ez),
ezpp_cs(ez), ezpp_hp(ez), ezpp_combo(ez),
ezpp_max_combo(ez), ezpp_ncircles(ez), ezpp_nsliders(ez),
ezpp_nspinners(ez), ezpp_nmiss(ez), ezpp_score_version(ez),
ezpp_stars(ez), ezpp_speed_stars(ez), ezpp_aim_stars(ez),
ezpp_aim_pp(ez), ezpp_speed_pp(ez), ezpp_acc_pp(ez), ezpp_pp(ez)
);
}
/* csv output ---------------------------------------------------------- */
void print_escaped_csv_string(char* str) {
char* chars_to_escape = "\\;";
char* p;
for (; *str; ++str) {
/* escape all characters in chars_to_escape */
for (p = chars_to_escape; *p; ++p) {
if (*p == *str) {
putchar('\\');
}
}
putchar(*str);
}
}
output_sig(output_csv) {
printf("oppai_version;%s\n", oppai_version_str());
if (result < 0) {
printf("code;%d\nerrstr;", result);
print_escaped_csv_string(errstr(result));
return;
}
printf("code;200\nerrstr;no error\n");
printf("artist;");
print_escaped_csv_string(ezpp_artist(ez));
puts("");
if (strcmp(ezpp_artist(ez), ezpp_artist_unicode(ez))) {
printf("artist_unicode;");
print_escaped_csv_string(ezpp_artist_unicode(ez));
puts("");
}
printf("title;");
print_escaped_csv_string(ezpp_title(ez));
puts("");
if (strcmp(ezpp_title(ez), ezpp_title_unicode(ez))) {
printf("title_unicode;");
print_escaped_csv_string(ezpp_title_unicode(ez));
puts("");
}
printf("version;");
print_escaped_csv_string(ezpp_version(ez));
puts("");
printf("creator;");
print_escaped_csv_string(ezpp_creator(ez));
puts("");
if (!mods_str) {
mods_str = "";
}
printf(
"mods_str;%s\nmods;%d\nod;%g\nar;%g\ncs;%g\nhp;%g\n"
"combo;%d\nmax_combo;%d\nnum_circles;%d\n"
"num_sliders;%d\nnum_spinners;%d\nmisses;%d\n"
"score_version;%d\nstars;%.17g\nspeed_stars;%.17g\n"
"aim_stars;%.17g\naim_pp;%.17g\nspeed_pp;%.17g\nacc_pp;%.17g\npp;%.17g",
mods_str, ezpp_mods(ez), ezpp_od(ez), ezpp_ar(ez),
ezpp_cs(ez), ezpp_hp(ez), ezpp_combo(ez),
ezpp_max_combo(ez), ezpp_ncircles(ez), ezpp_nsliders(ez),
ezpp_nspinners(ez), ezpp_nmiss(ez), ezpp_score_version(ez),
ezpp_stars(ez), ezpp_speed_stars(ez), ezpp_aim_stars(ez),
ezpp_aim_pp(ez), ezpp_speed_pp(ez), ezpp_acc_pp(ez), ezpp_pp(ez)
);
}
/* binary output ------------------------------------------------------- */
void write1(int v) {
char buf = (char)(v & 0xFF);
fwrite(&buf, 1, 1, stdout);
}
void write2(int v) {
char buf[2];
buf[0] = (char)(v & 0xFF);
buf[1] = (char)(v >> 8);
fwrite(buf, 1, 2, stdout);
}
void write4(int v) {
char buf[4];
buf[0] = (char)(v & 0xFF);
buf[1] = (char)((v >> 8) & 0xFF);
buf[2] = (char)((v >> 16) & 0xFF);
buf[3] = (char)((v >> 24) & 0xFF);
fwrite(buf, 1, 4, stdout);
}
void write_flt(float f) {
int* p = (int*)&f;
write4(*p);
}
void write_str(char* str) {
int len = al_min(0xFFFF, (int)strlen(str));
write2(len);
printf("%s", str);
write1(0);
}
output_sig(output_binary) {
int major, minor, patch;
(void)mods_str;
if (!freopen(0, "wb", stdout)) {
perror("freopen");
exit(1);
}
printf("binoppai");
oppai_version(&major, &minor, &patch);
write1(major);
write1(minor);
write1(patch);
write4(result);
if (result < 0) {
return;
}
/* TODO: use varargs to group calls of the same func */
write_str(ezpp_artist(ez));
write_str(ezpp_artist_unicode(ez));
write_str(ezpp_title(ez));
write_str(ezpp_title_unicode(ez));
write_str(ezpp_version(ez));
write_str(ezpp_creator(ez));
write4(ezpp_mods(ez));
write_flt(ezpp_od(ez));
write_flt(ezpp_ar(ez));
write_flt(ezpp_cs(ez));
write_flt(ezpp_hp(ez));
write4(ezpp_combo(ez));
write4(ezpp_max_combo(ez));
write2(ezpp_ncircles(ez));
write2(ezpp_nsliders(ez));
write2(ezpp_nspinners(ez));
write4(ezpp_score_version(ez));
write_flt(ezpp_stars(ez));
write_flt(ezpp_speed_stars(ez));
write_flt(ezpp_aim_stars(ez));
write2(0); /* legacy (nsingles) */
write2(0); /* legacy (nsigles_threshold) */
write_flt(ezpp_aim_pp(ez));
write_flt(ezpp_speed_pp(ez));
write_flt(ezpp_acc_pp(ez));
write_flt(ezpp_pp(ez));
}
/* gnuplot output ------------------------------------------------------ */
#define gnuplot_string(x) print_escaped_json_string_ex(x, 0)
void gnuplot_strains(ezpp_t ez, int type) {
int i;
for (i = 0; i < ezpp_nobjects(ez); ++i) {
printf("%.17g %.17g\n", ezpp_time_at(ez, i),
ezpp_strain_at(ez, i, type));
}
}
output_sig(output_gnuplot) {
if (result < 0 || ezpp_mode(ez) != MODE_STD) {
return;
}
puts("set encoding utf8;");
printf("set title \"");
gnuplot_string(ezpp_artist(ez));
printf(" - ");
gnuplot_string(ezpp_title(ez));
if (strcmp(ezpp_artist(ez), ezpp_artist_unicode(ez)) ||
strcmp(ezpp_title(ez), ezpp_title_unicode(ez)))
{
printf("(");
gnuplot_string(ezpp_artist_unicode(ez));
printf(" - ");
gnuplot_string(ezpp_title_unicode(ez));
printf(")");
}
printf(" [");
gnuplot_string(ezpp_version(ez));
printf("] mapped by ");
gnuplot_string(ezpp_creator(ez));
if (mods_str) printf(" +%s", mods_str);
puts("\";");
puts(
"set xlabel 'time (ms)';"
"set ylabel 'strain';"
"set multiplot layout 2,1 rowsfirst;"
"plot '-' with lines lc 1 title 'speed'"
);
gnuplot_strains(ez, DIFF_SPEED);
puts("e");
puts("unset title;");
puts("plot '-' with lines lc 2 title 'aim'");
gnuplot_strains(ez, DIFF_AIM);
}
/* ------------------------------------------------------------- */
#define CODE_DESC "the code and errstr fields " \
"should be checked for errors. a negative value for code " \
"indicates an error"
typedef struct output_module {
char* name;
fnoutput* func;
char* description[4];
/* null terminated array of strings because of c90 literal limits */
} output_module_t;
output_module_t modules[] = {
{ "null", output_null, { "no output", 0 } },
{ "text", output_text, { "plain text", 0 } },
{
"json", output_json,
{ "a single utf-8 json object.\n" CODE_DESC, 0 }
},
{
"csv", output_csv,
{ "fieldname;value\n"
"one value per line. ';' characters in strings will be "
"escaped to \"\\;\". utf-8.\n" CODE_DESC, 0 }
},
{
"binary",
output_binary,
{ "binary stream of values, encoded in little endian.\n"
"negative code values indicate an error, which matches "
"the error codes defined in oppai.c\n"
"for an example on how to read this in C, check out "
"examples/binary.c in oppai-ng's source\n"
"\n"
"floats and floats are represented using whatever "
"convention the host machine and compiler use. unless you "
"are on a really exotic machine it shouldn't matter\n"
"\n"
"strings (str) are encoded as a 2-byte integer indicating "
"the length in bytes, followed by the string bytes and ",
"a null (zero) terminating byte\n"
"\n"
"binoppai (8-byte magic), "
"int8 oppai_ver_major, int8 oppai_ver_minor, "
"int8 oppai_ver_patch, int error_code, "
"str artist, str artist_utf8, str title, str title_utf8, "
"str version, str creator, "
"int mods_bitmask, float od, float ar, float cs, "
"float hp, int combo, int max_combo, "
"int16 ncircles, int16 nsliders, int16 nspinner, "
"int score_version, float total_stars, ",
"float speed_stars, float aim_stars, int16 nsingles, "
"int16 nsingles_threshold, float aim_pp, "
"float speed_pp, float acc_pp, float pp",
0 }
},
{ "gnuplot", output_gnuplot, { "gnuplot .gp script", 0 } },
};
output_module_t* output_by_name(char* name) {
int i;
for (i = 0; i < array_len(modules); ++i) {
if (!strcmp(modules[i].name, name)) {
return &modules[i];
}
}
return 0;
}
int cmpsuffix(char* str, char* suffix) {
int sufflen = (int)al_min(strlen(str), strlen(suffix));
return strcmp(str + strlen(str) - sufflen, suffix);
}
char lowercase(char c) {
if (c >= 'A' && c <= 'Z') {
return c + ('a' - 'A');
}
return c;
}
char uppercase(char c) {
if (c >= 'a' && c <= 'z') {
return c - ('a' - 'A');
}
return c;
}
int strcmp_nc(char* a, char* b) {
for (;; ++a, ++b) {
char la = lowercase(*a);
char lb = lowercase(*b);
if (la > lb) {
return 1;
}
else if (la < lb) {
return -1;
}
if (!*a || *b) {
break;
}
}
return 0;
}
/* TODO: split main into smaller funcs for readability? */
int main(int argc, char* argv[]) {
int i;
int result;
ezpp_t ez = ezpp_new();
output_module_t* m;
char* output_name = "text";
char* mods_str = 0;
int mods = MODS_NOMOD;
float tmpf, speed_stars = 0, aim_stars = 0, accuracy_percent = 0;
int tmpi, n100 = 0, n50 = 0;
/* parse arguments ------------------------------------------------- */
me = argv[0];
if (argc < 2) {
usage();
return 1;
}
if (*argv[1] == '-' && strlen(argv[1]) > 1) {
char* a = argv[1] + 1;
if (!strcmp_nc(a, "version") || !strcmp_nc(a, "v")) {
puts(oppai_version_str());
return 0;
}
}
for (i = 2; i < argc; ++i) {
char* a = argv[i];
char* p;
int iswhite = 1;
for (p = a; *p; ++p) {
if (!isspace(*p)) {
iswhite = 0;
break;
}
}
if (iswhite) {
continue;
}
for (p = a; *p; ++p) {
*p = lowercase(*p);
}
if (*a == '-' && a[1] == 'o') {
output_name = a + 2;
if (!strcmp(output_name, "?")) {
int j;
int nmodules = sizeof(modules) / sizeof(modules[0]);
for (j = 0; j < nmodules; ++j) {
char** d = modules[j].description;
puts(modules[j].name);
for (; *d; ++d) {
printf("%s", *d);
}
puts("\n-");
}
return 0;
}
continue;
}
if (!cmpsuffix(a, "%") && sscanf(a, "%f", &accuracy_percent) == 1) {
continue;
}
if (!cmpsuffix(a, "x100") && sscanf(a, "%d", &n100) == 1) {
continue;
}
if (!cmpsuffix(a, "x50") && sscanf(a, "%d", &n50) == 1) {
continue;
}
if (!cmpsuffix(a, "speed") && sscanf(a, "%f", &speed_stars) == 1) {
continue;
}
if (!cmpsuffix(a, "aim") && sscanf(a, "%f", &aim_stars) == 1) {
continue;
}
if (!cmpsuffix(a, "xm") || !cmpsuffix(a, "xmiss") ||
!cmpsuffix(a, "m"))
{
if (sscanf(a, "%d", &tmpi) == 1) {
ezpp_set_nmiss(ez, tmpi);
continue;
}
}
if (!cmpsuffix(a, "x") && sscanf(a, "%d", &tmpi) == 1) {
ezpp_set_combo(ez, tmpi);
continue;
}
if (sscanf(a, "scorev%d", &tmpi)) {
ezpp_set_score_version(ez, tmpi);
continue;
}
if (sscanf(a, "ar%f", &tmpf)) {
ezpp_set_base_ar(ez, tmpf);
continue;
}
if (sscanf(a, "od%f", &tmpf)) {
ezpp_set_base_od(ez, tmpf);
continue;
}
if (sscanf(a, "cs%f", &tmpf)) {
ezpp_set_base_cs(ez, tmpf);
continue;
}
if (sscanf(a, "-m%d", &tmpi) == 1) {
ezpp_set_mode_override(ez, tmpi);
continue;
}
if (sscanf(a, "-end%d", &tmpi) == 1) {
ezpp_set_end(ez, tmpi);
continue;
}
if (!strcmp(a, "-taiko")) {
ezpp_set_mode_override(ez, MODE_TAIKO);
continue;
}
if (!strcmp(a, "-touch")) {
mods |= MODS_TOUCH_DEVICE;
continue;
}
/* this should be last because it uppercase's the string */
if (*a == '+') {
mods_str = a + 1;
for (p = mods_str; *p; ++p) {
*p = uppercase(*p);
}
#define m(mod) \
if (!strncmp(p, #mod, strlen(#mod))) { \
mods |= MODS_##mod; \
p += strlen(#mod); \
continue; \
}
for (p = mods_str; *p;) {
m(NF) m(EZ) m(TD) m(HD) m(HR) m(SD) m(DT) m(RX) m(HT) m(NC) m(FL)
m(AT) m(SO) m(AP) m(PF) m(NOMOD)
++p;
}
#undef m
continue;
}
info(">%s\n", a);
result = ERR_SYNTAX;
goto output;
}
if (accuracy_percent) {
ezpp_set_accuracy_percent(ez, accuracy_percent);
} else {
ezpp_set_accuracy(ez, n100, n50);
}
ezpp_set_mods(ez, mods);
ezpp_set_speed_stars(ez, speed_stars);
ezpp_set_aim_stars(ez, aim_stars);
result = ezpp(ez, argv[1]);
output:
m = output_by_name(output_name);
if (!m) {
info("output module '%s' does not exist. check 'oppai - -o?'\n",
output_name);
return 1;
}
m->func(result, ez, mods_str);
ezpp_free(ez); /* just so valgrind stops crying */
return result < 0;
}

2626
oppai.c Normal file

File diff suppressed because it is too large Load Diff

16
package Normal file
View File

@ -0,0 +1,16 @@
#!/bin/sh
dir=$(dirname $0)
dir=$(readlink -f $dir)
prevdir=$(pwd)
cd $dir
#git pull origin master || exit $?
for prefix in i386/ ""; do
container="${prefix}oppai-ng:ubuntu"
docker build -t "$container" \
--build-arg PREFIX="$prefix" . || exit $?
docker run --rm -v $dir:/tmp \
-e arch=$(echo ${prefix:-x86_64} | tr -d /) \
"$container" || exit $?
done
cd $prevdir

49
release Normal file
View File

@ -0,0 +1,49 @@
#!/bin/sh
dir=$(dirname $0)
olddir=$(pwd)
cd $dir
echo""
echo "compiling and stripping"
CC=musl-gcc ./build -static -no-pie || exit $?
CC=gcc ./libbuild || exit $?
echo""
echo "packaging"
folder="oppai-$(./oppai -version)-"
folder="${folder}$(gcc -dumpmachine)"
mkdir -p "$folder" || exit $?
mv ./oppai $folder/oppai || exit $?
mv ./liboppai.so $folder/liboppai.so || exit $?
git archive HEAD --prefix=src/ -o "$folder"/src.tar ||
exit $?
cd "$folder" || exit $?
tar xf src.tar || exit $?
cd .. || exit $?
rm "$folder".tar.xz
tar -cvJf "$folder".tar.xz \
"$folder"/oppai \
"$folder"/liboppai.so \
"$folder"/src \
|| exit $?
echo ""
file "$folder".tar.xz
tar tf "$folder".tar.xz
echo ""
file "$folder"/oppai
readelf --dynamic "$folder"/oppai
ldd "$folder"/oppai
echo ""
file "$folder"/liboppai.so
ldd "$folder"/liboppai.so
rm -rf "$folder"
cd $olddir

59
release.ps1 Normal file
View File

@ -0,0 +1,59 @@
# you must allow script execution by running
# 'Set-ExecutionPolicy RemoteSigned' in an admin powershell
# this requires vcvars to be already set (see vcvarsall17.ps1)
# 7zip is also required (choco install 7zip and add it to path)
$dir = Split-Path -Parent $MyInvocation.MyCommand.Definition
Push-Location "$dir"
git pull origin master -q
function Write-Header {
param ([string]$Text)
Write-Host $Text -Foreground Yellow -Background Black
}
function Header {
param ([string]$Title)
Write-Header ""
Write-Header "##########################################################"
Write-Header "> $Title"
}
cmd /c "build.bat"; if (-not $?) { exit $LastExitCode }
cmd /c "libbuild.bat"; if (-not $?) { exit $LastExitCode }
Header "Packaging"
$folder = "oppai-" + $(.\oppai.exe -version) + "-windows-"
$clout = & cl 2>&1 | %{ "$_" }
"$clout" -match "(Microsoft.*for )([a-z0-9\-_]+)" | Out-Null
if (-not $?) {
exit $LastExitCode
}
$folder = $folder + $Matches[2]
mkdir $folder; if (-not $?) { exit $LastExitCode }
Copy-Item oppai.exe $folder; if (-not $?) { exit $LastExitCode }
Copy-Item oppai.dll $folder; if (-not $?) { exit $LastExitCode }
Copy-Item oppai.lib $folder; if (-not $?) { exit $LastExitCode }
git archive HEAD --prefix=src\ -o $folder\src.zip
if (-not $?) {
exit $LastExitCode
}
Set-Location $folder; if (-not $?) { exit $LastExitCode }
&7z x src.zip; if (-not $?) { exit $LastExitCode }
Set-Location ..; if (-not $?) { exit $LastExitCode }
if (Test-Path "$folder.zip") {
Remove-Item "$folder.zip"
}
&7z a "$folder.zip" $folder\oppai.exe $folder\oppai.dll $folder\oppai.lib `
$folder\src
if (-not $?) {
exit $LastExitCode
}
Header "Result:"
&7z l "$folder.zip"
Remove-Item $folder -Force -Recurse
Pop-Location

17
swig/README.md Normal file
View File

@ -0,0 +1,17 @@
these are maintainer instructions, check the readme's in the binding
directories for user guides
# requirements
* swig
* docker
* twine (pip)
# building all bindings
```sh
./build.sh
```
# publishing all bindings
```sh
PUBLISH=1 ./build.sh
```

19
swig/build.sh Normal file
View File

@ -0,0 +1,19 @@
#!/bin/sh
runall() {
for d in ./*/; do
[ "$d" = "." ] && continue
cd "$d"
./build.sh || return $?
[ ! -z $PUBLISH ] && ./publish.sh
cd ..
done
}
dir="$(dirname "$0")"
olddir="$(pwd)"
cd "$dir"
runall
res=$?
cd "$olddir"
exit $res

11
swig/oppai.i Normal file
View File

@ -0,0 +1,11 @@
%module oppai
%feature("autodoc", "3");
%apply int *OUTPUT {int*}
%begin{
#define SWIG_PYTHON_2_UNICODE
}
%{
#define OPPAI_IMPLEMENTATION
#include "oppai.c"
%}
#include "oppai.c"

42
swig/python/README.rst Normal file
View File

@ -0,0 +1,42 @@
osu! pp and difficulty calculator. automatically generated C bindings for
https://github.com/Francesco149/oppai-ng
usage
===========
.. code-block:: sh
pip install oppai
.. code-block:: python
#!/usr/bin/env python
import sys
from oppai import *
ez = ezpp_new()
ezpp(ez, sys.argv[1])
print("%g pp" % ezpp_pp(ez))
ezpp_free(ez)
.. code-block:: sh
./example.py /path/to/file.osu
.. code-block:: sh
python -c 'help("oppai")'
for a list of functions, or just read the top of oppai.c for better doc
limitations
===========
for some reason, python3 doesn't provide a persisting pointer to strings
you pass to c code even if you aren't doing anything with them, so if you
want to reuse the handle at all you have to use ezpp_dup and ezpp_data_dup,
which create a copy of the strings you pass in. this is inefficient so
it's recommended to use autocalc mode and only call ezpp_dup or
ezpp_data_dup when you're actually changing map

10
swig/python/build.sh Normal file
View File

@ -0,0 +1,10 @@
#!/bin/sh
rm -rf ./dist
cp ../../oppai.c .
cp ../oppai.i .
swig -python -includeall oppai.i || exit
for img in quay.io/pypa/manylinux2010_x86_64 quay.io/pypa/manylinux2010_i686; do
docker run --user 1000:1000 --rm -v $(pwd):/io -w /io $img \
./build_wheels.sh || exit
done

View File

@ -0,0 +1,15 @@
#!/bin/sh
# this is meant to be used from docker
for pybin in /opt/python/*/bin
do
rm *.so
"$pybin/pip" wheel . -w dist/ || exit
done
"$pybin/python" ./setup.py sdist || exit
for w in dist/*linux_*.whl; do
auditwheel repair "$w" -w dist/ || exit
done
rm dist/*linux_*.whl

View File

@ -0,0 +1,9 @@
#!/usr/bin/env python
import sys
from oppai import *
ez = ezpp_new()
ezpp(ez, sys.argv[1])
print("%g pp" % ezpp_pp(ez))
ezpp_free(ez)

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python
import sys
from oppai import *
ez = ezpp_new()
ezpp_set_autocalc(ez, 1)
for osufile in sys.argv[1:]:
ezpp_dup(ez, osufile)
print("%s - %s [%s]" % (ezpp_artist(ez), ezpp_title(ez), ezpp_version(ez)))
print("%g stars" % ezpp_stars(ez))
for acc in range(95, 101):
ezpp_set_accuracy_percent(ez, acc)
print("%g%% -> %g pp" % (acc, ezpp_pp(ez)))
print("")
ezpp_free(ez)

View File

@ -0,0 +1,38 @@
#!/usr/bin/env python
import sys
from oppai import *
if sys.version_info[0] < 3:
# hack to force utf-8 on py < 3
reload(sys)
sys.setdefaultencoding("utf-8")
def mods_str(mods):
mods_str = "+"
if mods == 0:
mods_str += "nomod"
else:
if mods & MODS_HD: mods_str += "hd"
if mods & MODS_DT: mods_str += "dt"
if mods & MODS_HR: mods_str += "hr"
return mods_str
ez = ezpp_new()
ezpp_set_autocalc(ez, 1)
for osufile in sys.argv[1:]:
# by providing the map in memory we can speed up subsequent re-parses
f = open(osufile, 'r')
data = f.read()
f.close()
ezpp_data_dup(ez, data, len(data.encode('utf-8')))
print("%s - %s [%s]" % (ezpp_artist(ez), ezpp_title(ez), ezpp_version(ez)))
print("%g stars" % ezpp_stars(ez))
for mods in [ 0, MODS_HR, MODS_HD | MODS_HR, MODS_DT, MODS_HD | MODS_DT ]:
print(mods_str(mods))
ezpp_set_mods(ez, mods)
for acc in range(95, 101):
ezpp_set_accuracy_percent(ez, acc)
print("%g%% -> %g pp" % (acc, ezpp_pp(ez)))
print("")
ezpp_free(ez)

View File

@ -0,0 +1,14 @@
#!/usr/bin/env python
import sys
from oppai import *
# prints timing points (just a test for this interface)
ez = ezpp_new()
ezpp(ez, sys.argv[1])
for i in range(ezpp_ntiming_points(ez)):
time = ezpp_timing_time(ez, i)
ms_per_beat = ezpp_timing_ms_per_beat(ez, i)
change = ezpp_timing_change(ez, i)
print("%f | %f beats per ms | change: %d" % (time, ms_per_beat, change))
ezpp_free(ez)

3
swig/python/publish.sh Normal file
View File

@ -0,0 +1,3 @@
#!/bin/sh
twine upload dist/*

25
swig/python/python.yml Normal file
View File

@ -0,0 +1,25 @@
environment:
TWINE_USERNAME: lolisamurai
TWINE_PASSWORD:
secure: DTyX4L2loxFxlsbPYAwuga0DyOlGiOnJyEwi/j08gba0NyNx21TvRFMHpITIqcfg
cache:
- C:\ProgramData\chocolatey\bin
- C:\ProgramData\chocolatey\lib
- C:\Users\appveyor\AppData\Local\pip\Cache
install:
- IF NOT EXIST C:\ProgramData\chocolatey\bin\swig.exe choco install swig --version 3.0.12 --yes --limit-output
- python -m pip install twine
- python -m pip install cibuildwheel==0.10.1
build_script:
- cd swig/python
- copy ..\..\oppai.c .
- copy ..\oppai.i .
- swig -python -includeall oppai.i
- cibuildwheel --output-dir wheelhouse
- ps: >-
if ($env:APPVEYOR_REPO_TAG -eq "true") {
Invoke-Expression "python -m twine upload --skip-existing wheelhouse/*.whl"
}
artifacts:
- path: "swig\\python\\wheelhouse\\*.whl"
name: wheels

2
swig/python/setup.cfg Normal file
View File

@ -0,0 +1,2 @@
[build_ext]
swig-opts=-includeall

58
swig/python/setup.py Normal file
View File

@ -0,0 +1,58 @@
import os
import sys
def parse_version_str():
import re
version_re = re.compile('^#define OPPAI_VERSION_(MAJOR|MINOR|PATCH)')
version = []
with open('oppai.c', 'r') as f:
for line in f:
if version_re.match(line):
version.append(str(int(line.split(' ')[2])))
return '.'.join(version)
try:
from setuptools import setup, Extension
except ImportError:
from distutils.core import setup, Extension
try:
from oppai import oppai_version_str
except Exception:
def oppai_version_str():
try:
return parse_version_str()
except Exception:
return "INVALID"
oppai_classifiers = [
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 3",
"Intended Audience :: Developers",
"License :: Public Domain",
"Topic :: Software Development :: Libraries",
"Topic :: Utilities",
]
f = open("README.rst", "r")
oppai_readme = f.read()
f.close()
oppai_sources=['oppai.i']
if os.system('swig') != 0:
oppai_sources=['oppai_wrap.c', 'oppai.c']
setup(
name="oppai",
version=oppai_version_str(),
author="Franc[e]sco",
author_email="lolisamurai@tfwno.gf",
url="https://github.com/Francesco149/oppai-ng",
ext_modules=[Extension('_oppai', oppai_sources)],
py_modules=["oppai"],
description="osu! pp and difficulty calculator, C bindings",
long_description=oppai_readme,
license="Unlicense",
classifiers=oppai_classifiers,
keywords="osu! osu"
)

497
test.osu Normal file
View File

@ -0,0 +1,497 @@
osu file format v14
[General]
AudioFilename: audio.mp3
AudioLeadIn: 0
PreviewTime: 64748
Countdown: 0
SampleSet: Soft
StackLeniency: 0.7
Mode: 0
LetterboxInBreaks: 0
WidescreenStoryboard: 0
[Editor]
Bookmarks: 2807,8454,19748,31042,42336,53630,64924,76218
DistanceSpacing: 0.7
BeatDivisor: 4
GridSize: 16
TimelineZoom: 2.1
[Metadata]
Title:The Weekend Whip (TV Size) ~Speed Up Ver.~
TitleUnicode:The Weekend Whip (TV Size) ~Speed Up Ver.~
Artist:The Fold
ArtistUnicode:The Fold
Creator:Julaaaan
Version:Spin!
Source:
Tags:
BeatmapID:0
BeatmapSetID:-1
[Difficulty]
HPDrainRate:5.1
CircleSize:3.8
OverallDifficulty:9.1
ApproachRate:9.4
SliderMultiplier:1.9
SliderTickRate:1
[Events]
//Background and Video events
0,0,"spin.jpg",0,0
//Break Periods
//Storyboard Layer 0 (Background)
//Storyboard Layer 1 (Fail)
//Storyboard Layer 2 (Pass)
//Storyboard Layer 3 (Foreground)
//Storyboard Layer 4 (Overlay)
//Storyboard Sound Samples
[TimingPoints]
2807,352.941176470588,4,2,0,100,1,0
2807,-200,4,2,0,40,0,0
5630,-142.857142857143,4,2,0,50,0,0
7042,-125,4,2,0,60,0,0
8101,-90.9090909090909,4,2,0,70,0,0
8365,-90.9090909090909,4,2,0,5,0,0
8454,-111.111111111111,4,2,0,80,0,0
19748,-111.111111111111,4,2,0,70,0,0
30689,-90.9090909090909,4,2,0,70,0,0
30954,-90.9090909090909,4,2,0,5,0,0
31042,-111.111111111111,4,2,0,90,0,1
31748,-111.111111111111,4,2,0,70,0,0
32454,-111.111111111111,4,2,0,90,0,1
33159,-111.111111111111,4,2,0,70,0,0
33777,-111.111111111111,4,2,0,5,0,0
33865,-111.111111111111,4,2,0,80,0,0
36689,-111.111111111111,4,2,0,90,0,1
37395,-111.111111111111,4,2,0,70,0,0
38101,-111.111111111111,4,2,0,90,0,1
38806,-111.111111111111,4,2,0,70,0,0
39424,-111.111111111111,4,2,0,5,0,0
39512,-111.111111111111,4,2,0,80,0,0
42336,-111.111111111111,4,2,0,80,0,0
53630,-111.111111111111,4,2,0,90,0,1
54336,-111.111111111111,4,2,0,70,0,0
55042,-111.111111111111,4,2,0,90,0,1
55748,-111.111111111111,4,2,0,70,0,0
56365,-111.111111111111,4,2,0,5,0,0
56454,-111.111111111111,4,2,0,80,0,0
59277,-111.111111111111,4,2,0,90,0,1
59983,-111.111111111111,4,2,0,70,0,0
60689,-111.111111111111,4,2,0,90,0,1
61395,-111.111111111111,4,2,0,70,0,0
62012,-111.111111111111,4,2,0,5,0,0
62101,-111.111111111111,4,2,0,80,0,0
64924,-90.9090909090909,4,2,0,80,0,1
86101,-90.9090909090909,4,2,0,70,0,1
93159,-166.666666666667,4,2,0,40,0,0
94571,-125,4,2,0,60,0,0
96512,-125,4,2,0,30,0,0
[Colours]
Combo1 : 153,153,153
Combo2 : 237,27,36
Combo3 : 0,90,213
Combo4 : 198,250,255
Combo5 : 177,101,101
Combo6 : 24,170,97
Combo7 : 17,170,254
Combo8 : 255,255,255
[HitObjects]
105,119,2807,6,0,P|103:164|108:185,1,47.5
167,137,3159,2,0,P|173:177|181:194,1,47.5
327,122,3512,6,0,P|326:94|319:70,1,47.5
379,100,3865,2,0,P|375:74|366:53,1,47.5
416,230,4218,6,0,P|399:260|379:273,1,47.5
430,287,4571,2,0,P|411:310|386:322,1,47.5
178,244,4924,6,0,P|157:213|136:204,1,47.5
123,255,5277,2,0,P|91:240|67:241,1,47.5
225,353,5630,6,0,P|265:338|285:313,1,66.4999979705811
277,183,5983,2,0,P|236:209|221:235,1,66.4999979705811
394,271,6336,6,0,P|424:249|439:204,1,66.4999979705811
365,74,6689,2,0,P|336:109|324:142,1,66.4999979705811
303,271,7042,5,0,0:0:0:0:
209,53,7218,1,0,0:0:0:0:
191,228,7395,1,0,0:0:0:0:
310,44,7571,2,0,P|318:73|319:92,1,38
332,239,7748,5,0,0:0:0:0:
317,216,7836,1,0,0:0:0:0:
295,200,7924,1,0,0:0:0:0:
270,189,8012,1,0,0:0:0:0:
243,188,8101,2,0,B|178:182|131:221|131:221|155:232|169:220,1,156.750004783631
221,223,8454,5,0,0:0:0:0:
116,54,8630,1,0,0:0:0:0:
93,271,8806,1,0,0:0:0:0:
235,88,8983,1,0,0:0:0:0:
292,304,9159,5,0,0:0:0:0:
134,154,9336,1,0,0:0:0:0:
371,184,9512,1,0,0:0:0:0:
170,332,9689,1,0,0:0:0:0:
262,146,9865,6,0,P|287:220|280:254,1,85.4999973907472
222,358,10218,2,0,P|183:315|172:274,1,85.4999973907472
175,134,10571,6,0,P|119:125|83:133,1,85.4999973907472
1,273,10924,2,0,P|58:288|99:280,1,85.4999973907472
195,188,11277,6,0,P|228:235|241:288,1,85.4999973907472
165,368,11630,1,0,0:0:0:0:
288,162,11806,1,0,0:0:0:0:
288,338,11983,1,0,0:0:0:0:
105,141,12159,1,0,0:0:0:0:
47,326,12336,1,0,0:0:0:0:
226,192,12512,1,0,0:0:0:0:
165,368,12689,6,0,B|164:345|174:332|174:332|168:314|168:314|185:293|175:266,1,85.4999973907472
237,142,13042,1,0,0:0:0:0:
301,348,13218,1,0,0:0:0:0:
325,121,13395,5,0,0:0:0:0:
205,316,13571,1,0,0:0:0:0:
192,72,13748,5,0,0:0:0:0:
396,289,13924,1,0,0:0:0:0:
372,57,14101,6,0,P|347:101|343:152,1,85.4999973907472
352,238,14454,1,0,0:0:0:0:
330,168,14630,1,0,0:0:0:0:
215,239,14806,1,0,0:0:0:0:
281,218,14983,1,0,0:0:0:0:
201,114,15159,1,0,0:0:0:0:
149,240,15336,1,0,0:0:0:0:
276,139,15512,6,0,P|316:126|374:154,1,85.4999973907472
440,202,15865,1,0,0:0:0:0:
373,256,16042,1,0,0:0:0:0:
290,233,16218,1,0,0:0:0:0:
263,314,16395,1,0,0:0:0:0:
199,149,16571,1,0,0:0:0:0:
159,332,16748,6,0,B|76:279|78:177,1,170.999994781494
107,79,17277,1,0,0:0:0:0:
148,154,17454,1,0,0:0:0:0:
216,102,17630,1,0,0:0:0:0:
215,248,17806,1,0,0:0:0:0:
79,136,17983,1,0,0:0:0:0:
271,162,18159,1,0,0:0:0:0:
90,252,18336,6,0,P|136:212|159:176,1,85.4999973907472
187,75,18689,1,0,0:0:0:0:
255,286,18865,1,0,0:0:0:0:
303,72,19042,5,0,0:0:0:0:
149,285,19218,1,0,0:0:0:0:
196,28,19395,5,0,0:0:0:0:
337,257,19571,1,0,0:0:0:0:
381,25,19748,38,0,P|340:70|328:122,1,85.4999973907472
199,74,20101,1,0,0:0:0:0:
132,126,20277,1,0,0:0:0:0:
46,130,20454,2,0,P|40:207|57:242,1,85.4999973907472
114,337,20806,1,0,0:0:0:0:
184,171,20983,1,0,0:0:0:0:
286,310,21159,5,0,0:0:0:0:
320,142,21336,2,0,P|348:192|347:230,1,85.4999973907472
206,76,21689,2,0,P|166:122|153:159,1,85.4999973907472
441,103,22042,2,0,P|399:60|347:45,2,85.4999973907472
401,282,22571,6,0,P|350:253|325:210,1,85.4999973907472
265,120,22924,1,0,0:0:0:0:
217,191,23101,1,0,0:0:0:0:
144,146,23277,1,0,0:0:0:0:
114,311,23454,1,0,0:0:0:0:
26,116,23630,1,0,0:0:0:0:
185,266,23806,6,0,P|167:213|176:171,1,85.4999973907472
265,74,24159,1,0,0:0:0:0:
296,225,24336,1,0,0:0:0:0:
368,126,24512,1,0,0:0:0:0:
391,290,24689,1,0,0:0:0:0:
241,150,24865,1,0,0:0:0:0:
208,270,25042,1,0,0:0:0:0:
324,92,25218,1,0,0:0:0:0:
326,288,25395,6,0,P|288:263|241:245,1,85.4999973907472
80,194,25748,1,0,0:0:0:0:
145,139,25924,1,0,0:0:0:0:
228,159,26101,2,0,P|290:126|330:89,1,85.4999973907472
358,24,26454,1,0,0:0:0:0:
458,212,26630,1,0,0:0:0:0:
482,72,26806,5,0,0:0:0:0:
342,238,26983,1,0,0:0:0:0:
387,33,27159,1,0,0:0:0:0:
436,237,27336,2,0,P|438:288|422:340,1,85.4999973907472
241,264,27689,2,0,P|249:208|279:166,1,85.4999973907472
346,110,28042,1,0,0:0:0:0:
198,106,28218,6,0,P|155:131|121:164,1,85.4999973907472
62,270,28571,1,0,0:0:0:0:
84,72,28748,1,0,0:0:0:0:
159,231,28924,2,0,P|199:261|235:272,1,85.4999973907472
301,182,29277,2,0,P|353:186|387:195,1,85.4999973907472
470,328,29630,6,0,P|409:340|369:323,1,85.4999973907472
248,212,29983,1,0,0:0:0:0:
288,364,30159,1,0,0:0:0:0:
371,164,30336,1,0,0:0:0:0:
194,321,30512,1,0,0:0:0:0:
151,121,30689,2,0,B|182:110|216:124|216:124|248:113|248:113|275:127|306:115,1,156.750004783631
352,66,31042,5,0,0:0:0:0:
281,339,31218,1,0,0:0:0:0:
184,75,31395,1,0,0:0:0:0:
168,81,31748,1,0,0:0:0:0:
163,97,32101,1,0,0:0:0:0:
147,103,32454,5,0,0:0:0:0:
412,0,32630,1,0,0:0:0:0:
324,280,32806,1,0,0:0:0:0:
309,289,33159,1,0,0:0:0:0:
291,288,33512,2,0,P|226:267|145:293,1,128.249996086121
145,298,33865,5,0,0:0:0:0:
203,140,34042,1,0,0:0:0:0:
228,306,34218,2,0,P|254:268|288:240,1,85.4999973907472
394,178,34571,1,0,0:0:0:0:
357,310,34748,1,0,0:0:0:0:
305,159,34924,1,0,0:0:0:0:
436,264,35101,6,0,P|460:216|469:169,1,85.4999973907472
444,42,35454,2,0,P|383:79|359:113,1,85.4999973907472
386,209,35806,2,0,P|326:223|288:210,1,85.4999973907472
223,151,36159,2,0,P|195:208|201:252,2,85.4999973907472
362,101,36689,5,0,0:0:0:0:
470,337,36865,1,0,0:0:0:0:
265,214,37042,1,0,0:0:0:0:
258,229,37395,1,0,0:0:0:0:
261,245,37748,1,0,0:0:0:0:
250,258,38101,5,0,0:0:0:0:
145,27,38277,1,0,0:0:0:0:
97,277,38454,1,0,0:0:0:0:
113,273,38806,1,0,0:0:0:0:
122,287,39159,2,0,B|190:331|190:331|250:317,1,128.249996086121
274,328,39512,5,0,0:0:0:0:
241,174,39689,1,0,0:0:0:0:
379,266,39865,2,0,P|324:239|278:234,1,85.4999973907472
170,277,40218,1,0,0:0:0:0:
290,112,40395,1,0,0:0:0:0:
300,283,40571,1,0,0:0:0:0:
138,185,40748,1,0,0:0:0:0:
114,351,40924,6,0,P|172:313|192:277,1,85.4999973907472
221,169,41277,5,0,0:0:0:0:
306,341,41454,1,0,0:0:0:0:
358,123,41630,5,0,0:0:0:0:
208,299,41806,1,0,0:0:0:0:
203,71,41983,5,0,0:0:0:0:
376,258,42159,1,0,0:0:0:0:
366,49,42336,86,0,P|321:89|306:145,1,85.4999973907472
275,252,42689,1,0,0:0:0:0:
231,178,42865,1,0,0:0:0:0:
279,107,43042,1,0,0:0:0:0:
140,177,43218,1,0,0:0:0:0:
160,93,43395,1,0,0:0:0:0:
216,262,43571,1,0,0:0:0:0:
278,75,43748,6,0,P|329:82|361:129,1,85.4999973907472
359,268,44101,1,0,0:0:0:0:
259,168,44277,2,0,P|194:152|156:162,1,85.4999973907472
76,247,44630,2,0,P|121:273|172:268,2,85.4999973907472
121,48,45159,6,0,P|127:111|116:139,1,85.4999973907472
15,242,45512,1,0,0:0:0:0:
194,133,45689,1,0,0:0:0:0:
127,307,45865,2,0,P|120:254|131:209,1,85.4999973907472
179,91,46218,1,0,0:0:0:0:
195,264,46395,6,0,B|247:283|290:251|264:255|311:223|365:228,1,170.999994781494
405,275,46924,1,0,0:0:0:0:
300,91,47101,1,0,0:0:0:0:
246,253,47277,5,0,0:0:0:0:
387,54,47454,1,0,0:0:0:0:
330,325,47630,5,0,0:0:0:0:
213,70,47806,1,0,0:0:0:0:
156,337,47983,102,0,P|171:292|206:267,1,85.4999973907472
383,231,48336,1,0,0:0:0:0:
303,199,48512,1,0,0:0:0:0:
259,125,48689,2,0,P|210:106|146:110,1,85.4999973907472
54,164,49042,1,0,0:0:0:0:
169,10,49218,1,0,0:0:0:0:
180,224,49395,5,0,0:0:0:0:
93,70,49571,1,0,0:0:0:0:
62,264,49748,1,0,0:0:0:0:
171,112,49924,2,0,P|221:94|261:101,1,85.4999973907472
397,169,50277,2,0,P|351:208|314:222,1,85.4999973907472
228,178,50630,1,0,0:0:0:0:
118,238,50806,6,0,P|188:266|215:290,1,85.4999973907472
288,352,51159,1,0,0:0:0:0:
345,178,51336,1,0,0:0:0:0:
401,328,51512,1,0,0:0:0:0:
258,212,51689,2,0,P|249:247|211:295,1,85.4999973907472
155,198,52042,6,0,P|182:130|286:109,1,170.999994781494
436,210,52571,2,0,P|454:171|457:129,1,85.4999973907472
403,63,52924,1,0,0:0:0:0:
320,39,53101,1,0,0:0:0:0:
244,78,53277,2,0,P|190:85|133:65,1,85.4999973907472
40,35,53630,5,0,0:0:0:0:
34,255,53806,1,0,0:0:0:0:
184,27,53983,1,0,0:0:0:0:
194,40,54336,1,0,0:0:0:0:
211,41,54689,1,0,0:0:0:0:
217,56,55042,5,0,0:0:0:0:
470,0,55218,1,0,0:0:0:0:
273,174,55395,1,0,0:0:0:0:
265,189,55748,1,0,0:0:0:0:
272,204,56101,2,0,B|301:229|341:220|331:210|371:206|410:239,1,128.249996086121
399,254,56454,5,0,0:0:0:0:
386,80,56630,1,0,0:0:0:0:
494,202,56806,2,0,P|439:179|408:176,1,85.4999973907472
262,226,57159,1,0,0:0:0:0:
314,83,57336,1,0,0:0:0:0:
350,279,57512,1,0,0:0:0:0:
183,160,57689,6,0,P|179:217|202:253,1,85.4999973907472
295,305,58042,2,0,P|245:330|191:334,1,85.4999973907472
44,279,58395,2,0,P|75:243|115:223,1,85.4999973907472
282,198,58748,2,0,P|291:142|278:97,2,85.4999973907472
175,304,59277,5,0,0:0:0:0:
425,233,59454,1,0,0:0:0:0:
151,144,59630,1,0,0:0:0:0:
165,134,59983,1,0,0:0:0:0:
171,118,60336,1,0,0:0:0:0:
187,115,60689,5,0,0:0:0:0:
290,317,60865,1,0,0:0:0:0:
343,75,61042,1,0,0:0:0:0:
339,91,61395,1,0,0:0:0:0:
347,105,61748,2,0,B|361:148|361:148|348:188|348:188|359:229,1,128.249996086121
391,249,62101,5,0,0:0:0:0:
239,192,62277,1,0,0:0:0:0:
325,338,62454,2,0,P|305:287|304:252,1,85.4999973907472
334,102,62806,5,0,0:0:0:0:
199,265,62983,1,0,0:0:0:0:
199,76,63159,2,0,P|213:129|248:166,1,85.4999973907472
381,172,63512,6,0,P|380:197|375:218,1,42.7499986953736
276,282,63689,1,0,0:0:0:0:
259,262,63777,1,0,0:0:0:0:
237,248,63865,6,0,P|205:228|189:224,1,42.7499986953736
76,258,64042,2,0,P|102:283|127:295,1,42.7499986953736
186,172,64218,5,0,0:0:0:0:
214,162,64306,1,0,0:0:0:0:
244,163,64395,2,0,P|279:173|301:189,1,42.7499986953736
352,304,64571,5,0,0:0:0:0:
352,304,64924,70,0,P|371:247|366:200,1,104.500003189087
242,58,65277,2,0,B|191:108|191:108|150:111,1,104.500003189087
0,168,65630,5,0,0:0:0:0:
167,347,65806,1,0,0:0:0:0:
140,123,65983,2,0,P|167:197|150:266,1,104.500003189087
60,313,66336,5,0,0:0:0:0:
299,232,66512,1,0,0:0:0:0:
61,204,66689,1,0,0:0:0:0:
281,341,66865,2,0,P|221:324|179:289,1,104.500003189087
181,76,67218,2,0,P|232:139|235:176,1,104.500003189087
130,343,67571,1,0,0:0:0:0:
120,163,67748,6,0,P|167:214|212:226,1,104.500003189087
431,213,68101,2,0,B|381:153|381:153|348:151,1,104.500003189087
216,278,68454,5,0,0:0:0:0:
314,61,68630,1,0,0:0:0:0:
335,278,68806,5,0,0:0:0:0:
204,75,68983,1,0,0:0:0:0:
148,343,69159,6,0,P|199:308|244:298,1,104.500003189087
407,173,69512,2,0,P|338:180|296:161,1,104.500003189087
108,41,69865,5,0,0:0:0:0:
373,96,70042,1,0,0:0:0:0:
114,135,70218,1,0,0:0:0:0:
358,17,70395,6,0,B|300:35|271:87|296:80|269:131|190:144,1,209.000006378174
48,125,70924,5,0,0:0:0:0:
299,262,71101,1,0,0:0:0:0:
108,223,71277,5,0,0:0:0:0:
404,127,71454,1,0,0:0:0:0:
220,123,71630,5,0,0:0:0:0:
465,270,71806,1,0,0:0:0:0:
245,262,71983,6,0,P|291:237|321:171,1,104.500003189087
336,66,72336,5,0,0:0:0:0:
246,338,72512,1,0,0:0:0:0:
222,32,72689,5,0,0:0:0:0:
354,282,72865,1,0,0:0:0:0:
367,0,73042,5,0,0:0:0:0:
227,238,73218,1,0,0:0:0:0:
430,92,73395,6,0,P|390:107|340:171,1,104.500003189087
305,329,73748,1,0,0:0:0:0:
244,75,73924,2,0,P|226:142|237:194,1,104.500003189087
414,280,74277,5,0,0:0:0:0:
140,279,74454,1,0,0:0:0:0:
368,165,74630,1,0,0:0:0:0:
264,350,74806,6,0,P|263:307|239:243,1,104.500003189087
172,157,75159,1,0,0:0:0:0:
107,338,75336,1,0,0:0:0:0:
90,115,75512,5,0,0:0:0:0:
193,367,75689,1,0,0:0:0:0:
254,81,75865,5,0,0:0:0:0:
43,295,76042,1,0,0:0:0:0:
93,29,76218,54,0,P|135:86|146:153,1,104.500003189087
148,296,76571,2,0,B|231:303|231:303|292:270,1,104.500003189087
299,138,76924,5,0,0:0:0:0:
377,353,77101,1,0,0:0:0:0:
454,132,77277,2,0,P|436:200|383:250,1,104.500003189087
190,289,77630,5,0,0:0:0:0:
357,148,77806,1,0,0:0:0:0:
302,349,77983,1,0,0:0:0:0:
172,155,78159,2,0,P|222:198|255:249,1,104.500003189087
186,383,78512,2,0,P|129:339|107:293,1,104.500003189087
91,106,78865,1,0,0:0:0:0:
209,248,79042,6,0,P|266:220|295:171,1,104.500003189087
408,70,79395,2,0,B|453:142|453:142|452:174,1,104.500003189087
382,335,79748,5,0,0:0:0:0:
283,127,79924,1,0,0:0:0:0:
270,335,80101,5,0,0:0:0:0:
392,141,80277,1,0,0:0:0:0:
202,262,80454,6,0,P|150:225|139:181,1,104.500003189087
154,31,80806,2,0,B|81:56|81:56|64:83,1,104.500003189087
49,271,81159,5,0,0:0:0:0:
249,109,81336,1,0,0:0:0:0:
128,338,81512,1,0,0:0:0:0:
154,88,81689,6,0,B|142:168|195:244|195:244|203:198,1,209.000006378174
244,100,82218,5,0,0:0:0:0:
350,317,82395,1,0,0:0:0:0:
362,60,82571,5,0,0:0:0:0:
253,282,82748,1,0,0:0:0:0:
249,4,82924,5,0,0:0:0:0:
356,243,83101,1,0,0:0:0:0:
165,75,83277,6,0,P|203:119|257:137,1,104.500003189087
439,130,83630,5,0,0:0:0:0:
173,252,83806,1,0,0:0:0:0:
398,263,83983,5,0,0:0:0:0:
103,135,84159,1,0,0:0:0:0:
334,140,84336,5,0,0:0:0:0:
54,232,84512,1,0,0:0:0:0:
198,59,84689,6,0,P|228:127|232:180,1,104.500003189087
134,324,85042,1,0,0:0:0:0:
353,161,85218,2,0,P|332:237|287:275,1,104.500003189087
89,201,85571,5,0,0:0:0:0:
321,339,85748,1,0,0:0:0:0:
53,335,85924,1,0,0:0:0:0:
318,246,86101,6,0,P|244:252|187:278,1,104.500003189087
88,360,86454,1,0,0:0:0:0:
181,171,86630,1,0,0:0:0:0:
233,358,86806,6,0,P|176:322|146:283,1,104.500003189087
206,115,87159,2,0,P|265:170|288:222,1,104.500003189087
306,306,87512,69,0,0:0:0:0:
382,46,87689,1,0,0:0:0:0:
444,271,87865,5,0,0:0:0:0:
254,44,88042,1,0,0:0:0:0:
221,324,88218,5,0,0:0:0:0:
347,123,88395,1,0,0:0:0:0:
352,362,88571,5,0,0:0:0:0:
241,154,88748,1,0,0:0:0:0:
468,187,88924,5,0,0:0:0:0:
212,280,89101,1,0,0:0:0:0:
420,275,89277,5,0,0:0:0:0:
133,176,89454,1,0,0:0:0:0:
352,169,89630,5,0,0:0:0:0:
76,281,89806,1,0,0:0:0:0:
234,94,89983,1,0,0:0:0:0:
261,329,90159,22,0,B|273:255|273:255|247:204,1,104.500003189087
154,133,90512,1,0,0:0:0:0:
138,296,90689,5,0,0:0:0:0:
287,98,90865,1,0,0:0:0:0:
295,297,91042,5,0,0:0:0:0:
163,63,91218,1,0,0:0:0:0:
141,238,91395,5,0,0:0:0:0:
293,3,91571,2,0,B|312:75|312:75|302:104,1,104.500003189087
122,151,91924,1,0,0:0:0:0:
383,222,92101,5,0,0:0:0:0:
136,315,92277,1,0,0:0:0:0:
331,107,92454,5,0,0:0:0:0:
237,365,92630,1,0,0:0:0:0:
215,37,92806,5,0,0:0:0:0:
349,340,92983,1,0,0:0:0:0:
383,29,93159,22,0,P|380:74|372:98,1,56.9999982604981
292,101,93512,2,0,P|295:67|307:46,1,56.9999982604981
366,203,93865,6,0,P|354:255|337:279,1,56.9999982604981
272,276,94218,2,0,P|277:225|292:197,1,56.9999982604981
305,112,94571,6,0,P|259:98|213:104,2,76
296,276,95101,5,0,0:0:0:0:
303,250,95189,1,0,0:0:0:0:
305,223,95277,5,0,0:0:0:0:
228,127,95454,1,0,0:0:0:0:
194,268,95630,5,0,0:0:0:0:
352,147,95806,6,0,B|258:136|196:208|252:196|174:276|74:253,1,304

8
test/build Normal file
View File

@ -0,0 +1,8 @@
#!/bin/sh
dir="$(dirname "$0")"
. "$dir"/../cflags
$cc $cflags ${@:--DOPPAI_IMPLEMENTATION} \
test.c $ldflags -o oppai_test

16
test/build.bat Normal file
View File

@ -0,0 +1,16 @@
@echo off
set flags="%*"
IF "%1"=="" (
set flags=-DOPPAI_IMPLEMENTATION
)
del oppai_test.exe >nul 2>&1
del test.obj >nul 2>&1
cl -D_CRT_SECURE_NO_WARNINGS=1 ^
-DNOMINMAX=1 ^
-O2 -nologo -MT -Gm- -GR- -EHsc -W4 ^
%flags% ^
test.c ^
-Feoppai_test.exe ^
|| EXIT /B 1

21
test/download_suite Normal file
View File

@ -0,0 +1,21 @@
#!/bin/sh
dir="$(dirname "$0")"
if command -v realpath 2>&1 >/dev/null; then
wdir="$(realpath "$dir")"
else
wdir="$dir"
fi
olddir="$(pwd)"
cd "$wdir" || exit $?
url="$(cat ./suite_url)"
if [ $(find test_suite 2>/dev/null | tail -n +2 | wc -l) = "0" ]; then
curl -LO "$url" || exit $?
tar xf "$(basename $url)" || exit $?
else
echo "using existing test_suite"
fi
cd "$olddir"

19
test/download_suite.ps1 Normal file
View File

@ -0,0 +1,19 @@
# you must allow script execution by running
# 'Set-ExecutionPolicy RemoteSigned' in an admin powershell
# 7zip is also required (choco install 7zip and add it to path)
$url = Get-Content .\suite_url -Raw
$dir = Split-Path -Parent $MyInvocation.MyCommand.Definition
Push-Location "$dir"
if ((Test-Path .\test_suite) -and (Get-ChildItem .\test_suite | Measure-Object).Count -gt 0) {
Write-Host "using existing test_suite"
} else {
# my windows 7 install doesn't support Tls3
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
(New-Object System.Net.WebClient).DownloadFile($url, "$dir\test_suite.tar.gz")
&7z x .\test_suite.tar.gz
&7z x .\test_suite.tar
}
Pop-Location

79
test/download_suite.py Normal file
View File

@ -0,0 +1,79 @@
#!/usr/bin/env python
# very rough script that downloads unique maps from test_suite.json that
# gensuite.py generates
import sys
import json
try:
import httplib
except ImportError:
import http.client as httplib
try:
import urllib
except ImportError:
import urllib.parse as urllib
osu = httplib.HTTPSConnection('osu.ppy.sh')
def osu_get(path):
while True:
try:
osu.request('GET', path)
r = osu.getresponse()
raw = bytes()
while True:
try:
raw += r.read()
break
except httplib.IncompleteRead as e:
raw += e.partial
return raw
except (httplib.HTTPException, ValueError) as e:
sys.stderr.write('%s\n' % (traceback.format_exc()))
# prevents exceptions on next request if the
# response wasn't previously read due to errors
try:
osu.getresponse().read()
except httplib.HTTPException:
pass
time.sleep(5)
if len(sys.argv) != 2:
sys.stderr.write('usage: %s test_suite.json\n' % sys.argv[0])
sys.exit(1)
with open(sys.argv[1], 'r') as f:
scores = json.loads(f.read())
unique_maps = set([s['beatmap_id'] for m in [0, 1] for s in scores[m]])
i = 1
for b in unique_maps:
sys.stderr.write(
"[%.02f%% - %d/%d] %s" % (i / float(len(unique_maps)) * 100, i,
len(unique_maps), b)
)
i += 1
# TODO: tmp file and rename
try:
with open(b + '.osu', 'r') as f:
sys.stderr.write(' (already exists)\n')
continue
except FileNotFoundError:
pass
sys.stderr.write('\n')
with open(b + '.osu', 'wb') as f:
f.write(osu_get('/osu/' + b))

292
test/gentest.py Normal file
View File

@ -0,0 +1,292 @@
#!/usr/bin/env python
import sys
import os
import time
import json
import traceback
import argparse
import hashlib
if sys.version_info[0] < 3:
# hack to force utf-8
reload(sys)
sys.setdefaultencoding('utf-8')
try:
import httplib
except ImportError:
import http.client as httplib
try:
import urllib
except ImportError:
import urllib.parse as urllib
# -------------------------------------------------------------------------
parser = argparse.ArgumentParser(
description = (
'generates the oppai test suite. outputs c++ code to ' +
'stdout and the json dump to a file.'
)
)
parser.add_argument(
'-key',
default = None,
help = (
'osu! api key. required if -input-file is not present. ' +
'can also be specified through the OSU_API_KEY ' +
'environment variable'
)
)
parser.add_argument(
'-output-file',
default = 'test_suite.json',
help = 'dumps json to this file'
)
parser.add_argument(
'-input-file',
default = None,
help = (
'loads test suite from this json file instead of '
'fetching it from osu api. if set to "-", json will be '
'read from standard input'
)
)
args = parser.parse_args()
if args.key == None and 'OSU_API_KEY' in os.environ:
args.key = os.environ['OSU_API_KEY']
# -------------------------------------------------------------------------
osu_treset = time.time() + 60
osu_ncalls = 0
def osu_get(conn, endpoint, paramsdict=None):
# GETs /api/endpoint?paramsdict&k=args.key from conn
# return json object, exits process on api errors
global osu_treset, osu_ncalls, args
sys.stderr.write('%s %s\n' % (endpoint, str(paramsdict)))
paramsdict['k'] = args.key
path = '/api/%s?%s' % (endpoint, urllib.urlencode(paramsdict))
while True:
while True:
if time.time() >= osu_treset:
osu_ncalls = 0
osu_treset = time.time() + 60
sys.stderr.write('\napi ready\n')
if osu_ncalls < 60:
break
else:
sys.stderr.write('waiting for api cooldown...\r')
time.sleep(1)
try:
conn.request('GET', path)
osu_ncalls += 1
r = conn.getresponse()
raw = ''
while True:
try:
raw += r.read()
break
except httplib.IncompleteRead as e:
raw += e.partial
j = json.loads(raw)
if 'error' in j:
sys.stderr.write('%s\n' % j['error'])
sys.exit(1)
return j
except (httplib.HTTPException, ValueError) as e:
sys.stderr.write('%s\n' % (traceback.format_exc()))
try:
# prevents exceptions on next request if the
# response wasn't previously read due to errors
conn.getresponse().read()
except httplib.HTTPException:
pass
time.sleep(5)
def gen_modstr(bitmask):
# generates c++ code for a mod combination's bitmask
mods = []
allmods = {
(1<< 0, 'nf'), (1<< 1, 'ez'), (1<< 2, 'td'), (1<< 3, 'hd'),
(1<< 4, 'hr'), (1<< 6, 'dt'), (1<< 8, 'ht'),
(1<< 9, 'nc'), (1<<10, 'fl'), (1<<12, 'so')
}
for bit, string in allmods:
if bitmask & bit != 0:
mods.append(string)
if len(mods) == 0:
return 'nomod'
return ' | '.join(mods)
# -------------------------------------------------------------------------
if args.key == None:
sys.stderr.write(
'please set OSU_API_KEY or pass it as a parameter\n'
)
sys.exit(1)
scores = []
top_players = [
[ 124493, 4787150, 2558286, 1777162, 2831793, 50265 ],
[ 3174184, 8276884, 5991961, 2774767 ]
]
if args.input_file == None:
# fetch a fresh test suite from osu api
osu = httplib.HTTPSConnection('osu.ppy.sh')
for m in [0, 1]:
for u in top_players[m]:
params = { 'u': u, 'limit': 100, 'type': 'id', 'm': m }
batch = osu_get(osu, 'get_user_best', params)
for s in batch:
s['mode'] = m
scores += batch
# temporarily removed, not all std scores are recalc'd
#params = { 'm': 0, 'since': '2015-11-26' }
#maps = osu_get(osu, 'get_beatmaps', params)
# no taiko converts here because as explained below, tiny float
# errors can lead to completely broken conversions
for mode in [1]:
params = { 'm': mode, 'since': '2015-11-26' }
maps = osu_get(osu, 'get_beatmaps', params)
for m in maps:
params = { 'b': m['beatmap_id'], 'm': mode }
map_scores = osu_get(osu, 'get_scores', params)
if len(map_scores) == 0:
sys.stderr.write('W: map has no scores???\n')
continue
# note: api also returns qualified and loved, so ignore
# maps that don't have pp in rankings
if not 'pp' in map_scores[0] or map_scores[0]['pp'] is None:
sys.stderr.write('W: ignoring loved/qualified map\n')
continue
for s in map_scores:
s['beatmap_id'] = m['beatmap_id']
s['mode'] = mode
scores += map_scores
with open(args.output_file, 'w+') as f:
f.write(json.dumps(scores))
else:
# load existing test suite from json file
with open(args.input_file, 'r') as f:
scores = json.loads(f.read())
# sort by mode by map
scores.sort(key=lambda x: int(x['beatmap_id'])<<32|x['mode'],
reverse=True)
print('/* this code was automatically generated by gentest.py */')
print('')
# make code a little nicer by shortening mods
allmods = {
'nf', 'ez', 'td', 'hd', 'hr', 'dt', 'ht', 'nc', 'fl', 'so', 'nomod'
}
for mod in allmods:
print('#define %s MODS_%s' % (mod, mod.upper()))
print('''
typedef struct {
int mode;
int id;
int max_combo;
int n300, n100, n50, nmiss;
int mods;
double pp;
} score_t;
score_t suite[] = {''')
seen_hashes = []
for s in scores:
# due to tiny floating point errors, maps can even double
# in combo and not even lazer gets this right, taiko converts are hell
# so I'm just gonna exclude them
if s['mode'] == 1:
is_convert = False
with open('test_suite/'+s['beatmap_id']+'.osu') as f:
for line in f:
split = line.split(':')
if len(split) >= 2 and split[0] == 'Mode' and int(split[1]) == 0:
is_convert = True
break
if is_convert:
continue
# some taiko maps ignore combo scaling for no apparent reason
# so i will only include full combos for now
if int(s['countmiss']) != 0:
continue
if s['pp'] is None:
continue
# why is every value returned by osu api a string?
line = (
' { %d, %s, %s, %s, %s, %s, %s, %s, %s },' %
(
s['mode'], s['beatmap_id'], s['maxcombo'], s['count300'],
s['count100'], s['count50'], s['countmiss'],
gen_modstr(int(s['enabled_mods'])), s['pp']
)
)
# don't include identical scores by different people
s = hashlib.sha1(line).digest()
if s in seen_hashes:
continue
print(line)
seen_hashes.append(s)
print('};\n')
for mod in allmods:
print('#undef %s' % (mod))

11
test/pack_suite Normal file
View File

@ -0,0 +1,11 @@
#!/bin/sh
dir="$(dirname "$0")"
wdir="$(realpath "$dir")"
olddir="$(pwd)"
cd "$wdir" || exit $?
tar -zcvf test_suite_$(date +%F).tar.gz test_suite/*.osu
res=$?
cd "$olddir"
exit $res

1
test/suite_url Normal file
View File

@ -0,0 +1 @@
https://github.com/Francesco149/oppai-ng/releases/download/2.3.2/test_suite_2019-02-19.tar.gz

119
test/test.c Normal file
View File

@ -0,0 +1,119 @@
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../oppai.c"
#include "test_suite.c" /* defines suite */
#define ERROR_MARGIN 0.02f /* pp can be off by +- 2% */
/*
* margin is actually
* - 3x for < 100pp
* - 2x for 100-200pp
* - 1.5x for 200-300pp
*/
void print_score(score_t* s) {
char mods_str_buf[20];
char* mods_str = mods_str_buf;
strcpy(mods_str, "nomod");
#define m(mod) \
if (s->mods & MODS_##mod) { \
mods_str += sprintf(mods_str, #mod); \
} \
m(HR) m(NC) m(HT) m(SO) m(NF) m(EZ) m(DT) m(FL) m(HD)
#undef m
printf("m=%d %u +%s %dx %dx300 %dx100 %dx50 %dxmiss %g pp\n",
s->mode, s->id, mods_str_buf, s->max_combo, s->n300, s->n100,
s->n50, s->nmiss, s->pp);
}
int main() {
char fname_buf[4096];
char* fname = fname_buf;
int i, n = (int)(sizeof(suite) / sizeof(suite[0]));
float max_err[2] = { 0, 0 };
int max_err_map[2] = { 0, 0 };
float avg_err[2] = { 0, 0 };
int count[2] = { 0, 0 };
float error = 0;
float error_percent = 0;
ezpp_t ez = ezpp_new();
int err;
int last_id = 0;
fname += sprintf(fname, "test_suite/");
for (i = 0; i < n; ++i) {
score_t* s = &suite[i];
float margin;
float pptotal;
print_score(s);
if (s->id != last_id) {
sprintf(fname, "%u.osu", s->id);
last_id = s->id;
/* force reparse and don't reuse prev map ar/cs/od/hp */
ezpp_set_base_cs(ez, -1);
ezpp_set_base_ar(ez, -1);
ezpp_set_base_od(ez, -1);
ezpp_set_base_hp(ez, -1);
}
ezpp_set_mods(ez, s->mods);
ezpp_set_accuracy(ez, s->n100, s->n50);
ezpp_set_nmiss(ez, s->nmiss);
ezpp_set_combo(ez, s->max_combo);
ezpp_set_mode_override(ez, s->mode);
err = ezpp(ez, fname_buf);
if (err < 0) {
printf("%s\n", errstr(err));
exit(1);
}
pptotal = ezpp_pp(ez);
++count[s->mode];
margin = (float)s->pp * ERROR_MARGIN;
if (s->pp < 100) {
margin *= 3;
} else if (s->pp < 200) {
margin *= 2;
} else if (s->pp < 300) {
margin *= 1.5f;
}
error = (float)fabs(pptotal - (float)s->pp);
error_percent = error / (float)s->pp;
avg_err[s->mode] += error_percent;
if (error_percent > max_err[s->mode]) {
max_err[s->mode] = error_percent;
max_err_map[s->mode] = s->id;
}
if (error >= margin) {
printf("failed test: got %g pp, expected %g\n", pptotal, s->pp);
exit(1);
}
}
for (i = 0; i < 2; ++i) {
switch (i) {
case MODE_STD: puts("osu"); break;
case MODE_TAIKO: puts("taiko"); break;
}
avg_err[i] /= count[i];
printf("%d scores\n", count[i]);
printf("avg err %f\n", avg_err[i]);
printf("max err %f on %d\n", max_err[i], max_err_map[i]);
}
ezpp_free(ez);
return 0;
}

14745
test/test_suite.c Normal file

File diff suppressed because it is too large Load Diff

3
valgrind Normal file
View File

@ -0,0 +1,3 @@
#!/bin/sh
valgrind --leak-check=full --track-origins=yes --show-leak-kinds=all "$@"

15
vcvarsall17.ps1 Normal file
View File

@ -0,0 +1,15 @@
# https://stackoverflow.com/questions/2124753/how-can-i-use-powershell-with-the-visual-studio-command-prompt
param (
[string]$arch = "amd64"
)
Push-Location "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\Tools"
cmd /c "VsDevCmd.bat -arch=$arch&set" |
ForEach-Object {
if ($_ -match "=") {
$v = $_.split("="); set-item -force -path "ENV:\$($v[0])" -value "$($v[1])"
}
}
Pop-Location
Write-Host "`nVisual Studio 2017 Command Prompt variables set." -ForegroundColor Yellow