bokpass.js 94 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350
  1. // Permission to use, copy, modify, and/or distribute this software for
  2. // any purpose with or without fee is hereby granted.
  3. //
  4. // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
  5. // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
  6. // OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
  7. // FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
  8. // DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  9. // AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
  10. // OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  11. // Ported from the Tcl/Tk version. See the original for more documentation.
  12. var bokpass = (function () {
  13. "use strict";
  14. // JSON version of bok::strings
  15. var strings = {
  16. "en": {
  17. "games": {
  18. "jp1": "Bokura no Taiyō",
  19. "jp2": "Zoku Bokura no Taiyō: Taiyō Shōnen Jango",
  20. "jp3": "Shin Bokura no Taiyō: Gyakushū no Sabata",
  21. "jp4": "Bokura no Taiyō: Django & Sabata",
  22. "en1": "Boktai: The Sun Is in Your Hand",
  23. "en2": "Boktai 2: Solar Boy Django",
  24. "en3": "Boktai 3: Sabata's Counterattack",
  25. "en4": "Lunar Knights"
  26. },
  27. "labels": {
  28. "game-tabs": [
  29. { game: 1, name: "Boktai" },
  30. { game: 2, name: "Boktai 2 / Zoktai" },
  31. { game: 3, name: "Boktai 3 / Shinbok" },
  32. { game: 4, name: "Lunar Knights / Boktai DS" }
  33. ],
  34. "keyboard-tabs": [
  35. { key: "en64", name: "Base64" },
  36. { key: "lk64", name: "Base64 LK" },
  37. { key: "jp64", name: "Base64 JP" },
  38. { key: "en", name: "English" },
  39. { key: "ext", name: "Latin Ext." },
  40. { key: "hiri", name: "Hirigana" },
  41. { key: "kata", name: "Katakana" }
  42. ],
  43. "padding": "Padding", "header-padding": "Padding",
  44. "region": "Region", "region-name": "Region",
  45. "checksum": "Checksum", "offset": "Offset",
  46. "sol": "Sol", "sol/4": "Sol",
  47. "sol/4096": "Sol",
  48. "loan": "Loan", "loan/256": "Loan",
  49. "timezone": "Timezone", "timezone-name": "Timezone",
  50. "hours": "Hours", "minutes": "Minutes",
  51. "difficulty": "Difficulty", "difficulty-name": "Difficulty",
  52. "dungeons": "Dungeons", "clears": "Clears",
  53. "continues": "Continues", "caught": "Caught",
  54. "kills": "Kills", "kills/8": "Kills",
  55. "rank": "Rank", "rank-name": "Rank",
  56. "forges": "Forges", "races": "Races",
  57. "link-battles": "Link-Battles", "link-trades": "Link-Trades",
  58. "link-shopping": "Link-Shopping", "link-races": "Link-Races",
  59. "cross-linked": "Cross-Linked",
  60. "title": "Title", "title-name": "Title",
  61. "titles": "Titles", "title-list": "Titles",
  62. "side": "Side", "side-name": "Side",
  63. "style": "Style", "style-name": "Style",
  64. "sword": "Sword", "sword-name": "Sword",
  65. "gun": "Gun", "gun-name": "Gun",
  66. "terrennial": "Terrennial", "terrennial-name": "Terrennial",
  67. "climate": "Climate", "climate-name": "Climate",
  68. "endings": "Survivors", "ending-list": "Survivors",
  69. "name-dark": "Name(dark)", "name-solar": "Name(sol)",
  70. "name": "Name"
  71. },
  72. "regions": [
  73. "Unknown Region #0", "Japan", "North America", "Europe"
  74. ],
  75. "timezones": {
  76. "jp": [
  77. "Unknown Timezone #0",
  78. "Ibaraki", "Tochigi", "Gunma", "Saitama",
  79. "Chiba", "Tokyo", "Kanagawa", "Ogasawara",
  80. "Niigata", "Toyama", "Ishikawa", "Fukui",
  81. "Yamanashi", "Nagano", "Gifu", "Shizuoka",
  82. "Aichi", "Mie", "Shiga", "Kyoto",
  83. "Osaka", "Hyougo", "Nara", "Wakayama",
  84. "Tottori", "Shimane", "Okayama", "Hiroshima",
  85. "Yamaguchi", "Tokushima", "Kagawa", "Ehime",
  86. "Kouchi", "Fukuoka", "Saga", "Nagasaki",
  87. "Kumamoto", "Ooita", "Miyazaki", "Kagoshima",
  88. "Okinawa", "Ishigakijima", "Sapporo", "Hakodate",
  89. "Asahikawa", "Kushiro", "Obihiro", "Aomori",
  90. "Iwate", "Miyagi", "Akita", "Yamagata",
  91. "Fukushima"
  92. ],
  93. "na": [
  94. "Unknown Timezone #0",
  95. "St. John's", "Labrador City", "Halifax", "Quebec",
  96. "Montreal", "Ottawa", "Toronto", "Timmins",
  97. "Boston", "Albany", "Syracuse", "New York",
  98. "Philadelphia", "Pittsburgh", "Washington D.C.", "Norfolk",
  99. "Raleigh", "Charlotte", "Atlanta", "Jacksonville",
  100. "Tampa", "Miami", "Detroit", "Cleveland",
  101. "Columbus", "Lexington", "Thunder Bay", "Winnipeg",
  102. "Regina", "Thompson", "Indianapolis", "Chicago",
  103. "Milwaukee", "Minneapolis", "Bismark", "St. Louis",
  104. "Nashville", "Memphis", "Montgomery", "Jackson",
  105. "New Orleans", "Des Moines", "Lincoln", "Kansas City",
  106. "Topeka", "Springfield", "Little Rock", "Oklahoma City",
  107. "Dallas", "Houston", "Edmonton", "Calgary",
  108. "Yellowknife", "Denver", "Albuquerque", "Phoenix",
  109. "Boise", "Salt Lake City", "Vancouver", "Whitehorse",
  110. "Spokane", "Seattle", "Salem", "Reno",
  111. "Las Vegas", "Los Angeles", "San Diego", "San Francisco",
  112. "Anchorage", "Fairbanks", "Ketchikan", "Honolulu"
  113. ],
  114. "eu": [
  115. "Unknown Timezone #0",
  116. "Reykjavik", "Dublin", "Cork", "London",
  117. "Cardiff", "Edinburgh", "Belfast", "Liverpool",
  118. "Lisbon", "Porto", "Valletta", "Madrid",
  119. "Barcelona", "Valencia", "La Coruna", "Seville",
  120. "Paris", "Brest", "Lyons", "Bordeaux",
  121. "Marseilles", "Brussels", "Bastogne", "Amsterdam",
  122. "Rotterdam", "Luxembourg", "Berlin", "Hamburg",
  123. "Essen", "Frankfurt", "Munich", "Bern",
  124. "Geneve", "Vaduz", "Wien", "Innsbruck",
  125. "Rome", "Genova", "Venezia", "Palermo",
  126. "Sassari", "Oslo", "Bergen", "Trondheim",
  127. "Copenhagen", "Odense", "Stockholm", "Gothenburg",
  128. "Helsinki", "Turku", "Mikkeli", "Warszawa",
  129. "Gdansk", "Poznan", "Wroclaw", "Krakow",
  130. "Praha", "Bratislava", "Kosice", "Budapest",
  131. "Bucuresti", "Ljubljana", "Zagreb", "Sarajevo",
  132. "Beograd", "Skopje", "Tirane", "Sofiya",
  133. "Athinai", "Thessaloniki", "Iraklion", "Ankara",
  134. "Istanbul", "Izmir", "Konya", "Adana",
  135. "Jerusalem", "Pretoria", "Cape Town", "Durban",
  136. "Wellington", "Auckland", "Dunedin", "Sydney",
  137. "Melbourne", "Adelaide", "Perth", "Brisbane"
  138. ]
  139. },
  140. "ranks": [
  141. "S", "A+", "A", "A-", "B+", "B", "B-", "C+",
  142. "C", "C-", "D+", "D", "D-", "F+", "F", "F-"
  143. ],
  144. "difficulties": {
  145. "1": ["Easy", "Normal 1", "Normal 2", "Hard"],
  146. "2": [],
  147. "3": [],
  148. "4": ["Normal", "Hard", "Nightmare"]
  149. },
  150. "titles": {
  151. "1": [
  152. "Trigger of Sol", "Gun Master", "Gladiator", "Bishop",
  153. "Queen", "Berserker", "Death", "Solar Boy",
  154. "Dark Boy", "Solar Menchant", "Running Boy", "King",
  155. "Rook", "Knight", "Pawn"
  156. ],
  157. "2": [
  158. "Sword Master", "Spear Master", "Hammer Master", "Fist Master",
  159. "Gun Master", "Adept", "Day Walker", "Adventurer",
  160. "Agent", "Collector", "Dark Hunter", "Grand Master"
  161. ],
  162. "3": [
  163. "Adept", "Gladiator", "SP Agent", "Champion",
  164. "Dark Hunter", "Alchemist", "Collector", "Doll Master",
  165. "Storyteller", "Grandmaster"
  166. ],
  167. "4": [
  168. "Dark Knight", "Sol Gunner", "Sword Master", "Gun Master",
  169. "Guardian", "Treasure Hunter", "Collector", "Huntmaster",
  170. "Shooting Star", "Gladiator", "Special Agent", "Wanderer",
  171. "Adventurer", "Grand Master"
  172. ]
  173. },
  174. "sides": ["Red", "Black", "Grey"],
  175. "styles": ["Sword", "Spear", "Hammer", "Gun", "Fists", "No Style"],
  176. "endings": ["Otenko", "Everybody", "Nobody", "Sabata"],
  177. "swords": ["Vanargand", "Jormungandr", "Hel"],
  178. "guns": ["Knight", "Dragoon", "Bomber", "Witch", "Ninja"],
  179. "terrennials": [
  180. "Toasty", "Nero", "Ursula", "Ezra", "Alexander", "Tove", "War Rock"
  181. ],
  182. "climates": [
  183. "Balmy Sub-Tropical", "Arid Desert", "Tropical Rainforest",
  184. "Humind-Continental", "Frigid Arctic"
  185. ]
  186. }
  187. };
  188. // JSON version of bok::ctable
  189. var ctable = {
  190. "0x0": [
  191. "�", "�", "�", "�", "�", "�", "�", "�",
  192. "�", "�", "�", "�", "�", "�", "�", "�",
  193. "�", "�", "�", "�", "�", "�", "�", "�",
  194. "�", "�", "�", "�", "�", "�", "�", "�",
  195. " ", "!", "\"", "#", "÷", "%", "&", "'",
  196. "(", ")", "*", "+", ",", "-", ".", "/",
  197. "0", "1", "2", "3", "4", "5", "6", "7",
  198. "8", "9", ":", ";", "<", "=", ">", "?",
  199. "@", "A", "B", "C", "D", "E", "F", "G",
  200. "H", "I", "J", "K", "L", "M", "N", "O",
  201. "P", "Q", "R", "S", "T", "U", "V", "W",
  202. "X", "Y", "Z", "[", "×", "]", "^", "_",
  203. "`", "a", "b", "c", "d", "e", "f", "g",
  204. "h", "i", "j", "k", "l", "m", "n", "o",
  205. "p", "q", "r", "s", "t", "u", "v", "w",
  206. "x", "y", "z", "{", "|", "}", "¯", "⋅"
  207. ],
  208. "0x1F": [
  209. "�", "Ä", "�", "Ç", "É", "Ñ", "Ö", "Ü",
  210. "á", "à", "â", "ä", "�", "å", "ç", "é",
  211. "è", "ê", "ë", "í", "ì", "î", "ï", "ñ",
  212. "ó", "ò", "ô", "ö", "�", "ú", "ù", "û",
  213. "ü", "�", "°", "�", "�", "�", "�", "�",
  214. "ß", "�", "�", "�", "�", "�", "�", "�",
  215. "�", "�", "�", "�", "�", "�", "�", "�",
  216. "�", "�", "�", "�", "�", "�", "�", "�",
  217. "�", "¿", "¡", "�", "�", "�", "�", "�",
  218. "«", "»", "�", "�", "À", "�", "�", "Œ",
  219. "œ", "�", "�", "�", "�", "�", "�", "�",
  220. "ý", "ÿ", "Ý", "�", "�", "�", "�", "�",
  221. "�", "�", "�", "�", "�", "�", "Â", "Ê",
  222. "Á", "Ë", "È", "Í", "Î", "Ï", "Ì", "Ó",
  223. "Ô", "�", "Ò", "Ú", "Û", "Ù", "�", "�",
  224. "�", "�", "�", "�", "�", "�", "�", "�"
  225. ],
  226. "0x80": [
  227. "�", "あ", "い", "う", "え", "お", "か", "き",
  228. "く", "け", "こ", "さ", "し", "す", "せ", "そ",
  229. "よ", "た", "ち", "つ", "て", "と", "な", "に",
  230. "ぬ", "ね", "の", "は", "ひ", "ふ", "へ", "ほ",
  231. "下", "ま", "み", "む", "め", "も", "や", "ゆ",
  232. "よ", "ら", "り", "る", "れ", "ろ", "わ", "を",
  233. "左", "ん", "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "っ",
  234. "ゃ", "ゅ", "ょ", "が", "ぎ", "ぐ", "げ", "ご",
  235. "右", "ざ", "じ", "ず", "ぜ", "ぞ", "だ", "ぢ",
  236. "づ", "で", "ど", "ば", "び", "ぶ", "べ", "ぼ",
  237. "東", "ぱ", "ぴ", "ぷ", "ぺ", "ぽ", "。", "、",
  238. "~", "ー", "…", "�", "�", "�", "�", "�",
  239. "西", "ア", "イ", "ウ", "エ", "オ", "カ", "キ",
  240. "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ",
  241. "南", "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ",
  242. "ヌ", "ネ", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ",
  243. "北", "マ", "ミ", "ム", "メ", "モ", "ラ", "リ",
  244. "ル", "レ", "ロ", "ヤ", "ユ", "ヨ", "ワ", "ヲ",
  245. "大", "ン", "ァ", "ィ", "ゥ", "ェ", "ォ", "ッ",
  246. "ャ", "ュ", "ョ", "ガ", "ギ", "グ", "ゲ", "ゴ",
  247. "中", "ザ", "ジ", "ズ", "ゼ", "ゾ", "ダ", "ヂ",
  248. "ヅ", "デ", "ド", "バ", "ビ", "ブ", "ベ", "ボ",
  249. "小", "パ", "ピ", "プ", "ペ", "ポ", "・", ":",
  250. ";", "「", "」", "+", "×", "℃", "℉", "�",
  251. "�", "↑", "↓", "→", "←", "★", "♥", "♪",
  252. "ヴ", "Ⅰ", "Ⅱ", "Ⅲ", "�", "�", "�", "�",
  253. "風", "白", "黒", "赤", "青", "黄", "緑", "金",
  254. "銀", "紫", "�", "火", "炎", "災", "水", "氷",
  255. "永", "太", "陽", "年", "月", "日", "時", "分",
  256. "秒", "春", "夏", "秋", "冬", "之", "ヶ", "々",
  257. "=", "丈", "片", "己", "凶", "歯", "�", "�",
  258. "�", "�", "�", "�", "�", "�", "�", "�"
  259. ],
  260. "0x180": [
  261. "�", "あ", "い", "う", "え", "お", "か", "き",
  262. "く", "け", "こ", "さ", "し", "す", "せ", "そ",
  263. "よ", "た", "ち", "つ", "て", "と", "な", "に",
  264. "ぬ", "ね", "の", "は", "ひ", "ふ", "へ", "ほ",
  265. "下", "ま", "み", "む", "め", "も", "や", "ゆ",
  266. "よ", "ら", "り", "る", "れ", "ろ", "わ", "を",
  267. "左", "ん", "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "っ",
  268. "ゃ", "ゅ", "ょ", "が", "ぎ", "ぐ", "げ", "ご",
  269. "右", "ざ", "じ", "ず", "ぜ", "ぞ", "だ", "ぢ",
  270. "づ", "で", "ど", "ば", "び", "ぶ", "べ", "ぼ",
  271. "東", "ぱ", "ぴ", "ぷ", "ぺ", "ぽ", "。", "、",
  272. "~", "ー", "…", "�", "�", "�", "�", "�",
  273. "西", "ア", "イ", "ウ", "エ", "オ", "カ", "キ",
  274. "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ",
  275. "南", "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ",
  276. "ヌ", "ネ", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ",
  277. "北", "マ", "ミ", "ム", "メ", "モ", "ラ", "リ",
  278. "ル", "レ", "ロ", "ヤ", "ユ", "ヨ", "ワ", "ヲ",
  279. "大", "ン", "ァ", "ィ", "ゥ", "ェ", "ォ", "ッ",
  280. "ャ", "ュ", "ョ", "ガ", "ギ", "グ", "ゲ", "ゴ",
  281. "中", "ザ", "ジ", "ズ", "ゼ", "ゾ", "ダ", "ヂ",
  282. "ヅ", "デ", "ド", "バ", "ビ", "ブ", "ベ", "ボ",
  283. "小", "パ", "ピ", "プ", "ペ", "ポ", "・", ":",
  284. ";", "「", "」", "+", "×", "℃", "℉", "�",
  285. "�", "↑", "↓", "→", "←", "★", "♥", "♪",
  286. "ヴ", "Ⅰ", "Ⅱ", "Ⅲ", "�", "�", "�", "�",
  287. "風", "白", "黒", "赤", "青", "黄", "緑", "金",
  288. "銀", "紫", "�", "火", "炎", "災", "水", "氷",
  289. "永", "太", "陽", "年", "月", "日", "時", "分",
  290. "秒", "春", "夏", "秋", " ", "之", "ヶ", "々",
  291. "=", "丈", "片", "己", "凶", "歯", "�", "�",
  292. "�", "�", "�", "�", "�", "�", "�", "�"
  293. ],
  294. "0x81": [
  295. "�", "地", "均", "坂", "塔", "境", "塊", "填",
  296. "場", "増", "堀", "堤", "壊", "塚", "域", "城",
  297. "�", "現", "理", "球", "環", "�", "�", "�",
  298. "�", "切", "�", "功", "攻", "項", "崎", "靖",
  299. "端", "化", "代", "付", "何", "仕", "任", "仗",
  300. "仲", "伯", "件", "作", "伝", "休", "体", "仮",
  301. "住", "佐", "他", "使", "便", "信", "倍", "借",
  302. "価", "値", "低", "侮", "個", "保", "係", "供",
  303. "侵", "依", "偉", "備", "偽", "似", "俊", "傷",
  304. "像", "優", "候", "修", "例", "側", "倒", "働",
  305. "健", "併", "佳", "倫", "停", "傲", "儀", "�",
  306. "�", "�", "�", "�", "�", "�", "�", "�",
  307. "衝", "行", "往", "彼", "役", "徐", "復", "後",
  308. "待", "得", "徳", "術", "街", "御", "徴", "徹",
  309. "衛", "打", "払", "押", "択", "技", "抜", "投",
  310. "抗", "持", "担", "指", "捨", "排", "抵", "挑",
  311. "推", "提", "携", "授", "接", "掘", "操", "揮",
  312. "捕", "探", "換", "振", "掛", "援", "拠", "損",
  313. "拡", "把", "握", "掃", "撤", "�", "�", "�",
  314. "�", "�", "�", "�", "�", "�", "�", "�",
  315. "状", "牧", "物", "特", "犠", "牲", "独", "狙",
  316. "猛", "狂", "狩", "猫", "狐", "狼", "獲", "猟",
  317. "獄", "性", "快", "悦", "怪", "悟", "怖", "情",
  318. "慎", "慢", "燐", "憶", "�", "�", "�", "�",
  319. "�", "粒", "料", "粗", "精", "�", "�", "�",
  320. "�", "灯", "灼", "焼", "煙", "燥", "燃", "爆",
  321. "燼", "札", "材", "林", "杯", "村", "析", "相",
  322. "枚", "板", "松", "根", "格", "槍", "横", "株",
  323. "様", "棺", "桶", "桿", "植", "橋", "構", "機",
  324. "械", "�", "欄", "樹", "樋", "椛", "椿", "模",
  325. "根", "標", "�", "�", "�", "和", "利", "科",
  326. "称", "程", "種", "移", "秘", "積", "稼", "稲"
  327. ],
  328. "0x82": [
  329. "�", "礼", "祈", "社", "祝", "神", "視", "福",
  330. "�", "�", "�", "�", "初", "裕", "複", "被",
  331. "捕", "紅", "紀", "約", "紋", "紙", "細", "組",
  332. "統", "終", "純", "練", "級", "緒", "経", "絵",
  333. "給", "絃", "納", "紹", "絡", "結", "続", "継",
  334. "絶", "編", "縁", "博", "織", "総", "縦", "綾",
  335. "締", "績", "網", "縮", "絆", "幻", "郷", "�",
  336. "�", "次", "冷", "凍", "�", "�", "議", "論",
  337. "訳", "計", "討", "記", "許", "訓", "詳", "説",
  338. "話", "証", "読", "設", "語", "談", "試", "調",
  339. "誤", "課", "誠", "誘", "護", "認", "謙", "誕",
  340. "識", "謝", "泡", "汚", "浪", "液", "涙", "汰",
  341. "沙", "江", "況", "沢", "泊", "河", "注", "洋",
  342. "泣", "治", "活", "浴", "浩", "池", "波", "洗",
  343. "流", "法", "決", "油", "消", "温", "浮", "海",
  344. "�", "�", "�", "�", "�", "�", "�", "�"
  345. ]
  346. };
  347. // JSON version of bok::b64table
  348. var b64table = {
  349. "jp": [
  350. "あ", "い", "う", "え", "お", "か", "き", "く",
  351. "け", "こ", "さ", "し", "す", "せ", "そ", "た",
  352. "ち", "つ", "て", "と", "な", "に", "ぬ", "ね",
  353. "の", "は", "ひ", "ふ", "へ", "ほ", "ま", "み",
  354. "む", "め", "も", "や", "ゆ", "よ", "ら", "り",
  355. "る", "れ", "ろ", "わ", "を", "が", "ぎ", "ぐ",
  356. "げ", "ご", "ざ", "じ", "ず", "ぜ", "ぞ", "だ",
  357. "ぢ", "づ", "で", "ど", "ば", "び", "ぶ", "べ", "ぼ"
  358. ],
  359. "en": [
  360. "B", "C", "D", "F", "G", "H", "J", "K",
  361. "L", "M", "N", "P", "Q", "R", "S", "T",
  362. "V", "W", "X", "Y", "Z",
  363. "b", "c", "d", "f", "g", "h", "j", "k",
  364. "l", "m", "n", "p", "q", "r", "s", "t",
  365. "v", "w", "x", "y", "z",
  366. "0", "1", "2", "3", "4", "5", "6", "7",
  367. "8", "9", "?", "!", "@", "#", "=", "^",
  368. ">", "/", "-", "_", "+", ":", "."
  369. ],
  370. "lk": [
  371. "A", "B", "C", "D", "E", "F", "G", "H", "I",
  372. "J", "K", "L", "M", "N", "O", "P", "Q", "R",
  373. "S", "T", "U", "V", "W", "X", "Y", "Z",
  374. "a", "b", "c", "d", "e", "f", "g", "h", "i",
  375. "j", "k", "l", "m", "n", "o", "p", "q", "r",
  376. "s", "t", "u", "v", "w", "x", "y", "z",
  377. "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "?", "=", "."
  378. ]
  379. };
  380. // JSON version of bok::bitmaps using objects instead of lists.
  381. // Each value is an object with a "type" string, an "offset" integer, and a
  382. // "width" integer. "int" types can also have a "signed" boolean.
  383. // "str" types have a "count" integer in addition to "width", and can have
  384. // a "restrict" integer which corresponds to a table in 'ctables'.
  385. var bitmaps = {
  386. "jp": [ {}, {
  387. "region": {
  388. "type": "int", "offset": 0, "width": 3, "signed": false
  389. },
  390. "checksum": {
  391. "type": "int", "offset": 3, "width": 16, "signed": false
  392. },
  393. "offset": {
  394. "type": "int", "offset": 19, "width": 2, "signed": false
  395. },
  396. "sol/4": {
  397. "type": "int", "offset": 21, "width": 9, "signed": false
  398. },
  399. "timezone": {
  400. "type": "int", "offset": 30, "width": 6, "signed": false
  401. },
  402. "hours": {
  403. "type": "int", "offset": 36, "width": 6, "signed": false
  404. },
  405. "minutes": {
  406. "type": "int", "offset": 42, "width": 6, "signed": false
  407. },
  408. "difficulty": {
  409. "type": "int", "offset": 48, "width": 3, "signed": false
  410. },
  411. "dungeons": {
  412. "type": "int", "offset": 51, "width": 5, "signed": false
  413. },
  414. "clears": {
  415. "type": "int", "offset": 56, "width": 3, "signed": false
  416. },
  417. "continues": {
  418. "type": "int", "offset": 59, "width": 4, "signed": false
  419. },
  420. "caught": {
  421. "type": "int", "offset": 63, "width": 5, "signed": false
  422. },
  423. "kills": {
  424. "type": "int", "offset": 68, "width": 7, "signed": false
  425. },
  426. "rank": {
  427. "type": "int", "offset": 75, "width": 4, "signed": false
  428. },
  429. "title": {
  430. "type": "int", "offset": 79, "width": 5, "signed": false
  431. },
  432. "name": {
  433. "type": "str", "offset": 84, "count": 5, "width": 40, "restrict": 0x180
  434. },
  435. "link-battles": {
  436. "type": "int", "offset": 124, "width": 6, "signed": false
  437. },
  438. "link-trades": {
  439. "type": "int", "offset": 130, "width": 6, "signed": false
  440. },
  441. "loan": {
  442. "type": "int", "offset": 136, "width": 8, "signed": false
  443. },
  444. "padding": {
  445. "type": "int", "offset": 144, "width": 0, "signed": false
  446. }
  447. }, {
  448. "checksum": {
  449. "type": "int", "offset": 0, "width": 16, "signed": false
  450. },
  451. "header-padding": {
  452. "type": "int", "offset": 16, "width": 2, "signed": false
  453. },
  454. "region": {
  455. "type": "int", "offset": 18, "width": 3, "signed": false
  456. },
  457. "offset": {
  458. "type": "int", "offset": 21, "width": 3, "signed": false
  459. },
  460. "timezone": {
  461. "type": "int", "offset": 24, "width": 6, "signed": false
  462. },
  463. "side": {
  464. "type": "int", "offset": 30, "width": 3, "signed": false
  465. },
  466. "style": {
  467. "type": "int", "offset": 33, "width": 4, "signed": false
  468. },
  469. "kills/8": {
  470. "type": "int", "offset": 37, "width": 8, "signed": false
  471. },
  472. "forges": {
  473. "type": "int", "offset": 45, "width": 7, "signed": false
  474. },
  475. "link-battles": {
  476. "type": "int", "offset": 52, "width": 6, "signed": false
  477. },
  478. "link-shopping" : {
  479. "type": "int", "offset": 58, "width": 6, "signed": false
  480. },
  481. "sol/4096": {
  482. "type": "int", "offset": 64, "width": 10, "signed": true
  483. },
  484. "loan/256": {
  485. "type": "int", "offset": 74, "width": 8, "signed": true
  486. },
  487. "hours": {
  488. "type": "int", "offset": 82, "width": 6, "signed": false
  489. },
  490. "titles": {
  491. "type": "int", "offset": 88, "width": 16, "signed": false
  492. },
  493. "name": {
  494. "type": "str", "offset": 104, "count": 5, "width": 40, "restrict": 0x180
  495. },
  496. "padding": {
  497. "type": "int", "offset": 144, "width": 0, "signed": false
  498. }
  499. }, {
  500. "checksum": {
  501. "type": "int", "offset": 0, "width": 16, "signed": false
  502. },
  503. "header-padding": {
  504. "type": "int", "offset": 16, "width": 2, "signed": false
  505. },
  506. "region": {
  507. "type": "int", "offset": 18, "width": 3, "signed": false
  508. },
  509. "offset": {
  510. "type": "int", "offset": 21, "width": 3, "signed": false
  511. },
  512. "timezone": {
  513. "type": "int", "offset": 24, "width": 6, "signed": false},
  514. "kills/8": {
  515. "type": "int", "offset": 30, "width": 8, "signed": false
  516. },
  517. "forges": {
  518. "type": "int", "offset": 38, "width": 7, "signed": false
  519. },
  520. "races": {
  521. "type": "int", "offset": 45, "width": 9, "signed": false
  522. },
  523. "link-races": {
  524. "type": "int", "offset": 54, "width": 7, "signed": false
  525. },
  526. "cross-linked": {
  527. "type": "int", "offset": 61, "width": 1, "signed": false
  528. },
  529. "endings": {
  530. "type": "int", "offset": 62, "width": 6, "signed": false
  531. },
  532. "sol/4096": {
  533. "type": "int", "offset": 68, "width": 10, "signed": false
  534. },
  535. "loan/256": {
  536. "type": "int", "offset": 78, "width": 8, "signed": false
  537. },
  538. "hours": {
  539. "type": "int", "offset": 86, "width": 6, "signed": false
  540. },
  541. "titles": {
  542. "type": "int", "offset": 92, "width": 12, "signed": false
  543. },
  544. "name": {
  545. "type": "str", "offset": 104, "count": 5, "width": 40, "restrict": 0x180
  546. },
  547. "padding": {
  548. "type": "int", "offset": 144, "width": 0, "signed": false
  549. }
  550. }, {
  551. "checksum": {
  552. "type": "int", "offset": 0, "width": 16, "signed": false
  553. },
  554. "header-padding": {
  555. "type": "int", "offset": 16, "width": 2, "signed": false
  556. },
  557. "region": {
  558. "type": "int", "offset": 18, "width": 3, "signed": false
  559. },
  560. "offset": {
  561. "type": "int", "offset": 21, "width": 3, "signed": false
  562. },
  563. "titles": {
  564. "type": "int", "offset": 24, "width": 14, "signed": false
  565. },
  566. "difficulty": {
  567. "type": "int", "offset": 38, "width": 2, "signed": false
  568. },
  569. "hours": {
  570. "type": "int", "offset": 40, "width": 7, "signed": false
  571. },
  572. "sol/4096": {
  573. "type": "int", "offset": 47, "width": 15, "signed": false
  574. },
  575. "sword": {
  576. "type": "int", "offset": 62, "width": 3, "signed": false
  577. },
  578. "gun": {
  579. "type": "int", "offset": 65, "width": 3, "signed": false
  580. },
  581. "terrennial": {
  582. "type": "int", "offset": 68, "width": 3, "signed": false
  583. },
  584. "climate": {
  585. "type": "int", "offset": 71, "width": 3, "signed": false
  586. },
  587. "name-dark": {
  588. "type": "str", "offset": 74, "count": 10, "width": 80
  589. },
  590. "name-solar": {
  591. "type": "str", "offset": 154, "count": 10, "width": 80
  592. },
  593. "padding": {
  594. "type": "int", "offset": 234, "width": 6, "signed": false
  595. }
  596. }],
  597. "en": [ {}, {
  598. "region": {
  599. "type": "int", "offset": 0, "width": 3, "signed": false
  600. },
  601. "checksum": {
  602. "type": "int", "offset": 3, "width": 16, "signed": false
  603. },
  604. "offset": {
  605. "type": "int", "offset": 19, "width": 2, "signed": false
  606. },
  607. "sol/4": {
  608. "type": "int", "offset": 21, "width": 9, "signed": false
  609. },
  610. "timezone": {
  611. "type": "int", "offset": 30, "width": 7, "signed": false
  612. },
  613. "hours": {
  614. "type": "int", "offset": 37, "width": 6, "signed": false
  615. },
  616. "minutes": {
  617. "type": "int", "offset": 43, "width": 6, "signed": false
  618. },
  619. "difficulty": {
  620. "type": "int", "offset": 49, "width": 3, "signed": false
  621. },
  622. "dungeons": {
  623. "type": "int", "offset": 52, "width": 5, "signed": false
  624. },
  625. "clears": {
  626. "type": "int", "offset": 57, "width": 3, "signed": false
  627. },
  628. "continues": {
  629. "type": "int", "offset": 60, "width": 4, "signed": false
  630. },
  631. "caught": {
  632. "type": "int", "offset": 64, "width": 5, "signed": false
  633. },
  634. "kills": {
  635. "type": "int", "offset": 69, "width": 7, "signed": false
  636. },
  637. "rank": {
  638. "type": "int", "offset": 76, "width": 4, "signed": false
  639. },
  640. "title": {
  641. "type": "int", "offset": 80, "width": 5, "signed": false
  642. },
  643. "name": {
  644. "type": "str", "offset": 85, "count": 9, "width": 72, "restrict": 0
  645. },
  646. "link-battles": {
  647. "type": "int", "offset": 157, "width": 6, "signed": false
  648. },
  649. "link-trades": {
  650. "type": "int", "offset": 163, "width": 6, "signed": false
  651. },
  652. "loan": {
  653. "type": "int", "offset": 169, "width": 8, "signed": false
  654. },
  655. "padding": {
  656. "type": "int", "offset": 177, "width": 15, "signed": false
  657. }
  658. }, {
  659. "checksum": {
  660. "type": "int", "offset": 0, "width": 16, "signed": false
  661. },
  662. "header-padding": {
  663. "type": "int", "offset": 16, "width": 2, "signed": false
  664. },
  665. "region": {
  666. "type": "int", "offset": 18, "width": 3, "signed": false
  667. },
  668. "offset": {
  669. "type": "int", "offset": 21, "width": 3, "signed": false
  670. },
  671. "timezone": {
  672. "type": "int", "offset": 24, "width": 7, "signed": false
  673. },
  674. "side": {
  675. "type": "int", "offset": 31, "width": 3, "signed": false
  676. },
  677. "style": {
  678. "type": "int", "offset": 34, "width": 4, "signed": false
  679. },
  680. "kills/8": {
  681. "type": "int", "offset": 38, "width": 8, "signed": false
  682. },
  683. "forges": {
  684. "type": "int", "offset": 46, "width": 7, "signed": false
  685. },
  686. "link-battles": {
  687. "type": "int", "offset": 53, "width": 6, "signed": false
  688. },
  689. "link-shopping": {
  690. "type": "int", "offset": 59, "width": 6, "signed": false
  691. },
  692. "sol/4096": {
  693. "type": "int", "offset": 65, "width": 10, "signed": true
  694. },
  695. "loan/256": {
  696. "type": "int", "offset": 75, "width": 8, "signed": true
  697. },
  698. "hours": {
  699. "type": "int", "offset": 83, "width": 6, "signed": false
  700. },
  701. "titles": {
  702. "type": "int", "offset": 89, "width": 16, "signed": false
  703. },
  704. "name": {
  705. "type": "str", "offset": 105, "count": 9, "width": 72, "restrict": 0
  706. },
  707. "padding": {
  708. "type": "int", "offset": 177, "width": 15, "signed": false
  709. }
  710. }, {
  711. "checksum": {
  712. "type": "int", "offset": 0, "width": 16, "signed": false
  713. },
  714. "header-padding": {
  715. "type": "int", "offset": 16, "width": 2, "signed": false
  716. },
  717. "region": {
  718. "type": "int", "offset": 18, "width": 3, "signed": false
  719. },
  720. "offset": {
  721. "type": "int", "offset": 21, "width": 3, "signed": false
  722. },
  723. "timezone": {
  724. "type": "int", "offset": 24, "width": 7, "signed": false
  725. },
  726. "kills/8": {
  727. "type": "int", "offset": 31, "width": 8, "signed": false
  728. },
  729. "forges": {
  730. "type": "int", "offset": 39, "width": 7, "signed": false
  731. },
  732. "races": {
  733. "type": "int", "offset": 46, "width": 9, "signed": false
  734. },
  735. "link-races": {
  736. "type": "int", "offset": 55, "width": 7, "signed": false
  737. },
  738. "cross-linked": {
  739. "type": "int", "offset": 62, "width": 1, "signed": false
  740. },
  741. "endings": {
  742. "type": "int", "offset": 63, "width": 6, "signed": false
  743. },
  744. "sol/4096": {
  745. "type": "int", "offset": 69, "width": 10, "signed": false
  746. },
  747. "loan/256": {
  748. "type": "int", "offset": 79, "width": 8, "signed": false
  749. },
  750. "hours": {
  751. "type": "int", "offset": 87, "width": 6, "signed": false
  752. },
  753. "titles": {
  754. "type": "int", "offset": 93, "width": 12, "signed": false
  755. },
  756. "name": {
  757. "type": "str", "offset": 105, "count": 9, "width": 72, "restrict": 0
  758. },
  759. "padding": {
  760. "type": "int", "offset": 177, "width": 15, "signed": false
  761. }
  762. }, {
  763. "checksum": {
  764. "type": "int", "offset": 0, "width": 16, "signed": false
  765. },
  766. "header-padding": {
  767. "type": "int", "offset": 16, "width": 2, "signed": false
  768. },
  769. "region": {
  770. "type": "int", "offset": 18, "width": 3, "signed": false
  771. },
  772. "offset": {
  773. "type": "int", "offset": 21, "width": 3, "signed": false
  774. },
  775. "titles": {
  776. "type": "int", "offset": 24, "width": 14, "signed": false
  777. },
  778. "difficulty": {
  779. "type": "int", "offset": 38, "width": 2, "signed": false
  780. },
  781. "hours": {
  782. "type": "int", "offset": 40, "width": 7, "signed": false
  783. },
  784. "sol/4096": {
  785. "type": "int", "offset": 47, "width": 15, "signed": false
  786. },
  787. "sword": {
  788. "type": "int", "offset": 62, "width": 3, "signed": false
  789. },
  790. "gun": {
  791. "type": "int", "offset": 65, "width": 3, "signed": false
  792. },
  793. "terrennial": {
  794. "type": "int", "offset": 68, "width": 3, "signed": false
  795. },
  796. "climate": {
  797. "type": "int", "offset": 71, "width": 3, "signed": false
  798. },
  799. "name-dark": {
  800. "type": "str", "offset": 74, "count": 10, "width": 80
  801. },
  802. "name-solar": {
  803. "type": "str", "offset": 154, "count": 10, "width": 80
  804. },
  805. "padding": {
  806. "type": "int", "offset": 234, "width": 6, "signed": false
  807. }
  808. }]
  809. };
  810. // JSON version of bok::ui::keyboards,
  811. // Extra data is removed from the key lists and split between keyboards.alias
  812. // and keyboards.expand. Empty strings still skip to the next column.
  813. var keyboards = {
  814. "en64": [
  815. ["B", "C", "D", "F", "G", "H", "J", "@", "#", "^", "⌫", "⌦"],
  816. ["K", "L", "M", "N", "P", "Q", "R", "0", "1", "2", "3", "4"],
  817. ["S", "T", "V", "W", "X", "Y", "Z", "5", "6", "7", "8", "9"],
  818. ["b", "c", "d", "f", "g", "h", "j", "+", "-", "/", "." ],
  819. ["k", "l", "m", "n", "p", "q", "r", "=", "_", "?", "←", "→"],
  820. ["s", "t", "v", "w", "x", "y", "z", ":", ">", "!", "␣" ]
  821. ],
  822. "lk64": [
  823. ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", ".", "⌫", "⌦"],
  824. ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"],
  825. ["N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"],
  826. ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"],
  827. ["n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"],
  828. ["?", "=", "␣", "←", "→"]
  829. ],
  830. "jp64": [
  831. ["あ", "い", "う", "え", "お", "ま", "み", "む", "め", "も", "⌫", "⌦" ],
  832. ["か", "き", "く", "け", "こ", "や", "ゆ", "よ", "わ", "を", "ば", "び"],
  833. ["さ", "し", "す", "せ", "そ", "ら", "り", "る", "れ", "ろ", "ぶ", "べ"],
  834. ["た", "ち", "つ", "て", "と", "が", "ぎ", "ぐ", "げ", "ご", "ぼ" ],
  835. ["な", "に", "ぬ", "ね", "の", "ざ", "じ", "ず", "ぜ", "ぞ", "←", "→" ],
  836. ["は", "ひ", "ふ", "へ", "ほ", "だ", "ぢ", "づ", "で", "ど", "␣" ]
  837. ],
  838. "en": [
  839. ["A", "B", "C", "D", "E", "F", "G", "H", "I", "⌫", "⌦"],
  840. ["J", "K", "L", "M", "N", "O", "P", "Q", "R", ".", ","],
  841. ["S", "T", "U", "V", "W", "X", "Y", "Z", "", "'", "-"],
  842. ["a", "b", "c", "d", "e", "f", "g", "h", "i", "/" ],
  843. ["j", "k", "l", "m", "n", "o", "p", "q", "r", "←", "→"],
  844. ["s", "t", "u", "v", "w", "x", "y", "z", "", "␣" ]
  845. ],
  846. "ext": [
  847. ["&", "*", ";", "+", "-", "×", "÷", "=", "⌫", "⌦"],
  848. ["à", "á", "â", "ä", "è", "é", "ê", "ë", "(", ")"],
  849. ["ì", "í", "î", "ï", "ò", "ó", "ô", "ö", "[", "]"],
  850. ["ù", "ú", "û", "ü", "ç", "ñ", "œ", "ß", "!", "?"],
  851. ["À", "Á", "Â", "Ä", "È", "É", "Ê", "Ë", "¡", "¿"],
  852. ["Ì", "Í", "Î", "Ï", "Ò", "Ó", "Ô", "Ö", "←", "→"],
  853. ["Ù", "Ú", "Û", "Ü", "Ç", "Ñ", "Œ", "⋅", "␣" ]
  854. ],
  855. "hiri": [
  856. ["あ", "い", "う", "え", "お", "ら", "り", "る", "れ", "ろ", "⌫", "⌦" ],
  857. ["か", "き", "く", "け", "こ", "が", "ぎ", "ぐ", "げ", "ご", "ん", "・"],
  858. ["さ", "し", "す", "せ", "そ", "ざ", "じ", "ず", "ぜ", "ぞ", "ー", "~" ],
  859. ["た", "ち", "つ", "て", "と", "だ", "ぢ", "づ", "で", "ど" ],
  860. ["な", "に", "ぬ", "ね", "の", "ば", "び", "ぶ", "べ", "ぼ" ],
  861. ["は", "ひ", "ふ", "へ", "ほ", "ぱ", "ぴ", "ぷ", "ぺ", "ぽ" ],
  862. ["ま", "み", "む", "め", "も", "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "←", "→" ],
  863. ["や", "ゆ", "よ", "わ", "を", "っ", "ゃ", "ゅ", "ょ", "", "␣" ]
  864. ],
  865. "kata": [
  866. ["ア", "イ", "ウ", "エ", "オ", "ラ", "リ", "ル", "レ", "ロ", "⌫", "⌦" ],
  867. ["カ", "キ", "ク", "ケ", "コ", "ガ", "ギ", "グ", "ゲ", "ゴ", "ン", "・"],
  868. ["サ", "シ", "ス", "セ", "ソ", "ザ", "ジ", "ズ", "ゼ", "ゾ", "ー", "~" ],
  869. ["タ", "チ", "ツ", "テ", "ト", "ダ", "ヂ", "ヅ", "デ", "ド" ],
  870. ["ナ", "ニ", "ヌ", "ネ", "ノ", "バ", "ビ", "ブ", "ベ", "ボ" ],
  871. ["ハ", "ヒ", "フ", "ヘ", "ホ", "パ", "ピ", "プ", "ペ", "ポ" ],
  872. ["マ", "ミ", "ム", "メ", "モ", "ァ", "ィ", "ゥ", "ェ", "ォ", "←", "→" ],
  873. ["ヤ", "ユ", "ヨ", "ワ", "ヲ", "ッ", "ャ", "ュ", "ョ", "ヴ", "␣" ]
  874. ],
  875. "alias": {
  876. "⌫": "Backspace", "⌦": "Delete",
  877. "←": "ArrowLeft", "→": "ArrowRight",
  878. "␣": " "
  879. }, "expand": {
  880. "␣": true
  881. }};
  882. // 'decstr' converts a list of 8-bit integers to a string, mapping to
  883. // 'ctable'. If 'table' is specified then the decoding only uses table
  884. // 'table', essentially assuming that each 8-bit integer is preceded by
  885. // the table-change character for 'table'.
  886. function decstr(list, table = "") {
  887. var i, r, f = "", len;
  888. if (typeof(table) == "number")
  889. table = "0x" + table.toString(16).toUpperCase();
  890. else if (typeof(table) == "undefined")
  891. table = ""
  892. if (table != "" && !(table in ctable))
  893. throw new Error("Invalid table: " + table);
  894. for (len = list.length; list[len - 1] == 0; len--) {}
  895. for (i = 0, r = []; i < len; i++) {
  896. if (f == "") {
  897. if (table == "") {
  898. f = "0x" + list[i].toString(16).toUpperCase();
  899. if (f in ctable)
  900. continue;
  901. f = "0x" + (list[i] >>> 8).toString(16).toUpperCase();
  902. } else
  903. f = table;
  904. }
  905. r.push(f in ctable && (list[i] & 0xFF) < ctable[f].length ?
  906. ctable[f][list[i] & 0xFF] : "�");
  907. f = "";
  908. }
  909. return r.join("");
  910. }
  911. // 'encstr' converts a string to a list of 8-bit integers, mapping from
  912. // 'ctable'. If $tab is specified the encoder only uses the 'table' table,
  913. // essentially omitting the table-change characters.
  914. function encstr(str, table = "") {
  915. var i, r, k, t;
  916. if (typeof(table) == "number")
  917. table = "0x" + table.toString(16).toUpperCase();
  918. else if (typeof(table) == "undefined")
  919. table = "";
  920. if (table != "" && !(table in ctable))
  921. throw new Error("Invalid table: " + table);
  922. for (i = 0, r = []; i < str.length; i++) {
  923. if (str[i] == "�")
  924. break;
  925. if (table != "") {
  926. k = 0;
  927. t = ctable[table].indexOf(str[i]);
  928. } else {
  929. for (k in ctable)
  930. if ((t = ctable[k].indexOf(str[i])) >= 0)
  931. break;
  932. k = parseInt(k);
  933. }
  934. if (t < 0)
  935. break;
  936. if (k != 0)
  937. r.push(k);
  938. r.push(t);
  939. }
  940. return r;
  941. }
  942. // 'decb64' decodes the base64 string 'str' using 'b64table' into a list of
  943. // 6-bit integers, ignoring any [:space:] characters. If the 'game' is 4 the
  944. // "jp" & "lk" tables are used, otherwise the "jp" & "en" tables are used.
  945. function decb64(str, game = 1) {
  946. var en = game < 4 ? "en" : "lk";
  947. var r = [];
  948. var i, t;
  949. for (i = 0; i < str.length; i++) {
  950. if (/\s/.test(str[i]))
  951. continue;
  952. if ((t = b64table.jp.indexOf(str[i])) < 0 &&
  953. (t = b64table[en].indexOf(str[i])) < 0)
  954. throw new Error("Invalid character: '" + str[i] + "'");
  955. if (t < 64)
  956. r.push(t);
  957. }
  958. return r;
  959. }
  960. // 'encb64' encodes a base64 string from 'list', which is list of 6-bit
  961. // integers using the appropriate alphabet for the 'game' / 'lang' pair.
  962. // 'lang' is set based on the region in 'list' if it is not specified.
  963. function encb64(list, game = 1, lang = "", pad = true) {
  964. var table, chars, pad, i, j;
  965. if (typeof(lang) == "undefined" || lang == "")
  966. lang = getlang(list);
  967. table = lang == "jp" ? "jp" : game < 4 ? "en" : "lk";
  968. pad = pad ? game >= 4 ? 5 : lang == "jp" ? 6 : 8 : 0;
  969. for (i = 0, j = 0, chars = []; i < list.length; i++) {
  970. if (list[i] < 0 || list[i] >= 64)
  971. throw new Error("Invalid value: " + list[i]);
  972. if (pad > 0 && j > 0 && j % pad == 0)
  973. chars.push(" ");
  974. chars.push(b64table[table][list[i]]);
  975. j++;
  976. }
  977. return chars.join("");
  978. }
  979. // 'getint' gets an integer of width 'count' at bit-offset 'offset' from the
  980. // list 'list' of 'width' wide integers, interpreting it as a two's complement
  981. // signed integer if 'signed' is true.
  982. // The bits are read and written LSb first.
  983. function getint(list, offset, count, signed = false, width = 6) {
  984. var i, v;
  985. count = Math.min(count, list.length * width - offset);
  986. for (v = 0, i = 0; i < count; i++, offset++)
  987. v |= (list[offset / width | 0] >>> offset % width & 1) << i;
  988. if (signed && count > 0 && (v >>> count - 1 & 1))
  989. v = -(~v + 1 & (1 << count) - 1);
  990. return v
  991. }
  992. // 'putint' puts the integer 'v' of width 'count' into list 'list' at
  993. // bit-offset 'offset' and returns the 'list'. 'list' is a list of 'width'
  994. // wide integers. Zeros are added to the list as needed. 'v' is clamped to
  995. // [0,2^$count) for unsigned integers, and [-2^($count-1),2^($count-1)) for
  996. // signed integers. Signed numbers are converted to two's-complement
  997. // formatted 'width' wide unsigned integers before writing.
  998. // The bits are written and read LSb first.
  999. function putint(list, v, offset, count, signed = false, width = 6) {
  1000. var i, m;
  1001. m = (1 << count - (signed && count > 0 ? 1 : 0)) - 1;
  1002. v = signed ? Math.min(Math.max(v, -(m + 1)), m) + m + 1 ^ m + 1 :
  1003. Math.min(Math.max(v, 0), m);
  1004. for (i = 0; i < count; i++, offset++) {
  1005. while ((offset / width | 0) >= list.length)
  1006. list.push(0);
  1007. list[offset / width | 0] &= ~(1 << offset % width);
  1008. list[offset / width | 0] |= (v >>> i & 1) << offset % width;
  1009. }
  1010. return list
  1011. }
  1012. // 'getstr' gets the string converted from 'count' octets from the 'list' of
  1013. // 'width' wide integers starting at bit-offset 'offset'. 'table' is passed
  1014. // to 'decstr' along with the octets.
  1015. function getstr(list, offset, count, table = "", width = 6) {
  1016. var i, r;
  1017. count = Math.min(count, list.length * width - offset >>> 3);
  1018. for (i = 0, r = []; i < count; i++)
  1019. r[i] = getint(list, offset + i * 8, 8, 0, width);
  1020. return decstr(r, table);
  1021. }
  1022. // 'putstr' puts 'count' octets from the encoded 'str' into 'list' at bit-
  1023. // offset 'offset'. 'list' is a list of 'width' wide integers and 'table'
  1024. // is passed to 'encstr' along with $str. 'list' is then returned.
  1025. function putstr(list, str, offset, count, table = "", width = 6) {
  1026. var i, l = encstr(str, table);
  1027. for (i = 0; i < count; i++)
  1028. putint(list, i < l.length ? l[i] : 0, offset + i * 8, 8, 0, width);
  1029. return list;
  1030. }
  1031. // 'forkeys' iterates over the provided keys, or array of keys, or all keys,
  1032. // from the corresponding list for the 'game' / 'lang' pair in 'bitmaps'.
  1033. // Values are read from the list 'list' of 'width' wide integers and the
  1034. // callback is called with the key, value, and value from 'bitmaps'.
  1035. // If 'FORKEYS_RESTRICT' is set in 'flags' then keys are skipped if not
  1036. // present in 'list'. If 'FORKEYS_MODIFY' is set in 'flags' then the value
  1037. // in 'list' is set to the return value of the callback.
  1038. var FORKEYS_RESTRICT = 1;
  1039. var FORKEYS_MODIFY = 2;
  1040. function forkeys(list, game, lang, width, flags /*, [key, ...] callback */) {
  1041. var callback, args, map, i, k, v, f;
  1042. if (arguments.length < 6)
  1043. return new Error("Wrong number of arguments");
  1044. if (typeof(lang) == "undefined" || lang == "")
  1045. lang = getlang(list, game, width);
  1046. map = bitmaps[lang][game];
  1047. callback = arguments[arguments.length - 1];
  1048. args = Array.prototype.slice.call(arguments, 5, arguments.length - 1);
  1049. if (args.length == 1 && typeof(args[0]) == "object")
  1050. args = args[0];
  1051. if (args.length == 0)
  1052. args = Object.keys(map);
  1053. for (i = 0; i < args.length; i++) {
  1054. k = args[i];
  1055. if (!(k in map))
  1056. continue;
  1057. if ((flags & FORKEYS_RESTRICT) &&
  1058. map[k].offset + map[k].width > list.length * width)
  1059. continue;
  1060. switch (map[k].type) {
  1061. case "int":
  1062. f = "signed" in map[k] ? map[k].signed : false;
  1063. v = callback(k, getint(list, map[k].offset, map[k].width, f, width),
  1064. map[k]);
  1065. if (flags & FORKEYS_MODIFY)
  1066. putint(list, v, map[k].offset, map[k].width, f, width);
  1067. break;
  1068. case "str":
  1069. f = "restrict" in map[k] ? map[k].restrict : "";
  1070. v = callback(k, getstr(list, map[k].offset, map[k].count, f, width),
  1071. map[k]);
  1072. if (flags & FORKEYS_MODIFY)
  1073. putstr(list, v, map[k].offset, map[k].count, f, width);
  1074. break;
  1075. }
  1076. }
  1077. return list;
  1078. }
  1079. // 'getdict' returns a dictionary created by reading values from the 'list' of
  1080. // 'width' wide integers for each key, or every key if none are given, as
  1081. // specified in 'bitmaps' for the 'game' / 'lang' pair.
  1082. function getdict(list, game = "", lang = "", width = 6, /*, [key ...] */) {
  1083. var r = {};
  1084. forkeys(list, game, lang, width, 0,
  1085. Array.prototype.slice.call(arguments, 4), function (k, v) {r[k]=v});
  1086. return r;
  1087. }
  1088. // 'getkeys' returns a list of values similar to 'getdict'.
  1089. function getkeys(list, game = "", lang = "", width = 6 /*, key ... */) {
  1090. var r = [];
  1091. forkeys(list, game, lang, width, 0,
  1092. Array.protoype.slice.call(arguments, 4), function (k, v) {
  1093. r.push(v)
  1094. });
  1095. return r;
  1096. }
  1097. // 'getkeys' returns a single value similar to 'getkeys'
  1098. function getkey(list, game, lang, width, key) {
  1099. var r = undefined;
  1100. forkeys(list, game, lang, width, 0, key, function (k, v) {r = v});
  1101. return r;
  1102. }
  1103. // 'getregion' reads the region bits from the 'list' of 'width' wide integers
  1104. // for game 'game' and returns a short region code, or an error.
  1105. function getregion(list, game = 1, width = 6) {
  1106. var r = getkey(list, game, "jp", width, "region");
  1107. if (typeof(r) == "undefined" || r < 1 || r > 3)
  1108. throw new Error("Invalid region: " + r);
  1109. return ["", "jp", "na", "eu"][r];
  1110. }
  1111. // 'getlang' reads the region bits from the 'list' of 'width' wide integers
  1112. // for game 'game' and returns a short language code, or an error.
  1113. function getlang(list, game = 1, width = 6) {
  1114. return {jp: "jp", na: "en", eu: "en"}[getregion(list, game, width)];
  1115. }
  1116. // 'whichgame' searches 'dict' for keys specific to a particular game, and
  1117. // returns the game number of the first one found.
  1118. function whichgame(dict) {
  1119. var keys = [[
  1120. "dungeons", "clears", "caught", "rank", "title", "link-trades",
  1121. "rank-name", "title-name"
  1122. ], [
  1123. "side", "style", "link-shopping", "side-name", "style-name"
  1124. ], [
  1125. "races", "link-races", "cross-linked", "endings"
  1126. ], [
  1127. "sword", "gun", "terrennial", "climate", "name-dark", "name-solar",
  1128. "sword-name", "gun-name", "terrennial-name", "climate-name"
  1129. ]];
  1130. var i, j;
  1131. for (i = 0; i < keys.length; i++)
  1132. for (j = 0; j < keys[i].length; j++)
  1133. if (keys[i][j] in dict)
  1134. return i + 1;
  1135. throw new Error("Unknown game");
  1136. }
  1137. // 'putdict' inserts the values from each key/value pair in 'dict' into
  1138. // the 'list' of 'width' wide integers as specified in 'bitmaps' for the
  1139. // 'game' / 'lang' pair and returns the modified 'list'
  1140. function putdict(list, dict, game = "", lang = "", width = 6) {
  1141. var args;
  1142. if (typeof(game) == "undefined" || game == "")
  1143. game = whichgame(dict);
  1144. if (typeof(lang) == "undefined" || lang == "") {
  1145. if (dict.region < 0 || dict.region > 3)
  1146. throw new Error("Invalid region: " + dict.region);
  1147. lang = ["", "jp", "en", "en"][dict.region];
  1148. }
  1149. return forkeys(list, game, lang, width, FORKEYS_MODIFY, Object.keys(dict),
  1150. function (k) {return dict[k]});
  1151. }
  1152. // 'checksum' calculates the CRC-16 for the 'list' of 6-bit integers with an
  1153. // initializer of 0xFFFF, the 'game' appropriate polynomial, and the output
  1154. // inverted, and starting at the 'game' appropriate offset.
  1155. function checksum(list, game = 1) {
  1156. var j;
  1157. var i = [4, 3, 3, 3][game - 1];
  1158. var c = [0x1021, 0x1021, 0x8005, 0x180D][game - 1];
  1159. var v = 0xFFFF;
  1160. for (; i < list.length; i++) {
  1161. v ^= list[i] << 8;
  1162. for (j = 0; j < 8; j++)
  1163. v = (v << 1 ^ ((v & 0x8000) ? c : 0)) & 0xFFFF;
  1164. }
  1165. return ~v & 0xFFFF;
  1166. }
  1167. // 'xor' encodes/decodes the password bits from the 'list' of 6-bit integers
  1168. // the constant and the 'game' appropriate initalizer.
  1169. // 'm' is the initialized value, 't' is initialized to ~0, and the offset is
  1170. // read from the 'list'. For each iteration 'm' is multiplied by the constant
  1171. // then incremented by 1, and 't' is xored by the result. After 'offset' plus
  1172. // 4 iterations, starting at index 4 until the end of the $list, each $list
  1173. // item is xored by the last 6 bits of $t before the next iteration.
  1174. // The encoded list is returned.
  1175. // Using the full constants causes the doubles to lose precision, luckily only
  1176. // the last 6 bits of the actually matter, 'Math.imul' colud be used as well.
  1177. function xor(list, game = 1) {
  1178. //var m = [0x8C159, 0xB8E6E, 0x8C159, 0x5BB15][game - 1];
  1179. var m = [0x19, 0x2E, 0x19, 0x15][game - 1];
  1180. //var c = 0x6262C05D;
  1181. var c = 0x1D;
  1182. //var t = 0xFFFF;
  1183. var t = 0x3F;
  1184. var i = -getkey(list, game, "jp", 6, "offset");
  1185. for (; i < list.length; i++) {
  1186. if (i >= 4)
  1187. list[i] ^= t;
  1188. m = m * c + 1 & 0x3F;
  1189. t ^= m;
  1190. }
  1191. return list;
  1192. }
  1193. // 'normalizelist' sets 'skey' in 'dict' based on the value of 'vkey' based on
  1194. // the strings in 'list', or vice versa. 'name' is used for error messages
  1195. // and defaults to 'vkey'. If 'robust' is true then no errors are returned
  1196. // for invalid values, or known invalid strings.
  1197. function normalizelist(dict, list, vkey, skey, name = "", robust = false) {
  1198. var invalid, unknown, v, s;
  1199. if (typeof(name) == "undefined" || name == "")
  1200. name = vkey;
  1201. invalid = "Invalid " + name + " #";
  1202. unknown = "Unknown " + name + " #";
  1203. if (vkey in dict) {
  1204. v = dict[vkey];
  1205. s = v >= 0 && v < list.length ? list[v] : unknown + v;
  1206. if (!robust && s == unknown + v)
  1207. throw new Error(s);
  1208. dict[skey] = s;
  1209. } else if (skey in dict) {
  1210. s = dict[skey];
  1211. v = list.indexOf(s);
  1212. if (v < 0 && s.indexOf(unknown) == 0) {
  1213. v = parseInt(s.slice(unknown.length))
  1214. if (!robust && (v < 0 || isNaN(v)))
  1215. throw new Error(s);
  1216. }
  1217. if ((v < 0 || isNaN(v)) && s.indexOf(invalid) == 0) {
  1218. v = parseInt(s.slice(invalid.length))
  1219. if (!robust && (v < 0 || isNaN(v)))
  1220. throw new Error(s);
  1221. }
  1222. if ((v < 0 || isNaN(v)) && (s != "" || !robust))
  1223. throw new Error("Invalid " + skey + ": " + s);
  1224. dict[vkey] = v;
  1225. }
  1226. return dict;
  1227. }
  1228. // 'normalizebits' sets 'lkey' in 'dict' based on the value of 'vkey' based on
  1229. // the strings in 'list', or vice versa. 'name' is used for error messages
  1230. // and defaults to 'vkey'. If 'robust' is true then invalid values and items
  1231. // are ignored.
  1232. function normalizebits(dict, list, vkey, lkey, name = "", robust = false) {
  1233. var v, l, i, j;
  1234. if (typeof(name) == "undefined" || name == "")
  1235. name = vkey;
  1236. if (vkey in dict) {
  1237. v = dict[vkey];
  1238. if (typeof(v) != "number")
  1239. throw new Error("Invalid " + name + ": " + v);
  1240. for (i = 0, l = []; i < list.length; i++)
  1241. if (v & 1 << i) {
  1242. v &= ~(1 << i);
  1243. l.push(list[i]);
  1244. }
  1245. if (!robust && v != 0) {
  1246. for (; !(v & 1 << i); i++) {}
  1247. throw new Error("Unknown " + name + " #" + i);
  1248. }
  1249. dict[lkey] = l;
  1250. } else if (lkey in dict) {
  1251. l = dict[lkey];
  1252. for (i = 0, v = 0; i < l.length; i++) {
  1253. j = list.indexOf(l[i]);
  1254. if (!robust && i < 0)
  1255. throw new Error("Unknown " + name + ": " + l[i]);
  1256. v |= i >= 0 ? 1 << i : 0;
  1257. }
  1258. dict[vkey] = v;
  1259. }
  1260. return dict;
  1261. }
  1262. // 'normalize' normalizes the provided dictionary 'dict' using 'locale'
  1263. // strings such that if "sol/4096" or "timezone-name" exists then "sol" and
  1264. // "timezone" will be set to the equivalent values. If 'robust' is set then
  1265. // no ignorable errors are returned.
  1266. function normalize(dict, locale = "en", robust = false) {
  1267. var region, lang;
  1268. var game = whichgame(dict);
  1269. normalizelist(dict, strings[locale].regions, "region", "region-name",
  1270. strings[locale].labels.region, robust);
  1271. region = "region" in dict ? dict.region : 0;
  1272. if (!robust && (region < 1 || region > 3))
  1273. throw new Error("Unknown region");
  1274. lang = ["??", "jp", "en", "en", "??", "??", "??", "??"][region];
  1275. region = ["??", "jp", "na", "eu", "??", "??", "??", "??"][region];
  1276. dict.game = game;
  1277. dict["game-name"] = strings[locale].games[lang + game];
  1278. // Zeros
  1279. ["header-padding", "padding", "offset"].forEach(function (k) {
  1280. if (!(k in dict))
  1281. dict[k] = 0;
  1282. });
  1283. // Sol
  1284. if ("sol" in dict) {
  1285. dict["sol/4"] = dict.sol >>> 2;
  1286. dict["sol/4096"] = dict.sol >>> 12;
  1287. } else if ("sol/4" in dict) {
  1288. dict.sol = dict["sol/4"] << 2;
  1289. dict["sol/4096"] = dict["sol/4"] >>> 10;
  1290. } else if ("sol/4096" in dict) {
  1291. dict.sol = dict["sol/4096"] << 12;
  1292. dict["sol/4"] = dict["sol/4096"] << 10;
  1293. }
  1294. // Kills
  1295. if ("kills" in dict)
  1296. dict["kills/8"] = dict.kills >>> 3;
  1297. else if ("kills/8" in dict)
  1298. dict.kills = dict["kills/8"] << 3;
  1299. // Loans
  1300. if ("loan" in dict)
  1301. dict["loan/256"] = dict.loan >>> 8;
  1302. else if ("loan/256" in dict)
  1303. dict.loan = dict["loan/256"] << 8;
  1304. // Timezones
  1305. if (region in strings[locale].timezones)
  1306. normalizelist(dict, strings[locale].timezones[region], "timezone",
  1307. "timezone-name", strings[locale].labels.timezone, robust);
  1308. // Boktai 1
  1309. normalizelist(dict, strings[locale].ranks, "rank", "rank-name",
  1310. strings[locale].labels.rank, robust);
  1311. normalizelist(dict, strings[locale].titles[1], "title", "title-name",
  1312. strings[locale].labels.title, robust);
  1313. normalizelist(dict, strings[locale].difficulties[game], "difficulty",
  1314. "difficulty-name", strings[locale].labels.difficulty, robust);
  1315. // Boktai 2
  1316. normalizelist(dict, strings[locale].sides, "side", "side-name",
  1317. strings[locale].labels.side, robust);
  1318. normalizelist(dict, strings[locale].styles, "style", "style-name",
  1319. strings[locale].labels.style, robust);
  1320. // Titles
  1321. normalizebits(dict, strings[locale].titles[game], "titles", "titles-list",
  1322. strings[locale].labels.titles, robust);
  1323. // Endings
  1324. normalizebits(dict, strings[locale].endings, "endings", "ending-list",
  1325. strings[locale].labels.endings, robust);
  1326. // Favorites
  1327. normalizelist(dict, strings[locale].swords, "sword", "sword-name",
  1328. strings[locale].labels.swords, robust);
  1329. normalizelist(dict, strings[locale].guns, "gun", "gun-name",
  1330. strings[locale].labels.guns, robust);
  1331. normalizelist(dict, strings[locale].terrennials, "terrennial",
  1332. "terrennial-name", strings[locale].labels.terrennials, robust);
  1333. normalizelist(dict, strings[locale].climates, "climate",
  1334. "climate-name", strings[locale].labels.swords, robust);
  1335. return (dict);
  1336. }
  1337. // 'parseregion' returns the region number for the region speified by 'str',
  1338. // or an error if it is unmatched.
  1339. function parseregion(str) {
  1340. if (/^(japan|jp)$/i.test(str))
  1341. return 1;
  1342. if (/^((north.?)?america|na|united.?states|usa?|english)$/i.test(str))
  1343. return 2;
  1344. if (/^(europe|eu)$/i.test(str))
  1345. return 3;
  1346. throw new Error("Unknown region: " + region);
  1347. }
  1348. // 'decpass' decodes the base64 string 'pass' into a dictionary.
  1349. // 'normalize'ing it if 'normlocale' is provided'
  1350. function decpass(pass, game = 1, lang = "", normlocale = "") {
  1351. var len, sum, dict;
  1352. var l = decb64(pass, game);
  1353. if (typeof(lang) == "undefined" || lang == "")
  1354. lang = getlang(l, game);
  1355. len = {jp: [24, 24, 24, 40], en: [32, 32, 32, 40]}[lang][game-1];
  1356. if (l.length != len)
  1357. throw new Error("Invalid length: " + l.length + " != " + len);
  1358. sum = checksum(l, game);
  1359. dict = getdict(xor(l, game), game, lang);
  1360. if (dict.checksum != sum)
  1361. dict.checksum = dict.checksum + "!=" + sum
  1362. if (typeof(normlocale) == "string" && normlocale in strings)
  1363. normalize(dict, normlocale, true);
  1364. return dict;
  1365. }
  1366. // 'encpass' encodes 'dict' into a base64 password.
  1367. // 'normalize'ing it if 'normlocale' is provided'
  1368. function encpass(dict, game = "", lang = "", normlocale = "") {
  1369. var l;
  1370. if (typeof(normlocale) == "string" && normlocale in strings)
  1371. normalize(dict, normlocale, true);
  1372. if (typeof(lang) == "undefined" || lang == "")
  1373. lang = dict.region == 1 ? "jp" : "en";
  1374. if (typeof(game) == "undefined" || game == "")
  1375. game = whichgame(dict);
  1376. l = xor(putdict([], dict, game, lang), game);
  1377. putdict(l, {checksum: checksum(l, game)}, game, lang);
  1378. return encb64(l, game, lang);
  1379. }
  1380. // 'getuiinfo' returns a list of ui element types, values, and defaults
  1381. // depending on the values of 'game', 'region', and 'locale'.
  1382. // Each value is an object with a "name" string and a "type" string, and can
  1383. // have a "default" value. "list" types have a "values" array, "regionlist"
  1384. // types have a "values" object containing arrays for each short region code.
  1385. // "int" types have a "min" integer, a "max" integer, and can also have a
  1386. // "step" integer. "string" types have a "max" array or region-keyed object,
  1387. // can have a "max-bytes" array or region-keyed object, and can have a
  1388. // "restrict" boolean. "bits" types have a "values" array. Empty names or
  1389. // "break" types will will ensure the next input is at the start of a row.
  1390. var uiinfo = {};
  1391. function getuiinfo(game, region = "na", locale = "en") {
  1392. var regionnum, timezones;
  1393. if (!(locale in strings))
  1394. throw new Error("Unknown locale: " + locale);
  1395. regionnum = parseregion(region);
  1396. region = ["", "jp", "na", "eu"][regionnum]
  1397. if (!(locale in uiinfo))
  1398. uiinfo[locale] = {};
  1399. if (!(region in uiinfo[locale]))
  1400. uiinfo[locale][region] = {};
  1401. if (game in uiinfo[locale][region])
  1402. return uiinfo[locale][region][game];
  1403. timezones = {};
  1404. Object.keys(strings[locale].timezones).forEach(function (key) {
  1405. timezones[key] = strings[locale].timezones[key].slice(1);
  1406. });
  1407. switch (game) {
  1408. case 1:
  1409. return uiinfo[locale][region][game] = [
  1410. { name: "region-name", type: "list",
  1411. values: strings[locale].regions.slice(1),
  1412. default: regionnum - 1
  1413. },
  1414. { name: "offset", type: "int", min: 0, max: 3 },
  1415. { name: "sol", type: "int", min: 0, max: 0x7FC, step: 4 },
  1416. { name: "timezone-name", type: "regionlist",
  1417. values: timezones, default: { jp: 5, na: 14, eu: 21 }
  1418. },
  1419. { name: "", type: "break" },
  1420. { name: "hours", type: "int", min: 0, max: 63 },
  1421. { name: "minutes", type: "int", min: 0, max: 59, default: 1 },
  1422. { name: "difficulty-name", type: "list",
  1423. values: strings[locale].difficulties[1],
  1424. default: 2
  1425. },
  1426. { name: "dungeons", type: "int", min: 0, max: 29 },
  1427. { name: "clears", type: "int", min: 1, max: 7 },
  1428. { name: "continues", type: "int", min: 0, max: 15 },
  1429. { name: "caught", type: "int", min: 0, max: 31 },
  1430. { name: "kills", type: "int", min: 0, max: 127 },
  1431. { name: "rank-name", type: "list", values: strings[locale].ranks },
  1432. { name: "title-name", type: "list", values: strings[locale].titles[1] },
  1433. { name: "name", type: "string",
  1434. max: { jp: 5, na: 9, eu: 9 },
  1435. default: { jp: "ジャンゴ", na: "Django", eu: "Django" },
  1436. restrict: true
  1437. },
  1438. { name: "link-battles", type: "int", min: 0, max: 63 },
  1439. { name: "link-trades", type: "int", min: 0, max: 63 },
  1440. { name: "loan", type: "int", min: 0, max: 255 },
  1441. { name: "padding", type: "hidden", default: 0 }
  1442. ];
  1443. case 2:
  1444. return uiinfo[locale][region][game] = [
  1445. { name: "region-name", type: "list",
  1446. values: strings[locale].regions.slice(1),
  1447. default: regionnum - 1
  1448. },
  1449. { name: "offset", type: "int", min: 0, max: 7 },
  1450. { name: "timezone-name", type: "regionlist",
  1451. values: timezones, default: { jp: 5, na: 14, eu: 21 }
  1452. },
  1453. { name: "side-name", type: "list", values: strings[locale].sides },
  1454. { name: "style-name", type: "list", values: strings[locale].styles },
  1455. { name: "kills", type: "int", min: 0, max: 0x7F8, step: 8 },
  1456. { name: "forges", type: "int", min: 0, max: 127 },
  1457. { name: "link-battles", type: "int", min: 0, max: 63 },
  1458. { name: "link-shopping", type: "int", min: 0, max: 63 },
  1459. { name: "sol", type: "int", min: 0, max: 0x1FF000, step: 0x1000 },
  1460. { name: "loan", type: "int", min: 0, max: 0x7F00, step: 0x100 },
  1461. { name: "hours", type: "int", min: 0, max: 63 },
  1462. { name: "name", type: "string",
  1463. max: { jp: 5, na: 9, eu: 9 },
  1464. default: { jp: "ジャンゴ", na: "Django", eu: "Django" },
  1465. restrict: true
  1466. },
  1467. { name: "titles", type: "bits",
  1468. values: strings[locale].titles[2], default: 0x40
  1469. },
  1470. { name: "header-padding", type: "hidden", default: 0 },
  1471. { name: "padding", type: "hidden", default: 0 }
  1472. ];
  1473. case 3:
  1474. return uiinfo[locale][region][game] = [
  1475. { name: "region-name", type: "list",
  1476. values: strings[locale].regions.slice(1, 2),
  1477. },
  1478. { name: "offset", type: "int", min: 0, max: 7 },
  1479. { name: "timezone-name", type: "regionlist",
  1480. values: timezones, default: { jp: 5, na: 14, eu: 21 }
  1481. },
  1482. { name: "kills", type: "int", min: 0, max: 0x7F8, step: 8 },
  1483. { name: "forges", type: "int", min: 0, max: 127 },
  1484. { name: "races", type: "int", min: 0, max: 511 },
  1485. { name: "link-races", type: "int", min: 0, max: 127 },
  1486. { name: "cross-linked", type: "boolean" },
  1487. { name: "sol", type: "int", min: 0, max: 0x3FF000, step: 0x1000 },
  1488. { name: "loan", type: "int", min: 0, max: 0xFF00, step: 0x100 },
  1489. { name: "hours", type: "int", min: 0, max: 63 },
  1490. { name: "name", type: "string",
  1491. max: { jp: 5, na: 9, eu: 9 },
  1492. default: { jp: "ジャンゴ", na: "Django", eu: "Django" },
  1493. restrict: true
  1494. },
  1495. { name: "endings",
  1496. type: "bits", values: strings[locale].endings, default: 1 },
  1497. { name: "titles", type: "bits", values: strings[locale].titles[3] },
  1498. { name: "header-padding", type: "hidden", default: 0 },
  1499. { name: "padding", type: "hidden", default: 0 }
  1500. ];
  1501. case 4:
  1502. return uiinfo[locale][region][game] = [
  1503. { name: "region-name", type: "list",
  1504. values: strings[locale].regions.slice(1),
  1505. default: regionnum - 1
  1506. },
  1507. { name: "offset", type: "int", min: 0, max: 7 },
  1508. { name: "difficulty-name",
  1509. type: "list", values: strings[locale].difficulties[4] },
  1510. { name: "hours", type: "int", min: 0, max: 127 },
  1511. { name: "sol", type: "int", min: 0, max: 0x7FFF000, step: 0x1000 },
  1512. { name: "", type: "break" },
  1513. { name: "sword-name", type: "list", values: strings[locale].swords },
  1514. { name: "gun-name", type: "list", values: strings[locale].guns },
  1515. { name: "terrennial-name",
  1516. type: "list", values: strings[locale].terrennials },
  1517. { name: "climate-name",
  1518. type: "list", values: strings[locale].climates },
  1519. { name: "", type: "break" },
  1520. { name: "name-dark", type: "string",
  1521. max: { jp: 5, na: 10, eu: 10 },
  1522. "max-bytes": { jp: 10, na: 10, eu: 10 },
  1523. default: { jp: "サバタ", na: "Lucian", eu: "Lucian" }
  1524. },
  1525. { name: "name-solar", type: "string",
  1526. max: { jp: 5, na: 10, eu: 10 },
  1527. "max-bytes": { jp: 10, na: 10, eu: 10 },
  1528. default: { jp: "ジャンゴ", na: "Aaron", eu: "Aaron" }
  1529. },
  1530. { name: "titles", type: "bits", values: strings[locale].titles[4] },
  1531. { name: "header-padding", type: "hidden", default: 0 },
  1532. { name: "padding", type: "hidden", default: 0 }
  1533. ];
  1534. default:
  1535. throw new Error("Unknown game: " + game);
  1536. }
  1537. }
  1538. // 'uifindname' returns the first child element of 'root' with name 'name'.
  1539. function uifindname(root, name) {
  1540. var i, r;
  1541. if ("name" in root && root.name == name)
  1542. return root;
  1543. for (i = 0, r = undefined;
  1544. typeof(r) == "undefined" && i < root.children.length;
  1545. r = uifindname(root.children[i++], name)) {}
  1546. return r;
  1547. }
  1548. // 'uiregion' returns the region from 'uifindname(root, "region-name")' for
  1549. // 'locale' as an object with the "string", a "number", and a short "code".
  1550. function uiregion(root, locale = "en") {
  1551. var dict = {};
  1552. dict.string = uifindname(root, "region-name").value;
  1553. normalizelist(dict, strings[locale].regions, "number", "string",
  1554. strings[locale].labels.region, true);
  1555. dict.code = ["??", "jp", "na", "eu", "??", "??", "??", "??"][dict.number];
  1556. return dict;
  1557. }
  1558. // 'uivalidatestring' validates the string value of 'input' in 'root' with
  1559. // 'info' from 'getuiinfo', the 'locale', and the event 'type'. The character
  1560. // values are restricted to those in 'ctable'. 'info.max' restricts the
  1561. // character length of the value, and defaults to 'Infinity';
  1562. // 'info["max-bytes"]' restricts the byte length of the value, and defaults
  1563. // to 'info.max'. If 'info.restrict' is true then the table is restricted to
  1564. // 0x180 if the region is set to Japan or 0x0 otherwise. If the value is empty
  1565. // and the event 'type' is not "input" then it is set to the default string.
  1566. // 'info.default', 'info.max', and 'info["max-bytes"]' can all be region
  1567. // specific if they are objects with short region codes as keys.
  1568. function uivalidatestring(root, input, info, locale = "en", type = "") {
  1569. var s, l, f, carat;
  1570. var region = uiregion(root, locale).code;
  1571. var max = "max" in info ? info.max : Infinity;
  1572. var bmax = "max-bytes" in info ? info["max-bytes"] : max;
  1573. var def = "default" in info ? info.default : max;
  1574. max = typeof(max) == "object" && region in max ? max[region] : max;
  1575. bmax = typeof(bmax) == "object" && region in bmax ? bmax[region] : bmax;
  1576. def = typeof(def) == "object" && region in def ? def[region] : def;
  1577. carat = input.selectionStart;
  1578. s = input.value.slice(0, max);
  1579. f = "restrict" in info && info.restrict ? (region == "jp" ? 0x180 : 0) : "";
  1580. l = encstr(s, f);
  1581. if (l.length > bmax)
  1582. l = l.slice(0, bmax);
  1583. s = decstr(l, f);
  1584. if (type != "input" && s.length == 0)
  1585. s = def;
  1586. if (s != input.value) {
  1587. input.value = s;
  1588. input.setSelectionRange(carat, carat);
  1589. }
  1590. }
  1591. // 'uivalidateinteger' validates the string value of 'input' in 'root' with
  1592. // 'info' from 'getuiinfo', the 'locale', and the event 'type'. The value is
  1593. // restricted to numbers from 'info.min' to 'info.max', and if the value is
  1594. // not a number and 'type' is not "input" then it is set to 'info.default',
  1595. // which defaults to 'info.min'. 'info.default' can be region specific if
  1596. // it is an object with short region codes as keys.
  1597. function uivalidateinteger(root, input, info, locale = "en", type = "") {
  1598. var i;
  1599. var region = uiregion(root, locale).code;
  1600. var def = "default" in info ? info.default : info.min;
  1601. def = typeof(def) == "object" && region in def ? def[region] : def;
  1602. if (input.value == "" && type == "input")
  1603. return;
  1604. i = parseInt(input.value);
  1605. if (isNaN(i) && type == "input")
  1606. return;
  1607. if (isNaN(i))
  1608. input.value = def;
  1609. else if (i < info.min)
  1610. input.value = info.min;
  1611. else if (i > info.max)
  1612. input.value = info.max;
  1613. }
  1614. // 'updateregion' updates any region dependant entries/lists for 'game' in
  1615. // 'root' based on the value of 'uiregion' for 'locale'.
  1616. function uiupdateregion(root, game, locale) {
  1617. var dict, info, lang, cur, t, l, i, j, def;
  1618. dict = uiregion(root, locale);
  1619. lang = dict.code == "jp" ? "jp" : "en";
  1620. cur = uifindname(root, "password");
  1621. cur.value = encb64(forkeys(decb64(cur.value, game), game, lang, 6,
  1622. FORKEYS_RESTRICT | FORKEYS_MODIFY, "region", function (k, v) {
  1623. return dict.number;
  1624. }), game, lang);
  1625. info = getuiinfo(game, dict.code, locale);
  1626. for (i = 0; i < info.length; i++)
  1627. switch (info[i].type) {
  1628. case "string":
  1629. uivalidatestring(root, uifindname(root, info[i].name), info[i], locale);
  1630. break;
  1631. case "regionlist":
  1632. def = "default" in info[i] ? info[i].default : 0;
  1633. if (typeof(def) == "object" && dict.code in def)
  1634. def = def[dict.code];
  1635. cur = uifindname(root, info[i].name);
  1636. while (cur.children.length > 0)
  1637. cur.removeChild(cur.children[0]);
  1638. l = info[i].values;
  1639. l = dict.code in l ? l[dict.code] : l;
  1640. l = typeof(l) == "undefiend" || !("length" in l) ? [] : l;
  1641. for (j = 0; j < l.length; j++) {
  1642. cur.appendChild(t = document.createElement("option"));
  1643. t.selected = j == def;
  1644. t.append(l[j]);
  1645. }
  1646. break;
  1647. }
  1648. return dict.code;
  1649. }
  1650. // 'uicycleoffset' increments the offset bit in the current password in
  1651. // 'uifindname(root, "password")' for 'game' by 1.
  1652. // The language to use is derived from 'uiregion' for 'root' and 'locale'.
  1653. function uicycleoffset(root, game, locale = "en") {
  1654. var input = uifindname(root, "password");
  1655. var region = uiregion(root, locale).code;
  1656. input.value = encb64(forkeys(decb64(input.value, game), game,
  1657. region == "jp" ? "jp" : "en", 6, FORKEYS_RESTRICT | FORKEYS_MODIFY,
  1658. "offset", function (k, v, i) {
  1659. return v + 1 & (1 << i.width) - 1;
  1660. }), game, region == "jp" ? "jp" : "en");
  1661. }
  1662. // 'uiencode' encodes the input values in 'root' for 'game' with 'prefix' and
  1663. // 'locale' into the password entry.
  1664. function uiencode(root, game, locale = "en", prefix = "bokpass") {
  1665. var region = uiregion(root, locale).code;
  1666. var lang = region == "jp" ? "jp" : "en";
  1667. var info = getuiinfo(game, region, locale);
  1668. var dict, cur, i, j, t, v;
  1669. for (i = 0, dict = {}; i < info.length; i++) {
  1670. if (info[i].name == "" || info[i].type == "break")
  1671. continue;
  1672. if (info[i].type != "bits" &&
  1673. typeof(cur = uifindname(root, info[i].name)) == "undefined")
  1674. throw new Error("No input for: " + info[i].name);
  1675. switch (info[i].type) {
  1676. case "boolean":
  1677. v = cur.checked ? 1 : 0;
  1678. break;
  1679. case "bits":
  1680. for (v = 0, j = 0; typeof(cur = uifindname(
  1681. root, info[i].name + "-" + j)) != "undefined";
  1682. v |= cur.checked ? 1 << j : 0, j++) {}
  1683. break;
  1684. default:
  1685. v = cur.value;
  1686. break;
  1687. }
  1688. dict[info[i].name] = v;
  1689. }
  1690. cur = root.getElementsByClassName(prefix + "-errors")[0];
  1691. cur.innerText = "";
  1692. try {
  1693. normalize(dict, locale);
  1694. } catch (e) {
  1695. cur.innerText = "⚠ " + e;
  1696. } finally {
  1697. switch (game) {
  1698. case 1:
  1699. if (dict.minutes == 0 && dict.hours == 0)
  1700. cur.innerText = "⚠ " + strings[locale].labels.minutes + " or " +
  1701. strings[locale].labels.hours + " should be set"
  1702. else if (dict.minutes >= 60)
  1703. cur.innerText =
  1704. "⚠ " + strings[locale].labels.minutes + " should be less than 60";
  1705. else if (dict.clears == 0)
  1706. cur.innerText =
  1707. "⚠ " + strings[locale].labels.clears + " should be set";
  1708. else if (dict["name"] == "")
  1709. cur.innerText = "⚠ " + strings[locale].labels.name + " should be set";
  1710. break;
  1711. case 2:
  1712. if ((dict.titles & 1 << 6) == 0)
  1713. cur.innerText =
  1714. "⚠ " + strings[locale].titles[2][6] + " should be set";
  1715. else if (dict["name"] == "")
  1716. cur.innerText = "⚠ " + strings[locale].labels.name + " should be set";
  1717. break;
  1718. case 3:
  1719. if (dict["name"] == "")
  1720. cur.innerText = "⚠ " + strings[locale].labels.name + " should be set";
  1721. break;
  1722. case 4:
  1723. if (dict["name-dark"] == "")
  1724. cur.innerText =
  1725. "⚠ " + strings[locale].labels["name-dark"] + " should be set";
  1726. else if (dict["name-solar"] == "")
  1727. cur.innerText =
  1728. "⚠ " + strings[locale].labels["name-solar"] + " should be set";
  1729. break;
  1730. }
  1731. }
  1732. uifindname(root, "password").value = encpass(dict, game, lang, locale)
  1733. }
  1734. // 'uidecode' decodes the current password into the input values in 'root' for
  1735. // 'game' with 'prefix' and 'locale'.
  1736. function uidecode(root, game, locale = "en", prefix = "bokpass") {
  1737. var region = uiregion(root, locale).code;
  1738. var lang = region == "jp" ? "jp" : "en";
  1739. var info = getuiinfo(game, region, locale);
  1740. var cur = root.getElementsByClassName(prefix + "-errors")[0];
  1741. cur.innerText = "";
  1742. try {
  1743. var dict = decpass(uifindname(root, "password").value, game, lang,
  1744. locale);
  1745. } catch (e) {
  1746. cur.innerText = "⚠ " + e;
  1747. return;
  1748. } finally {
  1749. if (typeof(dict.checksum) == "string" && dict.checksum.indexOf("!=") >= 0)
  1750. cur.innerText = "⚠ checksum " + dict.checksum;
  1751. }
  1752. var cur, i, j, t, v;
  1753. for (i = 0; i < info.length; i++) {
  1754. if (info[i].name == "" || info[i].type == "break")
  1755. continue;
  1756. if (!(info[i].name in dict))
  1757. throw new Error("No value for: " + info[i].name);
  1758. v = dict[info[i].name];
  1759. if (info[i].type != "bits")
  1760. cur = uifindname(root, info[i].name);
  1761. switch (info[i].type) {
  1762. case "boolean":
  1763. cur.checked = v;
  1764. break;
  1765. case "bits":
  1766. for (j = 0; typeof(cur = uifindname(root, info[i].name + "-" + j)) !=
  1767. "undefined"; j++)
  1768. cur.checked = !!(v & 1 << j);
  1769. break;
  1770. case "regionlist":
  1771. case "list":
  1772. cur.value = v;
  1773. if (cur.value != v) {
  1774. cur.appendChild(t = document.createElement("option"));
  1775. t.selected = true;
  1776. t.append(v);
  1777. cur.value = v;
  1778. cur.dispatchEvent(new Event("change", {
  1779. bubbles: true, cancelable: true
  1780. }));
  1781. cur.addEventListener("change", (function (cur, t) {
  1782. return function (e) {
  1783. cur.removeChild(t);
  1784. }
  1785. })(cur, t), { once: true });
  1786. }
  1787. break;
  1788. default:
  1789. cur.value = v;
  1790. break;
  1791. }
  1792. }
  1793. }
  1794. // 'uiallowkeyboards' disables/enables all keyboard tabs for 'prefix'
  1795. // depending on the arguments given, defaulting to enabling all keyboards.
  1796. // If one is set to a keyboard not specified then the tab is changed to the
  1797. // first keyboard provided.
  1798. function uiallowkeyboards(prefix, /*, [kbd ...] */) {
  1799. var kbds = document.getElementsByClassName(prefix + "-keyboards");
  1800. var args = Array.prototype.slice.call(arguments, 1);
  1801. var kbd, i, j;
  1802. if (args.length == 1 && typeof(args[0]) == "object")
  1803. args = args[0];
  1804. if (args.length == 0)
  1805. for (i = 0; i < strings.en.labels["keyboard-tabs"].length; i++)
  1806. args.push(strings.en.labels["keyboard-tabs"][i].key);
  1807. for (i = 0; i < kbds.length; i++) {
  1808. kbd = uifindname(kbds[i], "keyboard-tabs");
  1809. if (typeof(kbd) == "undefined")
  1810. continue;
  1811. if (args.indexOf(kbd.value) < 0) {
  1812. kbd.value = args[0];
  1813. kbd.dispatchEvent(new Event("change", {
  1814. bubbles: true, cancelable: true
  1815. }));
  1816. }
  1817. for (j = 0; j < kbd.length; j++) {
  1818. kbd[j].disabled = args.indexOf(kbd[j].value) < 0;
  1819. kbd[j].selected = kbd[j].value == kbd.value;
  1820. }
  1821. }
  1822. }
  1823. // 'uicreate' initializes a grid of inputs based on the values from
  1824. // 'getuiinfo(game, region, locale)' using 'prefix' for classes and ids
  1825. // returning a "div" of class "'prefix'-game" containing them.
  1826. // The number of columns can be specified with 'options.columns', and
  1827. // 'options.nostyle' will disable the automatic grid styling entirely.
  1828. // 'options.invalid' will disable calling 'uivalidateinteger' and
  1829. // 'uivalidatestring' on "input" and "focusout" events, 'uivalidatestring'
  1830. // will still be called when the region is changed.
  1831. // If 'columns' is unset or zero it tries to guess the amount of columns to
  1832. // use for the automatic grid styling from the window dimentions.
  1833. // To automatically reflow the grid on resize use 'options.nostyle' and CSS
  1834. // with "@media" queries.
  1835. // Important classes are:
  1836. // "'prefix'-game" for the main "div"
  1837. // "'prefix'-password" for the password "div"
  1838. // "'prefix'-game-grid" for the input grid
  1839. // "'prefix'-clear" for elements that should start a row
  1840. // "'prefix'-errors" for the error "p"
  1841. // "'prefix'-string" for string inputs
  1842. // "'prefix'-int" for number inputs
  1843. // "'prefix'-list" for list inputs
  1844. // "'prefix'-boolean" for boolean inputs
  1845. // "'prefix'-bits" for bit-flag inputs
  1846. // "'prefix'-'name'" for individual inputs, where name is from 'getuiinfo'
  1847. function uicreate(region, game, prefix = "bokpass", locale = "en",
  1848. options = {}) {
  1849. var root, container, label, cur, t;
  1850. var i, j, cb, info, def, l, clear = false;
  1851. if (!("columns" in options && typeof(options.columns) == "number" &&
  1852. options.columns >= 1)) {
  1853. if ("self" in window && "matchMedia" in self)
  1854. for (options.columns = 1; options.columns < 4 && self.matchMedia(
  1855. "(min-width: " + (24 * (options.columns + 1)) + "em)").matches;
  1856. options.columns++) {}
  1857. else
  1858. options.columns = Math.min(Math.floor(
  1859. window.innerWidth / window.devicePixelRatio / 360), 4);
  1860. }
  1861. info = getuiinfo(game, region, locale);
  1862. root = document.createElement("div");
  1863. root.className = prefix + "-game";
  1864. root.appendChild(container = document.createElement("div"));
  1865. container.className = prefix + "-password";
  1866. if (!("nostyle" in options && options.nostyle)) {
  1867. container.style.width = "100%";
  1868. container.style.gridColumn = "span " + options.columns * 2;
  1869. container.style.display = "flex";
  1870. container.style.flexDirection = "row";
  1871. }
  1872. container.appendChild(cur = document.createElement("input"));
  1873. cur.type = "button";
  1874. cur.name = "encode";
  1875. cur.value = "↱";
  1876. if (!("nostyle" in options && options.nostyle)) {
  1877. cur.style.font = "large mono";
  1878. cur.style.minHeight = "1.5em";
  1879. cur.style.aspectRatio = 1;
  1880. }
  1881. cur.addEventListener("click", function () {
  1882. uiencode(root, game, locale, prefix);
  1883. });
  1884. container.appendChild(cur = document.createElement("input"));
  1885. cur.type = "text";
  1886. cur.name = "password";
  1887. cur.value = "";
  1888. if (!("nostyle" in options && options.nostyle)) {
  1889. cur.style.font = "large mono";
  1890. cur.style.flexGrow = 1;
  1891. }
  1892. cur.addEventListener("focusin", function (e) {
  1893. var r = root.parentElement.parentElement;
  1894. if (uiregion(root, locale).code == "jp")
  1895. uiallowkeyboards(prefix, "jp64");
  1896. else if (game == 4)
  1897. uiallowkeyboards(prefix, "lk64");
  1898. else
  1899. uiallowkeyboards(prefix, "en64");
  1900. });
  1901. cur.addEventListener("focusout", function (e) {
  1902. uiallowkeyboards(prefix);
  1903. });
  1904. container.appendChild(cur = document.createElement("input"));
  1905. cur.type = "button";
  1906. cur.name = "cycleoffset";
  1907. cur.value = "↻";
  1908. if (!("nostyle" in options && options.nostyle)) {
  1909. cur.style.font = "large mono";
  1910. cur.style.minHeight = "1.5em";
  1911. cur.style.aspectRatio = 1;
  1912. }
  1913. cur.addEventListener("click", function () {
  1914. uicycleoffset(root, game, locale);
  1915. });
  1916. container.appendChild(cur = document.createElement("input"));
  1917. cur.type = "button";
  1918. cur.name = "decode";
  1919. cur.value = "↴";
  1920. if (!("nostyle" in options && options.nostyle)) {
  1921. cur.style.font = "large mono";
  1922. cur.style.minHeight = "1.5em";
  1923. cur.style.aspectRatio = 1;
  1924. }
  1925. cur.addEventListener("click", function () {
  1926. uidecode(root, game, locale, prefix);
  1927. });
  1928. root.appendChild(container = document.createElement("div"));
  1929. container.className = prefix + "-game-grid";
  1930. if (!("nostyle" in options && options.nostyle)) {
  1931. container.style.display = "grid";
  1932. container.style.gridTemplateColumns =
  1933. "repeat(" + options.columns + ", auto 1fr)";
  1934. }
  1935. cb = {
  1936. string: function (info) {
  1937. return function (e) {
  1938. var r = root.parentElement.parentElement;
  1939. switch (e.type) {
  1940. case "focusin":
  1941. if (uiregion(container, locale).code == "jp") {
  1942. if (game == 4)
  1943. uiallowkeyboards(prefix, "hiri", "kata", "en", "ext");
  1944. else
  1945. uiallowkeyboards(prefix, "hiri", "kata");
  1946. } else if (game == 4)
  1947. uiallowkeyboards(prefix, "en", "ext", "hiri", "kata");
  1948. else
  1949. uiallowkeyboards(prefix, "en");
  1950. break;
  1951. default:
  1952. if ("invalid" in options && options.invalid)
  1953. uivalidatestring(container, e.target, info, locale, e.type);
  1954. break;
  1955. }
  1956. };
  1957. },
  1958. int: function (info) {
  1959. return function (e) {
  1960. if ("invalid" in options && options.invalid)
  1961. uivalidateinteger(container, e.target, info, locale, e.type);
  1962. };
  1963. }
  1964. };
  1965. for (i = 0; i < info.length; i++) {
  1966. if (info[i].name == "" || info[i].type == "break") {
  1967. clear = true;
  1968. continue;
  1969. }
  1970. if (info[i].type != "hidden") {
  1971. container.appendChild(label = document.createElement("label"));
  1972. if (clear || info[i].type == "bits") {
  1973. label.className = prefix + "-clear";
  1974. if (!("nostyle" in options && options.nostyle))
  1975. label.style.gridColumnStart = 1;
  1976. clear = false;
  1977. }
  1978. label.append(strings[locale].labels[info[i].name] + ":");
  1979. if (!("nostyle" in options && options.nostyle))
  1980. label.style.textAlign = "right";
  1981. }
  1982. def = "default" in info[i] ? info[i].default : undefined;
  1983. def = typeof(def) == "object" && region in def ? def[region] : def;
  1984. switch (info[i].type) {
  1985. case "string":
  1986. container.appendChild(cur = document.createElement("input"));
  1987. label.htmlFor = cur.id = prefix + "-" + info[i].name;
  1988. cur.className = prefix + "-string " + prefix + "-" + info[i].name;
  1989. cur.type = "text";
  1990. cur.name = info[i].name;
  1991. t = cb[info[i].type](info[i]);
  1992. cur.addEventListener("focusin", t);
  1993. cur.addEventListener("focusout", t);
  1994. cur.addEventListener("input", t);
  1995. break;
  1996. case "int":
  1997. container.appendChild(cur = document.createElement("input"));
  1998. label.htmlFor = cur.id = prefix + "-" + info[i].name;
  1999. cur.className = prefix + "-int " + prefix + "-" + info[i].name;
  2000. cur.type = "number";
  2001. cur.name = info[i].name;
  2002. cur.min = info[i].min;
  2003. cur.max = info[i].max;
  2004. if ("step" in info[i])
  2005. cur.step = info[i].step;
  2006. cur.value = typeof(def) == "undefined" ? cur.min : def;
  2007. t = cb[info[i].type](info[i]);
  2008. cur.addEventListener("input", t);
  2009. cur.addEventListener("focusout", t);
  2010. break;
  2011. case "boolean":
  2012. container.appendChild(cur = document.createElement("input"));
  2013. label.htmlFor = cur.id = prefix + "-" + info[i].name;
  2014. cur.className = prefix + "-boolean " + prefix + "-" + info[i].name;
  2015. cur.type = "checkbox";
  2016. cur.name = info[i].name;
  2017. cur.defaultChecked = typeof(def) == "undefined" ? false : def;
  2018. if (!("nostyle" in options && options.nostyle))
  2019. cur.style.justifySelf = "start";
  2020. break;
  2021. case "regionlist":
  2022. case "list":
  2023. container.appendChild(cur = document.createElement("select"));
  2024. label.htmlFor = cur.id = prefix + "-" + info[i].name;
  2025. cur.className = prefix + "-list " + prefix + "-" + info[i].name;
  2026. cur.name = info[i].name;
  2027. def = typeof(def) == "undefined" ? 0 : def;
  2028. l = region in info[i].values ? info[i].values[region] : info[i].values;
  2029. for (j = 0; j < l.length; j++) {
  2030. cur.appendChild(t = document.createElement("option"));
  2031. t.selected = j == def;
  2032. t.append(l[j]);
  2033. }
  2034. break;
  2035. case "bits":
  2036. def = typeof(def) == "undefined" ? 0 : def;
  2037. container.appendChild(t = document.createElement("div"));
  2038. t.id = prefix + "-" + info[i].name;
  2039. t.className = prefix + "-bits " + prefix + "-" + info[i].name;
  2040. if (!("nostyle" in options && options.nostyle)) {
  2041. t.style.gridColumnEnd = "span " + (options.columns * 2 - 1);
  2042. t.style.display = "grid";
  2043. t.style.gridTemplateColumns =
  2044. "repeat(" + Math.round(options.columns * 5 / 3) + ", 1fr auto)";
  2045. }
  2046. for (j = 0, l = info[i].values; j < l.length; j++) {
  2047. t.appendChild(label = document.createElement("label"));
  2048. label.append(l[j] + ":");
  2049. if (!("nostyle" in options && options.nostyle))
  2050. label.style.textAlign = "right";
  2051. t.appendChild(cur = document.createElement("input"));
  2052. cur.type = "checkbox";
  2053. cur.name = info[i].name + "-" + j;
  2054. label.htmlFor = cur.id = prefix + "-" + cur.name;
  2055. cur.defaultChecked = !!(def & 1 << j);
  2056. if (!("nostyle" in options && options.nostyle))
  2057. cur.style.justifySelf = "start";
  2058. }
  2059. if (!("nostyle" in options && options.nostyle)) {
  2060. // Pad the first row to help with alignment.
  2061. for (; j < Math.round(options.columns * 5 / 3); j++) {
  2062. t.appendChild(cur = document.createElement("br"));
  2063. t.appendChild(cur = document.createElement("input"));
  2064. cur.type = "checkbox";
  2065. cur.disabled = true;
  2066. cur.style.visibility = "hidden";
  2067. }
  2068. }
  2069. break;
  2070. case "hidden":
  2071. container.appendChild(cur = document.createElement("input"));
  2072. cur.className = prefix + "-hidden " + prefix + "-" + info[i].name;
  2073. cur.type = "hidden";
  2074. cur.name = info[i].name;
  2075. cur.value = typeof(def) == "undefined" ? 0 : def;
  2076. break;
  2077. }
  2078. }
  2079. root.appendChild(cur = document.createElement("p"));
  2080. cur.className = prefix + "-errors";
  2081. if (!("nostyle" in options && options.nostyle))
  2082. cur.style.textAlign = "center";
  2083. uifindname(root, "region-name").addEventListener("change", function (e) {
  2084. window.location.hash = prefix + "-" +
  2085. uiupdateregion(root, game, locale, prefix) + game;
  2086. });
  2087. uiupdateregion(root, game, locale, prefix);
  2088. return root;
  2089. }
  2090. // 'uitabs' creates lazily loaded "tabs" for each game , defaulting to 'game',
  2091. // with 'uicreate' with the same arguments given and returns a "div" of class
  2092. // "'prefix'-games" containing both the "tabs" and the "div" from 'uicreate'.
  2093. // If the url has a fragment of "'prefix'-'region''game'" then it overrides
  2094. // the default values with those and scrolls to the resulting "div" on "load".
  2095. function uitabs(region, game = 1, prefix = "bokpass", locale = "en",
  2096. options = {}) {
  2097. var root, cur, t, i;
  2098. var hash = document.location.hash.replace(/^#/, "");
  2099. if (hash.indexOf(prefix) == 0) {
  2100. hash = hash.slice(prefix.length).replace(/^[-_]+/, "");
  2101. try {
  2102. region = ["jp", "na", "eu"][parseregion(hash.replace(/\d+$/, "")) - 1];
  2103. } catch (e) {}
  2104. hash = hash.replace(/.*(\d+)$/, "$1");
  2105. if (hash != "" && !isNaN(hash = parseInt(hash)) && hash >= 1 && hash <= 4)
  2106. game = hash;
  2107. hash = true;
  2108. } else
  2109. hash = false;
  2110. root = document.createElement("div");
  2111. root.className = prefix + "-games";
  2112. root.appendChild(cur = document.createElement("select"));
  2113. cur.name = "game-tabs";
  2114. cur.className = prefix + "-tabs";
  2115. cur.style.display = "block";
  2116. cur.style.width = "100%";
  2117. for (i = 0; i < strings[locale].labels["game-tabs"].length; i++) {
  2118. cur.appendChild(t = document.createElement("option"));
  2119. t.value = strings[locale].labels["game-tabs"][i].game;
  2120. t.append(strings[locale].labels["game-tabs"][i].name);
  2121. t.selected = t.value == game;
  2122. }
  2123. cur.addEventListener("change", (function () {
  2124. var tabs = {};
  2125. var cur = undefined;
  2126. return function (e) {
  2127. uiallowkeyboards(prefix);
  2128. if (!(e.target.value in tabs))
  2129. tabs[e.target.value] =
  2130. uicreate(region, parseInt(e.target.value), prefix, locale,
  2131. options);
  2132. if (typeof(cur) == "undefined")
  2133. root.appendChild(tabs[e.target.value]);
  2134. else
  2135. root.replaceChild(tabs[e.target.value], cur);
  2136. cur = tabs[e.target.value];
  2137. };
  2138. })());
  2139. cur.dispatchEvent(new Event("change", {bubble: true, cancelable: true}));
  2140. if (hash)
  2141. window.addEventListener("load", function () {root.scrollIntoView()});
  2142. cur.addEventListener("change", function (e) {
  2143. window.location.hash = prefix + "-" + uiregion(root, locale).code +
  2144. e.target.value;
  2145. });
  2146. return root;
  2147. }
  2148. // 'keyboardpress' modifies the currently selected text input as if 'c' were
  2149. // pressed. If 'c' is in 'keyboards.alias' it is changed before processing.
  2150. // Supported special values are "ArrowUp", "ArrowDown", "ArrowLeft",
  2151. // "ArrowRight", "Backspace", and "Delete".
  2152. function keyboardpress(c) {
  2153. var t = document.activeElement, s, e;
  2154. c = c in keyboards.alias ? keyboards.alias[c] : c;
  2155. if (!("selectionStart" in t))
  2156. return;
  2157. s = t.selectionStart;
  2158. e = t.selectionEnd;
  2159. switch (c) {
  2160. case "ArrowUp":
  2161. s = 0;
  2162. break;
  2163. case "ArrowDown":
  2164. s = t.value.length;
  2165. break;
  2166. case "ArrowLeft":
  2167. s--;
  2168. break;
  2169. case "ArrowRight":
  2170. s = e + 1;
  2171. break;
  2172. case "Backspace":
  2173. if (s != e)
  2174. t.value = t.value.slice(0, s) + t.value.slice(e);
  2175. else if (t.selectionStart > 0)
  2176. t.value = t.value.slice(0, --s) + t.value.slice(e);
  2177. break;
  2178. case "Delete":
  2179. if (t.selectionStart != t.selectionEnd)
  2180. t.value = t.value.slice(0, s) + t.value.slice(e);
  2181. else if (t.selectionStart > 0)
  2182. t.value = t.value.slice(0, s++) + t.value.slice(e + 1);
  2183. break;
  2184. default:
  2185. t.value = t.value.slice(0, s++) + c + t.value.slice(e);
  2186. break;
  2187. }
  2188. t.setSelectionRange(s, s);
  2189. t.dispatchEvent(new Event("input", {bubble: true, cancelable: true}));
  2190. }
  2191. // 'keyboardcreate' initializes a grid of buttons from 'keys' that call
  2192. // 'keyboardpress' with their value.
  2193. // 'keys' is an array of strings, or the name of a keyboard in 'keyboards',
  2194. // If a key is a string in 'keyboards.expand' it is expanded to fill the grid
  2195. // columns.
  2196. function keyboardcreate(keys, prefix = "bokpass") {
  2197. var root, cur;
  2198. var max, diff, exp = [];
  2199. var col, i, j, k, span;
  2200. var cb = function (e) {
  2201. keyboardpress(e.target.value)
  2202. e.preventDefault();
  2203. };
  2204. if (typeof(keys) == "string" && keys in keyboards)
  2205. keys = keyboards[keys];
  2206. root = document.createElement("div");
  2207. root.className = prefix + "-keyboard";
  2208. root.style.display = "grid";
  2209. root.style.marginLeft = root.style.marginRight = "auto";
  2210. root.style.width = "fit-content";
  2211. for (i = 0, max = 1; i < keys.length; i++) {
  2212. max = keys[i].length > max ? keys[i].length : max;
  2213. for (j = 0, exp[i] = 0; j < keys[i].length; j++)
  2214. if (keys[i][j] in keyboards.expand && keyboards.expand[keys[i][j]])
  2215. exp[i]++;
  2216. }
  2217. root.style.gridTemplateColumns = "repeat(" + max + ", 1.75em)";
  2218. root.style.gridTemplateRows = "repeat(" + keys.length + ", 1.75em)";
  2219. for (i = 0; i < keys.length; i++) {
  2220. diff = max - keys[i].length;
  2221. for (j = 0, col = 1, k = 0; j < keys[i].length; j++, col += span) {
  2222. if (keys[i][j] == "") {
  2223. span = 1;
  2224. continue;
  2225. }
  2226. root.appendChild(cur = document.createElement("input"));
  2227. cur.type = "button";
  2228. cur.value = keys[i][j];
  2229. cur.style.font = "large mono";
  2230. cur.style.gridRow = i + 1;
  2231. span = 1;
  2232. if (keys[i][j] in keyboards.expand && keyboards.expand[keys[i][j]])
  2233. span += (diff / exp[i] | 0) + (++k == exp[i] ? diff % exp[i] : 0);
  2234. if (span == 1)
  2235. cur.style.gridColumn = col;
  2236. else
  2237. cur.style.gridColumn = col + " / span " + span;
  2238. cur.addEventListener("mousedown", cb);
  2239. }
  2240. }
  2241. return root;
  2242. }
  2243. // 'keyboardtabs' creates lazily loaded "tabs" for each keyboard with 'prefix'
  2244. // and 'locale' and returns a "div" of class "'prefix'-keyboards" containing
  2245. // both the "tabs" and the "div" from 'keyboardcreate'.
  2246. function keyboardtabs(prefix = "bokpass", locale = "en") {
  2247. var root, cur, t;
  2248. var i;
  2249. root = document.createElement("div");
  2250. root.className = prefix + "-keyboards";
  2251. root.appendChild(cur = document.createElement("select"));
  2252. cur.name = "keyboard-tabs";
  2253. cur.className = prefix + "-tabs";
  2254. cur.style.display = "block";
  2255. cur.style.width = "100%";
  2256. for (i = 0; i < strings[locale].labels["keyboard-tabs"].length; i++) {
  2257. cur.appendChild(t = document.createElement("option"));
  2258. t.value = strings[locale].labels["keyboard-tabs"][i].key;
  2259. t.append(strings[locale].labels["keyboard-tabs"][i].name);
  2260. t.selected = i == 0;
  2261. }
  2262. cur.addEventListener("change", (function () {
  2263. var tabs = {};
  2264. var cur = undefined;
  2265. return function (e) {
  2266. if (!(e.target.value in tabs))
  2267. tabs[e.target.value] = keyboardcreate(e.target.value, prefix);
  2268. if (typeof(cur) == "undefined")
  2269. root.appendChild(tabs[e.target.value]);
  2270. else
  2271. root.replaceChild(tabs[e.target.value], cur);
  2272. cur = tabs[e.target.value];
  2273. };
  2274. })());
  2275. cur.dispatchEvent(new Event("change"));
  2276. return root;
  2277. }
  2278. // 'uicreateall' returns a "div" with class 'prefix' containing the values
  2279. // from calling 'uitabs' and 'keyboardtabs'.
  2280. function uicreateall(region, game = 1, prefix = "bokpass", locale = "en",
  2281. options = {}) {
  2282. var root = document.createElement("div");
  2283. root.class = prefix;
  2284. root.id = prefix;
  2285. root.appendChild(uitabs(region, game, prefix, locale, options));
  2286. root.appendChild(keyboardtabs(prefix, locale));
  2287. return root;
  2288. }
  2289. return {
  2290. dec: { b64: decb64, str: decstr, pass: decpass },
  2291. enc: { b64: encb64, str: encstr, pass: encpass },
  2292. get: {
  2293. int: getint, str: getstr, keys: getkeys, key: getkey, dict: getdict
  2294. },
  2295. put: { int: putint, str: putstr, dict: putdict },
  2296. checksum: checksum,
  2297. xor: xor,
  2298. tabs: { ui: uitabs, kbd: keyboardtabs },
  2299. create: { ui: uicreate, kbd: keyboardcreate, all: uicreateall }
  2300. };
  2301. })();