Fix CFPreferences bundle ID matching, memmove NULL handling, and pthread inheritsched - #1
Open
j92580498-max wants to merge 102 commits into
Open
Fix CFPreferences bundle ID matching, memmove NULL handling, and pthread inheritsched#1j92580498-max wants to merge 102 commits into
j92580498-max wants to merge 102 commits into
Conversation
Includes setup notes for both the Rust emulator (touchHLE) and the AppDB (Python/FastAPI), covering build dependencies, RUSTFLAGS requirements, and service startup commands. Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com>
* gles1_on_gl2: convert types in glGet*v queries instead of asserting The GLES 1.1 spec (section 6.1.2 "Data Conversions") requires glGetIntegerv/glGetFloatv/glGetBooleanv to perform type conversion when the queried state's native type does not match the requested type. Our gles1_on_gl2 layer previously asserted that the type matched (with one POINT_SIZE_MAX exception), which made any guest app that queries e.g. a float-typed state via glGetIntegerv crash the whole emulator. "Pyramid Mummy" (com.magicbone.pyramidrun) triggers exactly this during startup: it calls glGetIntegerv on a non-int pname and we panic at src/gles/gles1_on_gl2.rs:845. Fix this by mirroring the fan-out used by GetFixedv: query the underlying state at its native type and convert each component per the spec rules. - GetIntegerv: Boolean -> 0/1, Float -> round-to-nearest clamped to GLint, FloatSpecial (normalized colors etc.) -> scaled to the GLint range. - GetBooleanv: any non-zero int/float maps to GL_TRUE. - GetFloatv: integer state widens to float; boolean maps to 0.0/1.0. Unknown ParamType variants fall through to the host driver rather than aborting, matching the rest of the layer's defensive style. Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com> * objc: initialise phantom host-object buffers via T::default() instead of raw zeroes The objc "phantom host object" fallback in src/objc/objects.rs is used by ObjC::borrow / ObjC::borrow_mut whenever a guest object has no real host-side record (or the record can't be downcast to the requested type). It used to hand out a *zeroed* buffer of size_of::<T>() and transmute it to &T / &mut T. That was unsound for any T whose zero bit-pattern is not a valid instance — most notably types containing a hashbrown HashMap, whose internal `ctrl` pointer must point at the `Group::static_empty()` sentinel rather than null. "Pyramid Mummy" hit this: after my preceding fix uncrashed the gles1_on_gl2 GetIntegerv assert, the Unity runtime ended up calling -[NSDictionary initWithDictionary:] with an object that isn't a real dictionary, the phantom fallback returned a zeroed DictionaryHostObject, and `HashMap::values()` SIGSEGV'd in `hashbrown::raw::RawIterRange::new` via an SSE2 _mm_load_si128 from a null `ctrl` pointer. Fix the phantom path by requiring `T: Default` for borrow / borrow_mut and writing a real `T::default()` value into the leaked buffer the first time it's allocated. This guarantees the returned reference always points at a valid instance of T regardless of which fields T contains. To satisfy the new bound this commit adds `Default` implementations (via `#[derive(Default)]` where possible, otherwise a small manual impl) to all host object types reachable through borrow/borrow_mut, and to a handful of helper types they embed (CGAffineTransform, CATransform3D, CFUUIDBytes, UuidBytes, GuestPathBuf, GuestFunction, SEL, CMAcceleration, CMRotationRate, FontKind, PredicateKind, InputStreamBacking, OutputStreamBacking, CGContextSubclass, CGBitmapContextData, Image, font::Font). A few enums get a hand-written impl choosing a sensible "empty" variant; a few struct fields whose underlying types come from external crates and don't implement Default (rusttype::Font, nibarchive::NIBArchive, std::time::Instant in CMMotionManagerHostObject) are wrapped in `Option<...>` with the real allocation paths populating them via `Some(...)`. As a defence-in-depth measure, init_with_dictionary_common now also short-circuits on a nil source dictionary instead of relying on the phantom path producing a valid empty HashMap; this matches Apple's documented behaviour for -[NSDictionary initWithDictionary:nil]. Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com>
Replace the manual git clone steps (which hardcoded the j92580498-max/touchHLE remote and failed when the workflow ran from a fork or branch that did not exist on that remote, e.g. 'fatal: couldn't find remote ref refs/heads/cursor/...') with actions/checkout, so the repository and ref that triggered the workflow are always checked out correctly. Submodules are now fetched via the checkout action's submodules: recursive option, removing the need for a separate 'git submodule update --init' step. Per request, all action references now point at their default branch (@main) instead of pinned major-version tags. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com>
The release CI build was failing on all targets (Android, Windows, macOS) with E0277 errors because borrow/borrow_mut require T: Default, but UIStoryboardHostObject did not derive Default. All of its fields (id, String, HashMap) already implement Default, so a derive is sufficient. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com>
The quick options menu already had a close button at the top-right
corner with a '×' glyph, but it was invisible:
- 'buttonWithType:UIButtonTypeRoundedRect' does not actually apply
the rounded-rect appearance, so the button kept the default
white title color on a clear background, which is invisible
against the white menu.
- The 20x20 frame combined with a 30pt font also made the glyph
too large for the bounds.
Enlarge the button to 36x36, drop the font size to 28pt, and
explicitly give the button a gray background, white title color
and rounded corners so the close button is clearly visible and
tappable.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com>
objc::borrow and borrow_mut require HostObject types to implement Default for phantom-fallback when the runtime is asked to access a missing or wrong-typed object. NSPipeHostObject and NSCacheHostObject were added without these impls, causing 29 E0277 compile errors. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com>
* ci: publish Hypertel v1.0.x release every 5 commits Add scripts to detect when five commits have landed since the last v1.0.* tag (or since Hypertel release automation was introduced) and extend the build workflow to upload macOS, Windows, and Android artifacts as a GitHub release named Hypertel. Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com> * ci: require all builds for Hypertel release and add commit changelog Only publish when macOS, Windows, and Android jobs succeed. Verify artifacts exist before packaging. Include linked commit list in the release notes. Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com>
* ci: publish Hypertel v1.0.x release every 5 commits Add scripts to detect when five commits have landed since the last v1.0.* tag (or since Hypertel release automation was introduced) and extend the build workflow to upload macOS, Windows, and Android artifacts as a GitHub release named Hypertel. Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com> * ci: require all builds for Hypertel release and add commit changelog Only publish when macOS, Windows, and Android jobs succeed. Verify artifacts exist before packaging. Include linked commit list in the release notes. Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com> * ci: allow manual Hypertel release via workflow_dispatch checkbox Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com> * fix: resolve Windows artifact path after CI download upload-artifact flattens touchHLE_windows_bundle/ so touchHLE.exe is at artifacts/windows/touchHLE.exe, not under touchHLE_windows_bundle/. Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com> * fix: manual Hypertel release changelog uses latest 5 commits Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com> * ci: publish Hypertel releases with softprops/action-gh-release@master Package zips and release notes in a script; create the GitHub release and upload assets via softprops/action-gh-release@master. Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com> * ci: include version in Hypertel release title Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com>
… ABI (#10) The objc borrow() fallback for missing objects requires T: Default. CFRunLoopSourceHostObject was missing it, causing 13 E0277 errors when building the library (including the Android Gradle cargo-ndk step). When cross-compiling dynarmic for Android, set CMAKE_ANDROID_ARCH_ABI to arm64-v8a so CMake detects arm64 instead of 32-bit arm, which otherwise breaks 64-bit floating-point bit helpers in vendored dynarmic. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com>
* Fix crashes and implement missing runtime APIs found in logs - bundle: don't panic on non-string MinimumOSVersion (number/real); return Option<String> - font: make Font::from_vec fallible; CGFontCreateWithDataProvider returns NULL on bad data (per Apple docs) - NSPropertyListSerialization: implement +propertyListWithData:options:format:error: (iOS 4.0+) with real NSError - objc runtime: implement objc_alloc and objc_autorelease (libobjc ARC fast paths) and export them * objc: implement missing objc_retainBlock (fixes pre-existing trunk build break) objc_retainBlock was imported and exported in src/objc.rs but never defined, so the crate failed to compile (E0432). Per Apple's objc4 runtime, objc_retainBlock(x) wraps _Block_copy(x); mirror touchHLE's _Block_copy behaviour (returns the same pointer). * Implement more documented runtime stubs from logs (real, per Apple docs) - GCD: real dispatch_queue_set_specific / dispatch_queue_get_specific / dispatch_get_specific backed by a (queue,key)->value store + current-queue tracking (was: call to unimplemented _dispatch_queue_set_specific) - objc: objc_unsafeClaimAutoreleasedReturnValue ARC fast path (was: return-0 stub) - UIViewController: real extendedLayoutIncludesOpaqueBars property + setter (was: 'does not respond to selector setExtendedLayoutIncludesOpaqueBars:') * Implement remaining documented runtime stubs (real, per Apple docs) - Foundation: real NSIndexSet class (indexSetWithIndexesInRange:, indexSetWithIndex:, count, containsIndex:, firstIndex/lastIndex, indexGreater/LessThanIndex:, equality, copy) — was 'Class NSIndexSet is unimplemented' - CoreText: CTFontManagerRegisterGraphicsFont(font, error) — was return-0 stub - CoreFoundation: CFErrorCopyDescription forwards to NSError -localizedDescription (toll-free bridged) — was return-0 stub * Fix two guest-triggered hard crashes (GameStop, Reckless Getaway) - objc/classes.rs: a class and its metaclass always share the same name in Apple's runtime (class_getName). The previous code only harmonized the two names when one was a synthetic placeholder for an unreadable name, and asserted otherwise — which aborted the whole VM for GameStop_iOS, whose class/metaclass names were both readable yet mismatched. Make harmonization unconditional (class side canonical) and drop the assert. - foundation/ns_string.rs: NSMutableString -setString:nil and -appendFormat:nil asserted on nil and crashed the emulator (e.g. Reckless Getaway hit 'assertion left != right'). Apple raises NSInvalidArgumentException but never aborts the process; log a warning and no-op instead, matching the lenient handling already used for the other NSMutableString implementation. * Foundation: add missing framework constants and real NSSet member:/setWithObjects:count: - Register ~55 previously-unhandled host constants in foundation STUB_CONSTANTS with documented Apple values: UIKeyInput* arrows/Escape, kCAAlignment*, AVAudioTimePitchAlgorithm*, kCMFormatDescriptionExtension_Depth, CVPixelBuffer pool keys, VTDecompression keys, kCTFontPostScriptNameKey, kCGPDFContext*, GKSessionErrorDomain, PKPaymentNetwork*, MKLaunchOptions*, MKMapRectNull, UIActivityTypePostToTencentWeibo, NSExtensionHost*/UIScene* notifications, NSMetadata/NSURL ubiquitous-item keys, MPNowPlayingInfoProperty*, AVMetadata*, kCGColorSpaceExtended(Linear)SRGB, AVCaptureExposureDurationCurrent (CMTime invalid), AVCaptureISOCurrent (-1.0f), and __NSArray0__/__NSDictionary0__ empty-collection singletons backed by real retained objects. - NSSet/NSMutableSet: implement -member: (real isEqual: lookup per Apple docs), +setWithObjects:count: and -initWithObjects:count: (C-array initialisers), mirroring the existing NSArray implementations. * dyld constants: fix leading-underscore symbol keys + add CAEmitterLayer/UITableViewIndexSearch/AVEncoder constants Constant export keys are matched against raw Mach-O symbol names, which carry a leading underscore (e.g. _matrix_identity_float4x4). Many entries in foundation/avfoundation STUB_CONSTANTS were written without it and so never resolved (logs still showed them as unhandled). Prefix all such keys with '_', fix ___NSArray0__/___NSDictionary0__ (triple underscore), and add CAEmitterLayer shape/render-mode keys, UITableViewIndexSearch and AVEncoderBitRatePerChannelKey. --------- Co-authored-by: j92580498-max <j92580498@gmail.com> Co-authored-by: busik007 <busik0877@gmail.com> Co-authored-by: busik007 <busik0877@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com>
The script declared #!/bin/sh but used bash-only [[ ]] tests, which fail on Debian/Ubuntu CI where /bin/sh is dash. Convert conditionals to POSIX [ ] syntax so the Hypertel release packaging step runs correctly. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com>
The publish-hypertel-release job already packages artifacts and creates the GitHub release via softprops/action-gh-release. The follow-up step that called publish-hypertel-release.sh failed on the flattened Windows artifact path and would have duplicated the release upload. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com>
Landscape games like Call of Duty: Zombies call setStatusBarOrientation: after launch and rotate their own OpenGL drawing. touchHLE was also rotating frames in present_renderbuffer, which produced a 90° sideways image in a landscape window. Only apply present-time rotation on iPhone when UIKit has already applied the autorotation affine transform to the EAGL view (landscape launch path, e.g. Resident Evil Degeneration with --landscape-left). Skip it when the view transform is still identity so self-rotating games display correctly. Also honor Info.plist UIInterfaceOrientation for initial window setup when portrait is supported, and add --landscape-left to COD Zombies defaults so the window matches the game from the first frame. Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: goodvids56 <goodvids56@users.noreply.github.com>
This reverts commit 9747980.
Port changes from touchHLE/touchHLE trunk commits: - 4a950ccf: Add AIFF audio type hint handling in AudioFileOpenURL - ea1d0347: Account for nil first object in NSArray arrayWithObjects: Skipped 98de7d8a (NSTimeZone -abbreviation stub) because Hypertle already implements abbreviation lookup via ZONE_TABLE. Co-authored-by: Cursor Agent <cursoragent@cursor.com>
…ng (#24) * Recover the app delegate when UIApplicationMain gets no usable delegate class Some apps (e.g. "The Steak" / Charlie the Steak) reach UIApplicationMain without a usable delegate class name: the argument is nil, or it was built from a class reference that resolved to nil (NSStringFromClass returns nil, which decodes to the empty string). With no main-nib connection setting the delegate either, the application was left with a nil delegate, so application:didFinishLaunchingWithOptions: was never delivered and the app stayed frozen on its launch image. Add a fallback: when the normal delegate resolution yields nothing, discover the app's delegate class by scanning the loaded classes for one that declares a UIApplicationDelegate launch method, and instantiate that. This only runs when the app would otherwise have no delegate at all, so it cannot affect apps that launch correctly today. - objc: add ObjC::class_declaring_instance_method, which finds a registered class declaring a given instance method, skipping substituted/placeholder classes and metaclasses. Selection is deterministic and prefers app-delegate-like names so an unrelated class cannot shadow the real one. - UIApplicationMain: resolve the delegate from the explicit class name when usable (now via try_get_known_class, so an unknown name falls through to discovery instead of installing a broken placeholder), otherwise fall back to the discovered delegate class; log clearly when none can be determined. Also drop the leftover DIAG_CALLER debug prints. https://claude.ai/code/session_01FQFBWFcV8FV2YgLJnE73gJ * Fix headless-mode panic when an app queries preferred languages/region `get_preferred_language_codes` / `get_preferred_country_codes` route through `Environment::on_parent_stack_in_coroutine`, which unwraps `self.window`. That window is absent in headless mode, so an app that calls `[NSLocale preferredLanguages]` (a very common thing to do during launch) panicked with an `Option::unwrap()` on `None`, even though the closures here do not actually use the window. Short-circuit both helpers when there is no window and report no preference, which the caller already handles by falling back to English. The windowed path is unchanged. * NSCalendar: implement +autoupdatingCurrentCalendar Apps commonly call `[NSCalendar autoupdatingCurrentCalendar]` during launch (e.g. for date math). It was unimplemented, so the runtime returned nil and the app then did calendar/date work against a nil object. Apple's autoupdating calendar only differs from `currentCalendar` in that it tracks later changes to the user's settings, which touchHLE has none of, so forward to `currentCalendar`. * Don't panic in the main run loop when running headless Once an app fully finishes launching it enters the main NSRunLoop, which unconditionally calls `uikit::handle_events` and `core_animation::recomposite_if_necessary`. Both reach for the window, which is absent in headless mode, so they panicked with "Tried to do something that needs a window..." (the stale comment in `handle_events` even claimed it was never called headless). Guard both: with no window there are no input events to drain and nothing to composite, so return immediately. This makes headless mode able to run an app past launch (useful for automated testing); windowed behaviour is unchanged. * UINavigationBar: make it a real UIView subclass `UINavigationBar` is declared `: UIView`, but `UINavigationBarHostObject` was a plain `HostObject` with no embedded `UIViewHostObject`. As a result every `UIView` message sent to a nav bar (frame, subviews, layout, hidden, ...) failed to borrow the view host object and silently fell back to a zeroed phantom, so the nav bar had no real view state. Observed with "The Steak", whose launch path touches a nav bar during third-party SDK setup. Embed the superclass host object via `impl_HostObject_with_superclass!`, initialize it in `+allocWithZone:`, and chain `-init`/`-initWithFrame:`/ `-initWithCoder:` to `super` so the backing layer and view state are set up like any other UIView subclass. Also make the wrong-type `borrow` fallback report the object's actual host type, which is what made this mismatch diagnosable in the first place. --------- Co-authored-by: Claude <noreply@anthropic.com>
touchHLE renders UIAlertView as a *blocking* SDL2 system dialog, whereas real iOS -[UIAlertView show] is asynchronous and returns immediately. Outfit7 titles such as Talking Angela create a UIAlertView with an empty (or nil) title *and* message as a transient placeholder and dismiss it programmatically once background work finishes. Presenting a blocking modal for such an alert froze the app behind an empty dialog box that the user could never meaningfully act on. When both title and message are empty, skip the native dialog and dismiss the alert asynchronously (delivering any delegate callbacks) so the guest run loop keeps going. Content-bearing alerts still show the SDL2 dialog as before. Verified by running the Talking Angela IPA: previously the app dead-ended at the empty UIAlertView; it now skips both empty alerts and proceeds to load the ChatScript engine and run the game loop. https://claude.ai/code/session_01TpoV3dfiQLfQwPwbxnvDX2 Co-authored-by: Claude <noreply@anthropic.com>
Text containing Arabic characters previously rendered as tofu boxes because none of the bundled Latin or Noto Sans CJK fonts contain Arabic glyphs. This adds Noto Sans Arabic (regular + bold, SIL OFL) as a fallback and extends UIFont's script detection to route Arabic text to it, mirroring the existing CJK fallback. - Bundle NotoSansArabic-Regular.ttf and NotoSansArabic-Bold.ttf - Add Font::sans_regular_ar() / sans_bold_ar() loaders - Refactor get_font() with is_cjk_char()/is_arabic_char() helpers covering Arabic, Arabic Supplement, Arabic Extended-A and Arabic Presentation Forms-A/B blocks - Document the new fonts in the fonts README and license notice Co-authored-by: Claude <noreply@anthropic.com>
Text containing Arabic was laid out by rusttype in logical order, left-to-right, with no contextual shaping. That made Arabic words appear reversed and made each letter render in its isolated, disconnected form. Add a self-contained shaping/reordering pass that runs on each line just before it is handed to rusttype: - Contextual joining: map Arabic letters to their Unicode presentation forms (isolated/initial/medial/final), including mandatory lam-alef ligatures. The bundled Noto Sans Arabic font maps these forms in its cmap, so no GSUB support is required. - Bidirectional reordering: a simplified UAX #9 pass reverses RTL runs into visual order while keeping Latin/number runs and combining marks correctly placed. The transform is applied at the two rusttype layout call sites in font.rs (width measurement and drawing) so measured and drawn widths stay consistent. Non-Arabic lines are returned untouched and borrowed, so existing behaviour is unchanged. No new dependencies are added. Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
…ules (#29) ns_log and ns_objc_runtime only export C functions (NSLog/NSLogv and the NSStringFrom*/NSClassFromString family) and define no Objective-C classes, so they have no CLASSES const. The CLASSES aggregation list erroneously referenced ns_log::CLASSES and ns_objc_runtime::CLASSES, causing two E0425 'cannot find value CLASSES' errors. Their FUNCTIONS are already correctly registered in the FUNCTIONS list. Co-authored-by: Claude <noreply@anthropic.com>
…c I/O (#158) * Implement POSIX aio family to fix apps that stream resources via async I/O aio_read/aio_error/aio_return (plus aio_write/suspend/cancel/fsync) were previously missing, so dyld installed return-0 stubs. Apps that load their resource files through POSIX async I/O (e.g. Plants vs. Zombies HD) therefore saw every request report success with zero bytes transferred, leaving the destination buffers empty. That produced a black screen followed by an early crash (an empty buffer led to a bogus malloc((size_t)-1)). These are now implemented synchronously, reusing the regular positional pread/pwrite paths and stashing each result keyed by the guest struct aiocb pointer so aio_error/aio_return report the real completion status. The aiocb prefix is modelled as #[repr(C, packed)] to match the iOS armv7 ABI, where off_t is only 4-byte aligned (fildes 0, offset 4, buf 12, nbytes 16). Verified by running the PvZ HD IPA under HyperHLE: the app no longer crashes, aio now reads real data (incl. the ~1.4 MB resource pack), all 204 'Failed to load particle' errors are gone, and the render loop stays alive past frame 60. * aio: warn when an app requests unsupported completion notification The synchronous aio implementation handles SIGEV_NONE (poll via aio_error/aio_return) perfectly, but does not yet deliver SIGEV_SIGNAL or SIGEV_THREAD completion callbacks. Read the aiocb's sigev_notify and log a warning in that case so a future app relying on async notification produces a diagnosable message instead of silently blocking. (PvZ HD uses SIGEV_NONE, so this is purely defensive.) --------- Co-authored-by: Claude <noreply@anthropic.com>
* Add workflow to download and run an IPA via touchHLE * Run downloaded IPA on Linux, Windows and macOS --------- Co-authored-by: Claude <noreply@anthropic.com>
* Add CI workflow for testing iOS apps under touchHLE (#30) * Add workflow to download and run an IPA via touchHLE * Run downloaded IPA on Linux, Windows and macOS --------- Co-authored-by: Claude <noreply@anthropic.com> * CI: provide software OpenGL on Windows so apps can launch (#31) The 'Download and run IPA' workflow aborted on the Windows runner with 'Couldn't create OpenGL ES 1.1 context!'. Windows GitHub Actions runners only expose Microsoft's GDI generic OpenGL 1.1, which cannot satisfy the OpenGL 2.1 context that touchHLE's GLES1-on-GL2 fallback requires, so both the native GLES path and the GL2 fallback fail and the emulator panics before the app ever runs. Drop Mesa3D's llvmpipe software renderer (a per-app opengl32.dll plus its dependencies) next to touchHLE.exe and force GALLIUM_DRIVER=llvmpipe so a complete OpenGL implementation is available without a GPU. Linux already gets llvmpipe via libgl1-mesa-dri and macOS ships a usable system OpenGL, so this is only needed on Windows. Also restore diagnostics in libc exit()/abort(): they previously exited silently, so run logs ended abruptly with no explanation. They now log the exit/abort (with a guest stack trace on abort) before quitting. Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
Updated decoding logic to align with Symphonia 0.6 changes, including adjustments to probe, track access, codec parameters, and decoder creation.
Updated alGetBufferi function parameter from *const ALint to *mut ALint to match OpenAL specification.
Updated GetBufferi function to accept a mutable pointer for the value parameter, aligning with OpenAL API requirements.
…etBufferi pointer (#161) * Add CI workflow for testing iOS apps under touchHLE (#30) * Add workflow to download and run an IPA via touchHLE * Run downloaded IPA on Linux, Windows and macOS --------- Co-authored-by: Claude <noreply@anthropic.com> * CI: provide software OpenGL on Windows so apps can launch (#31) The 'Download and run IPA' workflow aborted on the Windows runner with 'Couldn't create OpenGL ES 1.1 context!'. Windows GitHub Actions runners only expose Microsoft's GDI generic OpenGL 1.1, which cannot satisfy the OpenGL 2.1 context that touchHLE's GLES1-on-GL2 fallback requires, so both the native GLES path and the GL2 fallback fail and the emulator panics before the app ever runs. Drop Mesa3D's llvmpipe software renderer (a per-app opengl32.dll plus its dependencies) next to touchHLE.exe and force GALLIUM_DRIVER=llvmpipe so a complete OpenGL implementation is available without a GPU. Linux already gets llvmpipe via libgl1-mesa-dri and macOS ships a usable system OpenGL, so this is only needed on Windows. Also restore diagnostics in libc exit()/abort(): they previously exited silently, so run logs ended abruptly with no explanation. They now log the exit/abort (with a guest stack trace on abort) before quitting. Co-authored-by: Claude <noreply@anthropic.com> * Fix build: port symphonia_formats to Symphonia 0.6 API and alGetBufferi pointer (#32) The touchHLE lib failed to compile against symphonia 0.6.0, which reorganized its API relative to the 0.5-era code the module was written for: - probe() now returns the boxed FormatReader directly (no ProbeResult .format field) - Hint moved to symphonia::core::formats::probe - Track::codec_params is Option<CodecParameters> (an enum with an Audio variant carrying AudioCodecParameters) - decoders are created via CodecRegistry::make_audio_decoder with AudioDecoderOptions - next_packet() returns Result<Option<Packet>> (Ok(None) = end of stream) - decoded audio is a GenericAudioBufferRef exposing copy_bytes_to_vec_interleaved_as::<i16>() - SignalSpec/SampleBuffer are gone; AudioSpec (rate()/channels()) is used Also fix alGetBufferi to obtain a mutable pointer via ptr_at_mut, as GetBufferi writes the queried value back (matching alGetSourcei). Co-authored-by: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
…(#162) * Add CI workflow for testing iOS apps under touchHLE (#30) * Add workflow to download and run an IPA via touchHLE * Run downloaded IPA on Linux, Windows and macOS --------- Co-authored-by: Claude <noreply@anthropic.com> * CI: provide software OpenGL on Windows so apps can launch (#31) The 'Download and run IPA' workflow aborted on the Windows runner with 'Couldn't create OpenGL ES 1.1 context!'. Windows GitHub Actions runners only expose Microsoft's GDI generic OpenGL 1.1, which cannot satisfy the OpenGL 2.1 context that touchHLE's GLES1-on-GL2 fallback requires, so both the native GLES path and the GL2 fallback fail and the emulator panics before the app ever runs. Drop Mesa3D's llvmpipe software renderer (a per-app opengl32.dll plus its dependencies) next to touchHLE.exe and force GALLIUM_DRIVER=llvmpipe so a complete OpenGL implementation is available without a GPU. Linux already gets llvmpipe via libgl1-mesa-dri and macOS ships a usable system OpenGL, so this is only needed on Windows. Also restore diagnostics in libc exit()/abort(): they previously exited silently, so run logs ended abruptly with no explanation. They now log the exit/abort (with a guest stack trace on abort) before quitting. Co-authored-by: Claude <noreply@anthropic.com> * Fix build: port symphonia_formats to Symphonia 0.6 API and alGetBufferi pointer (#32) The touchHLE lib failed to compile against symphonia 0.6.0, which reorganized its API relative to the 0.5-era code the module was written for: - probe() now returns the boxed FormatReader directly (no ProbeResult .format field) - Hint moved to symphonia::core::formats::probe - Track::codec_params is Option<CodecParameters> (an enum with an Audio variant carrying AudioCodecParameters) - decoders are created via CodecRegistry::make_audio_decoder with AudioDecoderOptions - next_packet() returns Result<Option<Packet>> (Ok(None) = end of stream) - decoded audio is a GenericAudioBufferRef exposing copy_bytes_to_vec_interleaved_as::<i16>() - SignalSpec/SampleBuffer are gone; AudioSpec (rate()/channels()) is used Also fix alGetBufferi to obtain a mutable pointer via ptr_at_mut, as GetBufferi writes the queried value back (matching alGetSourcei). Co-authored-by: Claude <noreply@anthropic.com> * Fix NSKeyedArchiver infinite recursion crash + collection round-trip Fixes the Talking Angela crash (App called exit with a garbage pointer argument after the objc_msgSend depth guard tripped and corrupted string objects). Root cause and related issues: - ns_keyed_archiver: NSString's encodeWithCoder: allocated a fresh NSString for its contents and re-encoded that via encodeObject:forKey:. Since the new object is itself an NSString, encode_object kept calling encodeWithCoder: on each freshly-created string, recursing infinitely. This blew the objc_msgSend recursion guard (or the native stack), corrupted string objects, and eventually crashed the guest. NSString is now stored inline as a plist string in $objects, matching what the decoder already expects. - ns_array / ns_dictionary: the archiver wrote collections in a bespoke layout (NS.objects.0, NS.objects.1, NS.count for arrays; a single object reference for dictionaries) that the decoder — which follows Apple's inline UID-array format — could not read, so touchHLE-produced archives round-tripped to empty collections. Both now emit NS.objects / NS.keys as inline arrays of UID references via the new encode_objects_as_uid_array helper. - ns_array: add -[NSArray isEqual:], which delegates to isEqualToArray: for NSArray operands. Without it the NSObject pointer-identity default made two distinct-but-equal arrays (e.g. an array and its unarchived copy) compare unequal, breaking dictionary/array equality after a round-trip. - foundation: remove duplicate constant exports (e.g. _AVPlayerItemTimeJumpedNotification, _PHImageErrorKey, several SK*/kSec*), which made the no_duplicate_constants unit test fail and would emit duplicate C declarations in generated link stubs. - dyld: dump_host_symbols now emits a sanitized C identifier with an asm() label for symbols whose names aren't valid C identifiers (e.g. OBJC_IVAR_$_NSObject.isa), so the integration-test link stubs compile. https://claude.ai/code/session_016jmFdZ859mfXeJZ1eubdEH --------- Co-authored-by: Claude <noreply@anthropic.com>
…mount) (#163) * Add CI workflow for testing iOS apps under touchHLE (#30) * Add workflow to download and run an IPA via touchHLE * Run downloaded IPA on Linux, Windows and macOS --------- Co-authored-by: Claude <noreply@anthropic.com> * CI: provide software OpenGL on Windows so apps can launch (#31) The 'Download and run IPA' workflow aborted on the Windows runner with 'Couldn't create OpenGL ES 1.1 context!'. Windows GitHub Actions runners only expose Microsoft's GDI generic OpenGL 1.1, which cannot satisfy the OpenGL 2.1 context that touchHLE's GLES1-on-GL2 fallback requires, so both the native GLES path and the GL2 fallback fail and the emulator panics before the app ever runs. Drop Mesa3D's llvmpipe software renderer (a per-app opengl32.dll plus its dependencies) next to touchHLE.exe and force GALLIUM_DRIVER=llvmpipe so a complete OpenGL implementation is available without a GPU. Linux already gets llvmpipe via libgl1-mesa-dri and macOS ships a usable system OpenGL, so this is only needed on Windows. Also restore diagnostics in libc exit()/abort(): they previously exited silently, so run logs ended abruptly with no explanation. They now log the exit/abort (with a guest stack trace on abort) before quitting. Co-authored-by: Claude <noreply@anthropic.com> * Fix build: port symphonia_formats to Symphonia 0.6 API and alGetBufferi pointer (#32) The touchHLE lib failed to compile against symphonia 0.6.0, which reorganized its API relative to the 0.5-era code the module was written for: - probe() now returns the boxed FormatReader directly (no ProbeResult .format field) - Hint moved to symphonia::core::formats::probe - Track::codec_params is Option<CodecParameters> (an enum with an Audio variant carrying AudioCodecParameters) - decoders are created via CodecRegistry::make_audio_decoder with AudioDecoderOptions - next_packet() returns Result<Option<Packet>> (Ok(None) = end of stream) - decoded audio is a GenericAudioBufferRef exposing copy_bytes_to_vec_interleaved_as::<i16>() - SignalSpec/SampleBuffer are gone; AudioSpec (rate()/channels()) is used Also fix alGetBufferi to obtain a mutable pointer via ptr_at_mut, as GetBufferi writes the queried value back (matching alGetSourcei). Co-authored-by: Claude <noreply@anthropic.com> * Fix infinite loop constructing std::string from NULL char* (Turbo Dismount) Some apps (e.g. Turbo Dismount) hang at startup, spinning forever while mem::memmove logs "memmove with likely-negative size (0xffffffff = -1); src=0x0" over and over. Root cause: the app builds std::strings in a loop from a table that contains a NULL `const char*` entry under touchHLE. libstdc++ constructs `std::string(const char*)` via `_S_construct(NULL, NULL + npos)`, detects the NULL pointer, and calls `std::__throw_logic_error`. touchHLE already neuters the `__throw_*` helpers to a bare `BX LR` so guest C++ "soft" failures don't abort the host. But for this path the neutered throw simply falls through into the normal copy code with `beg == NULL` and `end - beg == npos`, producing `memcpy(dest, NULL, npos)` and a string whose length is npos. The oversized copy is skipped by Mem::memmove, but the resulting corrupt string leaves the app's construction loop unable to make progress — an effectively infinite hang. The documented intent of the throw-neutering is for std::string(NULL) to behave like an empty string. Make that actually happen: retarget the NULL-pointer branch inside libstdc++'s char `_S_construct` so it jumps to the function's existing empty-string path (which returns `_S_empty_rep()._M_refdata()`) instead of the throw / oversized-copy path. The patch locates the branch by instruction pattern and verifies the expected encodings, skipping with a warning if the bundled libstdc++ ever changes, so it can never silently mis-patch. Verified with com.secretexit.trdismount 1.2.7: the "likely-negative size" spam (previously 400k+ occurrences in seconds, pegging a CPU core) drops to zero and the app proceeds past startup. https://claude.ai/code/session_0112hTEYgwBzuThq3tqwhno1 --------- Co-authored-by: Claude <noreply@anthropic.com>
…ead inheritsched This commit fixes several issues identified from touchHLE logs running PopCap games (PvZ iPad/iPhone): 1. CFPreferences: Added reverse-DNS prefix matching for shortened bundle IDs - Games use 'com.popcap.pvz' and 'com.popcap.pvzipad' instead of full bundle IDs - Per Apple docs, applicationID should be in Java package name form 2. memmove: Zero-fill destination when src is NULL instead of skipping - Real iOS behavior for corrupted std::string internals - Fixes warnings in PvZ logs 3. pthread: Implemented pthread_attr_setinheritsched and pthread_attr_getinheritsched - Added inheritsched field to pthread_attr_t struct - Per Apple pthread(3) manpage with proper EINVAL validation 4. CFURL: Implemented CFURLCreateStringByReplacingPercentEscapes - Using NSString stringByRemovingPercentEncoding instead of stub References: - Apple CFPreferencesCopyAppValue docs - Apple pthread(3) manpage - Apple CFURL docs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR fixes several issues identified from touchHLE logs running PopCap games (PvZ iPad/iPhone):
Fixes
1. CFPreferences: Reverse-DNS bundle ID matching
Problem: Games use shortened bundle IDs like
com.popcap.pvzandcom.popcap.pvzipadinstead of the full bundle ID (com.popcap.ios.chs.PvZGreatWall,com.popcap.ios.chs.PvZiPad).Solution: Added reverse-DNS prefix matching so that
com.popcap.pvz*is accepted as the current app. Per Apple docs,applicationIDshould be in Java package name form.2. memmove: NULL source handling
Problem:
memmove(dest, NULL, size)was being skipped with a warning, but real iOS behavior for this edge case (triggered by corrupted std::string internals) should zero-fill the destination.Solution: When
src=0anddest != 0, perform zero-fill of destination instead of skipping entirely.3. pthread: attr_setinheritsched / attr_getinheritsched
Problem:
pthread_attr_setinheritschedwas a TODO stub, andpthread_attr_getinheritschedwas not exported.Solution: Implemented both functions per Apple pthread(3) manpage. Added
inheritschedfield topthread_attr_tstruct, set/get with proper EINVAL validation.4. CFURLCreateStringByReplacingPercentEscapes
Problem: Was stubbed with TODO, causing potential crashes.
Solution: Implemented proper percent-decoding using
NSString stringByRemovingPercentEncoding.Testing
com.popcap.pvzipad) and PvZ iPhone (com.popcap.pvz) logsReferences