Если сейчас приехать в пункт приема металлолома, то можно обнаружить просто огромные кучи различных телефонов и прочих электронных отходов, которые стоят под открытым небом и ждут, когда придёт их черёд окончательного разложения.
Однако при ближайшем рассмотрении выясняется, что многие девайсы оказываются полностью рабочими даже после недельного лежания под палящим солнцем и проливными дождями, а сдали их в чермет по причинам не нужен, надоел, купил новый и т. п.
Я не считаю это правильным, ведь даже в простые кнопочные звонилки имеется возможность вдохнуть новую жизнь, если знать один интересный, но малоизвестный факт: для них можно писать нативные приложения на C и использовать железо телефона в своих целях.
А это, на минуточку, как минимум: дисплей с подсветкой, вибромотор, динамик, клавиатура и GSM-радиомодуль с возможностью выхода в сеть. Сегодня мы с вами: узнаем, на каких аппаратных платформах работают китайские телефоны, какие существуют программные платформы и где взять для них SDK, а в практической части мы напишем 2D-игру с нуля, которая будет работать на многих китайских кнопочниках. Интересно? Тогда жду вас под катом!
- Не J2ME едины
- О китайских копиях телефонов в России
- Возможности и ограничения
- Проблемы с совместимостью
- Платформы и технологии в ранних китайских телефонах
- Особенности Mythroad
- Поддержка подделок и китайских телефонов
- MediaTek WRE (VXP)
- Недостатки платформы WRE
- Поддержка устройств
- Совместимость и проблемы
- MRE на кнопочных телефонах
- Чипсеты и характеристики китайских телефонов
- Описание игры
- Кроссплатформенный рантайм
- Реализация на платформе Windows
- Точка входа и инициализация
- Отрисовка спрайтов
- Обработка ввода
- Реализация на платформе MRP
- Что у нас получилось?
- Возможно, захочется почитать и это
Не 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 было доступно только компаниям из Китая, и не все приложения могли быть запущены на конкретном устройстве из-за проблем совместимости.

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

Чтобы насладиться всем функционалом у некитайских производителей, нужно было огромное желание ковыряться в настройках и коде телефона.
Платформы и технологии в ранних китайских телефонах
В ранних китайских телефонах использовалась платформа 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 годов.
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. Средние характеристики устройств были следующими:
| Чипсет | Процессор | Операционная система | Экран | Камера |
|---|---|---|---|---|
| MT6225 | ARM926EJ-S | — | 2.2 дюйма | 0.3 Мп |
| SC6520 | Cortex-A5 | — | 2.4 дюйма | 0.3 Мп |
| Coolsand | — | — | 2.0 дюйма | 0.3 Мп |

Однако в рамках данной статьи мы не будем ограничиваться лишь теорией и на практике напишем примитивную 2D-игрушку, которая будет работать сразу на трех платформах без каких-либо изменений в коде самой игры: Windows, MRP (Mythroad) и VXP.
Но для того, чтобы достигнуть такого уровня абстракции от платформы, нам необходимо написать рантайм, который оборачивает все необходимые платформозависимые функции для нашей игры.
Описание игры
Игрушка будет простой: 2D скролл-шутер с видом сверху, а-ля Asteroids. Летаем по космосу, и стреляем по враждебным корабликам, стараясь не попасть под вражеские лазеры.
Кроссплатформенный рантайм
Итак, что нам необходимо от абстракции для такой простой игры? Примерно такого набора функций хватит для нашей игры:
- Инициализация рендерера.
- Загрузка текстур.
- Отрисовка спрайтов.
- Обработка ввода пользователя.
Реализация на платформе Windows
На десктопе у нас будет фиксированное окно 240×320, в качестве GAPI будет использоваться аппаратно-ускоренный OpenGL, а для обработки ввода будет использоваться классически GetAsyncKeyState.
Точка входа и инициализация
// ...Отрисовка спрайтов
// ...Обработка ввода
// ...Реализация на платформе MRP
Перейдем к реализации рантайма для первой китайской платформы — MRP. Я использую нативное API платформы для рисования спрайтов, из-за особенностей работы софтварного билдера.

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 пользователей.




