osu-ds
osu-ds copied to clipboard
Help with creating beatmap files
hi there :) im very fascinated with the fact this even exists in the first place and would like some help getting this up and running
im able to compile a .nds and .elf(?) file just fine, and am able to open the rom on a flashcart. problem is im not sure how exactly to get any sort of maps to be compiled with this, or if theres anything im missing as the rom just opens to a frame counter. im very very new to compiling anything at all and have no clue what im doing so a bit of patience and guidance would be much appreciated
thank you!
Hey there, unfortunately this was written too many years ago for me to be able to provide any useful guidance. I seem to remember that the port required beatmaps to be exported into a custom format. That used to be built into the desktop editor; it's very likely that that code has since been culled (considering it's been 13 years!). Sorry that I'm not able to help any further!
I'll take a second look through git history and see if i can find the code for this. Last time I checked I did confirm that it doesn't seem to be present anymore in release builds, but I will provide it if I can locate it.
wow, crazy that you’re still willing to give light into this project :’) hope all goes well
I'm not sure how far you want to go from a development perspective, but here's the actual conversion code:
namespace osu.Helpers
{
internal class OsuDSHelper
{
internal const int DEGRESS_IN_CIRCLE = 32768;
internal const byte VERSION_ODS = 1;
internal static void Export()
{
Player p = new Player();
p.Initialize();
HitObjectManager h = p.hitObjectManager;
if (!Directory.Exists("osuds"))
Directory.CreateDirectory("osuds");
string folder = BeatmapManager.Current.Artist + " - " + BeatmapManager.Current.Title;
if (!Directory.Exists("osuds/" + folder))
Directory.CreateDirectory("osuds/" + folder);
BinaryWriter bw = new BinaryWriter(
File.OpenWrite("osuds/" + folder + "/" +
Path.GetFileNameWithoutExtension(BeatmapManager.Current.Filename) + ".ods"));
bw.Write(Encoding.ASCII.GetBytes("ODS"));
bw.Write(VERSION_ODS);
//metadata
WriteVarString(bw, BeatmapManager.Current.Title);
WriteVarString(bw, BeatmapManager.Current.Artist);
WriteVarString(bw, BeatmapManager.Current.Creator);
WriteVarString(bw, BeatmapManager.Current.Version);
WriteVarString(bw, Path.GetFileNameWithoutExtension(BeatmapManager.Current.AudioFilename) + ".raw");
bw.Write((byte)Math.Round(BeatmapManager.Current.DifficultyHpDrainRate));
bw.Write((byte)Math.Round(BeatmapManager.Current.DifficultyCircleSize));
bw.Write((byte)Math.Round(BeatmapManager.Current.DifficultyOverall));
bw.Write((float) BeatmapManager.Current.DifficultySliderMultiplier);
bw.Write((float) BeatmapManager.Current.DifficultySliderTickRate);
bw.Write((float) p.Ruleset.HpBar.HpDropRate);
bw.Write((byte) BeatmapManager.Current.DifficultyPeppyStars);
bw.Write((float) BeatmapManager.Current.DifficultyEyupStars);
//timing points
WriteVarLength(bw, BeatmapManager.Current.ControlPoints.Count);
double realBeatLength = BeatmapManager.Current.ControlPoints[0].beatLength;
foreach (ControlPoint t in BeatmapManager.Current.ControlPoints)
{
bw.Write((int) t.offset);
if (t.beatLength > 0)
{
bw.Write((float) t.beatLength);
realBeatLength = t.beatLength;
}
else //inherited section
{
bw.Write((float) (realBeatLength*(-100/t.beatLength)));
}
bw.Write((byte) t.sampleSet);
}
//breaks
WriteVarLength(bw, h.eventManager.eventBreaks.Count);
foreach (EventBreak e in h.eventManager.eventBreaks)
{
bw.Write(e.StartTime);
bw.Write(e.EndTime);
}
/*
//colour changes
WriteVarLength(bw, EventManager.backgroundTexture.Transformations.Count);
foreach (Transformation t in EventManager.backgroundTexture.Transformations)
{
bw.Write(t.Time1 + 100);
bw.Write(RGB15(t.EndColour));
}
*/
//hitobjects
WriteVarLength(bw, h.hitObjects.Count);
foreach (HitObject o in h.hitObjects)
{
bw.Write(o.StartTime);
bw.Write((byte) o.Type);
bw.Write((short) o.Position.X);
bw.Write((short) o.Position.Y);
bw.Write((byte) o.SoundType);
if (o.IsType(HitObjectType.Spinner))
bw.Write(o.EndTime);
else if (o.IsType(HitObjectType.Slider))
{
SliderOsu s = (SliderOsu) o;
bw.Write((short) s.SegmentCount);
//time to travel one length of the slider
bw.Write((int) (AudioEngine.beatLengthAt(o.StartTime)*s.SpatialLength/
(100*BeatmapManager.Current.DifficultySliderMultiplier)));
/*
List<Vector2> sliderpoints = new List<Vector2>();
switch (s.CurveType)
{
case CurveTypes.Bezier:
sliderpoints = Bezier(s.sliderCurvePoints, 15);
break;
case CurveTypes.Catmull:
sliderpoints = Catmull(s.sliderCurvePoints, 20);
break;
case CurveTypes.Linear:
sliderpoints = Linear(s.sliderCurvePoints, 15);
break;
}
Trim(ref sliderpoints, (int)s.sliderLength);
*/
List<Vector2> sliderpoints = GenerateSlider(s.sliderCurveSmoothLines);
/*
Form f = new Form();
f.Size = new Size(640, 480);
f.Text = o.StartTime.ToString();
f.Show();
foreach (Vector2 v in sliderpoints)
{
Label l = new Label();
l.Location = new Point((int) v.X, (int) v.Y);
l.BackColor = System.Drawing.Color.Red;
l.Size = new Size(50, 50);
f.Controls.Add(l);
}
*/
WriteVarLength(bw, sliderpoints.Count);
int angle = 0;
for (int i = 0; i < sliderpoints.Count; i++)
{
bw.Write((short) sliderpoints[i].X);
bw.Write((short) sliderpoints[i].Y);
if (i + 1 < sliderpoints.Count)
angle = CalculateAngle(sliderpoints[i], sliderpoints[i + 1]);
bw.Write(angle);
}
List<Vector2> tickpoints = CalculateTicks(sliderpoints, (int) s.SpatialLength);
WriteVarLength(bw, tickpoints.Count);
foreach (Vector2 v in tickpoints)
{
bw.Write((short) v.X);
bw.Write((short) v.Y);
}
}
}
bw.Close();
p.Dispose();
//TODO: convert music file to raw 22050Hz 8 bit signed mono
string newAudioFilename = "osuds/" + folder + "/" + Path.GetFileName(BeatmapManager.Current.AudioFilename);
if (!File.Exists(newAudioFilename))
File.Copy(BeatmapManager.Current.AudioFilename, newAudioFilename);
NotificationManager.ShowMessage("Exported!");
}
#region Helper functions
internal static short RGB15(Color color)
{
return (short) ((color.R >> 3) + ((color.G >> 3) << 5) + ((color.B >> 3) << 10));
}
internal static void WriteVarString(BinaryWriter bw, string str)
{
WriteVarLength(bw, str.Length);
bw.Write(Encoding.ASCII.GetBytes(str));
}
internal static void WriteVarLength(BinaryWriter bw, int length)
{
byte temp = (byte) (length & 0x7F);
while ((length >>= 7) > 0)
{
temp |= 0x80;
bw.Write(temp);
temp = (byte) (length & 0x7F);
}
bw.Write(temp);
}
/*
internal static List<Vector2> Linear(List<Vector2> sliderCurvePoints, int dlevel)
{
List<Vector2> output = new List<Vector2>();
for (int i = 0; i < sliderCurvePoints.Count - 1; i++)
{
int points = (int)(Length(sliderCurvePoints[i], sliderCurvePoints[i + 1]) / dlevel);
for (int j = 0; j < points; j++)
{
output.Add(Lerp(sliderCurvePoints[i], sliderCurvePoints[i + 1], (float)j / points));
}
}
return output;
}
internal static List<Vector2> Bezier(List<Vector2> sliderCurvePoints, int dlevel)
{
int lastIndex = 0;
List<Vector2> output = new List<Vector2>();
for (int i = 0; i < sliderCurvePoints.Count; i++)
{
if ((i > 0 && sliderCurvePoints[i] == sliderCurvePoints[i - 1]) || i == sliderCurvePoints.Count - 1)
{
List<Vector2> thisLength = sliderCurvePoints.GetRange(lastIndex, i - lastIndex + 1);
List<Vector2> points = CreateBezier(thisLength, dlevel);
output.AddRange(points);
lastIndex = i;
}
}
return output;
}
internal static List<Vector2> CreateBezier(List<Vector2> input, int dlevel)
{
Vector2[] working = new Vector2[input.Count];
List<Vector2> output = new List<Vector2>();
int points = dlevel * input.Count;
for (int iteration = 0; iteration < points; iteration++)
{
for (int i = 0; i < input.Count; i++)
working[i] = input[i];
for (int level = 0; level < input.Count; level++)
for (int i = 0; i < input.Count - level - 1; i++)
Lerp(working[i], working[i + 1], (float)iteration / points, out working[i]);
output.Add(working[0]);
}
return output;
}
internal static List<Vector2> Catmull(List<Vector2> sliderCurvePoints, int dlevel)
{
List<Vector2> output = new List<Vector2>();
for (int j = 0; j < sliderCurvePoints.Count - 1; j++)
{
Vector2 v1 = (j - 1 >= 0 ? sliderCurvePoints[j - 1] : sliderCurvePoints[j]);
Vector2 v2 = sliderCurvePoints[j];
Vector2 v3 = (j + 1 < sliderCurvePoints.Count
? sliderCurvePoints[j + 1]
: v2 + (v2 - v1));
Vector2 v4 = (j + 2 < sliderCurvePoints.Count
? sliderCurvePoints[j + 2]
: v3 + (v3 - v2));
for (int k = 0; k < dlevel; k++)
output.Add(CreateCatmull(v1, v2, v3, v4, (float)k / dlevel));
}
return output;
}
internal static Vector2 CreateCatmull(Vector2 value1, Vector2 value2, Vector2 value3, Vector2 value4, float amount)
{
Vector2 p;
float num = amount * amount;
float num2 = amount * num;
p.X = (short)(0.5f * ((((2f * value2.X) + ((-value1.X + value3.X) * amount)) + (((((2f * value1.X) - (5f * value2.X)) + (4f * value3.X)) - value4.X) * num)) + ((((-value1.X + (3f * value2.X)) - (3f * value3.X)) + value4.X) * num2)));
p.Y = (short)(0.5f * ((((2f * value2.Y) + ((-value1.Y + value3.Y) * amount)) + (((((2f * value1.Y) - (5f * value2.Y)) + (4f * value3.Y)) - value4.Y) * num)) + ((((-value1.Y + (3f * value2.Y)) - (3f * value3.Y)) + value4.Y) * num2)));
return p;
}
internal static void Trim(ref List<Vector2> points, int length)
{
double total = 0;
int i;
for (i = 0; total < length && i < points.Count - 1; i++)
total += Length(points[i], points[i + 1]);
if (total <= length)
return;
Vector2 lastpoint = points[i];
points.RemoveRange(i, points.Count - i);
double smalltotal = total - Length(points[i - 1], lastpoint);
points.Add(Lerp(points[i - 1], lastpoint, (float)((length - smalltotal) / (total - smalltotal))));
}
*/
internal static List<Vector2> GenerateSlider(List<Line> lines)
{
const double threshold = 10;
List<Vector2> temp = new List<Vector2>();
temp.Add(lines[0].p1);
double total = 0;
for (int i = 0; i < lines.Count - 1; i++)
{
double l = Length(lines[i].p1, lines[i + 1].p1);
total += l;
if (total > threshold)
{
temp.Add(Lerp(lines[i].p1, lines[i + 1].p1, (float) ((l + threshold - total)/l)));
total -= threshold;
}
}
temp.Add(lines[lines.Count - 1].p2);
return temp;
}
internal static List<Vector2> CalculateTicks(List<Vector2> points, int sliderlength)
{
List<Vector2> ticks = new List<Vector2>();
int length =
(int)
(BeatmapManager.Current.DifficultySliderMultiplier*100/BeatmapManager.Current.DifficultySliderTickRate);
int maxpoints = (sliderlength - 1)/length;
double total = 0;
for (int i = 0; ticks.Count < maxpoints && i < points.Count - 1; i++)
{
total += Length(points[i], points[i + 1]);
if (total >= length)
{
ticks.Add(points[i + 1]); //can't be bothered lerping, the difference should be minimal anyway
total -= length;
}
}
return ticks;
}
internal static int CalculateAngle(Vector2 a, Vector2 b)
{
return (int) (DEGRESS_IN_CIRCLE*Math.Atan2(b.Y - a.Y, b.X - a.X)/(2*Math.PI));
}
internal static Vector2 Lerp(Vector2 a, Vector2 b, float amount)
{
Vector2 v;
v.X = (short) (a.X + (b.X - a.X)*amount);
v.Y = (short) (a.Y + (b.Y - a.Y)*amount);
return v;
}
internal static void Lerp(Vector2 a, Vector2 b, float amount, out Vector2 output)
{
output = Lerp(a, b, amount);
}
internal static double Length(Vector2 a, Vector2 b)
{
return Math.Sqrt(((b.X - a.X)*(b.X - a.X) + (b.Y - a.Y)*(b.Y - a.Y)));
}
#endregion
}
}
Note that you'll obviously need some developer experience to make this work. I can probably get a working converter compiled in some way, but I'm posting this code in case someone else wants to take on that task instead.
Personal note: This file was last updated on 0bd6907f8c9ff3bc57981b7873204ae77bfa1f7f.
as i’m definitely not the one to make this work, if it’s of your interest and you’re up for the task with everything you’re handling at the moment id love to see a working converter to be used in the future, however if you are too busy i do understand and i can try to see if i can find someone else able to make this possibly work :) that’s amazing how you’re able to find stuff for a project this old and niche tho serious props to you