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.
This commit is contained in:
SashaGoncharov19 committed 2026-10-23 15:27:58 +04:00
commit 51d085e865
89 files changed
+7999

No files matched your search

+97
View File
@@ -0,0 +1,97 @@
// macOS CoreAudio device model for SDL's coreaudio backend (macOS build).
// FX_LIB_COREAUDIO: one virtual output device (the iPhone route), answering the AudioObject
// properties SDL 2.24 queries. Playback itself uses the real iOS AudioQueue.
// FX_LIB_AUDIOTOOLBOX: AudioQueueSetProperty passes through, except macOS-only CurrentDevice.
#include <AudioToolbox/AudioToolbox.h>
#include <CoreFoundation/CoreFoundation.h>
#include <dlfcn.h>
#include <string.h>
#include "fx.h"
#define FOURCC(a, b, c, d) ((uint32_t)(a) << 24 | (uint32_t)(b) << 16 | (uint32_t)(c) << 8 | (uint32_t)(d))
#ifdef FX_LIB_COREAUDIO
typedef uint32_t AudioObjectID;
typedef struct { uint32_t mSelector, mScope, mElement; } FXPropertyAddress;
typedef OSStatus (*FXPropertyListener)(AudioObjectID, uint32_t, const FXPropertyAddress *, void *);
enum {
kSystemObject = 1, kSpeaker = 0x100,
kDevices = FOURCC('d', 'e', 'v', '#'), kDefaultOutput = FOURCC('d', 'O', 'u', 't'),
kDefaultInput = FOURCC('d', 'I', 'n', ' '), kStreamConfiguration = FOURCC('s', 'l', 'a', 'y'),
kName = FOURCC('l', 'n', 'a', 'm'), kNominalSampleRate = FOURCC('n', 's', 'r', 't'),
kIsAlive = FOURCC('l', 'i', 'v', 'n'), kHogMode = FOURCC('o', 'i', 'n', 'k'),
kUID = FOURCC('u', 'i', 'd', ' '), kScopeInput = FOURCC('i', 'n', 'p', 't'),
kUnknownProperty = FOURCC('w', 'h', 'o', '?'), kBadObject = FOURCC('!', 'o', 'b', 'j'),
};
// Writes the property into `out` (when non-NULL) and returns its size, or 0 if unsupported.
static uint32_t fx_property(AudioObjectID object, const FXPropertyAddress *a, void *out, OSStatus *status) {
*status = 0;
if (object == kSystemObject) {
switch (a->mSelector) {
case kDevices: case kDefaultOutput: if (out) *(AudioObjectID *)out = kSpeaker; return 4;
case kDefaultInput: if (out) *(AudioObjectID *)out = 0; return 4; // no capture device
}
} else if (object == kSpeaker) {
int input = a->mScope == kScopeInput;
switch (a->mSelector) {
case kStreamConfiguration:
if (out) {
AudioBufferList *list = out;
list->mNumberBuffers = input ? 0 : 1;
list->mBuffers[0] = (AudioBuffer){2, 0, NULL};
}
return sizeof(AudioBufferList);
case kName: if (out) *(CFStringRef *)out = CFStringCreateCopy(NULL, CFSTR("iPhone")); return sizeof(CFStringRef);
case kUID: if (out) *(CFStringRef *)out = CFStringCreateCopy(NULL, CFSTR("fx-ios-output")); return sizeof(CFStringRef);
case kNominalSampleRate: if (out) *(Float64 *)out = 48000.0; return sizeof(Float64);
case kIsAlive: if (out) *(uint32_t *)out = !input; return 4;
case kHogMode: if (out) *(pid_t *)out = -1; return sizeof(pid_t);
}
} else {
*status = kBadObject;
return 0;
}
*status = kUnknownProperty;
fx_log("[fx] CoreAudio: unsupported property '%c%c%c%c' on object %u", a->mSelector >> 24, a->mSelector >> 16,
a->mSelector >> 8, a->mSelector, object);
return 0;
}
FX_EXPORT Boolean AudioObjectHasProperty(AudioObjectID object, const FXPropertyAddress *a) {
OSStatus status;
return fx_property(object, a, NULL, &status) && !status;
}
FX_EXPORT OSStatus AudioObjectGetPropertyDataSize(AudioObjectID object, const FXPropertyAddress *a, uint32_t qsize,
const void *q, uint32_t *size) {
OSStatus status;
uint32_t n = fx_property(object, a, NULL, &status);
if (!status && size) *size = n;
return status;
}
FX_EXPORT OSStatus AudioObjectGetPropertyData(AudioObjectID object, const FXPropertyAddress *a, uint32_t qsize,
const void *q, uint32_t *size, void *data) {
OSStatus status;
uint32_t n = fx_property(object, a, NULL, &status);
if (status) return status;
if (!size || *size < n) return FOURCC('!', 's', 'i', 'z');
fx_property(object, a, data, &status);
*size = n;
return status;
}
FX_EXPORT OSStatus AudioObjectAddPropertyListener(AudioObjectID o, const FXPropertyAddress *a, FXPropertyListener l, void *c) { return 0; }
FX_EXPORT OSStatus AudioObjectRemovePropertyListener(AudioObjectID o, const FXPropertyAddress *a, FXPropertyListener l, void *c) { return 0; }
#endif
#ifdef FX_LIB_AUDIOTOOLBOX
FX_EXPORT OSStatus AudioQueueSetProperty(AudioQueueRef queue, AudioQueuePropertyID id, const void *data, UInt32 size) {
static OSStatus (*real)(AudioQueueRef, AudioQueuePropertyID, const void *, UInt32);
if (!real) {
void *toolbox = dlopen("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox", RTLD_LAZY | RTLD_NOLOAD);
real = toolbox ? dlsym(toolbox, "AudioQueueSetProperty") : NULL;
}
if (id == FOURCC('a', 'q', 'c', 'd')) return 0; // kAudioQueueProperty_CurrentDevice: iOS routes itself
return real ? real(queue, id, data, size) : -1;
}
#endif