2004/05/26

実は当人たちの間では他の人と変わらないごく普通の関係のつもりだったりするものの、周りが普通の関係と見てくれないために結果的に二人の間で過剰に関係に気を使ってしまうために気まずい関係になる、そんな(たしか)ヤマアラシのジレンマ。

二人で話している分には別に冗談がエスカレートしても冗談と分かり切っているのでなんでもないことが、他人がいる場では必要以上に気を使わなきゃならなくなってしまうと、一旦関係をリセットした方がよいのかと思うことがあっても、当人達がリセットしても周りの目はリセットされないためにそれまでの関係を引きずることになってしまって結局改善されない関係。

いや、別に誰かのことを指していってるのではないですよ(´ー`)? 別にHTMLソース見てもこれ以上深いことはなんも書いてませんよ(´ー`)?

ROのはなし

マイグレーション

とりあえず相方がサーバー移住しないのでもちろん移住する気にもならず、そのままFenrir残留。まぁ、相方が移住すると言いだしても全力で引き留めるつもりではいましたがね。

で、カーニバルイベントがあったわけですが、各ダンジョン最下層をつなげたダンジョンをたどっていくとなんかアーチャースケルトンとアジト宝箱が出る場所に降り立ちまして、ニューマだしまくりでひたすら宝箱を空ける計5時間。スパナ2本とか超微妙なものをゲットしつつ、せっかくスパナがあるのでMBクリップとかを借りて(もらって)遊んでみたり、課金開始直後以来久々(実に1年半ぶりです)に当時いたギルドのマスターに再会してみたり(スパナ2本もいらないので1本あげたら悪魔HBくれました)。

あとはほお紅やらウサミミやらをもらったりしてそれなりに楽しんでみました。まぁ、肝心のピンク髪+ウサミミ+ほお紅+花びら+♀プリとかいうゴールデンショット(何)のスクリーンショットはすっかり撮り忘れてましたが。今日研究室の飲み会でROに限った話としてではなく一般的に「ピンク髪がいいー」とか「ウサミミがいいー」とか語っていた先輩とかを見て微妙にニヤニヤしていたのは、ココだけのヒミツということで。

超久しぶりに会った
ひっさびさに会った昔いたギルドのマスター(左)

ところで、今回のマイグレーションイベントは普段のROの世界観とはちょっとちがう「パラレルワールドの世界」という舞台設定になってたはずなんですが、GMメッセージで流れたメッセージを読み返してみると、

ニュース放送員 : イズルードからのニュースです。
ニュース放送員 : (有)闇転送が(株)闇転送となり、装いも新たに闇転送を始められたそうです。
ニュース放送員 : 話しかける際は十分にご注意ください。

(有)闇転送というのは第1回マイグレーションイベント(2003年6月末)に限定的に登場したNPCでして、あのときのマイグレーションイベントシナリオによると、あのときの舞台設定はパラレルワールドということは明記されず、突然モンスターによる大襲撃が始まったという触れではじまった4日間のイベントだったはず。ということは、第1回の時の(有)闇転送と第2回の(株)闇転送ではシナリオの舞台が違う(第2回はパラレルワールド扱い)ので関連性がねぇじゃねぇかっ(つまり、舞台背景の関連性がないので「装いも新たに」という表現はおかしい)。

なんて些細なことを突っ込んでみたくなるのはたぶん私だけ…でしょうね。

PacketLength解析

さて、10日ぐらい前にMitsukiさんのところで既に出てる話ですが、RoAddr.iniのPacketLengthの指してるアドレスから実際のOP長を求めるにはどうしたらいいんだろう…というか、二分木ってなんやねんという話をしてまして、もう既に解析は終了してるんですが、とりあえず実際にPacketLengthのアドレスからOPパケット長テーブルを導出する方法を記した日本語リソースがないようなので、ここにメモという形で掲載しておきます。

まず、PacketLengthの指すアドレスをDWORDで参照すると、どっかのアドレスが入っています。これが、二分木のルートノード構造体を指すアドレスです。ここで、このOP長を格納したノードがどのような構造になっているかというと、ツリー構成のためのアドレスが3つ続き、その次のDWORDでOPコード、そのまた次のDWORDがOPコードに対応するOP長となっています。とりあえずOPとかの用語はRTXの人の用語で統一してます。

で、ノードに入ったアドレス3つが何かを指しているわけですが、ざっと線でつないでいくと、第1アドレスと第3アドレスでツリーの右左を構成しているようなので、とりあえずノードを第3アドレス、第1アドレス、第2アドレスの順にたどればOKっぽい雰囲気なのでコード書いて検証してみたら、見事にスタックがあふれたので(ここまでは予想通り)、再帰の終了条件に第2アドレスの親ノードチェックをいれたら見事に停止。

あとはルートノードのどのアドレスからはじめるかなんですが、私の環境では第1アドレスからたどって普通に終了したものの、Mitsukiさんの所では停止せず、Mitsukiさんのところでは第2アドレスからはじめれば停止するという現象が観測されてさぁどうしようかと思ったんですが、結局Mitsukiさんが逆アセンブルしたところルートの第2アドレスからツリーをたどり始めて、第3,第1アドレスをたどっていけばOK(第2アドレスはそもそもすべて親を指しているようなのでたどる必要がない模様)との結論に。

いやぁ、私はまだまだ経験が浅くて逆アセンブルとかは全然出来ない(ということになっているacidlemon的公式スタンス)ので、逆アセンブルとかしてプログラム解析ができるのはすごいなぁ(まぁ、すごい云々よりもなにより「便利だなぁ」ということですよね、実際)なんてことを思いつつ、とりあえずこの情報でコードがかけたの同然なだとは思いますが、一応OP長テーブル導出関数のサンプルでも出しておきます。

#define MAX_OP 0x280 // now jRO's OP range is 0x0064 ~ 0x0200

int PacketLength[MAX_OP];

struct OPtree{
    DWORD addr1; // 1st address as left-tree
    DWORD addr2; // 2nd address as parent address
    DWORD addr3; // 3rd address as right-tree
    DWORD OPcode; // OP code
    DWORD OPlen; // OP length
};

void ParseWorker(HANDLE hProcess, DWORD addr){
    OPtree tmp;
    ReadProcessMemory(hProcess, (LPVOID)addr, &tmp, sizeof(OPtree), NULL);
    if(tmp.OPcode<MAX_OP){ // check buffer overflow
        PacketLength[tmp.OPcode]=tmp.OPlen;
        if(tmp.addr3){
            ParseWorker(hProcess, tmp.addr3);
        }
        if(tmp.addr1){
            ParseWorker(hProcess, tmp.addr1);
        }
    }
    return;
}

void ParsePacketLength(HANDLE hProcess, DWORD dwPacketLength){
    OPtree root;
    DWORD OP;

    ZeroMemory(PacketLength, sizeof(int)*MAX_OP);
    ReadProcessMemory(hProcess, (LPVOID)dwPacketLength, &OP, 4, NULL);
    ReadProcessMemory(hProcess, (LPVOID)OP, &root, sizeof(OPtree), NULL);

    ParseWorker(hProcess, (DWORD)root.addr2); // parse start from root's addr2
    return;
}

まぁ、動くかどうか検証してませんけど、たぶん動くんじゃないかなー。使い方はROのプロセスハンドルとRoAddr.iniのPacketLengthの値を突っ込んでParsePacketLengthを呼んでみてください。ParseWorkerががんばって再帰してツリーを走査してPacketLengthの配列に突っ込んでくれます。MAX_OPはとりあえず余裕もって0x280まで確保してます(マイグレ明けに来たパッチで0x0200までOPが増えましたね)。

うされもん @acidlemonについて

|'-')/ acidlemonです。鎌倉市在住で、鎌倉で働く普通のITエンジニアです。

30年弱住んだ北海道を離れ、鎌倉でまったりぽわぽわしています。

外部サイト情報

  • twitter
  • github
  • facebook
  • instagram
  • work on kayac