Last active
October 3, 2015 13:48
-
-
Save SmallJoker/1e7e59d9278d1189fc9a to your computer and use it in GitHub Desktop.
Code to serialize and deserialize a world
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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