rcstringextractor.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : leveledit *
  23. * *
  24. * $Archive:: /Commando/Code/Tools/LevelEdit/rcstringextractor.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 10/29/01 11:08a $*
  29. * *
  30. * $Revision:: 2 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "stdafx.h"
  36. #include "rcstringextractor.h"
  37. #include "translatedb.h"
  38. #include "stringsmgr.h"
  39. #include "textfile.h"
  40. //////////////////////////////////////////////////////////////////////
  41. //
  42. // RCStringExtractorClass
  43. //
  44. //////////////////////////////////////////////////////////////////////
  45. RCStringExtractorClass::RCStringExtractorClass (void) :
  46. CategoryIndex (0)
  47. {
  48. return;
  49. }
  50. //////////////////////////////////////////////////////////////////////
  51. //
  52. // Find_String
  53. //
  54. //////////////////////////////////////////////////////////////////////
  55. bool
  56. RCStringExtractorClass::Find_String
  57. (
  58. StringClass & line,
  59. const char * keyword,
  60. const char * replacement,
  61. StringClass & contents
  62. )
  63. {
  64. bool retval = false;
  65. StringClass new_line = line;
  66. //
  67. // Did we find the keyword?
  68. //
  69. char *buffer = ::strstr (new_line, keyword);
  70. if (buffer != NULL) {
  71. buffer += ::lstrlen (keyword);
  72. //
  73. // Skip to the start of the string
  74. //
  75. bool found_start = false;
  76. while (buffer[0] != 0) {
  77. if (buffer[0] == '\"') {
  78. buffer ++;
  79. found_start = true;
  80. break;
  81. }
  82. buffer ++;
  83. }
  84. if (found_start) {
  85. //
  86. // Skip past any format specifiers
  87. //
  88. if (buffer[0] == '%') {
  89. buffer += 2;
  90. }
  91. //
  92. // Record the start and end of the string
  93. //
  94. bool found_end = false;
  95. char *start = buffer;
  96. while (buffer[0] != 0) {
  97. if (buffer[0] == '\"') {
  98. found_end = true;
  99. break;
  100. }
  101. buffer ++;
  102. }
  103. if (found_end) {
  104. char *end = buffer;
  105. //
  106. // Return the contents to the caller
  107. //
  108. end[0] = 0;
  109. contents = start;
  110. if (contents.Get_Length () > 0) {
  111. //
  112. // Check to ensure this isn't already a translation string
  113. //
  114. if (::strstr (contents, "IDS_") == NULL) {
  115. //
  116. // Now fill in the replacement
  117. //
  118. start[0] = 0;
  119. line = new_line.Peek_Buffer ();
  120. line += replacement;
  121. line += "\"";
  122. line += end + 1;
  123. retval = true;
  124. }
  125. }
  126. }
  127. }
  128. }
  129. return retval;
  130. }
  131. //////////////////////////////////////////////////////////////////////
  132. //
  133. // Process_Line
  134. //
  135. //////////////////////////////////////////////////////////////////////
  136. void
  137. RCStringExtractorClass::Process_Line (StringClass &line, int &current_index)
  138. {
  139. StringClass contents;
  140. const char *KEYWORDS[] =
  141. {
  142. "CAPTION",
  143. "LTEXT",
  144. "PUSHBUTTON",
  145. "CONTROL",
  146. "RTEXT",
  147. "CTEXT"
  148. };
  149. const int KEYWORD_COUNT = sizeof (KEYWORDS) / sizeof (const char *);
  150. //
  151. // Make a string identifier from the prefix and the current index
  152. //
  153. StringClass string_id;
  154. string_id.Format ("%s%.3d", Prefix.Peek_Buffer (), current_index);
  155. //
  156. // Check to see if this line is one of the ones we need to process
  157. //
  158. for (int index = 0; index < KEYWORD_COUNT; index ++) {
  159. if (Find_String (line, KEYWORDS[index], string_id, contents)) {
  160. //
  161. // Create a new translation DB entry for this string
  162. //
  163. TDBObjClass *new_obj = new TDBObjClass;
  164. new_obj->Set_English_String (contents);
  165. new_obj->Set_ID_Desc (string_id);
  166. new_obj->Set_Category_ID (CategoryIndex);
  167. //
  168. // Add the new object to the database
  169. //
  170. TranslateDBClass::Add_Object (new_obj);
  171. current_index ++;
  172. break;
  173. }
  174. }
  175. return ;
  176. }
  177. //////////////////////////////////////////////////////////////////////////
  178. //
  179. // Set_Src_RC_Filename
  180. //
  181. //////////////////////////////////////////////////////////////////////////
  182. void
  183. RCStringExtractorClass::Set_Src_RC_Filename (const char *full_path)
  184. {
  185. SrcFilename = full_path;
  186. DestFilename = SrcFilename + "_extracted";
  187. return ;
  188. }
  189. //////////////////////////////////////////////////////////////////////////
  190. //
  191. // Extract_Strings
  192. //
  193. //////////////////////////////////////////////////////////////////////////
  194. void
  195. RCStringExtractorClass::Extract_Strings (void)
  196. {
  197. //
  198. // Check out the strings database
  199. //
  200. if (StringsMgrClass::Check_Out ()) {
  201. CategoryIndex = 0;
  202. //
  203. // Try to find the category these strings are to be imported into
  204. //
  205. TDBCategoryClass *category = TranslateDBClass::Find_Category (CategoryName);
  206. if (category != NULL) {
  207. CategoryIndex = category->Get_ID ();
  208. }
  209. //
  210. // Try to open the .RC file
  211. //
  212. TextFileClass file (SrcFilename);
  213. TextFileClass output_file (DestFilename);
  214. if (file.Open (RawFileClass::READ) && output_file.Open (RawFileClass::WRITE)) {
  215. //
  216. // Determine where to start our numbering scheme
  217. //
  218. int current_index = Find_Starting_Index ();
  219. //
  220. // Read each line from the file
  221. //
  222. StringClass curr_line;
  223. while (file.Read_Line (curr_line)) {
  224. Process_Line (curr_line, current_index);
  225. curr_line += "\r\n";
  226. output_file.Write (curr_line, ::lstrlen (curr_line));
  227. }
  228. //
  229. // Close the files
  230. //
  231. file.Close ();
  232. output_file.Close ();
  233. }
  234. //
  235. // Save the translation database and check it back into VSS
  236. //
  237. StringsMgrClass::Save_Translation_Database ();
  238. StringsMgrClass::Check_In ();
  239. }
  240. return ;
  241. }
  242. //////////////////////////////////////////////////////////////////////////
  243. //
  244. // Find_Starting_Index
  245. //
  246. //////////////////////////////////////////////////////////////////////////
  247. int
  248. RCStringExtractorClass::Find_Starting_Index (void)
  249. {
  250. int max_index = 0;
  251. int prefix_len = Prefix.Get_Length ();
  252. //
  253. // Loop over all the objects in the translation database
  254. //
  255. int count = TranslateDBClass::Get_Object_Count ();
  256. for (int index = 0; index < count; index ++) {
  257. TDBObjClass *obj = TranslateDBClass::Get_Object (index);
  258. if (obj != NULL) {
  259. //
  260. // Is this one of the strings we care about?
  261. //
  262. const StringClass &string_desc = obj->Get_ID_Desc ();
  263. if (::strnicmp (Prefix, string_desc, prefix_len) == 0) {
  264. //
  265. // Check to see if this is the largest index we've found yet
  266. //
  267. const char *suffix = (string_desc.Peek_Buffer () + prefix_len);
  268. int number = ::atoi (suffix);
  269. max_index = max (number, max_index);
  270. }
  271. }
  272. }
  273. return max_index + 1;
  274. }