{"id":389,"date":"2016-06-02T07:00:05","date_gmt":"2016-06-02T14:00:05","guid":{"rendered":"http:\/\/eriknovales.com\/blog\/?p=389"},"modified":"2016-06-02T07:00:05","modified_gmt":"2016-06-02T14:00:05","slug":"ludum-linguarum-the-simple-stuff","status":"publish","type":"post","link":"https:\/\/eriknovales.com\/?p=389","title":{"rendered":"Ludum Linguarum: The Simple Stuff"},"content":{"rendered":"<p><em>(Ludum Linguarum is an open source project that I recently started, and whose creation I\u2019ve been documenting in a series of posts. Its purpose is to let you pull localized content from games, and make flash cards for learning another language. <a href=\"https:\/\/github.com\/enovales\/LudumLinguarum\">It can be found on GitHub<\/a>.)<\/em><\/p>\n<p>When I started this project, I figured that support for individual games would fall into one of a small set of categories:<\/p>\n<ul>\n<li>Low effort, where the strings are either in a simple text file or some sort of well-structured file format like XML, where many good tools already exist to pull it apart. <\/li>\n<li>Cases where the file formats, while bespoke, are well documented, and where there may be tools and code that already exist to parse the file formats. <\/li>\n<li>The really hard cases \u2013 ones where there isn\u2019t a lot of (or <em>any<\/em>) extant information about how the game stores its resources, and extracting strings and metadata about them is more of a reverse-engineering exercise than anything else. <\/li>\n<\/ul>\n<p>In this post, I\u2019ll talk very quickly about a few really simple examples of games that I was able to knock out very quickly: <em><a href=\"http:\/\/store.steampowered.com\/app\/222420\/\">King of Fighters \u201898<\/a>, <a href=\"http:\/\/store.steampowered.com\/app\/222440\/\">King of Fighters 2002<\/a><\/em>, <em><a href=\"http:\/\/store.steampowered.com\/app\/204960\/\">Magical Drop V<\/a><\/em>, and <em><a href=\"http:\/\/store.steampowered.com\/app\/228960\/\">Skulls of the Shogun<\/a><\/em>.<\/p>\n<h3>King of Fighters \u201898 and King of Fighters 2002<\/h3>\n<h3><\/h3>\n<h3><\/h3>\n<p>While I was working on this project, I started on some of the other supported games first. But then, I decided to take a little break, and see if there were any games out there that would be really trivial to support. I just started browsing through my Steam library, and realized that fighting games were probably a good candidate \u2013 they contain limited amounts of text, but were definitely globalized.<\/p>\n<p>Both of these games use the Xbox 360 XDK\u2019s XUI library formats to present their UI. (I determined this by the presence of some files and directories with \u201cxui\u201d in their name.) All of the strings in the game are inside a file conveniently named <strong>strings.txt<\/strong> inside the <strong>data<\/strong> directory.<\/p>\n<p>This is a tab-delimited format with just four columns \u2013 a key for the string, a \u201ccategory\u201d comment field, and then columns for each supported language \u2013 \u201cen\u201d for English, and \u201cjp\u201d for Japan. (It\u2019s interesting that the <em><a href=\"https:\/\/en.wikipedia.org\/wiki\/ISO_3166-1\">country code<\/a><\/em> rather than the <em><a href=\"https:\/\/en.wikipedia.org\/wiki\/List_of_ISO_639-1_codes\">language code<\/a><\/em> was used for Japan \u2013 I\u2019m not sure if that was an unintentional mistake.)<\/p>\n<p>In this case, it\u2019s super simple to extract all of the strings, because of the simple formatting, and the one place that I need to look to find them all. I simply read in the file, and directly map the key column to the per-language text for each card.<\/p>\n<p>(It\u2019s worth noting that King of Fighters XIII doesn\u2019t use the same format or engine, so I wasn\u2019t able to just add support for it using the same code.)<\/p>\n<h3>Magical Drop V<\/h3>\n<p>Adding support for <em>Magical Drop V<\/em> just involved reading some XML files within its <strong>localization<\/strong> subdirectory, and massaging them slightly to remove invalid and undesirable text. For example, ampersands were not escaped in the XML files, which caused the .NET framework\u2019s XML parser to complain. I also stripped out some obvious placeholder values (\u201c&lt;string placeholder&gt;\u201d).<\/p>\n<p>Overall, it was really quite simple to add support for this game, with the game-specific code only running to about 50 lines.<\/p>\n<h3>Skulls of the Shogun<\/h3>\n<p><em>Skulls of the Shogun<\/em> is a game built on XNA and MonoGame, and actually uses <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/h6270d0z(v=vs.110).aspx\">the .NET framework\u2019s globalization support<\/a> to localize its strings. Thus, I was able to use the framework\u2019s support for loading satellite assemblies to pull out both the string keys used to refer to the strings, as well as the content itself, quite easily.<\/p>\n<p>I actually spent more time determining that I had to <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/ms172331(v=vs.110).aspx\">load the assemblies using the reflection-only context<\/a>, in order to keep my library and console application bit-length-independent, than writing the rest of the code to support this game!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>(Ludum Linguarum is an open source project that I recently started, and whose creation I\u2019ve been documenting in a series of posts. Its purpose is to let you pull localized content from games, and make flash cards for learning another language. It can be found on GitHub.) When I started this project, I figured that [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[7,9,10],"tags":[],"class_list":["post-389","post","type-post","status-publish","format-standard","hentry","category-development","category-games","category-ludum-linguarum"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/eriknovales.com\/index.php?rest_route=\/wp\/v2\/posts\/389","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/eriknovales.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/eriknovales.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/eriknovales.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/eriknovales.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=389"}],"version-history":[{"count":0,"href":"https:\/\/eriknovales.com\/index.php?rest_route=\/wp\/v2\/posts\/389\/revisions"}],"wp:attachment":[{"href":"https:\/\/eriknovales.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=389"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/eriknovales.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=389"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/eriknovales.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=389"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}