/* Copyleft {C} 2014 Michael Pohoreski License: GPL2 Description: command line utility to convert an 8K Apple .hgr to .tga (or .bmp) https://github.com/Michaelangel007/hgr2rgbntsc Notes: Extracted from AppleWin NTSC https://github.com/AppleWin/AppleWin/tree/AppleWin-Sheldon/source MSVC2010 Debug: Configuration Properties, Debugging Command Arguments: hgr/archon.hgr2 Working Directory: $(ProjectDir) */ // Includes _______________________________________________________________ #include "StdAfx.h" #include "wsvideo.cpp" #include "cs.cpp" // Defines ________________________________________________________________ #if defined(_MSC_VER) #define PACKED #pragma pack(push,1) #endif #if defined(__GNUC__) #define PACKED __attribute__ ((__packed__)) #endif // Types __________________________________________________________________ enum { _8K = 8 * 1024, _64K = 64 * 1024 }; enum TargaImageType_e { TARGA_TYPE_RGB = 2 }; struct TargaHeader_t { // Addr Bytes uint8_t nIdBytes ; // 00 01 size of ID field that follows 18 byte header (0 usually) uint8_t bHasPalette ; // 01 01 uint8_t iImageType ; // 02 01 type of image 0=none,1=indexed,2=rgb,3=grey,+8=rle packed int16_t iPaletteFirstColor ; // 03 02 int16_t nPaletteColors ; // 05 02 uint8_t nPaletteBitsPerEntry ; // 07 01 number of bits per palette entry 15,16,24,32 int16_t nOriginX ; // 08 02 image x origin int16_t nOriginY ; // 0A 02 image y origin int16_t nWidthPixels ; // 0C 02 int16_t nHeightPixels ; // 0E 02 uint8_t nBitsPerPixel ; // 10 01 image bits per pixel 8,16,24,32 uint8_t iDescriptor ; // 11 01 image descriptor bits (vh flip bits) // pixel data... //uint8_t aPixelData[1] ; // 12 ?? variable length RGB data } PACKED; enum BitmapImageType_e { BMP_TYPE_RGB = 0 // WinGDI.h BI_RGB }; struct bgra_t { uint8_t b; uint8_t g; uint8_t r; uint8_t a; // reserved on Win32 }; struct WinBmpHeader_t { // BITMAPFILEHEADER // Addr Size uint8_t nCookie[2] ; // 0x00 0x02 BM int32_t nSizeFile ; // 0x02 0x04 0 = ignore int16_t nReserved1 ; // 0x06 0x02 int16_t nReserved2 ; // 0x08 0x02 int32_t nOffsetData ; // 0x0A 0x04 // == 0x0D (14) // BITMAPINFOHEADER int32_t nStructSize ; // 0x0E 0x04 biSize int32_t nWidthPixels ; // 0x12 0x04 biWidth int32_t nHeightPixels ; // 0x16 0x04 biHeight int16_t nPlanes ; // 0x1A 0x02 biPlanes int16_t nBitsPerPixel ; // 0x1C 0x02 biBitCount int32_t nCompression ; // 0x1E 0x04 biCompression 0 = BI_RGB int32_t nSizeImage ; // 0x22 0x04 0 = ignore int32_t nXPelsPerMeter ; // 0x26 0x04 int32_t nYPelsPerMeter ; // 0x2A 0x04 int32_t nPaletteColors ; // 0x2E 0x04 int32_t nImportantColors; // 0x32 0x04 // == 0x28 (40) // RGBQUAD // pixelmap } PACKED; #ifdef _MSC_VER #pragma pack(pop) #endif // _MSC_VER // Video.h enum VideoFlag_e { VF_80COL = 0x00000001, VF_DHIRES = 0x00000002, VF_HIRES = 0x00000004, VF_MASK2 = 0x00000008, VF_MIXED = 0x00000010, VF_PAGE2 = 0x00000020, VF_TEXT = 0x00000040 }; enum VideoType_e { VT_COLOR_TV , VT_COLOR_MONITOR , VT_MONO_TV , VT_MONO_MONITOR , NUM_VIDEO_MODES }; // Globals (Private ) _____________________________________________________ uint8_t gaMemMain[ _64K ]; uint8_t gaMemAux [ _64K ]; uint32_t gaFrameBuffer[ FRAMEBUFFER_H ][ FRAMEBUFFER_W ]; int g_bVideoMode; VideoType_e g_eVideoType; int g_uHalfScanLines; // 1=For TV double each line char BAD_TARGA__HEADER_SIZE_Compiler_Packing_not_18[ sizeof( TargaHeader_t ) == 18 ]; char BAD_BITMAP_HEADER_SIZE_Compiler_Packing_not_54[ sizeof( WinBmpHeader_t ) == (14 + 40) ]; bool g_bOutputBMP = false; // Prototypes _____________________________________________________________ void convert( const char *pSrcFileName ); void init_mem(); void init_videomode(); void hgr2rgb(); void rgb2bmp( FILE *pDstFile ); void rgb2tga( FILE *pDstFile ); void rgb32to24write( FILE *pDstFile ); int usage(); // Implementation _________________________________________________________ //======================================================================== void convert( const char *pSrcFileName ) { char aDstFileName[ 256 ]; char *pDstFileName = aDstFileName; size_t nLen = strlen( pSrcFileName ); strcpy( aDstFileName , pSrcFileName ); strcat( pDstFileName + nLen, g_bOutputBMP ? ".bmp" : ".tga" ); printf( "Src: '%s'\n", pSrcFileName ); printf( "Dst: '%s'\n", pDstFileName ); FILE *pSrcFile = fopen( pSrcFileName, "rb" ); FILE *pDstFile = fopen( pDstFileName, "w+b" ); if( pSrcFile ) { size_t nPageHGR = 0x2000; fread( gaMemMain + nPageHGR, _8K, 1, pSrcFile ); hgr2rgb(); if( g_bOutputBMP ) rgb2bmp( pDstFile ); else rgb2tga( pDstFile ); } else printf( "ERROR: Couldn't open source file: '%s'\n", pSrcFileName ); fclose( pDstFile ); fclose( pSrcFile ); } //======================================================================== void hgr2rgb() { g_pFuncVideoUpdate( VIDEO_SCANNER_6502_CYCLES ); } //======================================================================== void init_mem() { memset( gaMemMain, 0, sizeof( gaMemMain ) ); memset( gaMemMain, 0, sizeof( gaMemAux ) ); memset( gaFrameBuffer, 0, sizeof( gaFrameBuffer ) ); } void init_videomode() { // From Video.cpp VideoSetMode() wsTextPage = 1; wsHiresPage = 1; if (g_bVideoMode & VF_PAGE2) { if (0 == (g_bVideoMode & VF_MASK2)) { wsTextPage = 2; wsHiresPage = 2; } } if (g_bVideoMode & VF_TEXT) { if (g_bVideoMode & VF_80COL) g_pFuncVideoUpdate = wsUpdateVideoText80; else g_pFuncVideoUpdate = wsUpdateVideoText40; } else if (g_bVideoMode & VF_HIRES) { if (g_bVideoMode & VF_DHIRES) if (g_bVideoMode & VF_80COL) g_pFuncVideoUpdate = wsUpdateVideoDblHires; else g_pFuncVideoUpdate = wsUpdateVideoHires0; else g_pFuncVideoUpdate = wsUpdateVideoHires; } else { if (g_bVideoMode & VF_DHIRES) if (g_bVideoMode & VF_80COL) g_pFuncVideoUpdate = wsUpdateVideoDblLores; else g_pFuncVideoUpdate = wsUpdateVideo7MLores; else g_pFuncVideoUpdate = wsUpdateVideoLores; } } uint8_t *MemGetMainPtr( uint16_t address ) { return &gaMemMain[ address ]; } uint8_t *MemGetAuxPtr ( uint16_t address ) { return &gaMemAux[ address ]; } //======================================================================== void rgb2bmp( FILE *pDstFile ) { WinBmpHeader_t bmp, *pBMP = &bmp; pBMP->nCookie[ 0 ] = 'B'; // 0x42 pBMP->nCookie[ 1 ] = 'M'; // 0x4d pBMP->nSizeFile = 0; pBMP->nReserved1 = 0; pBMP->nReserved2 = 0; pBMP->nOffsetData = sizeof(WinBmpHeader_t); pBMP->nStructSize = 0x28; // sizeof( WinBmpHeader_t ); pBMP->nWidthPixels = FRAMEBUFFER_W; pBMP->nHeightPixels = FRAMEBUFFER_H; pBMP->nPlanes = 1; pBMP->nBitsPerPixel = 24; pBMP->nCompression = BMP_TYPE_RGB; // BI_RGB; pBMP->nSizeImage = 0; pBMP->nXPelsPerMeter = 0; pBMP->nYPelsPerMeter = 0; pBMP->nPaletteColors = 0; pBMP->nImportantColors = 0; fwrite( (void*)&bmp, sizeof( WinBmpHeader_t ), 1, pDstFile ); rgb32to24write( pDstFile ); } //======================================================================== void rgb2tga( FILE *pDstFile ) { TargaHeader_t tga, *pTargaHeader = &tga; memset( (void*)pTargaHeader, 0, sizeof( TargaHeader_t ) ); pTargaHeader->iImageType = TARGA_TYPE_RGB; pTargaHeader->nWidthPixels = FRAMEBUFFER_W; pTargaHeader->nHeightPixels = FRAMEBUFFER_H; pTargaHeader->nBitsPerPixel = 24; fwrite( (void*)&tga , sizeof( TargaHeader_t ), 1, pDstFile ); //fwrite( (void*)&gaFrameBuffer, sizeof( gaFrameBuffer ), 1, pDstFile ); // See 32-bit note below rgb32to24write( pDstFile ); } void rgb32to24write( FILE *pDstFile ) { const int SRC_LINE_BYTES = FRAMEBUFFER_W * 4; // 32-bit const int DST_LINE_BYTES = FRAMEBUFFER_W * 3; // 24-bit uint8_t destLine[ DST_LINE_BYTES ]; /// Note: Framebuffer is 32-bit but we need to write 24-bit uint8_t *pSrc = (uint8_t*) &gaFrameBuffer[0][0]; uint8_t *pDst; if( g_uHalfScanLines ) pSrc += SRC_LINE_BYTES; // start on odd scanline for( int y = 0; y < FRAMEBUFFER_H; y++ ) { pDst = destLine; for( int x = 0; x < FRAMEBUFFER_W; x++ ) { *pDst++ = *pSrc++; // b *pDst++ = *pSrc++; // g *pDst++ = *pSrc++; // r /* */ pSrc++; // a skip } fwrite( (void*)&destLine, DST_LINE_BYTES, 1, pDstFile ); if( g_uHalfScanLines ) { fwrite( (void*)&destLine, DST_LINE_BYTES, 1, pDstFile ); y++; pSrc += SRC_LINE_BYTES; // skip odd source scanlines } } } //======================================================================== int usage() { printf( "hgr2rgb, Version 2 by Michael Pohoreski\n" "Convert filename.hgr to filename.hgr.tga (default)\n" "\n" "usage: [-bmp | -tga] [-tv] [-half] filename.hgr\n" "\n" " -tga Output to .tga (default)\n" " -bmp Output to .bmp\n" " -tv Use Color TV rendering (default is Color Monitor)\n" " -half Line double (default is single line rendering)\n" "\n" "Source code and examples can be found at:\n" " https://github.com/Michaelangel007/hgr2rgbntsc\n" ); return 0; } //======================================================================== int main( const int nArg, const char *aArg[] ) { init_mem(); // From: Video.cpp wsVideoCreateDIBSection() uint8_t *g_pFramebufferbits = (uint8_t*) &gaFrameBuffer[0][0]; for (int y = 0; y < 384; y++) wsLines[y] = g_pFramebufferbits + 4 * FRAMEBUFFER_W * ((FRAMEBUFFER_H - 1) - y - 18) + 80; g_eVideoType = VT_COLOR_MONITOR; // VT_COLOR_TV g_uHalfScanLines = 0; wsVideoInitModel( 1 ); // Apple //e wsVideoInit(); wsVideoStyle( g_eVideoType, g_uHalfScanLines ); // 1=single scan line, 2=double scan line g_bVideoMode = VF_HIRES; init_videomode(); if( nArg > 1 ) { int iArg = 1; for( int i = 1; i < nArg; i++ ) { if( strcmp( aArg[i], "-bmp" ) == 0 ) g_bOutputBMP = true; else if( strcmp( aArg[i], "-tga" ) == 0 ) g_bOutputBMP = false; else if( strcmp( aArg[i], "-tv" ) == 0 ) { g_eVideoType = VT_COLOR_TV; wsVideoStyle( g_eVideoType, g_uHalfScanLines ); } else if( strcmp( aArg[i], "-half" ) == 0 ) { g_uHalfScanLines = 1; wsVideoStyle( g_eVideoType, g_uHalfScanLines ); } else if( strcmp( aArg[i], "-?" ) == 0 ) return usage(); iArg = i; } if (iArg < nArg) convert( aArg[ iArg ] ); } else usage(); return 0; }