vc6_wine_environment - ryzom/ryzomcore GitHub Wiki


title: Visual C++ 6.0 build environment on Linux using Wine (draft) description: Toolchain package for building the original CVS-era NeL (github.com/ryzom/nel_cvs) with its original compiler β€” the 2000s counterpart of the VS2008 reference environment published: true date: 2026-10-30T00:00:00.000Z tags: editor: markdown dateCreated: 2026-08-06T00:00:00.000Z

Status: baseline packaged and validated (2026-10-30). A reproducible Wine prefix β€” VC6 SP5 + Processor Pack + the DirectX 9.0 SDK Update (Summer 2003), with the Wine-gap workarounds baked in β€” now exists as toolchain_vc6_prefix.7z and builds the CVS-era trees through the winecl6-* wrapper scripts. The Beta 2 reversal work runs on this same baseline with per-project externals overlaid. Companion pages: VS2008 under Wine for the conventions this mirrors, and Windows Compatibility Targets for the runtime-side matrix (which OS each era's binaries run on β€” this page covers the compiler-side ceilings instead). {.is-warning}

Why

The original NeL / Ryzom / Snowballs codebase β€” preserved at github.com/ryzom/nel_cvs β€” was built with Visual C++ 6.0 (cl 12.00), with Format-6.00 .dsp/.dsw project files (55 projects: nel, nelns, snowballs2, tools). Building that tree with its original compiler under Wine gives the authentic 2000s-era x87 reference, the same role the VS2008/Wine environment plays for the Max-2010-era pipeline.

Survey of the final CVS state (2026-08-06):

  • nel/include/nel/misc/types_nl.h hard-requires STLport (#error unless __SGI_STL_PORT β€” any STLport 4.x defines it). VC6's native STL is not an option by the project's own gate.
  • Drivers in-tree: 3D = OpenGL only, sound = OpenAL only. No DirectX SDK requirement for the core libraries; link lines are plain system libs (opengl32.lib, odbc32.lib, ...). No freetype in 3d.dsp at this tree state.
  • No exported .mak files β€” building the .dsp projects needs either MSDEV.EXE /MAKE (the VC6 command-line build driver) or an external build harness.

The packaged toolchain (2026-10-30)

Service pack: SP5, not SP6. An earlier revision of this page assumed SP6 (mislabeling cl 12.00.8804, which is SP5). The shipped package is VC6 SP5 + Processor Pack: cl.exe 12.00.8804, C1.DLL 12.00.8867, C1XX.DLL 12.00.8964, and the backend c2.dll 12.00.9044 from the Processor Pack β€” which installs on SP5 only, never on SP6. This is not just preference: objects compiled by this tree carry @comp.id 12.00.9044, matching the Rich header of the original Ryzom Beta 2 artifacts. A pure-SP5-no-PP variant (RTM c2 12.00.8168) is kept alongside for A/B work.

Package layout (single archive, ~235 MiB, built the same way as the VS2008 toolchain_v90_prefix.7z):

Path What From
drive_c/Program Files/Microsoft Visual Studio VC6 SP5+PP (VC98, Common, incl. MSPDB60.DLL in Common/MSDev98/Bin) working WinXP install, zipped
drive_c/DX90SDK DirectX 9.0 SDK Update (Summer 2003), trimmed to Include/Lib β€” ships un-pathed; stage via NL_VC6_DX (below) SDK archive
drive_c/…/VC98/Bin/CL.EXE, LINK.EXE injector wrappers, see below source included
wine-vc6/winecl6-{env,cc,lib,link,rc} build wrapper scripts source included
wine-vc6/src/{zmhook.c,cl_wrap.c} source of all custom executables β€”
README.md full documentation of the above, including a rebuild-from-archives recipe β€”

The prefix is win32 (WINEARCH=win32 at creation). No extern folder structuring is imposed β€” the Nevrax-convention C:\NEVRAX\EXTERN\* layout (a DX8-named shim over DX90SDK, per-library trees) is a per-project overlay, not baseline; where extern packages live is the consumer project's choice.

Deliberately excluded from the baseline: Platform SDKs (the Feb 2001 and Feb 2003 PSDKs are available separately for experiments β€” see the trap below for why they must stay off the include path), the 3ds Max SDK, STLport (per-project: era NeL stages its own), and the link-identity probe hooks used during the Ryzom Beta 2 work.

Externals are per-project, not in the baseline: STLport 4.x headers + VC6-built iostreams library (era-authentic 4.5.3 β€” upstream sourceforge tarball, md5 cd4dd34b623a8704fbc800de79705171, header-identical to the Nevrax-era wpkg package; must be first on INCLUDE β€” the NL_VC6_STLPORT env var stages it, /dev/null disables), FreeType 2.1.x, libxml2 2.4.x/2.6.x, libjpeg 6b, zlib, GLEXT, EAX 2.x β€” all built with this same VC6 where needed. The DX row in the dependency survey below stands: at the final CVS tree state the core needs no DX SDK; the Ryzom-era client is where the Summer 2003 SDK enters (see the reversal's pinned setup).

Environment wrapper

winecl6-env assembles the environment (INCLUDE/LIB/WINEPATH) and every winecl6-* script sources it, then runs the tool through Wine with Unix→Windows path translation (translate_args — same shape as the VS2008 layer, including the glued-/FI case; additionally drops /showIncludes, which cl 12.00 predates). Env knobs: NL_VC6_PREFIX (prefix location), NL_VC6_STLPORT (STLport staging; /dev/null for STLport-free builds such as modern ryzomcore), NL_VC6_DX (DX SDK staging: any Windows path to an SDK root with INCLUDE/LIB, e.g. 'C:\DX90SDK'; unset, the Nevrax C:\NEVRAX\EXTERN\DX8 location is used if it exists — reversal overlay convention —, otherwise no DX at all), WINEDEBUG (default -all).

Default include order in the pristine baseline is VC98 only. Fully staged (era NeL): STLport β†’ DX β†’ VC98 (+MFC/ATL). windows.h resolves to VC98 (the DX SDK has none); the only header collision is VC98 windows.h β†’ winnt.h β†’ DX basetsd.h, which is inert on _MSC_VER 1200 (_W64 is empty, INT_PTR stays 32-bit). LIB: DX libs first, then VC98, then STLport.

Never put a Platform SDK on this path for VC6. PSDK 2003 winbase.h Interlocked* overloads are C2733 on cl 12.00, and a later PSDK in front of VC98 also breaks VC6 C++ via the newer winnt.h (__w64). The PSDK include order is a VS2008-era concern; for VC6 it is simply wrong. {.is-warning}

Validation battery (banner, x87 float bit-exactness β€” 1.0f/3.0f β†’ 3eaaaaab under /O2 β€”, C++ /GX /MD smoke, STLport smoke, RC smoke) passes 8/8 on the packaged baseline; a PCH TU and a large client TU compile byte-identical (all content sections; only COFF TimeDateStamp / PDB NB10 build-time fields differ, as they must).

Wine traps (verified on VC6)

The VS2008 page's trap list mostly does not apply (VC6 has no manifests, no mt.exe, no SxS β€” plain MSVCRT.DLL, Wine's builtin serves; /Zi works: MSPDB60 is an in-process DLL, there is no mspdbsrv.exe server process until VC7). What bites on VC6 is the compiler's own memory appetite:

  • C1060 (compiler out of heap space) at stock /Zm800 /YX. /Zm scales the compiler's factoring heap; /Zm800 makes CL.EXE request two anonymous VirtualAlloc(NULL, 400 MB) blocks. Wine's 32-bit preloader parks builtins near 0x7a000000, the first heap lands just below, and the second fails. Two simpler fixes were tried and rejected: WINEPRELOADRESERVE alone (the reserve is not consulted by VirtualAlloc(NULL) β€” it goes through map_free_area, so reserving just makes C1060 hit earlier), and LARGEADDRESSAWARE on cl/c1xx/c2 (both heaps then fit in 4 GB, but /YX dies with C2437, allocator<char> already initialized).
  • The fix: IAT-level VirtualAlloc hook injected into the real compiler. Wine stubs LoadAppInitDlls, so the AppInit_DLLs registry route is unavailable; instead VC98/Bin/CL.EXE and LINK.EXE are a ~16 KB injector (cl_wrap.c) that launches the real cl_orig.exe/link_orig.exe suspended, injects zmhook.dll via a remote-thread LoadLibraryA, and resumes it. The hook matches Wine's kernel32!VirtualAlloc thunk prologue (8B FF 55 8B EC 5D FF 25 <IAT>), swaps the IAT slot, and pins VirtualAlloc(NULL, β‰₯128 MB) at 0x20000000, stepping 32 MB β€” combined with WINEPRELOADRESERVE=0x20000000-0x78000000 this reserves a corridor where the two /Zm800 heaps always land. Every pin is logged to C:\nevrax\extern\zmhook.log.
  • After a Wine upgrade, check that log β€” if the VirtualAlloc thunk prologue ever changes, the hook logs unexpected VirtualAlloc prologue and no-ops (C1060 returns). This is the one Wine-coupling to re-verify on toolchain updates.
  • /Fe:name.exe colon form β€” same as the VS2008 page: use the no-colon /Fename.exe form.
  • Case sensitivity has not needed the alias farm so far for VC98/STLport/DX headers (consistent internal casing); the dual-case farm trick exists in the externals tooling if a vendor tree ever needs it.
  • RC.EXE lives in Common/MSDev98/Bin in VC6 (not VC98/Bin) β€” the wrapper points there; /nologo is accepted at this vintage.
  • WINEARCH=win32 at prefix creation. wineboot -i on a 64-bit host without it gives a win64 prefix (Program Files (x86), syswow64). The 32-bit VC6 tools still run under WOW64 there β€” verified: a win64 prefix produced byte-identical object content against the win32 one β€” but the win32 convention is smaller, skips the WOW64 layer, and matches the VS2008 flow; verify #arch=win32 in system.reg after init.

Driving builds

The wrapper scripts compile/link/lib/rc directly β€” see the package README.md for the rebuild-from-archives recipe and full usage. Ninja/CMake-driven builds work the same way as the VS2008 flow (command-line only; MSDEV.EXE /MAKE for the .dsp projects remains untried under Wine β€” the fallback of a small .dsp parser emitting ninja still stands if the IDE route is ever needed).

First milestones

  1. validate_vc6.sh green β€” done (8/8, see above).
  2. STLport 4.5.3 built with this toolchain β€” done (era-authentic iostreams, VC6-built).
  3. A full DLL link against the original's link order β€” done for the STLport DLL during the Beta 2 work (/ORDER + first-object IAT ordering; see the reversal notes for the linker-layout methodology).
  4. CVS-era tree builds: nel/src/misc.dsp β†’ nlmisc.lib (smallest real target, exercises STLport + the header set), then snowballs2.
⚠️ **GitHub.com Fallback** ⚠️