I am learning C#, and stack on BinaryReader/Writer. How i can read/write file in this cause: I am need to store this data into binary file. (Sprite class)
public class Sprite
{
public Int32 id; //spriteID
public Int32 size; //sprite byte size
public byte[] dump; //sprite image byte-array
public Sprite()
{
id = 0;
size = 0;
dump = null;
}
}
I am get the sprite data by this function:
public Sprite createSprite(Image image, System.Drawing.Imaging.ImageFormat format)
{
using (MemoryStream ms = new MemoryStream())
{
Sprite tmpSpr = new Sprite();
image.Save(ms, format);
tmpSpr.dump = ms.ToArray();
tmpSpr.size = tmpSpr.Length;
tmpSpr.id = 1; //just for example
return tmpSpr;
}
}
But i am stack on solve this:
public static bool save(string filename)
{
FileStream fileStream = new FileStream(filename, FileMode.Create);
MemoryStream ms = new MemoryStream();
BinaryWriter wr = new BinaryWriter(ms);
//How i am should write a sprites here?
}
Here the unfinished reader function. Stack on reading too.
public static bool load(string filename, ref Dictionary<UInt32, Sprite> sprites)
{
FileStream fileStream = new FileStream(filename, FileMode.Open);
try
{
using (BinaryReader reader = new BinaryReader(fileStream))
{
UInt32 totalSprites = reader.ReadUInt32(); //total sprites in file
//something like that?
List<UInt32> spriteIndexes = new List<UInt32>();
for (uint i = 0; i < totalSprites; ++i)
{
UInt32 index = reader.ReadUInt32();
spriteIndexes.Add(index);
}
UInt32 id = 1;
foreach (UInt32 element in spriteIndexes)
{
//i am not sure here =(
reader.BaseStream.Seek(element, SeekOrigin.Begin);
UInt32 size = reader.ReadUInt32();
Sprite sprite;
if (sprites.TryGetValue(id, out sprite))
{
if (sprite != null && size > 0)
{
if (sprite.size > 0)
{
//generate warning
}
else
{
sprite.id = id;
sprite.size = size;
sprite.dump = reader.ReadBytes(size);
sprites[id] = sprite;
}
}
}
else
{
//i am not shure here too.
reader.BaseStream.Seek(size, SeekOrigin.Current);
}
++id;
}
}
}
finally
{
fileStream.Close();
}
return true;
}
The Sprite file, should have a totalSprites param, and list of all sprites, with their id, size, dump. Load function "seems" almost "done", but i dont have idea, how to write file for that "reader". Please, show the solution. Thanks for advance!
Your basic problem is that you don't know what the sprite's position in the file will be until you write it (unless you go through the array and count the bytes, which is possible), but you want to write the index ahead of the sprites. You have a few options:
First, why have an index at all? If you know how many sprites you have, you can just write that number and then read the file sequentially, one sprite at a time. That's the easiest:
using (var fs = File.OpenWrite(filename))
{
using (var writer = new BinaryWriter(fs))
{
// write sprite count
writer.Write(spriteList.Count);
// and write each sprite
foreach (var s in spriteList)
{
writer.Write(sprite.id);
writer.Write(sprite.size);
writer.Write(sprite.dump);
}
}
}
Reading is just the reverse: read the count, and then read that many sprites. There's no real need for an index unless you want the ability to read the fifth sprite, for example, without having to read the first four ahead of it.
If you really need the index, you can write a placeholder for it, then write the sprites, keeping track of their positions, and then seek back to the beginning of the file and write the real index. Like this:
var index = new List<long>(spriteList.Count);
using (var fs = File.OpenWrite(filename))
{
using (var writer = new BinaryWriter(fs))
{
// write sprite count
writer.Write(spriteList.Count);
var indexPos = writer.BaseStream.Position;
// write index placeholder
for (var i = 0; i < spriteList.Count; ++i)
{
writer.Write(0L);
}
// and write each sprite
for (var i = 0; i < spriteList.Count ++i)
{
// save current position
writer.Flush();
index[i] = writer.BaseStream.Position;
writer.Write(sprite.id);
writer.Write(sprite.size);
writer.Write(sprite.dump);
}
// Seek back to the index position
writer.Flush();
writer.BaseStream.Position = indexPos;
// and write the real index
foreach (var pos in index)
{
writer.Write(pos);
}
}
}
Another alternative is to write the sprites first and keep track of their positions in the index array as above, but write the index after the sprites. Then, to read, you read the sprite count from the start of the file, multiply that by 8 (the size of a long
), and seek to the end of the file minus that much. That is:
var spriteCount = reader.ReadInt32();
long indexSize = spriteCount * 8;
reader.BaseStream.Seek(-indexSize, SeekOrigin.End);
// now read the index of spriteCount values