/// <summary> /// Reads file from given stream and returns it. /// </summary> /// <param name="stream"></param> /// <returns></returns> public static DataDogFile Read(Stream stream) { var result = new DataDogFile(); if (stream.Length < 40) { throw new ArgumentException("Not a data dog file."); } using (var br = new BinaryReader(stream)) { var signature = Encoding.UTF8.GetString(br.ReadBytes(16)); var version = signature.Substring(9).Replace(" ", " ").Replace(" ", "."); if (!signature.StartsWith("DDBINFILE")) { throw new ArgumentException("Incorrect header."); } var stringsOffset = br.ReadInt32(); var stringsLength = br.ReadInt32(); var dataSize = br.ReadInt32(); var typesLength = br.ReadInt32(); var fieldsLength = br.ReadInt32(); var listsLength = br.ReadInt32(); var typeDefinitions = Encoding.UTF8.GetString(br.ReadBytes(typesLength)).TrimEnd('\0'); var fieldDefinitions = Encoding.UTF8.GetString(br.ReadBytes(fieldsLength)).TrimEnd('\0'); var listDefinitions = Encoding.UTF8.GetString(br.ReadBytes(listsLength)).TrimEnd('\0'); var stringBlock = br.ReadBytes(stringsLength); var data = br.ReadBytes(dataSize); var dataDogInfo = Encoding.GetEncoding("EUC-KR").GetString(br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position))).TrimEnd('\0'); var fieldVarTypes = FindVarTypes(dataDogInfo); result.DataDogInfo = dataDogInfo; var typeMatches = TypesRegex.Matches(typeDefinitions); foreach (Match match in typeMatches) { var name = match.Groups["name"].Value; var size = Convert.ToInt32(match.Groups["size"].Value); var strct = new DataTypeDefinition(name, size); result.Types[name] = strct; } var fieldMatches = FieldsRegex.Matches(fieldDefinitions); if (fieldMatches.Count != fieldDefinitions.Count(a => a == '|')) { throw new FormatException("Invalid number of fields."); } foreach (Match match in fieldMatches) { var typeName = match.Groups["typeName"].Value; if (!result.Types.TryGetValue(typeName, out var type)) { throw new TypeAccessException($"Type '{typeName}' not found for field."); } var fieldName = match.Groups["fieldName"].Value; var offset = Convert.ToInt32(match.Groups["offset"].Value); var size = Convert.ToInt32(match.Groups["size"].Value); var typeStr = match.Groups["type"].Value; var fieldReadType = GetReadType(typeStr); var fullFieldName = (typeName + "." + fieldName).ToLowerInvariant(); if (!fieldVarTypes.TryGetValue(fullFieldName, out var varType)) { throw new TypeAccessException($"Type not found for '{fullFieldName}'."); } var field = new DataFieldDefinition(fieldName, offset, fieldReadType, size, varType); type.Fields[fieldName] = field; } using (var dataStream = new MemoryStream(data)) using (var br2 = new BinaryReader(dataStream)) { var listMatches = ListRegex.Matches(listDefinitions); var read = 0; for (var i = 0; i < listMatches.Count; ++i) { var listMatch = listMatches[i]; var listName = listMatch.Groups["name"].Value; var listTypeName = listMatch.Groups["typeName"].Value; var listCount = Convert.ToInt32(listMatch.Groups["count"].Value); if (!result.Types.TryGetValue(listTypeName, out var type)) { throw new TypeAccessException($"Type '{listTypeName}' not found for data."); } var list = new DataObjectList(listName, type); for (var j = 0; j < listCount; ++j) { var match = listMatches[++i]; var objName = match.Groups["name"].Value; var typeName = match.Groups["typeName"].Value; var count = Convert.ToInt32(match.Groups["count"].Value); var offset = Convert.ToInt32(match.Groups["offset"].Value); dataStream.Seek(offset, SeekOrigin.Begin); var bytes = br2.ReadBytes(type.Size); var obj = new DataObject(objName, type); read += bytes.Length; foreach (var field in type.Fields.Values.OrderBy(a => a.Offset)) { var objField = new DataField(field.Name, field.VarType); switch (field.VarType) { case DataVarType.Byte: objField.Value = bytes[field.Offset]; break; case DataVarType.Bool: objField.Value = (bytes[field.Offset] != 0); break; case DataVarType.Integer: objField.Value = BitConverter.ToInt32(bytes, field.Offset); break; case DataVarType.Color: objField.Value = BitConverter.ToUInt32(bytes, field.Offset); break; case DataVarType.Float: objField.Value = BitConverter.ToSingle(bytes, field.Offset); break; case DataVarType.String: case DataVarType.Reference: var stringOffset = BitConverter.ToInt32(bytes, field.Offset); try { var nullIndex = Array.IndexOf(stringBlock, (byte)0, stringOffset); var str = Encoding.GetEncoding("EUC-KR").GetString(stringBlock, stringOffset, nullIndex - stringOffset); objField.Value = str; } catch (ArgumentOutOfRangeException) { Console.Write("Error: String not found."); } break; default: throw new InvalidDataException($"Unknown type '{field.VarType}'."); } obj.Fields[field.Name] = objField; } list.Objects.Add(obj); } result.Lists[list.Name] = list; } if (read != dataStream.Length) { throw new FormatException("Data wasn't read completely."); } } } return(result); }
/// <summary> /// Creates new instance. /// </summary> /// <param name="name"></param> /// <param name="type"></param> public DataObject(string name, DataTypeDefinition type) { this.Name = name; this.Type = type; }