На помойку? никак нет! пишем нативные приложения для дешевых китайских телефонов

На помойку? никак нет! пишем нативные приложения для дешевых китайских телефонов Моноблоки

Если сейчас приехать в пункт приема металлолома, то можно обнаружить просто огромные кучи различных телефонов и прочих электронных отходов, которые стоят под открытым небом и ждут, когда придёт их черёд окончательного разложения.

Однако при ближайшем рассмотрении выясняется, что многие девайсы оказываются полностью рабочими даже после недельного лежания под палящим солнцем и проливными дождями, а сдали их в чермет по причинам не нужен, надоел, купил новый и т. п.

Я не считаю это правильным, ведь даже в простые кнопочные звонилки имеется возможность вдохнуть новую жизнь, если знать один интересный, но малоизвестный факт: для них можно писать нативные приложения на C и использовать железо телефона в своих целях.

А это, на минуточку, как минимум: дисплей с подсветкой, вибромотор, динамик, клавиатура и GSM-радиомодуль с возможностью выхода в сеть. Сегодня мы с вами: узнаем, на каких аппаратных платформах работают китайские телефоны, какие существуют программные платформы и где взять для них SDK, а в практической части мы напишем 2D-игру с нуля, которая будет работать на многих китайских кнопочниках. Интересно? Тогда жду вас под катом!

Не J2ME едины

Думаю, многие мои читатели помнят о такой платформе, как J2ME. Java-приложения стали фактически основной возможностью расширения функционала телефонов в 2000-х годах. API для них был достаточно хорошо стандартизировано, программы не зависели от архитектуры процессора и ОС устройства, а порог вхождения для написания собственных приложений был довольно низкий и даже новички могли за пару дней написать свою игрушку или какое-нибудь GUI-приложение!

На помойку? никак нет! пишем нативные приложения для дешевых китайских телефонов

Однако не одним J2ME мы были едины: существовало множество платформ, которые так или иначе пытались занять нишу Java на рынке. Некоторые из них я упоминал в своей прошлой статье о написании 3D-игры под Sony Ericsson с нуля: например, была такая платформа на телефонах Sony Ericsson серии T, как Mophun, а CDMA-телефонами с чипсетами Qualcomm использовалась нативная платформа BREW. Пожалуй, я не буду упоминать о .sis и .cab — поскольку это форматы нативных приложений для смартфонов, а не простых фичефонов.

На помойку? никак нет! пишем нативные приложения для дешевых китайских телефонов

Игра для Mophun3D

О китайских копиях телефонов в России

В какой-то момент, ближе к 2006-2007 году, прилавки российских официальных ритейлеров (по большей части это были телефоны Fly) и неофициальных продавцов на рынках заполонили различные китайские телефоны. Эти телефоны предлагали невиданный функционал за копейки и визуально напоминали флагманские модели известных брендов.

Одним из самых популярных таких телефонов была Nokla TV E71/E72, выпущенная примерно в 2008 году и производимая до 2011 года. За 2-3 тысячи рублей пользователь получал здоровый 2.4 дисплей с разрешением 240×320, с тачскрином, гироскопом, огромным громким динамиком, поддержкой SD-карточек до 32Гб, фронтальной камерой и премиальным дизайном с вставками из алюминия.

Возможно, существовали даже полные копии существующих устройств от Nokia. Отличительной чертой было то, что китайцы предпочитали подделывать массовые модели на S40, пытаясь отхватить свою долю рынка у Nokia.

Возможности и ограничения

Многие пользователи не знали, что эти девайсы не только поддерживали сторонние приложения, но и способны были запускать настоящие нативные программы, написанные на полноценном C! Однако для этого необходимо было вводить специальный инженерный код в номеронабирателе и предварительно копировать приложение в нужную папку.

Платформа китайских устройств была предназначена для внутреннего рынка, что создавало ограничения в использовании. SDK было доступно только компаниям из Китая, и не все приложения могли быть запущены на конкретном устройстве из-за проблем совместимости.

Telephone Image

Проблемы с совместимостью

Были серьезные проблемы совместимости при запуске некоторых приложений на китайских копиях телефонов. Platform not originally designed for use by non-Chinese companies.

Nokia Copy

Чтобы насладиться всем функционалом у некитайских производителей, нужно было огромное желание ковыряться в настройках и коде телефона.

Платформы и технологии в ранних китайских телефонах

В ранних китайских телефонах использовалась платформа Mythroad (MRP, MiniJ) от китайской компании SkyWorks. Эта платформа лицензировалась производителям чипсетов и поддерживалась на телефонах с чипсетами MediaTek, Spreadtrum, MStar (и возможно Coolsand).

Особенности Mythroad

Платформа Mythroad предоставляла API для работы с железом телефона, разработки UI-приложений и игр. Кроме того, Mythroad позволяла хранить ресурсы в одном бинарнике с основной программой и имела какой-то интерпретируемый язык помимо возможности запуска нативного кода.

Для работы приложений на платформе Mythroad необходимо было скопировать менеджер приложений dsm_gm.mrp и игру в папку mythroad во внутренней памяти устройства или на флэшке, а затем набрать в номеронабирателе код *#220807#, иногда при отключенной первой SIM-карте.

Поддержка подделок и китайских телефонов

Mythroad поддерживалась на большинстве подделок под брендовые устройства Nokia, Sony Ericsson, Samsung, iPhone и на многих китайских кнопочных телефонах 2008-2010 годов.

Про моноблоки:  Моноблок леново 255

MediaTek WRE (VXP)

Ближе к 2010 году MediaTek разработала свою собственную платформу WRE (VXP), которая должна была заменить MRP. WRE была гораздо шире с точки зрения функционала, предоставляя доступ к UART, и её API был удобен для программистов. SDK был свободно доступен для всех.

Недостатки платформы WRE

Один из недостатков платформы WRE был связан с тем, что приложения без подписи привязывались к IMSI симки в устройстве, что требовало переподписания под каждую конкретную SIM или патчинг дампа оригинальной прошивки.

Поддержка устройств

Платформа WRE была поддерживаема на многих кнопочных телефонах и смарт-часиках 2010-2020 годов, включая телефоны Nokia, DNS, DEXP, Explay и т. д.

Совместимость и проблемы

Хотя для запуска приложений на платформе WRE было достаточно выбрать файл с разрешением VXP в проводнике и запустить его, некоторые проблемы совместимости всё же возникали. Например, при запуске VXP для версии 2.0 и выше мог появиться белый экран.

MRE на кнопочных телефонах

Далеко не все кнопочные телефоны поддерживают MRE, необходимо смотреть от устройства к устройству.

Чипсеты и характеристики китайских телефонов

Большинство китайских кнопочных телефонов работает на базе одних и тех же чипсетов. В конце нулевых чаще всего использовались чипсеты MT6225, SC6520 и некоторые чипы от Coolsand. Средние характеристики устройств были следующими:

ЧипсетПроцессорОперационная системаЭкранКамера
MT6225ARM926EJ-S2.2 дюйма0.3 Мп
SC6520Cortex-A52.4 дюйма0.3 Мп
Coolsand2.0 дюйма0.3 Мп

Таблица с характеристиками китайских телефонов

Однако в рамках данной статьи мы не будем ограничиваться лишь теорией и на практике напишем примитивную 2D-игрушку, которая будет работать сразу на трех платформах без каких-либо изменений в коде самой игры: Windows, MRP (Mythroad) и VXP.

Но для того, чтобы достигнуть такого уровня абстракции от платформы, нам необходимо написать рантайм, который оборачивает все необходимые платформозависимые функции для нашей игры.

Описание игры

Игрушка будет простой: 2D скролл-шутер с видом сверху, а-ля Asteroids. Летаем по космосу, и стреляем по враждебным корабликам, стараясь не попасть под вражеские лазеры.

Кроссплатформенный рантайм

Итак, что нам необходимо от абстракции для такой простой игры? Примерно такого набора функций хватит для нашей игры:

  1. Инициализация рендерера.
  2. Загрузка текстур.
  3. Отрисовка спрайтов.
  4. Обработка ввода пользователя.

Реализация на платформе Windows

На десктопе у нас будет фиксированное окно 240×320, в качестве GAPI будет использоваться аппаратно-ускоренный OpenGL, а для обработки ввода будет использоваться классически GetAsyncKeyState.

Точка входа и инициализация

// ...

Отрисовка спрайтов

// ...

Обработка ввода

// ...

Реализация на платформе MRP

Перейдем к реализации рантайма для первой китайской платформы — MRP. Я использую нативное API платформы для рисования спрайтов, из-за особенностей работы софтварного билдера.

MRP Platform

SDK для MRE можно найти здесь (SKYSDK.zip): оно уже пропатчено от необходимости покупки лицензии. MRP не развивается более 10 лет, поэтому, его можно считать Abandonware. Компилятор находится в compiler/mrpbuilder.NET1.exe.

За китайские SDK в публичном доступе нужно поблагодарить пользователя 4pda AjlekcaHgp MejlbHukoB, который раздобыл их на всяких csdn и выложил в свободный доступ.

У MRP собственная система сборки, основанная на конфигурациях. Поскольку MRP может работать на устройствах с разными платформами и размерами дисплеев, под каждую можно настроить свой конфиг, который пережмет ресурсы в нужный формат. Дабы ничего не ломать, я заюзал абсолютные пути:

Начинаем с функций обработки событий и инициализации, которые вызывает рантайм при старте приложения: mrc_init вызывается при старте приложения, а mrc_event при возникновении события. Вся инициализация очень простая: создаём таймер для обновления и перерисовки состояния игры и вызываем инициализацию игры:

С вводом тоже никаких проблем нет, нажатия кнопок прилетают как события в mrc_event. Переводим кейкоды MRE в наши кейкоды и сохраняем их состояние:

Опять же, отлаживать MRP-приложение под реальным устройством проблематично, поэтому платформозависимый код должен быть минимальным. Кроме того, обратите внимание, что некоторые функции в MRP зависят от библиотек-плагинов. Линкер слинкует вашу программу, но на реальном устройстве их вызов вывалится в SIGSEGV и софтресет устройства. Также нельзя использовать ничего из стандартной библиотеки именно в стандартных заголовочниках (т. е. stdlib.h, string.h и т. д.), часть стандартной библиотеки реализовывается MRP и дефайнится в mrc_base.h

Что интересно, защиты памяти толком нет. Если приложение падает в SIGSEGV или портит память — систему, судя по всему, ребутит Watchdog. Защиты памяти никакой, можно напрямую читать и писать в память ядра, а также писать в регистры периферии чипсета. jpegqs, покумекаем над этим? 🙂

Переходим к рендереру. Тут буквально две функции, gClearScreen очищает экран, а gDrawBitmap рисует произвольный спрайт с форматом пикселя RGB565. В качестве ROP используется BM_TRANSPARENT — таким образом, mrc_bitmapShowEx будет использовать левый верхний пиксель в качестве референсного цвета для реализации прозрачности без альфа-блендинга.

Да, всё вот так просто. Рантайм теперь запускается на реальных китайских девайсах и работает стабильно.

VXP сам по себе более функционален чем MRE, поскольку ориентирован исключительно на телефоны с чипсетами MediaTek. Но что самое приятное — есть доступ к уарту без каких либо костылей! То есть, если сделать GPIO-мост на условной ESP32, то мы можем получить готовый мощный МК с клавиатурой, кнопками, дисплеем, звуком и т. д. Звучит не хило, да? Кроме того, у нас есть доступ и к BT, и к GPRS, и к SMS без каких либо ограничений.

Про моноблоки:  Шины моноблок что это такое

На помойку? никак нет! пишем нативные приложения для дешевых китайских телефонов

Кроме того, на некоторых девайсах (в основном, фирменных Nokia а-ля 225), прошивка требует подписи у всех бинарников, либо если бинарник отладочный, то должна быть привязка к конкретному IMSI.

К тому же, каждая программа должна фиксированно указывать в заголовке, сколько Heap-памяти ей необходимо выделить. Оптимальный вариант — ~500Кб, тогда приложение запустится вообще на всех MRE-телефонах.

На помойку? никак нет! пишем нативные приложения для дешевых китайских телефонов

Зато у VXP есть адекватный симулятор под Windows. Но зачем он нам, если у нас порт игры под Win32 есть? 🙂

На помойку? никак нет! пишем нативные приложения для дешевых китайских телефонов

Начинаем с инициализации приложения. В процессе вызова точки входа, приложение должно назначить обработчики системных событий, коих бывает несколько. Для обработки ввода и базовых событий хватает всего три: sysevt (события окна), keyboard (физическая клавиатура. Есть полная поддержка QWERTY-клавиатур), pen (тачскрин).

Переходим к обработчику системных событий. Обратите внимание, что MRE-приложения могут работать в фоне, из-за чего необходимо ответственно подходить к созданию и освобождению объектов. Что важно усвоить с самого начала — в MRE нет понятия процессов и защиты памяти, как на ПК и полноценных смартфонах. Любая программа может попортить память или стек ОС, более того, программа использует аллокатор остальной системы, поэтому если ваша программа не «убирает» после себя, данные останутся в памяти со временем приведут к зависанию. Впрочем, WatchDog делает свою работу быстро и приводит телефон в чувство (софтресетом) за 1-2 секунды. Но как и в случае с MRE, есть приятный бонус: прямой доступ к регистрам чипсета 🙂

Переходим к обработке событий с кнопок. Тут всё абсолютно также, как и на MRE, лишь имена дейфанов поменялись 🙂

И наконец-то, к графике! Пожалуй, стоит сразу отметить, что более 20-30 FPS на большинстве устройств вы не получите даже с прямым доступом к фреймбуферу. Похоже, это связано с тем, что в MRE довольно замороченная графическая подсистема с поддержкой альфа-канала (только фиксированного во время вызова функции отрисовки картинки/примитивов, сам пиксельформат всегда RGB565) и нескольких слоев. Кроме того, похоже есть ограничения со стороны контроллера дисплея.

Софтварный вывод спрайтов

void vm_graphic_blt( VMBYTE * dst_disp_buf, VMINT x_dest, VMINT y_dest, VMBYTE * src_disp_buf, VMINT x_src, VMINT y_src, VMINT width, VMINT height, VMINT frame_index );

dst_disp_buf — это целевой RGB565-буфер. Логично предположить, что и src_disp_buf — тоже обычный RGB565-буфер! Но как бы не так. Документация крайне скудная, пришлось посидеть и покумекать, откуда в обычном 565 буфере возьмется индекс кадра. С подсказкой пришёл пользователь 4pda Ximik_Boda — он скинул структуру-заголовок, которая идёт перед началом каждого кадра. В документации об этом не сказано ровным счетом ничего!

На помойку? никак нет! пишем нативные приложения для дешевых китайских телефонов

На помойку? никак нет! пишем нативные приложения для дешевых китайских телефонов

Для реализации более-менее шустрого вывода графики, необходимо сначала создать канвас (фактически, Bitmap в MRE), создать и привязать к нему layer, получить указатель на буфер слоя и только потом скопировать туда нашу картинку. Да, вот так вот замороченно:

И только после этого всё заработало достаточно шустро 🙂 В остальном же платформа довольно неплохая. Да, без болячек не обошлось, но всё же перспективы вполне себе есть.

На данный момент, этого достаточно для нашей игры.

Рантайм у нас есть, а значит, можно начинать писать игрушку. Хоть пишем мы на Plain-C, я всё равно из проекта в проект использую +- одну и ту же архитектуру относительно системы сущностей, стейтов и т. п. Поэтому центральным объектом у нас станет CWorld, который хранит в себе на пулы с указателями на другие объектами в сцене, а также игрока и его состояние:

Система стейтов простая и понятная — фактически, между состояниями передавать ничего не нужно. При нажатии в главном меню на «старт», нам просто необходимо проинициализировать мир заново и начать геймплей, при смерти игрока — закинуть его обратно в состояние меню. Стейты представляют из себя три указателя на функции: переход (инициализация), обновление и отрисовка.

Поскольку мы хотим некоторой гибкости при создании новых классов противников, то вводим структуру CEnemyClass, которая описывает визуальную составляющую врагов и их флаги — могут ли они стрелять по игроку или просто летят вниз (астероиды), как они передвигаются (зигзагами например) и т. п.

А также описываем игрока:

Всё! Для текущего уровня реализации игры этого достаточно 🙂 Переходим к реализации игровой логики. Вообще, динамический аллокатор в играх для китайских платформ лучше использовать как можно меньше. Heap’а довольно мало (~600Кб), да и не совсем понятно, как этот аллокатор реализован, есть вероятность, что используется аллокатор и куча основной ОС.

Начинаем с реализации полёта кораблика. Для этого он должен реагировать на стрелки и не улетать за границы экрана, а ещё для красоты он должен «вылетать» из нижней границы экрана при старте игры:

Про моноблоки:  Моноблок компьютер с сенсорным компьютером

Переходим к динамическим пулам с объектами. Как вы уже заметили, их всего два — враги и летящие снаряды. Реализация спавна врагов/снарядов простая и понятная: мы обходим каждый элемент пула, если указатель на объект не-нулевой, значит объект всё ещё жив и используется на сцене. Если нулевой — значит ячейка свободна и можно заспавнить новый объект:

При обходе пула во время обновления кадра, мы обновляем состояние каждого объекта и если его функция Think вернула true, значит объект больше не нужен и его нужно удалить:

А вот и реализация Think:

Но кораблики должны же откуда-то появляться! Для этого у нас есть переменная nextSpawn, которая позволяет реализовать самый простой тип спавнера — относительно времени (или в нашем случае тиков):

Результат: мы уже можем полетать и поуворачиваться от вражеских корабликов!

Но для игры этого пока маловато. Давайте добавим возможность стрелять лазерами! Для этого реализуем обход пула снарядов и проверим на столкновение каждый заспавненный вражеский кораблик: если снаряд столкнулся с корабликом, то мы отнимем у кораблика HP, а снаряд — задеспавним. Если у кораблика осталось меньше или 0 HP, то в следующем кадре он будет убран из сцены.

Реализация стрельбы тоже совсем простая и также зависит от таймера:

Смотрим на результат:

Уже что-то напоминающее игру! Осталось лишь добавить подсчет очков, менюшку, разные виды противников, возможно какие-то бонусы и у нас будет готовая простенькая аркада. В целом, выше приведена достаточно неплохая архитектура для простых 2D-игр на Plain C. Фактически, она может быть хорошей базой и для ваших игр: в теме о китах на 4pda я встречал немало людей, которые банально не знали, с чего начать.

Что у нас получилось?

Но без тестов на реальных устройствах материал не был бы таким интересным! Поэтому давайте протестируем игру на двух реальных телефонах, как вы уже догадались, один — Nokla TV E71, а второй — клон Nokia 6700, который подарил мне мой читатель Никита.

На TV E71 игра идёт не сказать что очень бодро. Кадров 15 точно есть, что, учитывая разрешение 240×320, весьма неплохо для такого девайса.

На 6700,, даже учитывая более низкое разрешение — 176×220, дела примерно также — ~15FPS! Но поиграть всё равно можно. Уже хотите написать «автор наговнокодил, а теперь ноет из-за низкого FPS»? Ан-нет, я попробовал игры сторонних разработчиков — они идут примерно также 🙁 К сожалению, таковы аппаратные ограничения устройства.

Исходный код игры с Makefile’ами и файлами проектов для Visual Studio и MRELauncher доступны на моём GitHub. Свободно изучайте и используйте его в любых целях 🙂

Но в остальном же, демка получилась довольно прикольной, как и сам опыт программирования для китайских телефонов. В общем и целом, китайцы пытались максимально упростить API и привлечь разработчиков к своей платформе. Если ради примера взглянуть на API для Elf’ов на Motorola, можно ужаснуться от state-based архитектуры платформы P2K. А тут тебе init, event, draw — и всё!

Но популярности помешала непонятная закрытость платформы, костыльный запуск программ, отсутствие нормального симулятора. А ведь сколько фишек было: даже возможность писать и читать память ядра! А вы как считаете? Можно ли вдохнуть в китайские кнопочники новую жизнь, узнав о наличии возможности запуска нативного кода на них?

На помойку? никак нет! пишем нативные приложения для дешевых китайских телефонов

Крутые девайсы на фоне ковра, который старше автора в два раза. Всё как вы любите 🙂

P. S.: Друзья! Время от времени я пишу пост о поиске различных китайских девайсов (подделок, реплик, закосов на айфоны, самсунги, сони, HTC и т. п.) для будущих статей. Однако очень часто читатели пишут «где ж ты был месяц назад, мешок таких выбросил!», поэтому я решил в заключение каждой статьи вставлять объявление о поиске девайсов для контента. Есть желание что-то выкинуть или отправить в чермет? Даже нерабочую «невключайку» или полурабочую? А может, у этих девайсов есть шанс на более интересное существование! Смотрите в соответствующем посте, что я делаю с китайскими подделками на айфоны, самсунги, макбуки и айпады! Да и чего уж там говорить: эта статья уже сама по себе весьма наглядный пример!

Понравился материал? У меня есть канал в Телеге, куда я публикую бэкстейдж со статей, всякие мысли и советы касательно ремонта и программирования под различные девайсы, а также вовремя публикую ссылки на свои новые статьи. 1-2 поста в день, никакого мусора!

Возможно, захочется почитать и это

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Да. Не знал о том, что под китайцев можно писать свои приложения, да ещё и нативные!

Да, но без примера реальных проектов, людей к платформе не привлечь. Ждём примера использования такого девайса в качестве МК!

Нет. Гамно, аффтар мудак.

Проголосовали 154 пользователя. Воздержались 25 пользователей.

Были ли у вас такие китайчики?

Проголосовали 175 пользователей. Воздержались 13 пользователей.

Оцените статью
Про моноблоки