about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2019-03-28 18:53:21 -0400
committerJune McEnroe <june@causal.agency>2019-03-28 18:53:21 -0400
commitc5b75b347ecaca4f62280e04508d6362cd8fa093 (patch)
tree761dcf00b6c94120c9daa139e177cc12df55d7f6
parentRefactor EXE/DLL loading to be more general (diff)
downloadcards-c5b75b347ecaca4f62280e04508d6362cd8fa093.tar.gz
cards-c5b75b347ecaca4f62280e04508d6362cd8fa093.zip
Simplify (sort of) Cards API and add FreeCell loading
-rw-r--r--.gitignore1
-rw-r--r--cards.c220
-rw-r--r--cards.h42
-rw-r--r--dump.c39
-rw-r--r--freecell.c61
-rw-r--r--sol.c43
6 files changed, 220 insertions, 186 deletions
diff --git a/.gitignore b/.gitignore
index 7353963..c15f5a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 *.bmp
 *.o
 CARDS.DLL
+FREECELL.EXE
 SOL.EXE
 config.mk
 dump
diff --git a/cards.c b/cards.c
index b9153a5..fd108b9 100644
--- a/cards.c
+++ b/cards.c
@@ -82,69 +82,6 @@ fail:
 	return NULL;
 }
 
-static int setColorKey(struct Cards *cards) {
-	int i = Cards_Empty;
-	if (SDL_SetColorKey(cards->surfaces[i], SDL_TRUE, 1) < 0) return -1;
-	for (i = Cards_X; i <= Cards_O; ++i) {
-		if (SDL_SetColorKey(cards->surfaces[i], SDL_TRUE, 12) < 0) return -1;
-	}
-	return 0;
-}
-
-static int setAlphaCorners(struct Cards *cards) {
-	SDL_Surface *alpha = NULL;
-	for (int i = 0; i < Cards_Count; ++i) {
-		if (!cards->surfaces[i]) continue;
-
-		alpha = SDL_ConvertSurfaceFormat(
-			cards->surfaces[i], SDL_PIXELFORMAT_RGBA32, 0
-		);
-		if (!alpha) return -1;
-
-		if (SDL_SetSurfaceBlendMode(alpha, SDL_BLENDMODE_BLEND) < 0) goto fail;
-
-		SDL_Rect rects[8] = {
-			{ 0, 0, 2, 1 },
-			{ 0, 1, 1, 1 },
-			{ Cards_Width - 2, 0, 2, 1 },
-			{ Cards_Width - 1, 1, 1, 1 },
-			{ 0, Cards_Height - 1, 2, 1 },
-			{ 0, Cards_Height - 2, 1, 1 },
-			{ Cards_Width - 2, Cards_Height - 1, 2, 1 },
-			{ Cards_Width - 1, Cards_Height - 2, 1, 1 },
-		};
-		Uint32 trans = SDL_MapRGBA(alpha->format, 0x00, 0x00, 0x00, 0x00);
-		if (SDL_FillRects(alpha, rects, 8, trans) < 0) goto fail;
-
-		SDL_FreeSurface(cards->surfaces[i]);
-		cards->surfaces[i] = alpha;
-	}
-	return 0;
-
-fail:
-	SDL_FreeSurface(alpha);
-	return -1;
-}
-
-static int setBlackBorders(struct Cards *cards) {
-	for (int i = Cards_Diamond + Cards_A; i <= Cards_Heart + Cards_K; ++i) {
-		if (!cards->surfaces[i]) continue;
-		SDL_Rect rects[8] = {
-			{ 2, 0, Cards_Width - 4, 1 },
-			{ 2, Cards_Height - 1, Cards_Width - 4, 1 },
-			{ 0, 2, 1, Cards_Height - 4 },
-			{ Cards_Width - 1, 2, 1, Cards_Height - 4 },
-			{ 1, 1, 1, 1 },
-			{ Cards_Width - 2, 1, 1, 1 },
-			{ 1, Cards_Height - 2, 1, 1 },
-			{ Cards_Width - 2, Cards_Height - 2, 1, 1 },
-		};
-		Uint32 black = SDL_MapRGB(cards->surfaces[i]->format, 0x00, 0x00, 0x00);
-		if (SDL_FillRects(cards->surfaces[i], rects, 8, black) < 0) return -1;
-	}
-	return 0;
-}
-
 // exefmt.txt
 static int
 loadNE(SDL_Surface **surfaces, size_t count, SDL_RWops *rw, Uint16 neOffset) {
@@ -322,49 +259,123 @@ static int loadEXE(SDL_Surface **surfaces, size_t count, SDL_RWops *rw) {
 	}
 }
 
-struct Cards *Cards_Load(SDL_RWops *rw, enum Cards_Flags flags) {
-	struct Cards *cards = calloc(1, sizeof(*cards));
-	if (!cards) {
-		SDL_SetError("calloc error: %s", strerror(errno));
-		return NULL;
+static int setColorKey(SDL_Surface **surfaces, size_t count) {
+	size_t i = Cards_Empty;
+	if (i < count) {
+		if (SDL_SetColorKey(surfaces[i], SDL_TRUE, 1) < 0) return -1;
+	}
+	for (i = Cards_X; i <= Cards_O; ++i) {
+		if (i >= count) break;
+		if (SDL_SetColorKey(surfaces[i], SDL_TRUE, 12) < 0) return -1;
 	}
+	return 0;
+}
 
-	if (loadEXE(cards->surfaces, Cards_Count, rw) < 0) goto fail;
+static int setAlphaCorners(SDL_Surface **surfaces, size_t count) {
+	SDL_Surface *alpha = NULL;
+	for (size_t i = 0; i < count; ++i) {
+		if (!surfaces[i]) continue;
 
-	for (int i = Cards_Club + Cards_A; i <= Cards_Spade + Cards_K; ++i) {
-		if (cards->surfaces[i]) continue;
-		SDL_SetError("missing resource %d", i);
-		goto fail;
+		alpha = SDL_ConvertSurfaceFormat(surfaces[i], SDL_PIXELFORMAT_RGBA32, 0);
+		if (!alpha) return -1;
+
+		if (SDL_SetSurfaceBlendMode(alpha, SDL_BLENDMODE_BLEND) < 0) goto fail;
+
+		SDL_Rect rects[8] = {
+			{ 0, 0, 2, 1 },
+			{ 0, 1, 1, 1 },
+			{ Cards_CardWidth - 2, 0, 2, 1 },
+			{ Cards_CardWidth - 1, 1, 1, 1 },
+			{ 0, Cards_CardHeight - 1, 2, 1 },
+			{ 0, Cards_CardHeight - 2, 1, 1 },
+			{ Cards_CardWidth - 2, Cards_CardHeight - 1, 2, 1 },
+			{ Cards_CardWidth - 1, Cards_CardHeight - 2, 1, 1 },
+		};
+		Uint32 trans = SDL_MapRGBA(alpha->format, 0x00, 0x00, 0x00, 0x00);
+		if (SDL_FillRects(alpha, rects, 8, trans) < 0) goto fail;
+
+		SDL_FreeSurface(surfaces[i]);
+		surfaces[i] = alpha;
 	}
-	for (int i = Cards_Empty; i <= Cards_Back12; ++i) {
-		if (cards->surfaces[i]) continue;
-		SDL_SetError("missing resource %d", i);
-		goto fail;
+	return 0;
+
+fail:
+	SDL_FreeSurface(alpha);
+	return -1;
+}
+
+static int setBlackBorders(SDL_Surface **surfaces, size_t count) {
+	for (size_t i = Cards_Diamond + Cards_A; i <= Cards_Heart + Cards_K; ++i) {
+		if (i >= count) break;
+		if (!surfaces[i]) continue;
+		SDL_Rect rects[8] = {
+			{ 2, 0, Cards_CardWidth - 4, 1 },
+			{ 2, Cards_CardHeight - 1, Cards_CardWidth - 4, 1 },
+			{ 0, 2, 1, Cards_CardHeight - 4 },
+			{ Cards_CardWidth - 1, 2, 1, Cards_CardHeight - 4 },
+			{ 1, 1, 1, 1 },
+			{ Cards_CardWidth - 2, 1, 1, 1 },
+			{ 1, Cards_CardHeight - 2, 1, 1 },
+			{ Cards_CardWidth - 2, Cards_CardHeight - 2, 1, 1 },
+		};
+		Uint32 black = SDL_MapRGB(surfaces[i]->format, 0x00, 0x00, 0x00);
+		if (SDL_FillRects(surfaces[i], rects, 8, black) < 0) return -1;
 	}
-	for (int i = Cards_X; i <= Cards_O; ++i) {
-		if (cards->surfaces[i]) continue;
-		SDL_SetError("missing resource %d", i);
-		goto fail;
+	return 0;
+}
+
+static int
+checkRange(SDL_Surface **surfaces, size_t count, size_t a, size_t b) {
+	for (size_t i = a; i <= b; ++i) {
+		if (i >= count) break;
+		if (surfaces[i]) continue;
+		SDL_SetError("missing resource %zu", i);
+		return -1;
 	}
+	return 0;
+}
 
+int
+Cards_LoadCards(
+	SDL_Surface **surfaces, size_t count,
+	SDL_RWops *rw, enum Cards_Flag flags
+) {
+	memset(surfaces, 0, sizeof(*surfaces) * count);
+	if (loadEXE(surfaces, count, rw) < 0) return -1;
+	if (checkRange(surfaces, count, Cards_A, Cards_Back12) < 0) return -1;
+	if (checkRange(surfaces, count, Cards_X, Cards_O) < 0) return -1;
 	if (flags & Cards_ColorKey) {
-		if (setColorKey(cards) < 0) goto fail;
+		if (setColorKey(surfaces, count) < 0) return -1;
 	}
 	if (flags & Cards_AlphaCorners) {
-		if (setAlphaCorners(cards) < 0) goto fail;
+		if (setAlphaCorners(surfaces, count) < 0) return -1;
 	}
 	if (flags & Cards_BlackBorders) {
-		if (setBlackBorders(cards) < 0) goto fail;
+		if (setBlackBorders(surfaces, count) < 0) return -1;
 	}
+	return 0;
+}
 
-	return cards;
-
-fail:
-	Cards_Free(cards);
-	return NULL;
+int
+Cards_LoadFreeCell(
+	SDL_Surface **surfaces, size_t count,
+	SDL_RWops *rw, enum Cards_Flag flags
+) {
+	memset(surfaces, 0, sizeof(*surfaces) * count);
+	if (loadEXE(surfaces, count, rw) < 0) return -1;
+	if (checkRange(surfaces, count, Cards_KingRight, Cards_KingWin) < 0) {
+		return -1;
+	}
+	if (flags & Cards_ColorKey) {
+		for (size_t i = Cards_KingRight; i <= Cards_KingWin; ++i) {
+			if (i >= count) break;
+			if (SDL_SetColorKey(surfaces[i], SDL_TRUE, 2) < 0) return -1;
+		}
+	}
+	return 0;
 }
 
-int invertPalette(SDL_Surface *surface) {
+static int invertPalette(SDL_Surface *surface) {
 	const SDL_Palette *palette = surface->format->palette;
 	SDL_Palette *invert = SDL_AllocPalette(palette->ncolors);
 	if (!invert) return -1;
@@ -379,7 +390,7 @@ int invertPalette(SDL_Surface *surface) {
 	return 0;
 }
 
-int invertPixels(SDL_Surface *surface) {
+static int invertPixels(SDL_Surface *surface) {
 	if (SDL_LockSurface(surface) < 0) return -1;
 	Uint8 *pixels = surface->pixels;
 	for (int y = 0; y < surface->h; ++y) {
@@ -394,25 +405,14 @@ int invertPixels(SDL_Surface *surface) {
 	return 0;
 }
 
-int Cards_Invert(struct Cards *cards) {
-	for (int i = 0; i < Cards_Count; ++i) {
-		if (!cards->surfaces[i]) continue;
-		if (cards->surfaces[i]->format->palette) {
-			if (invertPalette(cards->surfaces[i]) < 0) return -1;
-		} else if (cards->surfaces[i]->format->BytesPerPixel == 4) {
-			if (invertPixels(cards->surfaces[i]) < 0) return -1;
-		} else {
-			SDL_SetError("unexpected surface format");
-			return -1;
-		}
+int Cards_InvertSurface(SDL_Surface *surface) {
+	if (surface->format->palette) {
+		if (invertPalette(surface) < 0) return -1;
+	} else if (surface->format->BytesPerPixel == 4) {
+		if (invertPixels(surface) < 0) return -1;
+	} else {
+		SDL_SetError("cannot invert surface format");
+		return -1;
 	}
 	return 0;
 }
-
-void Cards_Free(struct Cards *cards) {
-	for (int i = 0; i < Cards_Count; ++i) {
-		if (!cards->surfaces[i]) continue;
-		SDL_FreeSurface(cards->surfaces[i]);
-	}
-	free(cards);
-}
diff --git a/cards.h b/cards.h
index 8cd99c4..845b97b 100644
--- a/cards.h
+++ b/cards.h
@@ -19,19 +19,22 @@
 
 #include <SDL_rwops.h>
 #include <SDL_surface.h>
+#include <stddef.h>
 
 enum {
-	Cards_Width = 71,
-	Cards_Height = 96,
+	Cards_CardWidth = 71,
+	Cards_CardHeight = 96,
+	Cards_KingWidth = 32,
+	Cards_KingHeight = 32,
 };
 
-enum {
+enum Cards_Card {
 	Cards_Club,
 	Cards_Diamond = 13,
 	Cards_Heart = 26,
 	Cards_Spade = 39,
 
-	// Add rank to suit to obtain card face index.
+	// Add rank to suit to obtain card index.
 	Cards_A = 1,
 	Cards_2, Cards_3, Cards_4, Cards_5, Cards_6, Cards_7, Cards_8, Cards_9,
 	Cards_10, Cards_J, Cards_Q, Cards_K,
@@ -44,25 +47,34 @@ enum {
 	Cards_X = 67,
 	Cards_O,
 
-	Cards_Count,
+	Cards_CardCount,
 };
 
-// Some pointers will be NULL since there are gaps in the indices.
-struct Cards {
-	SDL_Surface *surfaces[Cards_Count];
+enum Cards_FreeCell {
+	Cards_KingRight = 1,
+	Cards_KingLeft,
+	Cards_KingWin,
+	Cards_FreeCellCount,
 };
 
-enum Cards_Flags {
-	// Set color key for Cards_Empty, Cards_X, Cards_O.
+enum Cards_Flag {
 	Cards_ColorKey = 1 << 0,
-	// Set alpha in card corners.
 	Cards_AlphaCorners = 1 << 1,
-	// Set red card borders to black.
 	Cards_BlackBorders = 1 << 2,
 };
 
-struct Cards *Cards_Load(SDL_RWops *rw, enum Cards_Flags flags);
-int Cards_Invert(struct Cards *cards);
-void Cards_Free(struct Cards *cards);
+int
+Cards_LoadCards(
+	SDL_Surface **surfaces, size_t count,
+	SDL_RWops *rw, enum Cards_Flag flags
+);
+
+int
+Cards_LoadFreeCell(
+	SDL_Surface **surfaces, size_t count,
+	SDL_RWops *rw, enum Cards_Flag flags
+);
+
+int Cards_InvertSurface(SDL_Surface *surface);
 
 #endif
diff --git a/dump.c b/dump.c
index f9f636f..127d6b1 100644
--- a/dump.c
+++ b/dump.c
@@ -28,14 +28,16 @@
 #include "cards.h"
 
 int main(int argc, char *argv[]) {
-	enum Cards_Flags flags = 0;
+	bool freecell = false;
+	enum Cards_Flag flags = 0;
 	bool invert = false;
 
 	int opt;
-	while (0 < (opt = getopt(argc, argv, "abik"))) {
+	while (0 < (opt = getopt(argc, argv, "abfik"))) {
 		switch (opt) {
 			break; case 'a': flags |= Cards_AlphaCorners;
 			break; case 'b': flags |= Cards_BlackBorders;
+			break; case 'f': freecell = true;
 			break; case 'i': invert = true;
 			break; case 'k': flags |= Cards_ColorKey;
 			break; default:  return EX_USAGE;
@@ -50,22 +52,31 @@ int main(int argc, char *argv[]) {
 	}
 	if (!rw) errx(EX_NOINPUT, "SDL_RWFromFile: %s", SDL_GetError());
 
-	struct Cards *cards = Cards_Load(rw, flags);
-	if (!cards) errx(EX_DATAERR, "Cards_Load: %s", SDL_GetError());
+	SDL_Surface *surfaces[Cards_CardCount];
+	if (freecell) {
+		int error = Cards_LoadFreeCell(
+			surfaces, Cards_CardCount, rw, flags
+		);
+		if (error) errx(EX_DATAERR, "Cards_LoadFreeCell: %s", SDL_GetError());
+	} else {
+		int error = Cards_LoadCards(surfaces, Cards_CardCount, rw, flags);
+		if (error) errx(EX_DATAERR, "Cards_LoadCards: %s", SDL_GetError());
+	}
 	SDL_RWclose(rw);
 
-	if (invert) {
-		int error = Cards_Invert(cards);
-		if (error) errx(EX_DATAERR, "Cards_Invert: %s", SDL_GetError());
-	}
+	for (size_t i = 0; i < Cards_CardCount; ++i) {
+		if (!surfaces[i]) continue;
+
+		if (invert) {
+			int error = Cards_InvertSurface(surfaces[i]);
+			if (error) {
+				errx(EX_DATAERR, "Cards_InvertSurface: %s", SDL_GetError());
+			}
+		}
 
-	for (int i = 0; i < Cards_Count; ++i) {
-		if (!cards->surfaces[i]) continue;
 		char name[sizeof("00.bmp")];
-		snprintf(name, sizeof(name), "%02d.bmp", i);
-		int error = SDL_SaveBMP(cards->surfaces[i], name);
+		snprintf(name, sizeof(name), "%02zu.bmp", i);
+		int error = SDL_SaveBMP(surfaces[i], name);
 		if (error) errx(EX_CANTCREAT, "SDL_SaveBMP: %s", SDL_GetError());
 	}
-
-	Cards_Free(cards);
 }
diff --git a/freecell.c b/freecell.c
index f7a19cb..e8b41f6 100644
--- a/freecell.c
+++ b/freecell.c
@@ -128,6 +128,9 @@ static bool gameAuto(void) {
 }
 
 enum {
+	CardWidth = Cards_CardWidth,
+	CardHeight = Cards_CardHeight,
+
 	StackMarginX = 7,
 	StackMarginY = 10,
 
@@ -136,16 +139,16 @@ enum {
 
 	CellsMarginX = 64,
 
-	FoundationX = CellX + 4 * Cards_Width + CellsMarginX,
+	FoundationX = CellX + 4 * CardWidth + CellsMarginX,
 	FoundationY = CellY,
 
 	TableauX = StackMarginX,
-	TableauY = CellY + Cards_Height + StackMarginY,
+	TableauY = CellY + CardHeight + StackMarginY,
 
 	FanDownDeltaY = 17,
 
-	WindowWidth = 8 * Cards_Width + 9 * StackMarginX + 1,
-	WindowHeight = TableauY + Cards_Height
+	WindowWidth = 8 * CardWidth + 9 * StackMarginX + 1,
+	WindowHeight = TableauY + CardHeight
 		+ 13 * FanDownDeltaY
 		+ StackMarginY,
 };
@@ -159,27 +162,27 @@ static struct List reveal;
 static void updateLayout(void) {
 	layoutClear(&layout);
 
-	SDL_Rect cell = { CellX, CellY, Cards_Width, Cards_Height };
+	SDL_Rect cell = { CellX, CellY, CardWidth, CardHeight };
 	for (uint i = Cell1; i <= Cell4; ++i) {
 		stackRects[i] = cell;
 		layoutStack(&layout, &cell, &stacks[i], &Flat);
-		cell.x += Cards_Width;
+		cell.x += CardWidth;
 	}
 
-	SDL_Rect found = { FoundationX, FoundationY, Cards_Width, Cards_Height };
+	SDL_Rect found = { FoundationX, FoundationY, CardWidth, CardHeight };
 	for (uint i = Foundation1; i <= Foundation4; ++i) {
 		stackRects[i] = found;
 		layoutStack(&layout, &found, &stacks[i], &Flat);
-		found.x += Cards_Width;
+		found.x += CardWidth;
 	}
 
-	SDL_Rect table = { TableauX, TableauY, Cards_Width, Cards_Height };
+	SDL_Rect table = { TableauX, TableauY, CardWidth, CardHeight };
 	for (uint i = Tableau1; i <= Tableau8; ++i) {
 		stackRects[i] = table;
 		stackRects[i].h = WindowHeight;
 		SDL_Rect rect = table;
 		layoutStack(&layout, &rect, &stacks[i], &FanDown);
-		table.x += Cards_Width + StackMarginX;
+		table.x += CardWidth + StackMarginX;
 	}
 }
 
@@ -276,8 +279,8 @@ static SDL_Renderer *render;
 
 static void renderOutlines(void) {
 	for (uint i = Foundation1; i <= Cell4; ++i) {
-		int right = stackRects[i].x + Cards_Width - 1;
-		int bottom = stackRects[i].y + Cards_Height - 1;
+		int right = stackRects[i].x + CardWidth - 1;
+		int bottom = stackRects[i].y + CardHeight - 1;
 		SDL_Point black[3] = {
 			{ stackRects[i].x, bottom - 1 },
 			{ stackRects[i].x, stackRects[i].y },
@@ -343,13 +346,15 @@ int main(void) {
 		return EXIT_FAILURE;
 	}
 
-	struct Cards *cards = Cards_Load(
-		rw, Cards_ColorKey | Cards_AlphaCorners | Cards_BlackBorders
+	SDL_Surface *surfaces[Cards_Empty];
+	int error = Cards_LoadCards(
+		surfaces, Cards_Empty,
+		rw, Cards_AlphaCorners | Cards_BlackBorders
 	);
-	if (!cards) err("Cards_Load");
+	if (error) err("Cards_LoadCards");
 	SDL_RWclose(rw);
 
-	int error = SDL_CreateWindowAndRenderer(
+	error = SDL_CreateWindowAndRenderer(
 		WindowWidth, WindowHeight, SDL_WINDOW_ALLOW_HIGHDPI,
 		&window, &render
 	);
@@ -359,22 +364,22 @@ int main(void) {
 	SDL_RenderSetIntegerScale(render, SDL_TRUE);
 	SDL_RenderSetLogicalSize(render, WindowWidth, WindowHeight);
 
-	SDL_Texture *textures[Cards_Count];
-	SDL_Texture *inverted[Cards_Count];
-	for (uint i = 0; i < Cards_Count; ++i) {
+	SDL_Texture *textures[Cards_Empty];
+	SDL_Texture *inverted[Cards_Empty];
+	for (uint i = 0; i < Cards_Empty; ++i) {
 		textures[i] = NULL;
-		if (!cards->surfaces[i]) continue;
-		textures[i] = SDL_CreateTextureFromSurface(render, cards->surfaces[i]);
-		if (!textures[i]) err("SDL_CreateTextureFromSurface");
-	}
-	if (Cards_Invert(cards) < 0) err("Cards_Invert");
-	for (uint i = 0; i < Cards_Count; ++i) {
 		inverted[i] = NULL;
-		if (!cards->surfaces[i]) continue;
-		inverted[i] = SDL_CreateTextureFromSurface(render, cards->surfaces[i]);
+		if (!surfaces[i]) continue;
+
+		textures[i] = SDL_CreateTextureFromSurface(render, surfaces[i]);
+		if (!textures[i]) err("SDL_CreateTextureFromSurface");
+
+		if (Cards_InvertSurface(surfaces[i]) < 0) err("Cards_InvertSurface");
+		inverted[i] = SDL_CreateTextureFromSurface(render, surfaces[i]);
 		if (!inverted[i]) err("SDL_CreateTextureFromSurface");
+
+		SDL_FreeSurface(surfaces[i]);
 	}
-	Cards_Free(cards);
 
 	srand(time(NULL));
 	gameDeal();
diff --git a/sol.c b/sol.c
index c7ad5ec..a2bd7c3 100644
--- a/sol.c
+++ b/sol.c
@@ -172,27 +172,30 @@ static bool gameMove(uint dest, Card card) {
 }
 
 enum {
+	CardWidth = Cards_CardWidth,
+	CardHeight = Cards_CardHeight,
+
 	StackMarginX = 11,
 	StackMarginY = 6,
 
 	StockX = StackMarginX,
 	StockY = StackMarginY,
 
-	WasteX = StockX + Cards_Width + StackMarginX,
+	WasteX = StockX + CardWidth + StackMarginX,
 	WasteY = StackMarginY,
 
-	FoundationX = WasteX + 2 * (Cards_Width + StackMarginX),
+	FoundationX = WasteX + 2 * (CardWidth + StackMarginX),
 	FoundationY = StackMarginY,
 
 	TableauX = StackMarginX,
-	TableauY = StockY + Cards_Height + StackMarginY,
+	TableauY = StockY + CardHeight + StackMarginY,
 
 	FanRightDeltaX = 14,
 	FanDownDeltaYBack = 3,
 	FanDownDeltaYFront = 15,
 
-	WindowWidth = 7 * Cards_Width + 8 * StackMarginX,
-	WindowHeight = 2 * (StackMarginY + Cards_Height)
+	WindowWidth = 7 * CardWidth + 8 * StackMarginX,
+	WindowHeight = 2 * (StackMarginY + CardHeight)
 		+ 6 * FanDownDeltaYBack
 		+ 12 * FanDownDeltaYFront
 		+ StackMarginY,
@@ -212,31 +215,31 @@ static uint backTexture = Cards_Back1;
 static void updateLayout(void) {
 	layoutClear(&layout);
 
-	SDL_Rect stock = { StockX, StockY, Cards_Width, Cards_Height };
+	SDL_Rect stock = { StockX, StockY, CardWidth, CardHeight };
 	stackRects[Stock] = stock;
 	listPush(&layout.main, &stock, Cards_O);
 	layoutStack(&layout, &stock, &stacks[Stock], &Square);
 
-	SDL_Rect waste = { WasteX, WasteY, Cards_Width, Cards_Height };
+	SDL_Rect waste = { WasteX, WasteY, CardWidth, CardHeight };
 	layoutStack(&layout, &waste, &stacks[Waste1], &Square);
 	layoutStack(&layout, &waste, &stacks[Waste3], &FanRight);
 
-	SDL_Rect found = { FoundationX, FoundationY, Cards_Width, Cards_Height };
+	SDL_Rect found = { FoundationX, FoundationY, CardWidth, CardHeight };
 	for (uint i = Foundation1; i <= Foundation4; ++i) {
 		stackRects[i] = found;
 		listPush(&layout.main, &found, Cards_Empty);
 		SDL_Rect rect = found;
 		layoutStack(&layout, &rect, &stacks[i], &Square);
-		found.x += Cards_Width + StackMarginX;
+		found.x += CardWidth + StackMarginX;
 	}
 
-	SDL_Rect table = { TableauX, TableauY, Cards_Width, Cards_Height };
+	SDL_Rect table = { TableauX, TableauY, CardWidth, CardHeight };
 	for (uint i = Tableau1; i <= Tableau7; ++i) {
 		stackRects[i] = table;
 		stackRects[i].h = WindowHeight;
 		SDL_Rect rect = table;
 		layoutStack(&layout, &rect, &stacks[i], &FanDown);
-		table.x += Cards_Width + StackMarginX;
+		table.x += CardWidth + StackMarginX;
 	}
 }
 
@@ -300,7 +303,7 @@ static bool mouseMotion(SDL_MouseMotionEvent motion) {
 
 static SDL_Window *window;
 static SDL_Renderer *render;
-static SDL_Texture *textures[Cards_Count];
+static SDL_Texture *textures[Cards_CardCount];
 
 static void renderList(const struct List *list) {
 	for (uint i = 0; i < list->len; ++i) {
@@ -349,13 +352,15 @@ int main(void) {
 		return EXIT_FAILURE;
 	}
 
-	struct Cards *cards = Cards_Load(
+	SDL_Surface *surfaces[Cards_CardCount];
+	int error = Cards_LoadCards(
+		surfaces, Cards_CardCount,
 		rw, Cards_ColorKey | Cards_AlphaCorners | Cards_BlackBorders
 	);
-	if (!cards) err("Cards_Load");
+	if (error) err("Cards_LoadCards");
 	SDL_RWclose(rw);
 
-	int error = SDL_CreateWindowAndRenderer(
+	error = SDL_CreateWindowAndRenderer(
 		WindowWidth, WindowHeight, SDL_WINDOW_ALLOW_HIGHDPI,
 		&window, &render
 	);
@@ -365,13 +370,13 @@ int main(void) {
 	SDL_RenderSetIntegerScale(render, SDL_TRUE);
 	SDL_RenderSetLogicalSize(render, WindowWidth, WindowHeight);
 
-	for (uint i = 0; i < Cards_Count; ++i) {
+	for (uint i = 0; i < Cards_CardCount; ++i) {
 		textures[i] = NULL;
-		if (!cards->surfaces[i]) continue;
-		textures[i] = SDL_CreateTextureFromSurface(render, cards->surfaces[i]);
+		if (!surfaces[i]) continue;
+		textures[i] = SDL_CreateTextureFromSurface(render, surfaces[i]);
 		if (!textures[i]) err("SDL_CreateTextureFromSurface");
+		SDL_FreeSurface(surfaces[i]);
 	}
-	Cards_Free(cards);
 
 	srand(time(NULL));
 	backTexture = Cards_Back1 + randUniform(12);