Files
factorio-ios/platform/native/shim/pngdecode.c
T
SashaGoncharov19 51d085e865 Native iPhone port of Factorio 2.0.77
Run the original macOS ARM64 game code on iOS:
- tools/retarget.py: header-only Mach-O retargeting to an iOS dylib
- platform/native: UIKit host and macOS API shims (AppKit for SDL's
  Cocoa backend, CoreGraphics displays, CoreAudio device, Metal device
  properties, Wuffs PNG decoding, Steam and peripheral stubs)
- touch, overlay, iOS keyboard and hardware keyboard input
- tools/build_native.py: build, sign, install, data copy, launch
- docs: build guide, controls, architecture

Also includes the earlier static-translation research (ARM64 -> WASM ->
native) under docs/research, runtime/, tests/ and tools/.

No game files are included; the build reads them from a local install.
2026-10-23 15:27:58 +04:00

84 lines
3.9 KiB
C

// Fast exact PNG decoding with Wuffs for the common Factorio sprite formats.
// Produces what AppKit's NSBitmapImageRep gives on macOS: tightly packed, non-premultiplied
// 8-bit samples with the source channel count (gray 1, gray+alpha 2, RGB 3, RGBA 4).
// Returns 0 for anything else (palette, tRNS, 16-bit, malformed) so the caller can fall back.
#define WUFFS_IMPLEMENTATION
#define WUFFS_CONFIG__MODULES
#define WUFFS_CONFIG__MODULE__BASE
#define WUFFS_CONFIG__MODULE__ADLER32
#define WUFFS_CONFIG__MODULE__CRC32
#define WUFFS_CONFIG__MODULE__DEFLATE
#define WUFFS_CONFIG__MODULE__ZLIB
#define WUFFS_CONFIG__MODULE__PNG
#include "../../../third_party/wuffs/wuffs-v0.3.c"
#include <stdlib.h>
#include <string.h>
#include "pngdecode.h"
static uint32_t be32(const uint8_t *p) { return (uint32_t)p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; }
// Channel count for supported PNGs (from IHDR, no tRNS before IDAT), else 0.
static int fx_png_channels(const uint8_t *d, size_t n) {
static const uint8_t sig[8] = {0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n'};
if (n < 33 || memcmp(d, sig, 8) || memcmp(d + 12, "IHDR", 4)) return 0;
uint8_t depth = d[24], type = d[25];
int channels = type == 0 ? 1 : type == 2 ? 3 : type == 4 ? 2 : type == 6 ? 4 : 0;
if (depth != 8 || !channels) return 0;
for (size_t at = 8; at + 12 <= n;) {
uint32_t len = be32(d + at);
const uint8_t *kind = d + at + 4;
if (!memcmp(kind, "IDAT", 4)) break;
if (!memcmp(kind, "tRNS", 4)) return 0;
if ((uint64_t)at + 12 + len > n) return 0;
at += 12 + (size_t)len;
}
return channels;
}
int fx_png_decode(const uint8_t *data, size_t size, uint8_t **out, int *width, int *height, int *channels) {
int ch = fx_png_channels(data, size);
if (!ch) return 0;
wuffs_png__decoder *dec = wuffs_png__decoder__alloc();
if (!dec) return 0;
// Skipping zlib/CRC verification does not change decoded pixels (verified byte-identical with
// tests/native/png_parity.m); a corrupt file decodes to garbage instead of failing.
wuffs_png__decoder__set_quirk_enabled(dec, WUFFS_BASE__QUIRK_IGNORE_CHECKSUM, true);
int ok = 0;
uint8_t *rgba = NULL, *work = NULL;
wuffs_base__io_buffer src = wuffs_base__ptr_u8__reader((uint8_t *)data, size, true);
wuffs_base__image_config ic = {0};
if (wuffs_png__decoder__decode_image_config(dec, &ic, &src).repr) goto done;
uint32_t w = wuffs_base__pixel_config__width(&ic.pixcfg), h = wuffs_base__pixel_config__height(&ic.pixcfg);
if (!w || !h || (uint64_t)w * h > (1u << 28)) goto done;
wuffs_base__pixel_config__set(&ic.pixcfg, WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL,
WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, w, h);
size_t pixels = (size_t)w * h;
rgba = malloc(pixels * 4);
uint64_t work_len = wuffs_png__decoder__workbuf_len(dec).max_incl;
work = work_len ? malloc(work_len) : NULL;
if (!rgba || (work_len && !work)) goto done;
wuffs_base__pixel_buffer pb;
if (wuffs_base__pixel_buffer__set_from_slice(&pb, &ic.pixcfg, wuffs_base__make_slice_u8(rgba, pixels * 4)).repr) goto done;
if (wuffs_png__decoder__decode_frame(dec, &pb, &src, WUFFS_BASE__PIXEL_BLEND__SRC,
wuffs_base__make_slice_u8(work, work_len), NULL).repr) goto done;
// Pack to the source channel count (gray sources decode with R == G == B).
if (ch != 4) {
uint8_t *dst = rgba;
for (size_t i = 0; i < pixels; i++) {
const uint8_t *p = rgba + i * 4;
if (ch == 1) { dst[i] = p[0]; }
else if (ch == 2) { dst[2 * i] = p[0]; dst[2 * i + 1] = p[3]; }
else { uint8_t r = p[0], g = p[1], b = p[2]; dst[3 * i] = r; dst[3 * i + 1] = g; dst[3 * i + 2] = b; }
}
}
*out = rgba; rgba = NULL;
*width = (int)w; *height = (int)h; *channels = ch;
ok = 1;
done:
free(rgba);
free(work);
free(dec);
return ok;
}