Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /****************************  containers.cpp  **********************************
  2. * Author:        Agner Fog
  3. * Date created:  2006-07-15
  4. * Last modified: 2016-07-07
  5. * Project:       objconv
  6. * Module:        containers.cpp
  7. * Description:
  8. * Objconv is a portable C++ program for converting object file formats.
  9. * Compile for console mode on any platform.
  10. *
  11. * This module contains container classes CMemoryBuffer and CFileBuffer for
  12. * dynamic memory allocation and file read/write. See containers.h for
  13. * further description.
  14. *
  15. * Copyright 2006-2016 GNU General Public License http://www.gnu.org/licenses
  16. *****************************************************************************/
  17.  
  18. #include "stdafx.h"
  19.  
  20. // Names of file formats
  21. SIntTxt FileFormatNames[] = {
  22.     {FILETYPE_COFF,         "COFF"},
  23.     {FILETYPE_OMF,          "OMF"},
  24.     {FILETYPE_ELF,          "ELF"},
  25.     {FILETYPE_MACHO_LE,     "Mach-O Little Endian"},
  26.     {FILETYPE_MACHO_BE,     "Mach-O Big Endian"},
  27.     {FILETYPE_DOS,          "DOS executable"},
  28.     {FILETYPE_WIN3X,        "Windows 3.x executable"},
  29.     {FILETYPE_LIBRARY,      "Function library"},
  30.     {FILETYPE_OMFLIBRARY,   "Function library (OMF)"},
  31.     {IMPORT_LIBRARY_MEMBER, "Windows import library member"},
  32.     {FILETYPE_MAC_UNIVBIN,  "MacIntosh universal binary"},  
  33.     {FILETYPE_MS_WPO,       "Whole program optimization intermediate file, Microsoft specific"},
  34.     {FILETYPE_INTEL_WPO,    "Whole program optimization intermediate file, Intel specific"},
  35.     {FILETYPE_WIN_UNKNOWN,  "Unknown subtype, Windows"},  
  36.     {FILETYPE_ASM,          "Disassembly"}
  37. };
  38.  
  39.  
  40. // Members of class CMemoryBuffer
  41. CMemoryBuffer::CMemoryBuffer() {  
  42.     // Constructor
  43.     buffer = 0;
  44.     NumEntries = DataSize = BufferSize = 0;
  45. }
  46.  
  47. CMemoryBuffer::~CMemoryBuffer() {
  48.     // Destructor
  49.     SetSize(0);                         // De-allocate buffer
  50. }
  51.  
  52. void CMemoryBuffer::SetSize(uint32 size) {
  53.     // Allocate, reallocate or deallocate buffer of specified size.
  54.     // DataSize is initially zero. It is increased by Push or PushString.
  55.     // Setting size > DataSize will allocate more buffer and fill it with zeroes but not increase DataSize.
  56.     // Setting size < DataSize will decrease DataSize so that some of the data are discarded.
  57.     // Setting size = 0 will discard all data and de-allocate the buffer.
  58.     if (size == 0) {
  59.         // Deallocate
  60.         if (buffer) delete[] buffer;     // De-allocate buffer
  61.         buffer = 0;
  62.         NumEntries = DataSize = BufferSize = 0;
  63.         return;
  64.     }
  65.     if (size < DataSize) {
  66.         // Request to delete some data
  67.         DataSize = size;
  68.         return;
  69.     }
  70.     if (size <= BufferSize) {
  71.         // Request to reduce size but not delete it
  72.         return;                          // Ignore
  73.     }
  74. //  size = (size + 15) & uint32(-16);   // Round up size to value divisible by 16
  75.     size = (size + BufferSize + 15) & uint32(-16);   // Double size and round up to value divisible by 16
  76.     int8 * buffer2 = 0;                 // New buffer
  77.     buffer2 = new int8[size];           // Allocate new buffer
  78.     if (buffer2 == 0) {err.submit(9006); return;} // Error can't allocate
  79.     memset (buffer2, 0, size);          // Initialize to all zeroes
  80.     if (buffer) {
  81.         // A smaller buffer is previously allocated
  82.         memcpy (buffer2, buffer, BufferSize); // Copy contents of old buffer into new
  83.         delete[] buffer;                 // De-allocate old buffer
  84.     }
  85.     buffer = buffer2;                   // Save pointer to buffer
  86.     BufferSize = size;                  // Save size
  87. }
  88.  
  89. uint32 CMemoryBuffer::Push(void const * obj, uint32 size) {
  90.     // Add object to buffer, return offset
  91.     // Parameters:
  92.     // obj = pointer to object, 0 if fill with zeroes
  93.     // size = size of object to push
  94.  
  95.     // Old offset will be offset to new object
  96.     uint32 OldOffset = DataSize;
  97.  
  98.     // New data size will be old data size plus size of new object
  99.     uint32 NewOffset = DataSize + size;
  100.  
  101.     if (NewOffset > BufferSize) {
  102.         // Buffer too small, allocate more space.
  103.         // We can use SetSize for this only if it is certain that obj is not
  104.         // pointing to an object previously allocated in the old buffer
  105.         // because it would be deallocated before copied into the new buffer:
  106.         // SetSize (NewOffset + NewOffset / 2 + 1024);
  107.  
  108.         // Allocate more space without using SetSize:
  109.         // Double the size + 1 kB, and round up size to value divisible by 16
  110.         uint32 NewSize = (NewOffset * 2 + 1024 + 15) & uint32(-16);
  111.         int8 * buffer2 = 0;                        // New buffer
  112.         // Allocate new buffer
  113.         buffer2 = new int8[NewSize];
  114.         if (buffer2 == 0) {
  115.             // Error can't allocate
  116.             err.submit(9006);  return 0;
  117.         }
  118.         // Initialize to all zeroes
  119.         memset (buffer2, 0, NewSize);
  120.         if (buffer) {
  121.             // A smaller buffer is previously allocated
  122.             // Copy contents of old buffer into new
  123.             memcpy (buffer2, buffer, BufferSize);
  124.         }
  125.         BufferSize = NewSize;                      // Save size
  126.         if (obj && size) {                        
  127.             // Copy object to new buffer
  128.             memcpy (buffer2 + OldOffset, obj, size);
  129.             obj = 0;                                // Prevent copying once more
  130.         }
  131.         // Delete old buffer after copying object
  132.         if (buffer) delete[] buffer;
  133.  
  134.         // Save pointer to new buffer
  135.         buffer = buffer2;
  136.     }
  137.     // Copy object to buffer if nonzero
  138.     if (obj && size) {
  139.         memcpy (buffer + OldOffset, obj, size);
  140.     }
  141.     if (size) {
  142.         // Adjust new offset
  143.         DataSize = NewOffset;
  144.         NumEntries++;
  145.     }
  146.     // Return offset to allocated object
  147.     return OldOffset;
  148. }
  149.  
  150. uint32 CMemoryBuffer::PushString(char const * s) {
  151.     // Add ASCIIZ string to buffer, return offset
  152.     return Push (s, uint32(strlen(s))+1);
  153. }
  154.  
  155. uint32 CMemoryBuffer::GetLastIndex() {
  156.     // Index of last object pushed (zero-based)
  157.     return NumEntries - 1;
  158. }
  159.  
  160. void CMemoryBuffer::SetDataSize(uint32 size) {
  161.     if (size > BufferSize) {
  162.         // Allocate more space
  163.         SetSize(size + 2048);
  164.     }
  165.     // Set DataSize to after alignment space
  166.     DataSize = size;
  167. }
  168.  
  169. void CMemoryBuffer::Align(uint32 a) {
  170.     // Align next entry to address divisible by a
  171.     SetDataSize((DataSize + a - 1) / a * a);
  172. }
  173.  
  174. // Members of class CFileBuffer
  175. CFileBuffer::CFileBuffer() : CMemoryBuffer() {  
  176.     // Default constructor
  177.     FileName = 0;
  178.     OutputFileName = 0;
  179.     FileType = WordSize = Executable = 0;
  180. }
  181.  
  182. CFileBuffer::CFileBuffer(char const * filename) : CMemoryBuffer() {  
  183.     // Constructor
  184.     FileName = filename;
  185.     FileType = WordSize = 0;
  186. }
  187.  
  188. void CFileBuffer::Read(int IgnoreError) {                  
  189.     // Read file into buffer
  190.     uint32 status;                             // Error status
  191.  
  192. #ifdef _MSC_VER  // Microsoft compiler prefers this:
  193.  
  194.     int fh;                                    // File handle
  195.     fh = _open(FileName, O_RDONLY | O_BINARY); // Open file in binary mode
  196.     if (fh == -1) {
  197.         // Cannot read file
  198.         if (!IgnoreError) err.submit(2103, FileName); // Error. Input file must be read
  199.         SetSize(0); return;                     // Make empty file buffer
  200.     }
  201.     DataSize = filelength(fh);                 // Get file size
  202.     if (DataSize <= 0) {
  203.         if (!IgnoreError) err.submit(2105, FileName); // Wrong size
  204.         return;}
  205.     SetSize(DataSize + 2048);                  // Allocate buffer, 2k extra
  206.     status = _read(fh, Buf(), DataSize);       // Read from file
  207.     if (status != DataSize) err.submit(2103, FileName);
  208.     status = _close(fh);                       // Close file
  209.     if (status != 0) err.submit(2103, FileName);
  210.  
  211. #else            // Works with most compilers:
  212.  
  213.     FILE * fh = fopen(FileName, "rb");
  214.     if (!fh) {
  215.         // Cannot read file
  216.         if (!IgnoreError) err.submit(2103, FileName); // Error. Input file must be read
  217.         SetSize(0); return;                     // Make empty file buffer
  218.     }
  219.     // Find file size
  220.     fseek(fh, 0, SEEK_END);
  221.     long int fsize = ftell(fh);
  222.     if (fsize <= 0 || (unsigned long)fsize >= 0xFFFFFFFF) {
  223.         // File too big or zero size
  224.         err.submit(2105, FileName); fclose(fh); return;
  225.     }
  226.     DataSize = (uint32)fsize;
  227.     rewind(fh);
  228.     // Allocate buffer
  229.     SetSize(DataSize + 2048);                 // Allocate buffer, 2k extra
  230.     // Read entire file
  231.     status = (uint32)fread(Buf(), 1, DataSize, fh);
  232.     if (status != DataSize) err.submit(2103, FileName);
  233.     status = fclose(fh);
  234.     if (status != 0) err.submit(2103, FileName);
  235.  
  236. #endif
  237. }
  238.  
  239. void CFileBuffer::Write() {                  
  240.     // Write buffer to file:
  241.     if (OutputFileName) FileName = OutputFileName;
  242.     // Two alternative ways to write a file:
  243.  
  244. #ifdef _MSC_VER       // Microsoft compiler prefers this:
  245.  
  246.     int fh;                                       // File handle
  247.     uint32 status;                                // Error status
  248.     // Open file in binary mode
  249.     fh = _open(FileName, O_RDWR | O_BINARY | O_CREAT | O_TRUNC, _S_IREAD | _S_IWRITE);
  250.     // Check if error
  251.     if (fh == -1) {err.submit(2104, FileName);  return;}
  252.     // Write file
  253.     status = _write(fh, Buf(), DataSize);
  254.     // Check if error
  255.     if (status != DataSize) err.submit(2104, FileName);
  256.     // Close file
  257.     status = _close(fh);
  258.     // Check if error
  259.     if (status != 0) err.submit(2104, FileName);
  260.  
  261. #else                // Works with most compilers:
  262.  
  263.     // Open file in binary mode
  264.     FILE * ff = fopen(FileName, "wb");
  265.     // Check if error
  266.     if (!ff) {err.submit(2104, FileName);  return;}
  267.     // Write file
  268.     uint32 n = (uint32)fwrite(Buf(), 1, DataSize, ff);
  269.     // Check if error
  270.     if (n != DataSize) err.submit(2104, FileName);
  271.     // Close file
  272.     n = fclose(ff);
  273.     // Check if error
  274.     if (n) {err.submit(2104, FileName);  return;}
  275.  
  276. #endif
  277. }
  278.  
  279. int CFileBuffer::GetFileType() {
  280.     // Detect file type
  281.     if (FileType) return FileType;            // File type already known
  282.     if (!DataSize) return 0;                  // No file
  283.     if (!Buf()) return 0;                     // No contents
  284.  
  285.     uint32 namelen = FileName ? (uint32)strlen(FileName) : 0;
  286.  
  287.     if (strncmp(Buf(),"!<arch>",7) == 0) {
  288.         // UNIX style library. Contains members of file type COFF, ELF or MACHO
  289.         FileType = FILETYPE_LIBRARY;
  290.     }
  291.     else if (strncmp(Buf(),ELFMAG,4) == 0) {
  292.         // ELF file
  293.         FileType = FILETYPE_ELF;
  294.         Executable = Get<Elf32_Ehdr>(0).e_type != ET_REL;
  295.         switch (Buf()[EI_CLASS]) {
  296.         case ELFCLASS32:
  297.             WordSize = 32; break;
  298.         case ELFCLASS64:
  299.             WordSize = 64; break;
  300.         }
  301.     }
  302.     else if (Get<uint32>(0) == MAC_MAGIC_32) {
  303.         // Mach-O 32 little endian
  304.         FileType = FILETYPE_MACHO_LE;
  305.         WordSize = 32;
  306.         Executable = Get<MAC_header_32>(0).filetype != MAC_OBJECT;
  307.     }
  308.     else if (Get<uint32>(0) == MAC_MAGIC_64) {
  309.         // Mach-O 64 little endian
  310.         FileType = FILETYPE_MACHO_LE;
  311.         WordSize = 64;
  312.         Executable = Get<MAC_header_64>(0).filetype != MAC_OBJECT;
  313.     }
  314.     else if (Get<uint32>(0) == MAC_CIGAM_32) {
  315.         // Mach-O 32 big endian
  316.         FileType = FILETYPE_MACHO_BE;
  317.         WordSize = 32;
  318.     }
  319.     else if (Get<uint32>(0) == MAC_CIGAM_64) {
  320.         // Mach-O 64 big endian
  321.         FileType = FILETYPE_MACHO_BE;
  322.         WordSize = 64;
  323.     }
  324.     else if (Get<uint32>(0) == MAC_CIGAM_UNIV) {
  325.         // MacIntosh universal binary
  326.         FileType = FILETYPE_MAC_UNIVBIN;
  327.         WordSize = 0;
  328.     }  
  329.     else if (Get<uint32>(0) == 0xFFFF0000 || Get<uint32>(0) == 0x10000) {
  330.         // Windows subtypes:
  331.         if (Get<uint16>(4) == 0) {
  332.             // This type only occurs when attempting to extract a member from an import library
  333.             FileType = IMPORT_LIBRARY_MEMBER;
  334.         }
  335.         else if (Get<uint16>(4) == 1) {
  336.             // Whole program optimization intermediate file for MS compiler. Undocumented
  337.             FileType = FILETYPE_MS_WPO;
  338.         }
  339.         else {
  340.             // Other subtypes not known
  341.             FileType = FILETYPE_WIN_UNKNOWN;
  342.         }
  343.         // Get word size
  344.         if (Get<uint16>(6) == PE_MACHINE_I386) {
  345.             WordSize = 32;
  346.         }
  347.         else if (Get<uint16>(6) == PE_MACHINE_X8664) {
  348.             WordSize = 64;
  349.         }
  350.         else {
  351.             WordSize = 0;
  352.         }
  353.     }
  354.     else if (Get<uint16>(0) == PE_MACHINE_I386) {
  355.         // COFF/PE 32
  356.         FileType = FILETYPE_COFF;
  357.         WordSize = 32;
  358.         Executable = (Get<SCOFF_FileHeader>(0).Flags & PE_F_EXEC) != 0;
  359.     }
  360.     else if (Get<uint16>(0) == PE_MACHINE_X8664) {
  361.         // COFF64/PE32+
  362.         FileType = FILETYPE_COFF;
  363.         WordSize = 64;
  364.         Executable = (Get<SCOFF_FileHeader>(0).Flags & PE_F_EXEC) != 0;
  365.     }
  366.     else if (Get<uint8>(0) == OMF_THEADR) {
  367.         // OMF 16 or 32
  368.         FileType = FILETYPE_OMF;
  369.         // Word size can only be determined by searching through records in file:
  370.         GetOMFWordSize(); // Determine word size
  371.     }
  372.     else if (Get<uint8>(0) == OMF_LIBHEAD) {
  373.         // OMF Library 16 or 32
  374.         FileType = FILETYPE_OMFLIBRARY;
  375.     }
  376.     else if ((Get<uint16>(0) & 0xFFF9) == 0x5A49) {
  377.         // DOS file or file with DOS stub
  378.         FileType = FILETYPE_DOS;
  379.         WordSize = 16;
  380.         Executable = 1;
  381.         uint32 Signature = Get<uint32>(0x3C);
  382.         if (Signature + 8 < DataSize) {
  383.             if (Get<uint16>(Signature) == 0x454E) {
  384.                 // Windows 3.x file
  385.                 FileType = FILETYPE_WIN3X;
  386.             }
  387.             else if (Get<uint16>(Signature) == 0x4550) {
  388.                 // COFF file
  389.                 uint16 MachineType = Get<uint16>(Signature + 4);
  390.                 if (MachineType == PE_MACHINE_I386) {
  391.                     FileType = FILETYPE_COFF;
  392.                     WordSize = 32;
  393.                 }
  394.                 else if (MachineType == PE_MACHINE_X8664) {
  395.                     FileType = FILETYPE_COFF;
  396.                     WordSize = 64;
  397.                 }
  398.             }
  399.         }
  400.     }
  401.     else if (namelen > 4 && stricmp(FileName + namelen - 4, ".com") == 0) {
  402.         // DOS .com file recognized only from its extension
  403.         FileType = FILETYPE_DOS;
  404.         WordSize = 16;  Executable = 1;
  405.     }
  406.     else if (Get<uint16>(0) == 0 && namelen > 4 && stricmp(FileName + namelen - 4, ".obj") == 0) {
  407.         // Possibly alias record in COFF library
  408.         FileType = FILETYPE_COFF;
  409.         WordSize = 0;
  410.         Executable = 0;
  411.     }
  412.     else {
  413.         // Unknown file type
  414.         int utype = Get<uint32>(0);        
  415.         err.submit(2018, utype, FileName);
  416.         FileType = 0;
  417.     }
  418.     return FileType;
  419. }
  420.  
  421.  
  422. char const * CFileBuffer::GetFileFormatName(int FileType) {
  423.     // Get name of file format type
  424.     return Lookup (FileFormatNames, FileType);
  425. }
  426.  
  427.  
  428. void CFileBuffer::SetFileType(int type) {
  429.     // Set file format type
  430.     FileType = type;
  431. }
  432.  
  433. void CFileBuffer::Reset() {
  434.     // Set all members to zero
  435.     SetSize(0);                          // Deallocate memory buffer
  436.     memset(this, 0, sizeof(*this));
  437. }
  438.  
  439. char * CFileBuffer::SetFileNameExtension(const char * f) {
  440.     // Set file name extension according to FileType
  441.     static char name[MAXFILENAMELENGTH+8];
  442.     int i;
  443.  
  444.     if (strlen(f) > MAXFILENAMELENGTH) err.submit(2203, f);
  445.     strncpy(name, f, MAXFILENAMELENGTH);
  446.  
  447.     // Search for last '.' in file name
  448.     for (i = (int)strlen(name)-1; i > 0; i--) if (name[i] == '.') break;
  449.     if (i < 1) {
  450.         // '.' not found. Append '.' to name
  451.         i = (int)strlen(name); if (i > MAXFILENAMELENGTH-4) i = MAXFILENAMELENGTH-4;
  452.     }
  453.     // Get default extension
  454.     if (cmd.OutputType == FILETYPE_ASM) {
  455.         strcpy(name+i, ".asm"); // Assembly file
  456.     }
  457.     else if (cmd.OutputType == FILETYPE_COFF || cmd.OutputType == FILETYPE_OMF) {
  458.         if ((FileType & (FILETYPE_LIBRARY | FILETYPE_OMFLIBRARY)) || (cmd.LibraryOptions & CMDL_LIBRARY_ADDMEMBER)) {
  459.             strcpy(name+i, ".lib"); // Windows function library
  460.         }
  461.         else {
  462.             strcpy(name+i, ".obj"); // Windows object file
  463.         }
  464.     }
  465.     else { // output type is ELF or MACHO
  466.         if ((FileType & (FILETYPE_LIBRARY | FILETYPE_OMFLIBRARY)) || (cmd.LibraryOptions & CMDL_LIBRARY_ADDMEMBER)) {
  467.             strcpy(name+i, ".a");   // Linux/BSD/Mac function library
  468.         }
  469.         else {
  470.             strcpy(name+i, ".o");   // Linux/BSD/Mac object file
  471.         }
  472.     }
  473.     return name;
  474. }
  475.  
  476. void CFileBuffer::CheckOutputFileName() {
  477.     // Make output file name or check that requested name is valid
  478.     if (!(cmd.FileOptions & CMDL_FILE_OUTPUT)) return;
  479.  
  480.     OutputFileName = cmd.OutputFile;
  481.     if (OutputFileName == 0) {
  482.         // Output file name not specified. Make filename
  483.         OutputFileName = cmd.OutputFile = SetFileNameExtension(FileName);
  484.     }
  485.     if (strcmp(FileName,OutputFileName) == 0 && !(cmd.FileOptions & CMDL_FILE_IN_OUT_SAME)) {
  486.         // Input and output files have same name
  487.         err.submit(2005, FileName);
  488.     }
  489. }
  490.  
  491. void operator >> (CFileBuffer & a, CFileBuffer & b) {
  492.     // Transfer ownership of buffer and other properties from a to b
  493.     b.SetSize(0);                            // De-allocate old buffer from target if it has one
  494.     b.buffer = a.buffer;                     // Transfer buffer
  495.     a.buffer = 0;                            // Remove buffer from source, so that buffer has only one owner
  496.  
  497.     // Copy properties
  498.     b.DataSize   = a.GetDataSize();          // Size of data, offset to vacant space
  499.     b.BufferSize = a.GetBufferSize();        // Size of allocated buffer
  500.     b.NumEntries = a.GetNumEntries();        // Number of objects pushed
  501.     b.Executable = a.Executable;             // File is executable
  502.     if (a.WordSize) b.WordSize = a.WordSize; // Segment word size (16, 32, 64)
  503.     if (a.FileName) b.FileName = a.FileName; // Name of input file
  504.     if (a.OutputFileName) b.OutputFileName = a.OutputFileName;// Name of output file
  505.     if (a.GetFileType())  b.FileType = a.GetFileType();       // Object file type
  506.     a.SetSize(0);                            // Reset a's properties
  507. }
  508.  
  509. void CFileBuffer::GetOMFWordSize() {
  510.     // Determine word size for OMF file.
  511.     // There is no simple way to get the word size. Looking for odd-numbered
  512.     // record types is not sufficient. A 32-bit OMF file may use 16-bit SEGDEF
  513.     // records. We have to look for segments with the 'P' attribute set. And
  514.     // even this is not completely safe, because MASM may generate empty 32-bit
  515.     // segments so we have to look only at segments with nonzero size.
  516.     // We can still have any mixture of 16- and 32-bit segments, though, so no
  517.     // method is absolutely safe.
  518.  
  519.     // We have to parse through all records in file buffer
  520.     uint8  RecordType;                            // Type of current record
  521.     uint32 RecordStart;                           // Index to start of current record
  522.     uint32 RecordEnd;                             // Index to end of current record
  523.     uint32 RecordLength;                          // Length of current record
  524.     uint32 Index = 0;                             // Current offset from buffer while reading
  525.     OMF_SAttrib SegAttr;                          // Segment attributed
  526.     uint32 SegLength;                             // Segment length
  527.  
  528.     WordSize = 16;                                // WordSize = 16 if no 32 bit records found
  529.  
  530.     while (Index < GetDataSize()) {
  531.         RecordStart = Index;                       // Record starts here
  532.         RecordType = Get<uint8>(Index++);          // Get first byte of record = type
  533.         RecordLength = Get<uint16>(Index);         // Next two bytes = length
  534.         Index += 2;
  535.         RecordEnd = RecordStart + RecordLength + 3;// End of record
  536.         if (RecordEnd > GetDataSize()) {
  537.             // Record goes beyond end of file
  538.             err.submit(2301);  break;
  539.         }
  540.         if ((RecordType & 1) && RecordType < OMF_LIBHEAD) { // Odd-numbered type means 32 bit
  541.             WordSize = 32;                                   // ..but this method is not safe
  542.         }
  543.         if ((RecordType & 0xFE) == OMF_SEGDEF) {   // Segment definition record
  544.             SegAttr.b = Get<uint8>(Index++);        // Get segment attributes
  545.             if (SegAttr.u.A == 0) {
  546.                 // Frame and Offset only included if A = 0
  547.                 Index += 2+1;
  548.             }
  549.             SegLength = (RecordType & 1) ? Get<uint32>(Index) : Get<uint16>(Index); // Segment length
  550.  
  551.             if (SegAttr.u.P && SegLength) {         // if segment has P attribute and nonzero length
  552.                 WordSize = 32;                       // .. then it is a 32-bit segment
  553.             }
  554.         }
  555.         Index = RecordEnd;                         // Point to next record
  556.     }
  557. }
  558.  
  559.  
  560. // Class CTextFileBuffer is used for building text files
  561. // Constructor
  562. CTextFileBuffer::CTextFileBuffer() {
  563.     column = 0;
  564.     // Use UNIX linefeeds only if GASM output
  565.     LineType = (cmd.SubType == SUBTYPE_GASM) ? 1 : 0;
  566. }
  567.  
  568. void CTextFileBuffer::Put(const char * text) {
  569.     // Write text string to buffer
  570.     uint32 len = (uint32)strlen(text);            // Length of text
  571.     Push(text, len);                              // Add to buffer without terminating zero
  572.     column += len;                                // Update column
  573. }
  574.  
  575. void CTextFileBuffer::Put(const char character) {
  576.     // Write single character to buffer
  577.     Push(&character, 1);                          // Add to buffer
  578.     column ++;                                    // Update column
  579. }
  580.  
  581. void CTextFileBuffer::NewLine() {
  582.     // Add linefeed
  583.     if (LineType == 0) {
  584.         Push("\r\n", 2);                           // DOS/Windows style linefeed
  585.     }
  586.     else {
  587.         Push("\n", 1);                             // UNIX style linefeed
  588.     }
  589.     column = 0;                                   // Reset column
  590. }
  591.  
  592. void CTextFileBuffer::Tabulate(uint32 i) {
  593.     // Insert spaces until column i
  594.     uint32 j;
  595.     if (i > column) {                             // Only insert spaces if we are not already past i
  596.         for (j = column; j < i; j++) Push(" ", 1); // Insert i - column spaces
  597.         column = i;                                // Update column
  598.     }
  599. }
  600.  
  601. void CTextFileBuffer::PutDecimal(int32 x, int IsSigned) {
  602.     // Write decimal number to buffer, unsigned or signed
  603.     char text[16];
  604.     sprintf(text, IsSigned ? "%i" : "%u", x);
  605.     Put(text);
  606. }
  607.  
  608. void CTextFileBuffer::PutHex(uint8 x, int MasmForm) {
  609.     // Write hexadecimal 8 bit number to buffer
  610.     // If MasmForm >= 1 then the function will write the number in a
  611.     // way that can be read by the assembler, e.g. 0FFH or 0xFF
  612.     char text[16];
  613.     if (MasmForm && cmd.SubType == SUBTYPE_GASM) {
  614.         // Needs 0x prefix
  615.         sprintf(text, "0x%02X", x);
  616.         Put(text);
  617.         return;
  618.     }
  619.     if (MasmForm && x >= 0xA0) {
  620.         Put("0");                                  // Make sure it doesn't begin with a letter
  621.     }
  622.     sprintf(text, "%02X", x);
  623.     Put(text);
  624.     if (MasmForm) Put("H");
  625. }
  626.  
  627. void CTextFileBuffer::PutHex(uint16 x, int MasmForm) {
  628.     // Write hexadecimal 16 bit number to buffer
  629.     // If MasmForm >= 1 then the function will write the number in a
  630.     // way that can be read by the assembler, e.g. 0FFH or 0xFF
  631.     // If MasmForm == 2 then leading zeroes are stripped
  632.     char text[16];
  633.     if (MasmForm && cmd.SubType == SUBTYPE_GASM) {
  634.         // Needs 0x prefix
  635.         sprintf(text, MasmForm==1 ? "0x%04X" : "0x%X", x);
  636.         Put(text);
  637.         return;
  638.     }
  639.     sprintf(text, (MasmForm < 2) ? "%04X" : "%X", x);
  640.     // Check if leading zero needed
  641.     if (MasmForm && text[0] > '9') {
  642.         Put("0");                                  // Leading zero needed
  643.     }
  644.     Put(text);
  645.     if (MasmForm) Put("H");
  646. }
  647.  
  648. void CTextFileBuffer::PutHex(uint32 x, int MasmForm) {
  649.     // Write hexadecimal 32 bit number to buffer
  650.     // If MasmForm >= 1 then the function will write the number in a
  651.     // way that can be read by the assembler, e.g. 0FFH or 0xFF
  652.     // If MasmForm == 2 then leading zeroes are stripped
  653.     char text[16];
  654.     if (MasmForm && cmd.SubType == SUBTYPE_GASM) {
  655.         // Needs 0x prefix
  656.         sprintf(text, MasmForm==1 ? "0x%08X" : "0x%X", x);
  657.         Put(text);
  658.         return;
  659.     }
  660.  
  661.     sprintf(text, (MasmForm < 2) ? "%08X" : "%X", x);
  662.     // Check if leading zero needed
  663.     if (MasmForm && text[0] > '9') {
  664.         Put("0");                                  // Leading zero needed
  665.     }
  666.     Put(text);
  667.     if (MasmForm) Put("H");
  668. }
  669.  
  670. void CTextFileBuffer::PutHex(uint64 x, int MasmForm) {
  671.     // Write unsigned hexadecimal 64 bit number to buffer
  672.     // If MasmForm >= 1 then the function will write the number in a
  673.     // way that can be read by the assembler, e.g. 0FFH or 0xFF
  674.     // If MasmForm == 2 then leading zeroes are stripped
  675.     char text[32];
  676.     if (MasmForm < 2) {  // Print all digits
  677.         sprintf(text, "%08X%08X", HighDWord(x), uint32(x));
  678.     }
  679.     else { // Skip leading zeroes
  680.         if (HighDWord(x)) {
  681.             sprintf(text, "%X%08X", HighDWord(x), uint32(x));
  682.         }
  683.         else {
  684.             sprintf(text, "%X", uint32(x));
  685.         }
  686.     }
  687.     if (MasmForm) {
  688.         if (cmd.SubType == SUBTYPE_GASM) {
  689.             // Needs 0x prefix
  690.             Put("0x");
  691.             Put(text);
  692.         }
  693.         else {
  694.             // use 0FFH form
  695.             if (text[0] > '9')  Put("0");           // Leading zero needed
  696.             Put(text);
  697.             Put("H");
  698.         }
  699.     }
  700.     else {
  701.         // write hexadecimal number only
  702.         Put(text);
  703.     }
  704. }
  705.  
  706. void CTextFileBuffer::PutFloat(float x) {
  707.     // Write floating point number to buffer
  708.     char text[64];
  709.     sprintf(text, "%.7G", x);
  710.     Put(text);
  711. }
  712.  
  713. void CTextFileBuffer::PutFloat(double x) {
  714.     // Write floating point number to buffer
  715.     char text[64];
  716.     sprintf(text, "%.16G", x);
  717.     Put(text);
  718. }
  719.