forum

.osu file format general questions;documentation out of date

posted
Total Posts
5
Topic Starter
Yazzn
I'm currently working on a simple open-source c++ parser for osu! beatmaps.

From what I could gather by reverse engineering the wiki documentation of the .osu file format seems to be out of date.
I'm not an expert in .NET reverse engineering but from what I have seen default values should be:
public float DifficultyApproachRate = 5f;
public float DifficultyCircleSize = 5f;
public float DifficultyHpDrainRate = 5f;
public float DifficultyOverall = 5f;
public double DifficultySliderMultiplier = 1.4;
public double DifficultySliderTickRate = 1.0;

I also have some questions and I'd very happy about any help I can get.
Is osu actually checking the version number? I think I've seen osu! checking created-at date of the beat map somewhere rather than using the osu file format versioning. Why is that? Can anyone provide more information about handling different beatmap versions.
Is there a some kind of changelog for the osu format? I can imagine since the rating update the old values are not meaning the same as the ones from a new osu file and are recalculated behind the scenes.

Here's what I have done so far:
https://gist.github.com/anonymous/dcc36a1d2fecb8c93759

I've written all the code in notepad++ and haven't compiled it once so there might be some errors in it..
chong601

Yazzn wrote:

I'm currently working on a simple open-source c++ parser for osu! beatmaps.

From what I could gather by reverse engineering the wiki documentation of the .osu file format seems to be out of date.
I'm not an expert in .NET reverse engineering but from what I have seen default values should be:
public float DifficultyApproachRate = 5f;
public float DifficultyCircleSize = 5f;
public float DifficultyHpDrainRate = 5f;
public float DifficultyOverall = 5f;
public double DifficultySliderMultiplier = 1.4;
public double DifficultySliderTickRate = 1.0;

I also have some questions and I'd very happy about any help I can get.
Is osu actually checking the version number? I think I've seen osu! checking created-at date of the beat map somewhere rather than using the osu file format versioning. Why is that? Can anyone provide more information about handling different beatmap versions.
Is there a some kind of changelog for the osu format? I can imagine since the rating update the old values are not meaning the same as the ones from a new osu file and are recalculated behind the scenes.

Here's what I have done so far:
https://gist.github.com/anonymous/dcc36a1d2fecb8c93759

I've written all the code in notepad++ and haven't compiled it once so there might be some errors in it..
You can take a look at euphyy's opsu! source code here (written in Java)... its quite complete (apart from some weird slider issues due to the way opsu works xD)...
I have tested about 60 old and new maps in opsu! and none of them have caught any parser errors... so I assume that the parser is complete (for now)
Nathanael

Yazzn wrote:

From what I could gather by reverse engineering the wiki documentation of the .osu file format seems to be out of date.
I'm not an expert in .NET reverse engineering but from what I have seen default values should be:
public float DifficultyApproachRate = 5f;
public float DifficultyCircleSize = 5f;
public float DifficultyHpDrainRate = 5f;
public float DifficultyOverall = 5f;
public double DifficultySliderMultiplier = 1.4;
public double DifficultySliderTickRate = 1.0;
Wiki article has been updated. You can also help out on editing at wiki.
JudgeTheDude
Took your code, compiled it, corrected some errors and added the #includes

Lots of the structs didn't have a ";" at the end, and the last class too. And the variable "osufile" in the main should have been "beatmap_file"

Code
#include <iostream>
#include <vector>
#include <fstream>

/*

namespace osu.GameplayElements.HitObjects.Osu
{
internal enum CurveTypes
{
Catmull,
Bezier,
Linear,
PerfectCurve,
}
}

namespace osu.GameplayElements.HitObjects
{
[Flags]
public enum HitObjectType
{
Normal = 1,
Slider = 2,
NewCombo = 4,
NormalNewCombo = NewCombo | Normal,
SliderNewCombo = NewCombo | Slider,
Spinner = 8,
ColourHax = 112,
Hold = 128,
ManiaLong = Hold,
}
}

namespace osu.GameplayElements.HitObjects
{
[Flags]
public enum HitObjectSoundType
{
None = 0,
Normal = 1,
Whistle = 2,
Finish = 4,
Clap = 8,
}
}

namespace osu.GameplayElements.Events
{
internal enum EventTypes
{
Background,
Video,
Break,
Colour,
Sprite,
Sample,
Animation,
}
}

namespace osu.GameplayElements.Events
{
internal enum StoryLayer
{
Background,
Fail,
Pass,
Foreground,
}
}

namespace osu.GameplayElements.Events
{
internal enum TriggerGroup
{
None,
HitSound,
Passing,
Failing,
HitObjectHit,
}
}

namespace osu.GameplayElements
{
internal enum FileSection
{
Unknown = 0,
General = 1,
Colours = 2,
Editor = 4,
Metadata = 8,
TimingPoints = 16,
Events = 32,
HitObjects = 64,
Difficulty = 128,
Variables = 256,
All = 511,
}
}


namespace osu.GameplayElements.Beatmaps
{
public abstract class BeatmapBase : MarshalByRefObject
{
public float DifficultyApproachRate = 5f;
public float DifficultyCircleSize = 5f;
public float DifficultyHpDrainRate = 5f;
public float DifficultyOverall = 5f;
public double DifficultySliderMultiplier = 1.4;
public double DifficultySliderTickRate = 1.0;
public string Artist = string.Empty;
public string Tags = string.Empty;
public string Title = string.Empty;
public string ArtistUnicode;
public string TitleUnicode;
}
}


namespace osu.Graphics.Sprites
{
internal enum Origins
{
TopLeft,
Centre,
CentreLeft,
TopRight,
BottomCentre,
TopCentre,
Custom,
CentreRight,
BottomLeft,
BottomRight,
}
}


namespace osu.Graphics.Sprites
{
[Flags]
internal enum TransformationType
{
None = 0,
Movement = 1,
Fade = 2,
Scale = 4,
Rotation = 8,
Colour = 16,
ParameterFlipHorizontal = 32,
ParameterFlipVertical = 64,
MovementX = 128,
MovementY = 256,
VectorScale = 512,
ParameterAdditive = 1024,
ClippingWidth = 2048,
ClippingHeight = 4096,
}
}


namespace osu.Graphics.Sprites
{
internal enum LoopTypes
{
LoopForever,
LoopOnce,
}
}


namespace osu_common
{
public enum PlayModes
{
Osu,
Taiko,
CatchTheBeat,
OsuMania,
}
}


namespace osu.GameModes.Edit.AiMod
{
public enum BeatmapDifficulty
{
Easy,
Normal,
Hard,
Insane,
Expert,
}
}

namespace osu.Audio
{
internal enum SampleSet
{
All = -1,
None = 0,
Normal = 1,
Soft = 2,
Drum = 3,
}
}


*/

enum modes : int {
OSU = 0,
TAIKO = 1,
CTB = 2,
OSU_MANIA = 3
};

struct audio_v12 {
std::string filename;

int lead_in;
int preview_time;
};

struct general_v12 {
modes mode;
audio_v12 audio;

std::string sample_set;

float stack_leniency;

bool countdown;
bool letterbox_in_breaks;
bool widescreen_storyboard;
};

struct editor_v12 {
std::vector<int> bookmarks;

float distance_spacing;

int beat_divisor;
int grid_size;
int timeline_zoom;
};

enum difficulties {
easy,
normal,
hard,
insane,
expert
};

struct metadata_v12 {
std::string title;
std::string unicode_title;
std::string artist;
std::string unicode_artist;
std::string tags;
std::string creator;

int beatmap_id;
int beatmap_set_id;

difficulties difficulty;
};

struct difficulty_v12 {
float circle_size = 5.f;
float hp_drain_rate = 5.f;
float overall_difficulty = 5.f;
float approach_rate = 5.f;
float slider_multiplier = 1.4f;
float slider_tick_rate = 1.f;
};

struct timing_point_v12 {
float ms_per_beat;

int offset;
int meter;
int sample_type;
int volume;

bool kiai_mode;
bool inherited;
};

union argb_colour {
struct {
std::uint8_t blue;
std::uint8_t green;
std::uint8_t red;
std::uint8_t alpha;
} components;
std::uint32_t value;
};

struct colours_v12 {
std::vector<argb_colour> combos;
argb_colour slider_border;
argb_colour slider_track_override;
};

struct hit_object_v12 {
int x;
int y;
int time;
int type;
int hit_sound;

std::string addition = "0:0:0:0:";
};

struct hit_circle_v12 : public hit_object_v12 { };

struct position {
int x;
int y;
};

struct slider_v12 : public hit_object_v12 {
std::uint8_t type;
std::vector<position> curve;
bool repeat;
int pixel_length;
// ... unknown
};

struct spinner_v12 : public hit_object_v12 {
int end_time;
};

class beatmap_v12 {
int version;

general_v12 general;
editor_v12 editor;
metadata_v12 metadata;
difficulty_v12 difficulty;
colours_v12 colours;

std::vector<timing_point_v12> timing_point;
std::vector<hit_object_v12> hit_objects;

friend std::istream& operator>>(std::istream& iss, beatmap_v12& data) {
std::string line;
beatmap_v12 tmp;
if (std::getline(iss, line)) {
tmp.version = std::stoi(line.substr(line.find_last_of(' ')));
if (tmp.version != 12) {
std::cerr << "Wrong version.";
}
//while (std::getline(iss, ))

data = std::move(tmp);
}
return iss;
}
};

int main() {
std::string filename;
std::getline(std::cin, filename);
std::ifstream beatmap_file(filename);
if (!beatmap_file.is_open()) {
std::cerr << "Failed to open " << filename << '.' << std::endl;
}
beatmap_v12 beatmap;
beatmap_file >> beatmap;
if (!beatmap_file.bad()) {
std::cerr << "Failed to parse " << filename << '.' << std::endl;
}
}
Topic Starter
Yazzn
I wrote the code in Notepad++ and haven't compiled it myself yet. Didn't have much time to work on it recently.
Please sign in to reply.

New reply