RLE7. Der Frame-Block beginnt wie gehabt:
char[4] "RLE7"
uint32 Datenlänge ohne Header
uint16 Breite
uint16 Länge
int16 unbekannt
int16 unbekannt
byte[x] komprimierte Daten
Die unbekannten Werte sind gerne mal negativ, ich vermute aus dem Bauch
heraus Positionsangaben, vielleicht einen Referenzpunkt ("Zentrum") im
Frame, der bei Animationen des Sprites hilft oder etwas ähnliches.
Bei der Methode RLE7 ist der Name Programm: Es handelt sich um eine
Lauflängenkodierung, deren Längenangaben auf 7 Bits beschränkt sind:
- Lese Opcode (1 Byte)
- Falls Opcode <= 0x7f, dann:
- Lese Farbe (1 Byte)
- Wiederhole Farbe (Opcode+1)-mal
- Sonst (Opcode > 0x7f):
- Schreibe Opcode direkt als Farbe
Als Beispiel hängt eine Dekompression des ersten Frames aus 007EC3D4.1
(paris1.clu) an (rle7.gif). Für mich sieht das verdächtig nach George
aus ... (Tut mir leid wegen der primitiven ASCII-Ausgabe, aber ich
wollte auf die Schnelle keinen vollständigen Konverter bauen. ;-))
00677
00678 void Screen::decompressTony(uint8 *src, uint32 compSize, uint8 *dest) {
00679 uint8 *endOfData = src + compSize;
00680 while (src < endOfData) {
00681 uint8 numFlat = *src++;
00682 if (numFlat) {
00683 memset(dest, *src, numFlat);
00684 src++;
00685 dest += numFlat;
00686 }
00687 if (src < endOfData) {
00688 uint8 numNoFlat = *src++;
00689 memcpy(dest, src, numNoFlat);
00690 src += numNoFlat;
00691 dest += numNoFlat;
00692 }
00693 }
00694 }
00695
00696 void Screen::decompressRLE7(uint8 *src, uint32 compSize, uint8 *dest) {
00697 uint8 *compBufEnd = src + compSize;
00698 while (src < compBufEnd) {
00699 uint8 code = *src++;
00700 if ((code > 127) || (code == 0))
00701 *dest++ = code;
00702 else {
00703 code++;
00704 memset(dest, *src++, code);
00705 dest += code;
00706 }
00707 }
00708 }
00709
00710 void Screen::decompressRLE0(uint8 *src, uint32 compSize, uint8 *dest) {
00711 uint8 *srcBufEnd = src + compSize;
00712 while (src < srcBufEnd) {
00713 uint8 color = *src++;
00714 if (color) {
00715 *dest++ = color;
00716 } else {
00717 uint8 skip = *src++;
00718 memset(dest, 0, skip);
00719 dest += skip;
00720 }
00721 }
00722 }
00723
Noch ein kurzes Update: Mittlerweile habe ich etwas gefunden, das
ziemlich eindeutig nach Paletten aussieht. In paris1.clu existieren
diverse Dateien mit genau 768 (= 256 * 3) Bytes Größe:
007E76D4.1
007ECEA8.2
007F0F6C.3
007F277C.3
007F915C.4
007FDAC0.5
00801A04.6
00802CC4.7
00804924.8
Die enthaltenen Bytes sind anscheinend immer <= 63, was für Farbangaben
aus der DOS-Zeit durchaus üblich ist. Durch einen einfachen Linksshift
um 2 bzw. eine Multiplikation mit 4 lassen sich die Werte in den
üblichen [0..255]-Farbraum umrechnen. Der Farbeintrag 0 (meist (0, 63,
0)) dürfte die Transparenzfarbe sein. Eigentlich ist nur noch zu klären,
in welcher Reihenfolge R, G und B gespeichert sind ...
Was -- und ob überhaupt -- die CDT damit zu tun haben, weiß ich zur Zeit
nicht. Allerdings meine ich mich zu erinnern, daß es spätestens in BS2
Transparenzeffekte gab (Person hinter Glasscheibe u.ä.) -- keine Ahnung,
ob die ältere Engine-Version das auch schon unterstützte.
public static byte[][] getDataFromSprite(File sprite) throws IOException {
ArrayList<Byte> b = new ArrayList<Byte>();
byte[] bytes = new byte[(int)sprite.length()];
int pointer = 0;
boolean start = false;
String temp = null;
FileInputStream fis = new FileInputStream(sprite);
fis.read(bytes);
Byte[][] retVal = new Byte[bytes[20] + 1][0];
// Header
for (pointer = 0; pointer < (bytes[24] & 0xFF); pointer++) {
b.add(bytes[pointer]);
}
retVal[0] = b.toArray(new Byte[b.size()]);
// Sprites
for (int i = 0; i < (bytes[20] & 0xFF); i++) {
b.clear();
start = true;
while (pointer < bytes.length) {
if (pointer + 3 < bytes.length) {
temp = new String(new byte[] {bytes[pointer], bytes[pointer + 1], bytes[pointer + 2], bytes[pointer + 3]});
if (!start && ("JIM ".equals(temp) || temp.startsWith("RLE7"))) {
break;
}
}
b.add(bytes[pointer++]);
start = false;
}
retVal[i + 1] = b.toArray(new Byte[b.size()]);
}
return convertByteTobyte(retVal);
}
public static void extractSprite(File f, HashMap<Integer, Integer> map) throws IOException {
byte[][] val = getDataFromSprite(f);
for (int i = 1; i < val.length; i++) {
ImageIO.write(convertFrame(val[i], map), "png", new File("C:/fluch/sprites/" + i + ".png"));
}
}
public static BufferedImage convertFrame(byte[] frame, HashMap<Integer, Integer> map) {
int alpha = new Color(0, 0, 0, 0).getRGB();
BufferedImage img = new BufferedImage(frame[8] & 0xFF, frame[10] & 0xFF, BufferedImage.TYPE_INT_ARGB);
if (frame[0] == 'R' && frame[1] == 'L' && frame[2] == 'E' && frame[3] == '7') { // RLE7
for (int i = 15, x = 0, y = 0; i < frame.length; i++) {
if ((frame[i] & 0xFF) < 128) {
int col = 0;
if (map.get(frame[i + 1] & 0xFF) == null) {
col = alpha;
}
else {
col = map.get(frame[i + 1] & 0xFF);
}
for (int j = frame[i] & 0xFF; j > -1; j--) {
if (img.getWidth() <= x) {
x = 0;
y++;
}
img.setRGB(x++, y, col);
}
i++;
}
else {
if (img.getWidth() <= x) {
x = 0;
y++;
}
img.setRGB(x++, y, map.get(frame[i] & 0xFF));
}
}
}
return img;
}
public static byte[][] convertByteTobyte(Byte[][] byt) {
byte[][] ret = new byte[byt.length][0];
for (int i = 0; i < ret.length; i++) {
ret[i] = convertByteTobyte(byt[i]);
}
return ret;
}
public static byte[] convertByteTobyte(Byte[] byt) {
byte[] ret = new byte[byt.length];
for (int i = 0; i < ret.length; i++) {
ret[i] = byt[i];
}
return ret;
}
Die Parallax-Layer-Dateien sind meiner Ansicht nach wie folgt aufgebaut:
char[16] "PARALLAX LAYER",0,0
uint16 Layerbreite
uint16 Layerhöhe
uint32[Layerhöhe] Zeilenoffsets
byte[x] Zeilendaten
Jedes Zeilenoffset führt zu genau einer Bildzeile des Layers innerhalb
der Datei. Da Parallax-Layer typischerweise "Bilder mit Löchern" sind,
sind die Zeilen dementsprechend aufgebaut:
// solange noch Zeilendaten vorhanden
uint8 Transparenzlänge
uint8 Farblänge
uint8[Farblänge] Farbangaben (d.h. Palettenreferenzen)
Alle Längen zusammen sollten genau die Layerbreite ergeben.
Ich habe das bisher nicht durch einen Konverter verifiziert, sondern bin
nur per Hand einige Zeileneinträge durchgegangen -- bisher scheint das
ganz gut hinzukommen.
Titel | Forum | Antworten | Datum | |
---|---|---|---|---|
Q | 2D Animations mit TimingFramework: Unbekannte Animationdauer | Spiele- und Multimedia-Programmierung | 2 |