Колись вже писав був статтю про локалізацію текстур у грі. На той момент треба було чік-чік і в продакшн, тому ми там чимось щось кудись експортували, моєю тулою обробили, потім назад якось запхали — доволі незручний процес.
Згодом зʼявився настрій то все покращити. А для цього треба розібратися, як у ресурсах гри що зберігається. На щастя основну роботу зворотної розробки вже проробили модери й навіть документували структуру файлів, тож мені лишалося цим скористатися.
Отже, в старих іграх на рушії Infinity Engine — Baldurʼs Gate, Planescape: Torment тощо — структура приблизно така: є key-файл (зазвичай називається
Але жорсткі диски в компах раніше були доволі повільні, а інколи й доволі малі, тому в певних випадках доводилося грати зі вставленим CD, який ще повільніший. Ну а файлові системи на вінді й зараз не фонтан у сенсі швидкодії. Тому читання такої кількості дрібних файлів напряму з жорсткого диска — це вирок. Натомість краще мати меншу кількість великих файлів. Саме так розробники й зробили (та й досі роблять).
Отож key-файл — це бінарний формат, який описує всі ресурси, а також де саме вони лежать. А лежать вони в BIF-файлах. Кожний BIF — теж бінарний: фактично архів, у який напаковані різні ресурси, тобто файли й тайлсети. Розробники їх згрупували якось для зручності.
Я вже мільйон разів писав, як мені подобається Kaitai Struct🏗 , тож не обійшлося без нього й цього разу. Писати бінарний парсер по готових спеках — якесь дивне задоволення, трохи медитативний процес навіть. Сидиш ото, пиришся в hex-редактор, щоб переконатися, що ніде не сплутав big-endian з little-endian — кайф! Хоча цього разу парсери конкретно для key та BIF сів писати мій дружбан.
А мені ж треба було вже працювати з конкретним типом ресурсів. В ідеалі я хотів би читати їх прямо з key+bif-файлів так само як це робить гра, але на той момент єдиним виходом було експортувати їх вручну іншим інструментом в локальну файлову систему. Постає питання: як написати код так, щоб потім не треба було його адаптувати?
Я пишу на🦶 і спочатку думав абстрагувати якось процес читання в якийсь
Тут я натрапив на лібу spf13/afero, яка ніби трохи спрощувала все це. Тож я просто завʼязався на інтерфейс😎
Повільнувато трохи тільки😅 Наприклад, видобування ~1200 файлів на 25 МБ загалом у мене на M1 Max ішло 2,5 хвилини 😂 Довелося зайнятися профілюванням, яке, маю зазначити, в Go дуже легко додати в програму! Посиділи трохи з тим же друганом, знайшли найболючіші місця, і тепер видобування всіх ресурсів на майже 2 ГБ загалом триває близько 12 секунд.
У підсумку скажу (знову) пару слів про Go💻 : я ще починаючи з торішнього Advent of Code писав, що мова мені не подобається. І в принципі це досі так. Вона якось деревʼяно відчувається, немає в ній наче фану зовсім. Але наскільки ж легко й зручно в ній доводити справу до кінця! От просто сідаєш і пишеш без виїбонів, а воно потім працює, так ще й компілюється в один бінарь для будь-якої системи. І тулінг зручний. Можливо, це наразі єдина розробка від ґуґла, яку я поважаю.
Згодом зʼявився настрій то все покращити. А для цього треба розібратися, як у ресурсах гри що зберігається. На щастя основну роботу зворотної розробки вже проробили модери й навіть документували структуру файлів, тож мені лишалося цим скористатися.
Отже, в старих іграх на рушії Infinity Engine — Baldurʼs Gate, Planescape: Torment тощо — структура приблизно така: є key-файл (зазвичай називається
chitin.key) і є тека data з низкою BIF-файлів (не дуже багато — штук 80, наприклад). Але фактично рушій працює з конкретними ресурсами: маю на увазі всілякі скрипти, зображення, описи анімацій, діалогів, звуки тощо. От їх якраз купа. Наприклад, у першій Baldurʼs Gate їх 37341 штука. Але жорсткі диски в компах раніше були доволі повільні, а інколи й доволі малі, тому в певних випадках доводилося грати зі вставленим CD, який ще повільніший. Ну а файлові системи на вінді й зараз не фонтан у сенсі швидкодії. Тому читання такої кількості дрібних файлів напряму з жорсткого диска — це вирок. Натомість краще мати меншу кількість великих файлів. Саме так розробники й зробили (та й досі роблять).
Отож key-файл — це бінарний формат, який описує всі ресурси, а також де саме вони лежать. А лежать вони в BIF-файлах. Кожний BIF — теж бінарний: фактично архів, у який напаковані різні ресурси, тобто файли й тайлсети. Розробники їх згрупували якось для зручності.
Я вже мільйон разів писав, як мені подобається Kaitai Struct
А мені ж треба було вже працювати з конкретним типом ресурсів. В ідеалі я хотів би читати їх прямо з key+bif-файлів так само як це робить гра, але на той момент єдиним виходом було експортувати їх вручну іншим інструментом в локальну файлову систему. Постає питання: як написати код так, щоб потім не треба було його адаптувати?
Я пишу на
Reader абощо, а потім передавати його у свої обробники всюди. Але це якось кволо. До того ж якщо подумати, будь-який файл — це вже Reader, а точніше io.Reader. Тож гіпотетично можна написати свою імплементацію. Правда, виявилося, що для Kaitai одного io.Reader замало — треба ще io.Seeker, щоб можна було читати з будь-якого офсета.Тут я натрапив на лібу spf13/afero, яка ніби трохи спрощувала все це. Тож я просто завʼязався на інтерфейс
afero.Fs усюди у своєму коді, і на той момент працював з локальною файлухою. А згодом написав свою «віртуальну» файлову систему, яка дає змогу працювати з усіма запакованими ресурсами гри «прозоро» для користувача, хоча насправді я читаю їх прямо з BIF-файлів без проміжного видобування. І вуаля: підмінив одну фс на іншу, а воно досі працює Повільнувато трохи тільки
У підсумку скажу (знову) пару слів про Go
Please open Telegram to view this post
VIEW IN TELEGRAM
Telegram
Cіпласпластик
Два з половиною роки роботи, мільйон перекладених слів… І все ж ледь не перше, що бачить гравець — це головне меню іноземною мовою 🙁
Непорядок? І ми так подумали, тож взялися за локалізацію написів.
Як ми здолали цю головоломку, читайте в статті від самих…
Непорядок? І ми так подумали, тож взялися за локалізацію написів.
Як ми здолали цю головоломку, читайте в статті від самих…
👍11🤣1
На торішньому Advent of Code я з певним успіхом намагався розвʼязувати задачі щодня різними мовами, зокрема на Haskell 💻 , 💻 , Nushell 🆕 , Swift 🕊 , Python 💻 , Red 🔺 , 💻 , Nim 👑 , 🦶 , Elixir 💻 , Dart 💻 , SWI-Prolog 🦉 , 💻 , Janet 👩🦰 , Crystal 🔮 , 💻 й 🕸 .
Не певен, чи цього року я робитиму так само, але накидайте мені, може, ще пропозицій? Напишіть, яку мову програмування на вашу думку варто спробувати й чому✍️ 🧐
Не певен, чи цього року я робитиму так само, але накидайте мені, може, ще пропозицій? Напишіть, яку мову програмування на вашу думку варто спробувати й чому
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🤣1
Please open Telegram to view this post
VIEW IN TELEGRAM
Steampowered
Mass Effect™ Legendary Edition on Steam
The Mass Effect™ Legendary Edition includes single-player base content and over 40 DLC from the highly acclaimed Mass Effect, Mass Effect 2, and Mass Effect 3 games, including promo weapons, armors, and packs — remastered and optimized for 4K Ultra HD.
До речі про Steam 💨
Днями вирішив зробити собі невеличку базу ігор, яку заповнювати, звісно, руками не будеш. Так виявилося, що у стіма є відкрита API-шка!
Можна отримати основну інформацію про гру, знаючи її ідентифікатор. Останній можна подивитися, наприклад, в урлі сторінки в магазині. І потім хоп-хоп — і вся інформація легко дістається:
І далі вже можна дивитися, шо там:
Або навіть отак:
(Підіть, може, хоча б ШБТ💙 попросіть у дискорді, щоб вони переклали… Ех.)
Я собі так навіть знятки екрана всі стягнув. Важать вони, до речі, доволі дофіга: на ≈150 ігор вийшло близько 900 МБ🤯 Треба, мабуть, у WebP конвертнути.
Апішка має ліміти на кількість запитів за певний проміжок часу. Скільки точно, я не знаю. Якщо робити десь два запита на секунду, то наче не рубає зʼєднання, але якщо флудити частіше, то сервер починає кидати якусь з 500-х помилок, здається. За який час його попускає, я теж хз — просто VPN перемкнув собі, і далі запрацювало норм.
Уважний читач (так, ти🫵 ) міг побачити, що URL-параметр в запиті називається
Тепер можна нагенерувати собі сторінок в обсідіані або кудись ще покласти😎
Днями вирішив зробити собі невеличку базу ігор, яку заповнювати, звісно, руками не будеш. Так виявилося, що у стіма є відкрита API-шка!
Можна отримати основну інформацію про гру, знаючи її ідентифікатор. Останній можна подивитися, наприклад, в урлі сторінки в магазині. І потім хоп-хоп — і вся інформація легко дістається:
let gameId = '1328670'
let info = http get https://store.steampowered.com/api/appdetails?appids=($gameId)
| get $gameId
| get data
І далі вже можна дивитися, шо там:
> $info.name
Mass Effect™ Legendary Edition
> $info.genres
╭───┬────┬─────────────╮
│ # │ id │ description │
├───┼────┼─────────────┤
│ 0 │ 1 │ Action │
│ 1 │ 3 │ RPG │
╰───┴────┴─────────────╯
> $info.ratings.pegi
╭──────────────┬──────────────╮
│ rating │ 18 │
│ descriptors │ Violence │
│ │ Bad Language │
│ │ Gambling │
│ use_age_gate │ true │
│ required_age │ 18+ │
╰──────────────┴──────────────╯
Або навіть отак:
> $info.supported_languages | split words | 'Ukrainian' in $in
false # 😭
(Підіть, може, хоча б ШБТ
Я собі так навіть знятки екрана всі стягнув. Важать вони, до речі, доволі дофіга: на ≈150 ігор вийшло близько 900 МБ
Апішка має ліміти на кількість запитів за певний проміжок часу. Скільки точно, я не знаю. Якщо робити десь два запита на секунду, то наче не рубає зʼєднання, але якщо флудити частіше, то сервер починає кидати якусь з 500-х помилок, здається. За який час його попускає, я теж хз — просто VPN перемкнув собі, і далі запрацювало норм.
Уважний читач (так, ти
appids, а не appid. Однак ні — одразу декілька передавати не можна, бо тоді їхній сервер повертає HTTP 400. Єдиний виняток — це комбінація з фільтром price_overview, яка працює:> http get https://store.steampowered.com/api/appdetails?appids=1328670,1091500&filters=price_overview | transpose -d | rename id val | select id val.data.price_overview
╭───┬─────────┬────────────────────────────────╮
│ # │ id │ val.data.price_overview │
├───┼─────────┼────────────────────────────────┤
│ 0 │ 1328670 │ ╭───────────────────┬────────╮ │
│ │ │ │ currency │ EUR │ │
│ │ │ │ initial │ 5999 │ │
│ │ │ │ final │ 479 │ │
│ │ │ │ discount_percent │ 92 │ │
│ │ │ │ initial_formatted │ 59,99€ │ │
│ │ │ │ final_formatted │ 4,79€ │ │
│ │ │ ╰───────────────────┴────────╯ │
│ 1 │ 1091500 │ ╭───────────────────┬────────╮ │
│ │ │ │ currency │ EUR │ │
│ │ │ │ initial │ 5999 │ │
│ │ │ │ final │ 5999 │ │
│ │ │ │ discount_percent │ 0 │ │
│ │ │ │ initial_formatted │ │ │
│ │ │ │ final_formatted │ 59,99€ │ │
│ │ │ ╰───────────────────┴────────╯ │
╰───┴─────────┴────────────────────────────────╯
Тепер можна нагенерувати собі сторінок в обсідіані або кудись ще покласти
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🤣1🤓1🫡1
#TIL на ґітгабі 🐈 в налаштуваннях можна ввімкнути режим високої контрастності. Значно ліпше виглядає!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤15😐9👍2🤡2👀1
Розкажу вам уберлайфхак для 💻 VS Code (як мінімум):
Виявляється, що та висота рядків, яку редактор якось обчислює «автоматично», — це повна хєрня. Не памʼятаю вже, чого я поліз дивитися налаштування, але якщо поставити для editor.lineHeight якесь адекватне значення, то раптом стає ЗНАЧНО ліпше🤩
Першу хвилину я через звичку ще сумнівався, але виграш очевидний: щільність інформації вища, очі важливий контекст захоплюють краще — в результаті мозку легше🧠
Виявляється, що та висота рядків, яку редактор якось обчислює «автоматично», — це повна хєрня. Не памʼятаю вже, чого я поліз дивитися налаштування, але якщо поставити для editor.lineHeight якесь адекватне значення, то раптом стає ЗНАЧНО ліпше
Першу хвилину я через звичку ще сумнівався, але виграш очевидний: щільність інформації вища, очі важливий контекст захоплюють краще — в результаті мозку легше
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
❤14👍13🤣1