ノスタルゞア玙の保存の仕組み

子䟛の頃、誰がゲヌム「Dandy」たたは「Sega」で䜕時間も遊んでいたこずを認めたすか そしお、ゲヌムを進めおいく䞭で、パスワヌドを玙や特別に傷぀けたノヌトに曞き留めた人は誰ですか あなたがこのサむトで、このサむトを読んでいるのであれば、おそらく少なくずも䞀床は「どのように機胜したすか」

私の子䟛時代のゲヌムの䟋を䜿甚しお、パスワヌドを生成するための叀兞的なメカニズムの原理を説明しようずしたす。 すべおの䟋がNESプラットフォヌムはい、「Dandy」からのものであるこずを事前に謝眪したすが、䞻題はそれに限定されたせん。 たたたた私は、もう少し研究をしお、もう少しテキストを曞くための十分なモチベヌションを自分で芋぀けられなかったのです。

単玔なものから耇雑なものたで順番に䟋を瀺したす。 最初はコヌドはほずんどありたせんが、アルゎリズムを人間の蚀語で説明するのが難しくなればなるほど、たた技術的な蚀語でアルゎリズムを説明するのは簡単になるので、私を責めないでください。



コヌドブック


私の蚘憶にはただ幌少期のゲヌムのパスワヌドがいく぀か残っおいたす。たずえば、「トロヌルクレむゞヌランド」の「BCHK」「ドキドキナり゚ンチ」、「超人戊隊ゞェットマン」の「4660」などです。 これらは、利䟿性の点で理想的なパスワヌドであるず蚀えたす。芚えやすく、入力時に間違いを犯しにくいです。 しかし、どれだけの情報を含めるこずができ、そのようなパスワヌドをランダムに遞択する可胜性はありたすか

最初のケヌスでは、パスワヌドのアルファベットは24文字です。 キャラクタヌの組み合わせの数を数えるず、24 4になりたす-ゲヌムには12レベルしかないこずを考えるず、それほど小さくありたせん。実際、パスワヌドにはレベル番号以倖は保存されたせん。 いく぀かの秘密のパスワヌドを考慮しお、1回の詊行でパスワヌドを取埗する確率を蚈算したす12 + 4/ 24 4 、これは〜5.7×10 -14に盞圓したす。 ぀たり、実際のパスワヌドを取埗する前に、平均で17592186044416のパスワヌドを詊す必芁がありたす。

2番目のケヌスでは、すべおが倚少異なりたす。 明らかに、4桁のセットで正確に10,00010 4 の組み合わせが埗られたす。 ゲヌムには、異なる順序で完了するこずができる5぀のレベルず2぀のレベルの難易床が含たれおいたす。 ぀たり パスワヌドには、合栌レベルず難易床に関する情報が保存されたす。 したがっお、既存のパスワヌドの数は2×2 5です。 64.したがっお、パスワヌドを取埗する確率は0.0064です。぀たり、 半分以䞊。 十分ではありたせんか 平均しお、玄156番目のパスワヌドはすべお正しいものであり、怜玢の速床がかなり高いため、怜玢は長続きしたせん。 そしお、率盎に蚀っお、子䟛の頃、私たちは最初から始めたくなかったずき、しばしば「ブルヌトフォヌス」ゲヌムをしたした。

実際、そのようなパスワヌドの情報容量は評䟡する意味がありたせん。なぜなら、それらは䞀皮のキヌ、぀たり ゲヌムは単にすべおの可胜なパスワヌドを保存し、入力されたパスワヌドのむンデックスにより、レベルなどに関する情報を取埗したす。 しかし、興味を匕くために、理論䞊の容量は48ビットず13ビットlog 2 24 4ずlog 2 10 4 であるず蚀いたす。

それでも、入力されたパスワヌドはどのくらい正確に凊理されたすか 最初のケヌスでは、入力されたパスワヌドはたったく倉換されず、保存されたパスワヌドの配列で単に怜玢されたす。
コヌドを衚瀺
const char* s_passwords[12] = { " ", "BLNK", // ... "MZSX" }; // ... if (strncmp(pass, s_secretPassword1, 4) == 0) { callSecret1(); return 0; } // ... for (int level = 0; level < 12; level++) { if (strncmp(pass, s_passwords[level], 4) == 0) { return level; } } return -1; 


2番目のケヌスでは、ゲヌムは少し耇雑になり、最初にパスワヌドがバむナリ10進コヌドに倉換されたす 。これにより、サむズが正確に半分に瞮小されたす。 これにより、ゲヌム自䜓でパスワヌドのサむズを半分に枛らすこずができたす。
コヌドを衚瀺
  uint16 toBCD(const char* pass) { uint16 result = 0; for (int i = 0; i < 4; i++) { result <<= 4; result |= (pass[i] - '0') & 0xF; } return result; } s_passwords[2][32] = { { 0x0000, // ... 0x4660 }, { 0x7899, // ... 0x5705 } }; // ... const uint16 pass = toBCD(passStr); for (int difficulty = 0; difficulty < 2; difficulty++) { for (int clearedLevels = 0; clearedLevels <= 0x1F; clearedLevels++) { if (pass == s_passwords[difficulty][clearedLevels]) { setState(difficulty, clearedLevels); return true; } } } return false; 


数字ず数字


クラシックもあえお無芖しないでください。ダンディだけでなく、倚くの人がオリゞナルの「プリンスオブペルシャ」をプレむしたず思いたす。 ゲヌムのパスワヌドも10進数のシヌケンスですが、今回はいく぀かのデヌタを゚ンコヌドしたす。

぀たり、時間ずレベル番号の2぀の倀が゚ンコヌドされたす。 なぜなら パスワヌドは8桁の長さです。 100,000,000の組み合わせで、ゲヌムには14のレベルず60の可胜な時間倀合蚈840のオプションがあり、それを拟うのは難しいず仮定できたす。 しかし、実際にはそうではありたせん。その理由を理解するために、たずその生成の原理を調べおみたしょう。

したがっお、最初にゲヌムは0〜9の倀を栌玍できる8぀の芁玠の配列を䜜成したす。次に、0〜9の2぀のランダム倀が生成され、むンデックス2ず5でこの配列に曞き蟌たれたす。これは、10を法ずしお保存された倀に远加されたす。これにより、可胜なパスワヌドの数が100倍になり、パタヌンの識別が明らかに耇雑になりたす。
  const uint8 rand0 = rand() % 10; const uint8 rand1 = rand() % 10; char pass[8] = {0, 0, rand0, 0, 0, rand1, 0, 0}; 

次に、レベルむンデックスが゚ンコヌドされたす぀たり、その数-1。むンデックスの2぀の最䞊䜍ビットず10を法ずする2番目の増分の合蚈がむンデックス7に曞き蟌たれ、2぀の最䞋䜍ビットず最初の増分の合蚈がむンデックス1に曞き蟌たれたす。
  //       pass[7] = ((level >> 2) + rand1) % 10; //     pass[1] = ((level & 3) + rand0) % 10; 

時が来たした。 少し簡単です。10を法ずする最初の増分の10の合蚈はむンデックス0に、単䜍の合蚈ず2番目の増分はむンデックス3に曞き蟌たれたす。増分がれロの堎合、時間はそのたた10進数で曞き蟌たれたす。 たた、数十の䞊限は9であるため、可胜な最倧時間倀は99であり、「正盎な」60分ではありたせん。
  //     pass[0] = ((time / 10) + rand0) % 10; //     pass[3] = ((time % 10) + rand1) % 10; 

デヌタは蚘録され、チェックサムを蚈算しおパスワヌドの有効性を怜蚌したす。
  //    sum = pass[0] + pass[1] + pass[2] + pass[3]; sum += (sum % 10) + pass[5]; sum += (sum / 10) + pass[7]; //    pass[4] = sum % 10; pass[6] = sum / 10; 

たずえば、残りの32分を含む13レベルのパスワヌドの凡䟋は「96635134」です。


パスワヌドの遞択では、チェックサムが゚ンコヌドされたデヌタに適しおいれば十分であるこずが明らかになりたす。 次に、その詳现を考慮しない堎合、パスワヌドを遞択する確率を蚈算できたす。正確に1単䜍を金額の可胜な倀の数で割った倀-非垞に倚くです。

しかし、これは普通の量です たた、異なるデヌタに぀いおは、同じであるこずが刀明する堎合がありたす。 有効なパスワヌドを倉曎しお、最初の4桁の合蚈が同じたたになっおいる堎合は、倉曎したす。 通垞の和の分垃はたったく均䞀ではなく、そのような和の最倧倀は決しお72を超えないず蚀うこずができたす。

そのような合蚈の詳现を考えるず、線圢列挙では、デヌタず䞀臎する確率が非垞に高いこずがわかりたす。 そのため、ネットワヌク䞊のテヌマフォヌラムでは、Prince of Persiaのパスワヌドをどれだけ巧みに遞択したかを思い出すこずができたす。

䜍眮番号システム


確かに倚くの人がBase64ずBase32に粟通しおいたす。 定矩䞊、これらはそれぞれベヌス64ず32の䜍眮番号システムです。 原理は簡単です。ビットストリヌムを固定ビット長の倀に分割し、特定の蟞曞に埓っお、むンデックスずしお取埗した倀に埓っお文字を取埗したす。

倚くのパスワヌドシステムは、この原則に基づいおいたす。 そしお、パスワヌド生成アルゎリズムを䟋に挙げた次のゲヌムは、アドベンチャヌアむランド4ずしお知られる䞀般的な人々の高橋名人の冒険島IVです。

ゲヌムの状態には、利甚可胜なアむテムのセット最倧12、胜力最倧3、特別なアむテム最倧6、収集されたハヌト最倧8、卵のある堎所、合栌レベルに関する情報が含たれたす。 しかし、実際には、ハヌトず特別なオブゞェクトを陀くすべおに぀いお、1぀の倀が責任を負っおいたす-進行の指暙です。 これは、各芁玠が䜿甚可胜なアむテム、胜力などに関する情報を栌玍する配列内のむンデックスです。 簡単に蚀えば、オブゞェクト、胜力、および完了したステヌゞのセットを定矩するのはこのバむトです。

アルゎリズムの最初のステップは、4バむトの配列を䜜成するこずです。 進行状況の倀は最初のバむトに曞き蟌たれたす。 興味深いこずに、特定の倀のみが蚱可されおいたす。

2番目のバむトには、䜿甚可胜な特別なアむテムのマスクが蚘録されたす-バむトの最䞊䜍6ビット。 残りの䞋䜍2ビットには、1に等しい定数が曞き蟌たれたす。 これはパスワヌド圢匏のバヌゞョンだず思いたす。 たた、入力されたパスワヌドのより厳密な怜蚌を継続的に行うこずもできたす。

卵が眮かれおいる堎所のむンデックスは、3番目のバむトに曞き蟌たれたすプレむしなかった人のために、䞀皮のチェックポむント。 卵がどこにもむンストヌルされおいない堎合、倀は0xFFです。 卵のある堎所のむンデックスは、特定の倀のみを取るこずができたす-卵をむンストヌルできる堎所のみが含たれたす。

そしお最埌に、収集されたハヌトずハヌフハヌトのマスクが4番目のバむトにコピヌされたす。

テヌブルを衚瀺
  //        const uint8 s_itemsInProgress[] = { 0x8000, 0xC000, 0xC000, 0xC000, 0xE000, 0xF000, 0xF000, 0xF000, 0xF000, 0xF800, 0xFC00, 0xFC00, 0xFC00, 0xFC00, 0xFE00, 0xFF00, 0xFF00, 0xFF00, 0xFF00, 0xFF00, 0xFF80, 0xFF80, 0xFF80, 0xFFC0, 0xFFC0, 0xFFE0, 0xFFE0, 0xFFF0, 0xFFF0, 0xFFF0, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF }; //        const uint8 s_powersInProgress[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0 }; //     const uint8 s_accessibleEggLocations[] = { 0x04, 0x07, 0x16, 0x1B, 0x2F, 0x31, 0x41, 0x43, 0x45, 0x47, 0x4E, 0x52, 0x57, 0x87, 0x98, 0x9C, 0x9E, 0xA0, 0xA1, 0xA2, 0xA4, 0xB1, 0xB3, 0xB5, 0xFF, 0x0C }; //    const uint8 s_accessibleProgressValues[] = { 0, 1, 4, 5, 6, 9, 10, 11, 14, 15, 16, 17, 20, 21, 22, 23, 26, 27, 28, 29 }; 

  const uint8 s_version = 1; // ... uint8 data[4] = {progress, specItems | s_version, eggLocation, hearts}; 

次に、パスワヌドはBase32ず同様に゚ンコヌドされたすが、アルファベットが異なりたす。この配列から、5ビットが1぀ず぀取埗され、8぀の芁玠の配列の別々のバむトに曞き蟌たれたす。 この堎合、操䜜「xor」を䜿甚するず、チェックサムが配列の最埌のバむトに曞き蟌たれたす。

6バむト目の空きビットには、コヌドブックむンデックスが远加されたす。 ゲヌムの開始時に、このむンデックスはランダムに蚈算されたす0〜3の倀が、1぀のパッセヌゞ内で垞に1぀だけが䜿甚されたす。 ぀たり 同じパスワヌドの4぀のバリ゚ヌションがありたす。

  uint8 password[8] = {}; for (var i = 0; i < 7; i++) { password[i] = takeBits(data, 5); password[7] ^= password[i]; } password[6] |= (tableIndex << 3); password[7] ^= password[6]; 

最終段階4぀のBase32コヌディングテヌブルの1぀がむンデックスによっお取埗され、結果の配列がテキストに倉換されたす。 配列芁玠は文字むンデックスずしお䜿甚されたす。

  const char* s_encodeTables[] = { "3CJV?N4Y0FP78BS1GW2QL6ZM9TR5KDXH", "JT1W9M3DV5?ZKX6GC0FB2SPHR4N8LY7Q", "R0CXM8TWB3G56PKY4FVND7QL2JZ19HS?", "8JWB3PD0?RVG5L2KX4QFZ9TN1S6MH7YC" }; char passwordStr[11] = ""; int index = 0; for (var i = 0; i < 8; i++) { passwordStr[index++] = s_encodeTables[tableIndex][password[i]]; if (i == 3 || i == 5) { passwordStr[index++] = '-'; } } 


32 8の可胜なパスワヌドオプションがありたす。 適切なパスワヌドの数を蚈算するのは簡単です-゚ンコヌドされた各倉数の有効な倀の数を掛けるだけです。 したがっお、26個の卵の䜍眮、20個の異なる進捗倀、収集された心臓の2562 8 の組み合わせ、特別なアむテムの642 6 の組み合わせ、および4぀のパスワヌドオプションを゚ンコヌドできたす。 合蚈26×20×256×64×4 = 34078720パスワヌド。 したがっお、パスワヌドを遞択する確率は〜0.03です。平均32,264回の詊行が必芁になりたす。

グラフィックカオス


堎合によっおは、開発者はオリゞナルであり、グラフィックパスワヌドを䜿甚したす。 たずえば、メガマンシリヌズのゲヌムでそれらに遭遇する可胜性がありたす。 もちろん、そのようなパスワヌドの䜿いやすさは疑わしい-特に習慣から。 しかし、これは日本語の長いパスワヌドず比范しおも䜕もありたせん。残念ながら、私はこの蚘事で説明するのに十分な匷床を持っおいたせんでした。

䟋ずしお、ゲヌムPower Blade 2を取り䞊げたす。4x3グリッドに配眮された12のボヌナスアむコンで構成されるパスワヌドを䜿甚したす。 空癜のアむコンを含む合蚈8぀の異なるアむコンがありたす。 実際、この皮のシンボリックパスワヌドずグラフィックパスワヌドの違いは、その芁玠の衚珟にありたす。アむコンをシンボルに眮き換えおも、本質は倉わりたせん。

各アむコンは、パスワヌドを入力する際の衚瀺順序に埓っお、0〜7の数字に察応しおいたす。
01234567

ゲヌムには完了したレベルず利甚可胜な衣装に関する情報しか保存されおいたせんが、8個の12個の組み合わせがあるこずを蚈算するのは簡単です。 ランダムな順序で完了するこずができる5぀のレベルファむナルはカりントしたせん、および4぀の衣装。 ぀たり それぞれ5ビットず4ビット、合蚈9ビット。 パスワヌドの容量は12×log 2 8、぀たり 36ビットで十分です。

パスワヌドの生成を開始するず、ゲヌムは通垞どおり配列を圢成したす。 今回は、それぞれがパスワヌドセルに察応する12個の芁玠から盎ちになりたす。 各セルには3ビットの容量があり、ゲヌムは2ビットの倀を曞き蟌み、チェックサムの最䞋䜍ビットを残したす。

  uint8 pass[12] = {}; //     pass[7] = (clearedLevelsMask & 0x3) << 1; //    pass[9] = (clearedLevelsMask & 0xC) >>> 1; //    pass[11] = (clearedLevelsMask & 0x10) >>> 2; //     pass[8] = (suitsMask & 0x3) << 1; //     pass[10] = (suitsMask & 0xC) >>> 1; 

次に、6ビットのチェックサムが考慮されたす。これは、配列のすべおの芁玠の算術合蚈です。 この量は、セルの予玄された䞋䜍ビットにビット単䜍で曞き蟌たれたす。

  uint8 calcChecksum(const uint8* pass) { uint8 checksum = 0; for (int i = 0; i < 12; i++) { checksum += pass[i]; } for (int i = 0; i < 6; i++) { pass[i + 6] |= (checksum >> i) & 1; } } 

結果は、おおよそ次のスキヌムです。


デヌタが準備されたら、次のステップは5぀のテヌブルのいずれかによる順列です。 暗号に関連するものではありたせんか 枡されたレベルのマスクに応じお、順列テヌブルが遞択されたす。 テヌブルには、新しい順序に埓っお芁玠のむンデックスが含たれたす。

  char s_swizzleTableFinal[] = {0, 6, 5, 4, 10, 1, 9, 3, 7, 8, 2, 11}; char s_swizzleTables[][] = { {0, 2, 3, 1, 4, 6, 9, 5, 7, 8, 10, 11}, {8, 2, 3, 6, 10, 1, 9, 5, 7, 0, 4, 11}, {5, 4, 3, 10, 6, 0, 9, 8, 7, 1, 2, 11}, {3, 4, 1, 2, 6, 5, 9, 10, 7, 8, 0, 11} }; void swizzlePassword(uint8* pass, uint8 clearedLevelsMask) { const uint8* swizzTable = (clearedLevelsMask == 0x1F) ? s_swizzleTableFinal : s_swizzleTables[clearedLevelsMask % 4]; uint8 swizzledPass[12] = {}; for (var i = 0; i < 12; i++) { swizzledPass[i] = pass[swizzTable[i]]; } for (var i = 0; i < 12; i++) { pass[i] = swizzledPass[i]; } } 

最埌のステップは、増分テヌブルを䜿甚するこずです。 ぀たり 各セルは、8を法ずするテヌブルの察応する芁玠ず合蚈されたす。これにより、同じ倀であっおもアむコンが異なるこずになりたす。

  void applyIncrementTable(uint8* pass) { for (var i = 0; i < 12; i++) { pass[i] = (pass[i] + s_incrementTable[i]) % 8; } } 

準備ができたパスワヌドがありたす。 そしお、このパスワヌドには36ビットのうち15ビットが䜿甚されおいるこずがわかりたした。

実際、これらのビットはそれほど䜿甚されおいたせん。 パスワヌドのデコヌド手順では、収集されたLボヌナスずEボヌナスに関する情報ず珟圚の番号を取埗したすが、これらすべおの倀がれロに等しいこずを確認したす。 このこずから、もずもずこの情報を保存するこずが蚈画されおいたず仮定できたすが、それを砎棄するこずにしたした。

これがゲヌムプレむのバランスの結果なのか、単に無効化された開発者ツヌルなのかは䞍明です。 詳现に぀いおはこちらをご芧ください 。

可倉長


自宅のどこかに、子䟛の頃の䞻なRPGであるリトルニンゞャブラザヌズのパスワヌドが曞かれたノヌトブックがありたす。 このゲヌムは超自然的なものではありたせんが、完成するたでに数幎かかりたした。 結局、それは私の最初のRPGであり、最初はそこで「スむング」するこずが可胜であるこずすら知らなかったので、ポンプを䜿わずに2番目のボスを倒すのに玄6か月かかりたした䞀緒にプレむする機䌚があるため。

か぀お、このゲヌムでパスワヌドシステムの構造を考えるようになりたした-䞀日䞭テレビの前に座っお、パスワヌドのパタヌンを探し、珟圚の特性ぞの䟝存を刀断しようずしおいたした。 その結果、1぀のパスワヌドを取埗しお金額を増やすこずさえできたしたが、これが唯䞀の良いケヌスでした。

しばらくしお、私のITスペシャリストになる過皋で、私はか぀おその事件に぀いお思い出したした。 そしお、圌は非垞に奜奇心が匷く、頭を悩たせるのが奜きだったので、圌は倧孊の䌑暇䞭にパスワヌドを生成するためのメカニズムを必ず芋぀けるこずを決めたした。 今では1日で十分でしたが、1週間かかりたしたが、それでも目暙は達成されたした。

このケヌスは、パスワヌドの長さが可倉であるずいう理由だけで、以前のケヌスよりも興味深いものです。ゲヌムを進めるに぀れお、新しいキャラクタヌが远加されたす。 さらに、パスワヌドには、以前のすべおを組み合わせたものよりもはるかに倚くのデヌタが保存されたす。 スクリヌンショットからわかるように、アルファベットは32文字で、パスワヌドの最倧長は54文字です。 これにより、32 54個の最倧長のパスワヌドが埗られたす。可倉長を考慮するず、32 1 + 32 2 + ... + 32 54オプションがありたす。 最倧長の1぀のパスワヌドに察応できる情報量を蚈算するず、270ビットlog 2 32 54 になりたす。

それでは、パスワヌドにはどのようなデヌタが保存されたすか これはRPGであるため、倚くの特性があり、それらのほずんどすべおを保存する必芁がありたす。

機胜リスト




特城
  • 文字レベル最倧50
  • 経隓量最倧65535
  • 最倧ヘルス最倧255
  • 金額最倧99999
  • Mボヌナスの数最倧6

衣装
  • 受け取ったプリズムベル赀、オレンゞ、黄色、緑、青、青、玫
  • 利甚可胜なアヌティファクト解毒剀、粟神
  • ストラむクのタむプ「鉄の爪」、「クラッシュブロヌ」、「メガストラむク」、「ファむアストラむク」、「ブラントストラむク」、「ゎヌルデンクロヌ」、「リヌストラむク」、「プリズムクロヌ」
  • 既存の剣「鷹の剣」、「虎の剣」、「eagleの剣」、「プリズムの剣」
  • シヌルド「うろこ状」、「鏡」、「炎」、「プリズム」
  • ロバ「癜」、「黒」、「ロヌブリヌ」、「神聖なロヌブ」
  • タリスマン「α」、「β」、「γ」、「σ」、「ω」
  • お守り「I」、「II」、「III」、「IV」
  • ランプの皮類マッチ、キャンドル、トヌチ、倪陜の欠片
  • 手裏剣の皮類「シングル」、「シリアル」、「ブヌメラン」、「フィクサヌ」 翻蚳できたせん 

アむテムなど
  • パンの数ラむトヒヌリングポヌションのアナログ、最倧8個
  • ミヌトロヌルの数匷力な癒しのポヌションのアナログ、最倧1個
  • ヘリコプタヌの数垂内ぞのポヌタルポヌタル、最倧8個
  • 薬の量2番目のプレむダヌを埩掻させるこずができたす、最倧1個
  • スケヌトボヌドの数最倧8個たで、戊堎から脱出できたす。
  • 爆匟の数最倧8個
  • ドラッグスタヌはありたすかレヌシングカヌです
  • ドラッグスタヌ甚バッテリヌの数
  • 䞡方のプレむダヌが利甚できる特別なヒット数

実際、このデヌタのすべおが保存されるわけではなく、保存されおいるすべおのデヌタがリストされるわけでもありたせん。 蚪れた郜垂、珟圚の堎所、発生したゲヌムむベントに関する情報を含むいく぀かのビットは蚀及されおいたせん。 これらの2぀の倀は経隓の量に盎接䟝存し、冗長デヌタであるため、レベルず正垞性は保持されたせん。 たた、2番目のプレむダヌの特別なヒットの数は保存されたせん。 基本的にオプションです。

それでは、どのように機胜したすか たず、ゲヌムは保存する必芁のある倉数のバむトぞのポむンタヌの配列を保存したす。 これらのポむンタヌに埓っお、ゲヌムはバむトの配列を圢成し、その埌、゚ンコヌドされたす。 この配列は、8バむトの4぀のグルヌプに分割されたす最埌のグルヌプでは6バむト。

  const char s_groupsBytes[4] = {8, 8, 8, 6}; const char* s_passDataMap[30] = { // Group 1 &currLocation, &bells, &moneyLo, &expLo &moneyMed, &expHi, &moneyHi, &kicks, // Group 2 &visitedCitiesLo, &visitedCitiesHi, &mBonusCount, &tStarsTypes, &punch, &usedTreasures, &tStars, &treasures, // Group 3 &sword, &bombs, &shield, &skboards, &robe, &dragster, &talisman, &meatbuns, // Group 4 &amulet, &sweetbuns, &light, &batteries, &whirlyBirds, &medicine }; 

パスワヌドをできるだけコンパクトにするために、すべおのヌル倀は無芖されたす。 この目的のために、配列の4぀のグルヌプごずに非れロ倀のマスクがコンパむルされたす。1バむトでは、i番目のビットが配列のi番目の芁玠をパスワヌドに含めるかどうかを瀺したす。 圢成された配列では、このマスクは察応するグルヌプの前に移動したす。

  uint8 valuesMask(const uint8* data, int group) { uint8 valuesMask = 0; const int startIndex = group * 8; for (int i = startIndex + s_groupsBytes[group] - 1; i >= startIndex; i--) { valuesMask <<= 1; if (data[i] != 0) { valuesMask |= 1; } } return valuesMask; } 

すべおのグルヌプ倀がれロに等しい堎合、最埌に同じ操䜜がグルヌプに適甚されたす。4ビットのマスクバむトの䞊䜍4ビットがあり、i番目のビット䞊䜍から䞋䜍はパスワヌドが含たれるこずを瀺したす。配列のi番目のグルヌプ。 このマスクは、これらのグルヌプの盎前のヘッダヌに曞き蟌たれたす。

さらに、配列の各バむトがその有効ビット数に察応する長さテヌブルがありたす。 ぀たり その結果、配列のバむト党䜓が゚ンコヌドされず、倀のビットのみが䜿甚されたす。 同じ手法がれロ以倖の倀のマスクにも適甚されたす。䜿甚されるマスクビットの数は、グルヌプ内のバむト数に察応したす。

最終的に生成される配列は、ビットスタックに類䌌しおいたす。倉数は「前眮眮換」によっお远加されたす。



  const char s_bitLengths[] = { 8, 7, 8, 8, 8, 8, 1, 7, 8, 2, 3, 4, 4, 2, 4, 2, 3, 4, 3, 4, 3, 1, 3, 1, 3, 4, 3, 4, 4, 1 }; void pushBits(uint8* data, uint8 value, int bitCount) { shiftRight(data, bitCount); writeBits(data, value, bitCount); } // ... uint8 encodedData[30] = {}; uint8 groupsMask = 0; for (int i = 3; i >= 0; i--) { groupsMask >>= 1; uint8 currMask = valuesMask(passData, i); if (currMask != 0) { groupsMask |= 0x80; const uint8 valuesCount = s_groupsBytes[i]; const int startIndex = i * 8; for (int j = startIndex + valuesCount - 1; j >= startIndex; j--) { if (passData[j] != 0) { pushBits(encodedData, passData[j], s_bitLengths[j]); } } pushBits(encodedData, currMask, valuesCount); } } 

次に、ヘッダヌの䞀皮である4バむトが配列の先頭に远加されたす。チェックサム、パスワヌドに含たれるグルヌプのマスク、および増分-32を法ずする文字の倀に远加される0〜31の8ビットランダム倀が栌玍されたす。

最初に、増分がヘッダヌの最埌に曞き蟌たれ、ヘッダヌの最埌のバむトから始たりたす。チェックサムが考慮されたす。これは、芁玠の合蚈にシリアル番号を掛けた20ビットのハッシュです。

  uint32 calcChecksum(uint8* data, int count) { uint32 sum = 0; for(int i = 0; i < count; i++) { sum += data[i] * (i + 1); } return sum; } // ... const uint8 increment = rand() % 32; shiftRight(encodedData, 32); encodedData[3] = increment; uint32 checksum = calcChecksum(&encodedData[3], (encodedDataBitLength + 7) / 8); encodedData[0] = checksum & 0xFF; encodedData[1] = (checksum >> 8) & 0xFF; encodedData[2] = ((checksum >> 16) & 0xF) | groupsMask; 

その埌、前の堎合ず同様に、デヌタはBase32ず同様に゚ンコヌドされたす。この堎合、最初に、チェックサムのヘッダヌずパスワヌドに含たれるグルヌプのマスクの䞊䜍4ビットが別々に゚ンコヌドされ、次に増分が別のパスワヌドシンボルで曞き蟌たれ、その埌にのみ他のすべおのデヌタが゚ンコヌドされたす。

  uint8 password[54] = {}; uint8* header = encodedData; uint8* body = &encodedData[4]; // Encode header (3 bytes + increment = 6 chars) for (int i = 0; i < 5; i++) { password[i] = takeBits(header, 3, 5); } password[5] = increment; const int charCount = (((byteCount + 1) * 8 + 4) / 5) - 1; // Encode password data for (var i = 0; i < charCount; i++) { password[i + 6] = takeBits(body, byteCount, 5); } 

増分倀は結果の倀に適甚されたす。ただし、シンボル自䜓は䟋倖で、倀自䜓が保存されたす。

  // Apply increment skipping increment char for (var i = 0; i < password.length; i++) { if (i != 5) { password[i] = (password[i] + increment) % 32; } } 

そしお実際、最終段階アルファベット順のテキストぞの倉換。

  const wchar_t* s_passwordCharTable = L"—BCD\u25a0FGH+JKLMN\u25cfPQRST\u25b2VWXYZ234567"; for (int i = 0; i < charCount; i++) { std::cout << s_passChars[password[i]]; if (i % 6 == 5) { std::cout << ' '; } } 


ゲヌムでのアルゎリズムの実装の機胜に぀いお少し
NES , , , . , «» .

: , ( «Base32») 4- . .

, , 9 ( ) — 8 + . , , .. 18-, , . , , .

0x12 (, 4 ), , . なぜなら , , .

  uint8 data[32] = {}; for (int index = 0; index < 34; index++) { register = index; if (register == 0 && !(mask & 0x80)) { register = 9; index = register; } if (register == 9 && !(mask & 0x40)) { register = 18; //  ! mask = register; } if (register == 18 && !(mask & 0x20)) { //        register = 27; index = register; } if (register == 27 && !(mask & 0x10)) { return; } decodeValue(input, &data[index]); } 

, . ? , , , , , 3- , ( ), , , .

- , , ! : , . — , .


ボヌナスずしお


JavaScriptの愛奜家は、
アドベンチャヌアむランド4高橋名人の冒険島 IVずPower Blade 2の蚘事のために特別に曞かれたパスワヌドゞェネレヌタヌ、およびほが5幎前のリトルニンゞャブラザヌズ甚のゞェネレヌタヌの高貎なバヌゞョンを詳しく調べるこずができたす。

厳密にコヌドを刀断しないようお願いしたす;りェブプログラミングは私の専門からはほど遠いです。

プリンスオブペルシャのパスワヌドゞェネレヌタヌはこちらにありたす。

そしお少し懐かしい...













参照資料


Source: https://habr.com/ru/post/J150958/


All Articles