using System; using System.Collections.Generic; using System.IO; using System.Text; namespace NTERA.EmuEra.Game.EraEmu.Sub { #region reader/writer共通データ public enum EraSaveFileType : byte { Normal = 0x00, Global = 0x01, Var = 0x02, CharVar = 0x03 } public enum EraSaveDataType : byte { Int = 0x00, IntArray = 0x01, IntArray2D = 0x02, IntArray3D = 0x03, Str = 0x10, StrArray = 0x11, StrArray2D = 0x12, StrArray3D = 0x13, //SOC = 0xFD,//キャラデータ始まり Separator = 0xFD,//データ区切り EOC = 0xFE,//キャラデータ終わり EOF = 0xFF//ファイル終端 } static class Ebdb//EraBinaryData中のマジックナンバーなバイト { public const byte Byte = 0xCF; public const byte Int16 = 0xD0;//直後の2バイトがInt16 public const byte Int32 = 0xD1;//直後の4バイトがInt32 public const byte Int64 = 0xD2;//直後の8バイトがInt64 public const byte String = 0xD8;//直後がString public const byte EoA1 = 0xE0;//データ区切り(一次元 public const byte EoA2 = 0xE1;//データ区切り(二次元 public const byte Zero = 0xF0;//直後にゼロが連続する数 public const byte ZeroA1 = 0xF1;//直後に空配列が連続する数(一次元 public const byte ZeroA2 = 0xF2;//直後に空配列が連続する数(二次元 public const byte EoD = 0xFF;//変数データ終わり } static class EraBDConst { //Headerはpngのパクリ public const UInt64 Header = 0x0A1A0A0D41524589UL; public const UInt32 Version1808 = 1808; public const UInt32 DataCount = 0; } #endregion /// /// 1808追加 新しいデータ保存形式 /// 将来形式を変更したときのためにabstractにしておく /// internal abstract class EraBinaryDataReader : IDisposable { private EraBinaryDataReader() {} protected EraBinaryDataReader(BinaryReader stream, int ver, UInt32[] buf) { reader = stream; version = ver; data = buf; } protected BinaryReader reader; protected readonly int version; protected readonly UInt32[] data; public abstract int ReaderVersion { get; } /// /// FileStreamからReaderを作成 /// 不正なファイルの場合はnullを返す・例外は投げない /// /// /// public static EraBinaryDataReader CreateReader(FileStream fs) { try { if ((fs == null) || (fs.Length < 16)) return null; BinaryReader reader = new BinaryReader(fs, Encoding.Unicode); if (reader.ReadUInt64() != EraBDConst.Header) return null; int version = (int)reader.ReadUInt32(); int datacount = (int)reader.ReadUInt32(); UInt32[] data = new UInt32[datacount]; for (int i = 0; i < datacount; i++) data[i] = reader.ReadUInt32(); if (version == EraBDConst.Version1808) return new EraBinaryDataReader1808(reader, version, data); return null; } catch { return null; } } public abstract EraSaveFileType ReadFileType(); /// /// システム用の特殊処理・圧縮なし /// /// public abstract Int64 ReadInt64(); public abstract string ReadString(); public abstract Int64 ReadInt(); public abstract void ReadIntArray(Int64[] refArray, bool needInit); public abstract void ReadIntArray2D(Int64[,] refArray, bool needInit); public abstract void ReadIntArray3D(Int64[, ,] refArray, bool needInit); public abstract void ReadStrArray(string[] refArray, bool needInit); public abstract void ReadStrArray2D(string[,] refArray, bool needInit); public abstract void ReadStrArray3D(string[, ,] refArray, bool needInit); public abstract KeyValuePair ReadVariableCode(); #region IDisposable メンバ public void Dispose() { if (reader != null) reader.Close(); reader = null; } #endregion public void Close() { Dispose(); } private sealed class EraBinaryDataReader1808 : EraBinaryDataReader { public EraBinaryDataReader1808(BinaryReader stream, int ver, UInt32[] buf) : base(stream, ver, buf) { } //public bool EOF //{ // get // { // return (reader.BaseStream.Length == reader.BaseStream.Position); // } //} public override int ReaderVersion => 1808; public override EraSaveFileType ReadFileType() { byte type = reader.ReadByte(); if (type >= 0 && type <= 3) return (EraSaveFileType)type; throw new FileEE("ファイルデータ型異常"); } private Int64 m_ReadInt() { byte b = reader.ReadByte(); if (b <= Ebdb.Byte) return b; if (b == Ebdb.Int16) return reader.ReadInt16(); if (b == Ebdb.Int32) return reader.ReadInt32(); if (b == Ebdb.Int64) return reader.ReadInt64(); throw new FileEE("バイナリデータの異常"); } public override Int64 ReadInt64() { return reader.ReadInt64(); } public override KeyValuePair ReadVariableCode() { EraSaveDataType type = (EraSaveDataType)reader.ReadByte(); if (type == EraSaveDataType.EOC || type == EraSaveDataType.EOF || type == EraSaveDataType.Separator) return new KeyValuePair(null, type); string key = reader.ReadString(); return new KeyValuePair(key, type); } //配列じゃないやつは特殊処理 public override Int64 ReadInt() { return m_ReadInt(); } public override string ReadString() { return reader.ReadString(); } public override void ReadIntArray(Int64[] refArray, bool needInit) { Int64[] oriArray = null; byte b; int x = 0; int saveLength0 = reader.ReadInt32(); if (refArray == null)//読み捨て。レアケースのはず refArray = new Int64[saveLength0]; int length0 = refArray.Length; //保存されたデータの方が大きいとき。レアケースのはず if (length0 < saveLength0) { oriArray = refArray; //1818修正 サイズ違いの時にあふれないように/配列は最大まで確保、作業するのは重複部分だけ refArray = new Int64[Math.Max(length0, saveLength0)]; length0 = Math.Min(length0, saveLength0); } while (true) { b = reader.ReadByte(); if (b == Ebdb.EoD) break; if (b == Ebdb.Zero) { int cnt = (int)m_ReadInt(); if (needInit) for (int i = 0; i < cnt; i++) refArray[x + i] = 0; x += cnt; continue; } if (b <= Ebdb.Byte) refArray[x] = b; else if (b == Ebdb.Int16) refArray[x] = reader.ReadInt16(); else if (b == Ebdb.Int32) refArray[x] = reader.ReadInt32(); else if (b == Ebdb.Int64) refArray[x] = reader.ReadInt64(); else throw new FileEE("バイナリデータの異常"); x++; } if (needInit) for (; x < length0; x++) refArray[x] = 0; if (oriArray != null) { for (x = 0; x < length0; x++) oriArray[x] = refArray[x]; } } public override void ReadIntArray2D(Int64[,] refArray, bool needInit) { Int64[,] oriArray = null; byte b; int x = 0; int y = 0; int saveLength0 = reader.ReadInt32(); int saveLength1 = reader.ReadInt32(); if (refArray == null) refArray = new Int64[saveLength0, saveLength1]; int length0 = refArray.GetLength(0); int length1 = refArray.GetLength(1); if (length0 < saveLength0 || length1 < saveLength1) { oriArray = refArray; //1818修正 サイズ違いの時にあふれないように/配列は最大まで確保、作業するのは重複部分だけ refArray = new Int64[Math.Max(length0, saveLength0), Math.Max(length1, saveLength1)]; length0 = Math.Min(length0, saveLength0); length1 = Math.Min(length1, saveLength1); } while (true) { b = reader.ReadByte(); if (b == Ebdb.EoD) break; if (b == Ebdb.ZeroA1) { int cnt = (int)m_ReadInt(); if (needInit) for (int i = 0; i < cnt; i++) for (y = 0; y < length1; y++) refArray[x + i, y] = 0; x += cnt; y = 0; continue; } if (b == Ebdb.EoA1) { if (needInit) for (; y < length1; y++) refArray[x, y] = 0; x++; y = 0; continue; } if (b == Ebdb.Zero) { int cnt = (int)m_ReadInt(); if (needInit) for (int i = 0; i < cnt; i++) refArray[x, y + i] = 0; y += cnt; continue; } if (b <= Ebdb.Byte) refArray[x, y] = b; else if (b == Ebdb.Int16) refArray[x, y] = reader.ReadInt16(); else if (b == Ebdb.Int32) refArray[x, y] = reader.ReadInt32(); else if (b == Ebdb.Int64) refArray[x, y] = reader.ReadInt64(); else throw new FileEE("バイナリデータの異常"); y++; } if (needInit) { for (; x < length0; x++) { for (; y < length1; y++) refArray[x, y] = 0; y = 0; } } if (oriArray != null) { for (x = 0; x < length0; x++) for (y = 0; y < length1; y++) oriArray[x, y] = refArray[x, y]; } } /// /// /// /// データを書き出す先。読み捨てるならnull /// データがない部分を0で埋める必要があるか public override void ReadIntArray3D(Int64[, ,] refArray, bool needInit) { Int64[, ,] oriArray = null; byte b; int x = 0; int y = 0; int z = 0; int saveLength0 = reader.ReadInt32(); int saveLength1 = reader.ReadInt32(); int saveLength2 = reader.ReadInt32(); if (refArray == null) refArray = new Int64[saveLength0, saveLength1, saveLength2]; int length0 = refArray.GetLength(0); int length1 = refArray.GetLength(1); int length2 = refArray.GetLength(2); if (length0 < saveLength0 || length1 < saveLength1 || length2 < saveLength2) { oriArray = refArray; //1818修正 サイズ違いの時にあふれないように/配列は最大まで確保、作業するのは重複部分だけ refArray = new Int64[Math.Max(length0, saveLength0), Math.Max(length1, saveLength1), Math.Max(length2, saveLength2)]; length0 = Math.Min(length0, saveLength0); length1 = Math.Min(length1, saveLength1); length2 = Math.Min(length2, saveLength2); } while (true) { b = reader.ReadByte(); if (b == Ebdb.EoD) break; if (b == Ebdb.ZeroA2)//cnt分だけ空の行列が連続 { int cnt = (int)m_ReadInt(); if (needInit) for (int i = 0; i < cnt; i++) for (y = 0; y < length1; y++) for (z = 0; z < length2; z++) refArray[x + i, y, z] = 0; x += cnt; y = 0; z = 0; continue; } if (b == Ebdb.EoA2)//行列終わりor残りが全て0 { if (needInit) { for (; y < length1; y++) { for (; z < length2; z++) refArray[x, y, z] = 0; z = 0; } } x++; y = 0; z = 0; continue; } if (b == Ebdb.ZeroA1)//cnt分だけ空の列が連続 { int cnt = (int)m_ReadInt(); if (needInit) for (int i = 0; i < cnt; i++) for (z = 0; z < length2; z++) refArray[x, y + i, z] = 0; y += cnt; z = 0; continue; } if (b == Ebdb.EoA1)//列終わりor残り全て0 { if (needInit) for (; z < length2; z++) refArray[x, y, z] = 0; y++; z = 0; continue; } if (b == Ebdb.Zero)//cnt分だけ0が連続 { int cnt = (int)m_ReadInt(); if (needInit) for (int i = 0; i < cnt; i++) refArray[x, y, z + i] = 0; z += cnt; continue; } if (b <= Ebdb.Byte) refArray[x, y, z] = b; else if (b == Ebdb.Int16) refArray[x, y, z] = reader.ReadInt16(); else if (b == Ebdb.Int32) refArray[x, y, z] = reader.ReadInt32(); else if (b == Ebdb.Int64) refArray[x, y, z] = reader.ReadInt64(); else throw new FileEE("バイナリデータの異常"); z++; } if (needInit) { for (; x < length0; x++) { for (; y < length1; y++) { for (; z < length2; z++) refArray[x, y, z] = 0; z = 0; } y = 0; } } if (oriArray != null) { for (x = 0; x < length0; x++) for (y = 0; y < length1; y++) for (z = 0; z < length2; z++) oriArray[x, y, z] = refArray[x, y, z]; } } public override void ReadStrArray(string[] refArray, bool needInit) { string[] oriArray = null; byte b; int x = 0; int saveLength0 = reader.ReadInt32(); if (refArray == null)//読み捨て。レアケースのはず refArray = new string[saveLength0]; int length0 = refArray.Length; //保存されたデータの方が大きいとき。レアケースのはず if (length0 < saveLength0) { oriArray = refArray; //1818修正 サイズ違いの時にあふれないように/配列は最大まで確保、作業するのは重複部分だけ refArray = new string[Math.Max(length0, saveLength0)]; length0 = Math.Min(length0, saveLength0); } while (true) { b = reader.ReadByte(); if (b == Ebdb.EoD) break; if (b == Ebdb.Zero) { int cnt = (int)m_ReadInt(); if (needInit) for (int i = 0; i < cnt; i++) refArray[x + i] = null; x += cnt; continue; } if (b == Ebdb.String) refArray[x] = ReadString(); else throw new FileEE("バイナリデータの異常"); x++; } if (needInit) for (; x < length0; x++) refArray[x] = null; if (oriArray != null) { for (x = 0; x < length0; x++) oriArray[x] = refArray[x]; } } public override void ReadStrArray2D(string[,] refArray, bool needInit) { string[,] oriArray = null; byte b; int x = 0; int y = 0; int saveLength0 = reader.ReadInt32(); int saveLength1 = reader.ReadInt32(); if (refArray == null) refArray = new string[saveLength0, saveLength1]; int length0 = refArray.GetLength(0); int length1 = refArray.GetLength(1); if (length0 < saveLength0 || length1 < saveLength1) { oriArray = refArray; //1818修正 サイズ違いの時にあふれないように/配列は最大まで確保、作業するのは重複部分だけ refArray = new string[Math.Max(length0, saveLength0), Math.Max(length1, saveLength1)]; length0 = Math.Min(length0, saveLength0); length1 = Math.Min(length1, saveLength1); } while (true) { b = reader.ReadByte(); if (b == Ebdb.EoD) break; if (b == Ebdb.ZeroA1) { int cnt = (int)m_ReadInt(); if (needInit) for (int i = 0; i < cnt; i++) for (y = 0; y < length1; y++) refArray[x + i, y] = null; x += cnt; y = 0; continue; } if (b == Ebdb.EoA1) { if (needInit) for (; y < length1; y++) refArray[x, y] = null; x++; y = 0; continue; } if (b == Ebdb.Zero) { int cnt = (int)m_ReadInt(); if (needInit) for (int i = 0; i < cnt; i++) refArray[x, y + i] = null; y += cnt; continue; } if (b == Ebdb.String) refArray[x, y] = ReadString(); else throw new FileEE("バイナリデータの異常"); y++; } if (needInit) { for (; x < length0; x++) { for (; y < length1; y++) refArray[x, y] = null; y = 0; } } if (oriArray != null) { for (x = 0; x < length0; x++) for (y = 0; y < length1; y++) oriArray[x, y] = refArray[x, y]; } } public override void ReadStrArray3D(string[, ,] refArray, bool needInit) { string[, ,] oriArray = null; byte b; int x = 0; int y = 0; int z = 0; int saveLength0 = reader.ReadInt32(); int saveLength1 = reader.ReadInt32(); int saveLength2 = reader.ReadInt32(); if (refArray == null) refArray = new string[saveLength0, saveLength1, saveLength2]; int length0 = refArray.GetLength(0); int length1 = refArray.GetLength(1); int length2 = refArray.GetLength(2); if (length0 < saveLength0 || length1 < saveLength1 || length2 < saveLength2) { oriArray = refArray; //1818修正 サイズ違いの時にあふれないように/配列は最大まで確保、作業するのは重複部分だけ refArray = new string[Math.Max(length0, saveLength0), Math.Max(length1, saveLength1), Math.Max(length2, saveLength2)]; length0 = Math.Min(length0, saveLength0); length1 = Math.Min(length1, saveLength1); length2 = Math.Min(length2, saveLength2); } while (true) { b = reader.ReadByte(); if (b == Ebdb.EoD) break; if (b == Ebdb.ZeroA2)//cnt分だけ空の行列が連続 { int cnt = (int)m_ReadInt(); if (needInit) for (int i = 0; i < cnt; i++) for (y = 0; y < length1; y++) for (z = 0; z < length2; z++) refArray[x + i, y, z] = null; x += cnt; y = 0; z = 0; continue; } if (b == Ebdb.EoA2)//行列終わりor残りが全て0 { if (needInit) { for (; y < length1; y++) { for (; z < length2; z++) refArray[x, y, z] = null; z = 0; } } x++; y = 0; z = 0; continue; } if (b == Ebdb.ZeroA1)//cnt分だけ空の列が連続 { int cnt = (int)m_ReadInt(); if (needInit) for (int i = 0; i < cnt; i++) for (z = 0; z < length2; z++) refArray[x, y + i, z] = null; y += cnt; z = 0; continue; } if (b == Ebdb.EoA1)//列終わりor残り全て0 { if (needInit) for (; z < length2; z++) refArray[x, y, z] = null; y++; z = 0; continue; } if (b == Ebdb.Zero)//cnt分だけ0が連続 { int cnt = (int)m_ReadInt(); if (needInit) for (int i = 0; i < cnt; i++) refArray[x, y, z + i] = null; z += cnt; continue; } if (b == Ebdb.String) refArray[x, y, z] = ReadString(); else throw new FileEE("バイナリデータの異常"); z++; } if (needInit) { for (; x < length0; x++) { for (; y < length1; y++) { for (; z < length2; z++) refArray[x, y, z] = null; z = 0; } y = 0; } } if (oriArray != null) { for (x = 0; x < length0; x++) for (y = 0; y < length1; y++) for (z = 0; z < length2; z++) oriArray[x, y, z] = refArray[x, y, z]; } } } } }