SpaceChemをOS X El Capitanでプレイする

SpaceChemってゲームを教えてもらったんです。
Zachtronics | SpaceChem

えーと、なんか化学合成パイプラインを作るゲームですね。原料として水素原子と炭素原子と酸素原子があるから…CとOに二重結合を作って…両脇にHをくっつけて…みたいな擬似化学をピタゴラ装置みたいなのでやるの。面白そうじゃない。どうもMac版もSteamにあるみたいだぞ。やってみよう!と思ったら
起動しないの。
SpaceChem Mac版はMonoを使っているらしいんですが、Monoの最新版がEl Capitanと相性が悪く起動できなくなってしまったようです。以前はiOS版もあったらしいんですが、同時にiOSでも動作不可になり、今はストアにない。Steam版もMacにインストールはできるけれど、よく見ると対応はWindowsLinuxって書いてある!これだから古いゲームは!ファック!返金だ返金!
いや待てよ…
Zachtronics | SpaceChem and Mac OS X
なるほど!
GitHub - leafi/fix-spacechem-os-x: Script/instructions to fix SpaceChem for OS X 10.11
なんか有志がMonoのパッチめいたものを作っており、これで動くらしい!インストール!
起動しない。
ファック!返金だ返金!
ところがさらにIssuesのところで対処が続いており、これで動いているということなんです。
still not running · Issue #5 · leafi/fix-spacechem-os-x · GitHub

あらためて手順をまとめてみよう

Monoをインストールする

Redirecting…
ここで古いバージョンをインストールしても良いようです。というのは、後述のパッチめいたものは単に問題のあるコンポーネントを4.2.4.4のものに置き換えるだけらしいので。
Index of /archive/4.2.4/macos-10-x86

パッチめいたものをインストールする

GitHub - leafi/fix-spacechem-os-x: Script/instructions to fix SpaceChem for OS X 10.11
ダウンロードしてappを起動するだけです。未署名なので、必要であればシステム環境設定から一時的に未署名のアプリを起動できるようにします。Monoの4.2.4.4をインストールした場合は不要だと思います。

SpaceChemをインストールする

SteamでSpaceChemを購入してきます。この対処法で起動しなくても自己責任なので、返金請求ができる状態でやりましょう。

SpaceChem内のライブラリを置き換える

still not running · Issue #5 · leafi/fix-spacechem-os-x · GitHub
SpaceChemのアプリケーションパッケージが

~/Library/Application Support/Steam/steamapps/common/SpaceChem/SpaceChem.app

にあります。右クリックして「パッケージの内容を表示」、Contents/Frameworks/SDL_image.framework を見つけます。
ここで、
https://www.libsdl.org/projects/SDL_image/release/SDL_image-1.2.10.dmg
をダウンロードしてきます。ダウンロードしてきたSDL_image.frameworkを、先ほどのSpaceChem内の同名のファイルと置き換えます。

起動します

動いた!やったぜ!一度だけクラッシュしましたが、後は普通に動いている気がします。頭を使うパズルゲームなのに起動するまでが既にパズルだなんてな!
この記事はOS X El Capitan 10.11.5で試しているので、macOS sierraとか今後のバージョンでどうなるのかは知りません。

古き良きMac/PCの自由でクソだったところ備忘録

USB Type-CとヘッドホンジャックしかついてないMacBookを買ったんですよ。で、そろそろiPhoneAndroid登場後、これらスマートフォンに比べてMac/PCのココが自由だったけどよく考えたらクソだな!ってところを書き残しておこうと思いました。なぜならMac/PCもどんどんiOSAndroidのやっていることを追っかけつつあり、そのうちこいつらがいかに自由でクソだったかも忘れてしまうだろうと思ったからです。

アクセス権

昔のシングルユーザーなパーソナルコンピュータにはそもそもアクセス権みたいなものすらなかったけれど、iPhone登場前のMacやPCには一応複数のユーザーがあって、他のユーザーのファイルにはアクセスできない、とか管理者権限がないとシステムファイルは変更できない、というのがありましたね。iPhoneでアプリが開発できるようになってみると、これがさらに「他のアプリの書類/設定には一切アクセスできない」ものでした。サンドボックスってやつですか。
Mac/PCでの作業だったら、例えばデスクトップとか書類フォルダに置いたファイルを他のアプリで開いたりしていたわけです。アドレス帳の情報なんかもどのアプリからも見られたし、ブラウザやメールソフトを乗り換える時は、新しいアプリから自動的に前のアプリのメールボックスや設定を読み込んだりしていました。そういうことはもうできなくなりました。
でも慣れてみるとこれ当然じゃないですか?ちょっと待って、今までのMac/PCってつまり何か新しいアプリをインストールしたとしますよ、そのアプリ何の権限もなしに私のブックマークとか、アドレス帳とか、メールボックスのファイルにアクセスできたわけですよ。そのアプリに悪意があったらどうするんですか?クソじゃないですか?

アプリストア

iPhoneは最初サードパーティアプリが作れませんでした。Apple謹製のだけ。他はWebアプリでやれ。そんなわけだから、アプリ開発が解放された時それはみんな大喜びしたわけです。ところが、アプリは公式アプリストアのみでの配信、しかも事前審査制、ときました。その頃まではMacやPCで何かアプリが欲しいと思ったら、製作者のサイトや、あるいは星の数ほどある何らかのダウンロードサイトか、そういうところをまず見つけて、ダウンロードし、インストールすればよかったのに。
これも慣れてみると当然じゃないですか?iPhoneなら途中Googleとかを経由するにしろ、最終的にはApp Storeからダウンロードすることになります。事前審査しているから、ちゃんと動かないとか、悪意がある可能性も少ない。しかもアップデートは自動です。ところがこの前PCのアプリを探したんですよ、今や化石じみたWindows 7で動くやつをね。そりゃあGoogleに聞けば目的のアプリはすぐ見つかりますよ。ところがまずそのダウンロードサイトには山ほど広告が貼ってあってね、しかも広告画像がダウンロードボタンに紛らわしいやつなんですよ。ダウンロードしたらしたで、「インストーラ」をまず起動しなきゃいけないんです。このインストーラがアプリごとに全然違う上に、なんとかツールバーを抱き合わせでインストールしようとしてくるからチェックを外さなきゃならない。アプリをアップデートする時は?自動アップデート機能がついているアプリもありますが、それはアプリごとにバラバラに動作します。そのアプリには何の罪もないんですが、これらの過程は紛れもないクソですよ。

JavaFlash、PDF

かつてiPhoneではFlashを見られず、プラグインが開発されることもない、となったときだいぶ問題になりました。Flashに比べれば影は薄いですが、Javaアプレットとかもそうです。でもこれらの実行環境、しょっちゅうセキュリティ上の問題が発覚しますね。例えばWindows PCを使っていたとしてです、Windows自体の脆弱性パッチ、Flashプラグイン脆弱性パッチ、Javaランタイムの脆弱性パッチ、Acrobat Readerの脆弱性パッチ。こんなのが毎週バラバラに出てくる。しかもこれらのアップデートをチェックしてるデーモンどっかにいるわけでしょ?全部バラバラなのが?定期的に起動して更新ファイルをダウンロードする?iPhoneに搭載しなくて大正解ですよ。動作どころかアップデートでバッテリーと容量制限使い尽くすんじゃないですか?クソです。

コンピューターウイルス

iOSAndroidに無いわけではないですよ。でも前述の通り、アプリは審査有りの公式ストアからしかインストールできない、実行はサンドボックス内、OSに脆弱性が残っているとしても少なくともFlashJava脆弱性は関係ないわけです。
方やMac/PCはどうですか?アプリはそこらへんからダウンロードしてきた素性の怪しいやつですら実行可能(許可は要りますが)、サンドボックスじゃないからユーザーディレクトリ内いじり放題。そりゃあウイルスだって作れるわけです。

ウイルス対策ソフト

MacやPCには必需品でしたね。メールの添付ファイルや、どっかからダウンロードしてきたファイル。ネットワークポートを叩いてくる得体の知れないやつら。そういうものからMac/PCを守ってくれると。でもちょっと待ってくださいよ、「ウイルス対策ソフト」がメールの添付ファイルを読み込むことができ…それを削除することもできる…サンドボックスがないからこういうことが出来るわけですが、アプリストアやサンドボックスがあればメールの添付ファイルを実行するなんてことはそもそも出来ないんですよ。「コンピューターウイルスの棲息に必要な環境」、というのは「ウイルス対策ソフトに必要な環境」そのものです。OS丸ごとiOSとかAndoridみたいになってればそもそもウイルス対策ソフトの必要性が薄い。もしあったとして、ウイルス対策ソフト自体がサンドボックスの中で動作させられるのでは、他アプリの作ったアイルをスキャンなんてできないわけです。一時期家電量販店でAndroid用のウイルス対策ソフト一杯売ってましたけど、もうあんなクソみたいな光景も無くなるんでしょうね。ウイルス対策ソフトメーカーの仕事?他のセキュリティ関係でなんか探しましょう。

DLL地獄

いろんなアプリで共通に使うコードは共有ライブラリにしておくと便利でした。そのライブラリを広く配布しておけば、アプリはそれを呼び出すだけでよいわけです。理想は。…じゃあその共有ライブラリがアップデートしたら?こっちのアプリは新しいバージョンの共有ライブラリの機能を使う…じゃあまず新しいバージョンの共有ライブラリをダウンロードしてからそのアプリをインストールして…ファック、別のアプリが動かなくなったぞ!現実はこうでした。iPhoneでどうなったか?共有ライブラリというものはシステムのもの以外にありません。あるアプリが必要なコードは全部アプリに収めてください。こうなりました。ランタイムとかライブラリを探したりアップデートしたりする必要はありません。容量の無駄遣い?何を言ってるんですか?携帯電話に何十GBのストレージがある時代に?


iPhone登場当時はえっ?そんなに不自由なの?と思ったところも今になってみるとそういう感じです。コンピューターは自ら檻に入り、古き良き自由はどっか行きましたが、クソ便利になりましたね。まだまだiOSだけで何もかもやる気にはならないけど、というかXcodeが無いわけだけど、いい時代じゃないですか?

LEDネームプレートにMacから書き込む(続き)

前回までのあらすじ

仮想シリアルポートを使って付属ソフトの通信内容を調べ、それを反映させてプロトコルをまとめてみました。
http://www.slideshare.net/rnatori/ss-42343500

それでもって日本語も書き込み可能になったソフトがこちら。まだ画像書き込みは対応していない。
http://www.rnatori.net/downloads/LEDBadgeWriter005.dmg

LEDネームプレートにMacから書き込む

LEDネームプレートという、バッテリー内蔵で小さくカワイイな電光掲示板を見つけた。
こういうの。NET-PL2303とか、LED Name Badge B1248とかの型番のやつ。

充電と表示内容の書き込みはPCからUSB接続でやるんだけれど、付属の書き込みソフトはWindows用のみ。そこで、Macからも書き込みができるアプリを作ってみた。(ただし書き込めるのは英数字のみ。バグで書き込み失敗してぶっ壊れても責任は負えませんのでごちゅういください)
http://www.rnatori.net/downloads/LEDBadgeWriter001.dmg

必要ドライバ

PL2303というUSB-シリアル変換チップのドライバをインストールする必要があります。
http://www.prolific.com.tw/us/showproduct.aspx?p_id=229&pcid=41

参考にした情報

Hacking a PL2303 LED Badge in Mac OS (Code Mania attendee badge)
紹介されているC#のコード(送信内容の解説付き)とPythonのコードを参考にした。
Led Mini Board communication Agreement
上記C#のコードと一緒に置いてある、メーカーのものらしき文書。英語がブロークンで分かりづらいが、青字で入れてあるのが直したもののようだ。

ORSSerialPort
Cocoaからお手軽にシリアルポートを扱うなんか。MIT License。

書き込みプロトコル

上記を読んでもわかるけど簡単にメモしておく。
シリアルポートは38400ボー、8-N-1。最初のパケットの前に0x00を送るらしい。ビッグエンディアン

パケットのフォーマット

通信は通常69バイトのパケットを送信することで行う。69バイトのうち、

  • 先頭1バイト:常に0x02
  • 1バイトのコマンド:ASCII文字で'1' '2' '3'のどれか。パケットを連続して送る場合、最初のパケットはコマンドを'1'、中間の複数パケットは'2'、後述の終止パケットは'3'
  • 2バイトのアドレス:内蔵EEPROMのアドレスを直接指定する。このLEDネームプレートは6つのテキストを持つことができ、それぞれのアドレスは0x0600から始まり256バイト毎に並んでいる。パケットは64バイトのペイロードを持つので、一つのテキストは最長4つのパケットで、64バイトずつアドレスを増やしながら送信することになる。0x0600以前のアドレスには内蔵フォントが書き込まれているらしい。0x0e00以降のアドレスには日本語等を表示する際の一時ビットマップフォント。これはまだ書き込み方法を調べてない。
  • 64バイトのペイロード:後述のテキストなどを格納する。
  • 1バイトのチェックサム:先頭の0x02以外のバイトのチェックサム
ペイロードのフォーマット

ペイロードには以下の内容を分割して入れていく。

  • 1バイトのスピード:ASCII文字の'1'〜'5'で表示スピードを指定する。大きいと速い。
  • 1バイトのファイル名:よくわからないけど6つのテキストの最初から1-6を指定している。
  • 1バイトの表示パターン:ASCII文字の'A'〜'E'で表示パターンを指定する。'A'はHOLD、静止表示、'B'はROTATE、いわゆる電光掲示板の流れる表示、など。
  • 1バイトの文字数:後続のテキストの長さ。
  • 250バイトのテキスト:0x80以下のASCII英数字。日本語を表示する場合は…まだ調べてない。一時ビットマップフォントのコードを指定しなきゃならないのだろう。
  • 1バイトのチェックサム…と書いてあるんだけど0x00でいい。
  • 1バイトの0x00

合わせて256バイト。C#のコードでは常に256バイト送ってるみたいなんだけど、テキストが短い場合少なくとも64バイト区切りで必要ないパケットは送らなくていいみたい。

終止パケット

上記69バイトのパケットを送り終わったら、4バイトの終止パケットを送る。

  • 先頭1バイト:常に0x02
  • 1バイトのコマンド:終止パケットは'3'
  • 1バイトの送信したテキストを表すビットフィールド:送信したテキストが1番と3番のみ、とかだったら0b00000101、のようにして指定するみたい。
  • 1バイトのチェックサム:先頭の0x02以外のバイトのチェックサム
他いろいろ
  • 69バイトのパケットを送ると即EEPROMに書き込むのでちょっと待ってね、最初の送信時は他にもいろいろ処理があるのでさらに待ってね、とか書いてあるので適当にウェイトを入れました。パケットの間に0x00を入れたりはしなくていいみたい。
  • 日本語を表示させたい場合は上記のように、12x12の一時的ビットマップフォントを生成して書き込まなきゃならないらしいけど、情報不足。

なんでLEDネームプレート買ったの

いや…「ニンジャスレイヤー」に「サイバーサングラス」ってガジェットが出てきてね?


このウキヨエのページで言うと「大事件デスヨ」って表示してるのがそれよ。あれよ、作中では内部にもディスプレイがあってARめいた風景が見られるらしいんだけど、カワイイなのはこの外部表示よ。
で、バッテリー内蔵の小型LED表示板があればこういうふうにそれっぽいの作れるじゃない?

日本語表示できなきゃ上記の用途にあまり意味がないのでは

ハイ

ハッシュタグなどから昔のツイートを検索しTogetterでまとめる

(これまでのあらすじ)ホビープログラマーの端くれ、rnatori。たまたまTLに流れてきたウキヨエを目にしたことにより、Twitterで翻訳版が連載中の「ニンジャスレイヤー」を読み始める。Togetterまとめを効率的に読むため専用iPhoneアプリまで作ったものの、Twitter連載という形が生む同時体験行為の重要性に気付き、ログが残っていない実況も読んでみたいと考えたのだった。

Topsyでツイートを検索する


http://topsy.com

Topsyという検索サイトでツイートの内容を検索することができます。TogetterやTwitterで普通に検索をかけても、古くて表示されないツイートなんかも普通に出てきます。期間を指定して検索することもできるので、ここでエピソードの放送中の時間と#njslyrを指定して検索すれば、当時の実況ツイートが出てくるというわけです。
とは言え、ここの期間指定画面はちょっと使いづらいので、直接URLで指定してしまった方が早いです。つまりはこのように。

http://topsy.com/s?q=%23njslyr%20-RT&sort=-date&mintime=1327323661&maxtime=1327342626

「#njslyr -RT」を検索していますが、このmintimeとmaxtimeはUNIX timeですので、例えば以下のようにします。

  • 本文のみのTogetterまとめやTwilogなどから、開始時刻と終了時刻をコピーしてくる(2012-01-14 16:26:19 とか)
  • UNIX timeへ簡単に変換してくれるサイトとかで変換する( http://konisimple.net/tool/unixtime/ とか)
  • maxtimeはちょっと増やす(感想タイム用)

ツイートのURLをリストアップする

で、こうして作ったURLで検索します。Togetterはこのページを直接読み込むことが出来ないので、表示された結果から個々のツイートへのリンクを全て抜き出します。さすがに何のツールもないと面倒なので、ブックマークレットを作ってみました*1

ListUpTweets
http://let.hatelabo.jp/RNatori/let/hLHVlOrnweA1

ブックマークレットをブラウザのブックマークバーなどに登録しておき、Topsyの検索結果画面でクリックすると、ツイートへのリンクを全て抜き出してテキストエリアに書き出し、選択します。ので、そのままコピーすればツイートのURLのリストが手に入ります。

Togetterに読み込ませる

Togetterの編集画面から、リストアップしたURLを読み込ませることができます。


ここでTwitterAPIのなんかの制限があるらしく、繰り返し繰り返し読み込ませているとしばらく出来なくなります。他のことでもして10分くらい放っておきます。

繰り返す

Topsyの検索結果表示は10件ごと10ページまでのようです。

これではとても足りないので、期間指定をやり直すか、もしくは10まで行った所でおもむろにアドレスバーを見ます。

http://topsy.com/s?q=%23njslyr%20-RT&sort=-date&offset=90&mintime=1327323661&maxtime=1327342626

このoffsetの部分ですね、10ずつ増やしていけば10ページ以降の結果も表示してくれるようなんです*2
こうしてTopsyとTogetterを往復し、時々APIの回復を待つ単純反復自動機械めいた作業をすることで、特定の期間の #njslyr タグを含むツイートをTogetterでまとめることができます。

Twitter.comからの期間指定検索

Twitterの検索からも次のように期間指定検索が出来ます。

#njslyr -RT since:2012-01-19 until:2012-01-20

しかしどうもTopsyに比べて引っ掛かるツイートが少なく、また時間までは指定できないらしいのでちょっとアレです。

*1:JavaScript正規表現はよく分かんないのでもっといいのがあるはずです

*2:せめてここも自動化すべきなのでは

PutOn Java

かざすだけでファイル転送するiOSアプリPutOnのJava版、とりあえず動くものを作ったので置いておきます。
PutOn Java (131215)
http://www.rnatori.net/downloads/PutOn131215.jar

つかいかた

  • お使いのMac、PCやなんかにJava SE 6以上のJavaランタイムをインストールしておきます。
  • 適当なフォルダに入れたjarファイルを起動します。
  • WiFiやなんかでMacまたはPCと同じネットワークに接続しているiPhoneまたはiPad上でPutOnアプリを起動します。
  • あとはPutOn同様に点滅信号にカメラを重ねます。

ちゅうい

  • 今のところ受信だけです。
  • 受信したファイルはjarファイルと同じフォルダに保存されます。デフォルトではその後でフォルトアプリケーションで開くようになっています。
  • 設定の保存用に、jarファイルと同じフォルダにPutOn-Settings.xmlファイルが作成されます。
  • エラー処理とかサボりまくっているので、うまく動かないこともあるでしょう。

JavaでBonjourを使ったファイル転送アプリを作る

PutOnPutOn for Macを俺だけは便利に使っていますが、困ったことといえばWindows PCにはファイル転送できないことです。
仕事でWindows PCを使うけれど別にVisual Studioとかを入れているわけでも無し、ここはマルチプラットフォームで動く(はず)のJavaでPutOnと通信できるアプリを作ってみようではありませんか。

JavaBonjourを使ったサービスの公開と発見を行う

Androidだとandroid.net.nsdというものが使えるらしいですし、AppleもなんかJavaAPIを公開していた気がしますが、JmDNSを使ってみることにしました。Apache License 2.0。

// 接続を待ち受けるServerSocketを作っておきます
serverSocket = new ServerSocket();
InetSocketAddress serverSocketAddress = new InetSocketAddress(InetAddress.getLocalHost(), 0);
serverSocket(serverSocketAddress);
InetAddress inetAddress = serverSocket.getInetAddress();
int port = serverSocket.getLocalPort();
// 実際は次の行は別スレッドで実行、待ち受けてます
Socket actualSocket = serverSocket.accept();

// サービスのタイプとか名前とかポートとか設定して公開します
serviceInfo = ServiceInfo.create("_hoge._tcp.local.", tempServerName, port, "Hoge Service");
try {
	jmDNS = JmDNS.create(inetAddress);
	jmDNS.setDelegate(this);
	jmDNS.registerService(serviceInfo);
	serverName = serviceInfo.getName();

// サービスをブラウズもします
	jmDNS.addServiceListener("_hoge._tcp.local.", this);
} catch (IOException e) {
	e.printStackTrace(); //とかなんとか
}

serverSocketに接続が来たらactualSocketのInputStreamとかOutputStreamでなんかします。
ブラウズしてサービスが見つかったら

public void serviceAdded(ServiceEvent serviceEvent) {
	ServiceInfo info = serviceEvent.getInfo();
	String serviceName = info.getName();
	// 以下うんたら
}

でうんたらします。PutOnはPutOn同士のサービスが見つからないと点滅信号を表示しないので、自分から接続する役ではなくてもブラウズはします。

JavaでBlowfishを使った複合化を行う

PutOnは通信内溶をBlowfishで暗号化し、鍵を点滅信号で伝達します。JavaでBlowfishを使った暗号化・複合化をするには、Cipherを使えば出来るみたいです。

byte[] decryptBytesWithKey(byte[] cryptBytes, byte[] keyBytes, byte[] initVectorBytes) {
	byte[] result = null;
	try {
		// Blowfish、CBCモード、PKCS7パディングで
		Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS7Padding");
		// 鍵
		SecretKeySpec key = new SecretKeySpec(keyBytes, "Blowfish");
		// 初期化ベクトル
		IvParameterSpec initVector = new IvParameterSpec(initVectorBytes);
		cipher.init(Cipher.DECRYPT_MODE, key, initVector);
		result = cipher.doFinal(cryptBytes);
			
	} catch (Exception e) {
		e.printStackTrace();
		// とかなんとか
	}
	return result;
}

あ、でも上記のコードはこれだけでは動きません。Java SE 6ではPKCS7パディングが無いらしいんです(暗号化ならPKCS5を使えばいいらしいんですけど。クソッタレですね)。そこで、Bouncy Castleというのを見つけてきました。MIT license。
これのProviderをつっこめばPKCS7Paddingが使えるようですよ。

Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 

JavaJSONをなんかする

Javaは今7でしたっけ?なんか6ではJSONのパースは標準でついてないらしいんですよ(クソッタレですね)。PutOnはファイルに関するメタデータとかをJSONで送っているのでこれでは困ります。そこでJSONICというのを見つけてきました。Javaのコレクションとか基本クラスとJSONをシンプルに変換できるようですよ。Apache License 2.0。

byte[] infoBytes; //これに復号したbyte[]を入れとく
Charset utf8CharSet = Charset.forName("UTF-8");
String jsonString = new String(infoBytes, utf8CharSet);
HashMap info = JSON.decode(jsonString); // これだけ。簡単ですね
Number fileLengthNumber = (Number) info.get("fileLength");
fileLength = fileLengthNumber.longValue();
String tempFileName = (String) info.get("fileName");

他気づいたこと

そんな感じでPutOnのJava版が出来そうですよ。Windows PCで動くといいけど。
作ってて気づいたのですが、PutOnとPutOn for Macは通常Bluetoothでは通信できません。でもBluetoothテザリングで接続しているiPhoneMac間だとできるんですね。当たり前か。
Javaで書けるってことはAndroidでも出来るのかもしれませんが、なんかAndroidだとマルチキャストがデフォルトで無効とかそういうのがあるようですよ。

JmDNSによるBonjourのサービス解決 - Kazzzの日記
http://d.hatena.ne.jp/Kazzz/20130305/p1