初めてHSPを触りつつDirect SSTPを書く

# 前回

コマンドラインからのゴースト操作のために、C#のTCPでSSTPを送信するプログラムを書きました。

[C#で伺か(SSP)にSSTP通信する - Qiita](https://qiita.com/khsk/items/177741a6c573790a9379)
[C#で伺か(SSP)にSSTP通信してエミリをつついてシルフィアをなでる - Qiita](https://qiita.com/khsk/items/254f02a0d63e3b937687)

しかしながら、いちいちソケット通信をはさむ必要からか反応速度が遅く、体験が悪かったです。

そこで、より高速が望めそうなプロトコル間通信として[Direct SSTP](http://usada.sakura.vg/contents/directsstp.html)で書き直すことを考えました。

# C#は諦めた

C#かつDirect SSTPといえば前述した記事にもある
[SSTPLib](http://ukiya.sakura.ne.jp/index.php?%E4%BC%BA%E3%81%8B%E9%96%A2%E9%80%A3%E3%83%84%E3%83%BC%E3%83%AB%2FSSTPLib)
が最有力です。
しかしながら、そもそもこのコードのリーディングを諦めてTCPに逃げた都合上、メモ帳でソースを読むだけでは何をしているのかさっぱりわかりませんでした。

FMOを取得するあたりまではなんとかわかったのですが、そこからバイト列を操作したりなんなりで、動く実物がないことには理解が限界でした。
高度にオブジェクト化されていることもメモ帳リーディングでの妨げになっていましたし…


# HSP版が読みやすい

そこでなんとかDirect SSTPの情報をネットからあさっていると
[HSPでDirectSSTPを送信する | すくりや](http://nikolat.herokuapp.com/life/send-direct-sstp-with-hsp/)
[HSPでDirectSSTPの送信結果を表示する | すくりや](http://nikolat.herokuapp.com/life/show-response-of-dsstp-on-hsp/)
を見つけました。
これのいいところは実物のコードと単体で動作するexeファイルが同梱されていることです。

しかもウィンドウ関連を入れても総行数200未満と大変読みやすそう。

ということで、サンプルをコピペしながら動作を理解することにしました。

# HSPの開発環境を作る

ただ、HSPのプログラミング環境を作る必要が私の勝手なラインの
「プログラミング言語をインストールしない」
に抵触するかが謎でした。
C#で言うとVisual StudioはNG。
csc.exeはセーフ。
という謎ライン。

HSPはどうかというと、

[HSP3 概要#INSTALL](http://www.onionsoft.net/hsp/v35/doclib/hsp3.htm#INSTALL)

>インストーラーを起動するか、または任意のディレクトリにhsp34フォルダを解凍する ことで使用できるようになります。(推奨ディレクトリは、「c:\hsp34」です) 
...
>5. アンインストール
フォルダをまとめて削除することでアンインストールすることができます。 
 標準のスクリプトエディタは、標準で以下のレジストリに設定を保存します。必要な場合は、こちらの削除も行なってください。
...
		HKEY_CURRENT_USER\Software\OnionSoftware\hsed3_3
			
とかなりポータブルで許容範囲内かなという感じでした。

また、極々簡単なコードの場合は、HSP製のサードパーティのCLI用コンパイラなどを取得すれば、HSP本体は不要でexeの作成までできるので、かなり依存性が少ないと感じました。

(実際にはWin32を扱うための機能だったりが必要だったためHSP本体をダウンロードし、必要なライブラリ(`.as`ファイル)のみを作成プログラムの同一ディレクトリにコピーしincludeを通しました。
なので`hsp351\common`を必要としただけで、
他のファイルや標準のエディタなどは一切起動しませんでした)

HSPのコンパイラはいろいろあるようですが、今回は更新日時が近かった
[hspc - SoupSeed](https://dev.onionsoft.net/seed/info.ax?id=1392)
を利用しました。

独立ファイル群を用意するだけでメモ帳開発ができるHSPはそこそこ手軽かなという印象です。

# サンプルを動かす

まずはコードで明示的にincludeされているモジュールを集めます。


`kernel32.as`
`mod_regexp.as`
`user32.as`
これらはHSP本体に付属しているのでダウンロードして`common`ディレクトリからコピーすればOKです。
役割はWindowsのプロセス間通信と正規表現のためのモジュールであることは名前から連想できると思います。

## マクロを読み込む

コードでincludeしている3つのモジュールを読み込んだだけではまだエラーがでます。
何の変哲もない以下のコード

```hsp
for i, 1, length(sharedMemVal), 1
;	hogehoge
next
```

がエラーになります。
HSPにはなんと`for`構文がないようです。
あるのは`repeat`などで、上記を書き換えると

```hsp
repeat length(sharedMemVal) - 1
	i = cnt + 1
loop
```

このようになります。
では`for`はなにかというと、


[HSP3 プログラミング・マニュアル#QUICK_START](http://www.onionsoft.net/hsp/v35/doclib/hspprog.htm#QUICK_START)
>繰り返しの記述は通常、repeat～loop命令で行ないます。これは、C言語のfor、while、doを 簡略化したものと捉えることができます。 この他にも、while～wend、do～until、for～next、switch～caseなどC言語ライクなマクロ命令が用意されています。 詳しくは、「標準マクロ定義ファイル」を参照してください。

ということでマクロとして用意されており、その定義ファイルを読み込まないと動作しないわけです。

[HSP3 プログラミング・マニュアル#HSPDEF](http://www.onionsoft.net/hsp/v35/doclib/hspprog.htm#HSPDEF)
から、ファイルは`hspdef.as`であり、明示的に`include`しなくとも直下ディレクトリを探索されることがわかるので、これも設置しましょう。

前述のようにサンプルコードの`for`を`repeat`にすることも検討したのですが、それを解決したところ依存する`mod_regexp.as`でも`for`が使われていることが判明したため、素直にマクロを読み込みました。


# バッチ化に向けて削る

サンプルが動けばあとはウィンドウ関連のコードなどを削っていけばいいと思います。
ウィンドウを完璧に消すには
`#packopt hide 1`
を追加してコンパイルでした。
ただプリントデバッグには便利なので最後に付けるといいかもしれません。

ここで、参考コードの`getSakuraFMO`の返り値を見てみます。

> ウインドウハンドル取得方法
filemapping object "Sakura" からデータを取得することで確実にウインドウハンドルを取得できます。メモリオブジェクトの文書を参照して下さい。

内容は「メモリオブジェクトの文書」と同一なのですが、

```
#defcfunc getSakuraFMO
	MAP_OBJ_NAME_SAKURA = "Sakura"
	sharedMemSize = 1024 * 64
	OpenFileMapping FILE_MAP_READ,FALSE,MAP_OBJ_NAME_SAKURA
	hMapObjSakura = stat
	if (hMapObjSakura==0) {
		return ""
	}
```

はまだわかりますが
その後の

```
	MapViewOfFile hMapObjSakura,FILE_MAP_READ,0,0,sharedMemSize
	sharedMemPtr = stat
	dupptr sharedMemVal, sharedMemPtr, sharedMemSize
	sfmo = ""
	for i, 1, length(sharedMemVal), 1
		l = sharedMemVal(i)
		b1 = (l & 0x000000FF) >> 0
		b2 = (l & 0x0000FF00) >> 8
		b3 = (l & 0x00FF0000) >> 16
		b4 = (l & 0xFF000000) >> 24
		sfmo += strf("%c%c%c%c", b1, b2, b3, b4)
	next
```

の仕組みがわかっていない状態ですね…
ともかく出力を見ると、私の場合は

![sakura.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62354/fa15a554-da35-6826-6eb0-09068cfb3f7c.png)

こんな感じです。
Direct SSTPは
「ウインドウメッセージを伺かのプロセスにSSTPを送ればいいよ」
と読めますが、実際には

1. `Sakura`のファイルマッピングオブジェクトを読み込む？
2. 読み込んだファイルマッピングオブジェクトをマップビューとして開く？
3. マップビューの内容をバイト列操作をしながらstringに書き出す
4. 書き出した中に格納されている**hwnd**(ウィンドウハンドル)の数値を抜き出す
5. hwndの数値に対してメッセージを`SendMessage`する

という手順を踏むようです。
そしてそのあたりに伺かとしてのサポートはないので単純なC、というかWin32API周りの知識を身につける必要がありそうです。

遠回りしましたが、そんなこんなでシェイプアップした`sample.hsp`がこちらです(開発中)

```
;#packopt hide 1
; テスト
#include "dsstphsp_1_2_0/dsstp.hsp"
#define WM_COPYDATA     0x004A

#include "mod_regexp.as"
#module
; "hwnd"とか指定するとhwndの配列が返ってくる 
#deffunc getParamsFromSakuraFMO str param, str sfmo, array ret
    re = "\\." + param + strf("%c", 1) + "([^\\r\\n]+)"
    matches result, sfmo, re, 0, 0, 0
    i = 0
    repeat stat
        split result(cnt), strf("%c", 1), r
        ret(i) = r(1)
        i += 1
    loop
    return
#global


oncmd gosub *OnCopyData, WM_COPYDATA

sdim msg, 4096
msg = {"
NOTIFY SSTP/1.1
Charset: Shift_JIS
Sender: HSP
Event: OnDive
Reference0: 道頓堀
Script: \\h\\s[5]どぼ〜ん。\\w9\\w9\\u\\s[11]…\\w5…\\w5…\\w5…\\w5…。\\e
Option: notranslate
HWnd: 0"}

sdim s, 4096
poke s, 0, msg

sfmo = getSakuraFMO()
mes sfmo

getParamsFromSakuraFMO "hwnd", sfmo, hwnds

hwndTarget = int(hwnds(0))
sendDSSTP hwndTarget, s
mes stat
wait 1000
end

*OnCopyData
    dupptr cds, lParam, 12
    data_id   = cds(0)
    data_size = cds(1)
    dupptr data_ref, cds(2), data_size
    if data_id == 9801 {
        sdim data, data_size/4
        memcpy data, data_ref, data_size
        mes data
    }
    dim cds, 1
    dim data_ref, 1
    end
```

ゴーストを複数立てないならhwndlsitではなく伺か本体のhwndでよく、任意のフィールドを引っ張る`getParamsFromSakuraFMO `ではなくhwnd狙い撃ちの抜き出し関数にすればもっとスリムになりそうですね。

あとはこれは引数駆動にしてメッセージを変動させれば、コマンド用DirectSSTPの完成ですね。