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.

420 lines
13 KiB

  1. //===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================
  2. //
  3. //==================================================================================================
  4. #if defined( WIN32 ) && !defined( _X360 )
  5. #include <windows.h> // SRC only!!
  6. #endif //
  7. #include "filesystem.h"
  8. #include "tier0/icommandline.h"
  9. #include "tier1/keyvalues.h"
  10. #include "tier2/keyvaluesmacros.h"
  11. #include "kvc_paintkit.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. //--------------------------------------------------------------------------------------------------
  15. //
  16. //--------------------------------------------------------------------------------------------------
  17. static void WriteIndent( IBaseFileSystem *pFileSystem, FileHandle_t hFile, bool bOptTabs, int nOptTabWidth, int nIndentLevel )
  18. {
  19. if ( nIndentLevel <= 0 )
  20. return;
  21. if ( bOptTabs || nOptTabWidth <= 0 )
  22. {
  23. char *szTabIndent = ( char * )stackalloc( nIndentLevel + 1 );
  24. V_memset( szTabIndent, '\t', nIndentLevel );
  25. szTabIndent[nIndentLevel] = '\0';
  26. pFileSystem->Write( szTabIndent, nIndentLevel, hFile );
  27. }
  28. else
  29. {
  30. const int nSpaceCount = nIndentLevel * nOptTabWidth;
  31. char *szSpaceIndent = ( char * )stackalloc( nSpaceCount + 1 );
  32. V_memset( szSpaceIndent, ' ', nSpaceCount );
  33. szSpaceIndent[nSpaceCount] = '\0';
  34. pFileSystem->Write( szSpaceIndent, nSpaceCount, hFile );
  35. }
  36. }
  37. //--------------------------------------------------------------------------------------------------
  38. //
  39. //--------------------------------------------------------------------------------------------------
  40. static void WritePadding( IBaseFileSystem *pFileSystem, FileHandle_t hFile, bool bOptTabs, int nOptTabWidth, int nCurrentColumn, int nTargetColumn )
  41. {
  42. if ( nCurrentColumn <= 0 || nTargetColumn <= 0 || nTargetColumn <= nCurrentColumn )
  43. {
  44. WriteIndent( pFileSystem, hFile, bOptTabs, nOptTabWidth, 1 );
  45. return;
  46. }
  47. if ( bOptTabs || nOptTabWidth <= 0 )
  48. {
  49. if ( nOptTabWidth <= 0 )
  50. {
  51. nOptTabWidth = 4; // Can't handle 0 tab width, use 4
  52. }
  53. nTargetColumn = ( nTargetColumn + nOptTabWidth - 1 ) / nOptTabWidth * nOptTabWidth; // Round to next tab stop
  54. Assert( nTargetColumn % nOptTabWidth == 0 );
  55. const int nTabCount = ( nTargetColumn - nCurrentColumn ) / nOptTabWidth;
  56. char *szTabPadding = ( char * )stackalloc( nTabCount + 1 );
  57. V_memset( szTabPadding, '\t', nTabCount );
  58. szTabPadding[nTabCount] = '\0';
  59. pFileSystem->Write( szTabPadding, nTabCount, hFile );
  60. }
  61. else
  62. {
  63. nTargetColumn = ( nTargetColumn + nOptTabWidth - 1 ) / nOptTabWidth * nOptTabWidth; // Round to next tab stop
  64. Assert( nTargetColumn % nOptTabWidth == 0 );
  65. const int nSpaceCount = nTargetColumn - nCurrentColumn;
  66. char *szSpacePadding = ( char * )stackalloc( nSpaceCount + 1 );
  67. V_memset( szSpacePadding, ' ', nSpaceCount );
  68. szSpacePadding[nSpaceCount] = '\0';
  69. pFileSystem->Write( szSpacePadding, nSpaceCount, hFile );
  70. }
  71. }
  72. //--------------------------------------------------------------------------------------------------
  73. //
  74. //--------------------------------------------------------------------------------------------------
  75. static void WriteConvertedString( IBaseFileSystem *pFileSystem, FileHandle_t hFile, const char *pszString )
  76. {
  77. // handle double quote chars within the string
  78. // the worst possible case is that the whole string is quotes
  79. int nSrcLen = Q_strlen( pszString );
  80. char *szConvertedString = ( char * )stackalloc( ( nSrcLen + 1 ) * sizeof( char ) * 2 );
  81. int j = 0;
  82. for ( int i = 0; i <= nSrcLen; ++i )
  83. {
  84. if ( pszString[i] == '\"' )
  85. {
  86. szConvertedString[j] = '\\';
  87. ++j;
  88. }
  89. szConvertedString[j] = pszString[i];
  90. ++j;
  91. }
  92. pFileSystem->Write( szConvertedString, V_strlen( szConvertedString ), hFile );
  93. }
  94. //--------------------------------------------------------------------------------------------------
  95. //
  96. //--------------------------------------------------------------------------------------------------
  97. template <size_t maxLenInChars> static int CleanFloatString( OUT_Z_ARRAY char (&pszFloatBuf)[maxLenInChars] )
  98. {
  99. int nStrLen = V_strlen( pszFloatBuf );
  100. const char *pDecimal = V_strnchr( pszFloatBuf, '.', maxLenInChars - 1 );
  101. if ( pDecimal )
  102. {
  103. ++pDecimal; // Keep number after decimal always
  104. char *pToStrip = pszFloatBuf + nStrLen - 1;
  105. while ( pToStrip > pDecimal && *pToStrip == '0' )
  106. {
  107. *pToStrip = '\0';
  108. --pToStrip;
  109. --nStrLen;
  110. }
  111. }
  112. return nStrLen;
  113. }
  114. //--------------------------------------------------------------------------------------------------
  115. //
  116. //--------------------------------------------------------------------------------------------------
  117. static void SaveToFile_R( KeyValues *pkv, IBaseFileSystem *pFileSystem, FileHandle_t hFile, bool bOptTabs, int nOptTabWidth, int nIndentLevel, int nValueColumn = -1 )
  118. {
  119. WriteIndent( pFileSystem, hFile, bOptTabs, nOptTabWidth, nIndentLevel );
  120. pFileSystem->Write( "\"", 1, hFile );
  121. WriteConvertedString( pFileSystem, hFile, pkv->GetName() );
  122. pFileSystem->Write( "\"", 1, hFile );
  123. if ( pkv->GetFirstSubKey() )
  124. {
  125. int nMaxSubKeyNameLength = 0;
  126. for ( KeyValues *pkvSubKey = pkv->GetFirstSubKey(); pkvSubKey; pkvSubKey = pkvSubKey->GetNextKey() )
  127. {
  128. const int nNameLength = V_strlen( pkvSubKey->GetName() );
  129. nMaxSubKeyNameLength = Max( nNameLength, nMaxSubKeyNameLength );
  130. }
  131. int nSubKeyValueColumn = -1;
  132. if ( nMaxSubKeyNameLength > 0 )
  133. {
  134. nSubKeyValueColumn = nMaxSubKeyNameLength + 1;
  135. }
  136. pFileSystem->Write( "\n", 1, hFile );
  137. WriteIndent( pFileSystem, hFile, bOptTabs, nOptTabWidth, nIndentLevel );
  138. pFileSystem->Write( "{\n", 2, hFile );
  139. for ( KeyValues *pkvSubKey = pkv->GetFirstSubKey(); pkvSubKey; pkvSubKey = pkvSubKey->GetNextKey() )
  140. {
  141. SaveToFile_R( pkvSubKey, pFileSystem, hFile, bOptTabs, nOptTabWidth, nIndentLevel + 1, nSubKeyValueColumn );
  142. }
  143. WriteIndent( pFileSystem, hFile, bOptTabs, nOptTabWidth, nIndentLevel );
  144. pFileSystem->Write( "}\n", 2, hFile );
  145. }
  146. else
  147. {
  148. if ( nValueColumn > 0 )
  149. {
  150. const int nNameLength = V_strlen( pkv->GetName() );
  151. WritePadding( pFileSystem, hFile, bOptTabs, nOptTabWidth, nNameLength, nValueColumn );
  152. }
  153. else
  154. {
  155. WriteIndent( pFileSystem, hFile, bOptTabs, nOptTabWidth, 1 ); // Don't know which column to align things on
  156. }
  157. pFileSystem->Write( "\"", 1, hFile );
  158. switch ( pkv->GetDataType() )
  159. {
  160. case KeyValues::TYPE_STRING:
  161. {
  162. const char *pszStringValue = pkv->GetString();
  163. if ( pszStringValue )
  164. {
  165. WriteConvertedString( pFileSystem, hFile, pszStringValue );
  166. }
  167. break;
  168. }
  169. case KeyValues::TYPE_WSTRING:
  170. {
  171. const wchar_t *pszWString = pkv->GetWString();
  172. if ( pszWString )
  173. {
  174. const size_t nTmpBufSize = 4096;
  175. char *szTmpBuf = ( char * )stackalloc( nTmpBufSize * sizeof( char ) );
  176. V_memset( szTmpBuf, '\0', nTmpBufSize );
  177. const int nResult = Q_UnicodeToUTF8( pszWString, szTmpBuf, nTmpBufSize );
  178. if ( nResult != 0 )
  179. {
  180. WriteConvertedString( pFileSystem, hFile, szTmpBuf );
  181. }
  182. }
  183. break;
  184. }
  185. case KeyValues::TYPE_INT:
  186. {
  187. char szTmpBuf[32] = {};
  188. V_snprintf( szTmpBuf, sizeof( szTmpBuf ), "%d", pkv->GetInt() );
  189. pFileSystem->Write( szTmpBuf, V_strlen( szTmpBuf ), hFile );
  190. break;
  191. }
  192. case KeyValues::TYPE_UINT64:
  193. {
  194. char szTmpBuf[32] = {};
  195. // write "0x" + 16 char 0-padded hex encoded 64 bit value
  196. #ifdef WIN32
  197. Q_snprintf( szTmpBuf, sizeof( szTmpBuf ), "0x%016I64X", *( ( uint64 * )pkv->GetUint64() ) );
  198. #else
  199. Q_snprintf( szTmpBuf, sizeof( szTmpBuf ), "0x%016llX", *( ( uint64 * )pkv->GetUint64() ) );
  200. #endif
  201. pFileSystem->Write( szTmpBuf, V_strlen( szTmpBuf ), hFile );
  202. break;
  203. }
  204. case KeyValues::TYPE_FLOAT:
  205. {
  206. char szTmpBuf[32] = {};
  207. V_snprintf( szTmpBuf, sizeof( szTmpBuf ), "%f", pkv->GetFloat() );
  208. int nStrLen = V_strlen( szTmpBuf );
  209. nStrLen = CleanFloatString( szTmpBuf );
  210. pFileSystem->Write( szTmpBuf, nStrLen, hFile );
  211. break;
  212. }
  213. case KeyValues::TYPE_COLOR:
  214. {
  215. char szTmpBuf[32] = {};
  216. const Color c = pkv->GetColor();
  217. V_snprintf( szTmpBuf, sizeof( szTmpBuf ), "%d", c.r() );
  218. pFileSystem->Write( " ", 1, hFile );
  219. V_snprintf( szTmpBuf, sizeof( szTmpBuf ), "%d", c.g() );
  220. pFileSystem->Write( " ", 1, hFile );
  221. V_snprintf( szTmpBuf, sizeof( szTmpBuf ), "%d", c.b() );
  222. pFileSystem->Write( " ", 1, hFile );
  223. V_snprintf( szTmpBuf, sizeof( szTmpBuf ), "%d", c.a() );
  224. break;
  225. }
  226. default:
  227. break;
  228. }
  229. pFileSystem->Write( "\"\n", 2, hFile );
  230. }
  231. }
  232. //--------------------------------------------------------------------------------------------------
  233. //
  234. //--------------------------------------------------------------------------------------------------
  235. static bool SaveCleanKeyValuesToFile( KeyValues *pkv, IBaseFileSystem *pFileSystem, const char *pszFileName, const char *pszPathID = nullptr, bool bOptTabs = true, int nOptSpaceIndent = 4 )
  236. {
  237. // Write out KeyValues to the specified file but cleaner than KeyValues::SaveToFile
  238. // create a write file
  239. FileHandle_t hFile = pFileSystem->Open( pszFileName, "wb", pszPathID );
  240. if ( hFile == FILESYSTEM_INVALID_HANDLE )
  241. {
  242. Msg( "CleanSaveKeyValuesToFile: Couldn't open file \"%s\" for writing in path \"%s\".\n",
  243. pszFileName ? pszFileName : "NULL", pszPathID ? pszPathID : "NULL" );
  244. return false;
  245. }
  246. for ( KeyValues *pkvTmp = pkv; pkvTmp; pkvTmp = pkvTmp->GetNextKey() )
  247. {
  248. SaveToFile_R( pkvTmp, pFileSystem, hFile, bOptTabs, nOptSpaceIndent, 0 );
  249. }
  250. pFileSystem->Close( hFile );
  251. return true;
  252. }
  253. //--------------------------------------------------------------------------------------------------
  254. //
  255. //--------------------------------------------------------------------------------------------------
  256. void ProcessPaintKitKeyValuesFile( const char *pszPaintKitKeyValuesFile, bool bOptFix, bool bOptVerbose )
  257. {
  258. if ( bOptVerbose )
  259. {
  260. Msg( "Processing: %s\n", pszPaintKitKeyValuesFile );
  261. }
  262. char szFullPath[MAX_PATH] = {};
  263. if ( !V_IsAbsolutePath( pszPaintKitKeyValuesFile ) )
  264. {
  265. g_pFullFileSystem->RelativePathToFullPath_safe( pszPaintKitKeyValuesFile, nullptr, szFullPath );
  266. if ( !V_IsAbsolutePath( szFullPath ) )
  267. {
  268. char szTmpA[MAX_PATH] = {};
  269. char szTmpB[MAX_PATH] = {};
  270. if ( !g_pFullFileSystem->GetCurrentDirectoryA( szTmpA, ARRAYSIZE( szTmpB ) ) )
  271. {
  272. Msg( "Error: non-resolvable, non-absolute path specified (%s) and couldn't determine current directory\n", pszPaintKitKeyValuesFile );
  273. return;
  274. }
  275. V_ComposeFileName( szTmpA, pszPaintKitKeyValuesFile, szTmpB, ARRAYSIZE( szTmpB ) );
  276. V_FixupPathName( szTmpA, ARRAYSIZE( szTmpA ), szTmpB );
  277. g_pFullFileSystem->GetCaseCorrectFullPath( szTmpB, szFullPath );
  278. }
  279. }
  280. if ( !V_IsAbsolutePath( szFullPath ) )
  281. {
  282. Msg( "Error: Couldn't find absolute path to file: %s\n", pszPaintKitKeyValuesFile );
  283. return;
  284. }
  285. if ( !g_pFullFileSystem->FileExists( szFullPath ) )
  286. {
  287. Msg( "Error: Couldn't find file: %s\n", szFullPath );
  288. return;
  289. }
  290. KeyValues *pkv = new KeyValues( "" );
  291. const bool bLoaded = pkv->LoadFromFile( g_pFullFileSystem, szFullPath );
  292. if ( bLoaded )
  293. {
  294. for ( KeyValues *pkvTmp = pkv; pkvTmp; pkvTmp = pkvTmp->GetNextKey() )
  295. {
  296. HandleKeyValuesMacros( pkvTmp );
  297. }
  298. char szFilename[MAX_PATH] = {};
  299. if ( bOptFix )
  300. {
  301. V_strcpy_safe( szFilename, szFullPath );
  302. V_strcat_safe( szFilename, "_fix" );
  303. if ( bOptVerbose )
  304. {
  305. Msg( " Saving: %s\n", szFilename );
  306. }
  307. if ( !SaveCleanKeyValuesToFile( pkv, g_pFullFileSystem, szFilename, nullptr, false ) )
  308. {
  309. Msg( "Error: Couldn't write: %s\n", szFilename );
  310. }
  311. }
  312. else
  313. {
  314. V_strcpy_safe( szFilename, szFullPath );
  315. V_strcat_safe( szFilename, "_bak" );
  316. if ( bOptVerbose )
  317. {
  318. Msg( " Copying: %s -> %s\n", szFullPath, szFilename );
  319. }
  320. if ( CopyFile( szFullPath, szFilename, false ) )
  321. {
  322. if ( bOptVerbose )
  323. {
  324. Msg( " Saving: %s\n", szFullPath );
  325. }
  326. if ( !SaveCleanKeyValuesToFile( pkv, g_pFullFileSystem, szFullPath, nullptr, false ) )
  327. {
  328. Msg( "Error: Couldn't write: %s\n", szFullPath );
  329. }
  330. }
  331. else
  332. {
  333. Msg( "Error: Couldn't copy %s to %s, aborting\n", szFullPath, szFilename );
  334. }
  335. }
  336. pkv->SaveToFile( g_pFullFileSystem, "c:/tmp/bar.txt" );
  337. }
  338. else
  339. {
  340. Msg( " + Couldn't Load: %s\n", szFullPath );
  341. }
  342. pkv->deleteThis();
  343. }
  344. //--------------------------------------------------------------------------------------------------
  345. //
  346. //--------------------------------------------------------------------------------------------------
  347. void ProcessPaintKitKeyValuesFiles( const CUtlVector< CUtlSymbol > &workList )
  348. {
  349. // This bit of hackery allows us to access files on the harddrive
  350. g_pFullFileSystem->AddSearchPath( "", "LOCAL", PATH_ADD_TO_HEAD );
  351. const bool bOptFix = CommandLine()->CheckParm( "-f" ) != nullptr;
  352. const bool bOptVerbose = CommandLine()->CheckParm( "-v" ) != nullptr;
  353. for ( int i = 0; i < workList.Count(); ++i )
  354. {
  355. ProcessPaintKitKeyValuesFile( workList[i].String(), bOptFix, bOptVerbose );
  356. }
  357. }