トップページ > 過去ログ > 記事閲覧
セーブデータの隠蔽、暗号化の基礎
名前:dal 日時: 2009/01/06 02:00

ゲームで使用する不変のデータはDXアーカイブによって隠せるのですが セーブデータのファイルはDXアーカイブが使えず、 メモ帳にドラッグ&ドロップするだけで容易に中身が見れて改変できる状態です。 この問題を解決するために、暗号化その他の処理を したいのですが、C言語の基本をきちんとできないまま、覚えたことのみ使用して 突っ走ってきてしまったのでつまずいています。アドバイスをお願いします。 セーブしたいデータの内容は、例えば struct persons{ char name[24]; int HP; int MP; short int LV; int EXP; short int …(その他パラメータが大量) }person[NumOfMember]; といったデータで、現在はタブ切りテキストで char savebuff[5000]; //多めに適当に   FILE* fp = fopen("savedata.txt","w");   sprintf(savebuff,"%s\t%d\t%d\t%d\t%d…",persons[0].name,persons[0].HP,…); fprintf(fp,"%s\n",savebuff); //1行あたり1人分のデータ格納して改行 といった記録の仕方をしています。タブ切りテキストの形式にしたのは タブ切りテキストしか読み込みの方法を知らなかったからです。 やりたいことは、複雑な暗号化というよりは セーブデータのファイルをメモ帳に突っ込んでも文字化けして改変不可 という程度のレベルを目指しています。 現状は、 1.fopenの2つ目の引数を"w"から"wb"にするとテキストではなくバイナリになって  メモ帳で文字化けするようになると思ったがそうはならず、甘くはなかった。 2.やはりXOR演算などで暗号化しないと駄目だと思い、それにはfprintfでは実現できず  fwriteという関数が必要なのではないかと考えた。 3.そうすると、fprintfを使った今までのセーブ方式は全とっかえが必要ではないかと思ったが charの配列のsavebuffの中身をsprintfで入れて、savebuffを全要素について  XOR処理してから格納すればいいと考えた。 4.sprintfでデータを入れたsavebuffを、各要素についてXORをかけて、  fwrite(fp,savebuff,sizeof(char),strlen(savebuff));によりバイナリでファイルに  書き込んでみたが、なぜか\t切りtxtファイルに記録してた時よりもはるかに容量が大きくなってしまった。  また、'\n'までXORされてしまったら、そもそもセーブデータをロードする時に FileRead_getsによる1行ずつの読み込みができないのではないかと考え、  色々とドツボにはまりそうだったので試行を思いとどまった(今ここ)です。 間違った方向性を試していくのが怖くなったので質問してしまいました。 char型、int型、short int型などが入り混じった構造体の、各要素を1行ずつにして メモ帳で改変できないセーブデータファイルを出力するには、 どのようにするのがいいのでしょうか。何かエレガントな基本的な方法があるのでしょうか。 現状のどこらへんは正しくてどこらへんがおかしなことをしているのでしょうか。 よろしくお願いします。

Page: 1 |

Re: セーブデータの隠蔽、暗号化の基礎 ( No.1 )
名前:GPGA 日時:2009/01/06 05:38

外部から見えなくするということは、無理にテキスト形式で保存する必要は 無いという認識でよいですか? であるならば、以下の方法はいかがですか? struct persons{ char name[24]; int HP; int MP; short int LV; int EXP; short int …(その他パラメータが大量) }person[NumOfMember]; // 暗号キー const unsigned char encKey = 0x89; // セーブ void Save(const char* szFileName) { FILE* fp; fp = fopen(szFileName, "wb"); // 暗号化をやる場合 #ifdef ENCRYPTION unsigned char* p = (char*)person; for (int i = 0; i < sizeof(person); ++i) { p[i] ^= encKey } #endif fwrite(person, sizeof(person), NumOfMember, fp); fclose(fp); } // ロード void Save(const char* szFileName) { FILE* fp; fp = fopen(szFileName, "rb"); if (fp == NULL) { printf("ファイルが存在しません。"); return ; } fread(person, sizeof(person), NumOfMember, fp); // 複合化する必要がある場合 #ifdef ENCRYPTION unsigned char* p = (char*)person; for (int i = 0; i < sizeof(person); ++i) { p[i] ^= encKey } #endif fclose(fp); } 上記のように、構造体配列を丸々保存してしまうのが一番簡単だと思います。
Re: セーブデータの隠蔽、暗号化の基礎 ( No.2 )
名前:dal 日時:2009/01/07 01:26

基礎を疎かにすることの愚かさを思い知りました。 思いて学ばざるすなわち危うしというやつを痛感しました。 今回の問題がすっきり解決したのみならず、 データというものがどういうものなのかということと、 テキストファイル、バイナリファイルとは どういうことなのか本質的な所を理解できていなかったのが、 非常に理解が深まりました。 本当にありがとうございました。
Re: セーブデータの隠蔽、暗号化の基礎 ( No.3 )
名前: 日時:2009/01/07 10:21

補足ですが、構造体をバイナリで保存する場合は、 以下の点にも注意が必要です。 ・その構造体にポインタが含まれていないこと ・その構造体のバイトアライメントを1にしておく事 ポインタ型はその性質上、実行時以外に意味を 持たないので、これをそのまま書き出しても 無意味です。 バイトアライメントはたとえば今後新しいOSなどが 出た場合に、アプリ側を新しくコンパイルすると 構造体自体のサイズが変わってしまう場合があります この場合は過去に記録された古いセーブデータを 読み込ませると誤動作を起こすきっかけになります >テキストファイル、バイナリファイルとは これがヒトに説明できるようになれば、 理解できたということだと思います。
Re: セーブデータの隠蔽、暗号化の基礎 ( No.4 )
名前:GPGA 日時:2009/01/07 12:56

2箇所ミスがありました。 ×fwrite(person, sizeof(person), NumOfMember, fp); ○fwrite(person, sizeof(persons), NumOfMember, fp); ×fread(person, sizeof(person), NumOfMember, fp); ○fread(person, sizeof(persons), NumOfMember, fp);

Page: 1 |