/// <summary> /// Read song timer and note data from memory /// </summary> /// <returns></returns> public RSMemoryReadout DoReadout() { // SONG ID // // Seems to be a zero terminated string in the format: Song_SONGID_Preview // //Candidate #1: FollowPointers(0x00F5C494, new int[] { 0xBC, 0x0 }) //Candidate #2: FollowPointers(0x00F80CEC, new int[] { 0x598, 0x1B8, 0x0 }) //Candidate #3: FollowPointers(0x00F5DAFC, new int[] { 0x608, 0x1B8, 0x0 }) string preview_name; switch (Environment.OSVersion.Platform) { case PlatformID.MacOSX: case PlatformID.Unix: /* more info in MacOSAPI.cs */ preview_name = preview_name = CreateStringFromBytes(FollowPointers(0x018FA000, new int[] { 0xE8, 0x0 }), 128); break; default: //bytes = MemoryHelper.ReadBytesFromMemory(PInfo, FollowPointers(0x00F5C80C, new int[] { 0x28, 0x10, 0x140 }), 128); preview_name = CreateStringFromBytes(FollowPointers(0x00F5C494, new int[] { 0xBC, 0x0 }), 128); break; } //Verify Play_ prefix and _Preview suffix if (preview_name.StartsWith("Play_") && preview_name.EndsWith("_Preview")) { //Remove Play_ prefix and _Preview suffix string song_id = preview_name.Substring(5, preview_name.Length - 13); //Assign to readout readout.songID = song_id; } else if (preview_name.StartsWith("Song_") && preview_name.EndsWith("_Preview.bnk")) { string song_id = preview_name.Substring(5, preview_name.Length - 17); //Assign to readout if (song_id != readout.songID) { readout.persistentID = string.Empty; NoteDataMacAddress_LAS = IntPtr.Zero; NoteDataMacAddress_SA = IntPtr.Zero; } readout.songID = song_id; } switch (Environment.OSVersion.Platform) { case PlatformID.MacOSX: case PlatformID.Unix: break; default: //PID string pid = CreateStringFromBytes(FollowPointers(0x00F5C5AC, new int[] { 0x18, 0x18, 0xC, 0x1C0, 0x0 }), 128); if (!string.IsNullOrEmpty(pid)) { readout.persistentID = pid; } break; } // CURRENT STATE switch (Environment.OSVersion.Platform) { case PlatformID.MacOSX: case PlatformID.Unix: string p = CreateStringFromBytes(FollowPointers(0x018FA9B8, new int[] { 0x48, 0xE0, 0 }), 128); if (!string.IsNullOrEmpty(p)) readout.gameState = p; break; default: string s = CreateStringFromBytes(FollowPointers(0x00F5C5AC, new int[] { 0x28, 0x8C, 0x0 }), 255); if (!string.IsNullOrEmpty(s)) readout.gameState = s; break; } // SONG TIMER // switch (Environment.OSVersion.Platform) { case PlatformID.MacOSX: case PlatformID.Unix: /* more info in MacOSAPI.cs */ ReadSongTimer(FollowPointers(0x018EE728, new int[] { 0x184 })); IntPtr noteDataRoot = IntPtr.Subtract(NoteDataMacAddress_LAS, 0x000C); IntPtr noteDataSARoot = IntPtr.Subtract(NoteDataMacAddress_SA, 0x000C); if (readout.gameState.ToLower().Contains("learnasong")) { if (readout.mode != RSMode.LEARNASONG) { readout.LASData.Clear(); readout.SAData.Clear(); NoteDataMacAddress_LAS = IntPtr.Zero; NoteDataMacAddress_SA = IntPtr.Zero; NDAddressStack.Clear(); } readout.mode = RSMode.LEARNASONG; ReadNoteData(noteDataRoot); } else if (readout.gameState.ToLower().Contains("scoreattack")) { if (readout.mode != RSMode.SCOREATTACK) { readout.LASData.Clear(); readout.SAData.Clear(); NoteDataMacAddress_LAS = IntPtr.Zero; NoteDataMacAddress_SA = IntPtr.Zero; NDAddressStack.Clear(); } readout.mode = RSMode.SCOREATTACK; ReadNoteData(noteDataRoot); readout.SAData.TotalNotesHit = readout.LASData.TotalNotesHit; readout.SAData.CurrentHitStreak = readout.LASData.CurrentHitStreak; readout.SAData.HighestHitStreak = readout.LASData.HighestHitStreak; readout.SAData.TotalNotesMissed = readout.LASData.TotalNotesMissed; readout.SAData.CurrentMissStreak = readout.LASData.CurrentMissStreak; ReadScoreAttackNoteData(noteDataSARoot); } else readout.mode = RSMode.UNKNOWN; break; default: //Weird static address: FollowPointers(0x01567AB0, new int[]{ 0x80, 0x20, 0x10C, 0x244 }) //Candidate #1: FollowPointers(0x00F5C5AC, new int[] { 0xB0, 0x538, 0x8 }) //Candidate #2: FollowPointers(0x00F5C4CC, new int[] { 0x5F0, 0x538, 0x8 }) ReadSongTimer(FollowPointers(0x00F5C5AC, new int[] { 0xB0, 0x538, 0x8 })); // NOTE DATA // // For learn a song: //Candidate #1: FollowPointers(0x00F5C5AC, new int[] {0xB0, 0x18, 0x4, 0x84, 0x0}) //Candidate #2: FollowPointers(0x00F5C4CC, new int[] {0x5F0, 0x18, 0x4, 0x84, 0x0}) // // For score attack: //Candidate #1: FollowPointers(0x00F5C5AC, new int[] { 0xB0, 0x18, 0x4, 0x4C, 0x0 }) //Candidate #2: FollowPointers(0x00F5C4CC, new int[] { 0x5F0, 0x18, 0x4, 0x4C, 0x0 }) //If note data is not valid, try the next mode //Learn a song if (readout.gameState.ToLower().Contains("learnasong")) { readout.mode = RSMode.LEARNASONG; ReadNoteData(FollowPointers(0x00F5C5AC, new int[] { 0xB0, 0x18, 0x4, 0x84, 0x0 })); } else if (readout.gameState.ToLower().Contains("scoreattack")) { readout.mode = RSMode.SCOREATTACK; ReadScoreAttackNoteData(FollowPointers(0x00F5C5AC, new int[] { 0xB0, 0x18, 0x4, 0x4C, 0x0 })); } else readout.mode = RSMode.UNKNOWN; break; } //Copy over everything when a song is running if (readout.songTimer > 0) { readout.CopyTo(ref prevReadout); } //Always copy over important fields prevReadout.songID = readout.songID; prevReadout.songTimer = readout.songTimer; prevReadout.gameState = readout.gameState; prevReadout.mode = readout.mode; return prevReadout; }
/// <summary> /// Read song timer and note data from memory /// </summary> /// <returns></returns> public RSMemoryReadout DoReadout() { // SONG ID // // Seems to be a zero terminated string in the format: Song_SONGID_Preview // //Candidate #1: FollowPointers(0x00F5C494, new int[] { 0xBC, 0x0 }) //Candidate #2: FollowPointers(0x00F80CEC, new int[] { 0x598, 0x1B8, 0x0 }) //Candidate #3: FollowPointers(0x00F5DAFC, new int[] { 0x608, 0x1B8, 0x0 }) byte[] bytes = MemoryHelper.ReadBytesFromMemory(rsProcessHandle, FollowPointers(0x00F5C494, new int[] { 0xBC, 0x0 }), 128); //Find the first 0 in the array int end = Array.IndexOf <byte>(bytes, 0); //If there was a 0 in the array if (end > 0) { //Copy into a char array char[] chars = new char[end]; Array.Copy(bytes, chars, end); //Create string from char array string preview_name = new string(chars); //Verify Play_ prefix and _Preview suffix if (preview_name.StartsWith("Play_") && preview_name.EndsWith("_Preview")) { //Remove Play_ prefix and _Preview suffix string song_id = preview_name.Substring(5, preview_name.Length - 13); //Assign to readout readout.songID = song_id; } } // SONG TIMER // //Weird static address: FollowPointers(0x01567AB0, new int[]{ 0x80, 0x20, 0x10C, 0x244 }) //Candidate #1: FollowPointers(0x00F5C5AC, new int[] { 0xB0, 0x538, 0x8 }) //Candidate #2: FollowPointers(0x00F5C4CC, new int[] { 0x5F0, 0x538, 0x8 }) ReadSongTimer(FollowPointers(0x00F5C5AC, new int[] { 0xB0, 0x538, 0x8 })); // NOTE DATA // // For learn a song: //Candidate #1: FollowPointers(0x00F5C5AC, new int[] {0xB0, 0x18, 0x4, 0x84, 0x0}) //Candidate #2: FollowPointers(0x00F5C4CC, new int[] {0x5F0, 0x18, 0x4, 0x84, 0x0}) // // For score attack: //Candidate #1: FollowPointers(0x00F5C5AC, new int[] { 0xB0, 0x18, 0x4, 0x4C, 0x0 }) //Candidate #2: FollowPointers(0x00F5C4CC, new int[] { 0x5F0, 0x18, 0x4, 0x4C, 0x0 }) //If note data is not valid, try the next mode //Learn a song if (!ReadNoteData(FollowPointers(0x00F5C5AC, new int[] { 0xB0, 0x18, 0x4, 0x84, 0x0 }))) { //Score attack if (!ReadScoreAttackNoteData(FollowPointers(0x00F5C5AC, new int[] { 0xB0, 0x18, 0x4, 0x4C, 0x0 }))) { readout.mode = RSMode.UNKNOWN; } } //Copy over everything when a song is running if (readout.songTimer > 0) { readout.CopyTo(ref prevReadout); } //Always copy over important fields prevReadout.songID = readout.songID; prevReadout.songTimer = readout.songTimer; return(prevReadout); }
/// <summary> /// Read song timer and note data from memory /// </summary> /// <returns></returns> public RSMemoryReadout DoReadout() { // SONG ID // // Seems to be a zero terminated string in the format: Song_SONGID_Preview // //Candidate #1: FollowPointers(0x00F5C494, new int[] { 0xBC, 0x0 }) //Candidate #2: FollowPointers(0x00F80CEC, new int[] { 0x598, 0x1B8, 0x0 }) //Candidate #3: FollowPointers(0x00F5DAFC, new int[] { 0x608, 0x1B8, 0x0 }) string preview_name = MemoryHelper.ReadStringFromMemory(rsProcessHandle, FollowPointers(0x00F5C494, new int[] { 0xBC, 0x0 })); //If there was string in memory if (preview_name != null) { //Verify Play_ prefix and _Preview suffix if (preview_name.StartsWith("Play_") && preview_name.EndsWith("_Preview")) { //Remove Play_ prefix and _Preview suffix string song_id = preview_name.Substring(5, preview_name.Length - 13); //Assign to readout readout.songID = song_id; } } // SONG TIMER // //Weird static address: FollowPointers(0x01567AB0, new int[]{ 0x80, 0x20, 0x10C, 0x244 }) //Candidate #1: FollowPointers(0x00F5C5AC, new int[] { 0xB0, 0x538, 0x8 }) //Candidate #2: FollowPointers(0x00F5C4CC, new int[] { 0x5F0, 0x538, 0x8 }) ReadSongTimer(FollowPointers(0x00F5C5AC, new int[] { 0xB0, 0x538, 0x8 })); // ARRANGEMENT HASH // // This is set to the arrangement persistent id while playing a song string arrangement_hash = MemoryHelper.ReadStringFromMemory(rsProcessHandle, FollowPointers(0x00F5C5AC, new int[] { 0x18, 0x18, 0xC, 0x1C0, 0x0 })); if (arrangement_hash != null) { readout.arrangementID = arrangement_hash; } // GAME STATE // // This one popped up while looking for arrangement hash, seems to be a logical string representing the current game stage // Can be garbled under unknown circumstances // Exists in two (and probably more) locations, where only one may be valid, this tries to get either // Prioritizing the one at 0x27C, because it is more human readable string game_stage = MemoryHelper.ReadStringFromMemory(rsProcessHandle, FollowPointers(0x00F5C5AC, new int[] { 0x18, 0x18, 0xC, 0x27C })); if (game_stage == null) { game_stage = MemoryHelper.ReadStringFromMemory(rsProcessHandle, FollowPointers(0x00F5C5AC, new int[] { 0x18, 0x18, 0xC, 0x14 })); } //If we got a game stage if (game_stage != null) { //Verify that it is at least 4 characters long, to filter out more garbage if (game_stage.Length > 4) { readout.gameStage = game_stage; } } // NOTE DATA // // For learn a song: //Candidate #1: FollowPointers(0x00F5C5AC, new int[] {0xB0, 0x18, 0x4, 0x84, 0x0}) //Candidate #2: FollowPointers(0x00F5C4CC, new int[] {0x5F0, 0x18, 0x4, 0x84, 0x0}) // // For score attack: //Candidate #1: FollowPointers(0x00F5C5AC, new int[] { 0xB0, 0x18, 0x4, 0x4C, 0x0 }) //Candidate #2: FollowPointers(0x00F5C4CC, new int[] { 0x5F0, 0x18, 0x4, 0x4C, 0x0 }) //If note data is not valid, try the next mode //Learn a song if (!ReadNoteData(FollowPointers(0x00F5C5AC, new int[] { 0xB0, 0x18, 0x4, 0x84, 0x0 }))) { //Score attack if (!ReadScoreAttackNoteData(FollowPointers(0x00F5C5AC, new int[] { 0xB0, 0x18, 0x4, 0x4C, 0x0 }))) { readout.mode = RSMode.UNKNOWN; } } //Copy over everything when a song is running if (readout.songTimer > 0) { readout.CopyTo(ref prevReadout); } //Always copy over important fields prevReadout.songID = readout.songID; prevReadout.gameStage = readout.gameStage; prevReadout.songTimer = readout.songTimer; return(prevReadout); }