Skip to content

Native ARM64 macOS port using wxWidgets#166

Open
wbreiler wants to merge 21 commits into
erich666:masterfrom
wbreiler:master
Open

Native ARM64 macOS port using wxWidgets#166
wbreiler wants to merge 21 commits into
erich666:masterfrom
wbreiler:master

Conversation

@wbreiler

@wbreiler wbreiler commented Jul 3, 2026

Copy link
Copy Markdown

Summary

Adds a native ARM64 macOS build of Mineways using wxWidgets (Cocoa backend), living alongside the existing Windows/MFC build under a new Mac/ directory. The Windows build and all shared core code (blockInfo, nbt, ObjFileManip, MinewaysMap, etc.) are untouched except for a couple of small cross-platform fixes described below — this is purely additive.

  • New Mac/ directory: MinewaysApp, MinewaysFrame, MapPanel, LocationDialog, ExportDialog, MacCullingSchemes, ImportSettings, plus Makefile/Info.plist/app icon for make / make app.
  • Feature parity with the Windows UI: world loading (including the scanned Open World list and Test Block World), map viewing/selection/zoom, the full Export dialog (all ExportFileData fields across 5 tabs), Culling Schemes (list + editor, matches or exceeds Windows with Hide All/Show All), the View menu (all render-mode toggles, Nether/End, Jump to Spawn/Player/Model), Export Map (PNG), Repeat Export, Reload World, and wxConfig-based persistence for settings/paths/window state.
  • Import Settings (Ctrl+I), full scope: both header re-import (parsing the #-comment block writeStatistics writes into every exported file back into ExportFileData) and the batch-scripting command language, ported from Win/Mineways.cpp's importSettings/importModelFile/readAndExecuteScript/interpretImportLine/interpretScriptLine. Preserves the atomic header-commit and two-pass script-execution semantics. Not ported (documented in Mac/ImportSettings.h, reported as a clear error rather than silently ignored): Sketchfab publish commands, Windows mouse-button remapping, "Custom printer" materials, block-name Translate: aliasing, Chunk size:, "Set unknown block ID:", script log-file management, and the Change blocks: sub-language.
  • Recent Exports submenu (issue Add most recently exported files as a feature #138 parity): up to 5 entries, deduped/newest-first, persisted immediately to wxConfig, wired into every successful export.

Two small cross-platform bug fixes found while building and testing this port, both applying to shared code:

  • Win/ObjFileManip.cpp hardcoded L"\\" as a path separator in 3 places for texture subdirectories, producing a literal tex\ directory name on macOS instead of a tex/ subdirectory. Fixed by using the existing-but-previously-unused gSeparator (Windows continues to get "\\", unchanged).
  • MinewaysFrame::RunExport (Mac-only) never re-derived export bounds from the live highlight state before exporting, so a script's Selection location... command had no effect on what actually got exported — fixed to match Win/Mineways.cpp's saveObjFile, which re-queries GetHighlightState unconditionally before every export.

Sketchfab publish integration is explicitly not included — there's no Mac equivalent yet, and it needs a real Sketchfab account/API token to build and verify against, which wasn't available while building this. Left for whoever has Sketchfab credentials to pick up; see Mac/ImportSettings.h and TODO.md for the exact scope note.

See Mac/TODO.md (repo-root TODO.md) for the full, itemized parity audit against the Windows UI, and CLAUDE.md for build instructions and architecture notes left for future contributors.

Test plan

  • make -C Mac builds cleanly (zero errors; pre-existing warnings only)
  • App launches and Test Block World renders without a real Minecraft install
  • Manual smoke test of world loading, map selection, export (OBJ/USD/STL/VRML/schematic), Culling Schemes editor, and settings persistence across relaunch
  • Standalone headless harnesses (built and torn down during development, not checked in) exercising SaveVolume, DrawMapToArray/writepng, wxConfig round-trips, Import Settings header re-import + scripting, and Recent Exports — all passing
  • Windows build unaffected — not verified on Windows directly in this environment (macOS-only); the shared-code changes are narrowly scoped and documented above for a maintainer to double-check on Windows

🤖 Generated with Claude Code

wbreiler and others added 16 commits June 23, 2026 16:11
All 10 core library objects now build natively for arm64-apple-darwin
using clang++ in Mac/. No errors; warnings only.

Files changed in Win/:
- stdafx.h: add #ifdef WIN32 guard so non-Windows builds get POSIX paths
  instead of windows.h/tchar.h/io.h; macOS PortaOpen/Create/Append
  redirect through _mwPorta*W() wide-char to UTF-8 helpers.
- nbt.h: guard #define ZLIB_WINAPI with #ifdef WIN32 so macOS links
  against system zlib without __stdcall calling-convention issues.
- nbt.cpp: hoist 'int inttype' before goto SectionsCode to fix
  clang's stricter goto-past-initialization check.
- ObjFileManip.cpp: fix goto-past-init for float norm, bool subtypeGroup,
  bool subtypeMaterial, int noteProgress/mkGroupsObjs/prevDataVal/
  prevSwatchLoc/noteFaceProgress, bool usePreviewSurface/float emission/
  int nextStart/startRun; rename 'or' variable to 'ovr' (C++ keyword
  conflict); fix qsort_s -> qsort_r wrapper (macOS argument order differs).

New Mac/:
- compat.h: Win32 type shims (HANDLE=FILE*, BOOL, DWORD, HKEY/HWND/
  HINSTANCE as void*), Windows CRT compat (_strdup, strcpy_s/strcat_s
  templates, _strlwr_s/_wcslwr_s, _stricmp/_wcsicmp, _wfopen_s,
  __time32_t/_time32/_localtime32_s/asctime_s, wcsncpy_s/wcscat_s),
  Win32 file I/O stubs (WriteFile/ReadFile/GetFileSize via FILE*),
  CreateDirectoryW via mkdir, MultiByteToWideChar/WideCharToMultiByte
  via mbstowcs/wcstombs, qsort_s->qsort_r wrapper, errno_t typedef.
- stdafx.h: macOS replacement precompiled header.
- zlib.h: redirect #include "zlib.h" to system <zlib.h>.
- Makefile: arm64 build producing 10 .o files; GUI files excluded.

Failure boundary: Mineways.cpp and MinewaysMap.cpp (the Win32 GUI)
cannot be compiled on macOS because they depend on WinMain/WndProc,
GDI drawing, common controls, Windows registry, and resource.h
(UTF-16 LE - clang refuses to parse it). A native macOS port requires
a new UI layer (Cocoa or wxWidgets/Qt) calling into the core library.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MinewaysFrame (wxFrame) + MapPanel (wxPanel) replace Win32 WndProc.
DrawMap() renders into a malloc'd RGBA buffer displayed via wxImage.
Mouse pan, scroll-wheel zoom, keyboard arrow nav all wired. File >
Open World shows wxDirDialog and calls GetSpawn/GetFileVersionId.
CullingStubs.cpp provides isBlockCulled() no-op until dialog is ported.
MinewaysMap.cpp now compiles clean via POSIX FindFirstFileW/opendir shims.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- TODO.md tracking all remaining work
- World saves submenu: scans ~/Library/.../saves with opendir, shows
  each world dir containing level.dat; Test Block World always first
- Selection rectangle: right-click drag calls SetHighlightState;
  DrawMap renders the overlay natively, no separate draw needed
- Space bar: snaps bottom depth to min solid height in active selection
- Mouse hover: IDBlock status bar update on EVT_MOTION
- Bottom slider wired to gTargetDepth
- Pixel format fix: DrawMap writes R,G,B,0xFF (already RGB); removed
  incorrect BGR swap that would have inverted colors
- Remove redundant MapPanel::m_bits double buffer
- Add Mac/mineways binary to .gitignore

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mac/LocationDialog.cpp replaces Win/Location.cpp (122 lines → 40 lines
via wxDialog). Shows X/Z text entries, validates integer input, jumps
the map view on OK.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
yoff was using block->minHeight directly instead of negating it,
and the grid/data/light memsets used maxHeight instead of the
actual allocated heightAlloc, under/over-clearing the buffers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- ExportDialog.cpp/h: lean wxDialog export/print dialog wired to
  SaveVolume(), replacing the ~1600-line Win/ExportPrint.cpp
- MacCullingSchemes.cpp/h: port of Win/CullingSchemes.cpp with
  wxConfig persistence and a wxCheckListBox-based editor dialog,
  replacing the CullingStubs.cpp no-op stub
- Info.plist + mineways.icns + `make app` target: bundle as
  Mineways.app, locating terrainExt.png relative to Resources/
- compat.h: expand leading ~/ in fopen paths (wide-char callers
  don't get shell tilde expansion)
- TODO.md updated to reflect completed Export/Culling/Packaging work

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Correctness fixes found via code review + a live repro of "export reports
success but writes nothing":

- Export always applied an unwanted 270deg rotation: radioRotate0/90/180/270
  were never set, so Win/ObjFileManip.cpp's rotateLocation() fell through
  to its 270deg branch on every export. Set radioRotate0=1.
- Depth sliders didn't actually constrain exports. New selections used the
  world's absolute max height instead of the Depth slider (added
  GetCurDepth()); worse, moving either slider after a selection was already
  drawn never touched the selection's stored Y bounds, so the sliders looked
  inert once a box existed. Both sliders now re-apply SetHighlightState with
  the live depth, matching Win/Mineways.cpp's slider handlers.
- SaveVolume's "nothing exportable in this selection" codes (MW_NO_BLOCKS_FOUND,
  MW_ALL_BLOCKS_DELETED) were being treated as full success because the Mac
  success check used the wrong threshold (MW_BEGIN_ERRORS instead of
  MW_BEGIN_NOTHING_TO_DO, per Win/Mineways.cpp). Confirmed via a standalone
  headless harness against SaveVolume before writing the fix. Now shows a
  distinct "Nothing exported" warning instead of a false "Export complete".
- Culling scheme defaults (barrier/structure-void hidden) were never seeded
  at Mac startup, unlike Windows' applyCullingScheme(NULL) at launch.
- Active culling scheme name was hardcoded blank in export statistics;
  getSelectedCullingSchemeW() was dead code.
- Unterminated strncpy when saving an edited culling scheme name >=254 bytes.
- No setlocale(LC_ALL, "") call, so wcstombs/mbstowcs could corrupt
  non-ASCII world/export paths under the default "C" locale.
- ScanWorldSaves now validates worlds via GetFileVersion/GetLevelName
  (skips unreadable/unsupported saves, shows the real level name) instead
  of just checking for level.dat's existence.

Cleanup:
- Deleted dead Mac/CullingStubs.cpp (superseded by MacCullingSchemes.cpp,
  not in the Makefile, mismatched signature).
- Mac/stdafx.h no longer duplicates Win/stdafx.h's !WIN32 branch (which the
  Mac include order silently shadowed); it now just includes it. Completed
  that branch with the macros it was missing (_wcsicmp, _countof, etc.) so
  it's an actual working single source of truth.
- NUM_CULL_ENTRIES in MacCullingSchemes.cpp no longer locally redefines the
  constant from Win/CullingSchemes.h (drift risk); now includes that header.
- MapPanel::OnPaint reuses a member RGB buffer instead of malloc/free every
  paint call.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Feature-parity pass following an audit against the Windows UI (see TODO.md).

View menu / navigation:
- Wire the dead OnOpenWorld handler (browse to any world folder) to a menu
  item — it existed but was never reachable.
- Add the &View menu, absent on Mac until now: all 8 render-mode toggles
  (show all objects, show biomes, elevation shading, lighting, cave mode,
  hide obscured, transparent water, map grid), View Nether/End, Jump to
  Spawn/Player, an Information dialog, Undo Selection, Select All, and Zoom
  out further. These all drive gOptions.worldType bits and shared renderer
  code that already existed and was already compiled in — this is menu/
  handler wiring, not new rendering logic. Spawn/player coordinates are now
  kept in globals instead of being discarded right after world load.
- Select All ports Win/Mineways.cpp:2108-2145's select-entire-map-or-
  schematic logic, including the minimum-depth calculation.

Export dialog (the big one):
- Mac/ExportDialog.cpp now operates directly on ExportFileData — the same
  struct Win/ExportPrint.cpp uses — instead of a lossy separate settings
  struct that silently hardcoded most options. New 5-tab dialog (General/
  Materials/Sizing/3D Print Prep/Advanced) covering rotation, all 4 model-
  sizing strategies (target height/minimize-for-material/fixed block size/
  target cost, with the shared physical-material and units tables), the
  full 3D-print prep set (fill bubbles, seal entrances/tunnels, connect
  parts/corner tips/edges, delete floaters, hollow + superhollow, melt
  snow, export-all + fatten), 5 material modes, texture channels, biome/
  leaves/borders/composite toggles, ZIP + keep-files, and the advanced
  instancing/grouping set (separate types, USD individual blocks, material
  per family, split by type, custom material, export MDL, doubled
  billboards, decimate).
- Two preset buttons ("Load Rendering Defaults" / "Load 3D-Printing
  Defaults") reproduce Windows' two separate export menu entries
  (initializeViewExportData/initializePrintExportData) inside Mac's one
  unified Export Model menu item.
- MinewaysFrame.cpp gained BuildExportFlags(), a line-for-line port of the
  exportFlags bitmask translation from Win/Mineways.cpp:5296-5542, so the
  dialog's choices actually reach SaveVolume() with the same effect as on
  Windows.
- Verified end-to-end with a standalone headless harness (built and torn
  down during development, not checked in) exercising OBJ/STL/USD exports
  through real SaveVolume() calls. That caught a genuine pre-existing
  cross-platform bug in Win/ObjFileManip.cpp: three call sites hardcoded
  L"\\" as a path separator when building the tile-texture and USD
  materials subdirectories. Backslash isn't a path separator on POSIX, so
  this produced a directory literally named "tex\" on macOS instead of a
  "tex/" subdirectory — invisible on Windows, silently broken on Mac, and
  never exercised before since the old minimal export dialog never enabled
  the tile-texture path. Fixed by using the already-present but previously
  unused gSeparator (set via SetSeparatorObj, "/" on Mac and "\\" on
  Windows) at all three sites; Windows output is unchanged.

Known simplification (marked with a ponytail: comment in ExportDialog.cpp):
per-file-type fields (block size, hollow thickness, physical material,
etc.) load once when the dialog opens for whatever file type was current,
rather than live-reloading when the File Type dropdown changes mid-dialog
the way Windows does. Not incorrect, just a minor UX gap.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- SaveVolume() now gets a real progress callback (was nullptr): a
  wxProgressDialog tracks the export, updated via ExportProgressCB which
  mirrors Win/Mineways.cpp's updateProgress() including its -999.0f
  "message only" sentinel. No wxPD_CAN_ABORT — confirmed Windows has no
  mid-export cancellation mechanism either (nothing in ObjFileManip.cpp
  polls a cancel flag), so a Cancel button would be a false promise.
- SaveExportFileData/LoadExportFileData hex-encode/decode the whole
  ExportFileData struct into wxConfig (same idiom as MacCullingSchemes.cpp's
  culled[] persistence), guarded by a stored sizeof() so a struct-layout
  change in a future build can't corrupt-load into a live struct. Verified
  byte-identical round-trip via a standalone test (built and torn down
  during development, not checked in).
- Recent Exports turned out not to be a re-export shortcut: traced Windows'
  IDM_RECENT_EXPORT_BASE handler and it re-imports/reopens the file via the
  same machinery as File > Import Settings, which doesn't exist on Mac yet.
  Left in the backlog blocked on that instead of building a shortcut that
  doesn't match Windows' actual behavior.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Small polish batch closing out several remaining backlog items:

- About box now matches Win/Mineways.rc's IDD_ABOUTBOX text verbatim
  (version, copyright, Minecraft-version compatibility line, minutor
  credit, mineways.com link).
- Help menu: keyboard (F1), troubleshooting, documentation, report a bug
  (wxLaunchDefaultBrowser to the same URLs Windows' ShellExecute calls
  use), and "Give more export memory!" wired to gOptions.moreExportMemory
  + MinimizeCacheBlocks (already-shared code).
- File menu: Reload World (R), Download Terrain Files (browser link),
  Repeat Export (Ctrl+X) — the last required extracting a RunExport()
  helper out of OnExportOBJ so both the dialog path and the repeat path
  share the same SaveVolume-calling code instead of duplicating it.
- Added Ctrl+T for Choose Terrain File, the one accelerator gap found by
  diffing against Win/Mineways.rc's IDC_MINEWAYS ACCELERATORS table.

Also caught and fixed a stale TODO.md entry: "World Information dialog"
was listed as not-yet-done but was actually already implemented in an
earlier session as OnViewInformation.

TODO.md updated with what's left: Jump to Model (F4), Export Map (Ctrl+M),
terrain-file history submenu, Import Settings (blocking Recent Exports),
and Sketchfab (needs real API credentials, flagged as likely out of scope
for unattended work).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Continuing the backlog pass:

- Jump to Model (F4): traced Win/Mineways.cpp:2481-2500 — despite the name,
  it just centers the view on the current selection's midpoint, not a
  tracked "last exported model". OnJumpModel matches that exactly.
- Export Map (Ctrl+M): ports Win/Mineways.cpp's saveMapFile() — re-renders
  the selected region fresh at the current zoom via DrawMapToArray (not a
  screenshot of the live buffer) and writes it with writepng. Both are
  shared, platform-independent code already compiled into the Mac binary.
  Verified with a standalone headless test exercising the DrawMapToArray +
  writepng round-trip against a real decodable PNG.
- "Choose Terrain File history" turned out to be a misreading of the
  Windows feature: loadTerrainList() isn't a recently-used list, it scans
  the app directory for terrainExt*.png files shipped alongside the
  executable (alternate texture packs), excluding _n/_r/_m/_e PBR-channel
  suffix files. Implemented as ScanTerrainFiles() (same pattern as
  ScanWorldSaves) populating a "Choose Terrain File" submenu with
  [default] + whatever's found. Also fixed a small pre-existing gap while
  touching this code: OnChooseTerrainFile never redrew the map after
  picking a new file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Traced Win/Mineways.cpp's importSettings/importModelFile/
readAndExecuteScript/interpretImportLine before attempting it in the
autonomous backlog pass: it's 1000+ lines covering both re-importing an
exported file's settings header and a full batch-scripting command
language, comparable in scope to the Export dialog rewrite. Not something
to fold into incremental backlog work without explicit scope confirmation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ports Win/Mineways.cpp's importSettings/importModelFile/readAndExecuteScript/
interpretImportLine/interpretScriptLine into new Mac/ImportSettings.cpp:
re-imports the '#'-comment header from any exported OBJ/USD/VRML/schematic
file back into ExportFileData, and runs Mineways batch scripts (a superset
of the same syntax plus navigation/export action commands). Preserves
Windows' atomic header-commit (a mid-parse error can't half-modify the live
settings) and two-pass script execution (syntax check, then a real run).

Sketchfab, mouse remapping, custom printer materials, block-name
translation, chunk size override, and the "Change blocks:" sub-language are
deliberately not ported (documented in ImportSettings.h) — reported as
clear errors rather than silently ignored.

Required exposing more of MinewaysFrame's internals (LoadWorldFromDir,
LoadSchematic, LoadTestBlockWorld, SetTerrainFile, SwitchToNether/TheEnd,
Jump*, RunExport, RedrawMap) so the interpreter can drive the same actions
the menu items do, plus SelectCullingSchemeByName in MacCullingSchemes.

Found and fixed a real bug while building the verification harness:
RunExport never re-derived the export bounds from the live highlight state
(it relied on the caller having pre-filled gEfd from GetHighlightState()),
so a script's "Selection location..." command silently had no effect on
what actually got exported. Windows' saveObjFile avoids this by always
re-querying GetHighlightState right before export; RunExport now does the
same.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ports Win/Mineways.cpp's addToRecentExports/populateRecentExportsMenu/
IDM_RECENT_EXPORT_BASE handler: up to 5 most-recent successful model exports,
newest first and deduped, in a File menu submenu placed right after Import
Settings. Persisted to wxConfig immediately on every change. Clicking an
entry re-opens it the same way File > Import Settings does, except
schematic-like extensions reopen as a world; a since-deleted file is pruned
from the list with a warning instead of erroring.

This was the last item in the wx Mac port backlog besides Sketchfab, which
stays explicitly deferred to the upstream maintainer (no Mac Sketchfab
integration, needs real API credentials).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@wbreiler

wbreiler commented Jul 3, 2026

Copy link
Copy Markdown
Author

Created from #165

@erich666

erich666 commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Very cool! But, I think I need to leave it as a branch for now - I am not a Mac developer and so cannot test these changes myself. Still, I think it's worth noting this code, for sure, so I've added a link to this branch here: https://www.realtimerendering.com/erich/minecraft/public/mineways/downloads.html#macPlatformHelp

@erich666

erich666 commented Jul 7, 2026

Copy link
Copy Markdown
Owner

(and, nice to see I'm not the only person using Claude Code on Mineways. It's pretty amazing, though occasionally goes wrong.)

wbreiler and others added 5 commits July 7, 2026 11:52
Builds the wxWidgets Mac port on every push/PR touching Mac/ or Win/,
uploading a self-contained Mineways.app (Homebrew wx dylibs bundled via
dylibbundler, verified to launch from a relocated copy with no Homebrew
in the loop). Pushing a vX.Y tag additionally codesigns with a Developer
ID cert, notarizes via notarytool (App Store Connect API key), staples,
and publishes a GitHub Release with the zipped app attached.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Full sign+notarize requires a paid Apple Developer account and a fussy
local cert/key export dance (hit errSecPassphraseRequired along the way).
Not worth it for a hobby open-source build — ship unsigned instead.
dylibbundler already ad-hoc signs the bundled app, so it launches with
just a one-time "unidentified developer" right-click-Open, no further
setup or secrets required.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Verified locally: an unsigned, quarantined download is rejected by
Gatekeeper (spctl) whether or not it's ad-hoc signed, which macOS shows
as "Mineways.app is damaged" rather than the older, clearer "unidentified
developer" prompt -- easy to mistake for a broken zip. `xattr -cr` after
download fixes it; confirmed the app launches clean afterward. Full-bundle
ad-hoc signing is cheap insurance and doesn't hurt. Release notes now
carry the one-line fix so users don't have to ask.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CLAUDE.md had no mention of Mac/ at all despite it now being a
feature-complete parallel GUI with its own CI/CD and release pipeline.
Add build commands, the unsigned/ad-hoc-signing distribution decision,
the origin/upstream remote split, and the version-drift note. Also fix
a stale pitfall claiming the memory directory is empty.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants