Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

276 lines
8.3 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Access to PSD image resources.
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include "tier0/platform.h"
  12. #include "tier1/utlbuffer.h"
  13. #include "bitmap/imageformat.h"
  14. #include "bitmap/psd.h"
  15. int Usage()
  16. {
  17. printf( "psdinfo ver. " __DATE__ " " __TIME__ "\n" );
  18. printf( "Usage: \n" );
  19. printf( " psdinfo [OPTIONS] psdfile.psd \n" );
  20. printf( "Options: \n" );
  21. printf( " -read read and print the info record (default) \n" );
  22. printf( " -write update the info record with data from pipe \n" );
  23. printf( "psdfile.psd the PSD file to process. \n" );
  24. printf( "\n" );
  25. return 1;
  26. }
  27. // Global options
  28. static struct Options {
  29. Options() :
  30. szFilename( "" ),
  31. bWriteInfo( false )
  32. {}
  33. char const *szFilename;
  34. bool bWriteInfo;
  35. } s_opts;
  36. // Map a file into a UtlBuffer
  37. bool LoadFileAndClose( FILE *fp, CUtlBuffer &buf, int numExtraBytesAlloc = 0 )
  38. {
  39. if (!fp)
  40. return false;
  41. fseek( fp, 0, SEEK_END );
  42. int nFileLength = ftell( fp );
  43. fseek( fp, 0, SEEK_SET );
  44. buf.EnsureCapacity( nFileLength + numExtraBytesAlloc );
  45. int nBytesRead = fread( buf.Base(), 1, nFileLength, fp );
  46. fclose( fp );
  47. buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
  48. return true;
  49. }
  50. // Reading the info
  51. int ReadInfo()
  52. {
  53. CUtlBuffer bufFile;
  54. if ( !LoadFileAndClose( fopen( s_opts.szFilename, "rb" ), bufFile ) )
  55. Error( "%s cannot be opened for read!\n", s_opts.szFilename );
  56. if ( !IsPSDFile( bufFile ) )
  57. Error( "%s is not a valid PSD file!\n", s_opts.szFilename );
  58. PSDImageResources imgres = PSDGetImageResources( bufFile );
  59. PSDResFileInfo resFileInfo( imgres.FindElement( PSDImageResources::eResFileInfo ) );
  60. PSDResFileInfo::ResFileInfoElement descr = resFileInfo.FindElement( PSDResFileInfo::eDescription );
  61. if ( descr.m_pvData )
  62. {
  63. unsigned char const *pvData = descr.m_pvData, *pvEnd = pvData + descr.m_numBytes;
  64. while ( unsigned char const *pvCR = ( unsigned char const * ) memchr( pvData, '\r', pvEnd - pvData ) )
  65. {
  66. printf( "%.*s\n", pvCR - pvData, pvData );
  67. pvData = pvCR + 1;
  68. }
  69. (pvEnd > pvData) ? printf( "%.*s\n", pvEnd - pvData, pvData ) : 0;
  70. }
  71. return 0;
  72. }
  73. struct Wr_PSDImageResources : public PSDImageResources {
  74. friend int WriteInfo();
  75. Wr_PSDImageResources( PSDImageResources const &x ) : PSDImageResources( x ) { }
  76. };
  77. struct Wr_PSDResFileInfo : public PSDResFileInfo {
  78. friend int WriteInfo();
  79. Wr_PSDResFileInfo( PSDResFileInfo const &x ) : PSDResFileInfo( x ) { }
  80. };
  81. void BufferMove( void const *pvDst, void const *pvSrc, size_t numBytes )
  82. {
  83. memmove( const_cast< void * >( pvDst ), pvSrc, numBytes );
  84. }
  85. // Writing the info
  86. int WriteInfo()
  87. {
  88. // We will have to:
  89. // a) patch the "numBytesImgResource",
  90. // b) probably insert our own section eResFileInfo
  91. // c) inside the section add eDescription
  92. int numDeltaBytes = 0;
  93. CUtlBuffer inBuf;
  94. while ( !feof( stdin ) )
  95. {
  96. char chBuffer[4096];
  97. if ( !fgets( chBuffer, sizeof( chBuffer ) - 1, stdin ) )
  98. break;
  99. chBuffer[ sizeof( chBuffer ) - 1 ] = 0;
  100. int len = strlen( chBuffer );
  101. if ( len && chBuffer[ len - 1 ] == '\n' )
  102. chBuffer[ len - 1 ] = '\r';
  103. inBuf.Put( chBuffer, len );
  104. }
  105. if ( inBuf.TellPut() &&
  106. '\r' == ( ( unsigned char const * ) inBuf.Base() )[ inBuf.TellPut() - 1 ] )
  107. inBuf.SeekPut( CUtlBuffer::SEEK_CURRENT, -1 );
  108. // Now once we have the info load up the PSD file
  109. CUtlBuffer bufFile;
  110. if ( !LoadFileAndClose( fopen( s_opts.szFilename, "rb" ), bufFile,
  111. inBuf.TellPut() + 0x100 ) ) // Having extra room for insertions
  112. Error( "%s cannot be opened for read!\n", s_opts.szFilename );
  113. unsigned char const *pvBufBase = ( unsigned char const * ) bufFile.Base();
  114. if ( !IsPSDFile( bufFile ) )
  115. Error( "%s is not a valid PSD file!\n", s_opts.szFilename );
  116. Wr_PSDImageResources imgres( PSDGetImageResources( bufFile ) );
  117. if ( !imgres.m_pvBuffer )
  118. Error( "%s does not have image resources to write!\n", s_opts.szFilename );
  119. Wr_PSDResFileInfo resFileInfo( PSDResFileInfo( imgres.FindElement( PSDImageResources::eResFileInfo ) ) );
  120. // Maybe the file info is missing?
  121. if ( !resFileInfo.m_res.m_pvData )
  122. {
  123. // Insert a standard file info
  124. unsigned char chStdFileInfo[] = { 0x38,0x42,0x49,0x4D, 0x04,0x04, 0,0,0,0, 0x00,0x07, 0x1C,0x02,0x00, 0x00,0x02, 0x00,0x02, 0 };
  125. unsigned int addSize = sizeof( chStdFileInfo );
  126. imgres.m_numBytes += addSize;
  127. ( ( unsigned int * ) imgres.m_pvBuffer )[ -1 ] = BigLong( imgres.m_numBytes );
  128. BufferMove( imgres.m_pvBuffer + addSize, imgres.m_pvBuffer, pvBufBase + bufFile.TellPut() - imgres.m_pvBuffer );
  129. BufferMove( imgres.m_pvBuffer, chStdFileInfo, addSize );
  130. resFileInfo.m_res.m_eType = PSDImageResources::eResFileInfo;
  131. resFileInfo.m_res.m_numBytes = 7;
  132. resFileInfo.m_res.m_pvData = imgres.m_pvBuffer + 12;
  133. bufFile.SeekPut( CUtlBuffer::SEEK_CURRENT, addSize );
  134. }
  135. PSDResFileInfo::ResFileInfoElement descr = resFileInfo.FindElement( PSDResFileInfo::eDescription );
  136. // If the description is present
  137. if ( descr.m_pvData )
  138. {
  139. // Fix the buffer
  140. numDeltaBytes = inBuf.TellPut() - descr.m_numBytes;
  141. if ( numDeltaBytes )
  142. BufferMove( descr.m_pvData + inBuf.TellPut(), descr.m_pvData + descr.m_numBytes, pvBufBase + bufFile.TellPut() - descr.m_pvData - descr.m_numBytes );
  143. // Copy into the buffer
  144. BufferMove( descr.m_pvData, inBuf.Base(), inBuf.TellPut() );
  145. descr.m_numBytes += numDeltaBytes;
  146. ( ( unsigned short * ) descr.m_pvData )[ -1 ] = BigShort( descr.m_numBytes );
  147. }
  148. else
  149. {
  150. // Need to insert the description
  151. numDeltaBytes = 5 + inBuf.TellPut();
  152. unsigned char const *pvInsertPoint = resFileInfo.m_res.m_pvData + resFileInfo.m_res.m_numBytes;
  153. BufferMove( pvInsertPoint + numDeltaBytes, pvInsertPoint, pvBufBase + bufFile.TellPut() - pvInsertPoint );
  154. // Copy into the buffer
  155. unsigned char signature[] = { 0x1C,0x02, PSDResFileInfo::eDescription };
  156. BufferMove( pvInsertPoint, signature, sizeof( signature ) );
  157. unsigned short usDescrBytes = inBuf.TellPut();
  158. * ( unsigned short * ) ( pvInsertPoint + 3 ) = BigShort( usDescrBytes );
  159. BufferMove( pvInsertPoint + 5, inBuf.Base(), inBuf.TellPut() );
  160. }
  161. // File size changed
  162. bufFile.SeekPut( CUtlBuffer::SEEK_CURRENT, numDeltaBytes );
  163. // Still remaining to fix: the file-info size and imgres size
  164. // File-info
  165. resFileInfo.m_res.m_numBytes += numDeltaBytes;
  166. if ( numDeltaBytes & 1 ) // Odd number of bytes delta
  167. {
  168. if ( resFileInfo.m_res.m_numBytes & 1 ) // It was even, becomes odd
  169. {
  170. ++ numDeltaBytes;
  171. BufferMove( resFileInfo.m_res.m_pvData + resFileInfo.m_res.m_numBytes + 1, resFileInfo.m_res.m_pvData + resFileInfo.m_res.m_numBytes, pvBufBase + bufFile.TellPut() - resFileInfo.m_res.m_pvData - resFileInfo.m_res.m_numBytes );
  172. const_cast< unsigned char & > ( resFileInfo.m_res.m_pvData[ resFileInfo.m_res.m_numBytes ] ) = 0x00;
  173. bufFile.SeekPut( CUtlBuffer::SEEK_CURRENT, +1 );
  174. }
  175. else // It was odd, becomes even
  176. {
  177. -- numDeltaBytes;
  178. BufferMove( resFileInfo.m_res.m_pvData + resFileInfo.m_res.m_numBytes, resFileInfo.m_res.m_pvData + resFileInfo.m_res.m_numBytes + 1, pvBufBase + bufFile.TellPut() - resFileInfo.m_res.m_pvData - resFileInfo.m_res.m_numBytes - 1 );
  179. bufFile.SeekPut( CUtlBuffer::SEEK_CURRENT, -1 );
  180. }
  181. }
  182. ( ( unsigned short * ) resFileInfo.m_res.m_pvData )[ -1 ] = BigShort( resFileInfo.m_res.m_numBytes );
  183. // Image resources
  184. imgres.m_numBytes += numDeltaBytes;
  185. ( ( unsigned int * ) imgres.m_pvBuffer )[ -1 ] = BigLong( imgres.m_numBytes );
  186. // Now write the file buffer
  187. {
  188. FILE *fp = fopen( s_opts.szFilename, "wb" );
  189. if ( !fp )
  190. Error( "%s cannot be opened for update!\n", s_opts.szFilename );
  191. fwrite( bufFile.Base(), 1, bufFile.TellPut(), fp );
  192. fclose( fp );
  193. }
  194. return 0;
  195. }
  196. // Application entry point
  197. int main( int argc, char **argv )
  198. {
  199. // Filename is the last argument
  200. if ( argc <= 1 )
  201. return Usage();
  202. else
  203. s_opts.szFilename = argv[argc - 1];
  204. // Read out all the options
  205. for ( int iArg = 1; iArg < argc - 1; ++ iArg )
  206. {
  207. if ( !stricmp( argv[iArg], "-read" ) )
  208. {
  209. s_opts.bWriteInfo = false;
  210. }
  211. else if ( !stricmp( argv[iArg], "-write" ) )
  212. {
  213. s_opts.bWriteInfo = true;
  214. }
  215. else
  216. {
  217. printf( "Unknown option \"%s\"!\n", argv[iArg] );
  218. return Usage();
  219. }
  220. }
  221. // Go ahead and perform the corresponding read or write
  222. return s_opts.bWriteInfo ? WriteInfo() : ReadInfo();
  223. }