1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350 |
- // Permission to use, copy, modify, and/or distribute this software for
- // any purpose with or without fee is hereby granted.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
- // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- // OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
- // FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
- // DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
- // AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- // OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- // Ported from the Tcl/Tk version. See the original for more documentation.
- var bokpass = (function () {
- "use strict";
- // JSON version of bok::strings
- var strings = {
- "en": {
- "games": {
- "jp1": "Bokura no Taiyō",
- "jp2": "Zoku Bokura no Taiyō: Taiyō Shōnen Jango",
- "jp3": "Shin Bokura no Taiyō: Gyakushū no Sabata",
- "jp4": "Bokura no Taiyō: Django & Sabata",
- "en1": "Boktai: The Sun Is in Your Hand",
- "en2": "Boktai 2: Solar Boy Django",
- "en3": "Boktai 3: Sabata's Counterattack",
- "en4": "Lunar Knights"
- },
- "labels": {
- "game-tabs": [
- { game: 1, name: "Boktai" },
- { game: 2, name: "Boktai 2 / Zoktai" },
- { game: 3, name: "Boktai 3 / Shinbok" },
- { game: 4, name: "Lunar Knights / Boktai DS" }
- ],
- "keyboard-tabs": [
- { key: "en64", name: "Base64" },
- { key: "lk64", name: "Base64 LK" },
- { key: "jp64", name: "Base64 JP" },
- { key: "en", name: "English" },
- { key: "ext", name: "Latin Ext." },
- { key: "hiri", name: "Hirigana" },
- { key: "kata", name: "Katakana" }
- ],
- "padding": "Padding", "header-padding": "Padding",
- "region": "Region", "region-name": "Region",
- "checksum": "Checksum", "offset": "Offset",
- "sol": "Sol", "sol/4": "Sol",
- "sol/4096": "Sol",
- "loan": "Loan", "loan/256": "Loan",
- "timezone": "Timezone", "timezone-name": "Timezone",
- "hours": "Hours", "minutes": "Minutes",
- "difficulty": "Difficulty", "difficulty-name": "Difficulty",
- "dungeons": "Dungeons", "clears": "Clears",
- "continues": "Continues", "caught": "Caught",
- "kills": "Kills", "kills/8": "Kills",
- "rank": "Rank", "rank-name": "Rank",
- "forges": "Forges", "races": "Races",
- "link-battles": "Link-Battles", "link-trades": "Link-Trades",
- "link-shopping": "Link-Shopping", "link-races": "Link-Races",
- "cross-linked": "Cross-Linked",
- "title": "Title", "title-name": "Title",
- "titles": "Titles", "title-list": "Titles",
- "side": "Side", "side-name": "Side",
- "style": "Style", "style-name": "Style",
- "sword": "Sword", "sword-name": "Sword",
- "gun": "Gun", "gun-name": "Gun",
- "terrennial": "Terrennial", "terrennial-name": "Terrennial",
- "climate": "Climate", "climate-name": "Climate",
- "endings": "Survivors", "ending-list": "Survivors",
- "name-dark": "Name(dark)", "name-solar": "Name(sol)",
- "name": "Name"
- },
- "regions": [
- "Unknown Region #0", "Japan", "North America", "Europe"
- ],
- "timezones": {
- "jp": [
- "Unknown Timezone #0",
- "Ibaraki", "Tochigi", "Gunma", "Saitama",
- "Chiba", "Tokyo", "Kanagawa", "Ogasawara",
- "Niigata", "Toyama", "Ishikawa", "Fukui",
- "Yamanashi", "Nagano", "Gifu", "Shizuoka",
- "Aichi", "Mie", "Shiga", "Kyoto",
- "Osaka", "Hyougo", "Nara", "Wakayama",
- "Tottori", "Shimane", "Okayama", "Hiroshima",
- "Yamaguchi", "Tokushima", "Kagawa", "Ehime",
- "Kouchi", "Fukuoka", "Saga", "Nagasaki",
- "Kumamoto", "Ooita", "Miyazaki", "Kagoshima",
- "Okinawa", "Ishigakijima", "Sapporo", "Hakodate",
- "Asahikawa", "Kushiro", "Obihiro", "Aomori",
- "Iwate", "Miyagi", "Akita", "Yamagata",
- "Fukushima"
- ],
- "na": [
- "Unknown Timezone #0",
- "St. John's", "Labrador City", "Halifax", "Quebec",
- "Montreal", "Ottawa", "Toronto", "Timmins",
- "Boston", "Albany", "Syracuse", "New York",
- "Philadelphia", "Pittsburgh", "Washington D.C.", "Norfolk",
- "Raleigh", "Charlotte", "Atlanta", "Jacksonville",
- "Tampa", "Miami", "Detroit", "Cleveland",
- "Columbus", "Lexington", "Thunder Bay", "Winnipeg",
- "Regina", "Thompson", "Indianapolis", "Chicago",
- "Milwaukee", "Minneapolis", "Bismark", "St. Louis",
- "Nashville", "Memphis", "Montgomery", "Jackson",
- "New Orleans", "Des Moines", "Lincoln", "Kansas City",
- "Topeka", "Springfield", "Little Rock", "Oklahoma City",
- "Dallas", "Houston", "Edmonton", "Calgary",
- "Yellowknife", "Denver", "Albuquerque", "Phoenix",
- "Boise", "Salt Lake City", "Vancouver", "Whitehorse",
- "Spokane", "Seattle", "Salem", "Reno",
- "Las Vegas", "Los Angeles", "San Diego", "San Francisco",
- "Anchorage", "Fairbanks", "Ketchikan", "Honolulu"
- ],
- "eu": [
- "Unknown Timezone #0",
- "Reykjavik", "Dublin", "Cork", "London",
- "Cardiff", "Edinburgh", "Belfast", "Liverpool",
- "Lisbon", "Porto", "Valletta", "Madrid",
- "Barcelona", "Valencia", "La Coruna", "Seville",
- "Paris", "Brest", "Lyons", "Bordeaux",
- "Marseilles", "Brussels", "Bastogne", "Amsterdam",
- "Rotterdam", "Luxembourg", "Berlin", "Hamburg",
- "Essen", "Frankfurt", "Munich", "Bern",
- "Geneve", "Vaduz", "Wien", "Innsbruck",
- "Rome", "Genova", "Venezia", "Palermo",
- "Sassari", "Oslo", "Bergen", "Trondheim",
- "Copenhagen", "Odense", "Stockholm", "Gothenburg",
- "Helsinki", "Turku", "Mikkeli", "Warszawa",
- "Gdansk", "Poznan", "Wroclaw", "Krakow",
- "Praha", "Bratislava", "Kosice", "Budapest",
- "Bucuresti", "Ljubljana", "Zagreb", "Sarajevo",
- "Beograd", "Skopje", "Tirane", "Sofiya",
- "Athinai", "Thessaloniki", "Iraklion", "Ankara",
- "Istanbul", "Izmir", "Konya", "Adana",
- "Jerusalem", "Pretoria", "Cape Town", "Durban",
- "Wellington", "Auckland", "Dunedin", "Sydney",
- "Melbourne", "Adelaide", "Perth", "Brisbane"
- ]
- },
- "ranks": [
- "S", "A+", "A", "A-", "B+", "B", "B-", "C+",
- "C", "C-", "D+", "D", "D-", "F+", "F", "F-"
- ],
- "difficulties": {
- "1": ["Easy", "Normal 1", "Normal 2", "Hard"],
- "2": [],
- "3": [],
- "4": ["Normal", "Hard", "Nightmare"]
- },
- "titles": {
- "1": [
- "Trigger of Sol", "Gun Master", "Gladiator", "Bishop",
- "Queen", "Berserker", "Death", "Solar Boy",
- "Dark Boy", "Solar Menchant", "Running Boy", "King",
- "Rook", "Knight", "Pawn"
- ],
- "2": [
- "Sword Master", "Spear Master", "Hammer Master", "Fist Master",
- "Gun Master", "Adept", "Day Walker", "Adventurer",
- "Agent", "Collector", "Dark Hunter", "Grand Master"
- ],
- "3": [
- "Adept", "Gladiator", "SP Agent", "Champion",
- "Dark Hunter", "Alchemist", "Collector", "Doll Master",
- "Storyteller", "Grandmaster"
- ],
- "4": [
- "Dark Knight", "Sol Gunner", "Sword Master", "Gun Master",
- "Guardian", "Treasure Hunter", "Collector", "Huntmaster",
- "Shooting Star", "Gladiator", "Special Agent", "Wanderer",
- "Adventurer", "Grand Master"
- ]
- },
- "sides": ["Red", "Black", "Grey"],
- "styles": ["Sword", "Spear", "Hammer", "Gun", "Fists", "No Style"],
- "endings": ["Otenko", "Everybody", "Nobody", "Sabata"],
- "swords": ["Vanargand", "Jormungandr", "Hel"],
- "guns": ["Knight", "Dragoon", "Bomber", "Witch", "Ninja"],
- "terrennials": [
- "Toasty", "Nero", "Ursula", "Ezra", "Alexander", "Tove", "War Rock"
- ],
- "climates": [
- "Balmy Sub-Tropical", "Arid Desert", "Tropical Rainforest",
- "Humind-Continental", "Frigid Arctic"
- ]
- }
- };
- // JSON version of bok::ctable
- var ctable = {
- "0x0": [
- "�", "�", "�", "�", "�", "�", "�", "�",
- "�", "�", "�", "�", "�", "�", "�", "�",
- "�", "�", "�", "�", "�", "�", "�", "�",
- "�", "�", "�", "�", "�", "�", "�", "�",
- " ", "!", "\"", "#", "÷", "%", "&", "'",
- "(", ")", "*", "+", ",", "-", ".", "/",
- "0", "1", "2", "3", "4", "5", "6", "7",
- "8", "9", ":", ";", "<", "=", ">", "?",
- "@", "A", "B", "C", "D", "E", "F", "G",
- "H", "I", "J", "K", "L", "M", "N", "O",
- "P", "Q", "R", "S", "T", "U", "V", "W",
- "X", "Y", "Z", "[", "×", "]", "^", "_",
- "`", "a", "b", "c", "d", "e", "f", "g",
- "h", "i", "j", "k", "l", "m", "n", "o",
- "p", "q", "r", "s", "t", "u", "v", "w",
- "x", "y", "z", "{", "|", "}", "¯", "⋅"
- ],
- "0x1F": [
- "�", "Ä", "�", "Ç", "É", "Ñ", "Ö", "Ü",
- "á", "à", "â", "ä", "�", "å", "ç", "é",
- "è", "ê", "ë", "í", "ì", "î", "ï", "ñ",
- "ó", "ò", "ô", "ö", "�", "ú", "ù", "û",
- "ü", "�", "°", "�", "�", "�", "�", "�",
- "ß", "�", "�", "�", "�", "�", "�", "�",
- "�", "�", "�", "�", "�", "�", "�", "�",
- "�", "�", "�", "�", "�", "�", "�", "�",
- "�", "¿", "¡", "�", "�", "�", "�", "�",
- "«", "»", "�", "�", "À", "�", "�", "Œ",
- "œ", "�", "�", "�", "�", "�", "�", "�",
- "ý", "ÿ", "Ý", "�", "�", "�", "�", "�",
- "�", "�", "�", "�", "�", "�", "Â", "Ê",
- "Á", "Ë", "È", "Í", "Î", "Ï", "Ì", "Ó",
- "Ô", "�", "Ò", "Ú", "Û", "Ù", "�", "�",
- "�", "�", "�", "�", "�", "�", "�", "�"
- ],
- "0x80": [
- "�", "あ", "い", "う", "え", "お", "か", "き",
- "く", "け", "こ", "さ", "し", "す", "せ", "そ",
- "よ", "た", "ち", "つ", "て", "と", "な", "に",
- "ぬ", "ね", "の", "は", "ひ", "ふ", "へ", "ほ",
- "下", "ま", "み", "む", "め", "も", "や", "ゆ",
- "よ", "ら", "り", "る", "れ", "ろ", "わ", "を",
- "左", "ん", "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "っ",
- "ゃ", "ゅ", "ょ", "が", "ぎ", "ぐ", "げ", "ご",
- "右", "ざ", "じ", "ず", "ぜ", "ぞ", "だ", "ぢ",
- "づ", "で", "ど", "ば", "び", "ぶ", "べ", "ぼ",
- "東", "ぱ", "ぴ", "ぷ", "ぺ", "ぽ", "。", "、",
- "~", "ー", "…", "�", "�", "�", "�", "�",
- "西", "ア", "イ", "ウ", "エ", "オ", "カ", "キ",
- "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ",
- "南", "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ",
- "ヌ", "ネ", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ",
- "北", "マ", "ミ", "ム", "メ", "モ", "ラ", "リ",
- "ル", "レ", "ロ", "ヤ", "ユ", "ヨ", "ワ", "ヲ",
- "大", "ン", "ァ", "ィ", "ゥ", "ェ", "ォ", "ッ",
- "ャ", "ュ", "ョ", "ガ", "ギ", "グ", "ゲ", "ゴ",
- "中", "ザ", "ジ", "ズ", "ゼ", "ゾ", "ダ", "ヂ",
- "ヅ", "デ", "ド", "バ", "ビ", "ブ", "ベ", "ボ",
- "小", "パ", "ピ", "プ", "ペ", "ポ", "・", ":",
- ";", "「", "」", "+", "×", "℃", "℉", "�",
- "�", "↑", "↓", "→", "←", "★", "♥", "♪",
- "ヴ", "Ⅰ", "Ⅱ", "Ⅲ", "�", "�", "�", "�",
- "風", "白", "黒", "赤", "青", "黄", "緑", "金",
- "銀", "紫", "�", "火", "炎", "災", "水", "氷",
- "永", "太", "陽", "年", "月", "日", "時", "分",
- "秒", "春", "夏", "秋", "冬", "之", "ヶ", "々",
- "=", "丈", "片", "己", "凶", "歯", "�", "�",
- "�", "�", "�", "�", "�", "�", "�", "�"
- ],
- "0x180": [
- "�", "あ", "い", "う", "え", "お", "か", "き",
- "く", "け", "こ", "さ", "し", "す", "せ", "そ",
- "よ", "た", "ち", "つ", "て", "と", "な", "に",
- "ぬ", "ね", "の", "は", "ひ", "ふ", "へ", "ほ",
- "下", "ま", "み", "む", "め", "も", "や", "ゆ",
- "よ", "ら", "り", "る", "れ", "ろ", "わ", "を",
- "左", "ん", "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "っ",
- "ゃ", "ゅ", "ょ", "が", "ぎ", "ぐ", "げ", "ご",
- "右", "ざ", "じ", "ず", "ぜ", "ぞ", "だ", "ぢ",
- "づ", "で", "ど", "ば", "び", "ぶ", "べ", "ぼ",
- "東", "ぱ", "ぴ", "ぷ", "ぺ", "ぽ", "。", "、",
- "~", "ー", "…", "�", "�", "�", "�", "�",
- "西", "ア", "イ", "ウ", "エ", "オ", "カ", "キ",
- "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ",
- "南", "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ",
- "ヌ", "ネ", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ",
- "北", "マ", "ミ", "ム", "メ", "モ", "ラ", "リ",
- "ル", "レ", "ロ", "ヤ", "ユ", "ヨ", "ワ", "ヲ",
- "大", "ン", "ァ", "ィ", "ゥ", "ェ", "ォ", "ッ",
- "ャ", "ュ", "ョ", "ガ", "ギ", "グ", "ゲ", "ゴ",
- "中", "ザ", "ジ", "ズ", "ゼ", "ゾ", "ダ", "ヂ",
- "ヅ", "デ", "ド", "バ", "ビ", "ブ", "ベ", "ボ",
- "小", "パ", "ピ", "プ", "ペ", "ポ", "・", ":",
- ";", "「", "」", "+", "×", "℃", "℉", "�",
- "�", "↑", "↓", "→", "←", "★", "♥", "♪",
- "ヴ", "Ⅰ", "Ⅱ", "Ⅲ", "�", "�", "�", "�",
- "風", "白", "黒", "赤", "青", "黄", "緑", "金",
- "銀", "紫", "�", "火", "炎", "災", "水", "氷",
- "永", "太", "陽", "年", "月", "日", "時", "分",
- "秒", "春", "夏", "秋", " ", "之", "ヶ", "々",
- "=", "丈", "片", "己", "凶", "歯", "�", "�",
- "�", "�", "�", "�", "�", "�", "�", "�"
- ],
- "0x81": [
- "�", "地", "均", "坂", "塔", "境", "塊", "填",
- "場", "増", "堀", "堤", "壊", "塚", "域", "城",
- "�", "現", "理", "球", "環", "�", "�", "�",
- "�", "切", "�", "功", "攻", "項", "崎", "靖",
- "端", "化", "代", "付", "何", "仕", "任", "仗",
- "仲", "伯", "件", "作", "伝", "休", "体", "仮",
- "住", "佐", "他", "使", "便", "信", "倍", "借",
- "価", "値", "低", "侮", "個", "保", "係", "供",
- "侵", "依", "偉", "備", "偽", "似", "俊", "傷",
- "像", "優", "候", "修", "例", "側", "倒", "働",
- "健", "併", "佳", "倫", "停", "傲", "儀", "�",
- "�", "�", "�", "�", "�", "�", "�", "�",
- "衝", "行", "往", "彼", "役", "徐", "復", "後",
- "待", "得", "徳", "術", "街", "御", "徴", "徹",
- "衛", "打", "払", "押", "択", "技", "抜", "投",
- "抗", "持", "担", "指", "捨", "排", "抵", "挑",
- "推", "提", "携", "授", "接", "掘", "操", "揮",
- "捕", "探", "換", "振", "掛", "援", "拠", "損",
- "拡", "把", "握", "掃", "撤", "�", "�", "�",
- "�", "�", "�", "�", "�", "�", "�", "�",
- "状", "牧", "物", "特", "犠", "牲", "独", "狙",
- "猛", "狂", "狩", "猫", "狐", "狼", "獲", "猟",
- "獄", "性", "快", "悦", "怪", "悟", "怖", "情",
- "慎", "慢", "燐", "憶", "�", "�", "�", "�",
- "�", "粒", "料", "粗", "精", "�", "�", "�",
- "�", "灯", "灼", "焼", "煙", "燥", "燃", "爆",
- "燼", "札", "材", "林", "杯", "村", "析", "相",
- "枚", "板", "松", "根", "格", "槍", "横", "株",
- "様", "棺", "桶", "桿", "植", "橋", "構", "機",
- "械", "�", "欄", "樹", "樋", "椛", "椿", "模",
- "根", "標", "�", "�", "�", "和", "利", "科",
- "称", "程", "種", "移", "秘", "積", "稼", "稲"
- ],
- "0x82": [
- "�", "礼", "祈", "社", "祝", "神", "視", "福",
- "�", "�", "�", "�", "初", "裕", "複", "被",
- "捕", "紅", "紀", "約", "紋", "紙", "細", "組",
- "統", "終", "純", "練", "級", "緒", "経", "絵",
- "給", "絃", "納", "紹", "絡", "結", "続", "継",
- "絶", "編", "縁", "博", "織", "総", "縦", "綾",
- "締", "績", "網", "縮", "絆", "幻", "郷", "�",
- "�", "次", "冷", "凍", "�", "�", "議", "論",
- "訳", "計", "討", "記", "許", "訓", "詳", "説",
- "話", "証", "読", "設", "語", "談", "試", "調",
- "誤", "課", "誠", "誘", "護", "認", "謙", "誕",
- "識", "謝", "泡", "汚", "浪", "液", "涙", "汰",
- "沙", "江", "況", "沢", "泊", "河", "注", "洋",
- "泣", "治", "活", "浴", "浩", "池", "波", "洗",
- "流", "法", "決", "油", "消", "温", "浮", "海",
- "�", "�", "�", "�", "�", "�", "�", "�"
- ]
- };
- // JSON version of bok::b64table
- var b64table = {
- "jp": [
- "あ", "い", "う", "え", "お", "か", "き", "く",
- "け", "こ", "さ", "し", "す", "せ", "そ", "た",
- "ち", "つ", "て", "と", "な", "に", "ぬ", "ね",
- "の", "は", "ひ", "ふ", "へ", "ほ", "ま", "み",
- "む", "め", "も", "や", "ゆ", "よ", "ら", "り",
- "る", "れ", "ろ", "わ", "を", "が", "ぎ", "ぐ",
- "げ", "ご", "ざ", "じ", "ず", "ぜ", "ぞ", "だ",
- "ぢ", "づ", "で", "ど", "ば", "び", "ぶ", "べ", "ぼ"
- ],
- "en": [
- "B", "C", "D", "F", "G", "H", "J", "K",
- "L", "M", "N", "P", "Q", "R", "S", "T",
- "V", "W", "X", "Y", "Z",
- "b", "c", "d", "f", "g", "h", "j", "k",
- "l", "m", "n", "p", "q", "r", "s", "t",
- "v", "w", "x", "y", "z",
- "0", "1", "2", "3", "4", "5", "6", "7",
- "8", "9", "?", "!", "@", "#", "=", "^",
- ">", "/", "-", "_", "+", ":", "."
- ],
- "lk": [
- "A", "B", "C", "D", "E", "F", "G", "H", "I",
- "J", "K", "L", "M", "N", "O", "P", "Q", "R",
- "S", "T", "U", "V", "W", "X", "Y", "Z",
- "a", "b", "c", "d", "e", "f", "g", "h", "i",
- "j", "k", "l", "m", "n", "o", "p", "q", "r",
- "s", "t", "u", "v", "w", "x", "y", "z",
- "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "?", "=", "."
- ]
- };
- // JSON version of bok::bitmaps using objects instead of lists.
- // Each value is an object with a "type" string, an "offset" integer, and a
- // "width" integer. "int" types can also have a "signed" boolean.
- // "str" types have a "count" integer in addition to "width", and can have
- // a "restrict" integer which corresponds to a table in 'ctables'.
- var bitmaps = {
- "jp": [ {}, {
- "region": {
- "type": "int", "offset": 0, "width": 3, "signed": false
- },
- "checksum": {
- "type": "int", "offset": 3, "width": 16, "signed": false
- },
- "offset": {
- "type": "int", "offset": 19, "width": 2, "signed": false
- },
- "sol/4": {
- "type": "int", "offset": 21, "width": 9, "signed": false
- },
- "timezone": {
- "type": "int", "offset": 30, "width": 6, "signed": false
- },
- "hours": {
- "type": "int", "offset": 36, "width": 6, "signed": false
- },
- "minutes": {
- "type": "int", "offset": 42, "width": 6, "signed": false
- },
- "difficulty": {
- "type": "int", "offset": 48, "width": 3, "signed": false
- },
- "dungeons": {
- "type": "int", "offset": 51, "width": 5, "signed": false
- },
- "clears": {
- "type": "int", "offset": 56, "width": 3, "signed": false
- },
- "continues": {
- "type": "int", "offset": 59, "width": 4, "signed": false
- },
- "caught": {
- "type": "int", "offset": 63, "width": 5, "signed": false
- },
- "kills": {
- "type": "int", "offset": 68, "width": 7, "signed": false
- },
- "rank": {
- "type": "int", "offset": 75, "width": 4, "signed": false
- },
- "title": {
- "type": "int", "offset": 79, "width": 5, "signed": false
- },
- "name": {
- "type": "str", "offset": 84, "count": 5, "width": 40, "restrict": 0x180
- },
- "link-battles": {
- "type": "int", "offset": 124, "width": 6, "signed": false
- },
- "link-trades": {
- "type": "int", "offset": 130, "width": 6, "signed": false
- },
- "loan": {
- "type": "int", "offset": 136, "width": 8, "signed": false
- },
- "padding": {
- "type": "int", "offset": 144, "width": 0, "signed": false
- }
- }, {
- "checksum": {
- "type": "int", "offset": 0, "width": 16, "signed": false
- },
- "header-padding": {
- "type": "int", "offset": 16, "width": 2, "signed": false
- },
- "region": {
- "type": "int", "offset": 18, "width": 3, "signed": false
- },
- "offset": {
- "type": "int", "offset": 21, "width": 3, "signed": false
- },
- "timezone": {
- "type": "int", "offset": 24, "width": 6, "signed": false
- },
- "side": {
- "type": "int", "offset": 30, "width": 3, "signed": false
- },
- "style": {
- "type": "int", "offset": 33, "width": 4, "signed": false
- },
- "kills/8": {
- "type": "int", "offset": 37, "width": 8, "signed": false
- },
- "forges": {
- "type": "int", "offset": 45, "width": 7, "signed": false
- },
- "link-battles": {
- "type": "int", "offset": 52, "width": 6, "signed": false
- },
- "link-shopping" : {
- "type": "int", "offset": 58, "width": 6, "signed": false
- },
- "sol/4096": {
- "type": "int", "offset": 64, "width": 10, "signed": true
- },
- "loan/256": {
- "type": "int", "offset": 74, "width": 8, "signed": true
- },
- "hours": {
- "type": "int", "offset": 82, "width": 6, "signed": false
- },
- "titles": {
- "type": "int", "offset": 88, "width": 16, "signed": false
- },
- "name": {
- "type": "str", "offset": 104, "count": 5, "width": 40, "restrict": 0x180
- },
- "padding": {
- "type": "int", "offset": 144, "width": 0, "signed": false
- }
- }, {
- "checksum": {
- "type": "int", "offset": 0, "width": 16, "signed": false
- },
- "header-padding": {
- "type": "int", "offset": 16, "width": 2, "signed": false
- },
- "region": {
- "type": "int", "offset": 18, "width": 3, "signed": false
- },
- "offset": {
- "type": "int", "offset": 21, "width": 3, "signed": false
- },
- "timezone": {
- "type": "int", "offset": 24, "width": 6, "signed": false},
- "kills/8": {
- "type": "int", "offset": 30, "width": 8, "signed": false
- },
- "forges": {
- "type": "int", "offset": 38, "width": 7, "signed": false
- },
- "races": {
- "type": "int", "offset": 45, "width": 9, "signed": false
- },
- "link-races": {
- "type": "int", "offset": 54, "width": 7, "signed": false
- },
- "cross-linked": {
- "type": "int", "offset": 61, "width": 1, "signed": false
- },
- "endings": {
- "type": "int", "offset": 62, "width": 6, "signed": false
- },
- "sol/4096": {
- "type": "int", "offset": 68, "width": 10, "signed": false
- },
- "loan/256": {
- "type": "int", "offset": 78, "width": 8, "signed": false
- },
- "hours": {
- "type": "int", "offset": 86, "width": 6, "signed": false
- },
- "titles": {
- "type": "int", "offset": 92, "width": 12, "signed": false
- },
- "name": {
- "type": "str", "offset": 104, "count": 5, "width": 40, "restrict": 0x180
- },
- "padding": {
- "type": "int", "offset": 144, "width": 0, "signed": false
- }
- }, {
- "checksum": {
- "type": "int", "offset": 0, "width": 16, "signed": false
- },
- "header-padding": {
- "type": "int", "offset": 16, "width": 2, "signed": false
- },
- "region": {
- "type": "int", "offset": 18, "width": 3, "signed": false
- },
- "offset": {
- "type": "int", "offset": 21, "width": 3, "signed": false
- },
- "titles": {
- "type": "int", "offset": 24, "width": 14, "signed": false
- },
- "difficulty": {
- "type": "int", "offset": 38, "width": 2, "signed": false
- },
- "hours": {
- "type": "int", "offset": 40, "width": 7, "signed": false
- },
- "sol/4096": {
- "type": "int", "offset": 47, "width": 15, "signed": false
- },
- "sword": {
- "type": "int", "offset": 62, "width": 3, "signed": false
- },
- "gun": {
- "type": "int", "offset": 65, "width": 3, "signed": false
- },
- "terrennial": {
- "type": "int", "offset": 68, "width": 3, "signed": false
- },
- "climate": {
- "type": "int", "offset": 71, "width": 3, "signed": false
- },
- "name-dark": {
- "type": "str", "offset": 74, "count": 10, "width": 80
- },
- "name-solar": {
- "type": "str", "offset": 154, "count": 10, "width": 80
- },
- "padding": {
- "type": "int", "offset": 234, "width": 6, "signed": false
- }
- }],
- "en": [ {}, {
- "region": {
- "type": "int", "offset": 0, "width": 3, "signed": false
- },
- "checksum": {
- "type": "int", "offset": 3, "width": 16, "signed": false
- },
- "offset": {
- "type": "int", "offset": 19, "width": 2, "signed": false
- },
- "sol/4": {
- "type": "int", "offset": 21, "width": 9, "signed": false
- },
- "timezone": {
- "type": "int", "offset": 30, "width": 7, "signed": false
- },
- "hours": {
- "type": "int", "offset": 37, "width": 6, "signed": false
- },
- "minutes": {
- "type": "int", "offset": 43, "width": 6, "signed": false
- },
- "difficulty": {
- "type": "int", "offset": 49, "width": 3, "signed": false
- },
- "dungeons": {
- "type": "int", "offset": 52, "width": 5, "signed": false
- },
- "clears": {
- "type": "int", "offset": 57, "width": 3, "signed": false
- },
- "continues": {
- "type": "int", "offset": 60, "width": 4, "signed": false
- },
- "caught": {
- "type": "int", "offset": 64, "width": 5, "signed": false
- },
- "kills": {
- "type": "int", "offset": 69, "width": 7, "signed": false
- },
- "rank": {
- "type": "int", "offset": 76, "width": 4, "signed": false
- },
- "title": {
- "type": "int", "offset": 80, "width": 5, "signed": false
- },
- "name": {
- "type": "str", "offset": 85, "count": 9, "width": 72, "restrict": 0
- },
- "link-battles": {
- "type": "int", "offset": 157, "width": 6, "signed": false
- },
- "link-trades": {
- "type": "int", "offset": 163, "width": 6, "signed": false
- },
- "loan": {
- "type": "int", "offset": 169, "width": 8, "signed": false
- },
- "padding": {
- "type": "int", "offset": 177, "width": 15, "signed": false
- }
- }, {
- "checksum": {
- "type": "int", "offset": 0, "width": 16, "signed": false
- },
- "header-padding": {
- "type": "int", "offset": 16, "width": 2, "signed": false
- },
- "region": {
- "type": "int", "offset": 18, "width": 3, "signed": false
- },
- "offset": {
- "type": "int", "offset": 21, "width": 3, "signed": false
- },
- "timezone": {
- "type": "int", "offset": 24, "width": 7, "signed": false
- },
- "side": {
- "type": "int", "offset": 31, "width": 3, "signed": false
- },
- "style": {
- "type": "int", "offset": 34, "width": 4, "signed": false
- },
- "kills/8": {
- "type": "int", "offset": 38, "width": 8, "signed": false
- },
- "forges": {
- "type": "int", "offset": 46, "width": 7, "signed": false
- },
- "link-battles": {
- "type": "int", "offset": 53, "width": 6, "signed": false
- },
- "link-shopping": {
- "type": "int", "offset": 59, "width": 6, "signed": false
- },
- "sol/4096": {
- "type": "int", "offset": 65, "width": 10, "signed": true
- },
- "loan/256": {
- "type": "int", "offset": 75, "width": 8, "signed": true
- },
- "hours": {
- "type": "int", "offset": 83, "width": 6, "signed": false
- },
- "titles": {
- "type": "int", "offset": 89, "width": 16, "signed": false
- },
- "name": {
- "type": "str", "offset": 105, "count": 9, "width": 72, "restrict": 0
- },
- "padding": {
- "type": "int", "offset": 177, "width": 15, "signed": false
- }
- }, {
- "checksum": {
- "type": "int", "offset": 0, "width": 16, "signed": false
- },
- "header-padding": {
- "type": "int", "offset": 16, "width": 2, "signed": false
- },
- "region": {
- "type": "int", "offset": 18, "width": 3, "signed": false
- },
- "offset": {
- "type": "int", "offset": 21, "width": 3, "signed": false
- },
- "timezone": {
- "type": "int", "offset": 24, "width": 7, "signed": false
- },
- "kills/8": {
- "type": "int", "offset": 31, "width": 8, "signed": false
- },
- "forges": {
- "type": "int", "offset": 39, "width": 7, "signed": false
- },
- "races": {
- "type": "int", "offset": 46, "width": 9, "signed": false
- },
- "link-races": {
- "type": "int", "offset": 55, "width": 7, "signed": false
- },
- "cross-linked": {
- "type": "int", "offset": 62, "width": 1, "signed": false
- },
- "endings": {
- "type": "int", "offset": 63, "width": 6, "signed": false
- },
- "sol/4096": {
- "type": "int", "offset": 69, "width": 10, "signed": false
- },
- "loan/256": {
- "type": "int", "offset": 79, "width": 8, "signed": false
- },
- "hours": {
- "type": "int", "offset": 87, "width": 6, "signed": false
- },
- "titles": {
- "type": "int", "offset": 93, "width": 12, "signed": false
- },
- "name": {
- "type": "str", "offset": 105, "count": 9, "width": 72, "restrict": 0
- },
- "padding": {
- "type": "int", "offset": 177, "width": 15, "signed": false
- }
- }, {
- "checksum": {
- "type": "int", "offset": 0, "width": 16, "signed": false
- },
- "header-padding": {
- "type": "int", "offset": 16, "width": 2, "signed": false
- },
- "region": {
- "type": "int", "offset": 18, "width": 3, "signed": false
- },
- "offset": {
- "type": "int", "offset": 21, "width": 3, "signed": false
- },
- "titles": {
- "type": "int", "offset": 24, "width": 14, "signed": false
- },
- "difficulty": {
- "type": "int", "offset": 38, "width": 2, "signed": false
- },
- "hours": {
- "type": "int", "offset": 40, "width": 7, "signed": false
- },
- "sol/4096": {
- "type": "int", "offset": 47, "width": 15, "signed": false
- },
- "sword": {
- "type": "int", "offset": 62, "width": 3, "signed": false
- },
- "gun": {
- "type": "int", "offset": 65, "width": 3, "signed": false
- },
- "terrennial": {
- "type": "int", "offset": 68, "width": 3, "signed": false
- },
- "climate": {
- "type": "int", "offset": 71, "width": 3, "signed": false
- },
- "name-dark": {
- "type": "str", "offset": 74, "count": 10, "width": 80
- },
- "name-solar": {
- "type": "str", "offset": 154, "count": 10, "width": 80
- },
- "padding": {
- "type": "int", "offset": 234, "width": 6, "signed": false
- }
- }]
- };
- // JSON version of bok::ui::keyboards,
- // Extra data is removed from the key lists and split between keyboards.alias
- // and keyboards.expand. Empty strings still skip to the next column.
- var keyboards = {
- "en64": [
- ["B", "C", "D", "F", "G", "H", "J", "@", "#", "^", "⌫", "⌦"],
- ["K", "L", "M", "N", "P", "Q", "R", "0", "1", "2", "3", "4"],
- ["S", "T", "V", "W", "X", "Y", "Z", "5", "6", "7", "8", "9"],
- ["b", "c", "d", "f", "g", "h", "j", "+", "-", "/", "." ],
- ["k", "l", "m", "n", "p", "q", "r", "=", "_", "?", "←", "→"],
- ["s", "t", "v", "w", "x", "y", "z", ":", ">", "!", "␣" ]
- ],
- "lk64": [
- ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", ".", "⌫", "⌦"],
- ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"],
- ["N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"],
- ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"],
- ["n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"],
- ["?", "=", "␣", "←", "→"]
- ],
- "jp64": [
- ["あ", "い", "う", "え", "お", "ま", "み", "む", "め", "も", "⌫", "⌦" ],
- ["か", "き", "く", "け", "こ", "や", "ゆ", "よ", "わ", "を", "ば", "び"],
- ["さ", "し", "す", "せ", "そ", "ら", "り", "る", "れ", "ろ", "ぶ", "べ"],
- ["た", "ち", "つ", "て", "と", "が", "ぎ", "ぐ", "げ", "ご", "ぼ" ],
- ["な", "に", "ぬ", "ね", "の", "ざ", "じ", "ず", "ぜ", "ぞ", "←", "→" ],
- ["は", "ひ", "ふ", "へ", "ほ", "だ", "ぢ", "づ", "で", "ど", "␣" ]
- ],
- "en": [
- ["A", "B", "C", "D", "E", "F", "G", "H", "I", "⌫", "⌦"],
- ["J", "K", "L", "M", "N", "O", "P", "Q", "R", ".", ","],
- ["S", "T", "U", "V", "W", "X", "Y", "Z", "", "'", "-"],
- ["a", "b", "c", "d", "e", "f", "g", "h", "i", "/" ],
- ["j", "k", "l", "m", "n", "o", "p", "q", "r", "←", "→"],
- ["s", "t", "u", "v", "w", "x", "y", "z", "", "␣" ]
- ],
- "ext": [
- ["&", "*", ";", "+", "-", "×", "÷", "=", "⌫", "⌦"],
- ["à", "á", "â", "ä", "è", "é", "ê", "ë", "(", ")"],
- ["ì", "í", "î", "ï", "ò", "ó", "ô", "ö", "[", "]"],
- ["ù", "ú", "û", "ü", "ç", "ñ", "œ", "ß", "!", "?"],
- ["À", "Á", "Â", "Ä", "È", "É", "Ê", "Ë", "¡", "¿"],
- ["Ì", "Í", "Î", "Ï", "Ò", "Ó", "Ô", "Ö", "←", "→"],
- ["Ù", "Ú", "Û", "Ü", "Ç", "Ñ", "Œ", "⋅", "␣" ]
- ],
- "hiri": [
- ["あ", "い", "う", "え", "お", "ら", "り", "る", "れ", "ろ", "⌫", "⌦" ],
- ["か", "き", "く", "け", "こ", "が", "ぎ", "ぐ", "げ", "ご", "ん", "・"],
- ["さ", "し", "す", "せ", "そ", "ざ", "じ", "ず", "ぜ", "ぞ", "ー", "~" ],
- ["た", "ち", "つ", "て", "と", "だ", "ぢ", "づ", "で", "ど" ],
- ["な", "に", "ぬ", "ね", "の", "ば", "び", "ぶ", "べ", "ぼ" ],
- ["は", "ひ", "ふ", "へ", "ほ", "ぱ", "ぴ", "ぷ", "ぺ", "ぽ" ],
- ["ま", "み", "む", "め", "も", "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "←", "→" ],
- ["や", "ゆ", "よ", "わ", "を", "っ", "ゃ", "ゅ", "ょ", "", "␣" ]
- ],
- "kata": [
- ["ア", "イ", "ウ", "エ", "オ", "ラ", "リ", "ル", "レ", "ロ", "⌫", "⌦" ],
- ["カ", "キ", "ク", "ケ", "コ", "ガ", "ギ", "グ", "ゲ", "ゴ", "ン", "・"],
- ["サ", "シ", "ス", "セ", "ソ", "ザ", "ジ", "ズ", "ゼ", "ゾ", "ー", "~" ],
- ["タ", "チ", "ツ", "テ", "ト", "ダ", "ヂ", "ヅ", "デ", "ド" ],
- ["ナ", "ニ", "ヌ", "ネ", "ノ", "バ", "ビ", "ブ", "ベ", "ボ" ],
- ["ハ", "ヒ", "フ", "ヘ", "ホ", "パ", "ピ", "プ", "ペ", "ポ" ],
- ["マ", "ミ", "ム", "メ", "モ", "ァ", "ィ", "ゥ", "ェ", "ォ", "←", "→" ],
- ["ヤ", "ユ", "ヨ", "ワ", "ヲ", "ッ", "ャ", "ュ", "ョ", "ヴ", "␣" ]
- ],
- "alias": {
- "⌫": "Backspace", "⌦": "Delete",
- "←": "ArrowLeft", "→": "ArrowRight",
- "␣": " "
- }, "expand": {
- "␣": true
- }};
- // 'decstr' converts a list of 8-bit integers to a string, mapping to
- // 'ctable'. If 'table' is specified then the decoding only uses table
- // 'table', essentially assuming that each 8-bit integer is preceded by
- // the table-change character for 'table'.
- function decstr(list, table = "") {
- var i, r, f = "", len;
- if (typeof(table) == "number")
- table = "0x" + table.toString(16).toUpperCase();
- else if (typeof(table) == "undefined")
- table = ""
- if (table != "" && !(table in ctable))
- throw new Error("Invalid table: " + table);
- for (len = list.length; list[len - 1] == 0; len--) {}
- for (i = 0, r = []; i < len; i++) {
- if (f == "") {
- if (table == "") {
- f = "0x" + list[i].toString(16).toUpperCase();
- if (f in ctable)
- continue;
- f = "0x" + (list[i] >>> 8).toString(16).toUpperCase();
- } else
- f = table;
- }
- r.push(f in ctable && (list[i] & 0xFF) < ctable[f].length ?
- ctable[f][list[i] & 0xFF] : "�");
- f = "";
- }
- return r.join("");
- }
- // 'encstr' converts a string to a list of 8-bit integers, mapping from
- // 'ctable'. If $tab is specified the encoder only uses the 'table' table,
- // essentially omitting the table-change characters.
- function encstr(str, table = "") {
- var i, r, k, t;
- if (typeof(table) == "number")
- table = "0x" + table.toString(16).toUpperCase();
- else if (typeof(table) == "undefined")
- table = "";
- if (table != "" && !(table in ctable))
- throw new Error("Invalid table: " + table);
- for (i = 0, r = []; i < str.length; i++) {
- if (str[i] == "�")
- break;
- if (table != "") {
- k = 0;
- t = ctable[table].indexOf(str[i]);
- } else {
- for (k in ctable)
- if ((t = ctable[k].indexOf(str[i])) >= 0)
- break;
- k = parseInt(k);
- }
- if (t < 0)
- break;
- if (k != 0)
- r.push(k);
- r.push(t);
- }
- return r;
- }
- // 'decb64' decodes the base64 string 'str' using 'b64table' into a list of
- // 6-bit integers, ignoring any [:space:] characters. If the 'game' is 4 the
- // "jp" & "lk" tables are used, otherwise the "jp" & "en" tables are used.
- function decb64(str, game = 1) {
- var en = game < 4 ? "en" : "lk";
- var r = [];
- var i, t;
- for (i = 0; i < str.length; i++) {
- if (/\s/.test(str[i]))
- continue;
- if ((t = b64table.jp.indexOf(str[i])) < 0 &&
- (t = b64table[en].indexOf(str[i])) < 0)
- throw new Error("Invalid character: '" + str[i] + "'");
- if (t < 64)
- r.push(t);
- }
- return r;
- }
- // 'encb64' encodes a base64 string from 'list', which is list of 6-bit
- // integers using the appropriate alphabet for the 'game' / 'lang' pair.
- // 'lang' is set based on the region in 'list' if it is not specified.
- function encb64(list, game = 1, lang = "", pad = true) {
- var table, chars, pad, i, j;
- if (typeof(lang) == "undefined" || lang == "")
- lang = getlang(list);
- table = lang == "jp" ? "jp" : game < 4 ? "en" : "lk";
- pad = pad ? game >= 4 ? 5 : lang == "jp" ? 6 : 8 : 0;
- for (i = 0, j = 0, chars = []; i < list.length; i++) {
- if (list[i] < 0 || list[i] >= 64)
- throw new Error("Invalid value: " + list[i]);
- if (pad > 0 && j > 0 && j % pad == 0)
- chars.push(" ");
- chars.push(b64table[table][list[i]]);
- j++;
- }
- return chars.join("");
- }
- // 'getint' gets an integer of width 'count' at bit-offset 'offset' from the
- // list 'list' of 'width' wide integers, interpreting it as a two's complement
- // signed integer if 'signed' is true.
- // The bits are read and written LSb first.
- function getint(list, offset, count, signed = false, width = 6) {
- var i, v;
- count = Math.min(count, list.length * width - offset);
- for (v = 0, i = 0; i < count; i++, offset++)
- v |= (list[offset / width | 0] >>> offset % width & 1) << i;
- if (signed && count > 0 && (v >>> count - 1 & 1))
- v = -(~v + 1 & (1 << count) - 1);
- return v
- }
- // 'putint' puts the integer 'v' of width 'count' into list 'list' at
- // bit-offset 'offset' and returns the 'list'. 'list' is a list of 'width'
- // wide integers. Zeros are added to the list as needed. 'v' is clamped to
- // [0,2^$count) for unsigned integers, and [-2^($count-1),2^($count-1)) for
- // signed integers. Signed numbers are converted to two's-complement
- // formatted 'width' wide unsigned integers before writing.
- // The bits are written and read LSb first.
- function putint(list, v, offset, count, signed = false, width = 6) {
- var i, m;
- m = (1 << count - (signed && count > 0 ? 1 : 0)) - 1;
- v = signed ? Math.min(Math.max(v, -(m + 1)), m) + m + 1 ^ m + 1 :
- Math.min(Math.max(v, 0), m);
- for (i = 0; i < count; i++, offset++) {
- while ((offset / width | 0) >= list.length)
- list.push(0);
- list[offset / width | 0] &= ~(1 << offset % width);
- list[offset / width | 0] |= (v >>> i & 1) << offset % width;
- }
- return list
- }
- // 'getstr' gets the string converted from 'count' octets from the 'list' of
- // 'width' wide integers starting at bit-offset 'offset'. 'table' is passed
- // to 'decstr' along with the octets.
- function getstr(list, offset, count, table = "", width = 6) {
- var i, r;
- count = Math.min(count, list.length * width - offset >>> 3);
- for (i = 0, r = []; i < count; i++)
- r[i] = getint(list, offset + i * 8, 8, 0, width);
- return decstr(r, table);
- }
- // 'putstr' puts 'count' octets from the encoded 'str' into 'list' at bit-
- // offset 'offset'. 'list' is a list of 'width' wide integers and 'table'
- // is passed to 'encstr' along with $str. 'list' is then returned.
- function putstr(list, str, offset, count, table = "", width = 6) {
- var i, l = encstr(str, table);
- for (i = 0; i < count; i++)
- putint(list, i < l.length ? l[i] : 0, offset + i * 8, 8, 0, width);
- return list;
- }
- // 'forkeys' iterates over the provided keys, or array of keys, or all keys,
- // from the corresponding list for the 'game' / 'lang' pair in 'bitmaps'.
- // Values are read from the list 'list' of 'width' wide integers and the
- // callback is called with the key, value, and value from 'bitmaps'.
- // If 'FORKEYS_RESTRICT' is set in 'flags' then keys are skipped if not
- // present in 'list'. If 'FORKEYS_MODIFY' is set in 'flags' then the value
- // in 'list' is set to the return value of the callback.
- var FORKEYS_RESTRICT = 1;
- var FORKEYS_MODIFY = 2;
- function forkeys(list, game, lang, width, flags /*, [key, ...] callback */) {
- var callback, args, map, i, k, v, f;
- if (arguments.length < 6)
- return new Error("Wrong number of arguments");
- if (typeof(lang) == "undefined" || lang == "")
- lang = getlang(list, game, width);
- map = bitmaps[lang][game];
- callback = arguments[arguments.length - 1];
- args = Array.prototype.slice.call(arguments, 5, arguments.length - 1);
- if (args.length == 1 && typeof(args[0]) == "object")
- args = args[0];
- if (args.length == 0)
- args = Object.keys(map);
- for (i = 0; i < args.length; i++) {
- k = args[i];
- if (!(k in map))
- continue;
- if ((flags & FORKEYS_RESTRICT) &&
- map[k].offset + map[k].width > list.length * width)
- continue;
- switch (map[k].type) {
- case "int":
- f = "signed" in map[k] ? map[k].signed : false;
- v = callback(k, getint(list, map[k].offset, map[k].width, f, width),
- map[k]);
- if (flags & FORKEYS_MODIFY)
- putint(list, v, map[k].offset, map[k].width, f, width);
- break;
- case "str":
- f = "restrict" in map[k] ? map[k].restrict : "";
- v = callback(k, getstr(list, map[k].offset, map[k].count, f, width),
- map[k]);
- if (flags & FORKEYS_MODIFY)
- putstr(list, v, map[k].offset, map[k].count, f, width);
- break;
- }
- }
- return list;
- }
- // 'getdict' returns a dictionary created by reading values from the 'list' of
- // 'width' wide integers for each key, or every key if none are given, as
- // specified in 'bitmaps' for the 'game' / 'lang' pair.
- function getdict(list, game = "", lang = "", width = 6, /*, [key ...] */) {
- var r = {};
- forkeys(list, game, lang, width, 0,
- Array.prototype.slice.call(arguments, 4), function (k, v) {r[k]=v});
- return r;
- }
- // 'getkeys' returns a list of values similar to 'getdict'.
- function getkeys(list, game = "", lang = "", width = 6 /*, key ... */) {
- var r = [];
- forkeys(list, game, lang, width, 0,
- Array.protoype.slice.call(arguments, 4), function (k, v) {
- r.push(v)
- });
- return r;
- }
- // 'getkeys' returns a single value similar to 'getkeys'
- function getkey(list, game, lang, width, key) {
- var r = undefined;
- forkeys(list, game, lang, width, 0, key, function (k, v) {r = v});
- return r;
- }
- // 'getregion' reads the region bits from the 'list' of 'width' wide integers
- // for game 'game' and returns a short region code, or an error.
- function getregion(list, game = 1, width = 6) {
- var r = getkey(list, game, "jp", width, "region");
- if (typeof(r) == "undefined" || r < 1 || r > 3)
- throw new Error("Invalid region: " + r);
- return ["", "jp", "na", "eu"][r];
- }
- // 'getlang' reads the region bits from the 'list' of 'width' wide integers
- // for game 'game' and returns a short language code, or an error.
- function getlang(list, game = 1, width = 6) {
- return {jp: "jp", na: "en", eu: "en"}[getregion(list, game, width)];
- }
- // 'whichgame' searches 'dict' for keys specific to a particular game, and
- // returns the game number of the first one found.
- function whichgame(dict) {
- var keys = [[
- "dungeons", "clears", "caught", "rank", "title", "link-trades",
- "rank-name", "title-name"
- ], [
- "side", "style", "link-shopping", "side-name", "style-name"
- ], [
- "races", "link-races", "cross-linked", "endings"
- ], [
- "sword", "gun", "terrennial", "climate", "name-dark", "name-solar",
- "sword-name", "gun-name", "terrennial-name", "climate-name"
- ]];
- var i, j;
- for (i = 0; i < keys.length; i++)
- for (j = 0; j < keys[i].length; j++)
- if (keys[i][j] in dict)
- return i + 1;
- throw new Error("Unknown game");
- }
- // 'putdict' inserts the values from each key/value pair in 'dict' into
- // the 'list' of 'width' wide integers as specified in 'bitmaps' for the
- // 'game' / 'lang' pair and returns the modified 'list'
- function putdict(list, dict, game = "", lang = "", width = 6) {
- var args;
- if (typeof(game) == "undefined" || game == "")
- game = whichgame(dict);
- if (typeof(lang) == "undefined" || lang == "") {
- if (dict.region < 0 || dict.region > 3)
- throw new Error("Invalid region: " + dict.region);
- lang = ["", "jp", "en", "en"][dict.region];
- }
- return forkeys(list, game, lang, width, FORKEYS_MODIFY, Object.keys(dict),
- function (k) {return dict[k]});
- }
- // 'checksum' calculates the CRC-16 for the 'list' of 6-bit integers with an
- // initializer of 0xFFFF, the 'game' appropriate polynomial, and the output
- // inverted, and starting at the 'game' appropriate offset.
- function checksum(list, game = 1) {
- var j;
- var i = [4, 3, 3, 3][game - 1];
- var c = [0x1021, 0x1021, 0x8005, 0x180D][game - 1];
- var v = 0xFFFF;
- for (; i < list.length; i++) {
- v ^= list[i] << 8;
- for (j = 0; j < 8; j++)
- v = (v << 1 ^ ((v & 0x8000) ? c : 0)) & 0xFFFF;
- }
- return ~v & 0xFFFF;
- }
- // 'xor' encodes/decodes the password bits from the 'list' of 6-bit integers
- // the constant and the 'game' appropriate initalizer.
- // 'm' is the initialized value, 't' is initialized to ~0, and the offset is
- // read from the 'list'. For each iteration 'm' is multiplied by the constant
- // then incremented by 1, and 't' is xored by the result. After 'offset' plus
- // 4 iterations, starting at index 4 until the end of the $list, each $list
- // item is xored by the last 6 bits of $t before the next iteration.
- // The encoded list is returned.
- // Using the full constants causes the doubles to lose precision, luckily only
- // the last 6 bits of the actually matter, 'Math.imul' colud be used as well.
- function xor(list, game = 1) {
- //var m = [0x8C159, 0xB8E6E, 0x8C159, 0x5BB15][game - 1];
- var m = [0x19, 0x2E, 0x19, 0x15][game - 1];
- //var c = 0x6262C05D;
- var c = 0x1D;
- //var t = 0xFFFF;
- var t = 0x3F;
- var i = -getkey(list, game, "jp", 6, "offset");
- for (; i < list.length; i++) {
- if (i >= 4)
- list[i] ^= t;
- m = m * c + 1 & 0x3F;
- t ^= m;
- }
- return list;
- }
- // 'normalizelist' sets 'skey' in 'dict' based on the value of 'vkey' based on
- // the strings in 'list', or vice versa. 'name' is used for error messages
- // and defaults to 'vkey'. If 'robust' is true then no errors are returned
- // for invalid values, or known invalid strings.
- function normalizelist(dict, list, vkey, skey, name = "", robust = false) {
- var invalid, unknown, v, s;
- if (typeof(name) == "undefined" || name == "")
- name = vkey;
- invalid = "Invalid " + name + " #";
- unknown = "Unknown " + name + " #";
- if (vkey in dict) {
- v = dict[vkey];
- s = v >= 0 && v < list.length ? list[v] : unknown + v;
- if (!robust && s == unknown + v)
- throw new Error(s);
- dict[skey] = s;
- } else if (skey in dict) {
- s = dict[skey];
- v = list.indexOf(s);
- if (v < 0 && s.indexOf(unknown) == 0) {
- v = parseInt(s.slice(unknown.length))
- if (!robust && (v < 0 || isNaN(v)))
- throw new Error(s);
- }
- if ((v < 0 || isNaN(v)) && s.indexOf(invalid) == 0) {
- v = parseInt(s.slice(invalid.length))
- if (!robust && (v < 0 || isNaN(v)))
- throw new Error(s);
- }
- if ((v < 0 || isNaN(v)) && (s != "" || !robust))
- throw new Error("Invalid " + skey + ": " + s);
- dict[vkey] = v;
- }
- return dict;
- }
- // 'normalizebits' sets 'lkey' in 'dict' based on the value of 'vkey' based on
- // the strings in 'list', or vice versa. 'name' is used for error messages
- // and defaults to 'vkey'. If 'robust' is true then invalid values and items
- // are ignored.
- function normalizebits(dict, list, vkey, lkey, name = "", robust = false) {
- var v, l, i, j;
- if (typeof(name) == "undefined" || name == "")
- name = vkey;
- if (vkey in dict) {
- v = dict[vkey];
- if (typeof(v) != "number")
- throw new Error("Invalid " + name + ": " + v);
- for (i = 0, l = []; i < list.length; i++)
- if (v & 1 << i) {
- v &= ~(1 << i);
- l.push(list[i]);
- }
- if (!robust && v != 0) {
- for (; !(v & 1 << i); i++) {}
- throw new Error("Unknown " + name + " #" + i);
- }
- dict[lkey] = l;
- } else if (lkey in dict) {
- l = dict[lkey];
- for (i = 0, v = 0; i < l.length; i++) {
- j = list.indexOf(l[i]);
- if (!robust && i < 0)
- throw new Error("Unknown " + name + ": " + l[i]);
- v |= i >= 0 ? 1 << i : 0;
- }
- dict[vkey] = v;
- }
- return dict;
- }
- // 'normalize' normalizes the provided dictionary 'dict' using 'locale'
- // strings such that if "sol/4096" or "timezone-name" exists then "sol" and
- // "timezone" will be set to the equivalent values. If 'robust' is set then
- // no ignorable errors are returned.
- function normalize(dict, locale = "en", robust = false) {
- var region, lang;
- var game = whichgame(dict);
- normalizelist(dict, strings[locale].regions, "region", "region-name",
- strings[locale].labels.region, robust);
- region = "region" in dict ? dict.region : 0;
- if (!robust && (region < 1 || region > 3))
- throw new Error("Unknown region");
- lang = ["??", "jp", "en", "en", "??", "??", "??", "??"][region];
- region = ["??", "jp", "na", "eu", "??", "??", "??", "??"][region];
- dict.game = game;
- dict["game-name"] = strings[locale].games[lang + game];
- // Zeros
- ["header-padding", "padding", "offset"].forEach(function (k) {
- if (!(k in dict))
- dict[k] = 0;
- });
- // Sol
- if ("sol" in dict) {
- dict["sol/4"] = dict.sol >>> 2;
- dict["sol/4096"] = dict.sol >>> 12;
- } else if ("sol/4" in dict) {
- dict.sol = dict["sol/4"] << 2;
- dict["sol/4096"] = dict["sol/4"] >>> 10;
- } else if ("sol/4096" in dict) {
- dict.sol = dict["sol/4096"] << 12;
- dict["sol/4"] = dict["sol/4096"] << 10;
- }
- // Kills
- if ("kills" in dict)
- dict["kills/8"] = dict.kills >>> 3;
- else if ("kills/8" in dict)
- dict.kills = dict["kills/8"] << 3;
- // Loans
- if ("loan" in dict)
- dict["loan/256"] = dict.loan >>> 8;
- else if ("loan/256" in dict)
- dict.loan = dict["loan/256"] << 8;
- // Timezones
- if (region in strings[locale].timezones)
- normalizelist(dict, strings[locale].timezones[region], "timezone",
- "timezone-name", strings[locale].labels.timezone, robust);
- // Boktai 1
- normalizelist(dict, strings[locale].ranks, "rank", "rank-name",
- strings[locale].labels.rank, robust);
- normalizelist(dict, strings[locale].titles[1], "title", "title-name",
- strings[locale].labels.title, robust);
- normalizelist(dict, strings[locale].difficulties[game], "difficulty",
- "difficulty-name", strings[locale].labels.difficulty, robust);
- // Boktai 2
- normalizelist(dict, strings[locale].sides, "side", "side-name",
- strings[locale].labels.side, robust);
- normalizelist(dict, strings[locale].styles, "style", "style-name",
- strings[locale].labels.style, robust);
- // Titles
- normalizebits(dict, strings[locale].titles[game], "titles", "titles-list",
- strings[locale].labels.titles, robust);
- // Endings
- normalizebits(dict, strings[locale].endings, "endings", "ending-list",
- strings[locale].labels.endings, robust);
- // Favorites
- normalizelist(dict, strings[locale].swords, "sword", "sword-name",
- strings[locale].labels.swords, robust);
- normalizelist(dict, strings[locale].guns, "gun", "gun-name",
- strings[locale].labels.guns, robust);
- normalizelist(dict, strings[locale].terrennials, "terrennial",
- "terrennial-name", strings[locale].labels.terrennials, robust);
- normalizelist(dict, strings[locale].climates, "climate",
- "climate-name", strings[locale].labels.swords, robust);
- return (dict);
- }
- // 'parseregion' returns the region number for the region speified by 'str',
- // or an error if it is unmatched.
- function parseregion(str) {
- if (/^(japan|jp)$/i.test(str))
- return 1;
- if (/^((north.?)?america|na|united.?states|usa?|english)$/i.test(str))
- return 2;
- if (/^(europe|eu)$/i.test(str))
- return 3;
- throw new Error("Unknown region: " + region);
- }
- // 'decpass' decodes the base64 string 'pass' into a dictionary.
- // 'normalize'ing it if 'normlocale' is provided'
- function decpass(pass, game = 1, lang = "", normlocale = "") {
- var len, sum, dict;
- var l = decb64(pass, game);
- if (typeof(lang) == "undefined" || lang == "")
- lang = getlang(l, game);
- len = {jp: [24, 24, 24, 40], en: [32, 32, 32, 40]}[lang][game-1];
- if (l.length != len)
- throw new Error("Invalid length: " + l.length + " != " + len);
- sum = checksum(l, game);
- dict = getdict(xor(l, game), game, lang);
- if (dict.checksum != sum)
- dict.checksum = dict.checksum + "!=" + sum
- if (typeof(normlocale) == "string" && normlocale in strings)
- normalize(dict, normlocale, true);
- return dict;
- }
- // 'encpass' encodes 'dict' into a base64 password.
- // 'normalize'ing it if 'normlocale' is provided'
- function encpass(dict, game = "", lang = "", normlocale = "") {
- var l;
- if (typeof(normlocale) == "string" && normlocale in strings)
- normalize(dict, normlocale, true);
- if (typeof(lang) == "undefined" || lang == "")
- lang = dict.region == 1 ? "jp" : "en";
- if (typeof(game) == "undefined" || game == "")
- game = whichgame(dict);
- l = xor(putdict([], dict, game, lang), game);
- putdict(l, {checksum: checksum(l, game)}, game, lang);
- return encb64(l, game, lang);
- }
- // 'getuiinfo' returns a list of ui element types, values, and defaults
- // depending on the values of 'game', 'region', and 'locale'.
- // Each value is an object with a "name" string and a "type" string, and can
- // have a "default" value. "list" types have a "values" array, "regionlist"
- // types have a "values" object containing arrays for each short region code.
- // "int" types have a "min" integer, a "max" integer, and can also have a
- // "step" integer. "string" types have a "max" array or region-keyed object,
- // can have a "max-bytes" array or region-keyed object, and can have a
- // "restrict" boolean. "bits" types have a "values" array. Empty names or
- // "break" types will will ensure the next input is at the start of a row.
- var uiinfo = {};
- function getuiinfo(game, region = "na", locale = "en") {
- var regionnum, timezones;
- if (!(locale in strings))
- throw new Error("Unknown locale: " + locale);
- regionnum = parseregion(region);
- region = ["", "jp", "na", "eu"][regionnum]
- if (!(locale in uiinfo))
- uiinfo[locale] = {};
- if (!(region in uiinfo[locale]))
- uiinfo[locale][region] = {};
- if (game in uiinfo[locale][region])
- return uiinfo[locale][region][game];
- timezones = {};
- Object.keys(strings[locale].timezones).forEach(function (key) {
- timezones[key] = strings[locale].timezones[key].slice(1);
- });
- switch (game) {
- case 1:
- return uiinfo[locale][region][game] = [
- { name: "region-name", type: "list",
- values: strings[locale].regions.slice(1),
- default: regionnum - 1
- },
- { name: "offset", type: "int", min: 0, max: 3 },
- { name: "sol", type: "int", min: 0, max: 0x7FC, step: 4 },
- { name: "timezone-name", type: "regionlist",
- values: timezones, default: { jp: 5, na: 14, eu: 21 }
- },
- { name: "", type: "break" },
- { name: "hours", type: "int", min: 0, max: 63 },
- { name: "minutes", type: "int", min: 0, max: 59, default: 1 },
- { name: "difficulty-name", type: "list",
- values: strings[locale].difficulties[1],
- default: 2
- },
- { name: "dungeons", type: "int", min: 0, max: 29 },
- { name: "clears", type: "int", min: 1, max: 7 },
- { name: "continues", type: "int", min: 0, max: 15 },
- { name: "caught", type: "int", min: 0, max: 31 },
- { name: "kills", type: "int", min: 0, max: 127 },
- { name: "rank-name", type: "list", values: strings[locale].ranks },
- { name: "title-name", type: "list", values: strings[locale].titles[1] },
- { name: "name", type: "string",
- max: { jp: 5, na: 9, eu: 9 },
- default: { jp: "ジャンゴ", na: "Django", eu: "Django" },
- restrict: true
- },
- { name: "link-battles", type: "int", min: 0, max: 63 },
- { name: "link-trades", type: "int", min: 0, max: 63 },
- { name: "loan", type: "int", min: 0, max: 255 },
- { name: "padding", type: "hidden", default: 0 }
- ];
- case 2:
- return uiinfo[locale][region][game] = [
- { name: "region-name", type: "list",
- values: strings[locale].regions.slice(1),
- default: regionnum - 1
- },
- { name: "offset", type: "int", min: 0, max: 7 },
- { name: "timezone-name", type: "regionlist",
- values: timezones, default: { jp: 5, na: 14, eu: 21 }
- },
- { name: "side-name", type: "list", values: strings[locale].sides },
- { name: "style-name", type: "list", values: strings[locale].styles },
- { name: "kills", type: "int", min: 0, max: 0x7F8, step: 8 },
- { name: "forges", type: "int", min: 0, max: 127 },
- { name: "link-battles", type: "int", min: 0, max: 63 },
- { name: "link-shopping", type: "int", min: 0, max: 63 },
- { name: "sol", type: "int", min: 0, max: 0x1FF000, step: 0x1000 },
- { name: "loan", type: "int", min: 0, max: 0x7F00, step: 0x100 },
- { name: "hours", type: "int", min: 0, max: 63 },
- { name: "name", type: "string",
- max: { jp: 5, na: 9, eu: 9 },
- default: { jp: "ジャンゴ", na: "Django", eu: "Django" },
- restrict: true
- },
- { name: "titles", type: "bits",
- values: strings[locale].titles[2], default: 0x40
- },
- { name: "header-padding", type: "hidden", default: 0 },
- { name: "padding", type: "hidden", default: 0 }
- ];
- case 3:
- return uiinfo[locale][region][game] = [
- { name: "region-name", type: "list",
- values: strings[locale].regions.slice(1, 2),
- },
- { name: "offset", type: "int", min: 0, max: 7 },
- { name: "timezone-name", type: "regionlist",
- values: timezones, default: { jp: 5, na: 14, eu: 21 }
- },
- { name: "kills", type: "int", min: 0, max: 0x7F8, step: 8 },
- { name: "forges", type: "int", min: 0, max: 127 },
- { name: "races", type: "int", min: 0, max: 511 },
- { name: "link-races", type: "int", min: 0, max: 127 },
- { name: "cross-linked", type: "boolean" },
- { name: "sol", type: "int", min: 0, max: 0x3FF000, step: 0x1000 },
- { name: "loan", type: "int", min: 0, max: 0xFF00, step: 0x100 },
- { name: "hours", type: "int", min: 0, max: 63 },
- { name: "name", type: "string",
- max: { jp: 5, na: 9, eu: 9 },
- default: { jp: "ジャンゴ", na: "Django", eu: "Django" },
- restrict: true
- },
- { name: "endings",
- type: "bits", values: strings[locale].endings, default: 1 },
- { name: "titles", type: "bits", values: strings[locale].titles[3] },
- { name: "header-padding", type: "hidden", default: 0 },
- { name: "padding", type: "hidden", default: 0 }
- ];
- case 4:
- return uiinfo[locale][region][game] = [
- { name: "region-name", type: "list",
- values: strings[locale].regions.slice(1),
- default: regionnum - 1
- },
- { name: "offset", type: "int", min: 0, max: 7 },
- { name: "difficulty-name",
- type: "list", values: strings[locale].difficulties[4] },
- { name: "hours", type: "int", min: 0, max: 127 },
- { name: "sol", type: "int", min: 0, max: 0x7FFF000, step: 0x1000 },
- { name: "", type: "break" },
- { name: "sword-name", type: "list", values: strings[locale].swords },
- { name: "gun-name", type: "list", values: strings[locale].guns },
- { name: "terrennial-name",
- type: "list", values: strings[locale].terrennials },
- { name: "climate-name",
- type: "list", values: strings[locale].climates },
- { name: "", type: "break" },
- { name: "name-dark", type: "string",
- max: { jp: 5, na: 10, eu: 10 },
- "max-bytes": { jp: 10, na: 10, eu: 10 },
- default: { jp: "サバタ", na: "Lucian", eu: "Lucian" }
- },
- { name: "name-solar", type: "string",
- max: { jp: 5, na: 10, eu: 10 },
- "max-bytes": { jp: 10, na: 10, eu: 10 },
- default: { jp: "ジャンゴ", na: "Aaron", eu: "Aaron" }
- },
- { name: "titles", type: "bits", values: strings[locale].titles[4] },
- { name: "header-padding", type: "hidden", default: 0 },
- { name: "padding", type: "hidden", default: 0 }
- ];
- default:
- throw new Error("Unknown game: " + game);
- }
- }
- // 'uifindname' returns the first child element of 'root' with name 'name'.
- function uifindname(root, name) {
- var i, r;
- if ("name" in root && root.name == name)
- return root;
- for (i = 0, r = undefined;
- typeof(r) == "undefined" && i < root.children.length;
- r = uifindname(root.children[i++], name)) {}
- return r;
- }
- // 'uiregion' returns the region from 'uifindname(root, "region-name")' for
- // 'locale' as an object with the "string", a "number", and a short "code".
- function uiregion(root, locale = "en") {
- var dict = {};
- dict.string = uifindname(root, "region-name").value;
- normalizelist(dict, strings[locale].regions, "number", "string",
- strings[locale].labels.region, true);
- dict.code = ["??", "jp", "na", "eu", "??", "??", "??", "??"][dict.number];
- return dict;
- }
- // 'uivalidatestring' validates the string value of 'input' in 'root' with
- // 'info' from 'getuiinfo', the 'locale', and the event 'type'. The character
- // values are restricted to those in 'ctable'. 'info.max' restricts the
- // character length of the value, and defaults to 'Infinity';
- // 'info["max-bytes"]' restricts the byte length of the value, and defaults
- // to 'info.max'. If 'info.restrict' is true then the table is restricted to
- // 0x180 if the region is set to Japan or 0x0 otherwise. If the value is empty
- // and the event 'type' is not "input" then it is set to the default string.
- // 'info.default', 'info.max', and 'info["max-bytes"]' can all be region
- // specific if they are objects with short region codes as keys.
- function uivalidatestring(root, input, info, locale = "en", type = "") {
- var s, l, f, carat;
- var region = uiregion(root, locale).code;
- var max = "max" in info ? info.max : Infinity;
- var bmax = "max-bytes" in info ? info["max-bytes"] : max;
- var def = "default" in info ? info.default : max;
- max = typeof(max) == "object" && region in max ? max[region] : max;
- bmax = typeof(bmax) == "object" && region in bmax ? bmax[region] : bmax;
- def = typeof(def) == "object" && region in def ? def[region] : def;
- carat = input.selectionStart;
- s = input.value.slice(0, max);
- f = "restrict" in info && info.restrict ? (region == "jp" ? 0x180 : 0) : "";
- l = encstr(s, f);
- if (l.length > bmax)
- l = l.slice(0, bmax);
- s = decstr(l, f);
- if (type != "input" && s.length == 0)
- s = def;
- if (s != input.value) {
- input.value = s;
- input.setSelectionRange(carat, carat);
- }
- }
- // 'uivalidateinteger' validates the string value of 'input' in 'root' with
- // 'info' from 'getuiinfo', the 'locale', and the event 'type'. The value is
- // restricted to numbers from 'info.min' to 'info.max', and if the value is
- // not a number and 'type' is not "input" then it is set to 'info.default',
- // which defaults to 'info.min'. 'info.default' can be region specific if
- // it is an object with short region codes as keys.
- function uivalidateinteger(root, input, info, locale = "en", type = "") {
- var i;
- var region = uiregion(root, locale).code;
- var def = "default" in info ? info.default : info.min;
- def = typeof(def) == "object" && region in def ? def[region] : def;
- if (input.value == "" && type == "input")
- return;
- i = parseInt(input.value);
- if (isNaN(i) && type == "input")
- return;
- if (isNaN(i))
- input.value = def;
- else if (i < info.min)
- input.value = info.min;
- else if (i > info.max)
- input.value = info.max;
- }
- // 'updateregion' updates any region dependant entries/lists for 'game' in
- // 'root' based on the value of 'uiregion' for 'locale'.
- function uiupdateregion(root, game, locale) {
- var dict, info, lang, cur, t, l, i, j, def;
- dict = uiregion(root, locale);
- lang = dict.code == "jp" ? "jp" : "en";
- cur = uifindname(root, "password");
- cur.value = encb64(forkeys(decb64(cur.value, game), game, lang, 6,
- FORKEYS_RESTRICT | FORKEYS_MODIFY, "region", function (k, v) {
- return dict.number;
- }), game, lang);
- info = getuiinfo(game, dict.code, locale);
- for (i = 0; i < info.length; i++)
- switch (info[i].type) {
- case "string":
- uivalidatestring(root, uifindname(root, info[i].name), info[i], locale);
- break;
- case "regionlist":
- def = "default" in info[i] ? info[i].default : 0;
- if (typeof(def) == "object" && dict.code in def)
- def = def[dict.code];
- cur = uifindname(root, info[i].name);
- while (cur.children.length > 0)
- cur.removeChild(cur.children[0]);
- l = info[i].values;
- l = dict.code in l ? l[dict.code] : l;
- l = typeof(l) == "undefiend" || !("length" in l) ? [] : l;
- for (j = 0; j < l.length; j++) {
- cur.appendChild(t = document.createElement("option"));
- t.selected = j == def;
- t.append(l[j]);
- }
- break;
- }
- return dict.code;
- }
- // 'uicycleoffset' increments the offset bit in the current password in
- // 'uifindname(root, "password")' for 'game' by 1.
- // The language to use is derived from 'uiregion' for 'root' and 'locale'.
- function uicycleoffset(root, game, locale = "en") {
- var input = uifindname(root, "password");
- var region = uiregion(root, locale).code;
- input.value = encb64(forkeys(decb64(input.value, game), game,
- region == "jp" ? "jp" : "en", 6, FORKEYS_RESTRICT | FORKEYS_MODIFY,
- "offset", function (k, v, i) {
- return v + 1 & (1 << i.width) - 1;
- }), game, region == "jp" ? "jp" : "en");
- }
- // 'uiencode' encodes the input values in 'root' for 'game' with 'prefix' and
- // 'locale' into the password entry.
- function uiencode(root, game, locale = "en", prefix = "bokpass") {
- var region = uiregion(root, locale).code;
- var lang = region == "jp" ? "jp" : "en";
- var info = getuiinfo(game, region, locale);
- var dict, cur, i, j, t, v;
- for (i = 0, dict = {}; i < info.length; i++) {
- if (info[i].name == "" || info[i].type == "break")
- continue;
- if (info[i].type != "bits" &&
- typeof(cur = uifindname(root, info[i].name)) == "undefined")
- throw new Error("No input for: " + info[i].name);
- switch (info[i].type) {
- case "boolean":
- v = cur.checked ? 1 : 0;
- break;
- case "bits":
- for (v = 0, j = 0; typeof(cur = uifindname(
- root, info[i].name + "-" + j)) != "undefined";
- v |= cur.checked ? 1 << j : 0, j++) {}
- break;
- default:
- v = cur.value;
- break;
- }
- dict[info[i].name] = v;
- }
- cur = root.getElementsByClassName(prefix + "-errors")[0];
- cur.innerText = "";
- try {
- normalize(dict, locale);
- } catch (e) {
- cur.innerText = "⚠ " + e;
- } finally {
- switch (game) {
- case 1:
- if (dict.minutes == 0 && dict.hours == 0)
- cur.innerText = "⚠ " + strings[locale].labels.minutes + " or " +
- strings[locale].labels.hours + " should be set"
- else if (dict.minutes >= 60)
- cur.innerText =
- "⚠ " + strings[locale].labels.minutes + " should be less than 60";
- else if (dict.clears == 0)
- cur.innerText =
- "⚠ " + strings[locale].labels.clears + " should be set";
- else if (dict["name"] == "")
- cur.innerText = "⚠ " + strings[locale].labels.name + " should be set";
- break;
- case 2:
- if ((dict.titles & 1 << 6) == 0)
- cur.innerText =
- "⚠ " + strings[locale].titles[2][6] + " should be set";
- else if (dict["name"] == "")
- cur.innerText = "⚠ " + strings[locale].labels.name + " should be set";
- break;
- case 3:
- if (dict["name"] == "")
- cur.innerText = "⚠ " + strings[locale].labels.name + " should be set";
- break;
- case 4:
- if (dict["name-dark"] == "")
- cur.innerText =
- "⚠ " + strings[locale].labels["name-dark"] + " should be set";
- else if (dict["name-solar"] == "")
- cur.innerText =
- "⚠ " + strings[locale].labels["name-solar"] + " should be set";
- break;
- }
- }
- uifindname(root, "password").value = encpass(dict, game, lang, locale)
- }
- // 'uidecode' decodes the current password into the input values in 'root' for
- // 'game' with 'prefix' and 'locale'.
- function uidecode(root, game, locale = "en", prefix = "bokpass") {
- var region = uiregion(root, locale).code;
- var lang = region == "jp" ? "jp" : "en";
- var info = getuiinfo(game, region, locale);
- var cur = root.getElementsByClassName(prefix + "-errors")[0];
- cur.innerText = "";
- try {
- var dict = decpass(uifindname(root, "password").value, game, lang,
- locale);
- } catch (e) {
- cur.innerText = "⚠ " + e;
- return;
- } finally {
- if (typeof(dict.checksum) == "string" && dict.checksum.indexOf("!=") >= 0)
- cur.innerText = "⚠ checksum " + dict.checksum;
- }
- var cur, i, j, t, v;
- for (i = 0; i < info.length; i++) {
- if (info[i].name == "" || info[i].type == "break")
- continue;
- if (!(info[i].name in dict))
- throw new Error("No value for: " + info[i].name);
- v = dict[info[i].name];
- if (info[i].type != "bits")
- cur = uifindname(root, info[i].name);
- switch (info[i].type) {
- case "boolean":
- cur.checked = v;
- break;
- case "bits":
- for (j = 0; typeof(cur = uifindname(root, info[i].name + "-" + j)) !=
- "undefined"; j++)
- cur.checked = !!(v & 1 << j);
- break;
- case "regionlist":
- case "list":
- cur.value = v;
- if (cur.value != v) {
- cur.appendChild(t = document.createElement("option"));
- t.selected = true;
- t.append(v);
- cur.value = v;
- cur.dispatchEvent(new Event("change", {
- bubbles: true, cancelable: true
- }));
- cur.addEventListener("change", (function (cur, t) {
- return function (e) {
- cur.removeChild(t);
- }
- })(cur, t), { once: true });
- }
- break;
- default:
- cur.value = v;
- break;
- }
- }
- }
- // 'uiallowkeyboards' disables/enables all keyboard tabs for 'prefix'
- // depending on the arguments given, defaulting to enabling all keyboards.
- // If one is set to a keyboard not specified then the tab is changed to the
- // first keyboard provided.
- function uiallowkeyboards(prefix, /*, [kbd ...] */) {
- var kbds = document.getElementsByClassName(prefix + "-keyboards");
- var args = Array.prototype.slice.call(arguments, 1);
- var kbd, i, j;
- if (args.length == 1 && typeof(args[0]) == "object")
- args = args[0];
- if (args.length == 0)
- for (i = 0; i < strings.en.labels["keyboard-tabs"].length; i++)
- args.push(strings.en.labels["keyboard-tabs"][i].key);
- for (i = 0; i < kbds.length; i++) {
- kbd = uifindname(kbds[i], "keyboard-tabs");
- if (typeof(kbd) == "undefined")
- continue;
- if (args.indexOf(kbd.value) < 0) {
- kbd.value = args[0];
- kbd.dispatchEvent(new Event("change", {
- bubbles: true, cancelable: true
- }));
- }
- for (j = 0; j < kbd.length; j++) {
- kbd[j].disabled = args.indexOf(kbd[j].value) < 0;
- kbd[j].selected = kbd[j].value == kbd.value;
- }
- }
- }
- // 'uicreate' initializes a grid of inputs based on the values from
- // 'getuiinfo(game, region, locale)' using 'prefix' for classes and ids
- // returning a "div" of class "'prefix'-game" containing them.
- // The number of columns can be specified with 'options.columns', and
- // 'options.nostyle' will disable the automatic grid styling entirely.
- // 'options.invalid' will disable calling 'uivalidateinteger' and
- // 'uivalidatestring' on "input" and "focusout" events, 'uivalidatestring'
- // will still be called when the region is changed.
- // If 'columns' is unset or zero it tries to guess the amount of columns to
- // use for the automatic grid styling from the window dimentions.
- // To automatically reflow the grid on resize use 'options.nostyle' and CSS
- // with "@media" queries.
- // Important classes are:
- // "'prefix'-game" for the main "div"
- // "'prefix'-password" for the password "div"
- // "'prefix'-game-grid" for the input grid
- // "'prefix'-clear" for elements that should start a row
- // "'prefix'-errors" for the error "p"
- // "'prefix'-string" for string inputs
- // "'prefix'-int" for number inputs
- // "'prefix'-list" for list inputs
- // "'prefix'-boolean" for boolean inputs
- // "'prefix'-bits" for bit-flag inputs
- // "'prefix'-'name'" for individual inputs, where name is from 'getuiinfo'
- function uicreate(region, game, prefix = "bokpass", locale = "en",
- options = {}) {
- var root, container, label, cur, t;
- var i, j, cb, info, def, l, clear = false;
- if (!("columns" in options && typeof(options.columns) == "number" &&
- options.columns >= 1)) {
- if ("self" in window && "matchMedia" in self)
- for (options.columns = 1; options.columns < 4 && self.matchMedia(
- "(min-width: " + (24 * (options.columns + 1)) + "em)").matches;
- options.columns++) {}
- else
- options.columns = Math.min(Math.floor(
- window.innerWidth / window.devicePixelRatio / 360), 4);
- }
- info = getuiinfo(game, region, locale);
- root = document.createElement("div");
- root.className = prefix + "-game";
- root.appendChild(container = document.createElement("div"));
- container.className = prefix + "-password";
- if (!("nostyle" in options && options.nostyle)) {
- container.style.width = "100%";
- container.style.gridColumn = "span " + options.columns * 2;
- container.style.display = "flex";
- container.style.flexDirection = "row";
- }
- container.appendChild(cur = document.createElement("input"));
- cur.type = "button";
- cur.name = "encode";
- cur.value = "↱";
- if (!("nostyle" in options && options.nostyle)) {
- cur.style.font = "large mono";
- cur.style.minHeight = "1.5em";
- cur.style.aspectRatio = 1;
- }
- cur.addEventListener("click", function () {
- uiencode(root, game, locale, prefix);
- });
- container.appendChild(cur = document.createElement("input"));
- cur.type = "text";
- cur.name = "password";
- cur.value = "";
- if (!("nostyle" in options && options.nostyle)) {
- cur.style.font = "large mono";
- cur.style.flexGrow = 1;
- }
- cur.addEventListener("focusin", function (e) {
- var r = root.parentElement.parentElement;
- if (uiregion(root, locale).code == "jp")
- uiallowkeyboards(prefix, "jp64");
- else if (game == 4)
- uiallowkeyboards(prefix, "lk64");
- else
- uiallowkeyboards(prefix, "en64");
- });
- cur.addEventListener("focusout", function (e) {
- uiallowkeyboards(prefix);
- });
- container.appendChild(cur = document.createElement("input"));
- cur.type = "button";
- cur.name = "cycleoffset";
- cur.value = "↻";
- if (!("nostyle" in options && options.nostyle)) {
- cur.style.font = "large mono";
- cur.style.minHeight = "1.5em";
- cur.style.aspectRatio = 1;
- }
- cur.addEventListener("click", function () {
- uicycleoffset(root, game, locale);
- });
- container.appendChild(cur = document.createElement("input"));
- cur.type = "button";
- cur.name = "decode";
- cur.value = "↴";
- if (!("nostyle" in options && options.nostyle)) {
- cur.style.font = "large mono";
- cur.style.minHeight = "1.5em";
- cur.style.aspectRatio = 1;
- }
- cur.addEventListener("click", function () {
- uidecode(root, game, locale, prefix);
- });
- root.appendChild(container = document.createElement("div"));
- container.className = prefix + "-game-grid";
- if (!("nostyle" in options && options.nostyle)) {
- container.style.display = "grid";
- container.style.gridTemplateColumns =
- "repeat(" + options.columns + ", auto 1fr)";
- }
- cb = {
- string: function (info) {
- return function (e) {
- var r = root.parentElement.parentElement;
- switch (e.type) {
- case "focusin":
- if (uiregion(container, locale).code == "jp") {
- if (game == 4)
- uiallowkeyboards(prefix, "hiri", "kata", "en", "ext");
- else
- uiallowkeyboards(prefix, "hiri", "kata");
- } else if (game == 4)
- uiallowkeyboards(prefix, "en", "ext", "hiri", "kata");
- else
- uiallowkeyboards(prefix, "en");
- break;
- default:
- if ("invalid" in options && options.invalid)
- uivalidatestring(container, e.target, info, locale, e.type);
- break;
- }
- };
- },
- int: function (info) {
- return function (e) {
- if ("invalid" in options && options.invalid)
- uivalidateinteger(container, e.target, info, locale, e.type);
- };
- }
- };
- for (i = 0; i < info.length; i++) {
- if (info[i].name == "" || info[i].type == "break") {
- clear = true;
- continue;
- }
- if (info[i].type != "hidden") {
- container.appendChild(label = document.createElement("label"));
- if (clear || info[i].type == "bits") {
- label.className = prefix + "-clear";
- if (!("nostyle" in options && options.nostyle))
- label.style.gridColumnStart = 1;
- clear = false;
- }
- label.append(strings[locale].labels[info[i].name] + ":");
- if (!("nostyle" in options && options.nostyle))
- label.style.textAlign = "right";
- }
- def = "default" in info[i] ? info[i].default : undefined;
- def = typeof(def) == "object" && region in def ? def[region] : def;
- switch (info[i].type) {
- case "string":
- container.appendChild(cur = document.createElement("input"));
- label.htmlFor = cur.id = prefix + "-" + info[i].name;
- cur.className = prefix + "-string " + prefix + "-" + info[i].name;
- cur.type = "text";
- cur.name = info[i].name;
- t = cb[info[i].type](info[i]);
- cur.addEventListener("focusin", t);
- cur.addEventListener("focusout", t);
- cur.addEventListener("input", t);
- break;
- case "int":
- container.appendChild(cur = document.createElement("input"));
- label.htmlFor = cur.id = prefix + "-" + info[i].name;
- cur.className = prefix + "-int " + prefix + "-" + info[i].name;
- cur.type = "number";
- cur.name = info[i].name;
- cur.min = info[i].min;
- cur.max = info[i].max;
- if ("step" in info[i])
- cur.step = info[i].step;
- cur.value = typeof(def) == "undefined" ? cur.min : def;
- t = cb[info[i].type](info[i]);
- cur.addEventListener("input", t);
- cur.addEventListener("focusout", t);
- break;
- case "boolean":
- container.appendChild(cur = document.createElement("input"));
- label.htmlFor = cur.id = prefix + "-" + info[i].name;
- cur.className = prefix + "-boolean " + prefix + "-" + info[i].name;
- cur.type = "checkbox";
- cur.name = info[i].name;
- cur.defaultChecked = typeof(def) == "undefined" ? false : def;
- if (!("nostyle" in options && options.nostyle))
- cur.style.justifySelf = "start";
- break;
- case "regionlist":
- case "list":
- container.appendChild(cur = document.createElement("select"));
- label.htmlFor = cur.id = prefix + "-" + info[i].name;
- cur.className = prefix + "-list " + prefix + "-" + info[i].name;
- cur.name = info[i].name;
- def = typeof(def) == "undefined" ? 0 : def;
- l = region in info[i].values ? info[i].values[region] : info[i].values;
- for (j = 0; j < l.length; j++) {
- cur.appendChild(t = document.createElement("option"));
- t.selected = j == def;
- t.append(l[j]);
- }
- break;
- case "bits":
- def = typeof(def) == "undefined" ? 0 : def;
- container.appendChild(t = document.createElement("div"));
- t.id = prefix + "-" + info[i].name;
- t.className = prefix + "-bits " + prefix + "-" + info[i].name;
- if (!("nostyle" in options && options.nostyle)) {
- t.style.gridColumnEnd = "span " + (options.columns * 2 - 1);
- t.style.display = "grid";
- t.style.gridTemplateColumns =
- "repeat(" + Math.round(options.columns * 5 / 3) + ", 1fr auto)";
- }
- for (j = 0, l = info[i].values; j < l.length; j++) {
- t.appendChild(label = document.createElement("label"));
- label.append(l[j] + ":");
- if (!("nostyle" in options && options.nostyle))
- label.style.textAlign = "right";
- t.appendChild(cur = document.createElement("input"));
- cur.type = "checkbox";
- cur.name = info[i].name + "-" + j;
- label.htmlFor = cur.id = prefix + "-" + cur.name;
- cur.defaultChecked = !!(def & 1 << j);
- if (!("nostyle" in options && options.nostyle))
- cur.style.justifySelf = "start";
- }
- if (!("nostyle" in options && options.nostyle)) {
- // Pad the first row to help with alignment.
- for (; j < Math.round(options.columns * 5 / 3); j++) {
- t.appendChild(cur = document.createElement("br"));
- t.appendChild(cur = document.createElement("input"));
- cur.type = "checkbox";
- cur.disabled = true;
- cur.style.visibility = "hidden";
- }
- }
- break;
- case "hidden":
- container.appendChild(cur = document.createElement("input"));
- cur.className = prefix + "-hidden " + prefix + "-" + info[i].name;
- cur.type = "hidden";
- cur.name = info[i].name;
- cur.value = typeof(def) == "undefined" ? 0 : def;
- break;
- }
- }
- root.appendChild(cur = document.createElement("p"));
- cur.className = prefix + "-errors";
- if (!("nostyle" in options && options.nostyle))
- cur.style.textAlign = "center";
- uifindname(root, "region-name").addEventListener("change", function (e) {
- window.location.hash = prefix + "-" +
- uiupdateregion(root, game, locale, prefix) + game;
- });
- uiupdateregion(root, game, locale, prefix);
- return root;
- }
- // 'uitabs' creates lazily loaded "tabs" for each game , defaulting to 'game',
- // with 'uicreate' with the same arguments given and returns a "div" of class
- // "'prefix'-games" containing both the "tabs" and the "div" from 'uicreate'.
- // If the url has a fragment of "'prefix'-'region''game'" then it overrides
- // the default values with those and scrolls to the resulting "div" on "load".
- function uitabs(region, game = 1, prefix = "bokpass", locale = "en",
- options = {}) {
- var root, cur, t, i;
- var hash = document.location.hash.replace(/^#/, "");
- if (hash.indexOf(prefix) == 0) {
- hash = hash.slice(prefix.length).replace(/^[-_]+/, "");
- try {
- region = ["jp", "na", "eu"][parseregion(hash.replace(/\d+$/, "")) - 1];
- } catch (e) {}
- hash = hash.replace(/.*(\d+)$/, "$1");
- if (hash != "" && !isNaN(hash = parseInt(hash)) && hash >= 1 && hash <= 4)
- game = hash;
- hash = true;
- } else
- hash = false;
- root = document.createElement("div");
- root.className = prefix + "-games";
- root.appendChild(cur = document.createElement("select"));
- cur.name = "game-tabs";
- cur.className = prefix + "-tabs";
- cur.style.display = "block";
- cur.style.width = "100%";
- for (i = 0; i < strings[locale].labels["game-tabs"].length; i++) {
- cur.appendChild(t = document.createElement("option"));
- t.value = strings[locale].labels["game-tabs"][i].game;
- t.append(strings[locale].labels["game-tabs"][i].name);
- t.selected = t.value == game;
- }
- cur.addEventListener("change", (function () {
- var tabs = {};
- var cur = undefined;
- return function (e) {
- uiallowkeyboards(prefix);
- if (!(e.target.value in tabs))
- tabs[e.target.value] =
- uicreate(region, parseInt(e.target.value), prefix, locale,
- options);
- if (typeof(cur) == "undefined")
- root.appendChild(tabs[e.target.value]);
- else
- root.replaceChild(tabs[e.target.value], cur);
- cur = tabs[e.target.value];
- };
- })());
- cur.dispatchEvent(new Event("change", {bubble: true, cancelable: true}));
- if (hash)
- window.addEventListener("load", function () {root.scrollIntoView()});
- cur.addEventListener("change", function (e) {
- window.location.hash = prefix + "-" + uiregion(root, locale).code +
- e.target.value;
- });
- return root;
- }
- // 'keyboardpress' modifies the currently selected text input as if 'c' were
- // pressed. If 'c' is in 'keyboards.alias' it is changed before processing.
- // Supported special values are "ArrowUp", "ArrowDown", "ArrowLeft",
- // "ArrowRight", "Backspace", and "Delete".
- function keyboardpress(c) {
- var t = document.activeElement, s, e;
- c = c in keyboards.alias ? keyboards.alias[c] : c;
- if (!("selectionStart" in t))
- return;
- s = t.selectionStart;
- e = t.selectionEnd;
- switch (c) {
- case "ArrowUp":
- s = 0;
- break;
- case "ArrowDown":
- s = t.value.length;
- break;
- case "ArrowLeft":
- s--;
- break;
- case "ArrowRight":
- s = e + 1;
- break;
- case "Backspace":
- if (s != e)
- t.value = t.value.slice(0, s) + t.value.slice(e);
- else if (t.selectionStart > 0)
- t.value = t.value.slice(0, --s) + t.value.slice(e);
- break;
- case "Delete":
- if (t.selectionStart != t.selectionEnd)
- t.value = t.value.slice(0, s) + t.value.slice(e);
- else if (t.selectionStart > 0)
- t.value = t.value.slice(0, s++) + t.value.slice(e + 1);
- break;
- default:
- t.value = t.value.slice(0, s++) + c + t.value.slice(e);
- break;
- }
- t.setSelectionRange(s, s);
- t.dispatchEvent(new Event("input", {bubble: true, cancelable: true}));
- }
- // 'keyboardcreate' initializes a grid of buttons from 'keys' that call
- // 'keyboardpress' with their value.
- // 'keys' is an array of strings, or the name of a keyboard in 'keyboards',
- // If a key is a string in 'keyboards.expand' it is expanded to fill the grid
- // columns.
- function keyboardcreate(keys, prefix = "bokpass") {
- var root, cur;
- var max, diff, exp = [];
- var col, i, j, k, span;
- var cb = function (e) {
- keyboardpress(e.target.value)
- e.preventDefault();
- };
- if (typeof(keys) == "string" && keys in keyboards)
- keys = keyboards[keys];
- root = document.createElement("div");
- root.className = prefix + "-keyboard";
- root.style.display = "grid";
- root.style.marginLeft = root.style.marginRight = "auto";
- root.style.width = "fit-content";
- for (i = 0, max = 1; i < keys.length; i++) {
- max = keys[i].length > max ? keys[i].length : max;
- for (j = 0, exp[i] = 0; j < keys[i].length; j++)
- if (keys[i][j] in keyboards.expand && keyboards.expand[keys[i][j]])
- exp[i]++;
- }
- root.style.gridTemplateColumns = "repeat(" + max + ", 1.75em)";
- root.style.gridTemplateRows = "repeat(" + keys.length + ", 1.75em)";
- for (i = 0; i < keys.length; i++) {
- diff = max - keys[i].length;
- for (j = 0, col = 1, k = 0; j < keys[i].length; j++, col += span) {
- if (keys[i][j] == "") {
- span = 1;
- continue;
- }
- root.appendChild(cur = document.createElement("input"));
- cur.type = "button";
- cur.value = keys[i][j];
- cur.style.font = "large mono";
- cur.style.gridRow = i + 1;
- span = 1;
- if (keys[i][j] in keyboards.expand && keyboards.expand[keys[i][j]])
- span += (diff / exp[i] | 0) + (++k == exp[i] ? diff % exp[i] : 0);
- if (span == 1)
- cur.style.gridColumn = col;
- else
- cur.style.gridColumn = col + " / span " + span;
- cur.addEventListener("mousedown", cb);
- }
- }
- return root;
- }
- // 'keyboardtabs' creates lazily loaded "tabs" for each keyboard with 'prefix'
- // and 'locale' and returns a "div" of class "'prefix'-keyboards" containing
- // both the "tabs" and the "div" from 'keyboardcreate'.
- function keyboardtabs(prefix = "bokpass", locale = "en") {
- var root, cur, t;
- var i;
- root = document.createElement("div");
- root.className = prefix + "-keyboards";
- root.appendChild(cur = document.createElement("select"));
- cur.name = "keyboard-tabs";
- cur.className = prefix + "-tabs";
- cur.style.display = "block";
- cur.style.width = "100%";
- for (i = 0; i < strings[locale].labels["keyboard-tabs"].length; i++) {
- cur.appendChild(t = document.createElement("option"));
- t.value = strings[locale].labels["keyboard-tabs"][i].key;
- t.append(strings[locale].labels["keyboard-tabs"][i].name);
- t.selected = i == 0;
- }
- cur.addEventListener("change", (function () {
- var tabs = {};
- var cur = undefined;
- return function (e) {
- if (!(e.target.value in tabs))
- tabs[e.target.value] = keyboardcreate(e.target.value, prefix);
- if (typeof(cur) == "undefined")
- root.appendChild(tabs[e.target.value]);
- else
- root.replaceChild(tabs[e.target.value], cur);
- cur = tabs[e.target.value];
- };
- })());
- cur.dispatchEvent(new Event("change"));
- return root;
- }
- // 'uicreateall' returns a "div" with class 'prefix' containing the values
- // from calling 'uitabs' and 'keyboardtabs'.
- function uicreateall(region, game = 1, prefix = "bokpass", locale = "en",
- options = {}) {
- var root = document.createElement("div");
- root.class = prefix;
- root.id = prefix;
- root.appendChild(uitabs(region, game, prefix, locale, options));
- root.appendChild(keyboardtabs(prefix, locale));
- return root;
- }
- return {
- dec: { b64: decb64, str: decstr, pass: decpass },
- enc: { b64: encb64, str: encstr, pass: encpass },
- get: {
- int: getint, str: getstr, keys: getkeys, key: getkey, dict: getdict
- },
- put: { int: putint, str: putstr, dict: putdict },
- checksum: checksum,
- xor: xor,
- tabs: { ui: uitabs, kbd: keyboardtabs },
- create: { ui: uicreate, kbd: keyboardcreate, all: uicreateall }
- };
- })();
|