HLS refers to two binary formats that both define a list of IDs, known as a hitlist. One format is a Pascal string with a 4-byte, little-endian length, representing a comma-seperated list of decimal values, or decimal ranges (e.g. "1025-1035"), succeeded by a single LF newline.
Inheritance: IDisposable
        /// <summary>
        /// Sets a track for this Subroutine.
        /// </summary>
        /// <param name="Index">The ID of a Hitlist to load.</param>
        /// <returns>The ID of the track that was set.</returns>
        private uint SetTrack(uint Index)
        {
            m_Hitlist = FileManager.GetHLS(Index);
            m_Track = FileManager.GetTRK(m_Hitlist.SoundsAndHitlists[(int)Index]);
            m_SoundID = m_Track.SoundID;

            return m_Hitlist.SoundsAndHitlists[(int)Index];
        }
        /// <summary>
        /// This runs one HIT instruction, and should be run once every frame for every subroutine.
        /// </summary>
        /// <returns>True if still running, otherwise yields.</returns>
        public override IEnumerable<object> process()
        {
            byte Var1 = 0, Var2 = 0; //Used by waiteq, waitne
            byte Datafield = 0; //Used by set/getsrcdatafield.
            byte TrackID = 0, Dest = 0;
            int Src = 0;

            if (!SimpleMode)
            {
                while (true)
                {
                    byte Opcode = ReadByte();

                    switch (Opcode)
                    {
                        case 0x2: //note_on - play a note, whose ID resides in the specified variable.
                            Dest = ReadByte();

                            if (m_SoundID == 0)
                                m_SoundID = m_Track.SoundID;

                            ISoundCodec Snd = FileManager.GetSound(m_SoundID);

                            if (Snd != null)
                            {
                                m_Notes.Add(new HITNoteEntry(m_SoundID, Snd));

                                SetVariable(Dest, m_Notes.Count - 1);
                                SoundPlayer.PlaySound(Snd.DecompressedWav(), Snd.GetSampleRate());
                            }
                            else
                                Debug.WriteLine("SubRoutine.cs: Couldn't find sound " + m_SoundID);

                            break;
                        case 0x4: //loadb - sign-extend a 1-byte constant to 4 bytes and write to a variable.
                            Dest = ReadByte();
                            var Constant = (sbyte)ReadByte();
                            SetVariable(Dest, Constant);

                            m_ZeroFlag = (Dest == 0);
                            m_SignFlag = (Dest < 0); //TODO: When to set this to false again?

                            break;
                        case 0x5: //loadl - write a 4-byte constant to a variable.
                            Dest = ReadByte();
                            Src = ReadInt32();

                            SetVariable(Dest, Src);

                            break;
                        case 0x6: //set/settt - copy the contents of one variable into another.
                            Dest = ReadByte();
                            Src = GetVariable(ReadByte());

                            SetVariable(Dest, Src);
                            m_ZeroFlag = (Dest == 0);
                            m_SignFlag = (Dest < 0); //TODO: When to set this to false again?

                            break;
                        case 0x7: //call - push the instruction pointer and jump to the given address.
                            m_Stack.Push(m_InstCounter);
                            m_InstCounter = (uint)ReadInt32();

                            break;
                        case 0x8: //return - kill this thread.
                            YieldComplete();
                            yield return false;
                            break;
                        case 0x9: //wait - wait for a length of time in milliseconds, specified by a variable.
                            Src = ReadByte();

                            if (m_WaitRemaining == -1) m_WaitRemaining = m_Registers[(byte)Src];
                            m_WaitRemaining -= 16; //assuming tick rate is 60 times a second
                            if (m_WaitRemaining > 0)
                            {
                                m_InstCounter -= 2;
                                yield return false;
                            }
                            else
                            {
                                m_WaitRemaining = -1;
                                yield return false;
                            }

                            break;
                        case 0xb: //wait_samp -  wait for the previously selected note to finish playing.
                            break;
                        case 0xc: //end - return from this function; pop the instruction pointer from the stack and jump.
                            YieldComplete(); //Not sure if this is correct?
                            yield return true;
                            break;
                        case 0xd: //jump - jump to a given address.
                            byte JmpAddress = ReadByte();

                            if (JmpAddress > 15)
                            {
                                m_InstCounter--;
                                m_InstCounter = ReadUInt32();
                            }
                            else
                            {
                                m_InstCounter = (uint)GetVariable(JmpAddress);
                                if (ReadByte() == 0) m_InstCounter += 2;
                                else m_InstCounter--;
                            }

                            break;
                        case 0xe: //test - examine the variable and set the flags.
                            Dest = ReadByte();

                            m_ZeroFlag = (Dest == 0);
                            m_SignFlag = (Dest < 0);

                            break;
                        case 0xf: //nop - no operation.
                            break;
                        case 0x10: //add - increment a "dest" variable by a "src" variable
                            m_Registers[ReadByte()] += m_Registers[ReadByte()];
                            m_ZeroFlag = (Dest == 0);
                            m_SignFlag = (Dest < 0);

                            break;
                        case 0x11: //sub - decrement a "dest" variable by a "src" variable.
                            m_Registers[ReadByte()] -= m_Registers[ReadByte()];
                            m_ZeroFlag = (Dest == 0);
                            m_SignFlag = (Dest < 0);

                            break;
                        case 0x12: //div - divide a "dest" variable by a "src" variable.
                            m_Registers[ReadByte()] /= m_Registers[ReadByte()];
                            m_ZeroFlag = (Dest == 0);
                            m_SignFlag = (Dest < 0);

                            break;
                        case 0x13: //mul - multiply a "dest" variable by a "src" variable.
                            m_Registers[ReadByte()] *= m_Registers[ReadByte()];
                            m_ZeroFlag = (Dest == 0);
                            m_SignFlag = (Dest < 0);

                            break;
                        case 0x14: //cmp - compare two variables and set the flags.
                            m_Registers[ReadByte()] -= m_Registers[ReadByte()];
                            m_ZeroFlag = (Dest == 0);
                            m_SignFlag = (Dest < 0);

                            break;
                        case 0x18: //rand - generate a random number between "low" and "high" variables, inclusive, and store
                                   //the result in the "dest" variable.
                            SetVariable(ReadByte(), m_Rand.Next((int)ReadByte(), (int)ReadByte()));
                            break;
                        case 0x20: //loop - jump back to the loop point (start of track subroutine by default).
                            if (m_LoopPoint != 0)
                                m_InstCounter = m_LoopPoint;
                            else
                                m_InstCounter = Address;

                            break;
                        case 0x021: //set_loop - set the loop point to the current position.
                            m_LoopPoint = m_InstCounter;

                            break;
                        case 0x27: //smart_choose - Set the specified variable to a random entry from the selected hitlist.
                            Dest = ReadByte();
                            int Max = m_Hitlist.SoundsAndHitlists.Count;

                            SetVariable(Dest, (int)m_Hitlist.SoundsAndHitlists[m_Rand.Next(Max)]);

                            break;
                        case 0x2d: //max - find the higher of a "dest" variable and a "src" constant and store the result
                                   //in the variable.

                            Dest = ReadByte();
                            Src = ReadInt32();

                            if (Src > Dest)
                                SetVariable(Dest, Src);

                            break;
                        case 0x32: //play_trk - play a track (by sending it the kSndobPlay event), whose ID resides in the
                                   //specified variable.

                            TrackID = ReadByte();

                            if (HitVM.IsInitialized)
                                HitVM.PlayTrack((uint)GetVariable(TrackID));

                            break;
                        case 0x33: //kill_trk - kill a track (by sending it the kSndobKill event), whose ID resides in the
                                   //specified variable.

                            TrackID = ReadByte();

                            if (HitVM.IsInitialized)
                                HitVM.KillTrack((uint)GetVariable(TrackID));

                            break;
                        case 0x3a: //test1 - unknown
                            break;
                        case 0x3b: //test2 - unknown
                            break;
                        case 0x3c: //test3 - unknown
                            break;
                        case 0x3d: //test4 - unknown
                            break;
                        case 0x3e: //ifeq - if the zero flag is set,  jump to the given address.
                            Src = ReadInt32();

                            if (m_ZeroFlag)
                                m_InstCounter = (uint)Src;

                            break;
                        case 0x3f: //ifne - if the zero flag is not set, jump to the given address.
                            Src = ReadInt32();

                            if (!m_ZeroFlag)
                                m_InstCounter = (uint)Src;

                            break;
                        case 0x40: //ifgt - if the sign flag is not set and the zero flag is not set, jump to the given address.
                            Src = ReadInt32();

                            if (!m_ZeroFlag && !m_SignFlag)
                                m_InstCounter = (uint)Src;

                            break;
                        case 0x41: //iflt - if the sign flag is set, jump to the given address.
                            Src = ReadInt32();

                            if (m_SignFlag)
                                m_InstCounter = (uint)Src;

                            break;
                        case 0x42: //ifge - if the sign flag is not set, jump to the given address.
                            Src = ReadInt32();

                            if (!m_SignFlag)
                                m_InstCounter = (uint)Src;

                            break;
                        case 0x43: //ifle - if the sign flag is set or the zero flag is set, jump to the given address.
                            Src = ReadInt32();

                            if (m_ZeroFlag || m_SignFlag)
                                m_InstCounter = (uint)Src;

                            break;
                        case 0x44: //smart_setlist - choose a global hitlist, or 0 for the one local to the track.
                            Src = ReadByte();

                            if (Src != 0)
                                m_Hitlist = FileManager.GetHLS((uint)GetVariable(Src));
                            else
                            {
                                uint SoundID = FileManager.GetTRK(TrackID).SoundID;

                                try
                                {
                                    FileManager.GetSound(SoundID);
                                }
                                catch
                                {
                                    m_Hitlist = FileManager.GetHLS(SoundID);
                                }
                            }

                            break;
                        case 0x45: //seqgroup_kill - kill all sounds belonging to the sequence group specified by the "group"
                                   //variable.
                            Src = ReadByte();

                            break;
                        case 0x47: //seqgroup_return - unknown.
                            byte Group = ReadByte();

                            break;
                        case 0x48: //getsrcdatafield - Read an object variable (whose ID is specified by the "field"
                                   //variable) of a source object (whose object ID is specified by the "source" variable),
                                   //store it in the "dest" variable, and update the flags.
                            Dest = ReadByte();
                            Src = ReadByte();
                            Datafield = ReadByte();

                            int ObjectVar = GetVariable(Src);
                            SetVariable(Dest, ObjectVar);
                            m_ZeroFlag = (ObjectVar == 0);
                            m_SignFlag = (ObjectVar < 0);

                            break;
                        case 0x49: //seqgroup_trkid - unknown.
                            Dest = ReadByte();
                            Src = ReadByte();

                            break;
                        case 0x4a: //setll - Copy the contents of one variable into another (equivalent to set and settt;
                                   //defaultsyms.txt says "ISN'T THIS THE SAME AS SET TOO?")
                            Dest = ReadByte();
                            Src = ReadByte();

                            SetVariable(Dest, Src);

                            break;
                        case 0x4b: //setlt - unknown.
                            Dest = ReadByte();
                            Src = ReadByte();

                            break;
                        case 0x4d: //waiteq - wait until two variables are equal.
                            Var1 = ReadByte();
                            Var2 = ReadByte();

                            if (GetVariable(Var1) != GetVariable(Var2))
                            {
                                m_InstCounter -= 3;
                                yield return false;
                            }

                            break;
                        case 0x53: //duck - unknown.
                            break;
                        case 0x54: //unduck - unknown.
                            break;
                        case 0x56: //setlg - set global = local (source: defaultsyms.txt).
                            Dest = ReadByte();
                            Src = ReadInt32();

                            HitVM.SetGlobalVar(Src, GetVariable(Dest));

                            break;
                        case 0x57: //setgl - read globally, set locally (source: defaultsyms.txt).
                            Dest = ReadByte();
                            Src = ReadInt32();

                            SetVariable(Dest, HitVM.GetGlobalVar(Src));

                            break;
                        case 0x59: //setsrcdatafield - set an object variable (whose ID is specified by the "field" variable) of
                                   //a source object (whose object ID is specified by the "source" variable) to the value
                                   //specified by the "value" variable.
                            Dest = ReadByte();
                            Src = ReadByte();
                            Datafield = ReadByte();

                            break;
                        case 0x5f: //smart_index - find the entry at the index specified by the "index" variable in the hitlist
                                   //specified by the "dest" variable and store that entry in the "dest" variable.
                            Dest = ReadByte();
                            byte Index = ReadByte();

                            uint HitlistID = (uint)GetVariable(Index);
                            uint TRKID = SetTrack(HitlistID);
                            SetVariable(Dest, (int)TRKID);

                            break;
                        case 0x60: //note_on_loop - play a note, whose ID resides in the specified variable, and immediately loop
                                   //it indefinitely.
                            Dest = ReadByte();

                            HITNoteEntry Note = new HITNoteEntry(m_SoundID, FileManager.GetSound(m_SoundID));
                            m_Notes.Add(Note);

                            SetVariable(Dest, m_Notes.Count - 1);
                            SoundPlayer.PlaySound(Note.Sound.DecompressedWav(), Note.Sound.GetSampleRate(), true);

                            break;
                    }
                }
            }
            else
            {
                ISoundCodec Snd = FileManager.GetSound(m_SoundID);
                SoundPlayer.PlaySound(Snd.DecompressedWav(), Snd.GetSampleRate(), false);
                yield return true;
            }
        }