about summary refs log tree commit diff
path: root/cards.c
diff options
context:
space:
mode:
Diffstat (limited to 'cards.c')
-rw-r--r--cards.c104
1 files changed, 101 insertions, 3 deletions
diff --git a/cards.c b/cards.c
index e67317d..174b081 100644
--- a/cards.c
+++ b/cards.c
@@ -171,7 +171,7 @@ static int loadNE(struct Cards *cards, SDL_RWops *rw, Uint16 neOffset) {
 	for (Uint16 i = 0; i < resourceCount; ++i) {
 		Uint16 offset = SDL_ReadLE16(rw);
 		Uint16 length = SDL_ReadLE16(rw);
-		SDL_ReadLE16(rw); // flags
+		/* Uint16 flags = */ SDL_ReadLE16(rw);
 		Uint16 id = SDL_ReadLE16(rw);
 		SDL_ReadLE32(rw); // reserved
 
@@ -194,9 +194,107 @@ static int loadNE(struct Cards *cards, SDL_RWops *rw, Uint16 neOffset) {
 	return 0;
 }
 
+// <https://docs.microsoft.com/en-us/windows/desktop/Debug/pe-format>
 static int loadPE(struct Cards *cards, SDL_RWops *rw, Uint16 peOffset) {
-	SDL_SetError("loadPE unimplemented");
-	return -1;
+	if (SDL_RWseek(rw, peOffset + 0x04 + 0x02, RW_SEEK_SET) < 0) return -1;
+	Uint16 sectionCount = SDL_ReadLE16(rw);
+
+	if (SDL_RWseek(rw, peOffset + 0x04 + 0x10, RW_SEEK_SET) < 0) return -1;
+	Uint16 optionalHeaderLength = SDL_ReadLE16(rw);
+	Uint16 sectionTableOffset = peOffset + 0x04 + 0x14 + optionalHeaderLength;
+
+	Uint32 resourceTableOffset = 0;
+	Uint32 resourceTableVirtual = 0;
+	for (Uint16 i = 0; i < sectionCount; ++i) {
+		Uint16 headerOffset = sectionTableOffset + 0x28 * i;
+
+		if (SDL_RWseek(rw, headerOffset, RW_SEEK_SET) < 0) return -1;
+		char name[8];
+		if (!SDL_RWread(rw, name, sizeof(name), 1)) return -1;
+		if (strncmp(".rsrc", name, sizeof(name))) continue;
+
+		if (SDL_RWseek(rw, headerOffset + 0x0C, RW_SEEK_SET) < 0) return -1;
+		resourceTableVirtual = SDL_ReadLE32(rw);
+
+		if (SDL_RWseek(rw, headerOffset + 0x14, RW_SEEK_SET) < 0) return -1;
+		resourceTableOffset = SDL_ReadLE32(rw);
+
+		break;
+	}
+	if (!resourceTableOffset) {
+		SDL_SetError("no resource table");
+		return -1;
+	}
+
+	if (SDL_RWseek(rw, resourceTableOffset + 0x0C, RW_SEEK_SET) < 0) return -1;
+	Uint16 typeNameCount = SDL_ReadLE16(rw);
+	Uint16 typeIDCount = SDL_ReadLE16(rw);
+
+	if (SDL_RWseek(rw, 0x08 * typeNameCount, RW_SEEK_CUR) < 0) return -1;
+
+	Uint32 bitmapTableOffset = 0;
+	for (Uint16 i = 0; i < typeIDCount; ++i) {
+		Uint32 typeID = SDL_ReadLE32(rw);
+		Uint32 subdirOffset = SDL_ReadLE32(rw);
+		if (typeID != 0x02) continue;
+		if (!(subdirOffset & (1 << 31))) {
+			SDL_SetError("bitmap type entry does not point to table");
+			return -1;
+		}
+		bitmapTableOffset = resourceTableOffset + (subdirOffset & ~(1 << 31));
+		break;
+	}
+
+	if (SDL_RWseek(rw, bitmapTableOffset + 0x0C, RW_SEEK_SET) < 0) return -1;
+	Uint16 nameNameCount = SDL_ReadLE16(rw);
+	Uint16 nameIDCount = SDL_ReadLE16(rw);
+
+	if (SDL_RWseek(rw, 0x08 * nameNameCount, RW_SEEK_CUR) < 0) return -1;
+
+	for (Uint16 i = 0; i < nameIDCount; ++i) {
+		Uint32 nameID = SDL_ReadLE32(rw);
+		Uint32 subdirOffset = SDL_ReadLE32(rw);
+		if (nameID >= Cards_Count) continue;
+		if (!(subdirOffset & (1 << 31))) {
+			SDL_SetError("bitmap name entry does not point to table");
+			return -1;
+		}
+
+		Sint64 nextName = SDL_RWtell(rw);
+		if (nextName < 0) return -1;
+
+		Uint32 langTableOffset =
+			resourceTableOffset + (subdirOffset & ~(1 << 31));
+
+		if (SDL_RWseek(rw, langTableOffset + 0x0C, RW_SEEK_SET) < 0) return -1;
+		Uint16 langNameCount = SDL_ReadLE16(rw);
+		Uint16 langIDCount = SDL_ReadLE16(rw);
+		if (langNameCount != 0 || langIDCount != 1) {
+			SDL_SetError("language table contains more than one entry");
+			return -1;
+		}
+
+		/* Uint32 langID = */ SDL_ReadLE32(rw);
+		Uint32 dataEntryOffset = SDL_ReadLE32(rw);
+		if (dataEntryOffset & (1 << 31)) {
+			SDL_SetError("language entry does not point to data");
+			return -1;
+		}
+		dataEntryOffset += resourceTableOffset;
+
+		if (SDL_RWseek(rw, dataEntryOffset, RW_SEEK_SET) < 0) return -1;
+		Uint32 dataVirtual = SDL_ReadLE32(rw);
+		Uint32 dataLength = SDL_ReadLE32(rw);
+		Uint32 dataOffset =
+			dataVirtual - (resourceTableVirtual - resourceTableOffset);
+
+		cards->surfaces[nameID] = dibSurface(rw, dataOffset, dataLength);
+		if (!cards->surfaces[nameID]) return -1;
+
+		if (SDL_RWseek(rw, nextName, RW_SEEK_SET) < 0) return -1;
+	}
+
+	return 0;
 }
 
 struct Cards *Cards_Load(SDL_RWops *rw, enum Cards_Flags flags) {