Stepmania パッケージ「Panzer Force」 - PF5thについていた独自プログラムについて Part.2
続いた
引き続きクライアント側
クソ機能
Private Sub TextBox1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles TextBox1.KeyDown
If e.KeyCode = Keys.X Then
MsgBox("ULTRA EXTREME FEVER!!", MsgBoxStyle.Information)
UnlockEx_PressX()
ElseIf e.KeyCode = Keys.A Then
MsgBox("Aをより多く", MsgBoxStyle.Information)
ElseIf e.KeyCode = Keys.J Then
MsgBox("ジミー・ライトニング「クレイジー!!」", MsgBoxStyle.Information)
End If
End Sub
なんだこれ・・・
TextBox1というのはアップデート内容が表示されている領域のこと
そこでXやAやらJキーを押すとメッセージが出るようになっていた
Songs タブ
収録曲のリストと、プレイのスコアを収集するもの
Public Const ScoreRoot As String = ".\..\..\data\machineprofile\stats.xml"
参照している stats.xml は machineprofile のほう
・・・てなんだっけな、Stepmania
はユーザープロファイルもあって、ユーザー個別に情報も収集できるけども、
Machineprofileとすると、そのStepmania内でプレイされた全プレイヤーのスコアが乗ったはず
基本的にローカルで一人プレイになるので、これでよしとしたのであろう
Public Sub InitScore()
If File.Exists(ScoreRoot) Then
csXMLInit() 'スコア情報のロード
For I As Integer = 0 To UBound(SongDatas) Step 1
If SongDatas(I).Title = "" Then Exit For
For I2 As Integer = 0 To 14 Step 1
'ロード順:0:Single-Beginner ~4:Single-Challenge 5:Double-Beginner~ 14:Solo-Challenge
ScoreList(I * 15 + I2) = csXMLSearch(SongDatas(I).FolderName, EnumDiff.Mode(CInt(Floor(I2 / 5))), EnumDiff.Difficult(I2 Mod 5))
'もし、~_Hardフォルダや_Challengeフォルダが見つかった場合、上書きする。
If IsNothing(csXMLSearch(SongDatas(I).FolderName & "_" & LCase(EnumDiff.Difficult(I2 Mod 5)), EnumDiff.Mode(CInt(Floor(I2 / 5))), EnumDiff.Difficult(I2 Mod 5)).Tiers) = False Then
ScoreList(I * 15 + I2) = csXMLSearch(SongDatas(I).FolderName & "_" & LCase(EnumDiff.Difficult(I2 Mod 5)), EnumDiff.Mode(CInt(Floor(I2 / 5))), EnumDiff.Difficult(I2 Mod 5))
End If
Next
Next
Dim UnlockSwitch82 As Boolean = False '82 = Unconscious Mischief : Hardcoreコース(6)完走(難易度問わず)
Dim UnlockSwitch83 As Boolean = False '83 = OUT DOOR SEX IN PARK NIGHT : Launen(10)コース完走
Dim UnlockSwitchC15 As Boolean = False 'C15 = Light Force : Step 1(3)コース完走
Dim UnlockSwitchC16 As Boolean = False 'C16 = Dark Force : Light Force(15)コース完走
For I As Integer = 0 To UBound(CourseDatas) Step 1
If CourseDatas(I).Title = "" Then Exit For
For I2 As Integer = 0 To 8 Step 1
CourseScoreList(I * 9 + I2) = csXMLSearchCourse(CourseDatas(I).CrsFileName & ".crs", EnumDiff.Mode(CInt(Floor(I2 / 3))), EnumDiff.CourseDiff(I2 Mod 3))
If CourseDatas(I).ID = 6 And CourseScoreList(I * 9 + I2).Tiers <> "Failed" And CourseScoreList(I * 9 + I2).Tiers <> "" Then '6 空白以外、Tiers06(最低ランク)以外
UnlockSwitch82 = True
End If
If CourseDatas(I).ID = 10 And CourseScoreList(I * 9 + I2).Tiers <> "Failed" And CourseScoreList(I * 9 + I2).Tiers <> "" Then '10 空白以外、Tiers06(最低ランク)以外
UnlockSwitch83 = True
End If
If CourseDatas(I).ID = 3 And CourseScoreList(I * 9 + I2).Tiers <> "Failed" And CourseScoreList(I * 9 + I2).Tiers <> "" Then '10 空白以外、Tiers06(最低ランク)以外
UnlockSwitchC15 = True
End If
If CourseDatas(I).ID = 15 And CourseScoreList(I * 9 + I2).Tiers <> "Failed" And CourseScoreList(I * 9 + I2).Tiers <> "" Then '10 空白以外、Tiers06(最低ランク)以外
UnlockSwitchC16 = True
End If
Next
Next
For I As Integer = 0 To UBound(WCourseDatas) Step 1
If WCourseDatas(I).Title = "" Then Exit For
For I2 As Integer = 0 To 2 Step 1
WCourseScoreList(I * 3 + I2) = csXMLSearchCourse(WCourseDatas(I).CrsFileName & ".crs", EnumDiff.Mode(I2))
Console.WriteLine(WCourseScoreList(I * 3 + I2).Percent & " / by " & WCourseDatas(I).CrsFileName)
Next
Next
GetTotal()
'特殊解禁
For I As Integer = 0 To UBound(SongDatas) Step 1
'単曲所持RP
If SongDatas(I).UnlockNeedPlay = 600000 And SongDatas(I).UnlockNeedRP >= SongDatas(I).HasRP Then SIDUnlock(I)
'Pure Boys (Challenge = 通称ヒットレル氏) 譜面数3
If SongDatas(I).ID = 56 And SongDatas(I).ClearFlag >= 3 Then SIDUnlockA(I, "feat.ヒットレル氏")
'NEW ANACHRONISM -Dream side- Score 100億 SID=81
If SongDatas(I).ID = 81 And ScoreTotal.Score >= 10000000000 Then SIDUnlock(I)
'NEW ANACHRONISM -Real side- HasRP=14 SID=81
If SongDatas(I).ID = 81 And SongDatas(I).HasRP >= 14 Then SIDUnlockA(I, "Real side")
'Unconscious Mischief : Hardcoreコース完走
If SongDatas(I).ID = 82 And UnlockSwitch82 Then SIDUnlock(I)
'OUT DOOR SEX IN PARK NIGHT : Launenコース完走
If SongDatas(I).ID = 83 And UnlockSwitch83 Then SIDUnlock(I)
Next
For I As Integer = 0 To UBound(CourseDatas) Step 1
'Light Force : Step 1コース完走
If CourseDatas(I).ID = 15 And UnlockSwitchC15 Then CIDUnlock(I)
'Dark Force : Light Force コース完走
If CourseDatas(I).ID = 16 And UnlockSwitchC16 Then CIDUnlock(I)
Next
Else
MsgBox("Stats.xmlが見つかりませんでした。スコアはロードされません。一回以上プレイした後、再度起動させてください。", MsgBoxStyle.Critical, ProgramTitle)
End If
End Sub
スコアを読み取る処理
エラーのトラップがIFのElse側とか不慣れ感
ここはスコア情報の読み取りというよりは、スコアの情報を用いた解禁処理と言う感じ
この解禁情報って全部明かしたんだったかどうだったか
XMLファイルの読み取り
このスコア周りを解析するためには XML を読まなきゃならんのだけども、
VB.NET主流の XmlDocumentとかはイマイチ使い勝手がよくわからんかった
例えば
<root>
<category>
<data>
<value1>123</value1>
</data>
<data>
<value1>234</value1>
</data>
</category>
</root>
こういうXMLがあったら、value1には「root/category/data/value1」みたいにアクセスしたいんだけども、
XMLDocument ではどうも直感的にこうできなかった・・・と思う
今ならわかるんだろうか
そこで、XMLファイルリーダーを自作することに・・・よく作ったな
Imports System.Xml
''' <summary>XMLファイル 読み取り/書き込み補助</summary>
''' <remarks>(C) 2010 dj Extrose</remarks>
Module ExXMLReader
''' <summary>データバッファ</summary>
Public XMLData(50) As String
''' <summary>
''' XMLファイルをバッファへロードします。
''' </summary>
''' <param name="Filename">読み込むXMLファイル</param>
Public Sub XMLLoad(ByVal Filename As String)
Dim xmlRdr As System.Xml.XmlReader = XmlReader.Create(New IO.StreamReader(Filename, System.Text.Encoding.UTF8))
'Txの定義と初期化。
Dim Tx(10) As String : For I As Integer = 0 To Tx.Length - 1 Step 1 : Tx(I) = "" : Next I
'XMLData初期化
XMLInit()
Dim Ic As Integer = 0
Dim Fl As Boolean = False
While xmlRdr.Read()
Select Case xmlRdr.NodeType
Case XmlNodeType.Element
Tx(xmlRdr.Depth) = xmlRdr.Name
XMLData(Ic) = MakeXMLObjectLine(Tx) & ">"
While xmlRdr.MoveToNextAttribute
XMLData(Ic) = XMLData(Ic) & xmlRdr.Name & "=" & xmlRdr.Value & ","
Fl = True
End While
If Fl Then Ic += 1 : Fl = False
Case XmlNodeType.Text
XMLData(Ic) = MakeXMLObjectLine(Tx) & ">" & xmlRdr.Value
Ic += 1
Case XmlNodeType.EndElement
Tx(xmlRdr.Depth) = ""
End Select
End While
xmlRdr.Close()
End Sub
''' <summary>
''' バッファに格納したデータをクリアします。
''' </summary>
Public Sub XMLInit()
For I As Integer = 0 To XMLData.Length - 1 Step 1 : XMLData(I) = "" : Next I
End Sub
''' <summary>
''' XMLデータを検索します。引数の最初から順に検索され、最後の引数に該当する検索結果を返します。見つからなかった場合、何も返しません。
''' </summary>
''' <param name="SearchStr">検索文字列。書式「[タグパス(例:TestTag.TestTag2)]>[属性名]=[値]」。属性を省略すると、タグで囲まれた値を検索します。</param>
Public Function XMLSearchStrict(ByVal ParamArray SearchStr() As String) As String
XMLSearchStrict = ""
Dim I As Integer = 0, Sl As Integer = 0, Tx As String
For Each Ss As String In SearchStr
'あいまい検索対策
Ss = Ss.ToUpper
Do
Application.DoEvents() '重いのでDoEvents
If XMLData(I) = "" Or XMLData.Length < I Then Exit For
Tx = XMLData(I).ToUpper
If Left(Tx, Ss.Length) = Ss Then
Sl = Ss.Length
Exit Do
End If
I += 1
Loop
Next Ss
If XMLData(I) <> "" Then XMLSearchStrict = Right(XMLData(I), XMLData(I).Length - Sl)
End Function
''' <summary>
''' XMLデータを検索します。引数の最初から順に検索され、最後の引数に該当する検索結果を返します。見つからなかった場合、何も返しません。第一引数は必須で、第一引数のタグ範囲内(終了するまで)に任意の第二引数が見つからなかった場合、何も返しません。
''' </summary>
''' <param name="SearchStr">検索文字列。書式「[タグパス(例:TestTag.TestTag2)]>[属性名]=[値]」。属性を省略すると、タグで囲まれた値を検索します。</param>
Public Function XMLSearchStrictEx(ByVal ParamArray SearchStr() As String) As String
XMLSearchStrictEx = ""
Dim I As Integer = 0, Sl As Integer = 0, Tx As String, I2 As Integer = UBound(XMLData)
Dim Ti2 As Integer = 0
'あいまい検索対策
For Each Ss As String In SearchStr
Ss = Ss.ToUpper
Do
If XMLData(I) = "" Or I > I2 Then Exit For
Tx = XMLData(I).ToUpper
If Left(Tx, Ss.Length) = Ss Then
Ti2 = I + 1
Sl = Ss.Length
Exit Do
End If
I += 1
Loop
'第二ループ:終点探査
Do
If XMLData(Ti2) = "" Or Ti2 > I2 Then Exit Do
Tx = XMLData(Ti2).ToUpper
Dim Ttx As String = "", Addes As String = ""
'属性指定まで含む場合
If InStr(Ss, "=") <> 0 Then Ttx = Split(Ss, "=")(0) : Addes = "="
'属性指定を含まない場合
If InStr(Ss, "=") = 0 Then Ttx = Split(Ss, ">")(0) : Addes = ">"
If Left(Tx, Ttx.Length + 1) = Ttx & Addes Then
I2 = Ti2 - 1
Exit Do
End If
Ti2 += 1
Loop
Next
If XMLData(I) <> "" And _
Left(XMLData(I), SearchStr(UBound(SearchStr)).Length) = SearchStr(UBound(SearchStr)) Then
XMLSearchStrictEx = Right(XMLData(I), XMLData(I).Length - Sl)
End If
End Function
''' <summary>
''' XMLデータを検索します。引数の最初から順に検索され、最後の引数に該当する検索結果を複数返します。見つからなかった場合、何も返しません。第一引数は必須で、第一引数のタグ範囲内(終了するまで)に任意の第二引数が見つからなかった場合、何も返しません。
''' </summary>
''' <param name="SearchStr">検索文字列。書式「[タグパス(例:TestTag.TestTag2)]>[属性名]=[値]」。属性を省略すると、タグで囲まれた値を検索します。</param>
Public Function XMLSearchMulti(ByVal ParamArray SearchStr() As String) As String()
XMLSearchMulti = Nothing
Dim I As Integer = 0, Sl As Integer = 0, Tx As String, I2 As Integer = UBound(XMLData)
Dim Ti2 As Integer = 0
'あいまい検索対策
For Each Ss As String In SearchStr
If Ss = SearchStr(UBound(SearchStr)) Then Exit For '最後の要素ならループ抜け
Ss = Ss.ToUpper
Do
If XMLData(I) = "" Or I > I2 Then Exit For
Tx = XMLData(I).ToUpper
If Left(Tx, Ss.Length) = Ss Then
Ti2 = I + 1
Sl = Ss.Length
Exit Do
End If
I += 1
Loop
'第二ループ:終点探査
Do
If XMLData(Ti2) = "" Or Ti2 > I2 Then Exit Do
Tx = XMLData(Ti2).ToUpper
Dim Ttx As String = "", Addes As String = ""
'属性指定まで含む場合
If InStr(Ss, "=") <> 0 Then Ttx = Split(Ss, "=")(0) : Addes = "="
'属性指定を含まない場合
If InStr(Ss, "=") = 0 Then Ttx = Split(Ss, ">")(0) : Addes = ">"
If Left(Tx, Ttx.Length + 1) = Ttx & Addes Then
I2 = Ti2 - 1
Exit Do
End If
Ti2 += 1
Loop
Next
Dim ResMulti As String(), ResTemp As String = ""
For Ia As Integer = I To I2 Step 1
Dim Ss As String = SearchStr(UBound(SearchStr))
Ss = Ss.ToUpper
Tx = XMLData(I).ToUpper
If Left(Tx, Ss.Length) = Ss Then
ResTemp = ResTemp & Right(XMLData(I), XMLData(I).Length - Ss.Length) & "<=>"
End If
Next
If ResTemp.Length <> 0 Then ResTemp = Left(ResTemp, ResTemp.Length - 1) 'カンマ取り
ResMulti = Split(ResTemp, "<=>")
If ResTemp <> "" Then
XMLSearchMulti = ResMulti
End If
End Function
''' <summary>
''' XMLデータを検索します。引数の最初から順に検索され、最後の引数に該当する検索結果を返します。見つからなかった場合、何も返しません。
''' </summary>
''' <param name="SearchStr">検索文字列。</param>
Public Function XMLSearch(ByVal ParamArray SearchStr() As String) As String
XMLSearch = ""
Dim I As Integer = 0, Sl As Integer = 0, Tx As String
For Each Ss As String In SearchStr
'あいまい検索対策
Ss = Ss.ToUpper
Do
Application.DoEvents() '重いのでDoEvents
If XMLData(I) = "" Or XMLData.Length < I Then Exit For
Tx = XMLData(I).ToUpper
If InStr(Tx, Ss) <> 0 Then
Sl = Ss.Length
Exit Do
End If
I += 1
Loop
Next Ss
If XMLData(I) <> "" Then XMLSearch = Split(XMLData(I), "=")(1)
End Function
Public Function XMLDataOutput() As Boolean
Try
Dim Sw As New System.IO.StreamWriter(".\XMLReaderDebug.log")
For Each St As String In XMLData
If St = "" Then Exit For
Sw.WriteLine(St)
Next St
Sw.Close()
XMLDataOutput = True
Catch ex As Exception
XMLDataOutput = False
End Try
End Function
''' <summary>
''' バッファサイズを変更します。
''' </summary>
''' <param name="Size">バッファサイズ。省略した場合、100に設定します。</param>
Public Sub XMLResizeBuffer(Optional ByVal Size As Integer = 50)
ReDim XMLData(Size)
For I As Integer = 0 To Size Step 1 : XMLData(I) = "" : Next I
End Sub
Private Function MakeXMLObjectLine(ByVal TagDatas As String()) As String
MakeXMLObjectLine = ""
For I As Integer = 0 To TagDatas.Length - 1 Step 1
If TagDatas(I) = "" Then Exit For
MakeXMLObjectLine = MakeXMLObjectLine & TagDatas(I) & "."
Next
'末端チョッパー
MakeXMLObjectLine = Left(MakeXMLObjectLine, MakeXMLObjectLine.Length - 1)
End Function
End Module
今ならもっと利便性のあるオブジェクトが存在するんだろうか
WindowsのGUIプログラム作るってなった時に、今やVB.netを選択することも無いだろうし
もっぺん勉強しようとはあんまり
で、このコードの仕様についてはもう覚えていないので、こういうときの
ChatGPT
仕様書を起こしてもらった
ちなみに2025年現在、このページ含め当サイトは markdown
でページを作っている
ChatGPTに出させるとまずmarkdownで出すので、コードブロックで読めるようにと要望した
ExXMLReader モジュール仕様書
==========================
作成者: dj Extrose
作成年: 2010年
概要:
XMLファイルの読み込みおよび検索機能を提供するVB.NETモジュールです。
読み込んだXMLはバッファ(XMLData)に保存され、階層構造付きで検索可能になります。
■ グローバル変数
- XMLData(50) As String
XMLの内容を格納する配列バッファ。初期サイズは50。
XMLResizeBufferによりサイズ変更可能。
■ 関数一覧
- Sub XMLLoad(Filename As String)
XMLファイルを読み込み、階層情報とともにXMLDataに格納します。
属性は「タグパス>属性=値,」の形式で保存されます。
- Sub XMLInit()
バッファXMLDataを初期化(空文字に)します。
- Function XMLSearchStrict(ParamArray SearchStr() As String) As String
指定されたタグパスと属性条件に一致するデータを厳密に1つ検索します。
例: "Root.Child>id=abc" のような文字列で検索。
- Function XMLSearchStrictEx(ParamArray SearchStr() As String) As String
XMLSearchStrictに似ていますが、範囲内(先頭タグ〜終了タグ)にあるかを判定。
先頭タグで囲まれた中に最終条件がない場合、何も返しません。
- Function XMLSearchMulti(ParamArray SearchStr() As String) As String()
複数マッチを検索して配列で返す。
先頭タグの範囲内に最終条件に一致するものを探し、すべて返却。
- Function XMLSearch(ParamArray SearchStr() As String) As String
あいまい検索で、最初に一致した要素の属性値を返します。
検索文字列が属性の場合、"属性=値"から値部分だけを返します。
- Function XMLDataOutput() As Boolean
XMLDataの中身をファイル「./XMLReaderDebug.log」に出力。
成功時はTrue、失敗時はFalseを返します。
- Sub XMLResizeBuffer(Optional Size As Integer = 50)
XMLDataのサイズを変更し、すべて初期化します。
■ 内部関数
- MakeXMLObjectLine(TagDatas As String()) As String
タグ配列を「Root.Child.GrandChild」のように「.」で結合し、階層表現を生成。
■ バッファに格納されるデータ形式(例)
Root>id=abc,
Root.Child>テキスト
Root.Child.GrandChild>type=main,text=hello
- タグの階層は「.」で区切られます。
- 属性は「>属性=値,」として記録。
- テキストノードは「>テキスト値」として格納。
■ 注意点・仕様補足
- 大文字小文字は無視して比較(内部処理でUpperCase変換)
- XMLDataのサイズは初期値50、必要に応じて変更可能
- 検索関数はDoEventsを含むため、UIがあるアプリでは応答に注意
- 属性の区切りはカンマ(,)ですが、末尾には付かないこともあります
- XMLのネスト構造をフラットな文字列で保持しているため、厳密な構文解析ではない
ChatGPT
すごいなあって思うと同時に、xmlを絶対に自分の使い勝手の良い読み方をしてやるという執念を感じる
加えて、オリジナルパーサが作れるということは規格化されていてこそだなとも
このオリジナルパーサを用いてstats.xml を読み取ったという
stats.xml の解析
ExXMLReader.XMLLoad(ScoreRoot)
まずxmlファイルを読み込む
''' <summary>複数のデータから最も高いハイスコアを取得します。</summary>
''' <param name="SongFolder">「カテゴリフォルダ/曲フォルダ」で指定します。</param>
''' <param name="Style"></param>
''' <param name="Difficulty"></param>
Public Function csXMLSearch(ByVal SongFolder As String, ByVal Style As String, ByVal Difficulty As String) As ScoreBoard
csXMLSearch = Nothing
Dim I As Integer = 0
'第1ループ:曲名探し
Do
If ExXMLReader.XMLData(I) = "" Then Exit Function
If ExXMLReader.XMLData(I) = "Stats.SongScores.Song>Dir=Songs/Panzer Force 5thmix/" & SongFolder & "/," Then Exit Do
I += 1
Loop
'第2ループ:スタイル/難易度探し I+1し維持
I += 1
Do
If ExXMLReader.XMLData(I) = "Stats.SongScores.Song.Steps>StepsType=" & Style & ",Difficulty=" & Difficulty & "," Then Exit Do
If ExXMLReader.XMLData(I) = "" Then Exit Function
'Stats.SongScores.Song で始まっていない行が見つかったらそこで終了
If Left(ExXMLReader.XMLData(I), 21) <> "Stats.SongScores.Song" Then Exit Function
'もし、曲名を発見してしまったらExit。対象文字は多分31字
If Left(ExXMLReader.XMLData(I), 32) = "Stats.SongScores.Song>Dir=Songs/" Then Exit Function
I += 1
Loop
'今IはSong.Stepsに居るので、二つ進め、HighScore.Name を取る (一つ先はNumTimesPlayed)
I += 1
Dim Pln As Integer = CInt(Split(ExXMLReader.XMLData(I), ">")(1))
I += 1
'第3ループ:新旧比較、スコア抽出
Dim Ds As ScoreBoard = ScoreBoardInit(), Dd As ScoreBoard = ScoreBoardInit()
Do
If ExXMLReader.XMLData(I) = "" Then Exit Do
'終了条件はStepsが見えるかSongsが見えるか。 前者37字 後者31字。
If Left(ExXMLReader.XMLData(I), 38) = "Stats.SongScores.Song.Steps>StepsType=" Or _
Left(ExXMLReader.XMLData(I), 32) = "Stats.SongScores.Song>Dir=Songs/" Then Exit Do
'Stats.SongScores.Song で始まっていない行が見つかったらそこで終了
If Left(ExXMLReader.XMLData(I), 21) <> "Stats.SongScores.Song" Then Exit Do
'順序に取る
If Left(ExXMLReader.XMLData(I), 57) = "Stats.SongScores.Song.Steps.HighScoreList.HighScore.Name>" Then I += 1 'Nameは存在したりしなかったりスルーらしい(ドヤ
Ds.Tiers = Split(ExXMLReader.XMLData(I), ">")(1) : I += 1 'Grade
Ds.Score = CLng(Split(ExXMLReader.XMLData(I), ">")(1)) : I += 1 'Score
Ds.Percent = CSng(Split(ExXMLReader.XMLData(I), ">")(1)) : I += 1 'PercentDP
Ds.SurviveSeconds = CSng(Split(ExXMLReader.XMLData(I), ">")(1)) : I += 1 'SurviveSeconds:スルー
If Left(ExXMLReader.XMLData(I), 62) = "Stats.SongScores.Song.Steps.HighScoreList.HighScore.Modifiers>" Then _
Ds.Modifiers = Split(ExXMLReader.XMLData(I), ">")(1) : I += 1 'Modifiers : 多分、ノーオプションだと項目が無い
Ds.DateTime = Split(ExXMLReader.XMLData(I), ">")(1) : I += 1 'DateTime
If Left(ExXMLReader.XMLData(I), 63) = "Stats.SongScores.Song.Steps.HighScoreList.HighScore.PlayerGuid>" Then I += 1 'PlayerGuidは存在したりしなかったりするらしい
I += 2 'MachineGuid,ProductID スルー
I += 7 'TapNoteScores 計7要素スルー
I += 2 'HoldNoteScores 計2要素スルー
I += 10 'RaderValues 計10要素スルー
'スコア比較 DdよりDsの方が大きい場合、DdをDsに置き換え
'追加条件:ModifierにC**0を含まない場合のみ置き換え (Cの位置が1なら、+3した4の位置が0でアウト) / Cross等が気になるが、オプション記述順からして多分問題ない・・・はず
'2011/08/02 追加条件:Modifiersが空白でなければ。
If Ds.Percent > Dd.Percent Then
'エラー回避
If Ds.Modifiers <> "" And Ds.Modifiers.Length >= 4 Then
If Ds.Modifiers.Substring(Ds.Modifiers.IndexOf("C") + 3, 1) <> "0" Then 'Cxx0を含んでいない場合
Dd = Ds
End If
Else 'Modifierが空白だった場合
Dd = Ds
End If
End If
Loop
Dd.Totalplay = Pln
csXMLSearch = Dd
End Function
stats.xml というのは曲⇒スタイル/難易度 の順で登場する
スタイル/難易度だけで検索してしまうと、その要素がどの曲かはそこに情報が無く、どの曲のものかわからない
そこで、直前にある曲名情報を引いて、次に曲を探し、そこから更にスコアのある行を検索する、という順序を意識した読み方をする
曲名
スタイル/難易度
スコア
スタイル/難易度
スタイル/難易度
曲名 ← ループ1
スタイル/難易度 ← ループ2
スコア ← ループ3
スタイル/難易度
:
ところで除外条件にしてある「Cxx0」というのは、beatmania IIDX
とかでいうハイスピ固定のもの
一部の曲というか譜面はソフランを攻略してこそというのもあるので、それを使ったスコアは除外したかった様子
・・・ただ、これCxx0でハイスコア取ってしまってたら、もう一生そのハイスコアが更新されないのでは?
後はコースタブもあるが、大体はこのスコアタブの処理の転用
というところで、今回はこんなところで