Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[BOX32][WRAPPER] Add libogg wrapper for BOX32#3596

Open
runlevel5 wants to merge 1 commit intoptitSeb:mainfrom
runlevel5:wrap-libogg-box32
Open

[BOX32][WRAPPER] Add libogg wrapper for BOX32#3596
runlevel5 wants to merge 1 commit intoptitSeb:mainfrom
runlevel5:wrap-libogg-box32

Conversation

@runlevel5
Copy link
Contributor

@runlevel5 runlevel5 commented Mar 1, 2026

Summary

  • Add 32-bit wrapper for libogg (libogg.so.0) with full struct alignment conversion between 32-bit and 64-bit layouts
  • All 6 ogg structs require conversion (pointers and longs differ in size): ogg_iovec_t, oggpack_buffer, ogg_page, ogg_packet, ogg_stream_state, ogg_sync_state
  • Uses khash shadow mapping for persistent state structs (ogg_stream_state, ogg_sync_state) and stack-local conversion for ephemeral structs (ogg_page, ogg_packet, oggpack_buffer)
  • Adds 5 new wrapper signatures to generated files
  • Tested on PPC64LE (POWER9) with a 32-bit x86 encode/decode test binary
  • Verified and tested (see test https://gist.github.com/runlevel5/a8586a79a1151c2c12b2d05d3cf78794)

Context

Games running under BOX32 (e.g., Grim Fandango Remastered on PPC64LE) that use libogg currently fall back to emulated mode. This wrapper allows the native host libogg to be used instead.

@runlevel5 runlevel5 changed the title Add libogg wrapper for BOX32 [BOX32][WRAPPER] Add libogg wrapper for BOX32 Mar 1, 2026
@ptitSeb
Copy link
Owner

ptitSeb commented Mar 1, 2026

Side note: I know with AI, writting a long PR description is Painless. But for the human reader I am, having redundant informations is just a waste of time. I don't need "Files Changed" chapter. I can see already what's changes in the PR. Same for what changes: I can see the diff already.
In general: I have more trust in the code than in comments, especialy A.I. generated.

@ptitSeb
Copy link
Owner

ptitSeb commented Mar 1, 2026

Speaking of trust: I double checked the first assertion: "ogg_stream_state doesn't need to be aligned".
Here is the structure:

typedef struct {
  unsigned char   *body_data;    /* bytes from packet bodies */
  long    body_storage;          /* storage elements allocated */
  long    body_fill;             /* elements stored; fill mark */
  long    body_returned;         /* elements of fill returned */


  int     *lacing_vals;      /* The values that will go to the segment table */
  ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact
                                this way, but it is simple coupled to the
                                lacing fifo */
  long    lacing_storage;
  long    lacing_fill;
  long    lacing_packet;
  long    lacing_returned;

  unsigned char    header[282];      /* working space for header encode */
  int              header_fill;

  int     e_o_s;          /* set when we have buffered the last packet in the
                             logical bitstream */
  int     b_o_s;          /* set after we've written the initial page
                             of a logical bitstream */
  long    serialno;
  long    pageno;
  ogg_int64_t  packetno;  /* sequence number for decode; the framing
                             knows where there's a hole in the data,
                             but we need coupling so that the codec
                             (which is in a separate abstraction
                             layer) also knows about the gap */
  ogg_int64_t   granulepos;

} ogg_stream_state;

The first element structure is a pointer. Pointer are 4 bytes in 32bits, and 8 bytes in 64bits. That's the whole point of 64bits. The structure absolutly needs alignment.

In your case, it works because the program doesn't touch the structure elements individual, and use it as an opaque one. But other software might not.

Copy link
Owner

@ptitSeb ptitSeb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ogg_stream_state needs alignement.

@runlevel5 runlevel5 marked this pull request as draft March 1, 2026 09:48
@runlevel5
Copy link
Contributor Author

ogg_stream_state needs alignement.

Yes. I am looking into it atm. Any guidance on an example how to align raw 32 bit pointer to 64 bit one is greatly appreciated.

@ptitSeb
Copy link
Owner

ptitSeb commented Mar 1, 2026

ogg_stream_state needs alignement.

Yes. I am looking into it atm. Any guidance on an example how to align raw 32 bit pointer to 64 bit one is greatly appreciated.

Well, that's all that is done in most wrappers for box32...

You can use wrapperhelper to write the core of the functions.
If the structures are simple enough, writting a converter in converter32.c/converter32.h is the solution.
If the sturctures are too complex, use pointer of pointers or stuffs like that, there is not much choices be to write specific code (see libX11 wrapping or SDL2 for examples).

(note that converter32 is not yet automaticaly written. That's the plan, but the tools are not there yet so it's written by end. The style is simple with no comments because, one day hopefully, this will be automatic)

Add 32-bit wrapper for libogg (libogg.so.0) to enable native wrapping
in BOX32 mode. This is a simple pass-through wrapper with no callbacks
or struct conversions needed.

New files:
- src/wrapped32/wrappedlibogg.c
- src/wrapped32/wrappedlibogg_private.h
- src/wrapped32/generated/wrappedlibogg{defs,types,undefs}32.h

New signature added to wrapper32 generated files:
- lFpi (intptr_t fn(void*, int32_t))
@runlevel5 runlevel5 force-pushed the wrap-libogg-box32 branch from 631e1c9 to e1eae77 Compare March 2, 2026 23:04
@runlevel5 runlevel5 marked this pull request as ready for review March 2, 2026 23:10
@runlevel5
Copy link
Contributor Author

runlevel5 commented Mar 2, 2026

All 6 ogg structs have fields (pointer, long, size_t) that differ between i386 (4 bytes) and native 64-bit (8 bytes), so every function needs struct conversion.

I used two strategies depending on struct lifetime:

  • Shadow mapping (khash) for ogg_stream_state and ogg_sync_state — these are allocated by the app and mutated by the library across multiple calls, so I maintain a persistent native-sized shadow per 32-bit address. The native copy is authoritative; I sync back to 32-bit after each call. I borrowed this pattern from wrappedlibxext.c (getShmInfo/delShmInfo).

  • Stack-local conversion for ogg_page, ogg_packet, oggpack_buffer — these are ephemeral (used within a single call), so I convert on the stack before/after calling the native function.

ogg_stream_state and ogg_sync_state only need a *_to_32 converter (native→32) because the native shadow is always the source of truth. The other structs need both *_to_native and *_to_32.

I tested this with a 32-bit x86 encode/decode test binary running through box64 on PPC64LE (POWER9) — full cycle (3 packets → 2 pages → decode back) passes with 0 errors.

kh_destroy(syncstate, sync_shadows); \
sync_shadows = NULL;

#include "wrappedlib_init32.h"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CUSTOM_INIT creates the two khash tables at library load. CUSTOM_FINI frees all shadow structs and destroys the hash tables at library unload, preventing leaks if the app doesn't call ogg_stream_clear/ogg_sync_clear before exit.

@runlevel5
Copy link
Contributor Author

Re: wrapper32.c / wrapper32.h / functions_list.txt changes

I added 5 new wrapper signatures: vFEpi, vFEpl, vFEppi, lFEp, lFEpi. These are needed because all functions are now GOM (routed through my32_* wrappers that take x64emu_t*), and the original pass-through signatures didn't include the E (emu) prefix.

@runlevel5
Copy link
Contributor Author

@ptitSeb I guess that should be it for this work, feel free to review and let me know if it could be improved further

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