Большинство игр под андроид, пишется с использованием OpenGL, в OpenGL разрешение задается функцией "glViewPort", ее и будем использовать. Наиболее оптимальным, функцию "glViewPort" размещать в функции "OnDrawFrame". Для этого, с помощью поиска, в папке "smali", которая находится в папке с распакованым апк-файлом, ищем файл, содержащий функцию "OnDrawFrame". Обычно этот файл имеет имя "GameRenderer.smali" или "[Название игры]Renderer.smali", в нашем случае это "SandstormRenderer.smali". Открываем его в блокноте, или другом текстовом редакторе, и находим в нем функцию "OnDrawFrame".
Вот фрагмент этой функции.
.method public onDrawFrame(Ljavax/microedition/khronos/opengles/GL10;)V
.locals 6
.parameter "gl"
.prologue
.line 174
const-wide/16 v0, 0x0
.line 177
.local v0, time:J
invoke-static {}, Ljava/lang/System;->currentTimeMillis()J
move-result-wide v0
.line 179
invoke-static {}, Lcom/gameloft/android/GAND/GloftMCHP/GLMediaPlayer;->update()V
.line 180
invoke-static {}, Lcom/gameloft/android/GAND/GloftMCHP/SandstormRenderer;->nativeRender()V
.line 186
const-wide/16 v2, 0x32
invoke-static {}, Ljava/lang/System;->currentTimeMillis()J
move-result-wide v4
sub-long/2addr v4, v0
sub-long v0, v2, v4
.line 188
const-wide/16 v2, 0x0
cmp-long v2, v0, v2
if-lez v2, :cond_0
.line 190
:try_start_0
invoke-static {v0, v1}, Ljava/lang/Thread;->sleep(J)V
:try_end_0
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
.line 193
:cond_0
:goto_0
return-void
.line 190
:catch_0
move-exception v2
goto :goto_0
.end method
В этот фрагмент кода необходимо добавить вызов функции "glViewPort", таким образом чтоб получилось, как в фрагменте кода ниже.
.method public onDrawFrame(Ljavax/microedition/khronos/opengles/GL10;)V
.locals 9
.parameter "gl"
.prologue
const/16 v8, 0x1E0
const/16 v7, 0x140
const/4 v6, 0x0
invoke-interface {p1, v6, v6, v8, v7}, Ljavax/microedition/khronos/opengles/GL10;->glViewport(IIII)V
.line 174
const-wide/16 v0, 0x0
.line 177
.local v0, time:J
invoke-static {}, Ljava/lang/System;->currentTimeMillis()J
move-result-wide v0
.line 179
invoke-static {}, Lcom/gameloft/android/GAND/GloftMCHP/GLMediaPlayer;->update()V
.line 180
invoke-static {}, Lcom/gameloft/android/GAND/GloftMCHP/SandstormRenderer;->nativeRender()V
.line 186
const-wide/16 v2, 0x32
invoke-static {}, Ljava/lang/System;->currentTimeMillis()J
move-result-wide v4
sub-long/2addr v4, v0
sub-long v0, v2, v4
.line 188
const-wide/16 v2, 0x0
cmp-long v2, v0, v2
if-lez v2, :cond_0
.line 190
:try_start_0
invoke-static {v0, v1}, Ljava/lang/Thread;->sleep(J)V
:try_end_0
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
.line 193
:cond_0
:goto_0
return-void
.line 190
:catch_0
move-exception v2
goto :goto_0
.end method
Мы добавили 3 константы(v6,v7,v8), присвоили им значение координат левого нижнего(v6;v6) и правого верхнего(v8;v7) угла экрана и передали их в функцию "glViewport". Я думаю понятно, почему для координат левого нижнего угла и для X и для Y используется одна и также константа v6? Так как и X и Y в этом углу равны между собой. Кроме этого изменяем 2-ю строчку в функции ".locals 6" на ".locals 9", это определяет количество констант/переменных используемых в функции, так как мы добавили 3 константы, то 6+3=9. Также обратите внимание, что имена констант (v6,v7,v8), не берутся случайно, а выбираются ориентируясь на уже используемые в функции константы. Если вдруг кто не понял, 0x1E0 в десятичном будет 480, а 0x140 - 320.
Также обратим внимание на функцию "onSurfaceCreated".
Мы добавили 3 константы(v6,v7,v8), присвоили им значение координат левого нижнего(v6;v6) и правого верхнего(v8;v7) угла экрана и передали их в функцию "glViewport". Я думаю понятно, почему для координат левого нижнего угла и для X и для Y используется одна и также константа v6? Так как и X и Y в этом углу равны между собой. Кроме этого изменяем 2-ю строчку в функции ".locals 6" на ".locals 9", это определяет количество констант/переменных используемых в функции, так как мы добавили 3 константы, то 6+3=9. Также обратите внимание, что имена констант (v6,v7,v8), не берутся случайно, а выбираются ориентируясь на уже используемые в функции константы. Если вдруг кто не понял, 0x1E0 в десятичном будет 480, а 0x140 - 320.
Также обратим внимание на функцию "onSurfaceCreated".
А именно на код
sget v3, Lcom/gameloft/android/GAND/GloftMCHP/SandstormGLSurfaceView;->mDevice_W:I
sget v4, Lcom/gameloft/android/GAND/GloftMCHP/SandstormGLSurfaceView;->mDevice_H:I
В этом коде переменным v3 и v4 присваивается реальное разрешение экрана устройства и передается функции "nativeInit". Так как игра все-таки рассчитана под разрешение 480х800, лучше чтоб игра думала что у вас устройство именно с таким разрешением. по-этому этот код заменяем на
const/16 v3, 0x320
const/16 v4, 0x1E0
Если этого не сделать могут возникнуть проблемы.
(Можно задать и 480х720, т.к. пропорции у экранов с 480х800 и 320х480 разные и при "сжатии" картинка будет немного сплюснута. Но в этом случае, на этапе "Корректировка координатной сетки сенсорного экрана", коэффициент необходимо будет считать не от 800, а от 720 и будет он не 1,666666666666667, а 720/480=1,5)
Примечание
В других играх вместо этого кода, может быть что-то наподобие этого
invoke-virtual {v0}, Landroid/view/Display;->getWidth()I
move-result v3
invoke-virtual {v0}, Landroid/view/Display;->getHeight()I
move-result v4
Все-равно его заменяем на:
const/16 v3, 0x320
const/16 v4, 0x1E0
Или имена переменных быть не v3, v4 а v1, v2. Или вообще ничего подобного не быть, тогда и менять ничего не нужно .
Или если возникают проблемы, то можно на всякий случай пройтись поиском по всем "*.smali" файлам и позаменять все вызовы функций "Landroid/view/Display;->getWidth()I" и "Landroid/view/Display;->getHeight()I", хуже от этого не будет.
Для некоторых игр, проделанной нами работы достаточно и они благополучно меняют разрешения всей графики на необходимое. Для этого снова запаковываем апк-файл и проверяем на телефоне как он работает. Если все удачно то переходим к пункту "Корректировка координатной сетки сенсорного экрана". Если нет то читаем дальше. В нашем случае, с игрой Modern Combat: Sandstorm, не все гладко, игровая графика смаштабировалась до необходимого разрешения, а меню - нет. Это значит, что где-то вызывается функция "glViewPort", и снова меняет разрешение на 480х800. Так-как в файлах "*.smali" функция "glViewPort" не вызывается, можно проверить с помощью поиска, следовательно дело в библиотеке "libsandstorm.so".
Основная идея следующего шага, удалить с библиотеке все вызовы функции "glViewPort".
Для анализа библиотеки нам понадобится "Ida Pro". Для удобства копируем "libsandstorm.so" в любую папку, запускаем "Ida Pro" и жмем на кнопку "New"
Далее выбераем "Various files", "Unknown file" и жмем "Ок".
В окне открытия файла указываем путь к библиотеке "l
ibsandstorm.so" и жмем "
Открыть".
В следующем окне изменяем "
Processor type" на "
ARM processorARM710a", далее жмем "
Set" и "
Ок".
Если после этого, появятся еще окна с чем либо, жмем "Ок". Теперь необходимо дождаться окончания дизассемблирования. Процесс этот достаточно долгий, по-этому можно пойти покурить или попить кофе
О том что дизассемблирования закончено, укажет сообщение "The initial autoanalysis has been finished." в нижнем окне "Output window".
Для большего удобства, в нашем случае, кликаем правой кнопкой мышки по голубой области, и в контекстном меню выбираем "Text view". Перемещаемся в самое начало ассемблерного кода, для осуществление поиска "glViewPort".
Жмем комбинацию клавиш "Alt+T" и в появившемся диалоге для поиска вводим "glViewPort" и жмем "Ок".
Нас интересуют вызовы функций "BLX glViewport", "BL glViewport", "B glViewport", "BX glViewport" и т. д. Любые другие упоминания о "glViewport" мы игнорируем, жмакаем "Ctrl+T" и продолжаем поиск.
Найдя необходимое место, переключаемся на "Hex View-A".
Удостоверяемся, что вызов функции занимает 4 байта и это "CE F7 D4 E8"(в вашем случае эти цифры могут быть другими), это необходимо для того чтоб видеть что нужно исправлять и не затереть случайно ничего лишнего.
Вызов этой функции нам необходимо выпилить, для этого нужно заменить "CE F7 D4 E8"(в вашем случае эти цифры могут быть другими) на "C0 46 С0 46"(Внимание!!! на "C0 46 С0 46" заменяем только в том случае, если в библиотеке используются инструкции Thumb, если используются только ARM инструкции - заменять необходимо на "00 00 A0 E1". Проверить какие используются инструкции достаточно просто. Посмотрите ближайшие инструкции в HEX виде, если среди них есть 2-х байтовые - значит используются инструкции Thumb, если все 4-х байтовые - значит ARM."). Запоминаем адрес "001F994A" и запускаем шестнадцатеричный редактор, я использую "UltraEdit". Открываем в нем нашу библиотеку.
Для того чтоб переместится по нужному нам адресу, жмем "Ctrl+G", в появившееся окно вводит "0x001F994A" и жмем "Ок".
Переместившись мы видим, что попали туда, куда нужно, весь шестнадцатеричный код сходится с тем что мы видели в "Ida Pro" на вкладке "Hex View-A".
Исправляем "CE F7 D4 E8" на "C0 46 С0 46".