cwinapipcmadpcm

how to correctly interpret this WAVEFORMATEX from decompiled raw bytes?


I am working on an old (1999~) win32 decompiled game codebase.

In it, there is the following sequence of bytes, which were directly taken from the disassembled binary:

static char source_pcm_format[50] =
{
    2, 0, 1, 0, 34, 86, 0, 0, 147, 43, 0, 0, 0, 2, 4, 0, 32, 0, 244, 3, 7, 0, 0, 1, 0, 0, 0, 2, 0, 255, 0, 0,
    0, 0, 192, 0, 64, 0, 240, 0, 0, 0, 204, 1, 48, 255, 136, 1, 24, 255
};

These bytes are passed into the function acmStreamOpen, casted to a LPWAVEFORMATEX:

mmresult = acmStreamOpen(&hACMStream, hACMDriver, (LPWAVEFORMATEX)source_pcm_format, &pcm_format, 0, 0, 0, 0);

this function call works correctly

When interpreting these bytes myself as a WAVEFORMATEX, the wFormatTag equals 2, which corresponds to ADPCMWAVEFORMAT.

When I interpret the bytes as a ADPCMWAVEFORMAT, things go weird:
enter image description here

The ADPCMWAVEFORAMT needs wNumCoefs amount of ADPCMCOEFSET. 50 bytes however are too few to store 256 coefficients. When printing the coefficients the following way (I assume its MS-ADPCM):

ADPCMWAVEFORMAT* adpcmfmt = calloc(1, sizeof(ADPCMWAVEFORMAT) + sizeof(ADPCMCOEFSET) * 7); // MS-ADPCM
char source_pcm_format[50] = {
    2, 0, 1, 0, 34, 86, 0, 0, 147, 43, 0, 0, 0, 2, 4, 0, 32, 0, 244, 3, 7, 0, 0, 1, 0, 0, 0, 2, 0, 255, 0, 0,
    0, 0, 192, 0, 64, 0, 240, 0, 0, 0, 204, 1, 48, 255, 136, 1, 24, 255
};
memcpy(adpcmfmt, source_pcm_format, sizeof(source_pcm_format));
fprintf(stderr, "\n");
for(int i =0; i < 7; ++i) {
    fprintf(stderr, "%d %d\n", adpcmfmt->aCoef[i].iCoef1, adpcmfmt->aCoef[i].iCoef2);
}
fflush(stderr);

I receive the following output:

0 512
-256 0
0 192
64 240
0 460
-208 392
-232 0

This seems to be the coefficient list for MS-ADPCM, but shifted by a few bytes:

aCoeff = { {256, 0}, {512, -256}, {0,0}, {192,64}, {240,0}, {460, -208}, {392,-232} }

My goal is to correctly declare and transform the raw bytes into a ADPCMWAVEFORMAT (or WAVEFORMATEX). I don't know why the code with the raw bytes works, when it seemingly contains invalid ADPCMWAVEFORMAT data.

Edit: sizeof(WAVEFORMATEX) + sizeof(ADPCMCOEFSET * 7) returns 52. So the bytes are 2 bytes less than what I would expect. Did something get added between 1999 and today ? wSamplesPerBlock seems to be the wNumCoefs amount, which is expected to be 7.


Solution

  • user @cremno correctly identified the issue. windows.h needs to be the first header to be included to ensure the struct alignment is properly set up for the struct definition of WAVEFORMATEX.

    This is the correct header order:

    // header order important here
    // clang-format off
    #include <windows.h>
    #include <mmreg.h>
    #include <mmiscapi.h>
    #include <MSAcm.h>
    // clang-format on
    

    This lets me create a proper ADPCMWAVEFORMAT variable like so:

    ADPCMWAVEFORMAT* adpcmfmt = calloc(1, sizeof(ADPCMWAVEFORMAT) + sizeof(ADPCMCOEFSET) * 7); // MS-ADPCM
    adpcmfmt->wfx.nSamplesPerSec = 22050;
    adpcmfmt->wfx.nAvgBytesPerSec = 11155;
    adpcmfmt->wfx.nBlockAlign = 512;
    adpcmfmt->wfx.wBitsPerSample = 4;
    adpcmfmt->wfx.cbSize = 32;
    adpcmfmt->wfx.wFormatTag = WAVE_FORMAT_ADPCM;
    adpcmfmt->wfx.nChannels = 1;
    adpcmfmt->wNumCoef = 7;
    adpcmfmt->wSamplesPerBlock = adpcmfmt->wfx.nBlockAlign * 2 / adpcmfmt->wfx.nChannels - 12;
    adpcmfmt->aCoef[0].iCoef1 = 256;
    adpcmfmt->aCoef[0].iCoef2 = 0;
    adpcmfmt->aCoef[1].iCoef1 = 512;
    adpcmfmt->aCoef[1].iCoef2 = -256;
    adpcmfmt->aCoef[2].iCoef1 = 0;
    adpcmfmt->aCoef[2].iCoef2 = 0;
    adpcmfmt->aCoef[3].iCoef1 = 192;
    adpcmfmt->aCoef[3].iCoef2 = 64;
    adpcmfmt->aCoef[4].iCoef1 = 240;
    adpcmfmt->aCoef[4].iCoef2 = 0;
    adpcmfmt->aCoef[5].iCoef1 = 460;
    adpcmfmt->aCoef[5].iCoef2 = -208;
    adpcmfmt->aCoef[6].iCoef1 = 392;
    adpcmfmt->aCoef[6].iCoef2 = -232;
    MMRESULT mmresult = acmStreamOpen(&hACMStream, NULL, (LPWAVEFORMATEX)adpcmfmt, &fmt, 0, 0, 0, 0);