<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.multimedia.cx/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=SpdC4c</id>
	<title>MultimediaWiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.multimedia.cx/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=SpdC4c"/>
	<link rel="alternate" type="text/html" href="https://wiki.multimedia.cx/index.php/Special:Contributions/SpdC4c"/>
	<updated>2026-09-28T17:06:31Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.5</generator>
	<entry>
		<id>https://wiki.multimedia.cx/index.php?title=Origin_MGI&amp;diff=7620</id>
		<title>Origin MGI</title>
		<link rel="alternate" type="text/html" href="https://wiki.multimedia.cx/index.php?title=Origin_MGI&amp;diff=7620"/>
		<updated>2007-04-10T22:19:23Z</updated>

		<summary type="html">&lt;p&gt;SpdC4c: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* Extension: mgi, tre&lt;br /&gt;
* Company: [[Origin Systems]]&lt;br /&gt;
&lt;br /&gt;
==Credit==&lt;br /&gt;
This document comes from [http://www.wotsit.org wotsit.org], and originated on GAP's (Game Audio Player) website (now defunct).&lt;br /&gt;
By Valery V. Anisimovsky (no valid email address known)&lt;br /&gt;
Dmitry Kirnocenskij (ejt@mail.ru) is credited with working out EA ADPCM decompression algorithm.&lt;br /&gt;
&lt;br /&gt;
==MGI File Header==&lt;br /&gt;
&lt;br /&gt;
The MGI file has the following header:&lt;br /&gt;
&lt;br /&gt;
 struct MGIHeader&lt;br /&gt;
 {&lt;br /&gt;
  char	szID[4];&lt;br /&gt;
  DWORD dwUnknown1;&lt;br /&gt;
  DWORD dwNumSecIndices;&lt;br /&gt;
  DWORD dwUnknown2;&lt;br /&gt;
  DWORD dwNumIntIndices;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
''szID'' -- string ID, which is equal to &amp;quot;\x8F\xC2\x35\x3F&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
''dwUnknown1, dwUnknown2'' -- seem to be always 0.&lt;br /&gt;
&lt;br /&gt;
''dwNumSecIndices'' -- the number of section indices used in section descriptors&lt;br /&gt;
(see below).&lt;br /&gt;
&lt;br /&gt;
''dwNumIntIndices'' -- seems to be the number of indices used in interactive&lt;br /&gt;
playback descriptors (see below).&lt;br /&gt;
&lt;br /&gt;
After the header comes the table of interactive playback descriptors. Each&lt;br /&gt;
descriptor has the following format:&lt;br /&gt;
&lt;br /&gt;
 struct MGIIntDesc&lt;br /&gt;
 {&lt;br /&gt;
  LONG	lIndex;&lt;br /&gt;
  DWORD dwSection;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
''lIndex'' -- seems to be the index of interactive sequence. Note that some&lt;br /&gt;
indices are negative values.&lt;br /&gt;
&lt;br /&gt;
''dwSection'' -- the pointer to the section (that is, the index of the section&lt;br /&gt;
descriptor correspondent to the section, NOT the index of section given in&lt;br /&gt;
the descriptor itself!)&lt;br /&gt;
&lt;br /&gt;
The number of descriptors in this table (its size) is very uncertain. I use&lt;br /&gt;
a kind of heuristic approach to get past this table, outlined below.&lt;br /&gt;
&lt;br /&gt;
After the table of interactive playback descriptors comes the (DWORD) number&lt;br /&gt;
of sections in the file (let it be denoted as dwNumSections). After this&lt;br /&gt;
number comes the table of (dwNumSections) section descriptors. Each descriptor&lt;br /&gt;
has the following format:&lt;br /&gt;
&lt;br /&gt;
 struct MGISecDesc&lt;br /&gt;
 {&lt;br /&gt;
  DWORD dwStart;&lt;br /&gt;
  DWORD dwIndex;&lt;br /&gt;
  DWORD dwOutSize;&lt;br /&gt;
 };&lt;br /&gt;
&lt;br /&gt;
''dwStart'' -- the starting position of the audio data for the section.&lt;br /&gt;
&lt;br /&gt;
''dwIndex'' -- the index of the section (NOT the index of the correspondent&lt;br /&gt;
descriptor). Several different sections may have the same index. The meaning&lt;br /&gt;
of this index seems to be quite uncertain, though it is not required for&lt;br /&gt;
non-interactive playback of MGI files.&lt;br /&gt;
&lt;br /&gt;
''dwOutSize'' -- the output size of the audio stream stored in the section&lt;br /&gt;
(in bytes). May be used for section length (in seconds) calculation.&lt;br /&gt;
Includes the outsizes for both compressed and non-compressed parts of the&lt;br /&gt;
section, that is, it's the whole outsize of the section.&lt;br /&gt;
&lt;br /&gt;
Now, here's the approach I use to get to the start of section descriptors&lt;br /&gt;
table. First, we should read MGIHeader. Then, we may read the interactive&lt;br /&gt;
playback table -- descriptor by descriptor and for each descriptor we should&lt;br /&gt;
check whether its (''dwIndex'') is less than header values (''dwNumSecIndices'')&lt;br /&gt;
and (''dwNumIntIndices''). If the suspicious descriptor is found (for which&lt;br /&gt;
the index value is out of range -- note that the descriptors with negative&lt;br /&gt;
indices are correct!) we may check whether this descriptors is really the&lt;br /&gt;
beginning of the section descriptors table. This check is simple to perform:&lt;br /&gt;
assume that audio data for the first section starts right after the section&lt;br /&gt;
descriptors table, then we can get the following equation:&lt;br /&gt;
''(dwDescPos) 4 (lIndex)*sizeof(MGISecDesc)=(dwSection)'',&lt;br /&gt;
where (''dwDescPos'') is the position in MGI file at which starts the suspicious&lt;br /&gt;
descriptor, (''lIndex'') and (''dwSection'') are the values from that descriptor.&lt;br /&gt;
When this equation holds we most likely found the beginning of the section&lt;br /&gt;
descriptors table.&lt;br /&gt;
&lt;br /&gt;
After the section descriptors table comes the audio data for the sections.&lt;br /&gt;
&lt;br /&gt;
== MGI Section Audio Data ==&lt;br /&gt;
&lt;br /&gt;
For each section we can get the starting position of the audio data from&lt;br /&gt;
the section's descriptor. Note that the last section seems to be always&lt;br /&gt;
empty (it starts at the end of the MGI file, has zero outsize and zero index).&lt;br /&gt;
Each section consists of two parts: EA ADPCM compressed and non-compressed.&lt;br /&gt;
First comes the compressed part and right after that comes non-compressed&lt;br /&gt;
part. To get the size of non-compressed tail you can use the following formula:&lt;br /&gt;
''dwTailSize=(dwSectionSize*0x70-dwOutSize*0x1E)/0x52,''&lt;br /&gt;
where (dwSectionSize) is the size of the whole section (which may be calculated&lt;br /&gt;
as the difference between the start positions of the next and the current&lt;br /&gt;
sections), (''dwOutSize'') is the value from the section's descriptor. This&lt;br /&gt;
formula can be easily derived knowing the fact the compressed data is composed&lt;br /&gt;
of blocks (0x1E bytes each -- for stereo stream) which are decompressed into&lt;br /&gt;
0x1C*4 bytes each (for stereo stream). Note that this formula is valid for&lt;br /&gt;
both stereo and mono MGI files.&lt;br /&gt;
&lt;br /&gt;
The compressed part contains EA ADPCM compressed stream. It's devided into&lt;br /&gt;
small blocks of 0x1E (stereo) or 0xF (mono) bytes. The non-compressed part&lt;br /&gt;
contains raw (16-bit signed) PCM data. All MGI files I've seen are stereo&lt;br /&gt;
22050 Hz 16-bit. The non-compressed part may be played right after the&lt;br /&gt;
compressed part.&lt;br /&gt;
&lt;br /&gt;
All sections may be played consequently right in their turn. Some MGI files&lt;br /&gt;
contain several quite independent tunes, though when played consequently,&lt;br /&gt;
those tunes form relatively seamless composition.&lt;br /&gt;
&lt;br /&gt;
== EA ADPCM Decompression Algorithm ==&lt;br /&gt;
&lt;br /&gt;
During the decompression four LONG variables must be maintained for stereo&lt;br /&gt;
stream: ''lCurSampleLeft, lCurSampleRight, lPrevSampleLeft, lPrevSampleRight''&lt;br /&gt;
and two -- for mono stream: ''lCurSample, lPrevSample''. At the beginning of each&lt;br /&gt;
section you must initialize these variables to zeros.&lt;br /&gt;
Note that LONG here is signed.&lt;br /&gt;
&lt;br /&gt;
The stream is divided into small blocks of 0x1E (stereo) or 0xF (mono) bytes.&lt;br /&gt;
You should process all blocks in their turn. Here's the code which&lt;br /&gt;
decompresses one stereo stream block.&lt;br /&gt;
&lt;br /&gt;
 BYTE  InputBuffer[InputBufferSize]; // buffer containing data for one block&lt;br /&gt;
 BYTE  bInput;&lt;br /&gt;
 DWORD i;&lt;br /&gt;
 LONG  c1left,c2left,c1right,c2right,left,right;&lt;br /&gt;
 BYTE  dleft,dright;&lt;br /&gt;
 &lt;br /&gt;
 bInput=InputBuffer[0];&lt;br /&gt;
 c1left=EATable[HINIBBLE(bInput)];   // predictor coeffs for left channel&lt;br /&gt;
 c2left=EATable[HINIBBLE(bInput) 4];&lt;br /&gt;
 c1right=EATable[LONIBBLE(bInput)];  // predictor coeffs for right channel&lt;br /&gt;
 c2right=EATable[LONIBBLE(bInput) 4];&lt;br /&gt;
 &lt;br /&gt;
 bInput=InputBuffer[1];&lt;br /&gt;
 dleft=HINIBBLE(bInput) 8;   // shift value for left channel&lt;br /&gt;
 dright=LONIBBLE(bInput) 8;  // shift value for right channel&lt;br /&gt;
 &lt;br /&gt;
 for (i=2;i&amp;lt;0x1E;i  )&lt;br /&gt;
 {&lt;br /&gt;
  left=HINIBBLE(InputBuffer[i]);  // HIGHER nibble for left channel&lt;br /&gt;
  left=(left&amp;lt;&amp;lt;0x1c)&amp;gt;&amp;gt;dleft;&lt;br /&gt;
  left=(left lCurSampleLeft*c1left lPrevSampleLeft*c2left 0x80)&amp;gt;&amp;gt;8;&lt;br /&gt;
  left=Clip16BitSample(left);&lt;br /&gt;
  lPrevSampleLeft=lCurSampleLeft;&lt;br /&gt;
  lCurSampleLeft=left;&lt;br /&gt;
 &lt;br /&gt;
  right=LONIBBLE(InputBuffer[i]); // LOWER nibble for right channel&lt;br /&gt;
  right=(right&amp;lt;&amp;lt;0x1c)&amp;gt;&amp;gt;dright;&lt;br /&gt;
  right=(right lCurSampleRight*c1right lPrevSampleRight*c2right 0x80)&amp;gt;&amp;gt;8;&lt;br /&gt;
  right=Clip16BitSample(right);&lt;br /&gt;
  lPrevSampleRight=lCurSampleRight;&lt;br /&gt;
  lCurSampleRight=right;&lt;br /&gt;
 &lt;br /&gt;
  // Now we've got lCurSampleLeft and lCurSampleRight which form one stereo&lt;br /&gt;
  // sample and all is set for the next step...&lt;br /&gt;
  Output((SHORT)lCurSampleLeft,(SHORT)lCurSampleRight); // send the sample to output&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
HINIBBLE and LONIBBLE are higher and lower 4-bit nibbles:&lt;br /&gt;
 #define HINIBBLE(byte) ((byte) &amp;gt;&amp;gt; 4)&lt;br /&gt;
 #define LONIBBLE(byte) ((byte)&lt;/div&gt;</summary>
		<author><name>SpdC4c</name></author>
	</entry>
</feed>