/// <summary>Creates a sampled sound.</summary> /// <param name="sampledSoundsPointer">A memory pointer for sampled sound data.</param> /// <param name="soundIndex">The index of the sound.</param> /// <returns>A Sound.</returns> private SoundSystem.Sound CreateSampledSound(memptr sampledSoundsPointer, int soundIndex) { SampledSound sfxInfo = GetSampledSound(sampledSoundsPointer, soundIndex); byte[] buffer = ConvertSamplesU8toS16(sfxInfo); return _soundSystem.CreateSound(buffer, (int) sfxInfo.hertz, soundIndex, false); }
/* ;=========================================================================== ; ; SCALING GRAPHICS ; ;=========================================================================== */ /* ;================ ; ; doline ; ; Big unwound scaling routine ; ;================ ;================================================== ; ; void scaleline (int scale, unsigned picseg, unsigned maskseg, ; unsigned screen, unsigned width) ; ;================================================== */ private void ScaleLine(ushort pixels, memptr scaleptr, memptr picptr, ushort screen, int x, int width) { // as: Functional Description: // This function modified the code in the subroutine doline to limit the number of pixels drawn to // the value specified by "pixels", after doline completed the previous code was then restored // Adapted for simpler display emulation // // dx = linewidth // es:di = Pointer to screen // ds:si = Pointer to scaling lookup table // as:bx = Pointer to picture // doline is called // doline is restored // return // // doline // This function is up to 256 draw pixel operations, ScaleLine modifies the code depending upon the length of the // line segment to draw writing a "mov ss, cx" followed by a "ret" instruction at the appropriate position // cx <- ss : Save Stack Segment in CX // { // This section repeated 256 times // al <- [ds:si], si++ // This is the scaled pixel index (ds:si = pointer to scaling lookup table) // al <- [ss:bx + al] // Read the pixel (ss:bx = pointer to picture) // al <- [es:di] // Read video ram to latches, write pixel to screen // di += dx // Next line down screen // } // ss <- cx : Restore Stack Segment from CX // return int dstIndex = screen * Display.AddressScale + x; byte[] videoBuffer = _display.VideoBuffer; for(int i = 0; i < pixels; i++) { byte offset = scaleptr.GetUInt8(i); byte color = picptr.GetUInt8(offset); for(int j = 0; j < width; j++) videoBuffer[dstIndex + j] = color; dstIndex += _display.Stride; } }
/// <summary>Creates a new huffnode.</summary> /// <param name="buffer">The byte array containing the data for the huffnode.</param> /// <param name="offset">The offset to the huffnode.</param> public huffnode(byte[] buffer, int offset) { _pointer = new memptr(buffer, offset); }
/// <summary>Creates a new spksndtype.</summary> /// <param name="pointer">The pointer to the sound data.</param> public spksndtype(memptr pointer) { _pointer = pointer; }
/// <summary>Converts a planar pic and caches it.</summary> /// <param name="index">The index of the pic.</param> /// <param name="grseg">The grseg for the pic.</param> /// <param name="width">The width in pixels.</param> /// <param name="height">The height in pixels.</param> /// <param name="count">The number of sub images.</param> /// <returns>The converted pic.</returns> public byte[] CachePic(int index, memptr grseg, int width, int height, int count) { byte[] buffer = new byte[width * height * count]; // Draw the picture byte[] source = grseg.Buffer; int srcIndex = grseg.BaseIndex; int byteWidth = width >> 3; int dstBaseIndex = 0; for(int i = 0; i < count; i++) { byte planeBit = 1; for(int plane = 0; plane < 4; plane++) { int dstIndex = dstBaseIndex; for(int y = 0; y < height; y++) { for(int x = 0; x < byteWidth; x++) { byte pixels = source[srcIndex++]; if((pixels & 0x80) != 0) buffer[dstIndex] |= planeBit; dstIndex++; if((pixels & 0x40) != 0) buffer[dstIndex] |= planeBit; dstIndex++; if((pixels & 0x20) != 0) buffer[dstIndex] |= planeBit; dstIndex++; if((pixels & 0x10) != 0) buffer[dstIndex] |= planeBit; dstIndex++; if((pixels & 0x08) != 0) buffer[dstIndex] |= planeBit; dstIndex++; if((pixels & 0x04) != 0) buffer[dstIndex] |= planeBit; dstIndex++; if((pixels & 0x02) != 0) buffer[dstIndex] |= planeBit; dstIndex++; if((pixels & 0x01) != 0) buffer[dstIndex] |= planeBit; dstIndex++; } } planeBit <<= 1; } dstBaseIndex += width * height; } _pics[index] = buffer; return buffer; }
//=========================================================================== /* ==================================== = = BloadinMM = ==================================== */ private void BloadinMM(string filename, ref memptr spot) { if(_sys.FileExists(filename)) { int length = _sys.FileLength(filename); MMGetPtr(ref spot, length); LoadFile(filename, spot); } else { Quit("BloadinMM: Can't find file " + filename); } }
/// <summary>Initialises a new memptr.</summary> /// <param name="other">Another memory pointer to copy.</param> public memptr(memptr other) : this(other, 0) { }
//========================================================================== /* ========================== = = SegRead = = Read from a file to a segment pointer = ========================== */ private void SegRead(ref memptr handle, memptr dest, int length) { if(length > 0xffff) Quit("SegRead doesn't support 64K reads yet!"); Array.Copy(handle.Buffer, handle.BaseIndex, dest.Buffer, dest.BaseIndex, length); handle.Offset(length); }
//========================================================================== ///////////////////////////////////////////////////////// // // InitGrFile // ///////////////////////////////////////////////////////// private void InitGrFile() { // // calculate some offsets in the header // grhuffman = new huffnode(grhead); grstarts = new memptr(grhead.Buffer, grhead.dataoffsets); OptimizeNodes(grhuffman); // // Open the graphics file, leaving it open until the game is finished // grhandle = _sys.open("EGAGRAPH." + EXTENSION); if(grhandle.IsNull) Quit("Cannot open EGAGRAPH." + EXTENSION + "!"); memptr buffer = new memptr(); // // load the pic and sprite headers into the data segment // needgr[STRUCTPIC] = 1; // make sure this chunk never reloads grsegs[STRUCTPIC] = new memptr(null, 0xffff); GetChunkLength(STRUCTPIC); // position file pointer MMGetPtr(ref buffer, chunkcomplen); SegRead(ref grhandle, buffer, chunkcomplen); // as: Added temp pointer memptr temp = new memptr(new byte[pictable.Length * pictype.SizeOf], 0); HuffExpand(buffer.Buffer, buffer.BaseIndex, temp.Buffer, 0, temp.Buffer.Length, grhuffman); // as: Initialise pictypes for(int i = 0; i < pictable.Length; i++) { pictable[i] = new pictype(temp); temp.Offset(pictype.SizeOf); } MMFreePtr(ref buffer); }
//========================================================================== /* ===================== = = MMSetPurge = = Sets the purge level for a block = ===================== */ private void MMSetPurge(ref memptr baseptr, short purge) { }
//========================================================================== //////////////////////////////////////////////////////////// // // Allocate memory and load file in // //////////////////////////////////////////////////////////// private void LoadIn(string filename, /*char huge **baseptr*/ out memptr baseptr) { if(!_sys.FileExists(filename)) { _sys.printf("Error loading file '%s'!\n", filename); _sys.exit(1); } int len = _sys.FileLength(filename); baseptr = new memptr(_sys.farmalloc(len)); LoadFile(filename, baseptr); }
//========================================================================== /* ==================== = = MMGetPtr = = Allocates an unlocked, unpurgable block = Start looking at the top of memory = ==================== */ private void MMGetPtr(ref memptr baseptr, int size) { baseptr.Buffer = new byte[size]; baseptr.BaseIndex = 0; }
//========================================================================== /* ===================== = = MMFreePtr = = Frees up a block and NULL's the pointer = ===================== */ private void MMFreePtr(ref memptr baseptr) { baseptr.Buffer = null; baseptr.BaseIndex = 0; }
/// <summary>Creates a speaker sound.</summary> /// <param name="soundsPointer">A memory pointer to the speaker sound data.</param> /// <param name="soundIndex">The index of the sound.</param> /// <returns>A Sound.</returns> private SoundSystem.Sound CreateSpeakerSound(memptr soundsPointer, int soundIndex) { spksndtype speakerSound = GetSpeakerSound(soundsPointer, soundIndex); byte[] samples = GetSpeakerSamplesS16(speakerSound, soundsPointer, _speakerSampleFrequency, _speakerSampleAmplitude); return _soundSystem.CreateSound(samples, _speakerSampleFrequency, soundIndex, true); }
public static void HuffExpand(memptr source, memptr dest, int length, huffnode hufftable) { HuffExpand(source.Buffer, source.BaseIndex, dest.Buffer, dest.BaseIndex, length, hufftable); }
private void CacheGrFile() { // // make unneeded chunks purgable // for(short i = 0; i < NUMCHUNKS; i++) if(grsegs[i].Buffer != null && needgr[i] == 0) MMSetPurge(ref grsegs[i], 3); MMSortMem(); // // load new stuff // grhandle.BaseIndex = 0; int filepos = 0; for(short i = 0; i < NUMCHUNKS; i++) { if(grsegs[i].Buffer == null && needgr[i] != 0) { int newpos = grstarts.GetInt32(i * 4); if(newpos != filepos) grhandle.Offset(newpos - filepos); // chunk lengths int compressed = grstarts.GetInt32((i + 1) * 4) - grstarts.GetInt32(i * 4) - 4; int expanded; if(i >= STARTTILE8) { // // tiles are of a known size // if(i < STARTTILE8M) // tile 8s are all in one chunk! expanded = BLOCK * NUMTILE8; else if(i < STARTTILE16) expanded = MASKBLOCK * NUMTILE8M; else if(i < STARTTILE16M) // all other tiles are one/chunk expanded = BLOCK * 4; else if(i < STARTTILE32) expanded = MASKBLOCK * 4; else if(i < STARTTILE32M) expanded = BLOCK * 16; else expanded = MASKBLOCK * 16; compressed = grstarts.GetInt32((i + 1) * 4) - grstarts.GetInt32(i * 4); } else { // // other things have a length header at start of chunk // expanded = grhandle.GetInt32(0); grhandle.Offset(4); compressed = grstarts.GetInt32((i + 1) * 4) - grstarts.GetInt32(i * 4) - 4; } // // allocate space for expanded chunk // MMGetPtr(ref grsegs[i], expanded); // // if the entire compressed length can't fit in the general purpose // buffer, allocate a temporary memory block for it // if(compressed <= BUFFERSIZE) { SegRead(ref grhandle, bufferseg, compressed); HuffExpand(bufferseg, grsegs[i], expanded, grhuffman); } else { memptr bigbufferseg = new memptr(); // for compressed MMGetPtr(ref bigbufferseg, compressed); SegRead(ref grhandle, bigbufferseg, compressed); HuffExpand(bigbufferseg, grsegs[i], expanded, grhuffman); MMFreePtr(ref bigbufferseg); } filepos = grstarts.GetInt32((i + 1) * 4); // file pointer is now at start of next one } } // as: pic cache _picCache.CachePic(STARTTILE8, grsegs[STARTTILE8], 8, 8, 72); }
public static void RLEWExpand(memptr source, memptr dest) { int length = source.GetInt32(0); memptr end = new memptr(dest, length); source.Offset(4); // skip length words // // expand it // do { ushort value = source.GetUInt16(0); source.Offset(2); if(value != RLETAG) { // // uncompressed // dest.SetUInt16(0, value); dest.Offset(2); } else { // // compressed string // ushort count = source.GetUInt16(0); source.Offset(2); value = source.GetUInt16(0); source.Offset(2); if(dest.BaseIndex + count * 2 > end.BaseIndex) throw new Exception("RLEWExpand error!"); for(ushort i = 1; i <= count; i++) { dest.SetUInt16(0, value); dest.Offset(2); } } } while(dest.BaseIndex < end.BaseIndex); }
//========================================================================== /* ===================== = = CachePic = = Make sure a graphic chunk is in memory = ===================== */ private void CachePic(short picnum) { if(grsegs[picnum].Buffer != null) return; grhandle.BaseIndex = grstarts.GetInt32(picnum * 4); // chunk lengths int compressed = grstarts.GetInt32((picnum + 1) * 4) - grstarts.GetInt32(picnum * 4) - 4; // as: redundant int expanded; if(picnum >= STARTTILE8) { // // tiles are of a known size // if(picnum < STARTTILE8M) // tile 8s are all in one chunk! expanded = BLOCK * NUMTILE8; else if(picnum < STARTTILE16) expanded = MASKBLOCK * NUMTILE8M; else if(picnum < STARTTILE16M) // all other tiles are one/chunk expanded = BLOCK * 4; else if(picnum < STARTTILE32) expanded = MASKBLOCK * 4; else if(picnum < STARTTILE32M) expanded = BLOCK * 16; else expanded = MASKBLOCK * 16; compressed = grstarts.GetInt32((picnum + 1) * 4) - grstarts.GetInt32(picnum * 4); } else { // // other things have a length header at start of chunk // expanded = grhandle.GetInt32(0); grhandle.Offset(4); compressed = grstarts.GetInt32((picnum + 1) * 4) - grstarts.GetInt32(picnum * 4) - 4; } // // allocate space for expanded chunk // MMGetPtr(ref grsegs[picnum], expanded); memptr bigbufferseg = new memptr(); // for compressed MMGetPtr(ref bigbufferseg, compressed); SegRead(ref grhandle, bigbufferseg, compressed); HuffExpand(bigbufferseg, grsegs[picnum], expanded, grhuffman); MMFreePtr(ref bigbufferseg); // as: Add to pic cache if necessary if(_picCache[picnum] == null) _picCache.CachePic(picnum, grsegs[picnum], pictable[picnum - STARTPICS]); }
/// <summary>Initialises a new memptr from another memptr.</summary> /// <param name="other">Another memory pointer.</param> /// <param name="offset">The offset in bytes.</param> public memptr(memptr other, int offset) { _buffer = other.Buffer; _baseIndex = other.BaseIndex + offset; }
//========================================================================== /* ====================== = = LoadLevel = = Loads LEVEL00.EXT (00 = global variable level) = ====================== */ private void LoadLevel() { // // load the new level in and decompress // string filename, num; if(level < 10) { num = level.ToString(); filename = "LEVEL0"; } else { num = level.ToString(); filename = "LEVEL"; } filename = string.Concat(filename, num); filename = string.Concat(filename, "." + EXTENSION); memptr bufferseg = new memptr(); BloadinMM(filename, ref bufferseg); ushort length = bufferseg.GetUInt16(0); if(levelseg.Buffer != null) MMFreePtr(ref levelseg); MMGetPtr(ref levelseg, length); // as: Made RLEWExpand static try { RLEWExpand(bufferseg, levelseg); } catch(Exception ex) { // as: Quit on failure Quit(ex.Message); } MMFreePtr(ref bufferseg); levelheader = new LevelDef(levelseg); // // copy plane 0 to tilemap // memptr planeptr = new memptr(levelseg, 32); int index = 0; for(short y = 0; y < levelheader.height; y++) for(short x = 0; x < levelheader.width; x++, index += 2) tilemap[x, y] = planeptr.GetUInt16(index); // // spawn tanks // planeptr = new memptr(levelseg, 32 + levelheader.planesize); StartLevel(planeptr); MMFreePtr(ref levelseg); }
/// <summary>Creates a new fontstruct.</summary> /// <param name="pointer">A pointer to the font data.</param> public fontstruct(memptr pointer) { _pointer = pointer; }
private const float DISTANCE = 1000.0f; // center point z distance /* ===================== = = Intro = ===================== */ private void Intro() { memptr shapeseg; short i, f, sx, sy, page; ushort[] pageptr = new ushort[2]; ushort[] pagewidth = new ushort[2]; ushort[] pageheight = new ushort[2]; float x, y, z, angle, step, sn, cs, sizescale, scale; float ytop, xmid, minz, worldycenter, worldxcenter; FadeOut(); SetLineWidth(SCREENWIDTH); screenofs = 0; CacheDrawPic(STARSPIC); pxl = 0; pxh = 320; py = 180; #if DISABLED && !CATALOG CPPrint("Copyright (c) 1991-93 Softdisk Publishing\n"); //CPPrint("'I' for information"); #endif EGAWRITEMODE(1); EGAMAPMASK(15); CopyEGA(40, 200, 0, 0x4000); CopyEGA(40, 200, 0, 0x8000); CopyEGA(40, 200, 0, 0xc000); StopDrive(); CachePic(STARTPICS + LOGOPIC); // as: The following is disabled because it throws an exception // due to trying to make a 0x0 shape #if DISABLED_BROKEN shapeseg = new memptr(); SC_MakeShape( grsegs[STARTPICS + LOGOPIC], 0, 0, ref shapeseg); #endif // as: the following was commented out, it displays the logo zooming in an arc // this can be enabled with the -intrologo command shapeseg = new memptr(); if(_sys.IntroLogo) { SC_MakeShape( grsegs[STARTPICS + LOGOPIC], pictable[LOGOPIC].width, pictable[LOGOPIC].height, ref shapeseg); } MMFreePtr(ref grsegs[STARTPICS + LOGOPIC]); FadeIn(); sx = 160; sy = 180; /* ============================================================================= SCALED PICTURE DIRECTOR ============================================================================= */ minz = (float) (Math.Cos(MAXANGLE) * RADIUS); // closest point minz += DISTANCE; sizescale = 256 * minz; // closest point will be full size ytop = 80 - (PICHEIGHT / 2) * (sizescale / DISTANCE) / 256; z = sizescale / (DISTANCE * 256); ytop = ytop / z; // world coordinates worldycenter = ytop - RADIUS; xmid = (float) (Math.Sin(MAXANGLE) * RADIUS / 2); worldxcenter = -xmid; if(_sys.SimulateIntroSound) _sfxPlayer.PlaySound(INTROSND); f = 1; page = 0; inttime = 0; screenofs = 0; pagewidth[0] = 0; pagewidth[1] = 0; do { step = f / NUMFRAMES; angle = MAXANGLE * step; sn = (float) Math.Sin(angle); cs = (float) Math.Cos(angle); x = worldxcenter + sn * RADIUS / 2; y = worldycenter + sn * RADIUS; z = DISTANCE + cs * RADIUS; scale = sizescale / z; sx = (short) (160 + (short) (x * scale / 256)); sy = (short) (100 - (short) (y * scale / 256)); inttime = 0; _sys.sound((ushort) ((short) (sn * 1500))); if(_sys.IntroLogo) { // // erase old position // if(pagewidth[page] != 0) { EGAWRITEMODE(1); EGAMAPMASK(15); CopyEGA(pagewidth[page], pageheight[page], (ushort) (pageptr[page] + 0x8000), pageptr[page]); } // // draw new position // EGAWRITEMODE(2); if(SC_ScaleShape(sx, sy, (ushort) ((short) scale < 40 ? 10 : scale / 4), shapeseg)) { pagewidth[page] = scaleblockwidth; pageheight[page] = scaleblockheight; pageptr[page] = scaleblockdest; } else { pagewidth[page] = 0; } } EGAWRITEMODE(0); EGABITMASK(255); // // display it // SetScreen(screenofs, 0); page ^= 1; screenofs = (ushort) (0x4000 * page); f++; if(f < NUMFRAMES) { f += (short) inttime; if(f > NUMFRAMES) f = (short) NUMFRAMES; } else { f++; // last frame is shown } if(NBKscan > 0x7f) break; } while(f <= NUMFRAMES); if(_sys.SimulateIntroSound) // as: Intro sound stops when a key is pressed _sfxPlayer.StopSound(); _sys.nosound(); for(i = 0; i < 200; i++) { WaitVBL(1); if(NBKscan > 0x7f) { // as: With the following enabled, pressing I during the intro displays a // window with a message, part of the text overflows the window though // this can be enabled with the -introinfo command //#if false if(_sys.IntroInfo) { if(NBKscan == 0x97) //'I' for info { screenofs ^= 0x4000; CenterWindow(24, 10); py += 2; CPPrint(_strings[Strings.Intro1]); // as: string replacements CPPrint(_strings[Strings.Intro2]); // as: string replacements CPPrint(_strings[Strings.Intro3]); // as: string replacements CPPrint(_strings[Strings.Intro4]); // as: string replacements CPPrint(_strings[Strings.Intro5]); // as: string replacements CPPrint(_strings[Strings.Intro6]); // as: string replacements CPPrint(_strings[Strings.Intro7]); // as: string replacements ClearKeys(); Ack(); } ClearKeys(); break; } //#endif } } MMFreePtr(ref shapeseg); }
/// <summary>Converts a planar pic and caches it.</summary> /// <param name="index">The index of the pic.</param> /// <param name="grseg">The grseg for the pic.</param> /// <param name="picType">The pic type.</param> /// <returns>The converted pic.</returns> public byte[] CachePic(int index, memptr grseg, pictype picType) { return CachePic(index, grseg, picType.width * Display.ColumnScale, picType.height, 1); }
//========================================================================== ///////////////////////////////////////////////////////// // // Load a LARGE file into a FAR buffer! // ///////////////////////////////////////////////////////// private uint LoadFile(string filename, memptr buffer) { return _sys.FileReadAllBytes(filename, buffer.Buffer); }
/// <summary>Creates a new grheadtype.</summary> /// <param name="buffer">A byte array containing the data.</param> public grheadtype(byte[] buffer) { _pointer = new memptr(buffer, 0); }
private void SaveCtrls() { memptr handle = new memptr(new byte[key.Length + 2 + 4 + 2]); for(int i = 0; i < key.Length; i++) handle.SetInt8(i, key[i]); handle.Offset(key.Length); handle.SetInt8(0, keyB1); handle.Offset(1); handle.SetInt8(0, keyB2); handle.Offset(1); handle.SetInt32(0, highscore); handle.Offset(4); handle.SetInt16(0, bestlevel); handle.Offset(2); _sys.SaveControls(handle); // as: Handled separately now }
/// <summary>Creates a new LevelDef.</summary> /// <param name="pointer">The pointer to the level data.</param> public LevelDef(memptr pointer) { _pointer = pointer; }
/* ====================== = = HuffExpand = ====================== */ public static void HuffExpand(byte[] sourceBuffer, int sourceIndex, byte[] destBuffer, int destIndex, int length, huffnode hufftable) { memptr source = new memptr(sourceBuffer, sourceIndex); memptr dest = new memptr(destBuffer, destIndex); ushort bit, _byte, code; huffnode nodeon, headptr; headptr = new huffnode(hufftable, 254); // head node is allways node 254 // as: The disabled C code that was in this function appears to be the C version of the asm code // this came in handy during the conversion nodeon = new huffnode(headptr); // as: bugfix - refactored to prevent the out of bounds read that can occur occasionally with the final byte bit = 256; _byte = 0; while(length != 0) { if(bit == 256) { bit = 1; _byte = source.GetUInt8(0); source.Offset(1); } if((_byte & bit) != 0) code = nodeon.bit1; else code = nodeon.bit0; bit <<= 1; if(code < 256) { dest.SetUInt8(0, (byte) code); dest.Offset(1); nodeon = headptr; length--; } else { nodeon = new huffnode(hufftable, code - 256); } } }
/// <summary>Creates a new SampledSound.</summary> /// <param name="pointer">The pointer to the SampledSound structure.</param> public SampledSound(memptr pointer) { _pointer = pointer; }
/// <summary>Initialises the speaker sound player.</summary> /// <param name="speakerSoundsPointer">The pointer to the speaker sounds data.</param> public void InitSpeakerSound(memptr speakerSoundsPointer) { int count = 0; bool[] soundUsed = new bool[_soundLinks.Length]; for(int i = MinSoundIndex; i <= MaxSoundIndex; i++) { int soundIndex = _soundLinks[i]; if(!soundUsed[soundIndex]) { soundUsed[soundIndex] = true; count++; } } for(int i = 0; i < count; i++) _speakerSounds.Add(CreateSpeakerSound(speakerSoundsPointer, MinSoundIndex + i)); }