diff options
Diffstat (limited to 'graphics')
-rw-r--r-- | graphics/VectorRendererSpec.cpp | 30 | ||||
-rw-r--r-- | graphics/module.mk | 6 | ||||
-rw-r--r-- | graphics/scaler.cpp | 52 | ||||
-rw-r--r-- | graphics/scaler.h | 3 | ||||
-rwxr-xr-x | graphics/scaler/Normal2xARM.s | 171 | ||||
-rw-r--r-- | graphics/scaler/scale2x.cpp | 38 | ||||
-rw-r--r-- | graphics/scaler/scale2x.h | 10 | ||||
-rwxr-xr-x | graphics/scaler/scale2xARM.s | 182 | ||||
-rw-r--r-- | graphics/scaler/scalebit.cpp | 12 | ||||
-rw-r--r-- | graphics/sjis.cpp | 112 | ||||
-rw-r--r-- | graphics/sjis.h | 79 | ||||
-rw-r--r-- | graphics/video/coktelvideo/coktelvideo.cpp | 1652 | ||||
-rw-r--r-- | graphics/video/coktelvideo/coktelvideo.h | 256 | ||||
-rw-r--r-- | graphics/video/smk_decoder.cpp | 6 | ||||
-rw-r--r-- | graphics/video/smk_decoder.h | 4 |
15 files changed, 1865 insertions, 748 deletions
diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp index b68f4822d8..a575ee8b94 100644 --- a/graphics/VectorRendererSpec.cpp +++ b/graphics/VectorRendererSpec.cpp @@ -366,22 +366,24 @@ applyScreenShading(GUI::ThemeEngine::ShadingStyle shadingStyle) { uint8 r, g, b; uint lum; - const uint32 shiftMask = (uint32)~( - (1 << _format.rShift) | (1 << _format.gShift) | (1 << _format.bShift) | (_format.aLoss == 8 ? 0 : (1 << _format.aShift))) >> 1; + // Mask to clear the last bit of every color component and all unused bits + const uint32 colorMask = ~((1 << _format.rShift) | (1 << _format.gShift) | (1 << _format.bShift) // R/G/B components + | (_format.aLoss == 8 ? 0 : (1 << _format.aShift)) // Alpha component + | ~(_alphaMask | _redMask | _greenMask | _blueMask)); // All unused bits if (shadingStyle == GUI::ThemeEngine::kShadingDim) { int n = (pixels + 7) >> 3; switch (pixels % 8) { case 0: do { - *ptr = (*ptr >> 1) & shiftMask; ++ptr; - case 7: *ptr = (*ptr >> 1) & shiftMask; ++ptr; - case 6: *ptr = (*ptr >> 1) & shiftMask; ++ptr; - case 5: *ptr = (*ptr >> 1) & shiftMask; ++ptr; - case 4: *ptr = (*ptr >> 1) & shiftMask; ++ptr; - case 3: *ptr = (*ptr >> 1) & shiftMask; ++ptr; - case 2: *ptr = (*ptr >> 1) & shiftMask; ++ptr; - case 1: *ptr = (*ptr >> 1) & shiftMask; ++ptr; + *ptr = (*ptr & colorMask) >> 1; ++ptr; + case 7: *ptr = (*ptr & colorMask) >> 1; ++ptr; + case 6: *ptr = (*ptr & colorMask) >> 1; ++ptr; + case 5: *ptr = (*ptr & colorMask) >> 1; ++ptr; + case 4: *ptr = (*ptr & colorMask) >> 1; ++ptr; + case 3: *ptr = (*ptr & colorMask) >> 1; ++ptr; + case 2: *ptr = (*ptr & colorMask) >> 1; ++ptr; + case 1: *ptr = (*ptr & colorMask) >> 1; ++ptr; } while (--n > 0); } @@ -421,10 +423,10 @@ calcGradient(uint32 pos, uint32 max) { PixelType output = 0; pos = (MIN(pos * Base::_gradientFactor, max) << 12) / max; - output |= ((_gradientStart&_redMask) + ((Base::_gradientBytes[0] * pos) >> 12)) & _redMask; - output |= ((_gradientStart&_greenMask) + ((Base::_gradientBytes[1] * pos) >> 12)) & _greenMask; - output |= ((_gradientStart&_blueMask) + ((Base::_gradientBytes[2] * pos) >> 12)) & _blueMask; - output |= ~(_redMask | _greenMask | _blueMask); + output |= ((_gradientStart & _redMask) + ((Base::_gradientBytes[0] * pos) >> 12)) & _redMask; + output |= ((_gradientStart & _greenMask) + ((Base::_gradientBytes[1] * pos) >> 12)) & _greenMask; + output |= ((_gradientStart & _blueMask) + ((Base::_gradientBytes[2] * pos) >> 12)) & _blueMask; + output |= _alphaMask; return output; } diff --git a/graphics/module.mk b/graphics/module.mk index 2387bb708c..ed14051243 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -37,6 +37,12 @@ MODULE_OBJS += \ scaler/scale3x.o \ scaler/scalebit.o +ifdef USE_ARM_SCALER_ASM +MODULE_OBJS += \ + scaler/scale2xARM.o \ + scaler/Normal2xARM.o +endif + ifndef DISABLE_HQ_SCALERS MODULE_OBJS += \ scaler/hq2x.o \ diff --git a/graphics/scaler.cpp b/graphics/scaler.cpp index 082258bfc0..8c677cc083 100644 --- a/graphics/scaler.cpp +++ b/graphics/scaler.cpp @@ -186,6 +186,57 @@ void Normal1x(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPit } #ifndef DISABLE_SCALERS +#ifdef USE_ARM_SCALER_ASM +extern "C" void Normal2xAspectMask(const uint8 *srcPtr, + uint32 srcPitch, + uint8 *dstPtr, + uint32 dstPitch, + int width, + int height, + uint32 mask); + +void Normal2xAspect(const uint8 *srcPtr, + uint32 srcPitch, + uint8 *dstPtr, + uint32 dstPitch, + int width, + int height) { + if (gBitFormat == 565) { + Normal2xAspectMask(srcPtr, + srcPitch, + dstPtr, + dstPitch, + width, + height, + 0x07e0F81F); + } else { + Normal2xAspectMask(srcPtr, + srcPitch, + dstPtr, + dstPitch, + width, + height, + 0x03e07C1F); + } +} + +extern "C" void Normal2xARM(const uint8 *srcPtr, + uint32 srcPitch, + uint8 *dstPtr, + uint32 dstPitch, + int width, + int height); + +void Normal2x(const uint8 *srcPtr, + uint32 srcPitch, + uint8 *dstPtr, + uint32 dstPitch, + int width, + int height) { + Normal2xARM(srcPtr, srcPitch, dstPtr, dstPitch, width, height); +} + +#else /** * Trivial nearest-neighbour 2x scaler. */ @@ -209,6 +260,7 @@ void Normal2x(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPit dstPtr += dstPitch << 1; } } +#endif /** * Trivial nearest-neighbour 3x scaler. diff --git a/graphics/scaler.h b/graphics/scaler.h index 4cea9ee2fb..bdae161bd1 100644 --- a/graphics/scaler.h +++ b/graphics/scaler.h @@ -45,6 +45,9 @@ DECLARE_SCALER(AdvMame2x); DECLARE_SCALER(AdvMame3x); DECLARE_SCALER(Normal1x); DECLARE_SCALER(Normal2x); +#ifdef USE_ARM_SCALER_ASM +DECLARE_SCALER(Normal2xAspect); +#endif DECLARE_SCALER(Normal3x); DECLARE_SCALER(Normal1o5x); DECLARE_SCALER(TV2x); diff --git a/graphics/scaler/Normal2xARM.s b/graphics/scaler/Normal2xARM.s new file mode 100755 index 0000000000..5de50d9c17 --- /dev/null +++ b/graphics/scaler/Normal2xARM.s @@ -0,0 +1,171 @@ +@ ScummVM Scumm Interpreter +@ Copyright (C) 2009 The ScummVM project +@ +@ This program is free software@ you can redistribute it and/or +@ modify it under the terms of the GNU General Public License +@ as published by the Free Software Foundation@ either version 2 +@ of the License, or (at your option) any later version. +@ +@ This program is distributed in the hope that it will be useful, +@ but WITHOUT ANY WARRANTY@ without even the implied warranty of +@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +@ GNU General Public License for more details. +@ +@ You should have received a copy of the GNU General Public License +@ along with this program@ if not, write to the Free Software +@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +@ +@ $URL$ +@ $Id$ +@ +@ @author Robin Watts (robin@wss.co.uk) + + .text + + .global Normal2xARM + .global Normal2xAspectMask + + + @ Assumes dst is aligned (so did the C) + @ Assumes 16bit (so did the C) +Normal2xARM: + @ r0 = src + @ r1 = srcPitch + @ r2 = dst + @ r3 = dstPitch + @ r4 = w + @ r5 = h + STMFD r13!,{r4-r11,r14} + LDR r4, [r13,#4*9] @ r4 = w + LDR r5, [r13,#4*10] @ r5 = h + ADD r12,r2, r3 + SUB r1, r1, r4, LSL #1 + SUB r6, r3, r4, LSL #2 + ADD r3, r3, r6 +yloop: + SUBS r14,r4, #4 + BLT thin +xloop: + LDRH r6, [r0], #2 + LDRH r7, [r0], #2 + LDRH r8, [r0], #2 + LDRH r9, [r0], #2 + ORR r6, r6, r6, LSL #16 + ORR r7, r7, r7, LSL #16 + ORR r8, r8, r8, LSL #16 + ORR r9, r9, r9, LSL #16 + STMIA r2!, {r6-r9} + STMIA r12!,{r6-r9} + SUBS r14,r14,#4 + BGE xloop + ADDS r14,r14,#4 + BNE thin + ADD r0, r0, r1 + ADD r2, r2, r3 + ADD r12,r12,r3 + SUBS r5, r5, #1 + BGT yloop + + LDMFD r13!,{r4-r11,PC} +thin: + LDRH r6, [r0], #2 + ORR r6, r6, r6, LSL #16 + STR r6, [r2], #4 + STR r6, [r12],#4 + SUBS r14,r14,#1 + BGT thin + ADD r0, r0, r1 + ADD r2, r2, r3 + ADD r12,r12,r3 + SUBS r5, r5, #1 + BGT yloop + + LDMFD r13!,{r4-r11,PC} + + + @ Horrid filter calculations + @ AAAAAAAAAAAABBBBBBBBBBBBCCCCCCCCCCCCDDDDDDDDDDDDEEEEEEEEEEEE + @ <-+-><-+-><-+-><-+-><-+-><-+-><-+-><-+-><-+-><-+-><-+-><-+-> + @ Ideal: A,A,(A*2+B*3)/5,B,(B*4+C)/5,C,C,(C+D*4)/5,D,(D*3+E*2)/5,E,E + @ Actual: A,A,(A*3+B*5)/8,B,(B*7+C)/8,C,C,(C+D*7)/8,D,(D*5+E*3)/8,E,E + + @ Assumes dst is aligned (so did the C) + @ Assumes 16bit (so did the C) +Normal2xAspectMask: + @ r0 = src + @ r1 = srcPitch + @ r2 = dst + @ r3 = dstPitch + @ r4 = w + @ r5 = h + @ r12= mask + STMFD r13!,{r4-r11,r14} + LDR r4, [r13,#4*9] @ r4 = w + LDR r5, [r13,#4*10] @ r5 = h + LDR r12,[r13,#4*11] @ r12= mask + MOV r11,#2 + SUB r11,r11,r1, LSL #2 @ r11= 2-srcPitch*4 + MOV r14,#4 + SUB r14,r14,r3, LSL #3 + SUB r14,r14,r3, LSL #1 + SUB r14,r14,r3 @ r14 = 4-dstPitch*11 +yloop_aspect: +xloop_aspect: + LDRH r6, [r0], r1 @ r6 = A + LDRH r7, [r0], r1 @ r7 = B + LDRH r8, [r0], r1 @ r8 = C + LDRH r9, [r0], r1 @ r9 = D + LDRH r10,[r0], r11 @ r10= E + ORR r6, r6, r6, LSL #16 @ r6 = output 0, 1 + ORR r7, r7, r7, LSL #16 @ r7 = output 3 + ORR r8, r8, r8, LSL #16 @ r8 = output 5,6 + ORR r9, r9, r9, LSL #16 @ r9 = output 8 + ORR r10,r10,r10,LSL #16 @ r10= output 10, 11 + STR r6, [r2], r3 @ output 0 (A) + STR r6, [r2], r3 @ output 1 (A) + AND r6, r6, r12 @ r6 = A split + ADD r6, r6, r6, LSL #1 @ r6 = A*3 + STR r7, [r2, r3] @ output 3 (B) + AND r7, r7, r12 @ r7 = B split + ADD r6, r6, r7 @ r6 = A*3 + B + ADD r6, r6, r7, LSL #2 @ r6 = A*3 + B*5 + AND r6, r12,r6, LSR #3 @ r6 = (A*3 + B*5)>>3 + ORR r6, r6, r6, ROR #16 @ r6 = output 2 + STR r6, [r2], r3, LSL #1 @ output 2 (A*3+B*5)>>3 + RSB r7, r7, r7, LSL #3 @ r7 = B*7 + AND r6, r8, r12 @ r6 = C split + ADD r7, r7, r6 @ r7 = B*7+C + AND r7, r12,r7, LSR #3 @ r7 = (B*7 + C)>>3 + ORR r7, r7, r7, ROR #16 @ r7 = output 4 + STR r7, [r2], r3 @ output 4 (B*7+C)>>3 + STR r8, [r2], r3 @ output 5 (C) + STR r8, [r2], r3 @ output 6 (C) + STR r9, [r2, r3] @ output 8 (D) + AND r9, r9, r12 @ r9 = D split + RSB r7, r9, r9, LSL #3 @ r7 = D*7 + ADD r6, r6, r7 @ r6 = C+D*7 + AND r6, r12,r6, LSR #3 @ r6 = (C + D*7)>>3 + ORR r6, r6, r6, ROR #16 @ r6 = output 7 + STR r6, [r2], r3, LSL #1 @ output 7 (C+D*7)>>3 + ADD r9, r9, r9, LSL #2 @ r9 = D*5 + AND r6, r10,r12 @ r6 = E split + ADD r9, r9, r6 @ r9 = D*5+E + ADD r9, r9, r6, LSL #1 @ r9 = D*5+E*3 + AND r9, r12,r9, LSR #3 @ r9 = (D*5 + E*3)>>3 + ORR r9, r9, r9, ROR #16 @ r9 = output 9 + STR r9, [r2], r3 @ output 9 (D*5+E*3)>>3 + STR r10,[r2], r3 @ output 10 (E) + STR r10,[r2], r14 @ output 11 (E) + SUBS r4, r4, #1 + BGT xloop_aspect + LDR r4, [r13,#4*9] @ r4 = w + ADD r0, r0, r1, LSL #2 + ADD r0, r0, r1 + SUB r0, r0, r4, LSL #1 + ADD r2, r2, r3, LSL #3 + ADD r2, r2, r3, LSL #2 + SUB r2, r2, r4, LSL #2 + SUBS r5, r5, #5 + BGT yloop_aspect + + LDMFD r13!,{r4-r11,PC} diff --git a/graphics/scaler/scale2x.cpp b/graphics/scaler/scale2x.cpp index 226d379c91..877bbb9509 100644 --- a/graphics/scaler/scale2x.cpp +++ b/graphics/scaler/scale2x.cpp @@ -158,7 +158,7 @@ void scale2x_32_def(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_ui /***************************************************************************/ /* Scale2x MMX implementation */ -#if defined(__GNUC__) && defined(__i386__) +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) /* * Apply the Scale2x effect at a single row. @@ -205,7 +205,7 @@ static inline void scale2x_8_mmx_single(scale2x_uint8* dst, const scale2x_uint8* __asm__ __volatile__( /* central runs */ - "shrl $3, %4\n" + "shr $3, %4\n" "jz 1f\n" "0:\n" @@ -261,12 +261,12 @@ static inline void scale2x_8_mmx_single(scale2x_uint8* dst, const scale2x_uint8* "movq %%mm3, 8(%3)\n" /* next */ - "addl $8, %0\n" - "addl $8, %1\n" - "addl $8, %2\n" - "addl $16, %3\n" + "add $8, %0\n" + "add $8, %1\n" + "add $8, %2\n" + "add $16, %3\n" - "decl %4\n" + "dec %4\n" "jnz 0b\n" "1:\n" @@ -283,7 +283,7 @@ static inline void scale2x_16_mmx_single(scale2x_uint16* dst, const scale2x_uint __asm__ __volatile__( /* central runs */ - "shrl $2, %4\n" + "shr $2, %4\n" "jz 1f\n" "0:\n" @@ -339,12 +339,12 @@ static inline void scale2x_16_mmx_single(scale2x_uint16* dst, const scale2x_uint "movq %%mm3, 8(%3)\n" /* next */ - "addl $8, %0\n" - "addl $8, %1\n" - "addl $8, %2\n" - "addl $16, %3\n" + "add $8, %0\n" + "add $8, %1\n" + "add $8, %2\n" + "add $16, %3\n" - "decl %4\n" + "dec %4\n" "jnz 0b\n" "1:\n" @@ -361,7 +361,7 @@ static inline void scale2x_32_mmx_single(scale2x_uint32* dst, const scale2x_uint __asm__ __volatile__( /* central runs */ - "shrl $1, %4\n" + "shr $1, %4\n" "jz 1f\n" "0:\n" @@ -417,12 +417,12 @@ static inline void scale2x_32_mmx_single(scale2x_uint32* dst, const scale2x_uint "movq %%mm3, 8(%3)\n" /* next */ - "addl $8, %0\n" - "addl $8, %1\n" - "addl $8, %2\n" - "addl $16, %3\n" + "add $8, %0\n" + "add $8, %1\n" + "add $8, %2\n" + "add $16, %3\n" - "decl %4\n" + "dec %4\n" "jnz 0b\n" "1:\n" diff --git a/graphics/scaler/scale2x.h b/graphics/scaler/scale2x.h index 2101790905..cefa14f22a 100644 --- a/graphics/scaler/scale2x.h +++ b/graphics/scaler/scale2x.h @@ -33,7 +33,7 @@ void scale2x_8_def(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8 void scale2x_16_def(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count); void scale2x_32_def(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count); -#if defined(__GNUC__) && defined(__i386__) +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) void scale2x_8_mmx(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count); void scale2x_16_mmx(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count); @@ -52,5 +52,13 @@ static inline void scale2x_mmx_emms(void) #endif +#if defined(USE_ARM_SCALER_ASM) + +extern "C" void scale2x_8_arm(scale2x_uint8* dst0, scale2x_uint8* dst1, const scale2x_uint8* src0, const scale2x_uint8* src1, const scale2x_uint8* src2, unsigned count); +extern "C" void scale2x_16_arm(scale2x_uint16* dst0, scale2x_uint16* dst1, const scale2x_uint16* src0, const scale2x_uint16* src1, const scale2x_uint16* src2, unsigned count); +extern "C" void scale2x_32_arm(scale2x_uint32* dst0, scale2x_uint32* dst1, const scale2x_uint32* src0, const scale2x_uint32* src1, const scale2x_uint32* src2, unsigned count); + +#endif + #endif diff --git a/graphics/scaler/scale2xARM.s b/graphics/scaler/scale2xARM.s new file mode 100755 index 0000000000..3ca237e02d --- /dev/null +++ b/graphics/scaler/scale2xARM.s @@ -0,0 +1,182 @@ +@ ScummVM Scumm Interpreter +@ Copyright (C) 2009 The ScummVM project +@ +@ This program is free software@ you can redistribute it and/or +@ modify it under the terms of the GNU General Public License +@ as published by the Free Software Foundation@ either version 2 +@ of the License, or (at your option) any later version. +@ +@ This program is distributed in the hope that it will be useful, +@ but WITHOUT ANY WARRANTY@ without even the implied warranty of +@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +@ GNU General Public License for more details. +@ +@ You should have received a copy of the GNU General Public License +@ along with this program@ if not, write to the Free Software +@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +@ +@ $URL$ +@ $Id$ +@ +@ @author Robin Watts (robin@wss.co.uk) + + .text + + .global scale2x_8_arm + .global scale2x_16_arm + .global scale2x_32_arm + + @ r0 = dst0 + @ r1 = dst1 + @ r2 = src + @ r3 = src_prev + @ r4 = src_next + @ r5 = len + + + @ We hold: r10 B + @ r8 r14 r7 D E F + @ r12 H +scale2x_8_arm: + STMFD r13!,{r4-r5,r7-r8,r10-r11,r14} + LDR r4, [r13,#4*7] + LDR r5, [r13,#4*8] + + LDRB r14,[r2], #1 + CMP r3, #0 @ Set NE + @ Stall on Xscale + MOV r8, r14 + B loop8 +same8: + STRB r14,[r0], #1 + STRB r14,[r0], #1 + STRB r14,[r1], #1 + STRB r14,[r1], #1 + SUBS r5, r5, #1 + MOV r8, r14 + MOV r14,r7 + BLT end8 +loop8: + LDRNEB r7, [r3], #1 @ As long as we aren't on the last pixel + LDRB r10,[r2], #1 + LDRB r12,[r4], #1 + @ Stall on Xscale + CMP r7, r8 + CMPNE r10,r12 + BEQ same8 + CMP r8, r10 + STREQB r8, [r0], #1 + STRNEB r14,[r0], #1 + CMP r10,r7 + STREQB r7, [r0], #1 + STRNEB r14,[r0], #1 + CMP r8, r12 + STREQB r8, [r1], #1 + STRNEB r14,[r1], #1 + CMP r12,r7 + STREQB r7, [r1], #1 + STRNEB r14,[r1], #1 + + SUBS r5, r5, #1 + MOV r8, r14 + MOV r14,r7 + BGE loop8 +end8: + + LDMFD r13!,{r4-r5,r7-r8,r10-r11,PC} + +scale2x_16_arm: + STMFD r13!,{r4-r5,r7-r8,r10-r11,r14} + LDR r4, [r13,#4*7] + LDR r5, [r13,#4*8] + + LDRH r14,[r3], #2 + CMP r3, #0 @ Set NE + @ Stall on Xscale + MOV r8, r14 + B loop16 +same16: + STRH r14,[r0], #2 + STRH r14,[r0], #2 + STRH r14,[r1], #2 + STRH r14,[r1], #2 + SUBS r5, r5, #1 + MOV r8, r14 + MOV r14,r7 + BLT end16 +loop16: + LDRNEH r7, [r3], #2 @ As long as we aren't on the last pixel + LDRH r10,[r2], #2 + LDRH r12,[r4], #2 + @ Stall on Xscale + CMP r7, r8 + CMPNE r10,r12 + BEQ same16 + CMP r8, r10 + STREQH r8, [r0], #2 + STRNEH r14,[r0], #2 + CMP r10,r7 + STREQH r7, [r0], #2 + STRNEH r14,[r0], #2 + CMP r8, r12 + STREQH r8, [r1], #2 + STRNEH r14,[r1], #2 + CMP r12,r7 + STREQH r7, [r1], #2 + STRNEH r14,[r1], #2 + + SUBS r5, r5, #1 + MOV r8, r14 + MOV r14,r7 + BGE loop16 +end16: + + LDMFD r13!,{r4-r5,r7-r8,r10-r11,PC} + +scale2x_32_arm: + STMFD r13!,{r4-r5,r7-r8,r10-r11,r14} + LDR r4, [r13,#4*7] + LDR r5, [r13,#4*8] + + LDR r14,[r3], #4 + CMP r3, #0 @ Set NE + @ Stall on Xscale + MOV r8, r14 + B loop32 +same32: + STR r14,[r0], #4 + STR r14,[r0], #4 + STR r14,[r1], #4 + STR r14,[r1], #4 + SUBS r5, r5, #1 + MOV r8, r14 + MOV r14,r7 + BLT end32 +loop32: + LDRNE r7, [r3], #4 @ As long as we aren't on the last pixel + LDR r10,[r2], #4 + LDR r12,[r4], #4 + @ Stall on Xscale + CMP r7, r8 + CMPNE r10,r12 + BEQ same32 + CMP r8, r10 + STREQ r8, [r0], #4 + STRNE r14,[r0], #4 + CMP r10,r7 + STREQ r7, [r0], #4 + STRNE r14,[r0], #4 + CMP r8, r12 + STREQ r8, [r1], #4 + STRNE r14,[r1], #4 + CMP r12,r7 + STREQ r7, [r1], #4 + STRNE r14,[r1], #4 + + SUBS r5, r5, #1 + MOV r8, r14 + MOV r14,r7 + BGE loop32 +end32: + + LDMFD r13!,{r4-r5,r7-r8,r10-r11,PC} diff --git a/graphics/scaler/scalebit.cpp b/graphics/scaler/scalebit.cpp index a6fd8df58f..6a3b47b0f5 100644 --- a/graphics/scaler/scalebit.cpp +++ b/graphics/scaler/scalebit.cpp @@ -55,10 +55,14 @@ static inline void stage_scale2x(void* dst0, void* dst1, const void* src0, const void* src1, const void* src2, unsigned pixel, unsigned pixel_per_row) { switch (pixel) { -#if defined(__GNUC__) && defined(__i386__) +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) case 1 : scale2x_8_mmx(DST(8,0), DST(8,1), SRC(8,0), SRC(8,1), SRC(8,2), pixel_per_row); break; case 2 : scale2x_16_mmx(DST(16,0), DST(16,1), SRC(16,0), SRC(16,1), SRC(16,2), pixel_per_row); break; case 4 : scale2x_32_mmx(DST(32,0), DST(32,1), SRC(32,0), SRC(32,1), SRC(32,2), pixel_per_row); break; +#elif defined(USE_ARM_SCALER_ASM) + case 1 : scale2x_8_arm(DST(8,0), DST(8,1), SRC(8,0), SRC(8,1), SRC(8,2), pixel_per_row); break; + case 2 : scale2x_16_arm(DST(16,0), DST(16,1), SRC(16,0), SRC(16,1), SRC(16,2), pixel_per_row); break; + case 4 : scale2x_32_arm(DST(32,0), DST(32,1), SRC(32,0), SRC(32,1), SRC(32,2), pixel_per_row); break; #else case 1 : scale2x_8_def(DST(8,0), DST(8,1), SRC(8,0), SRC(8,1), SRC(8,2), pixel_per_row); break; case 2 : scale2x_16_def(DST(16,0), DST(16,1), SRC(16,0), SRC(16,1), SRC(16,2), pixel_per_row); break; @@ -125,7 +129,7 @@ static void scale2x(void* void_dst, unsigned dst_slice, const void* void_src, un --count; } -#if defined(__GNUC__) && defined(__i386__) +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) scale2x_mmx_emms(); #endif } @@ -225,7 +229,7 @@ static void scale4x_buf(void* void_dst, unsigned dst_slice, void* void_mid, unsi --count; } -#if defined(__GNUC__) && defined(__i386__) +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) scale2x_mmx_emms(); #endif } @@ -303,7 +307,7 @@ int scale_precondition(unsigned scale, unsigned pixel, unsigned width, unsigned break; } -#if defined(__GNUC__) && defined(__i386__) +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) switch (scale) { case 2 : case 4 : diff --git a/graphics/sjis.cpp b/graphics/sjis.cpp index 5392a1c9a4..405d8622c2 100644 --- a/graphics/sjis.cpp +++ b/graphics/sjis.cpp @@ -27,17 +27,33 @@ #ifdef GRAPHICS_SJIS_H #include "common/debug.h" +#include "common/archive.h" +#include "common/endian.h" namespace Graphics { -bool FontTowns::loadFromStream(Common::ReadStream &stream) { - for (uint i = 0; i < (kFontRomSize / 2); ++i) - _fontData[i] = stream.readUint16BE(); - return !stream.err(); +FontSJIS *FontSJIS::createFont(const Common::Platform platform) { + FontSJIS *ret = 0; + + // Try the font ROM of the specified platform + if (platform == Common::kPlatformFMTowns) { + ret = new FontTowns(); + if (ret && ret->loadData()) + return ret; + delete ret; + } + + // Try ScummVM's font. + ret = new FontSjisSVM(); + if (ret && ret->loadData()) + return ret; + delete ret; + + return 0; } template<typename Color> -void FontTowns::drawCharInternOutline(const uint16 *glyph, uint8 *dst, int pitch, Color c1, Color c2) const { +void FontSJIS16x16::drawCharInternOutline(const uint16 *glyph, uint8 *dst, int pitch, Color c1, Color c2) const { uint32 outlineGlyph[18]; memset(outlineGlyph, 0, sizeof(outlineGlyph)); @@ -72,7 +88,7 @@ void FontTowns::drawCharInternOutline(const uint16 *glyph, uint8 *dst, int pitch } template<typename Color> -void FontTowns::drawCharIntern(const uint16 *glyph, uint8 *dst, int pitch, Color c1) const { +void FontSJIS16x16::drawCharIntern(const uint16 *glyph, uint8 *dst, int pitch, Color c1) const { for (int y = 0; y < 16; ++y) { Color *lineBuf = (Color *)dst; uint16 line = *glyph++; @@ -88,8 +104,12 @@ void FontTowns::drawCharIntern(const uint16 *glyph, uint8 *dst, int pitch, Color } } -void FontTowns::drawChar(void *dst, uint16 ch, int pitch, int bpp, uint32 c1, uint32 c2) const { - const uint16 *glyphSource = _fontData + sjisToChunk(ch & 0xFF, ch >> 8) * 16; +void FontSJIS16x16::drawChar(void *dst, uint16 ch, int pitch, int bpp, uint32 c1, uint32 c2) const { + const uint16 *glyphSource = getCharData(ch); + if (!glyphSource) { + warning("FontSJIS16x16::drawChar: Font does not offer data for %02X %02X", ch & 0xFF, ch >> 8); + return; + } if (bpp == 1) { if (!_outlineEnabled) @@ -106,7 +126,25 @@ void FontTowns::drawChar(void *dst, uint16 ch, int pitch, int bpp, uint32 c1, ui } } -uint FontTowns::sjisToChunk(uint8 f, uint8 s) { +// FM-TOWNS ROM font + +bool FontTowns::loadData() { + Common::SeekableReadStream *data = SearchMan.createReadStreamForMember("FMT_FNT.ROM"); + if (!data) + return false; + + for (uint i = 0; i < (kFontRomSize / 2); ++i) + _fontData[i] = data->readUint16BE(); + + bool retValue = !data->err(); + delete data; + return retValue; +} + +const uint16 *FontTowns::getCharData(uint16 ch) const { + uint8 f = ch & 0xFF; + uint8 s = ch >> 8; + // copied from scumm\charset.cpp enum { KANA = 0, @@ -188,7 +226,61 @@ uint FontTowns::sjisToChunk(uint8 f, uint8 s) { } debug(6, "Kanji: %c%c f 0x%x s 0x%x base 0x%x c %d p %d chunk %d cr %d index %d", f, s, f, s, base, c, p, chunk, cr, ((chunk_f + chunk) * 32 + (s - base)) + cr); - return ((chunk_f + chunk) * 32 + (s - base)) + cr; + return _fontData + (((chunk_f + chunk) * 32 + (s - base)) + cr) * 16; +} + +// ScummVM SJIS font + +bool FontSjisSVM::loadData() { + Common::SeekableReadStream *data = SearchMan.createReadStreamForMember("SJIS.FNT"); + if (!data) + return false; + + uint32 magic1 = data->readUint32BE(); + uint32 magic2 = data->readUint32BE(); + + if (magic1 != MKID_BE('SCVM') || magic2 != MKID_BE('SJIS')) { + delete data; + return false; + } + + uint32 version = data->readUint32BE(); + if (version != 1) { + delete data; + return false; + } + uint numChars = data->readUint16BE(); + + _fontData = new uint16[numChars * 16]; + assert(_fontData); + + for (uint i = 0; i < numChars * 16; ++i) + _fontData[i] = data->readUint16BE(); + + bool retValue = !data->err(); + delete data; + return retValue; +} + +const uint16 *FontSjisSVM::getCharData(uint16 c) const { + const uint8 fB = c & 0xFF; + const uint8 sB = c >> 8; + + // We only allow 2 byte SJIS characters. + if (fB <= 0x80 || fB >= 0xF0 || (fB >= 0xA0 && fB <= 0xDF) || sB == 0x7F) + return 0; + + int base = fB; + base -= 0x81; + if (base >= 0x5F) + base -= 0x40; + + int index = sB; + index -= 0x40; + if (index >= 0x3F) + --index; + + return _fontData + (base * 0xBC + index) * 16; } } // end of namespace Graphics diff --git a/graphics/sjis.h b/graphics/sjis.h index f7321742af..b5d997a19a 100644 --- a/graphics/sjis.h +++ b/graphics/sjis.h @@ -35,6 +35,7 @@ #include "common/scummsys.h" #include "common/stream.h" +#include "common/util.h" #include "graphics/surface.h" @@ -50,6 +51,24 @@ public: virtual ~FontSJIS() {} /** + * Creates the first SJIS font, which ROM/font file is present. + * It will also call loadData, so the user can just start + * using the font. + * + * It'll prefer the platform specific ROM file, when platform + * is set to a value, which's font ROM is supported. + * So far that is only kPlatformFMTowns. + * + * The last file tried is ScummVM's SJIS.FNT file. + */ + static FontSJIS *createFont(const Common::Platform platform = Common::kPlatformUnknown); + + /** + * Load the font data. + */ + virtual bool loadData() = 0; + + /** * Enable outline drawing. * * After changing outline state, getFontHeight and getFontWidth might return @@ -69,6 +88,10 @@ public: /** * Draws a SJIS encoded character on the given surface. + * + * TODO: Currently there is no assurance, that this method will only draw within + * the surface boundaries. Thus the caller has to assure the glyph will fit at + * the specified position. */ void drawChar(Graphics::Surface &dst, uint16 ch, int x, int y, uint32 c1, uint32 c2) const { drawChar(dst.getBasePtr(x, y), ch, c1, c2, dst.pitch, dst.bytesPerPixel); @@ -78,7 +101,7 @@ public: * Draws a SJIS char on the given raw buffer. * * @param dst pointer to the destination - * @param ch character to draw + * @param ch character to draw (in little endian) * @param pitch pitch of the destination buffer (size in *bytes*) * @param bpp bytes per pixel of the destination buffer * @param c1 forground color @@ -88,18 +111,11 @@ public: }; /** - * FM-TOWNS ROM based SJIS compatible font. - * - * This is used in KYRA and SCI. + * A base class to render 16x16 monochrome SJIS fonts. */ -class FontTowns : public FontSJIS { +class FontSJIS16x16 : public FontSJIS { public: - FontTowns() : _outlineEnabled(false) {} - - /** - * Loads the ROM data from the given read stream. - */ - bool loadFromStream(Common::ReadStream &stream); + FontSJIS16x16() : _outlineEnabled(false) {} void enableOutline(bool enable) { _outlineEnabled = enable; } @@ -115,14 +131,51 @@ private: template<typename Color> void drawCharIntern(const uint16 *glyph, uint8 *dst, int pitch, Color c1) const; + bool _outlineEnabled; +protected: + + virtual const uint16 *getCharData(uint16 c) const = 0; +}; + +/** + * FM-TOWNS ROM based SJIS compatible font. + * + * This is used in KYRA and SCI. + */ +class FontTowns : public FontSJIS16x16 { +public: + /** + * Loads the ROM data from "FMT_FNT.ROM". + */ + bool loadData(); + +private: enum { kFontRomSize = 262144 }; - bool _outlineEnabled; uint16 _fontData[kFontRomSize / 2]; - static uint sjisToChunk(uint8 low, uint8 high); + const uint16 *getCharData(uint16 c) const; +}; + +/** + * Our custom SJIS FNT. + */ +class FontSjisSVM : public FontSJIS16x16 { +public: + FontSjisSVM() : _fontData(0) {} + ~FontSjisSVM() { delete[] _fontData; } + + /** + * Load the font data from "SJIS.FNT". + */ + bool loadData(); + +private: + uint16 *_fontData; + + const uint16 *getCharData(uint16 c) const; }; // TODO: Consider adding support for PC98 ROM diff --git a/graphics/video/coktelvideo/coktelvideo.cpp b/graphics/video/coktelvideo/coktelvideo.cpp index c88c5beee2..063ad190e6 100644 --- a/graphics/video/coktelvideo/coktelvideo.cpp +++ b/graphics/video/coktelvideo/coktelvideo.cpp @@ -41,52 +41,79 @@ Imd::~Imd() { clear(); } -bool Imd::load(Common::SeekableReadStream &stream) { - unload(); +uint32 Imd::getFeatures() const { + return _features; +} - _stream = &stream; +uint16 Imd::getFlags() const { + return _flags; +} - // Version - uint16 handle = _stream->readUint16LE(); - _version = _stream->readByte(); +int16 Imd::getX() const { + return _x; +} - // Version checking - if ((handle != 0) || (_version < 2)) { - warning("IMD Version incorrect (%d,%X)", handle, _version); - unload(); - return false; - } +int16 Imd::getY() const { + return _y; +} - // Rest header - _features = _stream->readByte(); - _framesCount = _stream->readUint16LE(); - _x = _stream->readSint16LE(); - _y = _stream->readSint16LE(); - _width = _stream->readSint16LE(); - _height = _stream->readSint16LE(); - _flags = _stream->readUint16LE(); - _firstFramePos = _stream->readUint16LE(); +int16 Imd::getWidth() const { + return _width; +} - // IMDs always have video - _features |= kFeaturesVideo; - // IMDs always have palettes - _features |= kFeaturesPalette; +int16 Imd::getHeight() const { + return _height; +} - // Palette - _stream->read((byte *) _palette, 768); +uint16 Imd::getFramesCount() const { + return _framesCount; +} + +uint16 Imd::getCurrentFrame() const { + return _curFrame; +} + +int16 Imd::getFrameRate() const { + if (!_hasSound) + return _frameRate; + return 1000 / (_soundSliceLength >> 16); +} + +uint32 Imd::getSyncLag() const { + return _skipFrames; +} + +const byte *Imd::getPalette() const { + return _palette; +} + +bool Imd::getFrameCoords(int16 frame, + int16 &x, int16 &y, int16 &width, int16 &height) { + + return false; +} + +bool Imd::hasExtraData(const char *fileName) const { + return false; +} + +Common::MemoryReadStream *Imd::getExtraData(const char *fileName) { + return 0; +} + +bool Imd::loadCoordinates() { // Standard coordinates if (_version >= 3) { _stdX = _stream->readUint16LE(); if (_stdX > 1) { warning("IMD: More than one standard coordinate quad found (%d)", _stdX); - unload(); return false; } if (_stdX != 0) { - _stdX = _stream->readSint16LE(); - _stdY = _stream->readSint16LE(); - _stdWidth = _stream->readSint16LE(); + _stdX = _stream->readSint16LE(); + _stdY = _stream->readSint16LE(); + _stdWidth = _stream->readSint16LE(); _stdHeight = _stream->readSint16LE(); _features |= kFeaturesStdCoords; } else @@ -94,8 +121,14 @@ bool Imd::load(Common::SeekableReadStream &stream) { } else _stdX = -1; - // Offset to frame positions table - uint32 framesPosPos = 0; + return true; +} + +bool Imd::loadFrameTableOffsets(uint32 &framesPosPos, uint32 &framesCoordsPos) { + framesPosPos = 0; + framesCoordsPos = 0; + + // Frame positions if (_version >= 4) { framesPosPos = _stream->readUint32LE(); if (framesPosPos != 0) { @@ -105,15 +138,43 @@ bool Imd::load(Common::SeekableReadStream &stream) { } } - // Offset to frame coordinates - uint32 framesCoordsPos = 0; + // Frame coordinates if (_features & kFeaturesFrameCoords) framesCoordsPos = _stream->readUint32LE(); - // Sound + return true; +} + +bool Imd::assessVideoProperties() { + // Sizes of the frame data and extra video buffer + if (_features & kFeaturesDataSize) { + _frameDataSize = _stream->readUint16LE(); + if (_frameDataSize == 0) { + _frameDataSize = _stream->readUint32LE(); + _vidBufferSize = _stream->readUint32LE(); + } else + _vidBufferSize = _stream->readUint16LE(); + } else { + _frameDataSize = _width * _height + 500; + if (!(_flags & 0x100) || (_flags & 0x1000)) + _vidBufferSize = _frameDataSize; + } + + // Allocating working memory + _frameData = new byte[_frameDataSize + 500]; + assert(_frameData); + memset(_frameData, 0, _frameDataSize + 500); + _vidBuffer = new byte[_vidBufferSize + 500]; + assert(_vidBuffer); + memset(_vidBuffer, 0, _vidBufferSize + 500); + + return true; +} + +bool Imd::assessAudioProperties() { if (_features & kFeaturesSound) { - _soundFreq = _stream->readSint16LE(); - _soundSliceSize = _stream->readSint16LE(); + _soundFreq = _stream->readSint16LE(); + _soundSliceSize = _stream->readSint16LE(); _soundSlicesCount = _stream->readSint16LE(); if (_soundFreq < 0) @@ -123,8 +184,7 @@ bool Imd::load(Common::SeekableReadStream &stream) { _soundSlicesCount = -_soundSlicesCount - 1; if (_soundSlicesCount > 40) { - warning("IMD: More than 40 sound slices found (%d)", _soundSlicesCount); - unload(); + warning("Imd::load(): More than 40 sound slices found (%d)", _soundSlicesCount); return false; } @@ -133,57 +193,103 @@ bool Imd::load(Common::SeekableReadStream &stream) { _frameLength = _soundSliceLength >> 16; _soundStage = 1; - _hasSound = true; + _hasSound = true; _audioStream = Audio::makeAppendableAudioStream(_soundFreq, 0); } else _frameLength = 1000 / _frameRate; - // Sizes of the frame data and extra video buffer - if (_features & kFeaturesDataSize) { - _frameDataSize = _stream->readUint16LE(); - if (_frameDataSize == 0) { - _frameDataSize = _stream->readUint32LE(); - _vidBufferSize = _stream->readUint32LE(); - } else - _vidBufferSize = _stream->readUint16LE(); - } else { - _frameDataSize = _width * _height + 500; - if (!(_flags & 0x100) || (_flags & 0x1000)) - _vidBufferSize = _frameDataSize; - } + return true; +} - // Frame positions table +bool Imd::loadFrameTables(uint32 framesPosPos, uint32 framesCoordsPos) { + // Positions table if (_framesPos) { _stream->seek(framesPosPos, SEEK_SET); for (int i = 0; i < _framesCount; i++) _framesPos[i] = _stream->readUint32LE(); } - // Frame coordinates table + // Coordinates table if (_features & kFeaturesFrameCoords) { _stream->seek(framesCoordsPos, SEEK_SET); _frameCoords = new Coord[_framesCount]; assert(_frameCoords); for (int i = 0; i < _framesCount; i++) { - _frameCoords[i].left = _stream->readSint16LE(); - _frameCoords[i].top = _stream->readSint16LE(); - _frameCoords[i].right = _stream->readSint16LE(); + _frameCoords[i].left = _stream->readSint16LE(); + _frameCoords[i].top = _stream->readSint16LE(); + _frameCoords[i].right = _stream->readSint16LE(); _frameCoords[i].bottom = _stream->readSint16LE(); } } + return true; +} + +bool Imd::load(Common::SeekableReadStream &stream) { + unload(); + + _stream = &stream; + + uint16 handle; + + handle = _stream->readUint16LE(); + _version = _stream->readByte(); + + // Version checking + if ((handle != 0) || (_version < 2)) { + warning("Imd::load(): Version incorrect (%d,%X)", handle, _version); + unload(); + return false; + } + + // Rest header + _features = _stream->readByte(); + _framesCount = _stream->readUint16LE(); + _x = _stream->readSint16LE(); + _y = _stream->readSint16LE(); + _width = _stream->readSint16LE(); + _height = _stream->readSint16LE(); + _flags = _stream->readUint16LE(); + _firstFramePos = _stream->readUint16LE(); + + // IMDs always have video + _features |= kFeaturesVideo; + // IMDs always have palettes + _features |= kFeaturesPalette; + + // Palette + _stream->read((byte *) _palette, 768); + + if (!loadCoordinates()) { + unload(); + return false; + } + + uint32 framesPosPos, frameCoordsPos; + if (!loadFrameTableOffsets(framesPosPos, frameCoordsPos)) { + unload(); + return false; + } + + if (!assessAudioProperties()) { + unload(); + return false; + } + + if (!assessVideoProperties()) { + unload(); + return false; + } + + if (!loadFrameTables(framesPosPos, frameCoordsPos)) { + unload(); + return false; + } + // Seek to the first frame _stream->seek(_firstFramePos, SEEK_SET); - // Allocating working memory - _frameData = new byte[_frameDataSize + 500]; - assert(_frameData); - memset(_frameData, 0, _frameDataSize + 500); - _vidBuffer = new byte[_vidBufferSize + 500]; - assert(_vidBuffer); - memset(_vidBuffer, 0, _vidBufferSize + 500); - return true; } @@ -195,7 +301,7 @@ void Imd::setFrameRate(int16 frameRate) { if (frameRate == 0) frameRate = 1; - _frameRate = frameRate; + _frameRate = frameRate; _frameLength = 1000 / _frameRate; } @@ -213,11 +319,11 @@ void Imd::setXY(int16 x, int16 y) { for (int i = 0; i < _framesCount; i++) { if (_frameCoords[i].left != -1) { if (x >= 0) { - _frameCoords[i].left = _frameCoords[i].left - _x + x; + _frameCoords[i].left = _frameCoords[i].left - _x + x; _frameCoords[i].right = _frameCoords[i].right - _x + x; } if (y >= 0) { - _frameCoords[i].top = _frameCoords[i].top - _y + y; + _frameCoords[i].top = _frameCoords[i].top - _y + y; _frameCoords[i].bottom = _frameCoords[i].bottom - _y + y; } } @@ -234,8 +340,8 @@ void Imd::setVideoMemory(byte *vidMem, uint16 width, uint16 height) { deleteVidMem(); _hasOwnVidMem = false; - _vidMem = vidMem; - _vidMemWidth = width; + _vidMem = vidMem; + _vidMemWidth = width; _vidMemHeight = height; } @@ -245,12 +351,17 @@ void Imd::setVideoMemory() { if ((_width > 0) && (_height > 0)) { setXY(0, 0); _hasOwnVidMem = true; - _vidMem = new byte[_width * _height]; - _vidMemWidth = _width; + _vidMem = new byte[_width * _height]; + _vidMemWidth = _width; _vidMemHeight = _height; + + memset(_vidMem, 0, _width * _height); } } +void Imd::setDoubleMode(bool doubleMode) { +} + void Imd::enableSound(Audio::Mixer &mixer) { // Only possible on the first frame if (_curFrame > 0) @@ -270,7 +381,7 @@ void Imd::disableSound() { delete _audioStream; _audioStream = 0; - _soundStage = 0; + _soundStage = 0; } _soundEnabled = false; _mixer = 0; @@ -313,8 +424,9 @@ void Imd::seekFrame(int32 frame, int16 whence, bool restart) { } else if (restart && (_soundStage == 0)) { for (int i = ((frame > _curFrame) ? _curFrame : 0); i <= frame; i++) processFrame(i); + return; } else - error("Frame %d is not directly accessible", frame); + error("Imd::seekFrame(): Frame %d is not directly accessible", frame); // Seek _stream->seek(framePos); @@ -326,7 +438,7 @@ CoktelVideo::State Imd::nextFrame() { } void Imd::waitEndFrame() { - if (_soundEnabled && _hasSound) { + if (_soundEnabled && _hasSound) {; if (_soundStage != 2) return; @@ -404,8 +516,9 @@ void Imd::deleteVidMem(bool del) { } _hasOwnVidMem = false; - _vidMem = 0; - _vidMemWidth = _vidMemHeight = 0; + _vidMem = 0; + _vidMemWidth = 0; + _vidMemHeight = 0; } void Imd::clear(bool del) { @@ -420,42 +533,126 @@ void Imd::clear(bool del) { _stream = 0; - _version = 0; + _version = 0; _features = 0; - _flags = 0; - _x = _y = _width = _height = 0; + _flags = 0; + + _x = _y = _width = _height = 0; _stdX = _stdY = _stdWidth = _stdHeight = 0; + _framesCount = _curFrame = 0; - _framesPos = 0; + + _framesPos = 0; _firstFramePos = 0; - _frameCoords = 0; + _frameCoords = 0; _frameDataSize = _vidBufferSize = 0; - _frameData = _vidBuffer = 0; - _frameDataLen = 0; + _frameData = _vidBuffer = 0; + _frameDataLen = 0; memset(_palette, 0, 768); deleteVidMem(del); - _hasSound = false; + _hasSound = false; _soundEnabled = false; - _soundStage = 0; - _skipFrames = 0; + _soundStage = 0; + _skipFrames = 0; - _soundFlags = 0; - _soundFreq = 0; - _soundSliceSize = 0; + _soundFlags = 0; + _soundFreq = 0; + _soundSliceSize = 0; _soundSlicesCount = 0; _soundSliceLength = 0; + _audioStream = 0; - _audioStream = 0; - - _frameRate = 12; - _frameLength = 0; + _frameRate = 12; + _frameLength = 0; _lastFrameTime = 0; } +void Imd::nextSoundSlice(bool hasNextCmd) { + if (hasNextCmd || !_soundEnabled) { + _stream->seek(_soundSliceSize, SEEK_CUR); + return; + } + + byte *soundBuf = new byte[_soundSliceSize]; + + _stream->read(soundBuf, _soundSliceSize); + unsignedToSigned(soundBuf, _soundSliceSize); + + _audioStream->queueBuffer(soundBuf, _soundSliceSize); +} + +bool Imd::initialSoundSlice(bool hasNextCmd) { + int dataLength = _soundSliceSize * _soundSlicesCount; + + if (hasNextCmd || !_soundEnabled) { + _stream->seek(dataLength, SEEK_CUR); + return false; + } + + byte *soundBuf = new byte[dataLength]; + + _stream->read(soundBuf, dataLength); + unsignedToSigned(soundBuf, dataLength); + + _audioStream->queueBuffer(soundBuf, dataLength); + + return _soundStage == 1; +} + +void Imd::emptySoundSlice(bool hasNextCmd) { + if (hasNextCmd || !_soundEnabled) + return; + + byte *soundBuf = new byte[_soundSliceSize]; + + memset(soundBuf, 0, _soundSliceSize); + + _audioStream->queueBuffer(soundBuf, _soundSliceSize); +} + +void Imd::videoData(uint32 size, State &state) { + _stream->read(_frameData, size); + _frameDataLen = size; + + if (_vidMemWidth <= state.right) { + state.left = 0; + state.right -= state.left; + } + if (_vidMemWidth <= state.right) + state.right = _vidMemWidth - 1; + if (_vidMemHeight <= state.bottom) { + state.top = 0; + state.bottom -= state.top; + } + if (_vidMemHeight <= state.bottom) + state.bottom = _vidMemHeight -1; + + state.flags |= renderFrame(state.left, state.top, state.right, state.bottom); + state.flags |= _frameData[0]; +} + +void Imd::calcFrameCoords(uint16 frame, State &state) { + if (_stdX != -1) { + state.left = _stdX; + state.top = _stdY; + state.right = _stdWidth + state.left - 1; + state.bottom = _stdHeight + state.top - 1; + state.flags |= kStateStdCoords; + } + if (_frameCoords && + (_frameCoords[frame].left != -1)) { + state.left = _frameCoords[frame].left; + state.top = _frameCoords[frame].top; + state.right = _frameCoords[frame].right; + state.bottom = _frameCoords[frame].bottom; + state.flags |= kStateFrameCoords; + } +} + CoktelVideo::State Imd::processFrame(uint16 frame) { State state; uint32 cmd = 0; @@ -475,47 +672,35 @@ CoktelVideo::State Imd::processFrame(uint16 frame) { if (!_vidMem) setVideoMemory(); - state.left = _x; - state.top = _y; - state.right = _width + state.left - 1; - state.bottom = _height + state.top - 1; + state.left = _x; + state.top = _y; + state.right = _width + state.left - 1; + state.bottom = _height + state.top - 1; do { - if (frame != 0) { - if (_stdX != -1) { - state.left = _stdX; - state.top = _stdY; - state.right = _stdWidth + state.left - 1; - state.bottom = _stdHeight + state.top - 1; - state.flags |= kStateStdCoords; - } - if (_frameCoords && - (_frameCoords[frame].left != -1)) { - state.left = _frameCoords[frame].left; - state.top = _frameCoords[frame].top; - state.right = _frameCoords[frame].right; - state.bottom = _frameCoords[frame].bottom; - state.flags |= kStateFrameCoords; - } - } + if (frame != 0) + calcFrameCoords(frame, state); cmd = _stream->readUint16LE(); - if ((cmd & 0xFFF8) == 0xFFF0) { - if (cmd == 0xFFF0) { + if ((cmd & kCommandBreakMask) == kCommandBreak) { + // Flow control + + if (cmd == kCommandBreak) { _stream->seek(2, SEEK_CUR); cmd = _stream->readUint16LE(); } - if (cmd == 0xFFF1) { + // Break + if (cmd == kCommandBreakSkip0) { state.flags = kStateBreak; continue; - } else if (cmd == 0xFFF2) { // Skip (16 bit) + } else if (cmd == kCommandBreakSkip16) { cmd = _stream->readUint16LE(); _stream->seek(cmd, SEEK_CUR); state.flags = kStateBreak; continue; - } else if (cmd == 0xFFF3) { // Skip (32 bit) + } else if (cmd == kCommandBreakSkip32) { cmd = _stream->readUint32LE(); _stream->seek(cmd, SEEK_CUR); state.flags = kStateBreak; @@ -523,57 +708,24 @@ CoktelVideo::State Imd::processFrame(uint16 frame) { } } + // Audio if (_soundStage != 0) { - byte *soundBuf; - - // Next sound slice data - if (cmd == 0xFF00) { - - if (!hasNextCmd && _soundEnabled) { - soundBuf = new byte[_soundSliceSize]; - assert(soundBuf); - - _stream->read(soundBuf, _soundSliceSize); - unsignedToSigned(soundBuf, _soundSliceSize); - _audioStream->queueBuffer(soundBuf, _soundSliceSize); - } else - _stream->seek(_soundSliceSize, SEEK_CUR); + if (cmd == kCommandNextSound) { + nextSoundSlice(hasNextCmd); cmd = _stream->readUint16LE(); - // Initial sound data (all slices) - } else if (cmd == 0xFF01) { - int dataLength = _soundSliceSize * _soundSlicesCount; - - if (!hasNextCmd && _soundEnabled) { - soundBuf = new byte[dataLength]; - assert(soundBuf); - - _stream->read(soundBuf, dataLength); - unsignedToSigned(soundBuf, dataLength); - - if (_soundStage == 1) - startSound = true; - - _audioStream->queueBuffer(soundBuf, dataLength); - } else - _stream->seek(dataLength, SEEK_CUR); + } else if (cmd == kCommandStartSound) { + startSound = initialSoundSlice(hasNextCmd); cmd = _stream->readUint16LE(); - // Empty sound slice - } else if (!hasNextCmd && (_soundEnabled)) { - soundBuf = new byte[_soundSliceSize]; - assert(soundBuf); - - memset(soundBuf, 0, _soundSliceSize); - - _audioStream->queueBuffer(soundBuf, _soundSliceSize); - } + } else + emptySoundSlice(hasNextCmd); } // Set palette - if (cmd == 0xFFF4) { + if (cmd == kCommandPalette) { _stream->seek(2, SEEK_CUR); state.flags |= kStatePalette; @@ -583,8 +735,8 @@ CoktelVideo::State Imd::processFrame(uint16 frame) { hasNextCmd = false; - // Jump to frame - if (cmd == 0xFFFD) { + if (cmd == kCommandJump) { + // Jump to frame frame = _stream->readSint16LE(); if (_framesPos) { @@ -595,37 +747,17 @@ CoktelVideo::State Imd::processFrame(uint16 frame) { state.flags |= kStateJump; } - } else if (cmd == 0xFFFC) { + } else if (cmd == kCommandVideoData) { + uint32 size = _stream->readUint32LE() + 2; - state.flags |= 1; - cmd = _stream->readUint32LE(); - _stream->read(_frameData, cmd + 2); - _frameDataLen = cmd + 2; + videoData(size, state); - if (_vidMemWidth <= state.right) { - state.left = 0; - state.right -= state.left; - } - if (_vidMemWidth <= state.right) - state.right = _vidMemWidth - 1; - if (_vidMemHeight <= state.bottom) { - state.top = 0; - state.bottom -= state.top; - } - if (_vidMemHeight <= state.bottom) - state.bottom = _vidMemHeight -1; - - state.flags |= renderFrame(state.left, state.top, state.right, state.bottom); - state.flags |= _frameData[0]; + state.flags |= 1; - // Frame video data } else if (cmd != 0) { + uint32 size = cmd + 2; - _stream->read(_frameData, cmd + 2); - _frameDataLen = cmd + 2; - - state.flags |= renderFrame(state.left, state.top, state.right, state.bottom); - state.flags |= _frameData[0]; + videoData(size, state); } else state.flags |= kStateNoVideoData; @@ -643,24 +775,133 @@ CoktelVideo::State Imd::processFrame(uint16 frame) { _audioStream->finish(); _mixer->stopHandle(_audioHandle); _audioStream = 0; - _soundStage = 0; + _soundStage = 0; } _lastFrameTime = g_system->getMillis(); return state; } +// A whole, completely filled block +void Imd::renderBlockWhole(byte *dest, const byte *src, int16 width, int16 height, + int16 destWidth, int16 destHeight) { + + int16 w = MIN(width, destWidth); + int16 h = MIN(height, destHeight); + + for (int i = 0; i < h; i++) { + memcpy(dest, src, w); + + src += width; + dest += destWidth; + } +} + +// A quarter-wide whole, completely filled block +void Imd::renderBlockWhole4X(byte *dest, const byte *src, int16 width, int16 height, + int16 destWidth, int16 destHeight) { + + for (int i = 0; i < height; i++) { + byte *destBak = dest; + + for (int j = 0; j < width; j += 4, dest += 4, src++) + memset(dest, *src, 4); + + dest = destBak + destWidth; + } +} + +// A half-high whole, completely filled block +void Imd::renderBlockWhole2Y(byte *dest, const byte *src, int16 width, int16 height, + int16 destWidth, int16 destHeight) { + + while (height > 1) { + memcpy(dest , src, width); + memcpy(dest + destWidth, src, width); + + height -= 2; + dest += 2 * destWidth; + src += width; + } + + if (height == 1) + memcpy(dest, src, width); +} + +// A sparse block +void Imd::renderBlockSparse(byte *dest, const byte *src, int16 width, int16 height, + int16 destWidth, int16 destHeight) { + + for (int i = 0; i < height; i++) { + byte *destBak = dest; + uint16 pixWritten = 0; + + while (pixWritten < width) { + uint16 pixCount = *src++; + + if (pixCount & 0x80) { // Data + pixCount = MIN((pixCount & 0x7F) + 1, width - pixWritten); + memcpy(dest, src, pixCount); + + pixWritten += pixCount; + dest += pixCount; + src += pixCount; + } else { // "Hole" + pixWritten += pixCount + 1; + dest += pixCount + 1; + } + + } + + dest = destBak + destWidth; + } +} + +// A half-high sparse block +void Imd::renderBlockSparse2Y(byte *dest, const byte *src, int16 width, int16 height, + int16 destWidth, int16 destHeight) { + + for (int i = 0; i < height; i += 2) { + byte *destBak = dest; + uint16 pixWritten = 0; + + while (pixWritten < width) { + uint16 pixCount = *src++; + + if (pixCount & 0x80) { // Data + pixCount = MIN((pixCount & 0x7F) + 1, width - pixWritten); + memcpy(dest , src, pixCount); + memcpy(dest + destWidth, src, pixCount); + + pixWritten += pixCount; + dest += pixCount; + src += pixCount; + } else { // "Hole" + pixWritten += pixCount + 1; + dest += pixCount + 1; + } + + } + + dest = destBak + destWidth; + } +} + uint32 Imd::renderFrame(int16 left, int16 top, int16 right, int16 bottom) { if (!_frameData || !_vidMem || (_width <= 0) || (_height <= 0)) return 0; uint32 retVal = 0; - int16 width = right - left + 1; - int16 height = bottom - top + 1; - int16 sW = _vidMemWidth; - byte *dataPtr = _frameData; + + int16 width = right - left + 1; + int16 height = bottom - top + 1; + int16 sW = _vidMemWidth; + int16 sH = _vidMemHeight; + + byte *dataPtr = _frameData; byte *imdVidMem = _vidMem + sW * top + left; byte *srcPtr; + uint8 type = *dataPtr++; if (type & 0x10) { // Palette data @@ -676,88 +917,30 @@ uint32 Imd::renderFrame(int16 left, int16 top, int16 right, int16 bottom) { srcPtr = dataPtr; - if (type & 0x80) { // Frame data is compressed + if (type & 0x80) { + // Frame data is compressed + srcPtr = _vidBuffer; type &= 0x7F; if ((type == 2) && (width == sW)) { + // Directly uncompress onto the video surface deLZ77(imdVidMem, dataPtr); return retVal; } else deLZ77(srcPtr, dataPtr); } - uint16 pixCount, pixWritten; - byte *imdVidMemBak; - - if (type == 2) { // Whole block - for (int i = 0; i < height; i++) { - memcpy(imdVidMem, srcPtr, width); - srcPtr += width; - imdVidMem += sW; - } - } else if (type == 1) { // Sparse block - imdVidMemBak = imdVidMem; - for (int i = 0; i < height; i++) { - pixWritten = 0; - while (pixWritten < width) { - pixCount = *srcPtr++; - if (pixCount & 0x80) { // Data - pixCount = MIN((pixCount & 0x7F) + 1, width - pixWritten); - memcpy(imdVidMem, srcPtr, pixCount); - - pixWritten += pixCount; - imdVidMem += pixCount; - srcPtr += pixCount; - } else { // "Hole" - pixCount = (pixCount + 1) % 256; - pixWritten += pixCount; - imdVidMem += pixCount; - } - } - imdVidMemBak += sW; - imdVidMem = imdVidMemBak; - } - } else if (type == 0x42) { // Whole quarter-wide block - for (int i = 0; i < height; i++) { - imdVidMemBak = imdVidMem; - - for (int j = 0; j < width; j += 4, imdVidMem += 4, srcPtr++) - memset(imdVidMem, *srcPtr, 4); - - imdVidMemBak += sW; - imdVidMem = imdVidMemBak; - } - } else if ((type & 0xF) == 2) { // Whole half-high block - for (; height > 1; height -= 2, imdVidMem += sW + sW, srcPtr += width) { - memcpy(imdVidMem, srcPtr, width); - memcpy(imdVidMem + sW, srcPtr, width); - } - if (height == -1) - memcpy(imdVidMem, srcPtr, width); - } else { // Sparse half-high block - imdVidMemBak = imdVidMem; - for (int i = 0; i < height; i += 2) { - pixWritten = 0; - while (pixWritten < width) { - pixCount = *srcPtr++; - if (pixCount & 0x80) { // Data - pixCount = MIN((pixCount & 0x7F) + 1, width - pixWritten); - memcpy(imdVidMem, srcPtr, pixCount); - memcpy(imdVidMem + sW, srcPtr, pixCount); - - pixWritten += pixCount; - imdVidMem += pixCount; - srcPtr += pixCount; - } else { // "Hole" - pixCount = (pixCount + 1) % 256; - pixWritten += pixCount; - imdVidMem += pixCount; - } - } - imdVidMemBak += sW + sW; - imdVidMem = imdVidMemBak; - } - } + // Evaluate the block type + if (type == 0x01) + renderBlockSparse (imdVidMem, srcPtr, width, height, sW, sH); + else if (type == 0x02) + renderBlockWhole (imdVidMem, srcPtr, width, height, sW, sH); + else if (type == 0x42) + renderBlockWhole4X (imdVidMem, srcPtr, width, height, sW, sH); + else if ((type & 0x0F) == 0x02) + renderBlockWhole2Y (imdVidMem, srcPtr, width, height, sW, sH); + else + renderBlockSparse2Y(imdVidMem, srcPtr, width, height, sW, sH); return retVal; } @@ -849,7 +1032,45 @@ void Imd::deLZ77(byte *dest, byte *src) { } } -const uint16 Vmd::_tableADPCM[128] = { +inline void Imd::unsignedToSigned(byte *buffer, int length) { + while (length-- > 0) *buffer++ ^= 0x80; +} + + +Vmd::ExtraData::ExtraData() { + memset(name, 0, 16); + + offset = 0; + size = 0; + realSize = 0; +} + + +Vmd::Part::Part() { + type = kPartTypeSeparator; + field_1 = 0; + field_E = 0; + size = 0; + left = 0; + top = 0; + right = 0; + bottom = 0; + id = 0; + flags = 0; +} + + +Vmd::Frame::Frame() { + parts = 0; + offset = 0; +} + +Vmd::Frame::~Frame() { + delete[] parts; +} + + +const uint16 Vmd::_tableDPCM[128] = { 0x0000, 0x0008, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0060, 0x0070, 0x0080, 0x0090, 0x00A0, 0x00B0, 0x00C0, 0x00D0, 0x00E0, 0x00F0, 0x0100, 0x0110, 0x0120, 0x0130, 0x0140, 0x0150, 0x0160, 0x0170, 0x0180, 0x0190, 0x01A0, 0x01B0, 0x01C0, @@ -865,6 +1086,26 @@ const uint16 Vmd::_tableADPCM[128] = { 0x0F00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, 0x3000, 0x4000 }; +const int32 Vmd::_tableADPCM[] = { + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, + 32767, 0 +}; + +const int32 Vmd::_tableADPCMStep[] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 +}; + Vmd::Vmd(Graphics::PaletteLUT *palLUT) : _palLUT(palLUT) { clear(false); } @@ -873,84 +1114,11 @@ Vmd::~Vmd() { clear(); } -bool Vmd::load(Common::SeekableReadStream &stream) { - unload(); - - _stream = &stream; - - uint16 headerLength = _stream->readUint16LE(); - uint16 handle = _stream->readUint16LE(); - _version = _stream->readUint16LE(); - - if (!(_version & 2)) - _features |= kFeaturesPalette; - else - _features |= kFeaturesFullColor; - - // 0x4 (4) - - // Version checking - if (headerLength != 814) { - warning("VMD Version incorrect (%d, %d, %d)", headerLength, handle, _version); - unload(); - return false; - } - - _framesCount = _stream->readUint16LE(); - - // 0x6 (6) - - _x = _stream->readSint16LE(); - _y = _stream->readSint16LE(); - _width = _stream->readSint16LE(); - _height = _stream->readSint16LE(); - - // 0xE (14) - - if ((_width != 0) && (_height != 0)) { - _hasVideo = true; - _features |= kFeaturesVideo; - if (_features & kFeaturesFullColor) - _codecIndeo3 = new Indeo3(_width, _height, _palLUT); - } else - _hasVideo = false; - - if (_width > 320) { - if (!(_version & 4)) { - _version |= 4; - handle = 0; - } - } - - if (handle > 2) { - warning("VMD Version incorrect (%d, %d, %d)", headerLength, handle, _version); - unload(); - return false; - } - - _bytesPerPixel = handle + 1; - - if (_bytesPerPixel > 1) { +bool Vmd::assessVideoProperties() { + if (_bytesPerPixel > 1) _features |= kFeaturesFullColor; - _features &= ~kFeaturesPalette; - } - - _flags = _stream->readUint16LE(); - - _partsPerFrame = _stream->readUint16LE(); - _firstFramePos = _stream->readUint32LE(); - _stream->skip(4); // Unknown - - // 0x1A (26) - - _stream->read((byte *) _palette, 768); - - // 0x31A (794) - - _frameDataSize = _stream->readUint32LE(); - _vidBufferSize = _stream->readUint32LE(); - - _doubleMode = false; + else + _features |= kFeaturesPalette; if ((_version & 2) && !(_version & 8)) { _externalCodec = true; @@ -958,7 +1126,21 @@ bool Vmd::load(Common::SeekableReadStream &stream) { } else _externalCodec = false; - _preScaleX = 1; + if (_externalCodec) { + if (_videoCodec == MKID_BE('iv32')) { + _features &= ~kFeaturesPalette; + _features |= kFeaturesFullColor; + _codecIndeo3 = new Indeo3(_width, _height, _palLUT); + } else { + char *fourcc = (char *) &_videoCodec; + + warning("Vmd::assessVideoProperties(): Unknow video codec FourCC \'%c%c%c%c\'", + fourcc[3], fourcc[2], fourcc[1], fourcc[0]); + return false; + } + } + + _preScaleX = 1; _postScaleX = 1; if (_externalCodec) @@ -971,10 +1153,10 @@ bool Vmd::load(Common::SeekableReadStream &stream) { _blitMode = n - 1; if (_bytesPerPixel == 2) { - _preScaleX = n; + _preScaleX = n; _postScaleX = 1; } else if (_bytesPerPixel == 3) { - _preScaleX = 1; + _preScaleX = 1; _postScaleX = n; } @@ -985,8 +1167,6 @@ bool Vmd::load(Common::SeekableReadStream &stream) { if (!_externalCodec && !(_flags & 0x1000)) _scaleExternalX = _bytesPerPixel; - // 0x322 (802) - if (_hasVideo) { if ((_frameDataSize == 0) || (_frameDataSize > 1048576)) _frameDataSize = _width * _height + 1000; @@ -1010,42 +1190,75 @@ bool Vmd::load(Common::SeekableReadStream &stream) { if (_externalCodec && _codecIndeo3) _features |= kFeaturesSupportsDouble; - _soundFreq = _stream->readSint16LE(); - _soundSliceSize = _stream->readSint16LE(); - _soundSlicesCount = _stream->readSint16LE(); - _soundFlags = _stream->readUint16LE(); - _hasSound = (_soundFreq != 0); + return true; +} - // 0x32A (810) +bool Vmd::assessAudioProperties() { + bool supportedFormat = true; - if (_hasSound) { - _features |= kFeaturesSound; + _features |= kFeaturesSound; - _soundStereo = (_soundFlags & 0x8000) ? 1 : ((_soundFlags & 0x200) ? 2 : 0); - if (_soundStereo > 0) { - warning("TODO: VMD stereo"); - unload(); - return false; - } + _soundStereo = (_soundFlags & 0x8000) ? 1 : ((_soundFlags & 0x200) ? 2 : 0); + + if (_soundSliceSize < 0) { + _soundBytesPerSample = 2; + _soundSliceSize = -_soundSliceSize; + + if (_soundFlags & 0x10) { + _audioFormat = kAudioFormat16bitADPCM; + _soundHeaderSize = 3; + _soundDataSize = _soundSliceSize >> 1; + + if (_soundStereo > 0) + supportedFormat = false; + + } else { + _audioFormat = kAudioFormat16bitDPCM; + _soundHeaderSize = 1; + _soundDataSize = _soundSliceSize; + + if (_soundStereo == 1) { + supportedFormat = false; + } else if (_soundStereo == 2) { + _soundDataSize = 2 * _soundDataSize + 2; + _soundHeaderSize = 4; + } - if (_soundSliceSize < 0) { - _soundBytesPerSample = 2; - _soundSliceSize = -_soundSliceSize; } + } else { + _soundBytesPerSample = 1; + _audioFormat = kAudioFormat8bitDirect; + _soundHeaderSize = 0; + _soundDataSize = _soundSliceSize; - _soundSliceLength = (uint32) (((double) (1000 << 16)) / - ((double) _soundFreq / (double) _soundSliceSize)); - _frameLength = _soundSliceLength >> 16; + if (_soundStereo > 0) + supportedFormat = false; + } - _soundStage = 1; - _audioStream = Audio::makeAppendableAudioStream(_soundFreq, - (_soundBytesPerSample == 2) ? Audio::Mixer::FLAG_16BITS : 0); - } else - _frameLength = 1000 / _frameRate; + if (!supportedFormat) { + warning("Vmd::assessAudioProperties(): Unsupported audio format: %d bits, encoding %d, stereo %d", + _soundBytesPerSample * 8, _audioFormat, _soundStereo); + return false; + } - _frameInfoOffset = _stream->readUint32LE(); + _soundSliceLength = (uint32) (((double) (1000 << 16)) / + ((double) _soundFreq / (double) _soundSliceSize)); + _frameLength = _soundSliceLength >> 16; + + _soundStage = 1; + + uint32 flags = 0; + + flags |= (_soundBytesPerSample == 2) ? Audio::Mixer::FLAG_16BITS : 0; + flags |= (_soundStereo > 0) ? Audio::Mixer::FLAG_STEREO : 0; + + _audioStream = Audio::makeAppendableAudioStream(_soundFreq, flags); - int numExtraData = 0; + return true; +} + +void Vmd::readFrameTable(int &numExtraData) { + numExtraData = 0; _stream->seek(_frameInfoOffset); _frames = new Frame[_framesCount]; @@ -1054,14 +1267,15 @@ bool Vmd::load(Common::SeekableReadStream &stream) { _stream->skip(2); // Unknown _frames[i].offset = _stream->readUint32LE(); } + for (uint16 i = 0; i < _framesCount; i++) { bool separator = false; for (uint16 j = 0; j < _partsPerFrame; j++) { - _frames[i].parts[j].type = (PartType) _stream->readByte(); + _frames[i].parts[j].type = (PartType) _stream->readByte(); _frames[i].parts[j].field_1 = _stream->readByte(); - _frames[i].parts[j].size = _stream->readUint32LE(); + _frames[i].parts[j].size = _stream->readUint32LE(); if (_frames[i].parts[j].type == kPartTypeAudio) { @@ -1070,13 +1284,17 @@ bool Vmd::load(Common::SeekableReadStream &stream) { } else if (_frames[i].parts[j].type == kPartTypeVideo) { - _frames[i].parts[j].left = _stream->readUint16LE(); - _frames[i].parts[j].top = _stream->readUint16LE(); - _frames[i].parts[j].right = _stream->readUint16LE(); - _frames[i].parts[j].bottom = _stream->readUint16LE(); + _frames[i].parts[j].left = _stream->readUint16LE(); + _frames[i].parts[j].top = _stream->readUint16LE(); + _frames[i].parts[j].right = _stream->readUint16LE(); + _frames[i].parts[j].bottom = _stream->readUint16LE(); _frames[i].parts[j].field_E = _stream->readByte(); - _frames[i].parts[j].flags = _stream->readByte(); + _frames[i].parts[j].flags = _stream->readByte(); + } else if (_frames[i].parts[j].type == kPartTypeSpeech) { + _frames[i].parts[j].id = _stream->readUint16LE(); + // Speech text file name + _stream->skip(8); } else if (_frames[i].parts[j].type == kPartTypeExtraData) { if (!separator) numExtraData++; @@ -1091,16 +1309,9 @@ bool Vmd::load(Common::SeekableReadStream &stream) { } } +} - _stream->seek(_firstFramePos); - - if (numExtraData == 0) - return true; - - _extraData.reserve(numExtraData); - - numExtraData = 0; - +void Vmd::readExtraData() { uint32 ssize = _stream->size(); for (uint16 i = 0; i < _framesCount; i++) { _stream->seek(_frames[i].offset); @@ -1112,9 +1323,10 @@ bool Vmd::load(Common::SeekableReadStream &stream) { if (_frames[i].parts[j].type == kPartTypeExtraData) { ExtraData data; - data.offset = _stream->pos() + 20; - data.size = _frames[i].parts[j].size; + data.offset = _stream->pos() + 20; + data.size = _frames[i].parts[j].size; data.realSize = _stream->readUint32LE(); + _stream->read(data.name, 16); data.name[15] = '\0'; @@ -1129,6 +1341,110 @@ bool Vmd::load(Common::SeekableReadStream &stream) { _stream->skip(_frames[i].parts[j].size); } } +} + +bool Vmd::load(Common::SeekableReadStream &stream) { + unload(); + + _stream = &stream; + + uint16 headerLength; + uint16 handle; + + headerLength = _stream->readUint16LE(); + handle = _stream->readUint16LE(); + _version = _stream->readUint16LE(); + + bool readPalette; + + // Version checking + if (headerLength == 50) { + // Newer version, used in Addy 5 upwards + warning("Vmd::load(): TODO: Addy 5 videos"); + readPalette = false; + } else if (headerLength == 814) { + // Old version + readPalette = true; + } else { + warning("Vmd::load(): Version incorrect (%d, %d, %d)", headerLength, handle, _version); + unload(); + return false; + } + + _framesCount = _stream->readUint16LE(); + + _x = _stream->readSint16LE(); + _y = _stream->readSint16LE(); + _width = _stream->readSint16LE(); + _height = _stream->readSint16LE(); + + if ((_width != 0) && (_height != 0)) { + + _hasVideo = true; + _features |= kFeaturesVideo; + + } else + _hasVideo = false; + + _bytesPerPixel = 1; + if (_version & 4) + _bytesPerPixel = handle + 1; + + if (_bytesPerPixel > 3) { + warning("Vmd::load(): Requested %d bytes per pixel (%d, %d, %d)", _bytesPerPixel, headerLength, handle, _version); + unload(); + return false; + } + + _flags = _stream->readUint16LE(); + + _partsPerFrame = _stream->readUint16LE(); + _firstFramePos = _stream->readUint32LE(); + + _videoCodec = _stream->readUint32BE(); + + if (readPalette) + _stream->read((byte *) _palette, 768); + + _frameDataSize = _stream->readUint32LE(); + _vidBufferSize = _stream->readUint32LE(); + + _doubleMode = false; + + if (_hasVideo) { + if (!assessVideoProperties()) { + unload(); + return false; + } + } + + _soundFreq = _stream->readSint16LE(); + _soundSliceSize = _stream->readSint16LE(); + _soundSlicesCount = _stream->readSint16LE(); + _soundFlags = _stream->readUint16LE(); + + _hasSound = (_soundFreq != 0); + + if (_hasSound) { + if (!assessAudioProperties()) { + unload(); + return false; + } + } else + _frameLength = 1000 / _frameRate; + + _frameInfoOffset = _stream->readUint32LE(); + + int numExtraData; + readFrameTable(numExtraData); + + _stream->seek(_firstFramePos); + + if (numExtraData == 0) + return true; + + _extraData.reserve(numExtraData); + readExtraData(); _stream->seek(_firstFramePos); return true; @@ -1151,11 +1467,11 @@ void Vmd::setXY(int16 x, int16 y) { if (_frames[i].parts[j].type == kPartTypeVideo) { if (x >= 0) { - _frames[i].parts[j].left = _frames[i].parts[j].left - _x + x; + _frames[i].parts[j].left = _frames[i].parts[j].left - _x + x; _frames[i].parts[j].right = _frames[i].parts[j].right - _x + x; } if (y >= 0) { - _frames[i].parts[j].top = _frames[i].parts[j].top - _y + y; + _frames[i].parts[j].top = _frames[i].parts[j].top - _y + y; _frames[i].parts[j].bottom = _frames[i].parts[j].bottom - _y + y; } } @@ -1243,7 +1559,8 @@ void Vmd::clear(bool del) { delete[] _vidMemBuffer; } - _hasVideo = true; + _hasVideo = true; + _videoCodec = 0; _codecIndeo3 = 0; @@ -1253,16 +1570,19 @@ void Vmd::clear(bool del) { _extraData.clear(); _soundBytesPerSample = 1; - _soundStereo = 0; - - _externalCodec = false; - _doubleMode = false; - _blitMode = 0; - _bytesPerPixel = 1; - _preScaleX = 1; - _postScaleX = 1; + _soundStereo = 0; + _soundHeaderSize = 0; + _soundDataSize = 0; + _audioFormat = kAudioFormat8bitDirect; + + _externalCodec = false; + _doubleMode = false; + _blitMode = 0; + _bytesPerPixel = 1; + _preScaleX = 1; + _postScaleX = 1; _scaleExternalX = 1; - _vidMemBuffer = 0; + _vidMemBuffer = 0; } CoktelVideo::State Vmd::processFrame(uint16 frame) { @@ -1272,15 +1592,17 @@ CoktelVideo::State Vmd::processFrame(uint16 frame) { seekFrame(frame); state.flags |= kStateNoVideoData; - state.left = 0x7FFF; - state.top = 0x7FFF; - state.right = 0; + state.left = 0x7FFF; + state.top = 0x7FFF; + state.right = 0; state.bottom = 0; if (!_vidMem) setVideoMemory(); for (uint16 i = 0; (i < _partsPerFrame) && (frame < _framesCount); i++) { + uint32 pos = _stream->pos(); + Part &part = _frames[frame].parts[i]; if (part.type == kPartTypeAudio) { @@ -1313,20 +1635,27 @@ CoktelVideo::State Vmd::processFrame(uint16 frame) { } else if (part.flags == 3) { if (_soundEnabled) { - emptySoundSlice(_soundSliceSize * _soundBytesPerSample); + emptySoundSlice(_soundDataSize * _soundBytesPerSample); if (_soundStage == 1) startSound = true; } _stream->skip(part.size); + } else if (part.flags == 4) { + warning("Vmd::processFrame(): TODO: Addy 5 sound type 4 (%d)", part.size); + disableSound(); + _stream->skip(part.size); } else { - warning("Unknown sound part type %d", part.flags); + warning("Vmd::processFrame(): Unknown sound type %d", part.flags); _stream->skip(part.size); } + _stream->seek(pos + part.size); + } else if ((part.type == kPartTypeVideo) && !_hasVideo) { - warning("Header claims there's no video, but video frame part found"); + warning("Vmd::processFrame(): Header claims there's no video, but video found (%d)", part.size); + _stream->skip(part.size); } else if ((part.type == kPartTypeVideo) && _hasVideo) { state.flags &= ~kStateNoVideoData; @@ -1361,12 +1690,19 @@ CoktelVideo::State Vmd::processFrame(uint16 frame) { state.bottom = MAX(state.bottom, b); } - } else if (part.type == 4) { + } else if (part.type == kPartTypeSeparator) { + } else if (part.type == kPartTypeExtraData) { + _stream->skip(part.size); + } else if (part.type == kPartType4) { // Unknown _stream->skip(part.size); + } else if (part.type == kPartTypeSpeech) { + state.flags |= kStateSpeech; + state.speechId = part.id; + // Always triggers when speech starts + _stream->skip(part.size); } else { - // Unknow type -// warning("Unknown frame part type %d, size %d (%d of %d)", part.type, part.size, i + 1, _partsPerFrame); + warning("Vmd::processFrame(): Unknown frame part type %d, size %d (%d of %d)", part.type, part.size, i + 1, _partsPerFrame); } } @@ -1380,7 +1716,7 @@ CoktelVideo::State Vmd::processFrame(uint16 frame) { _audioStream->finish(); _mixer->stopHandle(_audioHandle); _audioStream = 0; - _soundStage = 0; + _soundStage = 0; } // If these are still 0x7FFF, no video data has been processed @@ -1391,7 +1727,7 @@ CoktelVideo::State Vmd::processFrame(uint16 frame) { return state; } -void Vmd::deRLE(byte *&srcPtr, byte *&destPtr, int16 len) { +void Vmd::deRLE(byte *&destPtr, const byte *&srcPtr, int16 len) { srcPtr++; if (len & 1) @@ -1418,23 +1754,60 @@ void Vmd::deRLE(byte *&srcPtr, byte *&destPtr, int16 len) { } } +// A run-length-encoded sparse block +void Vmd::renderBlockRLE(byte *dest, const byte *src, int16 width, int16 height, + int16 destWidth, int16 destHeight) { + + for (int i = 0; i < height; i++) { + byte *destBak = dest; + uint16 pixWritten = 0; + + while (pixWritten < width) { + uint16 pixCount = *src++; + + if (pixCount & 0x80) { + pixCount = (pixCount & 0x7F) + 1; + + if (*src != 0xFF) { // Normal copy + memcpy(dest, src, pixCount); + dest += pixCount; + src += pixCount; + } else + deRLE(dest, src, pixCount); + + pixWritten += pixCount; + } else { // "Hole" + dest += pixCount + 1; + pixWritten += pixCount + 1; + } + + } + + dest = destBak + destWidth; + } + +} + uint32 Vmd::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { if (!_frameData || !_vidMem || (_width <= 0) || (_height <= 0)) return 0; - int16 width = right - left + 1; - int16 height = bottom - top + 1; - int16 sW = _vidMemWidth; - int16 sH = _vidMemHeight; + int16 width = right - left + 1; + int16 height = bottom - top + 1; + int16 sW = _vidMemWidth; + int16 sH = _vidMemHeight; uint32 dataLen = _frameDataLen; - byte *dataPtr = _frameData; + + byte *dataPtr = _frameData; byte *imdVidMem = _vidMem + sW * top + left; byte *srcPtr; - uint8 type; - if ((width < 0) || (height < 0)) + if ((left < 0) || (top < 0) || (right < 0) || (bottom < 0)) + return 1; + if ((width <= 0) || (height <= 0)) return 1; + uint8 type; byte *dest = imdVidMem; if (Indeo3::isIndeo3(dataPtr, dataLen)) { @@ -1445,12 +1818,12 @@ uint32 Vmd::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { width * (_doubleMode ? 2 : 1), height * (_doubleMode ? 2 : 1))) return 0; - type = 2; + type = 2; srcPtr = _vidBuffer; - width = _width * (_doubleMode ? 2 : 1); + width = _width * (_doubleMode ? 2 : 1); height = _height * (_doubleMode ? 2 : 1); - right = left + width - 1; - bottom = top + height - 1; + right = left + width - 1; + bottom = top + height - 1; } else { @@ -1459,8 +1832,8 @@ uint32 Vmd::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { return 0; } - type = *dataPtr++; - srcPtr = dataPtr; + type = *dataPtr++; + srcPtr = dataPtr; if (_blitMode > 0) { dest = _vidMemBuffer + postScaleX(_width) * (top - _y) + postScaleX((left - _x)); @@ -1469,10 +1842,13 @@ uint32 Vmd::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { sH = _height; } - if (type & 0x80) { // Frame data is compressed + if (type & 0x80) { + // Frame data is compressed + srcPtr = _vidBuffer; type &= 0x7F; if ((type == 2) && (postScaleX(width) == sW)) { + // Directly uncompress onto the video surface deLZ77(dest, dataPtr); blit(imdVidMem, dest, width, height); return 1; @@ -1482,109 +1858,22 @@ uint32 Vmd::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { } - uint16 pixCount, pixWritten; - byte *destBak; - - if (type == 1) { // Sparse block - destBak = dest; - for (int i = 0; i < height; i++) { - pixWritten = 0; - while (pixWritten < postScaleX(width)) { - pixCount = *srcPtr++; - if (pixCount & 0x80) { // Data - pixCount = MIN<int>((pixCount & 0x7F) + 1, postScaleX(width) - pixWritten); - memcpy(dest, srcPtr, pixCount); - - pixWritten += pixCount; - dest += pixCount; - srcPtr += pixCount; - } else { // "Hole" - pixCount = (pixCount + 1) % 256; - pixWritten += pixCount; - dest += pixCount; - } - } - destBak += sW; - dest = destBak; - } - } else if (type == 2) { // Whole block - int16 w = MIN<int32>(postScaleX(width), sW); - int16 h = MIN(height, sH); - - for (int i = 0; i < h; i++) { - memcpy(dest, srcPtr, w); - srcPtr += postScaleX(width); - dest += sW; - } - - } else if (type == 3) { // RLE block - for (int i = 0; i < height; i++) { - destBak = dest; - - pixWritten = 0; - while (pixWritten < width) { - pixCount = *srcPtr++; - if (pixCount & 0x80) { - pixCount = (pixCount & 0x7F) + 1; - - if (*srcPtr != 0xFF) { // Normal copy - memcpy(dest, srcPtr, pixCount); - dest += pixCount; - srcPtr += pixCount; - } else - deRLE(srcPtr, dest, pixCount); - - pixWritten += pixCount; - } else { // "Hole" - dest += pixCount + 1; - pixWritten += pixCount + 1; - } - - } - destBak += sW; - dest = destBak; - } - } else if (type == 0x42) { // Whole quarter-wide block - for (int i = 0; i < height; i++) { - destBak = dest; + width = postScaleX(width); + + // Evaluate the block type + if (type == 0x01) + renderBlockSparse (dest, srcPtr, width, height, sW, sH); + else if (type == 0x02) + renderBlockWhole (dest, srcPtr, width, height, sW, sH); + else if (type == 0x03) + renderBlockRLE (dest, srcPtr, width, height, sW, sH); + else if (type == 0x42) + renderBlockWhole4X (dest, srcPtr, width, height, sW, sH); + else if ((type & 0x0F) == 0x02) + renderBlockWhole2Y (dest, srcPtr, width, height, sW, sH); + else + renderBlockSparse2Y(dest, srcPtr, width, height, sW, sH); - for (int j = 0; j < width; j += 4, dest += 4, srcPtr++) - memset(dest, *srcPtr, 4); - - destBak += sW; - dest = destBak; - } - } else if ((type & 0xF) == 2) { // Whole half-high block - for (; height > 1; height -= 2, dest += sW + sW, srcPtr += width) { - memcpy(dest, srcPtr, width); - memcpy(dest + sW, srcPtr, width); - } - if (height == -1) - memcpy(dest, srcPtr, width); - } else { // Sparse half-high block - destBak = dest; - for (int i = 0; i < height; i += 2) { - pixWritten = 0; - while (pixWritten < width) { - pixCount = *srcPtr++; - if (pixCount & 0x80) { // Data - pixCount = MIN((pixCount & 0x7F) + 1, width - pixWritten); - memcpy(dest, srcPtr, pixCount); - memcpy(dest + sW, srcPtr, pixCount); - - pixWritten += pixCount; - dest += pixCount; - srcPtr += pixCount; - } else { // "Hole" - pixCount = (pixCount + 1) % 256; - pixWritten += pixCount; - dest += pixCount; - } - } - destBak += sW + sW; - dest = destBak; - } - } dest = _vidMemBuffer + postScaleX(_width) * (top - _y) + postScaleX(left - _x); blit(imdVidMem, dest, width, height); @@ -1673,129 +1962,278 @@ void Vmd::blit24(byte *dest, byte *src, int16 srcPitch, int16 width, int16 heigh dither->nextLine(); dest += _vidMemWidth; - src += 3 * srcPitch; + src += 3 * srcPitch; } delete dither; } -void Vmd::emptySoundSlice(uint32 size) { - if (!_audioStream) - return; +byte *Vmd::deDPCM(const byte *data, uint32 &size, int32 init[2]) { + if (!data || (size == 0)) + return 0; - byte *soundBuf = new byte[size]; - assert(soundBuf); + int channels = (_soundStereo > 0) ? 2 : 1; - memset(soundBuf, 0, size); + uint32 inSize = size; + uint32 outSize = size + channels; + + int16 *out = new int16[outSize]; + byte *sound = (byte *) out; + + int channel = 0; + + for (int i = 0; i < channels; i++) { + *out++ = TO_BE_16(init[channel]); - _audioStream->queueBuffer(soundBuf, size); + channel = (channel + 1) % channels; + } + + while (inSize-- > 0) { + if (*data & 0x80) + init[channel] -= _tableDPCM[*data++ & 0x7F]; + else + init[channel] += _tableDPCM[*data++]; + + init[channel] = CLIP<int32>(init[channel], -32768, 32767); + *out++ = TO_BE_16(init[channel]); + + channel = (channel + 1) % channels; + } + + size = outSize * 2; + return sound; } -void Vmd::soundSlice8bit(uint32 size) { +// Yet another IMA ADPCM variant +byte *Vmd::deADPCM(const byte *data, uint32 &size, int32 init, int32 index) { + if (!data || (size == 0)) + return 0; + + uint32 outSize = size * 2; + + int16 *out = new int16[outSize]; + byte *sound = (byte *) out; + + index = CLIP<int32>(index, 0, 88); + + int32 predictor = _tableADPCM[index]; + + uint32 dataByte = 0; + bool newByte = true; + + size *= 2; + while (size -- > 0) { + byte code = 0; + + if (newByte) { + dataByte = *data++; + code = (dataByte >> 4) & 0xF; + } else + code = dataByte & 0xF; + + newByte = !newByte; + + index += _tableADPCMStep[code]; + index = CLIP<int32>(index, 0, 88); + + int32 value = predictor / 8; + + if (code & 4) + value += predictor; + if (code & 2) + value += predictor / 2; + if (code & 1) + value += predictor / 4; + + if (code & 8) + init -= value; + else + init += value; + + init = CLIP<int32>(init, -32768, 32767); + + predictor = _tableADPCM[index]; + + *out++ = TO_BE_16(init); + } + + size = outSize * 2; + return sound; +} + +byte *Vmd::soundEmpty(uint32 &size) { if (!_audioStream) - return; + return 0; byte *soundBuf = new byte[size]; - assert(soundBuf); + memset(soundBuf, 0, size); + + return soundBuf; +} +byte *Vmd::sound8bitDirect(uint32 &size) { + if (!_audioStream) { + _stream->skip(size); + return 0; + } + + byte *soundBuf = new byte[size]; _stream->read(soundBuf, size); unsignedToSigned(soundBuf, size); - _audioStream->queueBuffer(soundBuf, size); + return soundBuf; } -void Vmd::soundSlice16bit(uint32 size, int16 &init) { - if (!_audioStream) - return; +byte *Vmd::sound16bitDPCM(uint32 &size) { + if (!_audioStream) { + _stream->skip(size); + return 0; + } - byte *dataBuf = new byte[size]; - byte *soundBuf = new byte[size * 2]; + int32 init[2]; - _stream->read(dataBuf, size); - deADPCM(soundBuf, dataBuf, init, size); - _audioStream->queueBuffer(soundBuf, size * 2); + init[0] = _stream->readSint16LE(); + size -= 2; + + if (_soundStereo > 0) { + init[1] = _stream->readSint16LE(); + size -= 2; + } - delete[] dataBuf; + byte *data = new byte[size]; + byte *sound = 0; + + if (_stream->read(data, size) == size) + sound = deDPCM(data, size, init); + + delete[] data; + + return sound; } -void Vmd::filledSoundSlice(uint32 size) { - if (_soundBytesPerSample == 1) { - soundSlice8bit(size); - } else if (_soundBytesPerSample == 2) { - int16 init = _stream->readSint16LE(); - soundSlice16bit(size - 2, init); +byte *Vmd::sound16bitADPCM(uint32 &size) { + if (!_audioStream) { + _stream->skip(size); + return 0; } + + int32 init = _stream->readSint16LE(); + size -= 2; + + int32 index = _stream->readByte(); + size--; + + byte *data = new byte[size]; + byte *sound = 0; + + if (_stream->read(data, size) == size) + sound = deADPCM(data, size, init, index); + + delete[] data; + + return sound; } -void Vmd::filledSoundSlices(uint32 size, uint32 mask) { - int n = MIN<int>(_soundSlicesCount - 1, 31); - for (int i = 0; i < n; i++) { +void Vmd::emptySoundSlice(uint32 size) { + byte *sound = soundEmpty(size); - if (mask & 1) - emptySoundSlice(_soundSliceSize * _soundBytesPerSample); - else - filledSoundSlice(_soundSliceSize + 1); + if (sound) + _audioStream->queueBuffer(sound, size); +} + +void Vmd::filledSoundSlice(uint32 size) { + byte *sound = 0; + if (_audioFormat == kAudioFormat8bitDirect) + sound = sound8bitDirect(size); + else if (_audioFormat == kAudioFormat16bitDPCM) + sound = sound16bitDPCM(size); + else if (_audioFormat == kAudioFormat16bitADPCM) + sound = sound16bitADPCM(size); + + if (sound) + _audioStream->queueBuffer(sound, size); +} + +uint8 Vmd::evaluateMask(uint32 mask, bool *fillInfo, uint8 &max) { + max = MIN<int>(_soundSlicesCount - 1, 31); + + uint8 n = 0; + for (int i = 0; i < max; i++) { + + if (!(mask & 1)) { + n++; + *fillInfo++ = true; + } else + *fillInfo++ = false; mask >>= 1; } - if (_soundSlicesCount > 32) - filledSoundSlice((_soundSlicesCount - 32) * _soundSliceSize); + + return n; } -void Vmd::deADPCM(byte *soundBuf, byte *dataBuf, int16 &init, uint32 n) { - int16 *out = (int16 *) soundBuf; +void Vmd::filledSoundSlices(uint32 size, uint32 mask) { + bool fillInfo[32]; + + uint8 max; + uint8 n = evaluateMask(mask, fillInfo, max); + + int32 extraSize; + + extraSize = size - n * _soundDataSize; + + if (_soundSlicesCount > 32) + extraSize -= (_soundSlicesCount - 32) * _soundDataSize; + + if (n > 0) + extraSize /= n; - int32 s = init; - for (uint32 i = 0; i < n; i++) { - if (dataBuf[i] & 0x80) - s -= _tableADPCM[dataBuf[i] & 0x7F]; + for (uint8 i = 0; i < max; i++) + if (fillInfo[i]) + filledSoundSlice(_soundDataSize + extraSize); else - s += _tableADPCM[dataBuf[i]]; + emptySoundSlice(_soundDataSize * _soundBytesPerSample); - s = CLIP<int32>(s, -32768, 32767); - *out++ = TO_BE_16(s); - } + if (_soundSlicesCount > 32) + filledSoundSlice((_soundSlicesCount - 32) * _soundDataSize + _soundHeaderSize); } -bool Vmd::getAnchor(int16 frame, uint16 partType, +bool Vmd::getPartCoords(int16 frame, PartType type, int16 &x, int16 &y, int16 &width, int16 &height) { - uint32 pos = _stream->pos(); + if (frame >= _framesCount) + return false; - _stream->seek(_frameInfoOffset); - // Offsets to frames - _stream->skip(_framesCount * 6); - // Jump to the specified frame - _stream->skip(_partsPerFrame * frame * 16); + Frame &f = _frames[frame]; - // Find the anchor part - uint16 i; - for (i = 0; i < _partsPerFrame; i++) { - byte type = _stream->readByte(); + // Look for a part matching the requested type, stopping at a separator + Part *part = 0; + for (int i = 0; i < _partsPerFrame; i++) { + Part &p = f.parts[i]; - if ((type == kPartTypeSeparator) || (type == partType)) + if ((p.type == kPartTypeSeparator) || (p.type == type)) { + part = &p; break; - - _stream->skip(15); + } } - if (i == _partsPerFrame) { - // No anchor - - _stream->seek(pos); + if (!part) return false; - } - _stream->skip(5); - x = _stream->readSint16LE(); - y = _stream->readSint16LE(); - width = _stream->readSint16LE() - x + 1; - height = _stream->readSint16LE() - y + 1; + x = part->left; + y = part->top; + width = part->right - part->left + 1; + height = part->bottom - part->top + 1; - _stream->seek(pos); return true; } +bool Vmd::getFrameCoords(int16 frame, + int16 &x, int16 &y, int16 &width, int16 &height) { + + return getPartCoords(frame, kPartTypeVideo, x, y, width, height); +} + bool Vmd::hasExtraData(const char *fileName) const { for (uint i = 0; i < _extraData.size(); i++) if (!scumm_stricmp(_extraData[i].name, fileName)) diff --git a/graphics/video/coktelvideo/coktelvideo.h b/graphics/video/coktelvideo/coktelvideo.h index 58b56e18ec..db80b4c43d 100644 --- a/graphics/video/coktelvideo/coktelvideo.h +++ b/graphics/video/coktelvideo/coktelvideo.h @@ -76,7 +76,9 @@ public: /** Had to explicitely seek to the frame. */ kStateSeeked = 0x2000, /** Reached a break-point. */ - kStateBreak = 0x8000 + kStateBreak = 0x8000, + /** Frame marks the beginning of speech. */ + kStateSpeech = 0x4000000 }; struct State { @@ -90,8 +92,10 @@ public: int16 bottom; /** Set accordingly to what was done. */ uint32 flags; + /** The id of the spoken words. */ + uint16 speechId; - State() : left(0), top(0), right(0), bottom(0), flags(0) { } + State() : left(0), top(0), right(0), bottom(0), flags(0), speechId(0) { } }; virtual ~CoktelVideo() { } @@ -123,8 +127,8 @@ public: /** Returns the current frame's palette. */ virtual const byte *getPalette() const = 0; - /** Reads the video's anchor pointer */ - virtual bool getAnchor(int16 frame, uint16 partType, + /** Returns the frame's coordinates */ + virtual bool getFrameCoords(int16 frame, int16 &x, int16 &y, int16 &width, int16 &height) = 0; /** Returns whether that extra data file exists */ @@ -171,9 +175,6 @@ public: /** Wait for the frame to end. */ virtual void waitEndFrame() = 0; - /** Notifies the video that it was paused for duration ms. */ - virtual void notifyPaused(uint32 duration) = 0; - /** Copy the current frame. * * @param dest The memory to which to copy the current frame. @@ -198,29 +199,26 @@ public: Imd(); ~Imd(); - uint32 getFeatures() const { return _features; } - uint16 getFlags() const { return _flags; } - int16 getX() const { return _x; } - int16 getY() const { return _y; } - int16 getWidth() const { return _width; } - int16 getHeight() const { return _height; } - uint16 getFramesCount() const { return _framesCount; } - uint16 getCurrentFrame() const { return _curFrame; } - int16 getFrameRate() const { - if (_hasSound) - return 1000 / (_soundSliceLength >> 16); - return _frameRate; - } - uint32 getSyncLag() const { return _skipFrames; } - const byte *getPalette() const { return _palette; } - - bool getAnchor(int16 frame, uint16 partType, - int16 &x, int16 &y, int16 &width, int16 &height) { return false; } - - bool hasExtraData(const char *fileName) const { return false; } - Common::MemoryReadStream *getExtraData(const char *fileName) { return 0; } - - void notifyPaused(uint32 duration) { } + uint32 getFeatures() const; + uint16 getFlags() const; + + int16 getX() const; + int16 getY() const; + int16 getWidth() const; + int16 getHeight() const; + + uint16 getFramesCount() const; + uint16 getCurrentFrame() const; + int16 getFrameRate() const; + uint32 getSyncLag() const; + + const byte *getPalette() const; + + bool getFrameCoords(int16 frame, + int16 &x, int16 &y, int16 &width, int16 &height); + + bool hasExtraData(const char *fileName) const; + Common::MemoryReadStream *getExtraData(const char *fileName); void setFrameRate(int16 frameRate); @@ -231,7 +229,7 @@ public: void setVideoMemory(byte *vidMem, uint16 width, uint16 height); void setVideoMemory(); - void setDoubleMode(bool doubleMode) { } + void setDoubleMode(bool doubleMode); void enableSound(Audio::Mixer &mixer); void disableSound(); @@ -248,6 +246,22 @@ public: uint16 x, uint16 y, uint16 pitch, int16 transp = -1); protected: + enum Command { + kCommandNextSound = 0xFF00, + kCommandStartSound = 0xFF01, + + kCommandBreak = 0xFFF0, + kCommandBreakSkip0 = 0xFFF1, + kCommandBreakSkip16 = 0xFFF2, + kCommandBreakSkip32 = 0xFFF3, + kCommandBreakMask = 0xFFF8, + + kCommandPalette = 0xFFF4, + kCommandVideoData = 0xFFFC, + + kCommandJump = 0xFFFD + }; + struct Coord { int16 left; int16 top; @@ -256,56 +270,104 @@ protected: } PACKED_STRUCT; Common::SeekableReadStream *_stream; + + // Properties uint16 _version; uint32 _features; uint16 _flags; - int16 _x, _y, _width, _height; - int16 _stdX, _stdY, _stdWidth, _stdHeight; - uint16 _framesCount, _curFrame; + + // Current coordinates + int16 _x; + int16 _y; + int16 _width; + int16 _height; + + // Standard coordinates gives by the header + int16 _stdX; + int16 _stdY; + int16 _stdWidth; + int16 _stdHeight; + + uint16 _framesCount; + uint16 _curFrame; + uint32 *_framesPos; - uint32 _firstFramePos; + uint32 _firstFramePos; Coord *_frameCoords; - uint32 _frameDataSize, _vidBufferSize; - byte *_frameData, *_vidBuffer; + // Buffer for raw frame data + byte *_frameData; + uint32 _frameDataSize; uint32 _frameDataLen; - byte _palette[768]; + // Buffer for uncompressed raw frame data + byte *_vidBuffer; + uint32 _vidBufferSize; - bool _hasOwnVidMem; - byte *_vidMem; - uint16 _vidMemWidth, _vidMemHeight; + byte _palette[768]; - bool _hasSound; - bool _soundEnabled; - uint8 _soundStage; // (0: no sound, 1: loaded, 2: playing) - uint32 _skipFrames; + // Video memory + bool _hasOwnVidMem; + byte *_vidMem; + uint16 _vidMemWidth; + uint16 _vidMemHeight; + // Sound properties uint16 _soundFlags; - int16 _soundFreq; - int16 _soundSliceSize; - int16 _soundSlicesCount; + int16 _soundFreq; + int16 _soundSliceSize; + int16 _soundSlicesCount; uint32 _soundSliceLength; + // Current sound state + bool _hasSound; + bool _soundEnabled; + uint8 _soundStage; // (0: no sound, 1: loaded, 2: playing) + uint32 _skipFrames; + Audio::AppendableAudioStream *_audioStream; Audio::SoundHandle _audioHandle; - int16 _frameRate; + // Current video state + int16 _frameRate; uint32 _frameLength; uint32 _lastFrameTime; Audio::Mixer *_mixer; - void unsignedToSigned(byte *buffer, int length) { - while (length-- > 0) *buffer++ ^= 0x80; - } + void unsignedToSigned(byte *buffer, int length); void deleteVidMem(bool del = true); void clear(bool del = true); + bool loadCoordinates(); + bool loadFrameTableOffsets(uint32 &framesPosPos, uint32 &framesCoordsPos); + bool assessVideoProperties(); + bool assessAudioProperties(); + bool loadFrameTables(uint32 framesPosPos, uint32 framesCoordsPos); + State processFrame(uint16 frame); uint32 renderFrame(int16 left, int16 top, int16 right, int16 bottom); void deLZ77(byte *dest, byte *src); + + void renderBlockWhole (byte *dest, const byte *src, int16 width, int16 height, + int16 destWidth, int16 destHeight); + void renderBlockWhole4X (byte *dest, const byte *src, int16 width, int16 height, + int16 destWidth, int16 destHeight); + void renderBlockWhole2Y (byte *dest, const byte *src, int16 width, int16 height, + int16 destWidth, int16 destHeight); + void renderBlockSparse (byte *dest, const byte *src, int16 width, int16 height, + int16 destWidth, int16 destHeight); + void renderBlockSparse2Y(byte *dest, const byte *src, int16 width, int16 height, + int16 destWidth, int16 destHeight); + + void calcFrameCoords(uint16 frame, State &state); + + void nextSoundSlice(bool hasNextCmd); + bool initialSoundSlice(bool hasNextCmd); + void emptySoundSlice(bool hasNextCmd); + + void videoData(uint32 size, State &state); }; class Vmd : public Imd { @@ -313,7 +375,7 @@ public: Vmd(Graphics::PaletteLUT *palLUT = 0); ~Vmd(); - bool getAnchor(int16 frame, uint16 partType, + bool getFrameCoords(int16 frame, int16 &x, int16 &y, int16 &width, int16 &height); bool hasExtraData(const char *fileName) const; @@ -335,38 +397,58 @@ public: protected: enum PartType { kPartTypeSeparator = 0, - kPartTypeAudio = 1, - kPartTypeVideo = 2, - kPartTypeExtraData = 3 + kPartTypeAudio = 1, + kPartTypeVideo = 2, + kPartTypeExtraData = 3, + kPartType4 = 4, + kPartTypeSpeech = 5 }; + + enum AudioFormat { + kAudioFormat8bitDirect = 0, + kAudioFormat16bitDPCM = 1, + kAudioFormat16bitADPCM = 2 + }; + struct ExtraData { - char name[16]; + char name[16]; uint32 offset; uint32 size; uint32 realSize; + + ExtraData(); } PACKED_STRUCT; + struct Part { PartType type; - byte field_1; - byte field_E; - uint32 size; - int16 left; - int16 top; - int16 right; - int16 bottom; - byte flags; + byte field_1; + byte field_E; + uint32 size; + int16 left; + int16 top; + int16 right; + int16 bottom; + uint16 id; + byte flags; + + Part(); } PACKED_STRUCT; + struct Frame { uint32 offset; - Part *parts; + Part *parts; - Frame() : parts(0) { } - ~Frame() { delete[] parts; } + Frame(); + ~Frame(); } PACKED_STRUCT; - static const uint16 _tableADPCM[128]; + // Tables for the audio decompressors + static const uint16 _tableDPCM[128]; + static const int32 _tableADPCM[]; + static const int32 _tableADPCMStep[]; - bool _hasVideo; + bool _hasVideo; + uint32 _videoCodec; uint32 _frameInfoOffset; uint16 _partsPerFrame; @@ -374,9 +456,14 @@ protected: Common::Array<ExtraData> _extraData; - byte _soundBytesPerSample; - byte _soundStereo; // (0: mono, 1: old-style stereo, 2: new-style stereo) + // Sound properties + byte _soundBytesPerSample; + byte _soundStereo; // (0: mono, 1: old-style stereo, 2: new-style stereo) + uint32 _soundHeaderSize; + uint32 _soundDataSize; + AudioFormat _audioFormat; + // Video properties bool _externalCodec; byte _blitMode; byte _bytesPerPixel; @@ -392,10 +479,21 @@ protected: void clear(bool del = true); + bool getPartCoords(int16 frame, PartType type, + int16 &x, int16 &y, int16 &width, int16 &height); + + bool assessVideoProperties(); + bool assessAudioProperties(); + void readFrameTable(int &numExtraData); + void readExtraData(); + State processFrame(uint16 frame); uint32 renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom); - void deRLE(byte *&srcPtr, byte *&destPtr, int16 len); + void renderBlockRLE(byte *dest, const byte *src, int16 width, int16 height, + int16 destWidth, int16 destHeight); + + void deRLE(byte *&destPtr, const byte *&srcPtr, int16 len); inline int32 preScaleX(int32 x) const; inline int32 postScaleX(int32 x) const; @@ -404,12 +502,18 @@ protected: void blit16(byte *dest, byte *src, int16 srcPitch, int16 width, int16 height); void blit24(byte *dest, byte *src, int16 srcPitch, int16 width, int16 height); + byte *deDPCM(const byte *data, uint32 &size, int32 init[2]); + byte *deADPCM(const byte *data, uint32 &size, int32 init, int32 v28); + + byte *soundEmpty(uint32 &size); + byte *sound8bitDirect(uint32 &size); + byte *sound16bitDPCM(uint32 &size); + byte *sound16bitADPCM(uint32 &size); + + uint8 evaluateMask(uint32 mask, bool *fillInfo, uint8 &max); void emptySoundSlice(uint32 size); - void soundSlice8bit(uint32 size); - void soundSlice16bit(uint32 size, int16 &init); void filledSoundSlice(uint32 size); void filledSoundSlices(uint32 size, uint32 mask); - void deADPCM(byte *soundBuf, byte *dataBuf, int16 &init, uint32 n); }; } // End of namespace Graphics diff --git a/graphics/video/smk_decoder.cpp b/graphics/video/smk_decoder.cpp index 9db8f0c730..8952f553b7 100644 --- a/graphics/video/smk_decoder.cpp +++ b/graphics/video/smk_decoder.cpp @@ -348,8 +348,8 @@ uint32 BigHuffmanTree::getCode(BitStream &bs) { return v; } -SmackerDecoder::SmackerDecoder(Audio::Mixer *mixer) - : _audioStarted(false), _audioStream(0), _mixer(mixer) { +SmackerDecoder::SmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) + : _audioStarted(false), _audioStream(0), _mixer(mixer), _soundType(soundType) { } SmackerDecoder::~SmackerDecoder() { @@ -583,7 +583,7 @@ bool SmackerDecoder::decodeNextFrame() { } if (!_audioStarted) { - _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_audioHandle, _audioStream, -1, 255); + _mixer->playInputStream(_soundType, &_audioHandle, _audioStream, -1, 255); _audioStarted = true; } } else { diff --git a/graphics/video/smk_decoder.h b/graphics/video/smk_decoder.h index e70d0e6454..e28d85119d 100644 --- a/graphics/video/smk_decoder.h +++ b/graphics/video/smk_decoder.h @@ -55,7 +55,8 @@ class BigHuffmanTree; */ class SmackerDecoder : public VideoDecoder { public: - SmackerDecoder(Audio::Mixer *mixer); + SmackerDecoder(Audio::Mixer *mixer, + Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType); virtual ~SmackerDecoder(); int getHeight(); @@ -113,6 +114,7 @@ private: // The RGB palette byte *_palette; + Audio::Mixer::SoundType _soundType; Audio::Mixer *_mixer; bool _audioStarted; Audio::AppendableAudioStream *_audioStream; |