Skip to content

Instantly share code, notes, and snippets.

@SmallJoker
Last active October 3, 2015 13:48
Show Gist options
  • Save SmallJoker/1e7e59d9278d1189fc9a to your computer and use it in GitHub Desktop.
Save SmallJoker/1e7e59d9278d1189fc9a to your computer and use it in GitHub Desktop.
Code to serialize and deserialize a world
/*
Code to serialize and deserialize a world
Copyright (C) 2015 Krock/SmallJoker <[email protected]>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
using System;
using System.IO;
namespace Data_RW
{
struct Mapblock
{
public int FG, BG;
public byte arg3, arg4, arg5;
public Mapblock(Mapblock me)
{
FG = me.FG;
BG = me.BG;
arg3 = me.arg3;
arg4 = me.arg4;
arg5 = me.arg5;
}
}
class Data_RW_02
{
int world_width,
world_height;
public Data_RW_02(int world_width, int world_height)
{
this.world_width = world_width;
this.world_height = world_height;
for (int i = 0; i < DB_FLAGS.Length; i++)
DB_FLAGS_MASK |= DB_FLAGS[i];
}
ushort[] DB_FLAGS = new ushort[] {
1 << 15, // X
1 << 14, // Y
1 << 13, // FG
1 << 12, // BG
1 << 11, // arg3
};
int DB_FLAGS_MASK = 0;
System.Text.Encoding enc = System.Text.Encoding.UTF8;
class SaveEntry
{
public int x = 0,
y = 0,
FG = 0,
BG = 0;
public byte arg3 = 0;
public SaveEntry() { }
public SaveEntry(SaveEntry me)
{
x = me.x;
y = me.y;
FG = me.FG;
BG = me.BG;
arg3 = me.arg3;
}
public int this[int i]
{
get
{
switch (i) {
case 0: return x;
case 1: return y;
case 2: return FG;
case 3: return BG;
case 4: return arg3;
default: return -1;
}
}
}
}
public byte[] serializeData(Mapblock[,] blocks, ref string[] text)
{
#region Define variables
MemoryStream stream = new MemoryStream();
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(0xC0FFEE02);
SaveEntry last = new SaveEntry(),
cur = new SaveEntry();
#endregion
for (; cur.y < world_height; cur.y++) {
cur.x = 0;
for (; cur.x < world_width; cur.x++) {
Mapblock b = blocks[cur.x, cur.y];
cur.FG = b.FG;
cur.BG = b.BG;
if (cur.BG >= 500)
cur.BG -= 480;
if (getBlockArgCount(cur.FG) > 0)
cur.arg3 = b.arg3;
if (cur.FG == last.FG &&
cur.BG == last.BG &&
cur.arg3 == last.arg3) {
// Continue when no update is required
continue;
}
int header = 0,
pos = -1;
if (cur.y == last.y && cur.x - last.x == 1)
last.x = cur.x;
if (cur.y == last.y + 1 && cur.x - last.x < 0)
last.y = cur.y;
for (int i = 0; i < DB_FLAGS.Length; i++) {
if (cur[i] == last[i])
continue;
// Set modification flag
header |= DB_FLAGS[i];
if (pos >= 0)
continue;
if (cur[i] >= 2048)
throw new Exception("Number overflow, can not send " + cur[i]);
header |= cur[i];
pos = i + 1;
}
writer.Write((ushort)header);
for (; pos < DB_FLAGS.Length; pos++) {
if (cur[pos] == last[pos])
continue;
int value = cur[pos];
if (pos == 3 || pos == 4) // BG, arg3
writer.Write((byte)value);
else
writer.Write((ushort)value);
}
last = new SaveEntry(cur);
}
}
writer.Write((ushort)0xDEAD);
#region Portals
for (int y = 0; y < world_height; y++) {
for (int x = 0; x < world_width; x++) {
if (blocks[x, y].FG != 242)
continue;
writer.Write(blocks[x, y].arg4);
writer.Write(blocks[x, y].arg5);
}
}
#endregion
#region Texts
if (text != null) {
writer.Write((ushort)0xDEAD);
// Remove unused text entries
bool[] used_text = new bool[text.Length];
byte last_used = 0;
for (int y = 0; y < world_height; y++) {
for (int x = 0; x < world_width; x++) {
if (blocks[x, y].FG == 1000) {
byte arg3 = blocks[x, y].arg3;
if (arg3 >= text.Length)
throw new Exception("Text array incomplete. Can not find #" + arg3);
used_text[arg3] = true;
last_used = Math.Max(arg3, last_used);
}
}
}
writer.Write(last_used);
for (int i = 0; i <= last_used; i++) {
if (!used_text[i]) {
text[i] = null;
writer.Write((byte)0);
continue;
}
if (string.IsNullOrEmpty(text[i]))
text[i] = "#ERROR";
writer.Write((byte)text[i].Length);
writer.Write(enc.GetBytes(text[i]));
}
}
#endregion
writer.Close();
stream.Close();
return stream.ToArray();
}
void deserializeEntry(BinaryReader reader, ref SaveEntry dat)
{
int header = reader.ReadUInt16();
if (header == 0xDEAD) {
// Continue until world is done
dat.y = world_height;
return;
}
bool readHeader = true;
for (int i = 0; i < DB_FLAGS.Length; i++) {
if ((header & DB_FLAGS[i]) == 0)
continue;
int value = 0;
if (readHeader) {
value = header & ~DB_FLAGS_MASK;
readHeader = false;
} else if (i == 3 || i == 4)
value = reader.ReadByte();
else
value = reader.ReadUInt16();
switch (i) {
case 0: dat.x = Math.Min(value, world_width - 1); break;
case 1: dat.y = Math.Min(value, world_height - 1); break;
case 2: dat.FG = value; break;
case 3: dat.BG = value; break;
case 4: dat.arg3 = (byte)value; break;
}
}
}
public void deserializeData(ref Mapblock[,] blocks, ref string[] text, byte[] data)
{
#region Define variables
MemoryStream stream = new MemoryStream(data);
BinaryReader reader = new BinaryReader(stream);
if (reader.ReadUInt32() != 0xC0FFEE02)
throw new Exception("Can not get coffee V02");
SaveEntry cur = new SaveEntry(),
next = new SaveEntry();
// Not required if the input data is correct
blocks = new Mapblock[world_width, world_height];
#endregion
bool first = true;
while (cur.y < world_height) {
deserializeEntry(reader, ref next);
if (next.y == cur.y && next.x == cur.x && !first)
next.x = Math.Min(cur.x + 1, world_width - 1);
if (next.y == cur.y && next.x - cur.x < 0)
next.y = cur.y + 1;
first = false;
while (cur.y < world_height) {
// Stop when tail is reached
if (cur.x == next.x && cur.y == next.y)
break;
int args = getBlockArgCount(cur.FG);
if (cur.BG != 0 && cur.BG < 500)
cur.BG += 480;
Mapblock b = new Mapblock();
b.FG = cur.FG;
b.BG = cur.BG;
if (args > 0)
b.arg3 = cur.arg3;
blocks[cur.x, cur.y] = b;
// Jump to next line
cur.x++;
if (cur.x == world_width) {
cur.x = 0;
cur.y++;
}
}
// Push tail to new head
cur = new SaveEntry(next);
}
#region Portals
for (int y = 0; y < world_height; y++) {
for (int x = 0; x < world_width; x++) {
if (blocks[x, y].FG != 242)
continue;
Mapblock b = new Mapblock(blocks[x, y]);
b.arg4 = reader.ReadByte();
b.arg5 = reader.ReadByte();
blocks[x, y] = b;
}
}
#endregion
#region Texts
if (stream.Position + 2 < stream.Length) {
ushort security = reader.ReadUInt16();
if (security == 0xDEAD) {
text = new string[reader.ReadByte() + 1];
for (int i = 0; i < text.Length; i++) {
int length = reader.ReadByte();
if (length == 0)
continue;
text[i] = enc.GetString(reader.ReadBytes(length));
}
}
}
#endregion
reader.Close();
stream.Close();
}
byte getBlockArgCount(int id)
{
if (id == 43 || id == 77 || id == 1000)
return 1;
if (id == 242)
return 3;
return 0;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment