2012-02-04
複数台の Wiimote をPCに認識させて使う
dev |
![]()
タイトルの通りですが、snesgtだったりPowerPointだったりいろいろな使い道のあるHIDことWiiリモコンですが、制御ソフトを開発する人の多くは一人プレイヤーだからかどういうわけか1台のみ制御可というソフトが多いところで、複数台(Bluetoothの限界上は7台?)のWiimoteを認識させる場合について。
Wiimote自体は多数の機能を持っているため、例えばモーションセンサやジャイロなどの機能を使おうとすると場合によっては複数のソフトウェアやドライバが必要になりますが、通常のソフトウェアでの使用であれば、キーボードエミュレーションの機能があれば十二分という場合が殆どであり、その場合は GlovePie 一つで事足ります。
また、Bluetoothスタックによるかも知れませんが、少なくとも東芝スタックにおいては、Wiimote自体は汎用HID(ヒューマンインターフェースデバイス)としてシステムに登録されるため、追加のデバイスドライバも必要にはなりません。従って、Win7以降、x64/x86の環境による違いを意識する必要がほとんど無いということも利点となります。
今回はWiimote+GlovePieという例ですが、これはPS3のコントローラーと混載した環境でも成立します。従って、複数人で何かしらのソフトウェアを用いる場合、ソフトウェア側で予め適切にキーの割り当てを行っておき、そのキーに対し、GlovePie側でWiimoteもしくはPS3のコントローラーボタンを各キーに割り当てるという作業を行うことで、多数のWiimoteを使用する環境が成立するという仕組みです。
なお、MotionPlus内蔵のWiimoteは、現時点で最新版の GlovePie 0.45 では対応していないようで、こちらの記事から別途 PIEFree.exe のみ取得する必要があります。
以下に複数台の Wiimote を使用する場合のスクリプト例を記載します。wiimoteオブジェクトの配列、とかではなく、認識した順番に wiimote1, wiimote2,…というような割り当てになるようなので、そのまま順次記載していけば良いと思われます。
wiimote1.Led1=true key.A=wiimote1.One key.B=wiimote1.Two key.C=wiimote1.Up key.D=wiimote1.Right key.E=wiimote1.Down Key.F=wiimote1.Left wiimote2.Led2=true key.G=wiimote2.One key.H=wiimote2.Two key.I=wiimote2.Up key.J=wiimote2.Right key.K=wiimote2.Down key.L=wiimote2.Left wiimote3.Led3=true key.M=wiimote3.One key.N=wiimote3.Two key.O=wiimote3.Up key.P=wiimote3.Right key.Q=wiimote3.Down key.R=wiimote3.Left wiimote4.Led4=true key.S=wiimote4.One key.T=wiimote4.Two key.U=wiimote4.Up key.V=wiimote4.Right key.W=wiimote4.Down key.X=wiimote4.Left
2011-12-17
もうパスワードで悩まない!
dev |
![]()
とある通販サイトで適当にパスワード打ち込んだら、後日思い出せなくなって涙目になったのでカッとなってパスワードジェネレーターを作ってみた。
Bookmarklet にしてご利用ください。Firefox 8.0でのみ動作確認しています。
概要
ソースコードは最下部に記載しています。ドメイン名とマスターパスワード(Salt)から、都度パスワードを算出する Bookmarklet です。マスターパスワードが変わらない限り、同じドメインのページでは常に同じパスワードが発行されますので、もうこのサイトのパスワード何だったっけ、と頭を悩ます必要がありません。ただし、マスターパスワードが漏洩すると元も子もありませんので、マスターパスワードの管理だけはしっかりするようにしてください。
初回実行時には記号使用の有無と文字数を聞かれます。これらの値はブラウザの LocalStorage に保存されるため、履歴が有効であれば次回以降は生成されたパスワードのみが表示されます。
下記ソースをコピーし、都度マスターパスワードを打ち込む場合は
var salt = prompt('Salt?');
に、いちいち打ち込むのが面倒くさい場合は
var salt = '適当なパスワードに書き変えてください';
としてください。書き換える場合は日本語でもアルファベットでも構いませんが、強度的には日本語の方が望ましいです。
詳細
元ネタはこちら。ソースが Obsolete になってたので、書き直すついでに Bookmarklet 化して、HMACを使うようにアルゴリズムを変更。
設定した Salt を秘密鍵に、ドメイン名をメッセージとして HMAC を算出し、求めたバイト列を元に、パスワードとして使う文字列を切り出しています。一応、なんちゃって重複排除機能を搭載。
Salt は UTF-8 対応の文字列であれば何でも構いません。日本語の文章でも問題はなく、むしろ攻撃回避の観点からはそちらの方が望ましいとも言えます。また、HMACの構造上、仮に算出されたパスワードから HMAC ハッシュ値が判明したとしても、元の秘密鍵にたどり着くことはできませんが、念のためパスワード文字列の算出に用いる文字列をシャッフルして、パスワードからハッシュ値への解読をしにくいような対策を施しています*1。
HMAC-SHA256 の算出には Crypto-js を使用しており、動的にコードを読みに行っています。このため、外部コードの読み込みにかかるタイムラグを回避するため、 setTimeout で時間差実行しています。
パスワード算出には乱数・変数を使用していませんので、 salt を変更しない限り、あるドメインに対して常に同じパスワードが返されます。これは、例えば毎月パスワードを変えている、などという場合は不便になるので、
var hmacBytes = Crypto.HMAC(Crypto.SHA256, document.domain + '_' + (new Date()).getFullYear().toString() + ((new Date()).getMonth() + 1).toString(), salt, { asBytes: true });
というように書き換える等の対処が必要です。
問題点
といったところでしょうか。
強度の確認
英大文字・小文字、数字および指示がある場合は記号も、必ず一字は使用するような設計にしています。強度確認は パスワード チェッカー: 安全性の高いパスワードの使用 | Microsoft セキュリティ が便利です。
ソース
javascript: (function(){ /* Salt がマスターパスワードになります。漏らさないよう注意。 */ var salt = prompt('Salt?') || '適当なパスワードに書き変えてください'; if (salt == '' || typeof(salt) != 'string') { alert('salt が設定されていません!'); }else{ if(!document.getElementById('crypto-sha256-hmac')){ var s = document.createElement('script'); s.id = 'crypto-sha256-hmac'; s.src = 'https://crypto-js.googlecode.com/files/2.3.0-crypto-sha256-hmac.js'; void(document.body.appendChild(s)); } var mod = function(e, s){ return Math.abs(parseInt(e)) % s.length; }; var shuffle = function(s, seed){ var q = ''; var p = -2; while(mod(seed.slice(p-1, p), s) == mod(seed.slice(p, p+1), s)){ if ( Math.abs(p-2) > seed.length ){ p = -2; break; }else{ p--; } } var border = [ mod(seed.slice(p-1, p), s), mod(seed.slice(p, p+1), s)].sort(function(a,b){return a-b;}); switch ( mod(seed.slice(p-2, p-1), 'aaa') ){ case 0: q = s.substring(0, border[0]) + s.substring(border[1]) + s.substring(border[0], border[1]); break; case 1: q = s.substring(border[0], border[1]) + s.substring(0, border[0]) + s.substring(border[1]); break; case 2: q = s.substring(border[1]) + s.substring(border[0], border[1]) + s.substring(0, border[0]); break; } return q; }; var digit = '0123456789'; var uc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; var lc = 'abcdefghijklmnopqrstuvwxyz'; var symbol = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; var allstr = ''; for(var i=33; i<127; i++){ allstr += String.fromCharCode(i); } var genPass = function(binarray, len, isUseSymbol){ var i = 3; var src = ''; var str = []; var dupCheck = true; var isDuplicated = false; var duplication = []; str.push(uc.charAt(mod(binarray[0], uc))); str.push(lc.charAt(mod(binarray[1], lc))); str.push(digit.charAt(mod(binarray[2], digit))); if(isUseSymbol){ str.push(symbol.charAt(mod(binarray[3], symbol))); i = 4; src = shuffle(allstr, binarray); }else{ src = shuffle(digit + uc + lc, binarray); } while(str.length < len){ if(dupCheck){ for(var j = 0; j < str.length; j++){ if( str[j] == src.charAt(mod(binarray[i], src)) ){ isDuplicated = true; duplication.push(mod(binarray[i], src)); break; } } } if(!isDuplicated){ str.push(src.charAt(mod(binarray[i], src))); } isDuplicated = false; i++; if (i >= binarray.length) { for(var k = 0; k < duplication.length; k++){ if(str.length < len){ str.push(src.charAt(duplication[k])); } } dupCheck = false; i = 0; } } return shuffle(str.join(''), binarray); }; setTimeout(function(){ var hmacBytes = Crypto.HMAC(Crypto.SHA256, document.domain, salt, { asBytes: true }); var l = parseInt(localStorage.getItem('length')); var useSymbol = eval(localStorage.getItem('useSymbol')); var q = 3; if ( ( isNaN(l) ) || ( typeof(useSymbol) != 'boolean' ) ) { useSymbol = window.confirm('記号を使用しますか?'); if (useSymbol) { q = 4; } l = prompt(q + '文字以上、' + hmacBytes.length + '文字以下で文字数を設定してください。', 10); if ( ( !isNaN(parseInt(l)) ) && ( parseInt(l) <= hmacBytes.length ) && ( parseInt(l) >= q ) ){ localStorage.setItem('length', l.toString()); localStorage.setItem('useSymbol', useSymbol.toString()); }else{ return 0; } } window.prompt('Pass phrase:', genPass(hmacBytes, l, useSymbol)); }, 500); } })();
2011-11-23
Chromium Updator 改
dev |
![]()
Google が開発に参加している Chromium ブラウザの自動更新スクリプトが、 Google のサーバー構成変更に伴って久しく使えなくなっていたので手直ししました。
see) http://d.hatena.ne.jp/RobinEgg/20090818/p1
必要なもの:
- Wget for win32
- 7za / Command line version でOK
全てのバイナリを
"%USERPROFILE%\AppData\Local\Chromium"
@echo off setlocal set CHROME="%USERPROFILE%\AppData\Local\Chromium" if not exist %CHROME% mkdir %CHROME% cd /d %CHROME% if exist %CHROME%\revision move /Y revision revision.old wget "http://commondatastorage.googleapis.com/chromium-browser-continuous/Win/LAST_CHANGE" -O .\revision if not exist %CHROME%\revision.old goto getNew fc .\revision .\revision.old > nul if errorlevel 1 goto getNew exit /b :getNew del /f /s /q .\chrome-win32.zip for /f "usebackq tokens=*" %%i in (`type revision`) do @set REV=%%i wget "http://commondatastorage.googleapis.com/chromium-browser-continuous/Win/%REV%/chrome-win32.zip" -O .\chrome-win32.zip if errorlevel 0 7za.exe x -y .\chrome-win32.zip rd /S /Q Application move /Y chrome-win32 Application if errorlevel 1 del /F /Q revision* endlocal
相変わらずエラー処理も何もないですが、ご自由にお使いください。
2011-07-10
SimpleDLNAServer 公開
dev |
![]()
フリーの UPnP AV / DLNA サーバーを公開しました。
開発動機
- PS3 Media Server(以下PMS)は非常に高性能ですが、高性能故に動作が緩慢だったり、設定項目が多すぎて簡単に使えないというジレンマがあった。
- PMSでmp3配信を行うと、酷い文字化けに遭遇することが多かった。
- 折角クライアントに PS3 を使うのに、生 ts の配信に難があったり、トランスコードに頭を悩ませるのはおかしい。
- foobar2000 + foo_upnp.dll では、ライブラリ内容を一気に配信しようとするため、数百エレメントを持つライブラリだとクライアント側が非常にしんどいことになる。
などなどです。
流石にネットワーク周りまで一から書き上げることは難しいと思っていたため、フリーのライブラリはないかしらと探していたところ、Platinum UPnP というライブラリがあり、ドキュメントは整備されていないもののサンプルプログラムが付属していたため、これを参考に書き上げました。
特徴
上記開発動機で書いた悩みを解消するのが最大の目的でしたので、それがそのまま特徴になります。
- 配信するフォルダと拡張子を指定するだけの簡単設定
- トランスコードや外部アプリケーションとの連携は行わない
- 生 ts 、 M2TS の配信にも対応
- mp3 の文字コード問題(Id3 latin1/SJIS問題)を解消
- 数百エレメントを持つ巨大ライブラリは分割して配信
- 配信エレメントは拡張子でフィルタリング、拡張子に対応する Mimetype はユーザー側で編集可能
- 要求がある度リアルタイムにディレクトリ構造を読みに行くため、録画鯖にも最適
などなど。
対象
PS3 (とXBOX)がメインターゲットですが、そのほかの DLNA クライアントでも動作は可能だと思います。実際、手元では
- foobar2000 + foo_upnp.dll
- Skifta on Android 2.3
で正常動作を確認しています。ただし、最近発売されている薄型テレビでは各社独自の実装が行われているようですので、これについては動作するかどうかは一切検証していませんし、わかりません。
開発環境・依存関係
Platinum を使用している関係上、ライセンスは GPL v2 (もしくはそれ以降)となります。
詳細
2011-07-09
.net/C# で GetDetailsOf を使う
dev |
![]()
音楽や動画ファイルのメタデータを取得する際、 taglib などの外部ライブラリを使用しても良いのですが、使う範囲が限られているのであれば、 Windows 標準の shell32.dll を使用するのも一つの選択肢です。
ただ、これには弱点があり、 XP/Vista/7 間で微妙に互換性がないことと、特に AAC/mp4 ファイルのメタデータ取得ができたりできなかったり、という問題があります。
エクスプローラーの詳細表示と同じ情報を取得できるので、特に文字化けを気にしなくて済むというのは利点ですが、AAC 系のファイルは XP では扱いづらいこと、また未だに XP マシンが跋扈している現状、使い勝手は正直良くないですが、とりあえず載せてみます。
3OS 間で互換性がない、というのは、 Shell32.ShellClass のインスタンスを作成する際、例えば 7 で開発したものをそのまま XP に持って行くと、 InvalidCastException が発生し、インスタンスを作れない、というものです。これを回避するには、 Activator.CreateInstance メソッドを利用します。
shell32.dll は未だに COM Component ですので、参照設定から "Microsoft Shell Controls And Automation" を登録しておきます。
public void test() { Shell32.Folder folder = null; string someDirectory = @"c:\hoge\fuga"; string someFile = @"test.mp3"; try { Type wshell = Type.GetTypeFromProgID("Shell.Application"); object wshInstance = Activator.CreateInstance(wshell); folder = (Shell32.Folder)wshell.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, wshInstance, new object[] { someDirectory }); } catch { } Shell32.FolderItem folderitem = folder.ParseName(someFile); // 再生時間の表示。ただし7上。XPでは 27 ではなく 21 となる。 System.Diagnostics.Debug.WriteLine(folder.GetDetailsOf(folderitem, 27)); }