initial commit
This commit is contained in:
commit
491faab301
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal 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
29
.travis.yml
Normal 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
6
Dockerfile
Normal 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
67
appveyor.yml
Normal 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
6
build
Normal 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
12
build.bat
Normal 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
45
cflags
Normal 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
26
examples/FFI.cs
Normal 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
133
examples/binary.c
Normal 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
16
examples/min.c
Normal 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
61
examples/reuse.c
Normal 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
42
examples/reuse_mem.c
Normal 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
37
libbuild
Normal 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
14
libbuild.bat
Normal 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
966
main.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
16
package
Normal file
16
package
Normal 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
49
release
Normal 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
59
release.ps1
Normal 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
17
swig/README.md
Normal 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
19
swig/build.sh
Normal 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
11
swig/oppai.i
Normal 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
42
swig/python/README.rst
Normal 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
10
swig/python/build.sh
Normal 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
|
15
swig/python/build_wheels.sh
Normal file
15
swig/python/build_wheels.sh
Normal 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
|
9
swig/python/examples/basic.py
Normal file
9
swig/python/examples/basic.py
Normal 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)
|
16
swig/python/examples/reuse.py
Normal file
16
swig/python/examples/reuse.py
Normal 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)
|
38
swig/python/examples/reuse_mem.py
Normal file
38
swig/python/examples/reuse_mem.py
Normal 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)
|
14
swig/python/examples/timing.py
Normal file
14
swig/python/examples/timing.py
Normal 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
3
swig/python/publish.sh
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
twine upload dist/*
|
25
swig/python/python.yml
Normal file
25
swig/python/python.yml
Normal 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
2
swig/python/setup.cfg
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[build_ext]
|
||||||
|
swig-opts=-includeall
|
58
swig/python/setup.py
Normal file
58
swig/python/setup.py
Normal 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
497
test.osu
Normal 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
8
test/build
Normal 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
16
test/build.bat
Normal 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
21
test/download_suite
Normal 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
19
test/download_suite.ps1
Normal 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
79
test/download_suite.py
Normal 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
292
test/gentest.py
Normal 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
11
test/pack_suite
Normal 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
1
test/suite_url
Normal 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
119
test/test.c
Normal 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
14745
test/test_suite.c
Normal file
File diff suppressed because it is too large
Load Diff
3
valgrind
Normal file
3
valgrind
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
valgrind --leak-check=full --track-origins=yes --show-leak-kinds=all "$@"
|
15
vcvarsall17.ps1
Normal file
15
vcvarsall17.ps1
Normal 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
|
Loading…
x
Reference in New Issue
Block a user