NintendoSpyリポジトリを読む
https://gyazo.com/d4155d02939054d3d753b1a029ec6096
firmware.ino
Serial.Write()を使って、USB変換をしてシリアル通信をしてるぽい
すごく参考になる
デジタル入力をしてみる まで追って読んでいくと良い
では、本体の方ではシリアル通信を受け取って変数に格納している部分があるはずで、それを探すたびに出る
写経中
read_loop:ってなんだろうとc#を調べてたけどなんも出てこなかった
よく考えたらarduinoのファイルだった
下の方でgotoがあって、loopを書いてるんだと思った
code:firmware.ino
read_loop:
...
goto read_loop;
なんかいっぱいnopって書いてあるのがある
NO Operationのこと らしい
code:firmware.ino
// NO Operation. nopが16個と改行で構成されているString
#define MICROSECOND_NOPS "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" ...
// 値 = line を読むのに ~2us待つ
asm volatile(MICROSECOND_NOPS MICROSECOND_NOPS);
asm→アセンブリ?
1 clock は 16MHz の場合 0.0625us = 625ns
nop ひとつで 1clock = 0.0625us の wait 相当
volatile はコンパイラが最適化時にこのコードを消さないようにするためのもの。おまじない的に常に書いておく。
volatile がないと、例えば asm("nop"); のような、1クロックだけ待たせるために nop を書いたとしても、コンパイラは何の処理もしていないコードと判断して、最適化時に消してしまったりする。
0.0625*16 = 1
それが2つで2usか
asmはアセンブリを実行する関数で、stringで実行内容を与える
実行内容はvolatileで保護したstringで、スペースで1us分のnopが入ったstringを繋いでいる?
Nicohood's Nintendo API
GCコンをはじめとした任天堂コントローラーをArduinoで扱いやすくするようなAPIが開発されていた
これを利用してNintendoSpyを使っても対応できるように、GameCube.csでも書かれていた
パケットのサイズが変わるらしく、それに対応するように書かれていた
実際書き方はすごく簡素化されている。もしかしたら自分も使ったほうがよいかも?
SetupWindow.xaml
アプリケーションの画面がつくれるやつ
このファイルを開くと、セットアップ画面のレイアウトが画面に表示された
void goButton_Click()でGoボタンを押した際の挙動が定義されていて、viewwindowが呼び出されていた
改行の先にある.ShowDialog()ってなんだろ
C#のShowDialogメソッドを利用すると、モーダルダイアログとして呼び出せるのですね。
どういう……?
このままデバッグなしで実行したら、Goを押したら正常終了した
ブレークポイントをviewwindow呼び出しの直前で設定したらPortについてNullがでた
System.ArgumentNullException: '値を Null にすることはできません。
パラメーター名:PortName'
code:cs
System.ArgumentNullException
HResult=0x80004003
Message=値を Null にすることはできません。
パラメーター名:PortName
Source=System
スタック トレース:
場所 System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
場所 System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
場所 System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
場所 System.Windows.UIElement.RaiseEvent(RoutedEventArgs e)
場所 System.Windows.Controls.Primitives.ButtonBase.OnClick()
場所 System.Windows.Controls.Button.OnClick()
場所 System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
場所 System.Windows.UIElement.OnMouseLeftButtonUpThunk(Object sender, MouseButtonEventArgs e)
場所 System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
場所 System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
場所 System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
場所 System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
場所 System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)
場所 System.Windows.UIElement.OnMouseUpThunk(Object sender, MouseButtonEventArgs e)
場所 System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
場所 System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
場所 System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
場所 System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
場所 System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
場所 System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
場所 System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
場所 System.Windows.Input.InputManager.ProcessStagingArea()
場所 System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
場所 System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
場所 System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
場所 System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
場所 System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
場所 MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
場所 MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
場所 System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
場所 System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
場所 System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
場所 MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
場所 MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
場所 System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
場所 System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
場所 System.Windows.Application.RunDispatcher(Object ignore)
場所 System.Windows.Application.RunInternal(Window window)
場所 System.Windows.Application.Run(Window window)
場所 System.Windows.Application.Run()
場所 NintendoSpy.App.Main()
この例外は、最初にこの呼び出し履歴
NintendoSpy.SerialMonitor.SerialMonitor(string) (SerialMonitor.cs 内)
NintendoSpy.Readers.SerialControllerReader.SerialControllerReader(string, System.Func<byte[], NintendoSpy.Readers.ControllerState>) (SerialControllerReader.cs 内)
NintendoSpy.InputSource..cctor.AnonymousMethod__30_3(string) (InputSource.cs 内)
NintendoSpy.SetupWindow.goButton_Click(object, System.Windows.RoutedEventArgs) (SetupWindow.xaml.cs 内)
DispatcherTimerってなんだ?
System.Windows.Threading.DispatcherTimer
それぞれ.NET Frameworkが持つ2つのGUIシステム向けに作成されています。それぞれのメッセージループの仕組みを意識して作られているので各々のGUIテクノロジ専用のタイマーとなります。
この2つのタイマーはイベントが発生するとそのイベントはGUIスレッド上で発生します。従って、この2つのTimerに設定したイベントハンドラ内でGUIコントロールを操作した場合は例外(InvaliedOperationException)が発生しません。
この2つのタイマーは前述のタイマーより精度が低いです。指定した時間に発生することを期待してはいけません。またUIで別の処理が進んでいる場合、ハンドラ内の処理が待たされる時もあります。
ViewWindow.xaml
goButtonのメソッドでnewされていた。こちらが多分コントローラーの入力可視化画面
内容は正直さっぱりわからん……
MenuItemとは
ControllerGrid
ViewWindowのGridの名前
画面に画像やその他を描画している?
Grid.Children.Add(grid/image)
getGridAnalogTriggeer
getImageForElement
画像をskinのConfigをもとに画像のサイズをオブジェクトに呼び出している?
Window_Closing
参照しているのが自動生成のコードしかない
ウィンドウを閉じたときの挙動を追加している?
こっちが本体か
System.ComponentModel.CancelEventArg
_reader.Finish()
IControllerReaderのFinish()は、 void Finish();で何も書かれていない
GameCube.csは?
関係ない
_{hoge}WithImages
Skin.Buttonとか と System.Windows.Controls.ImageのTupleを、Listでまとめている?
class System.Collections.Generic.List<T>()
<T>
Listを作るジェネリッククラスが定義されていると
ジェネリッククラスの中で使うカッコも <>になるのか?
code:cs
foreach (var button in _buttonsWithImages)
{
if (!newState.Buttons.ContainsKey (button.Item1.Name)) continue;
button.Item2.Visibility = newState.Buttons button.Item1.Name ? Visibility.Visible : Visibility.Hidden ; }
ボタンの一覧を取りたい:
newState.Buttons.Keys
真似てボタン一覧を表示するものを作ってみたら、ウィンドウを動かさないと画面が更新されない……なんで?
ボタンを入力したままにしてもそれが一覧から消えることはない
SetupWindow.xaml.csのupdatePortList()が更新される?
SetupWindowは非表示にしている。その処理の間はViewWindowが消えてる?
Hideをコメントアウトして表示するようにしたら、ブレークポイントに到達しなくなっちゃった
rangeButtonsってなんだろう
多分LRトリガーの深度なんだろうけど
skin.cs
skin.xml
画像の配置を座標で記述している
スティックにはx,yのrangeに記載がある
trig_r/l
height=8
バーの上下でトリガーの深さを可視化するもの
SerialMonitor.cs
違いは?
using System.Windows.Threading;
上記クラスで使われている
Windows Presentation Foundation (WPF) スレッド化システムをサポートする型が含まれています。
WPFでアプリケーションを作っているから、このクラスを使っているということ?
using System.Threading.Tasks;
visual studioの生成したクラスファイルに最初から記述されている
GameCube.cs
NICOHOOD_PACKET_SIZE = 8;
IControllerReader.cs
event
SerialControllerReader.cs
IControllerReaderを継承
packetParserとSerialMonitorを呼び出す
Finish()
SerialMonitorを停止している
serialMonitor_Disconnectedで呼び出して停止している
InputSource.cs
自動実装プロパティって名前かっこいい
public string TypeTag { get; private set; }
publicなのでクラス外部からも参照可能
代入は不可能
privateでなければ、外部から代入が可能
SerialControllerReaderを呼び出している
code:cs
static public readonly InputSource GAMECUBE = new InputSource("gamecube", "GameCube", true, false,
port => new SerialControllerReader (port, GameCube.ReadFromPacket));
SetupWindow.xaml.cs
Properties.Settings.Default とは
プロジェクト > プロジェクトのプロパティ > 設定
NintendoSpyはこんな感じ
https://gyazo.com/1ecde0ce59ec08fc0059df9ca76d3fbb
SourceSelectComboBox_SelectionChanged
ComboBox
プルダウン式の選択メニュー
_vm.Sources
ListView<InputSource>
ViewWindowをそこそこに起動してみる
SerialMonitor.csの _datPort.Open();でエラーが出る
Goボタンをクリックするともう一度SetupWindowが表示されて、二回目を押すとこれになる
1度目の処理で使われていて、更に使おうとしている気がする
画面の遷移がうまくいっていない?
直せた↓
108行目にbreakpointつけてみる
何か書いてないとブレークポイントは入れられない
breakpointのつけるショートカット調べる
F9
この書き方でコメントアウトをするとifが飛ばされてnewが実行されるが
code:cs
/* DelayのDefault値が0なので一旦無視する
if (_vm.DelayInMilliseconds > 0)
reader = new DelayedControllerReader(reader, _vm.DelayInMilliseconds);
*/
new ViewWindow(reader).ShowDialog();
この書き方だとifがnewに適用される。csにおいてインデントは人間の勝手なので
code:cs
if (_vm.DelayInMilliseconds > 0)
/* DelayのDefault値が0なので一旦無視する
reader = new DelayedControllerReader(reader, _vm.DelayInMilliseconds);
*/
new ViewWindow(reader).ShowDialog();
DelayInMillisecondsは0なので実行されなかった
Iで始まるものはインタフェース
readerをViewWindowに渡している
IControllerReader
StateEventHandler ControllerStateChanged
(IControllerReader sender, ControllerState state)
ControllerState
public IReadOnlyDictionary<string, bool> Buttons { get; private set; }
public IReadOnlyDictionary<string, float> Analogs { get; private set; }
reader = _vm.Sources.SelectedItem.BuildReader(_vm.Ports.SelectedItem);
GameCubeの要素どこで渡してるんだと思ったがSkinか?
_vmの中に含まれている
code:cs
public SetupWindowViewModel () {
Ports = new ListView <string> ();
XIAndGamepad = new ListView<uint>();
Skins = new ListView <Skin> ();
Sources = new ListView <InputSource> ();
Backgrounds = new ListView <Skin.Background> ();
}
BlinkReductionFilter.cs
点滅減少フィルター?
ボタンのフレームレベルの連打は点灯させない
スティックの強い傾きが続いてる中で0.1の変化があったとしても(ニュートラルからみて)、破棄する
という処理をしているProcessというものを、ControllerStateに対して追加で実装している