Skip to content
Snippets Groups Projects
Commit b82d9b1e authored by Fang Lu's avatar Fang Lu
Browse files

osufs - complete metadata parsing

* Added song sanity check
* Not tested
parent 7ecfe748
No related branches found
No related tags found
No related merge requests found
......@@ -17,6 +17,8 @@
#include "wavenc.h"
#define ISHOT(ptr) (((uint8_t*)ptr)[0]==0xcc)
using namespace std;
osu_dir osu_open_dir(const char *dirname) {
......@@ -49,6 +51,7 @@ osu_song osu_read_osu(const char *filename, mdev *dev, osu_meta *meta,
osu_song ret;
char line[1024];
memset(&ret, 0xcc, sizeof(ret));
strncpy(ret.magic, "DEAD", 4);
sprintf(line, "%s/%s", cont->dirname, filename);
......@@ -119,6 +122,10 @@ osu_song osu_read_osu(const char *filename, mdev *dev, osu_meta *meta,
}
strncpy(ret.magic, "BEAT", 4);
printf("Parsing done. Performing sanity check...\n");
osu_check_beatmap_sanity(&ret);
return ret;
}
......@@ -182,9 +189,10 @@ int osu_parse_general(char *line, osu_song *song,
fprintf(stderr, "Could not parse StackLeniency string `%s'\n", val);
return 1;
}
song->stack_leniency = round(slen * 256);
song->stack_leniency = round(slen * 0x100);
return 0;
}
return 0;
}
......@@ -247,11 +255,131 @@ int osu_parse_metadata(char *line, osu_song *song,
int osu_parse_difficulty(char *line, osu_song *song,
mdev *dev, osu_meta *meta, osu_dir *cont) {
char *val = osu_util_str_split(line, ':'), *key = line;
if (!val) {
fprintf(stderr, "Difficulty Section: field has no value\n");
return 1;
}
// HPDrainRate (Decimal) specifies the HP Drain difficulty.
if (strcasecmp(key, "HPDrainRate") == 0) {
float drain;
if (sscanf(val, "%f", &drain) != 1) {
fprintf(stderr, "Could not parse HPDrainRate string `%s'\n", val);
return 1;
}
song->hp_drain = round(drain * 0x100);
return 0;
}
// CircleSize (Decimal) specifies the size of hit object circles.
if (strcasecmp(key, "CircleSize") == 0) {
float circlesize;
if (sscanf(val, "%f", &circlesize) != 1) {
fprintf(stderr, "Could not parse CircleSize string `%s'\n", val);
return 1;
}
song->circle_size = round(circlesize * 0x100);
return 0;
}
// OverallDifficulty (Decimal) specifies the amount of time allowed to click
// a hit object on time.
if (strcasecmp(key, "OverallDifficulty") == 0) {
float diff;
if (sscanf(val, "%f", &diff) != 1) {
fprintf(stderr, "Could not parse OverallDifficulty string `%s'\n", val);
return 1;
}
song->difficulty = round(diff * 0x100);
return 0;
}
// ApproachRate (Decimal) specifies the amount of time taken for the
// approach circle and hit object to appear.
if (strcasecmp(key, "ApproachRate") == 0) {
float approach;
if (sscanf(val, "%f", &approach) != 1) {
fprintf(stderr, "Could not parse ApproachRate string `%s'\n", val);
return 1;
}
song->approach_rate = round(approach * 0x100);
return 0;
}
// SliderMultiplier (Decimal) specifies a multiplier for the slider velocity
// Default value is 1.4
if (strcasecmp(key, "SliderMultiplier") == 0) {
float slider_mult;
if (sscanf(val, "%f", &slider_mult) != 1) {
fprintf(stderr, "Could not parse SliderMultiplier string `%s'\n", val);
return 1;
}
song->slider_mult = round(slider_mult * 0x100);
return 0;
}
// SliderTickRate (Decimal) specifies how often slider ticks appear
// Default value is 1
if (strcasecmp(key, "SliderTickRate") == 0) {
float slider_tick;
if (sscanf(val, "%f", &slider_tick) != 1) {
fprintf(stderr, "Could not parse SliderTickRate string `%s'\n", val);
return 1;
}
song->slider_tick = round(slider_tick * 0x100);
return 0;
}
return 0;
}
int osu_parse_events(char *line, osu_song *song,
mdev *dev, osu_meta *meta, osu_dir *cont) {
// Events section is in CSV format
switch (line[0]) {
case '0': {
// Background image
if (strncmp(line, "0,0,\"", 5) != 0) {
fprintf(stderr, "Warning: bad background image: %s\n", line);
return 1;
}
char *fname = line + 5, *c = fname;
for (; *c; c++) {
if (*c == '"')
break;
}
if (*c != '"') {
fprintf(stderr, "Warning: background filename not quoted: %s\n", line);
return 1;
}
*c = '\0';
if (cont->fs.find(fname) == cont->fs.end()) {
printf("Allocating storage for image %s\n", fname);
uint16_t addr = meta->available_block;
song->cover_begin = addr;
char path[1024];
sprintf(path, "%s/%s", cont->dirname, fname);
addr = img_write_dither128(path, dev, addr);
song->cover_len = addr - song->cover_begin;
meta->available_block = addr;
cont->fs[fname] = make_pair(song->cover_begin, song->cover_len);
} else {
auto finfo = cont->fs[fname];
printf("Reusing storage (%x) for image %s\n", finfo.first, fname);
song->cover_begin = finfo.first;
song->cover_len = finfo.second;
}
return 0;
}
case '2': {
// Break
return 0;
}
}
return 0;
}
......@@ -262,6 +390,25 @@ int osu_parse_timing(char *line, osu_song *song,
int osu_parse_colors(char *line, osu_song *song,
mdev *dev, osu_meta *meta, osu_dir *cont) {
char *val = osu_util_str_split(line, ':'), *key = line;
if (!val) {
fprintf(stderr, "ComboColours Section: field has no value\n");
return 1;
}
if (strncasecmp(key, "Combo",5) != 0) {
return 0;
}
int n = key[6] - '1', r, g, b;
if (sscanf(val, "%d,%d,%d", &r, &g, &b) != 3) {
fprintf(stderr, "Warning: bad combo color: %s\n", val);
return 1;
}
song->combo_colors[n][0] = r;
song->combo_colors[n][1] = g;
song->combo_colors[n][2] = b;
return 0;
}
......@@ -292,3 +439,61 @@ char *osu_util_str_split(char *line, char delim) {
}
return pval;
}
void osu_check_beatmap_sanity(osu_song *bm) {
#define TEST_ENTRY_HOT(entryname) \
if (ISHOT(&(bm->entryname))) {\
fprintf(stderr, "Warning: Beatmap has no " #entryname "\n");\
}
// Check header magic
if (strncmp(bm->magic, "BEAT", 4) != 0) {
fprintf(stderr, "CRITICAL: Beatmap has invalid magic\n");
return;
}
// Check mandatory fields
TEST_ENTRY_HOT(cover_begin)
TEST_ENTRY_HOT(cover_len)
TEST_ENTRY_HOT(beatmap_begin)
TEST_ENTRY_HOT(beatmap_len)
TEST_ENTRY_HOT(audio_begin)
TEST_ENTRY_HOT(audio_len)
TEST_ENTRY_HOT(difficulty)
TEST_ENTRY_HOT(hp_drain)
TEST_ENTRY_HOT(circle_size)
TEST_ENTRY_HOT(approach_rate)
TEST_ENTRY_HOT(slider_mult)
TEST_ENTRY_HOT(slider_tick)
TEST_ENTRY_HOT(preview_blk_offset)
TEST_ENTRY_HOT(beatmap_id)
TEST_ENTRY_HOT(beatmap_setid)
TEST_ENTRY_HOT(audio_leadin)
TEST_ENTRY_HOT(stack_leniency)
TEST_ENTRY_HOT(title)
TEST_ENTRY_HOT(artist)
TEST_ENTRY_HOT(creator)
TEST_ENTRY_HOT(version)
// Check combo
TEST_ENTRY_HOT(combo_colors[0]);
bool combo_defined = true;
int ncombo = 1;
for (int i=1; i<8; i++) {
if (ISHOT(&(bm->combo_colors[i]))) {
combo_defined = false;
} else {
if (!combo_defined) {
fprintf(stderr, "Warning: extra combo color %d\n", i);
} else {
ncombo++;
}
}
}
printf("Combo colors defined: %d\n", ncombo);
printf("Sanity check completed.\n");
}
......@@ -47,5 +47,6 @@ int osu_parse_hitobjs(char *line, osu_song *song,
mdev *dev, osu_meta *meta, osu_dir *cont);
char *osu_util_str_split(char *line, char delim);
void osu_check_beatmap_sanity(osu_song *bm);
#endif /* osu_h */
......@@ -36,7 +36,7 @@ extern "C" {
int16_t approach_rate; // 34 - 35
int16_t slider_mult; // 36 - 37
int16_t slider_tick; // 38 - 39
uint8_t combo_colors[3][8]; // 40 - 63
uint8_t combo_colors[8][3]; // 40 - 63
uint32_t preview_blk_offset; // 64 - 67
uint32_t beatmap_id; // 68 - 71
uint32_t beatmap_setid; // 72 - 75
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment