// Wing Commander III - MVE Decompressor // code reconstruction by Mario Brito (mbrito@student.dei.uc.pt) #define SIZE (640*330) #define BYTE unsigned char BYTE frame[SIZE]; BYTE prev_frame[SIZE]; BYTE buffer1[SIZE]; BYTE buffer2[SIZE]; BYTE * part1; BYTE * part2; BYTE * part3; BYTE * part4; void video_wc3::bytecopy(BYTE * dest, BYTE * src, int count) { // I don't use memcpy because the memory locations often overlap and memcpy doesn't like that // It's not uncommon, for example, for dest = src+1, to turn byte A into pattern AAAAAAAA // This was originally repz movsb for (int i = 0; i < count; i ++) dest[i] = src[i]; } void video_wc3::wc3_build_frame (BYTE * prev, BYTE * current, int dimx, int dimy) { int frame_size = dimx * dimy; BYTE * pcurrent = current; BYTE pixel; int size = 0; BYTE flag = 0; int func; while (pcurrent < current + frame_size) { func = *part1++; size = 0; switch (func) { case 0: flag ^= 1; continue; case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: size = func; break; case 12: case 13: case 14: case 15: case 16: case 17: case 18: size += (func - 10); break; case 9: case 19: size = (*part2++); break; case 10: case 20: size = (part2[0] << 8) | part2[1]; part2 += 2; break; case 11: case 21: size = (part2[0] << 16) | (part2[1] << 8) | part2[2]; part2 += 3; break; } if (func < 12) { flag = flag ^ 1; if ( flag ) pcurrent += size; else { while (size > 0) { pixel = *part4++; *pcurrent++ = pixel; size --; } } } else { int x = (*part3 >> 4) & 0xf; int y = *part3 & 0xf; part3++; // extend sign if (x & 8) x |= 0xfffffff0; if (y & 8) y |= 0xfffffff0; bytecopy (pcurrent, x + y * dimx + prev - current + pcurrent, size); pcurrent += size; flag = 0; } } } void video_wc3::wc3_do_part4 (BYTE * dest, BYTE * src) { int func; int size; int offset; int byte1, byte2, byte3; for (;;) { func = *src++; if ( (func & 0x80) == 0 ) { offset = *src++; size = func & 3; bytecopy(dest, src, size); dest += size; src += size; size = ((func & 0x1c) >> 2) + 3; bytecopy (dest, dest - ( ((func & 0x60) << 3) + offset + 1 ), size); dest += size; } else if ( (func & 0x40) == 0 ) { byte1 = *src++; byte2 = *src++; size = byte1 >> 6; bytecopy (dest, src, size); dest += size; src += size; size = (func & 0x3f) + 4; bytecopy (dest, dest - (((byte1 & 0x3f) << 8) + byte2 + 1), size); dest += size; } else if ( (func & 0x20) == 0 ) { byte1 = *src++; byte2 = *src++; byte3 = *src++; size = func & 3; bytecopy (dest, src, size); dest += size; src += size; size = byte3 + 5 + ((func & 0xc) << 6); bytecopy (dest, dest - ((((func & 0x10) >> 4) << 0x10) + 1 + (byte1 << 8) + byte2), size); dest += size; } else { size = ((func & 0x1f) << 2) + 4; if (size > 0x70) break; bytecopy (dest, src, size); dest += size; src += size; } } size = func & 3; bytecopy(dest, src, size); dest += size; src += size; } void video_wc3::wc3_do_part1 (BYTE * dest, BYTE * src) { BYTE byte = *src++; BYTE ival = byte + 0x16; BYTE * ptr = src + byte*2; BYTE val = ival; int counter = 0; BYTE bits = *ptr++; while ( val != 0x16 ) { if ( (1 << counter) & bits ) val = src[byte + val - 0x17]; else val = src[val - 0x17]; if ( val < 0x16 ) { *dest++ = val; val = ival; } if (counter++ == 7) { counter = 0; bits = *ptr++; } } } void video_wc3::wc3_decode_frame (BYTE * current_frame, BYTE * compr, int dimx, int dimy) { BYTE * ptr; part1 = buffer1; part4 = buffer2; wc3_do_part1(part1, compr + ((compr[1] << 8) | compr[0]) ); part2 = compr + ((compr[3] << 8) | compr[2]); part3 = compr + ((compr[5] << 8) | compr[4]); ptr = compr + ((compr[7] << 8) | compr[6]); if (*ptr++ == 2) wc3_do_part4(part4, ptr); else part4 = ptr; wc3_build_frame(prev_frame, current_frame, dimx, dimy); memcpy (prev_frame, current_frame, dimx*dimy); }