1 /*
2
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 */
19 // ==============================================================
20 // Convert a raw QT movie (from the LG .MOV file) into a nice compressed movie.
21 // Make a separate movie for each palette change.
22 // ==============================================================
23
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "2d.h"
28 #include "fix.h"
29 #include "lg.h"
30 #include "quiktime.h"
31 #include "rect.h"
32
33 #include "InitMac.h"
34 #include "ShockBitmap.h"
35 extern Ptr gScreenAddress;
36 extern long gScreenRowbytes;
37
38 #include <ImageCompression.h>
39 #include <Movies.h>
40 #include <QuickTimeComponents.h>
41 #include <Sound.h>
42
43 typedef struct {
44 long frameNum;
45 short palID;
46 } PalChange;
47
48 //--------------------------
49 // Globals the user should set
50 //--------------------------
51 char gInputMov[] = "INTRO.QTM";
52 FSSpec gInputPal = {0, 0, "Intro Palettes"};
53 FSSpec gInputSnd = {0, 0, "INTRO"};
54
55 #define codec 'smc '
56 #define spatialQ codecHighQuality
57 #define temporalQ codecHighQuality
58 #define codecType bestCompressionCodec
59 #define kPrevious codecFlagUpdatePreviousComp
60
61 // Globals
62 WindowPtr gMainWindow;
63 extern short gMainVRef;
64 short gResNum;
65 ulong *gSampleTimes;
66 ulong *gChunkOffsets;
67 long gNumFrames;
68
69 ImageDescription **gImageDescriptionH = 0L; // Contains info about the sample
70 Movie gMovie = 0; // Our movie, track and media
71 Track gTrack;
72 Media gMedia;
73 short gMovieResNum;
74 Rect gMovieRect;
75 Handle gCompressedFrameBitsH = 0L; // Buffer for the compressed data
76 ImageSequence gSeq;
77
78 QT_ChunkInfo chunkInfo[] = {QT_CLIP, FALSE, QT_CRGN, TRUE, QT_DINF, FALSE, QT_DREF, TRUE, QT_EDTS, FALSE,
79 QT_ELST, TRUE, QT_HDLR, TRUE, QT_KMAT, TRUE, QT_MATT, FALSE, QT_MDAT, TRUE,
80 QT_MDIA, FALSE, QT_MDHD, TRUE, QT_MINF, FALSE, QT_MOOV, FALSE, QT_MVHD, TRUE,
81 QT_SMHD, TRUE, QT_STBL, FALSE, QT_STCO, TRUE, QT_STSC, TRUE, QT_STSD, TRUE,
82 QT_STSH, TRUE, QT_STSS, TRUE, QT_STSZ, TRUE, QT_STTS, TRUE, QT_TKHD, TRUE,
83 QT_TRAK, FALSE, QT_UDTA, FALSE, QT_VMHD, TRUE, 0, 0};
84 TrackType currTrackType;
85
86 // Prototypes
87 void main(void);
88 void CheckError(OSErr error, Str255 displayString);
89 void SetInputSpecs(void);
90 void SetPalette(short palID);
91 void CreateAMovie(void);
92 void EndAMovie(void);
93
94 uchar QuikReadChunkHdr(FILE *fp, QT_ChunkHdr *phdr);
95 QT_ChunkInfo *QuikFindChunkInfo(QT_ChunkHdr *phdr);
96 void QuikSkipChunk(FILE *fp, QT_ChunkHdr *phdr);
97
98 void CreateMySoundTrack(Movie theMovie);
99 void CreateSoundDescription(Handle sndHandle, SoundDescriptionHandle sndDesc, long *sndDataOffset, long *numSamples,
100 long *sndDataSize);
101 long GetSndHdrOffset(Handle sndHandle);
102
103 void MyCreateTextTrack(Movie theMovie);
104
105 // ---------------------------------------------------------------
106 // MAIN PROGRAM
107 // ---------------------------------------------------------------
108
main(void)109 void main(void) {
110 grs_screen *screen;
111 Ptr p;
112 long stupid;
113 OSErr result;
114
115 FILE *fp;
116 uchar *dbuff;
117 ulong dbuffLen;
118 QT_ChunkHdr chunkHdr;
119 QT_ChunkInfo *pinfo;
120
121 short palResNum, sndResNum;
122 Handle palChgHdl;
123 PalChange *pcp;
124
125 Rect r;
126 long compressedFrameSize; /* Size of current compressed frame */
127
128 //---------------------
129 // Init graphics system
130 //---------------------
131 InitMac();
132 CheckConfig();
133
134 SetupWindows(&gMainWindow); // setup everything
135 SetupOffscreenBitmaps();
136
137 gr_init();
138 gr_set_mode(GRM_640x480x8, TRUE);
139 screen = gr_alloc_screen(grd_cap->w, grd_cap->h);
140 gr_set_screen(screen);
141 SetRect(&gMovieRect, 0, 0, 600, 300);
142
143 //---------------------
144 // Setup the input FileSpecs
145 //---------------------
146 SetInputSpecs();
147
148 //---------------------
149 // Setup Quicktime stuff.
150 //---------------------
151 if (EnterMovies() != noErr) // Start up the movie tools
152 {
153 ParamText("Can't startup QuickTime.", "", "", "");
154 StopAlert(1000, nil);
155 CleanupAndExit();
156 }
157
158 //----------------------
159 // Open the input QuickTime movie.
160 //----------------------
161 fp = fopen(gInputMov, "rb");
162 if (fp == NULL)
163 CheckError(1, "Can't open the input movie!!");
164
165 dbuffLen = 64000;
166 dbuff = (uchar *)malloc(dbuffLen);
167 /*
168 //----------------------
169 // Open the input Sound file.
170 //----------------------
171 sndResNum = FSpOpenResFile(&gInputSnd, fsRdPerm);
172 if (sndResNum == -1)
173 CheckError(1, "Can't open the sound file!!");
174 */
175 //----------------------
176 // Open the input Palettes file, read the palette changes, and set the first palette.
177 //----------------------
178 palResNum = FSpOpenResFile(&gInputPal, fsRdPerm);
179 if (palResNum == -1)
180 CheckError(1, "Can't open the palette file!!");
181 palChgHdl = GetResource('pchg', 128);
182 if (!palChgHdl)
183 CheckError(1, "Can't load the palette changes resource!!");
184 SetPalette(128);
185
186 // Allocate a description handle for the movies.
187 gImageDescriptionH = (ImageDescription **)NewHandle(sizeof(ImageDescription));
188 CheckError(MemError(), "Can't alloc description for video.");
189
190 // Create the first movie.
191 CreateAMovie();
192
193 //-----------------------------------
194 // Get the Chunk Offset and Sample-to-Time tables.
195 //-----------------------------------
196 while (TRUE) {
197 if (!QuikReadChunkHdr(fp, &chunkHdr))
198 break;
199 if (chunkHdr.length == 0)
200 break;
201 pinfo = QuikFindChunkInfo(&chunkHdr);
202 if (pinfo->isleaf) {
203 if ((chunkHdr.ctype != QT_MDAT)) {
204 if (chunkHdr.length > dbuffLen) {
205 dbuffLen = chunkHdr.length;
206 dbuff = (uchar *)realloc(dbuff, dbuffLen);
207 }
208 fread(dbuff, chunkHdr.length - sizeof(QT_ChunkHdr), 1, fp);
209
210 // For the sample-to-time table, create our own table giving a time
211 // for each frame.
212 if (chunkHdr.ctype == QT_STTS) {
213 QTS_STTS *p = (QTS_STTS *)dbuff;
214 short i, j, si;
215
216 gSampleTimes = (ulong *)malloc(1200 * sizeof(ulong));
217 si = 0;
218 for (i = 0; i < p->numEntries; i++)
219 for (j = 0; j < p->time2samp[i].count; j++)
220 gSampleTimes[si++] = p->time2samp[i].duration;
221 }
222
223 // For the chunk offsets table, read it in to a memory block.
224 if (chunkHdr.ctype == QT_STCO) {
225 QTS_STCO *p = (QTS_STCO *)dbuff;
226
227 gNumFrames = p->numEntries;
228 gChunkOffsets = (ulong *)malloc(p->numEntries * sizeof(ulong));
229 BlockMove(p->offset, gChunkOffsets, p->numEntries * sizeof(ulong));
230 }
231 } else
232 QuikSkipChunk(fp, &chunkHdr);
233 }
234 }
235
236 //----------------------------
237 // Show the movie, one frame at a time.
238 //----------------------------
239 {
240 long f, line;
241 Ptr frameBuff;
242 Ptr imgp, scrp;
243 RGBColor black = {0, 0, 0};
244 RGBColor white = {0xffff, 0xffff, 0xffff};
245 char buff[64];
246 PalChange *pc;
247
248 frameBuff = malloc(600 * 300);
249 CheckError(MemError(), "Can't allocate a frame buffer for input movie.");
250
251 SetRect(&r, 0, -17, 300, 0);
252 for (f = 0; f < gNumFrames; f++) {
253 fseek(fp, gChunkOffsets[f], SEEK_SET);
254 fread(frameBuff, 600 * 300, 1, fp);
255
256 // Check for a palette change. If one occurs, then finish up the current movie,
257 // change the palette, then open a new movie.
258 HLock(palChgHdl);
259 pc = (PalChange *)*palChgHdl;
260 while (pc->frameNum) {
261 if (pc->frameNum == f) {
262 EndAMovie();
263 SetPalette(pc->palID);
264 CreateAMovie();
265 break;
266 }
267 pc++;
268 }
269 HUnlock(palChgHdl);
270
271 imgp = frameBuff;
272 scrp = gScreenAddress;
273 for (line = 0; line < 300; line++) {
274 BlockMove(imgp, scrp, 600);
275 imgp += 600;
276 scrp += gScreenRowbytes;
277 }
278
279 // Display the frame number.
280
281 sprintf(buff, "Frame: %d", f);
282 RGBForeColor(&black);
283 PaintRect(&r);
284 MoveTo(1, -6);
285 RGBForeColor(&white);
286 DrawText(buff, 0, strlen(buff));
287
288 // Add the frame to the QuickTime movie.
289
290 HLock(gCompressedFrameBitsH);
291 result = CompressSequenceFrame(gSeq, ((CGrafPort *)(gMainWindow))->portPixMap, &gMovieRect, kPrevious,
292 *gCompressedFrameBitsH, &compressedFrameSize, 0L, 0L);
293 CheckError(result, "Can't compress a frame.");
294 HUnlock(gCompressedFrameBitsH);
295
296 result = AddMediaSample(gMedia, gCompressedFrameBitsH, 0L, compressedFrameSize, gSampleTimes[f],
297 (SampleDescriptionHandle)gImageDescriptionH, 1L, 0, 0L);
298 CheckError(result, "Can't add the frame sample.");
299 }
300 }
301
302 EndAMovie();
303
304 if (gImageDescriptionH)
305 DisposeHandle((Handle)gImageDescriptionH);
306
307 CloseResFile(palResNum);
308 CloseResFile(sndResNum);
309 fclose(fp);
310 ExitMovies();
311
312 gr_clear(0xFF);
313 CleanupAndExit();
314 }
315
316 //------------------------------------------------------------------------
317 // Exit in case of an error.
318 //------------------------------------------------------------------------
CheckError(OSErr error,Str255 displayString)319 void CheckError(OSErr error, Str255 displayString) {
320 if (error == noErr)
321 return;
322 ParamText(displayString, "", "", "");
323 StopAlert(1000, nil);
324 ExitMovies();
325 CleanupAndExit();
326 }
327
328 //------------------------------------------------------------------------
329 // Setup the input file specs.
330 //------------------------------------------------------------------------
SetInputSpecs(void)331 void SetInputSpecs(void) {
332 short vRefNum;
333 long temp, parID;
334
335 GetWDInfo(gMainVRef, &vRefNum, &parID, &temp);
336
337 gInputPal.vRefNum = vRefNum;
338 gInputPal.parID = parID;
339
340 gInputSnd.vRefNum = vRefNum;
341 gInputSnd.parID = parID;
342 }
343
344 //------------------------------------------------------------------------
345 // Load in the 'mpal' resource and set it.
346 //------------------------------------------------------------------------
SetPalette(short palID)347 void SetPalette(short palID) {
348 Handle gPalHdl;
349
350 gPalHdl = GetResource('mpal', palID);
351 if (!gPalHdl) {
352 ParamText("Can't load a palette resource!!", "", "", "");
353 StopAlert(1000, nil);
354 ExitMovies();
355 CleanupAndExit();
356 }
357 gr_clear(0xFF);
358 HLock(gPalHdl);
359 gr_set_pal(0, 256, (uchar *)*gPalHdl);
360 HUnlock(gPalHdl);
361 }
362
363 //------------------------------------------------------------------------
364 // Create a new movie file, and get the movie ready to add video samples.
365 //------------------------------------------------------------------------
CreateAMovie(void)366 void CreateAMovie(void) {
367 OSErr result;
368 Point dlgPos = {120, 120};
369 SFReply sfr;
370 FSSpec mySpec;
371 Str255 name = "Output Movie";
372 long maxCompressedFrameSize;
373
374 SFPutFile(dlgPos, "Save Movie as:", name, 0L, &sfr);
375 if (!sfr.good) {
376 ExitMovies();
377 CleanupAndExit();
378 }
379
380 ClearMoviesStickyError();
381 FSMakeFSSpec(sfr.vRefNum, 0, sfr.fName, &mySpec);
382 result = CreateMovieFile(&mySpec, 'TVOD', 0, createMovieFileDeleteCurFile, &gMovieResNum, &gMovie);
383 CheckError(result, "Can't create output movie file.");
384
385 SetMovieColorTable(gMovie, gMainColorHand);
386
387 // Add the sound track here.
388 // MyCreateTextTrack(gMovie);
389
390 gTrack = NewMovieTrack(gMovie, 600L << 16, 300L << 16, 0);
391 CheckError(GetMoviesError(), "New video track.");
392
393 gMedia = NewTrackMedia(gTrack, VideoMediaType, 30, 0L, 0L);
394 CheckError(GetMoviesError(), "New Media for video track.");
395
396 BeginMediaEdits(gMedia); // We do this since we are adding samples to the media
397 GetMaxCompressionSize(((CGrafPort *)(gMainWindow))->portPixMap, &gMovieRect, 8, spatialQ, codec, codecType,
398 &maxCompressedFrameSize);
399
400 gCompressedFrameBitsH = NewHandle(maxCompressedFrameSize);
401 CheckError(MemError(), "Can't allocate output frame buffer.");
402
403 result = CompressSequenceBegin(&gSeq, ((CGrafPort *)(gMainWindow))->portPixMap, 0L, &gMovieRect, 0L, 8, codec,
404 codecType, spatialQ, temporalQ, 15, 0L, kPrevious, gImageDescriptionH);
405 CheckError(result, "Can't begin sequence.");
406 }
407
408 //------------------------------------------------------------------------
409 // We're finished writing to the movie, so save the video track and close it down.
410 //------------------------------------------------------------------------
EndAMovie(void)411 void EndAMovie(void) {
412 OSErr result;
413
414 CDSequenceEnd(gSeq);
415 EndMediaEdits(gMedia);
416
417 result = InsertMediaIntoTrack(gTrack, 0L, 0L, GetMediaDuration(gMedia), 1L << 16);
418 CheckError(result, "Can't insert media into track.");
419
420 // Finally, we're done with the movie.
421 result = AddMovieResource(gMovie, gMovieResNum, 0L, 0L);
422 CheckError(result, "Can't add the movie resource.");
423
424 CloseMovieFile(gMovieResNum);
425
426 if (gCompressedFrameBitsH)
427 DisposeHandle(gCompressedFrameBitsH);
428
429 if (gMovie)
430 DisposeMovie(gMovie);
431 }
432
433 // ===============================================================
434 // QuickTime file dumping routines.
435 // ===============================================================
436
437 // --------------------------------------------------------------
438 //
439 // QuikReadChunkHdr() reads in the next chunk header, returns TRUE if ok.
440
QuikReadChunkHdr(FILE * fp,QT_ChunkHdr * phdr)441 uchar QuikReadChunkHdr(FILE *fp, QT_ChunkHdr *phdr) {
442 fread(phdr, sizeof(QT_ChunkHdr), 1, fp);
443
444 switch (phdr->ctype) {
445 case QT_TRAK:
446 currTrackType = TRACK_OTHER;
447 break;
448
449 case QT_VMHD:
450 currTrackType = TRACK_VIDEO;
451 break;
452
453 case QT_SMHD:
454 currTrackType = TRACK_AUDIO;
455 break;
456 }
457
458 return (feof(fp) == 0);
459 }
460
461 // --------------------------------------------------------------
462 //
463 // QuikFindChunkInfo() finds info for a chunk.
464
QuikFindChunkInfo(QT_ChunkHdr * phdr)465 QT_ChunkInfo *QuikFindChunkInfo(QT_ChunkHdr *phdr) {
466 static QT_Ctype lastType = 0;
467 static QT_ChunkInfo *lastInfoPtr = NULL;
468
469 QT_ChunkInfo *pinfo;
470
471 if (lastType == phdr->ctype)
472 return ((QT_ChunkInfo *)lastInfoPtr);
473
474 pinfo = chunkInfo;
475 while (pinfo->ctype) {
476 if (pinfo->ctype == phdr->ctype) {
477 lastType = phdr->ctype;
478 lastInfoPtr = pinfo;
479 return ((QT_ChunkInfo *)pinfo);
480 }
481 ++pinfo;
482 }
483 return NULL;
484 }
485
486 // --------------------------------------------------------------
487 //
488 // QuikSkipChunk() skips over the data in the current chunk.
489
QuikSkipChunk(FILE * fp,QT_ChunkHdr * phdr)490 void QuikSkipChunk(FILE *fp, QT_ChunkHdr *phdr) { fseek(fp, phdr->length - sizeof(QT_ChunkHdr), SEEK_CUR); }
491
492 //================================================================
493 // QuickTime Sound Track routines.
494 //================================================================
495
496 #define kSoundSampleDuration 1
497 #define kSyncSample 0
498 #define kTrackStart 0
499 #define kMediaStart 0
500 #define kFix1 0x00010000
501
502 //----------------------------------------------------------------
CreateMySoundTrack(Movie theMovie)503 void CreateMySoundTrack(Movie theMovie) {
504 Track theTrack;
505 Media theMedia;
506 Handle sndHandle = nil;
507 SoundDescriptionHandle sndDesc = nil;
508 long sndDataOffset;
509 long sndDataSize;
510 long numSamples;
511 OSErr err = noErr;
512
513 sndHandle = GetIndResource('snd ', 1);
514 CheckError(ResError(), "GetResource 'snd '");
515 if (sndHandle == nil)
516 return;
517
518 sndDesc = (SoundDescriptionHandle)NewHandle(4);
519 CheckError(MemError(), "NewHandle for SoundDesc");
520
521 CreateSoundDescription(sndHandle, sndDesc, &sndDataOffset, &numSamples, &sndDataSize);
522
523 theTrack = NewMovieTrack(theMovie, 0, 0, kFullVolume);
524 CheckError(GetMoviesError(), "New Sound Track");
525
526 theMedia = NewTrackMedia(theTrack, SoundMediaType, FixRound((**sndDesc).sampleRate), nil, 0);
527 CheckError(GetMoviesError(), "New Media snd.");
528
529 err = BeginMediaEdits(theMedia);
530 CheckError(err, "BeginMediaEdits snd.");
531
532 err = AddMediaSample(theMedia, sndHandle, sndDataOffset, sndDataSize, 1, (SampleDescriptionHandle)sndDesc, numSamples,
533 0, nil);
534 CheckError(err, "AddMediaSample snd.");
535
536 err = EndMediaEdits(theMedia);
537 CheckError(err, "EndMediaEdits snd.");
538
539 err = InsertMediaIntoTrack(theTrack, 0, 0, GetMediaDuration(theMedia), kFix1);
540 CheckError(err, "InsertMediaIntoTrack snd.");
541
542 if (sndDesc != nil)
543 DisposeHandle((Handle)sndDesc);
544 }
545
546 //----------------------------------------------------------------
CreateSoundDescription(Handle sndHandle,SoundDescriptionHandle sndDesc,long * sndDataOffset,long * numSamples,long * sndDataSize)547 void CreateSoundDescription(Handle sndHandle, SoundDescriptionHandle sndDesc, long *sndDataOffset, long *numSamples,
548 long *sndDataSize) {
549 long sndHdrOffset = 0;
550 long sampleDataOffset;
551 SoundHeaderPtr sndHdrPtr = nil;
552 long numFrames;
553 long samplesPerFrame;
554 long bytesPerFrame;
555 SignedByte sndHState;
556 SoundDescriptionPtr sndDescPtr;
557
558 *sndDataOffset = 0;
559 *numSamples = 0;
560 *sndDataSize = 0;
561
562 SetHandleSize((Handle)sndDesc, sizeof(SoundDescription));
563 CheckError(MemError(), "SetHandleSize for sndDesc.");
564
565 sndHdrOffset = GetSndHdrOffset(sndHandle);
566 if (sndHdrOffset == 0)
567 CheckError(-1, "GetSndHdrOffset ");
568
569 // we can use pointers since we don't move memory
570 sndHdrPtr = (SoundHeaderPtr)(*sndHandle + sndHdrOffset);
571 sndDescPtr = *sndDesc;
572 sndDescPtr->descSize = sizeof(SoundDescription); // total size of sound desc structure
573 sndDescPtr->resvd1 = 0;
574 sndDescPtr->resvd2 = 0;
575 sndDescPtr->dataRefIndex = 1;
576 sndDescPtr->compressionID = 0;
577 sndDescPtr->packetSize = 0;
578 sndDescPtr->version = 0;
579 sndDescPtr->revlevel = 0;
580 sndDescPtr->vendor = 0;
581
582 switch (sndHdrPtr->encode) {
583 case stdSH:
584 sndDescPtr->dataFormat = 'raw '; // uncompressed offset-binary data
585 sndDescPtr->numChannels = 1; // number of channels of sound
586 sndDescPtr->sampleSize = 8; // number of bits per sample
587 sndDescPtr->sampleRate = sndHdrPtr->sampleRate; // sample rate
588 *numSamples = sndHdrPtr->length;
589 *sndDataSize = *numSamples;
590 bytesPerFrame = 1;
591 samplesPerFrame = 1;
592 sampleDataOffset = (Ptr)&sndHdrPtr->sampleArea - (Ptr)sndHdrPtr;
593 break;
594
595 case extSH: {
596 ExtSoundHeaderPtr extSndHdrP = (ExtSoundHeaderPtr)sndHdrPtr;
597
598 sndDescPtr->dataFormat = 'raw '; // uncompressed offset-binary data
599 sndDescPtr->numChannels = extSndHdrP->numChannels; // number of channels of sound
600 sndDescPtr->sampleSize = extSndHdrP->sampleSize; // number of bits per sample
601 sndDescPtr->sampleRate = extSndHdrP->sampleRate; // sample rate
602 numFrames = extSndHdrP->numFrames;
603 *numSamples = numFrames;
604 bytesPerFrame = extSndHdrP->numChannels * (extSndHdrP->sampleSize / 8);
605 samplesPerFrame = 1;
606 *sndDataSize = numFrames * bytesPerFrame;
607 sampleDataOffset = (Ptr)(&extSndHdrP->sampleArea) - (Ptr)extSndHdrP;
608 } break;
609
610 default:
611 CheckError(-1, "Corrupt sound data or unsupported format.");
612 break;
613 }
614 *sndDataOffset = sndHdrOffset + sampleDataOffset;
615 }
616
617 //----------------------------------------------------------------
618 typedef SndCommand *SndCmdPtr;
619
620 typedef struct {
621 short format;
622 short numSynths;
623 } Snd1Header, *Snd1HdrPtr, **Snd1HdrHndl;
624
625 typedef struct {
626 short format;
627 short refCount;
628 } Snd2Header, *Snd2HdrPtr, **Snd2HdrHndl;
629 typedef struct {
630 short synthID;
631 long initOption;
632 } SynthInfo, *SynthInfoPtr;
633
GetSndHdrOffset(Handle sndHandle)634 long GetSndHdrOffset(Handle sndHandle) {
635 short howManyCmds;
636 long sndOffset = 0;
637 Ptr sndPtr;
638
639 if (sndHandle == nil)
640 return 0;
641 sndPtr = *sndHandle;
642 if (sndPtr == nil)
643 return 0;
644
645 if ((*(Snd1HdrPtr)sndPtr).format == firstSoundFormat) {
646 short synths = ((Snd1HdrPtr)sndPtr)->numSynths;
647 sndPtr += sizeof(Snd1Header) + (sizeof(SynthInfo) * synths);
648 } else {
649 sndPtr += sizeof(Snd2Header);
650 }
651
652 howManyCmds = *(short *)sndPtr;
653
654 sndPtr += sizeof(howManyCmds);
655
656 // sndPtr is now at the first sound command--cruise all
657 // commands and find the first soundCmd or bufferCmd
658
659 while (howManyCmds > 0) {
660 switch (((SndCmdPtr)sndPtr)->cmd) {
661 case (soundCmd + dataOffsetFlag):
662 case (bufferCmd + dataOffsetFlag):
663 sndOffset = ((SndCmdPtr)sndPtr)->param2;
664 howManyCmds = 0; // done, get out of loop
665 break;
666 default: // catch any other type of commands
667 sndPtr += sizeof(SndCommand);
668 howManyCmds--;
669 break;
670 }
671 }
672
673 return sndOffset;
674 }
675
676 /* DEATH MOVIE
677
678 // English.
679 char theText1[] = "They find your body and give it new life.";
680 char theText2[] = "As a cyborg, you will serve SHODAN well.";
681 // German.
682 char theText1[] = "Sie finden deine Leiche und geben ihr neues Leben.";
683 char theText2[] = "Als Cyborg wirst du SHODAN treu dienen.";
684 // French.
685 char theText1[] = "Ils ont trouv ton corps et lui ont redonn la vie.";
686 char theText2[] = "En tant que cyborgue, tu serviras bien SHODAN.";
687
688 // Timing - 507 total
689 blank 9
690 text 1 150
691 blank 17
692 text 2 331
693 */
694
695 /* ENDGAME MOVIE
696
697 // English.
698 char theText1[] = "It's over.";
699 char theText2[] = "They offered you a nice, boring job at Trioptimum; it
700 never occured to you to take it."; char theText3[] = "Old habits die hard.";
701 // German.
702 char theText1[] = "Es ist vorbei.";
703 char theText2[] = "Sie haben dir einen netten Job bei Triop angeboten; es
704 wre dir nie eingefallen, ihr Angebot anzunehmen."; char theText3[] = "Du
705 kannst es eben einfach nicht lassen.";
706 // French.
707 char theText1[] = "C'est fini.";
708 char theText2[] = "Ils vous ont offert un bon boulot ennuyeux sur Triop;
709 a ne vous est jamais venu
l'ide de l'accepter.";
710 char theText3[] = "On a du mal
se dbarasser des mauvaises habitudes.";
711
712 // Timing - 507 total
713 blank 9
714 text 1 180
715 blank 17
716 text 2 176
717 blank 30
718 text 2 1021
719 */
MyCreateTextTrack(Movie theMovie)720 void MyCreateTextTrack(Movie theMovie) {
721 Track theTrack;
722 Media theMedia;
723 OSErr err = noErr;
724 char blankText[] = " ";
725 char theText1[] = "Ils ont trouv ton corps et lui ont redonn la vie.";
726 char theText2[] = "En tant que cyborgue, tu serviras bien SHODAN.";
727 RGBColor black = {0, 0, 0};
728 RGBColor white = {0xeeee, 0xeeee, 0xeeee};
729 Rect textBox;
730 TimeValue retTime;
731
732 SetRect(&textBox, 0, 320, 600, 350);
733 theTrack = NewMovieTrack(theMovie, 600L << 16, 350L << 16, 0);
734 CheckError(GetMoviesError(), "New text track.");
735
736 theMedia = NewTrackMedia(theTrack, TextMediaType, 30, 0L, 0L);
737 CheckError(GetMoviesError(), "New Media for text track.");
738
739 err = BeginMediaEdits(theMedia);
740 CheckError(err, "BeginMediaEdits: text.");
741
742 err = AddTextSample(GetMediaHandler(theMedia), (Ptr)blankText, strlen(blankText), geneva, 14, bold, &white, &black,
743 teCenter, &textBox, 0, 0, 0, 0, nil, 9, &retTime);
744 CheckError(err, "AddTextSample 1.");
745 err = AddTextSample(GetMediaHandler(theMedia), (Ptr)theText1, strlen(theText1), geneva, 14, bold, &white, &black,
746 teCenter, &textBox, 0, 0, 0, 0, nil, 150, &retTime);
747 CheckError(err, "AddTextSample 2.");
748 err = AddTextSample(GetMediaHandler(theMedia), (Ptr)blankText, strlen(blankText), geneva, 14, bold, &white, &black,
749 teCenter, &textBox, 0, 0, 0, 0, nil, 17, &retTime);
750 CheckError(err, "AddTextSample 3.");
751 err = AddTextSample(GetMediaHandler(theMedia), (Ptr)theText2, strlen(theText2), geneva, 14, bold, &white, &black,
752 teCenter, &textBox, 0, 0, 0, 0, nil, 331, &retTime);
753 CheckError(err, "AddTextSample 4.");
754
755 err = EndMediaEdits(theMedia);
756 CheckError(err, "EndMediaEdits: text.");
757
758 err = InsertMediaIntoTrack(theTrack, 0, 0, GetMediaDuration(theMedia), kFix1);
759 CheckError(err, "InsertMediaIntoTrack: text.");
760 }
761