Permanent
Creation

RPGツクールXP/VX用のRGSS素材です。
一部英語版対応です。 (A few materials can be used for English RPG MAKER XP/VX.)
競合率はルファーの独断と偏見なのでたまにハズレます。


#==============================================================================
# ■ セーブデータのチェック
#------------------------------------------------------------------------------
#  セーブ失敗やデータ改ざんの対策です。
# (C) ルファー
# WebSite  https://tkoolerlufar.pages.dev/
#==============================================================================

=begin
  =対応RGSS
  1 2

  =必要なライブラリ
  ルファー作スクリプト素材共通ライブラリ

  =他の素材との競合の可能性
  !.. セーブファイルの名前や保存先を変更していない限りは大丈夫

  =RPGツクールXPでの導入方法
  Scene_Save より下に新セクションを作成して、
  このプログラムをそこにコピーする

  =セーブファイルの扱いについて
  セーブに失敗したりデータを改ざんされたりすると、
  セーブデータの中身がおかしくなってしまいます。
  それを検知した場合、そのファイルを削除して
  バックアップファイルから元のファイルを復元します。
  大体256壊のうち255壊くらい検知できます。
  バックアップファイルの名前は「Save0(ファイル番号).rxdata」となります。
  名前がこのようになっているのは、「ゲームデータの圧縮」を行う際に
  デバッグ時のセーブデータのバックアップが
  圧縮データに含まれないようにするためです。

  =セーブファイルの最大数を増やしてる方へ
  61行目にある「SAVEFILES_MAX」という定数の値を変更すれば、
  その数のセーブファイルに対応できます。
  大きすぎてもエラーにはなりませんが、
  処理が遅くなるので最大数ちょうどになるように指定してください。

  =更新履歴
  2010/03/01 公開

  =再定義しているメソッド (RPGツクールXPのみ)
  Scene_File#main (エイリアス使用)
  Scene_Save#on_decision (エイリアス使用)
  Scene_Save#write_save_data (エイリアス使用)

  =再定義しているメソッド (RPGツクールVXのみ)
  Scene_File#start (エイリアス使用)
  Scene_File#do_save (エイリアス使用)
  Scene_File#write_save_data (エイリアス使用)

  =新規定義しているメソッド
  なし
=end
#----------------------------------------------------------------------------
# ◇ Lufar モジュール
#----------------------------------------------------------------------------
module Lufar
  #--------------------------------------------------------------------------
  # ○ 定数
  #--------------------------------------------------------------------------
  SAVEFILES_MAX = 4                     ## 最大ファイル数
  #--------------------------------------------------------------------------
  # ◇ File クラス
  #--------------------------------------------------------------------------
  class File < ::File
    #------------------------------------------------------------------------
    # ○ ファイルのコピー
    #     source      : コピー元のファイル名
    #     destination : コピー先のファイル名
    #------------------------------------------------------------------------
    def self.copy(source, destination)
      unless source.instance_of?(String) and destination.instance_of?(String)
        raise(TypeError, sprintf(
          "Lufar::File.copy の引数の型が異常です:\nsource=%s\ndestination=%s",
          source.inspect, destination.inspect
          ), caller)
      end
      begin
        file1 = self.open(source, "rb")
        begin
          file2 = self.open(destination, "wb")
        rescue
          file1.close
          raise
        end
        file2.write(file1.read())
        file1.close
        file2.close
        nil
      rescue SystemCallError
        $@ = caller
        raise
      end
    end
  end
  #--------------------------------------------------------------------------
  # ◇ SafeFile モジュール
  #--------------------------------------------------------------------------
  module SafeFile
    module_function
    ##-----------------------------------------------------------------------
    ## ○ ファイル名の作成
    ##     file_index : セーブファイルのインデックス (0~)
    ##-----------------------------------------------------------------------
    def make_filename(file_index)
      return sprintf("Save%d.r%cdata", file_index + 1, rpgxp? ? ?x : ?v)
    end
    ##-----------------------------------------------------------------------
    ## ○ バックアップファイル名の作成
    ##     file_index : セーブファイルのインデックス (0~)
    ##-----------------------------------------------------------------------
    def make_backup_filename(file_index)
      return sprintf("Save0%d.r%cdata", file_index + 1, rpgxp? ? ?x : ?v)
    end
    #------------------------------------------------------------------------
    # ○ パリティチェック
    #------------------------------------------------------------------------
    def check_parity(file)
      unless file.kind_of?(::File)
        raise(TypeError, sprintf(
          "パリティチェックはファイルストリームのみ可能です(%s)",
          file.class.to_s), caller)
      end
      begin
        parity_byte = 0xFF
        file.rewind
        file.each_byte do |byte|
          parity_byte ^= byte
        end
        return parity_byte == 0
      rescue SystemCallError
        $@ = caller
        raise
      end
    end
    #------------------------------------------------------------------------
    # ○ バックアップの作成
    #------------------------------------------------------------------------
    def make_backup(file_index)
      begin
        File.copy(make_filename(file_index), make_backup_filename(file_index))
      rescue SystemCallError
        $@ = caller
        raise
      end
    end
    #------------------------------------------------------------------------
    # ○ バックアップの復元
    #------------------------------------------------------------------------
    def restore_backup(file_index)
      begin
        File.copy(make_backup_filename(file_index), make_filename(file_index))
      rescue SystemCallError
        $@ = caller
        raise
      end
    end
    #------------------------------------------------------------------------
    # ○ セーブファイルの破損・改ざんチェック
    #------------------------------------------------------------------------
    def check_savefiles
      ## パリティチェック
      ## セーブファイル破損・改造の可能性があれば削除
      ## このときバックアップがあれば復元
      for i in 0...SAVEFILES_MAX
        filename = make_filename(i)
        ## 空きファイルなら次へ
        next if !FileTest.file?(filename)
        file = File.open(filename, "rb")
        flg = check_parity(file)
        file.close
        ## 正常なら次へ
        next if flg
        File.delete(filename)
        filename = make_backup_filename(i)
        ## バックアップファイルが無ければ次へ
        next if !FileTest.file?(filename)
        ## バックアップも破損・改造の可能性があれば削除
        file = File.open(filename, "rb")
        flg = check_parity(file)
        file.close
        if flg
          restore_backup(i)
        else
          File.delete(filename)
        end
      end
    end
    #------------------------------------------------------------------------
    # ○ パリティバイトの書き込み
    #------------------------------------------------------------------------
    def write_parity_byte(file)
      unless file.kind_of?(::File)
        raise(TypeError, sprintf(
          "パリティバイトの書き込みはファイルストリームのみ可能です(%s)",
          file.class.to_s), caller)
        end
        begin
        file.reopen(file.path, "r+b")
        parity_byte = 0xFF
        file.each_byte do |byte|
          parity_byte ^= byte
        end
        file.putc(parity_byte)
      rescue SystemCallError
        $@ = caller
        raise
      end
    end
  end
end
#----------------------------------------------------------------------------
# ◆ Scene_File クラス
#----------------------------------------------------------------------------
class Scene_File
  if rpgxp?
    #------------------------------------------------------------------------
    # ● メイン処理
    #------------------------------------------------------------------------
    alias _Lufar_Backup_old_main main
    def main
      Lufar::SafeFile.check_savefiles
      _Lufar_Backup_old_main
    end
  else
    #------------------------------------------------------------------------
    # ● 開始処理
    #------------------------------------------------------------------------
    alias _Lufar_Backup_old_start start
    def start
      Lufar::SafeFile.check_savefiles
      _Lufar_Backup_old_start
    end
  end
end
if rpgxp?
  #--------------------------------------------------------------------------
  # ◆ Scene_Save クラス
  #--------------------------------------------------------------------------
  class Scene_Save < Scene_File
    #------------------------------------------------------------------------
    # ● 決定時の処理
    #------------------------------------------------------------------------
    alias _Lufar_Backup_old_od on_decision
    def on_decision(filename)
      ## 決定時の処理
      _Lufar_Backup_old_od(filename)
      ## セーブデータのバックアップ
      Lufar::SafeFile.make_backup(@file_index)
    end
    #------------------------------------------------------------------------
    # ● セーブデータの書き込み
    #     file : 書き込み用ファイルオブジェクト (オープン済み)
    #------------------------------------------------------------------------
    alias _Lufar_Backup_old_wsd write_save_data
    def write_save_data(file)
      ## セーブデータの書き込み
      _Lufar_Backup_old_wsd(file)
      ## パリティバイトの書き込み
      Lufar::SafeFile.write_parity_byte(file)
    end
  end
else
  #--------------------------------------------------------------------------
  # ◆ Scene_File クラス
  #--------------------------------------------------------------------------
  class Scene_File
    #------------------------------------------------------------------------
    # ● セーブの実行
    #------------------------------------------------------------------------
    alias _Lufar_Backup_old_ds do_save
    def do_save
      ## 決定時の処理
      _Lufar_Backup_old_ds
      ## セーブデータのバックアップ
      Lufar::SafeFile.make_backup(@index)
    end
    #------------------------------------------------------------------------
    # ● セーブデータの書き込み
    #     file : 書き込み用ファイルオブジェクト (オープン済み)
    #------------------------------------------------------------------------
    alias _Lufar_Backup_old_wsd write_save_data
    def write_save_data(file)
      ## セーブデータの書き込み
      _Lufar_Backup_old_wsd(file)
      ## パリティバイトの書き込み
      Lufar::SafeFile.write_parity_byte(file)
    end
  end
end

戻る