Example #1
0
            internal override Event ReadEntry(BinaryReaderEx br)
            {
                EventType type = br.GetEnum32 <EventType>(br.Position + 8);

                switch (type)
                {
                case EventType.Light:
                    var light = new Event.Light(br);
                    Lights.Add(light);
                    return(light);

                case EventType.Sound:
                    var sound = new Event.Sound(br);
                    Sounds.Add(sound);
                    return(sound);

                case EventType.SFX:
                    var sfx = new Event.SFX(br);
                    SFXs.Add(sfx);
                    return(sfx);

                case EventType.WindSFX:
                    var windSFX = new Event.WindSFX(br);
                    WindSFXs.Add(windSFX);
                    return(windSFX);

                case EventType.Treasure:
                    var treasure = new Event.Treasure(br);
                    Treasures.Add(treasure);
                    return(treasure);

                case EventType.Generator:
                    var generator = new Event.Generator(br);
                    Generators.Add(generator);
                    return(generator);

                case EventType.Message:
                    var message = new Event.Message(br);
                    Messages.Add(message);
                    return(message);

                case EventType.ObjAct:
                    var objAct = new Event.ObjAct(br);
                    ObjActs.Add(objAct);
                    return(objAct);

                case EventType.SpawnPoint:
                    var spawnPoint = new Event.SpawnPoint(br);
                    SpawnPoints.Add(spawnPoint);
                    return(spawnPoint);

                case EventType.MapOffset:
                    var mapOffset = new Event.MapOffset(br);
                    MapOffsets.Add(mapOffset);
                    return(mapOffset);

                case EventType.Navmesh:
                    var navmesh = new Event.Navmesh(br);
                    Navmeshes.Add(navmesh);
                    return(navmesh);

                case EventType.Environment:
                    var environment = new Event.Environment(br);
                    Environments.Add(environment);
                    return(environment);

                case EventType.PseudoMultiplayer:
                    var pseudoMultiplayer = new Event.PseudoMultiplayer(br);
                    PseudoMultiplayers.Add(pseudoMultiplayer);
                    return(pseudoMultiplayer);

                default:
                    throw new NotImplementedException($"Unsupported event type: {type}");
                }
            }
Example #2
0
        public static OJN Decode(string filename)
        {
            if (!File.Exists(filename))
            {
                throw new FileNotFoundException("OJN File not found", filename);
            }

            if (!Check(filename))
            {
                throw new FormatException("Invalid O2Jam Chart.");
            }

            var ojn = DecodeHeader(filename);

            using (var stream = new BufferStream(filename))
            {
                ojn.Events = new Dictionary <OJN.Difficulty, Event[]>();
                foreach (var difficulty in Enum.GetValues(typeof(OJN.Difficulty)) as OJN.Difficulty[])
                {
                    if (difficulty == OJN.Difficulty.MX)
                    {
                        continue;
                    }

                    var events = new List <Event>();
                    int offset = ojn.BlockOffset[difficulty];
                    int count  = ojn.BlockCount[difficulty];

                    stream.Seek(offset, SeekOrigin.Begin);
                    for (int block = 0; block < count; block++)
                    {
                        var ev = new Event
                        {
                            Measure   = stream.ReadInt32(),
                            LaneIndex = stream.ReadInt16(),
                            Tempo     = stream.ReadInt16()
                        };

                        // We don't convert the lane index directly to channel, we keep them both
                        // This allow the Event class to be usable for various number of channels (e.g: 3key / 5key / 10key)
                        // The decoder is responsible to convert this index into correct channel enum
                        // Lane Index then still preserved, this might needed in certain cases such as encoding the chart back to the file or identify the lane index of BGM.
                        // While channel may make judgement and rendering process easier by distinguish event types between playable and background events
                        ev.Channel = ev.LaneIndex > (int)Event.ChannelType.Note7 ? Event.ChannelType.BGM : (Event.ChannelType)ev.LaneIndex;

                        for (int i = 0; i < ev.Tempo; i++)
                        {
                            ev.Beat = (int)(i * (192f / ev.Tempo));
                            ev.Cell = i;

                            if (ev.Channel == Event.ChannelType.BPM ||
                                ev.Channel == Event.ChannelType.Measurement)
                            {
                                var time = new Event.Time(ev)
                                {
                                    Value = stream.ReadSingle()
                                };

                                if (time.Value != 0)
                                {
                                    events.Add(time);
                                }
                            }
                            else
                            {
                                int refId = stream.ReadInt16();
                                if ((int)ev.Channel < 0 || refId <= 0)
                                {
                                    stream.Seek(2, SeekOrigin.Current);
                                    continue;
                                }

                                int audio = stream.ReadByte();
                                int flag  = stream.ReadByte();
                                var type  = (Event.SignatureType)flag;

                                float volume = ((audio >> 4) & 0x0F);
                                volume = volume == 0 ? 100 : ((volume / 16f) * 100f);

                                float pan = (audio & 0x0F);
                                if (pan == 0)
                                {
                                    pan = 8;
                                }
                                pan = ((pan - 8) / 8f) * 100;

                                int id     = (refId - 1) + (flag % 8 > 3 ? 1000 : 0);
                                var sample = new Event.Sound(ev)
                                {
                                    Id        = id,
                                    Signature = type,
                                    Pan       = pan,
                                    Volume    = volume
                                };


                                events.Add(sample);
                            }
                        }
                    }

                    // Long Note Pairing
                    foreach (var ev in events.FindAll((e) => e is Event.Sound && e.Signature == Event.SignatureType.Hold))
                    {
                        var sln = ev as Event.Sound;
                        var eln = events.Find((e) =>
                        {
                            var se = e as Event.Sound;
                            return(se != null &&
                                   se.Pair == null &&
                                   se.Signature == Event.SignatureType.Release &&
                                   se.Channel == sln.Channel);
                        }) as Event.Sound;

                        if (eln != null)
                        {
                            sln.Pair = eln;
                            eln.Pair = sln;
                        }
                    }

                    // Clean up
                    events.RemoveAll((e) => {
                        var se = e as Event.Sound;
                        return(se != null && ((se.Signature == Event.SignatureType.Hold && se.Pair == null) /*|| se.Signature == Event.SignatureType.Release*/));
                    });

                    // Sort event based on offset
                    events.Sort((a, b) => a.Offset.CompareTo(b.Offset));

                    // Calculate Timestamp
                    var timeEvents = events.FindAll((e) => e.Channel == Event.ChannelType.BPM);
                    for (int i = 0; i < events.Count; i++)
                    {
                        var ev = events[i] as Event.Sound;
                        if (ev == null)
                        {
                            continue;
                        }

                        double bpm         = ojn.BPM;
                        double timeOffset  = 0;
                        double bpmPosition = 0;
                        foreach (var time in timeEvents)
                        {
                            if (time.Offset < ev.Offset)
                            {
                                timeOffset += ((time.Offset - bpmPosition) / (192f / 4f)) * (60f / bpm);

                                bpm         = ((Event.Time)time).Value;
                                bpmPosition = time.Offset;
                            }
                            else if (time.Offset > ev.Offset)
                            {
                                break;
                            }
                        }

                        var timestamp = TimeSpan.FromSeconds(timeOffset + (((ev.Offset - bpmPosition) / (192f / 4f)) * (60f / bpm)));;
                        events[i] = new Event.Sound(ev, timestamp);
                    }

                    ojn.Events[difficulty] = events.ToArray();
                }

                return(ojn);
            }
        }