Author Topic: AGI v2.230  (Read 2171 times)

0 Members and 8 Guests are viewing this topic.

Offline lance.ewing

AGI v2.230
« on: June 28, 2026, 04:02:36 AM »
Before I forget (because I did last time), I was notified about 16 months ago by Chris Benshoof that an AGI v2.230 interpreter version had been found at that time. Here is a link to the scummvm bug that relates to it:

https://bugs.scummvm.org/ticket/15764

That ticket has a link to the zip with the interpreter in it.

This version sits between 2.089 and 2.272. There is some discussion on the ticket about adding "support for v2.230 view mirror data" to scummvm, with a link to the commit:

https://github.com/scummvm/scummvm/commit/0614e80c822469293acef12f406afa0fb4818288

A comment in the code says:  2.230 (early version of xmascard): mirror data is in loop header.

Apparently the cell count field uses the upper nibble of that byte for things other than the cell count, two of those (the top two) being related to mirroring. This is what Chris Benshoof sent me regarding it:

"All the xmas views with mirroring only have two loops, and they all have the same mirror nibble: $C. The upper two bits are two distinct mirror flags; they're tested independently but I don't know their specific purpose. The lower two bits are read as a number and I'm comfortable just assuming that's the loop number."

Maybe @AGKorsson can dig out the debugger and work out why it needs two separate mirror bits, and confirm that the lower two bits really are for the loop number. I know that you'll be interested in this one, if you haven't yet seen it.



Offline lskovlun

Re: AGI v2.230
« Reply #1 on: June 28, 2026, 04:25:25 AM »
In GAL, the movement speed is encoded in each view. You can't set it from the script.

Offline lskovlun

Re: AGI v2.230
« Reply #2 on: June 28, 2026, 04:40:03 AM »
Oh, and the ScummVM patch has the following:
Code: [Select]
                // loop-header:
-               //  celCount:BYTE
+               //  celCount:BYTE [ upper nibble used as mirror data in 2.230 ]
                //  relativeCelOffset[0]:WORD
                //  relativeCelOffset[1]:WORD
                //  etc.
-               byte loopHeaderCelCount = resourceData[loopOffset];
-
-               loopData->celCount = loopHeaderCelCount;
+               byte loopHeaderCelCountByte = resourceData[loopOffset];
+               const bool isMirrorDataInLoopHeader = (getVersion() == 0x2230);
+               if (isMirrorDataInLoopHeader) {
+                       loopData->celCount = loopHeaderCelCountByte & 0x0f;
+               } else {
+                       loopData->celCount = loopHeaderCelCountByte;
+               }
So I think we can assume it's a cel count.

Offline lance.ewing

Re: AGI v2.230
« Reply #3 on: June 28, 2026, 05:02:13 AM »
The lower nibble of that byte is the cell count for the loop. That was the known bit, but interpreters like AGILE assume the whole byte is the cell count.

The original AGI interpreter source that we have from the SQ2 disk has this:

Code: [Select]
/* loop structure
*/
typedef struct loopstrc {
UBYTE numcels; /* number of cels in the loop */
WORD celofs[10]; /* offsets to the cels */
} LOOP;

which also implies the whole thing later became only for the cell count.

But for this earlier 2.230 version, only the bottom 4 bits were the cell count and the top 4-bits were other things, the top 2 being related to mirroring.

Offline AGKorson

Re: AGI v2.230
« Reply #4 on: June 28, 2026, 08:26:28 PM »
I can easily compare this to near versions to see how it actually differs from them.

Offline Collector

Re: AGI v2.230
« Reply #5 on: June 29, 2026, 07:15:57 AM »
Will AGILE handle this interpreter or will it have similar issues?
KQII Remake Pic

Offline lance.ewing

Re: AGI v2.230
« Reply #6 on: June 29, 2026, 08:06:04 AM »
Will AGILE handle this interpreter or will it have similar issues?
interpreters like AGILE assume the whole byte is the cell count.

Currently, AGILE does not support this. I'll wait until we know a bit more about the full use of that top nibble before adding support.

Offline AGKorson

Re: AGI v2.230
« Reply #7 on: Yesterday at 10:14:22 PM »
COFIRMED: Version 2.230 uses the cel count byte of a loop to handle mirroring.

When a loop is set (when loading a view or using the set.loop command), cel count is set to the lower nibble of the cel count byte. Then bit 7 is tested:
  • if bit 7 is set, then bits 5 and 4 are read as a two-bit number (i.e. 0,1,2 or 3). This number is compared to the desired loop being set. If they match, the loop is OK and no flipping is done. If they don't match, the cel count byte is updated to indicate the new loop (i.e. bits 5 and 4 are changed to match the desired loop value). THEN bit 6 is checked; if set, all cels in the loop are flipped, if bit 6 is not set, the cels are not flipped.
  • if bit 7 is NOT set, then no further checks are made, and none of the cels are flipped.

What this means is that both bits 7 and 6 must be set for mirroring to work. My suspicion is that bit 6 was going to be used to do something else at one point, but they switched to the newer mirroring format before it was finished.

It also means mirroring would be limited to loops 0 through 3, and the max number of loops that could be in a view was 16. In the mirroring format added in v2.272, loops 0 through 7 could be mirrored, and up to 255 loops could be in a view.

Note that in this version, when a loop changes ALL cels are flipped immediately; in v2.272 and beyond, each cel is flipped individually when it is being drawn to the screen.

My educated guess is that they decided to change the format for mirroring so that more loops could be involved (8 loops available for mirroring instead of 4, and more than 16 loops allowed in a view) and also to minimize clock cycles (by only flipping when a cel is needed, instead of doing all of them every time a loop is changed).

This version also includes a number of changes that show up in v2.272 but not all of them, which confirms that it fits between v2.089 and v2.272.

Offline AGKorson

Re: AGI v2.230
« Reply #8 on: Yesterday at 10:27:50 PM »
The lower nibble of that byte is the cell count for the loop. That was the known bit, but interpreters like AGILE assume the whole byte is the cell count.

The original AGI interpreter source that we have from the SQ2 disk has this:

Code: [Select]
/* loop structure
*/
typedef struct loopstrc {
UBYTE numcels; /* number of cels in the loop */
WORD celofs[10]; /* offsets to the cels */
} LOOP;

which also implies the whole thing later became only for the cell count.

But for this earlier 2.230 version, only the bottom 4 bits were the cell count and the top 4-bits were other things, the top 2 being related to mirroring.
In v2.089, the entire byte was also the cel count, same as all versions other than 2.230. So I don't think there was an evolution from just the lower nibble to the entire byte as the cel count. It started as the entire byte, changed in v2.230 to just the lower nibble as an experiment in mirroring, then changed back to the entire byte when they went a different route for mirroring.

Offline Collector

Re: AGI v2.230
« Reply #9 on: Today at 12:15:22 AM »
interpreters like AGILE assume the whole byte is the cell count.

Currently, AGILE does not support this. I'll wait until we know a bit more about the full use of that top nibble before adding support.

There is probably not much more that I can contribute to AGILE at this point, but I finally gave up on recovering my GitHub account and disassociated my main email from my old account and setup a new account and forked your current repo over. Unfortunately no way to reclaim my contributions. At least it will make it easier to update my fork with your fix.

I did moved my existing BitBucket repos over, too. GitHub is a lot easier to deal with.
KQII Remake Pic

Offline pmkelly

Re: AGI v2.230
« Reply #10 on: Today at 02:01:36 AM »
I made a separate post about my recent reverse engineering experiments, but I fed the v2.230 interpreter to Codex and had it compare with the other versions.

Here is the commit it made, documenting the differences: https://github.com/peterkelly/agi-re/commit/4e53168e817f6dd9bfaad2687e990f896224ff7f


SMF 2.0.19 | SMF © 2021, Simple Machines
Simple Audio Video Embedder

Page created in 0.044 seconds with 22 queries.