├── flappy.gif ├── FlappyBird ├── mykeystore.jks ├── app │ └── src │ │ └── main │ │ ├── jni │ │ ├── Application.mk │ │ ├── shaders.h │ │ ├── audio.h │ │ ├── utils.h │ │ ├── game.h │ │ ├── mouse.h │ │ ├── texture.h │ │ ├── utils.c │ │ ├── init.h │ │ ├── Android.mk │ │ ├── mouse.c │ │ ├── shaders.c │ │ ├── upng.h │ │ ├── main.c │ │ ├── init.c │ │ ├── texture.c │ │ ├── audio.c │ │ ├── android_native_app_glue.h │ │ ├── android_native_app_glue.c │ │ ├── game.c │ │ └── upng.c │ │ ├── res │ │ └── mipmap │ │ │ └── icon.png │ │ ├── assets │ │ ├── audio │ │ │ ├── die.mp3 │ │ │ ├── hit.mp3 │ │ │ ├── point.mp3 │ │ │ └── wing.mp3 │ │ ├── buttons │ │ │ ├── ok.png │ │ │ ├── menu.png │ │ │ ├── pause.png │ │ │ ├── score.png │ │ │ ├── share.png │ │ │ ├── start.png │ │ │ └── resume.png │ │ └── sprites │ │ │ ├── 0.png │ │ │ ├── 1.png │ │ │ ├── 2.png │ │ │ ├── 3.png │ │ │ ├── 4.png │ │ │ ├── 5.png │ │ │ ├── 6.png │ │ │ ├── 7.png │ │ │ ├── 8.png │ │ │ ├── 9.png │ │ │ ├── new.png │ │ │ ├── base.png │ │ │ ├── logo.png │ │ │ ├── panel.png │ │ │ ├── 0_small.png │ │ │ ├── 1_small.png │ │ │ ├── 2_small.png │ │ │ ├── 3_small.png │ │ │ ├── 4_small.png │ │ │ ├── 5_small.png │ │ │ ├── 6_small.png │ │ │ ├── 7_small.png │ │ │ ├── 8_small.png │ │ │ ├── 9_small.png │ │ │ ├── gameover.png │ │ │ ├── message.png │ │ │ ├── gold-medal.png │ │ │ ├── pipe-green.png │ │ │ ├── bronze-medal.png │ │ │ ├── silver-medal.png │ │ │ ├── sparkle-sheet.png │ │ │ ├── background-day.png │ │ │ ├── platinum-medal.png │ │ │ ├── yellowbird-midflap.png │ │ │ ├── yellowbird-upflap.png │ │ │ └── yellowbird-downflap.png │ │ └── AndroidManifest.xml ├── FlappyBird.vcxproj.filters ├── Makefile ├── build.sh ├── build.bat └── FlappyBird.vcxproj ├── .env.example ├── FlappyBird.sln ├── .gitattributes ├── BUILDING.md ├── README_RU.md ├── README.md └── .gitignore /flappy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/flappy.gif -------------------------------------------------------------------------------- /FlappyBird/mykeystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/mykeystore.jks -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi-v7a arm64-v8a 2 | APP_PLATFORM := android-21 3 | APP_STL := c++_static -------------------------------------------------------------------------------- /FlappyBird/app/src/main/res/mipmap/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/res/mipmap/icon.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/audio/die.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/audio/die.mp3 -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/audio/hit.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/audio/hit.mp3 -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/audio/point.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/audio/point.mp3 -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/audio/wing.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/audio/wing.mp3 -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/buttons/ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/buttons/ok.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/0.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/1.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/2.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/3.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/4.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/5.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/6.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/7.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/8.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/9.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/new.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/buttons/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/buttons/menu.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/buttons/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/buttons/pause.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/buttons/score.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/buttons/score.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/buttons/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/buttons/share.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/buttons/start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/buttons/start.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/base.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/logo.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/panel.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/buttons/resume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/buttons/resume.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/0_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/0_small.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/1_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/1_small.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/2_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/2_small.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/3_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/3_small.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/4_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/4_small.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/5_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/5_small.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/6_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/6_small.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/7_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/7_small.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/8_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/8_small.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/9_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/9_small.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/gameover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/gameover.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/message.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/gold-medal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/gold-medal.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/pipe-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/pipe-green.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/bronze-medal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/bronze-medal.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/silver-medal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/silver-medal.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/sparkle-sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/sparkle-sheet.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/background-day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/background-day.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/platinum-medal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/platinum-medal.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/yellowbird-midflap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/yellowbird-midflap.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/yellowbird-upflap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/yellowbird-upflap.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/assets/sprites/yellowbird-downflap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VadimBoev/FlappyBird/HEAD/FlappyBird/app/src/main/assets/sprites/yellowbird-downflap.png -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/shaders.h: -------------------------------------------------------------------------------- 1 | #ifndef SHADERS_H 2 | #define SHADERS_H 3 | 4 | GLuint loadShader(GLenum type, const char* source); 5 | GLuint createProgram(const char* vertexSource, const char* fragmentSource); 6 | 7 | #endif // SHADERS_H -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | ANDROID_SDK_ROOT=/path/to/your/android/sdk 2 | ANDROID_NDK_ROOT=/path/to/your/android/ndk 3 | ANDROID_HOME=/path/to/your/android/home 4 | KEYSTORE_PASSWORD=your_keystore_password 5 | PACKAGE_NAME=your.app.package.name 6 | BUILD_TOOLS_VERSION=30.0.3 -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/audio.h: -------------------------------------------------------------------------------- 1 | #ifndef AUDIO_H 2 | #define AUDIO_H 3 | 4 | void CreateAudioEngine(); 5 | void PlayAudio(const char* assetPath); 6 | void PauseAudio(); 7 | void ResumeAudio(); 8 | void StopAudio(); 9 | void DestroyAudioPlayer(); 10 | void DestroyAudioEngine(); 11 | 12 | #endif // AUDIO_H -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include 5 | #include 6 | 7 | void Log(const char* fmt, ...); 8 | 9 | //#if defined(__aarch64__) 10 | uint64_t getTickCount(); 11 | //#else 12 | //uint32_t getTickCount(); 13 | //#endif 14 | 15 | #endif // UTILS_H -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/game.h: -------------------------------------------------------------------------------- 1 | #ifndef GAME_H 2 | #define GAME_H 3 | 4 | bool InitGame(); 5 | void Render(); 6 | void ShutdownGame(); 7 | bool ButtonBump(GLuint textureid, float posX, float posY, float width, float height); 8 | bool Button(float posX, float posY, float width, float height); 9 | bool IsClick(float posX, float posY, float width, float height); 10 | 11 | 12 | #endif // GAME_H -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/mouse.h: -------------------------------------------------------------------------------- 1 | #ifndef MOUSE_H 2 | #define MOUSE_H 3 | 4 | #include 5 | 6 | typedef struct { 7 | float x; 8 | float y; 9 | bool isDown; 10 | bool isReleased; 11 | bool isMoved; 12 | } MouseState; 13 | 14 | extern MouseState mouse; 15 | 16 | void MouseInit(MouseState* mouse); 17 | void MouseReset(MouseState* mouse); 18 | bool IsMouseInSquare(int mouse_x, int mouse_y, int x, int y, int w, int h); 19 | 20 | #endif // MOUSE_H -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/texture.h: -------------------------------------------------------------------------------- 1 | #ifndef TEXTURE_H 2 | #define TEXTURE_H 3 | 4 | #define M_PI 3.14159265358979323846f 5 | 6 | GLuint LoadTexture(const char* assetPath); 7 | void RenderTexture(GLuint texture, float x, float y, float width, float height); 8 | void RenderTexturePro(GLuint texture, float x, float y, float width, float height, float angle); 9 | 10 | void CreateBox(uint32_t color, float posX, float posY, float width, float height); 11 | 12 | #endif // TEXTURE_H -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/utils.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include 3 | #include 4 | #include 5 | 6 | void Log(const char* fmt, ...) 7 | { 8 | va_list args; 9 | va_start(args, fmt); 10 | __android_log_vprint(ANDROID_LOG_INFO, "flappy", fmt, args); 11 | va_end(args); 12 | } 13 | 14 | uint64_t getTickCount() 15 | { 16 | struct timespec now; 17 | clock_gettime(CLOCK_MONOTONIC, &now); 18 | return (uint64_t)now.tv_sec * 1000 + (uint64_t)now.tv_nsec / 1000000; 19 | } -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/init.h: -------------------------------------------------------------------------------- 1 | #ifndef INIT_H 2 | #define INIT_H 3 | 4 | #include "android_native_app_glue.h" 5 | #include 6 | #include 7 | 8 | extern bool g_Initialized; 9 | extern struct android_app* g_App; 10 | extern int32_t WindowSizeX; 11 | extern int32_t WindowSizeY; 12 | 13 | extern GLuint textureProgram; 14 | 15 | extern GLuint colorProgram; 16 | extern GLuint gPositionHandle; 17 | extern GLuint gColorHandle; 18 | 19 | 20 | void Init(struct android_app* app); 21 | void MainLoopStep(); 22 | void Shutdown(); 23 | 24 | #endif // INIT_H -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_MODULE := flappybird 6 | LOCAL_LDLIBS := -llog -landroid -lGLESv2 -lEGL -lOpenSLES -lm -u ANativeActivity_onCreate 7 | 8 | FILE_LIST := $(wildcard $(LOCAL_PATH)/*.c) 9 | #FILE_LIST += $(wildcard $(LOCAL_PATH)/folder/*.c) 10 | 11 | LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%) 12 | 13 | LOCAL_CFLAGS := -Oz -fvisibility=hidden -ffunction-sections -fdata-sections -fno-stack-protector -fomit-frame-pointer -flto 14 | LOCAL_LDFLAGS := -Wl,--gc-sections -s 15 | 16 | include $(BUILD_SHARED_LIBRARY) -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/mouse.c: -------------------------------------------------------------------------------- 1 | #include "mouse.h" 2 | MouseState mouse; 3 | 4 | void MouseInit(MouseState* mouse) 5 | { 6 | mouse->x = 0.0f; 7 | mouse->y = 0.0f; 8 | mouse->isDown = false; 9 | mouse->isReleased = false; 10 | mouse->isMoved = false; 11 | } 12 | 13 | void MouseReset(MouseState* mouse) 14 | { 15 | mouse->x = -1.0f; 16 | mouse->y = -1.0f; 17 | mouse->isDown = false; 18 | mouse->isReleased = false; 19 | mouse->isMoved = false; 20 | } 21 | 22 | bool IsMouseInSquare(int mouse_x, int mouse_y, int x, int y, int w, int h) 23 | { 24 | if (x <= mouse_x && mouse_x <= x + w && 25 | y <= mouse_y && mouse_y <= y + h) 26 | { 27 | return true; 28 | } 29 | else 30 | { 31 | return false; 32 | } 33 | } -------------------------------------------------------------------------------- /FlappyBird/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/shaders.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "shaders.h" 3 | #include "utils.h" 4 | 5 | GLuint loadShader(GLenum type, const char* source) 6 | { 7 | GLuint shader = glCreateShader(type); 8 | glShaderSource(shader, 1, &source, NULL); 9 | glCompileShader(shader); 10 | 11 | GLint success; 12 | glGetShaderiv(shader, GL_COMPILE_STATUS, &success); 13 | if (!success) { 14 | GLchar infoLog[512]; 15 | glGetShaderInfoLog(shader, 512, NULL, infoLog); 16 | Log("SHADER -> COMPILATION_FAILED %s",infoLog); 17 | } 18 | return shader; 19 | } 20 | 21 | GLuint createProgram(const char* vertexSource, const char* fragmentSource) 22 | { 23 | GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vertexSource); 24 | GLuint fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentSource); 25 | 26 | GLuint program = glCreateProgram(); 27 | glAttachShader(program, vertexShader); 28 | glAttachShader(program, fragmentShader); 29 | glLinkProgram(program); 30 | 31 | GLint success; 32 | glGetProgramiv(program, GL_LINK_STATUS, &success); 33 | if (!success) { 34 | GLchar infoLog[512]; 35 | glGetProgramInfoLog(program, 512, NULL, infoLog); 36 | Log("ERROR::PROGRAM::LINKING_FAILED %s", infoLog); 37 | } 38 | return program; 39 | } -------------------------------------------------------------------------------- /FlappyBird/FlappyBird.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /FlappyBird.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.10.35013.160 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FlappyBird", "FlappyBird\FlappyBird.vcxproj", "{F1BB0F05-20A0-4EA7-A326-1516C80260D7}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|ARM = Debug|ARM 11 | Debug|ARM64 = Debug|ARM64 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|ARM = Release|ARM 15 | Release|ARM64 = Release|ARM64 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {F1BB0F05-20A0-4EA7-A326-1516C80260D7}.Debug|ARM.ActiveCfg = Debug|ARM 21 | {F1BB0F05-20A0-4EA7-A326-1516C80260D7}.Debug|ARM.Build.0 = Debug|ARM 22 | {F1BB0F05-20A0-4EA7-A326-1516C80260D7}.Debug|ARM64.ActiveCfg = Debug|ARM64 23 | {F1BB0F05-20A0-4EA7-A326-1516C80260D7}.Debug|ARM64.Build.0 = Debug|ARM64 24 | {F1BB0F05-20A0-4EA7-A326-1516C80260D7}.Debug|x64.ActiveCfg = Debug|x64 25 | {F1BB0F05-20A0-4EA7-A326-1516C80260D7}.Debug|x64.Build.0 = Debug|x64 26 | {F1BB0F05-20A0-4EA7-A326-1516C80260D7}.Debug|x86.ActiveCfg = Debug|x86 27 | {F1BB0F05-20A0-4EA7-A326-1516C80260D7}.Debug|x86.Build.0 = Debug|x86 28 | {F1BB0F05-20A0-4EA7-A326-1516C80260D7}.Release|ARM.ActiveCfg = Release|ARM 29 | {F1BB0F05-20A0-4EA7-A326-1516C80260D7}.Release|ARM.Build.0 = Release|ARM 30 | {F1BB0F05-20A0-4EA7-A326-1516C80260D7}.Release|ARM64.ActiveCfg = Release|ARM64 31 | {F1BB0F05-20A0-4EA7-A326-1516C80260D7}.Release|ARM64.Build.0 = Release|ARM64 32 | {F1BB0F05-20A0-4EA7-A326-1516C80260D7}.Release|x64.ActiveCfg = Release|x64 33 | {F1BB0F05-20A0-4EA7-A326-1516C80260D7}.Release|x64.Build.0 = Release|x64 34 | {F1BB0F05-20A0-4EA7-A326-1516C80260D7}.Release|x86.ActiveCfg = Release|x86 35 | {F1BB0F05-20A0-4EA7-A326-1516C80260D7}.Release|x86.Build.0 = Release|x86 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {41EBC1E7-65C8-4ADE-968B-5B693EE2FAEF} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /BUILDING.md: -------------------------------------------------------------------------------- 1 | # FlappyBird Android Build Instructions 2 | 3 | This document explains how to build the FlappyBird Android game on macOS, Linux, and Windows using either the provided shell script or Makefile. 4 | 5 | ## Prerequisites 6 | 7 | Before building, you need to set up your environment variables: 8 | 9 | 1. Copy the `.env.example` file from the project root to `.env` in the project root: 10 | ```bash 11 | cp .env.example .env 12 | ``` 13 | 14 | 2. Edit the `.env` file in the project root to match your local setup: 15 | 16 | Make sure to: 17 | - Update the paths to your Android SDK and NDK installations 18 | - Change the `PACKAGE_NAME` variable to match your desired package name for the app 19 | - Update the `BUILD_TOOLS_VERSION` variable to match the version of the Android build tools you have installed 20 | - Set a secure `KEYSTORE_PASSWORD` 21 | 22 | The build system uses the Android command-line tools. You'll need to specify the path to your Android SDK and NDK in the `.env` file. 23 | 24 | ## Building with Shell Script 25 | 26 | To build using the shell script: 27 | 28 | ```bash 29 | cd FlappyBird 30 | ./build.sh 31 | ``` 32 | 33 | The signed APK will be generated at: 34 | `app/build/outputs/apk/FlappyBird-signed.apk` 35 | 36 | Note: The script will automatically install and run the app on a connected device. 37 | 38 | ## Building with Makefile 39 | 40 | To build using the Makefile: 41 | 42 | ```bash 43 | cd FlappyBird 44 | make 45 | ``` 46 | 47 | To clean the build: 48 | ```bash 49 | make clean 50 | ``` 51 | 52 | To install and run on a connected device: 53 | ```bash 54 | make install 55 | # or to also start the app: 56 | make run 57 | ``` 58 | 59 | To debug the app: 60 | ```bash 61 | make debug 62 | ``` 63 | 64 | ## Build Process Overview 65 | 66 | 1. **Native Code Compilation**: The NDK builds the C code into shared libraries for armeabi-v7a and arm64-v8a architectures 67 | 2. **APK Creation**: An empty APK is created with the manifest, resources, and assets 68 | 3. **Library Inclusion**: Native libraries are added to the APK in the correct location (`lib//`) 69 | 4. **Alignment**: The APK is aligned for better performance 70 | 5. **Signing**: The APK is signed with the provided keystore 71 | 6. **Cleanup**: Temporary files are removed 72 | 73 | ## Troubleshooting 74 | 75 | If you encounter issues: 76 | 1. Ensure all paths in the `.env` file are correct for your system 77 | 2. Verify that the Android tools are properly installed 78 | 3. Check that you have the required Android platform (android-30) and build tools (30.0.3) 79 | 4. Make sure your device is connected and recognized by adb (`adb devices`) 80 | 5. Ensure the keystore file `mykeystore.jks` exists in the FlappyBird directory or create one with: 81 | ```bash 82 | keytool -genkeypair -dname "cn=Mark Jones, ou=JavaSoft, o=Sun, c=US" -alias business -keypass your_password -keystore mykeystore.jks -storepass your_password -validity 20000 83 | ``` 84 | -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/upng.h: -------------------------------------------------------------------------------- 1 | /* 2 | uPNG -- derived from LodePNG version 20100808 3 | 4 | Copyright (c) 2005-2010 Lode Vandevenne 5 | Copyright (c) 2010 Sean Middleditch 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 23 | 3. This notice may not be removed or altered from any source 24 | distribution. 25 | */ 26 | 27 | #if !defined(UPNG_H) 28 | #define UPNG_H 29 | 30 | typedef enum upng_error { 31 | UPNG_EOK = 0, /* success (no error) */ 32 | UPNG_ENOMEM = 1, /* memory allocation failed */ 33 | UPNG_ENOTFOUND = 2, /* resource not found (file missing) */ 34 | UPNG_ENOTPNG = 3, /* image data does not have a PNG header */ 35 | UPNG_EMALFORMED = 4, /* image data is not a valid PNG image */ 36 | UPNG_EUNSUPPORTED = 5, /* critical PNG chunk type is not supported */ 37 | UPNG_EUNINTERLACED = 6, /* image interlacing is not supported */ 38 | UPNG_EUNFORMAT = 7, /* image color format is not supported */ 39 | UPNG_EPARAM = 8 /* invalid parameter to method call */ 40 | } upng_error; 41 | 42 | typedef enum upng_format { 43 | UPNG_BADFORMAT, 44 | UPNG_RGB8, 45 | UPNG_RGB16, 46 | UPNG_RGBA8, 47 | UPNG_RGBA16, 48 | UPNG_LUMINANCE1, 49 | UPNG_LUMINANCE2, 50 | UPNG_LUMINANCE4, 51 | UPNG_LUMINANCE8, 52 | UPNG_LUMINANCE_ALPHA1, 53 | UPNG_LUMINANCE_ALPHA2, 54 | UPNG_LUMINANCE_ALPHA4, 55 | UPNG_LUMINANCE_ALPHA8, 56 | UPNG_INDEX1, 57 | UPNG_INDEX2, 58 | UPNG_INDEX4, 59 | UPNG_INDEX8 60 | } upng_format; 61 | 62 | typedef struct upng_t upng_t; 63 | 64 | upng_t* upng_new_from_bytes(const unsigned char* buffer, unsigned long size); 65 | upng_t* upng_new_from_file(const char* path); 66 | void upng_free(upng_t* upng); 67 | 68 | upng_error upng_header(upng_t* upng); 69 | upng_error upng_decode(upng_t* upng); 70 | 71 | upng_error upng_get_error(const upng_t* upng); 72 | unsigned upng_get_error_line(const upng_t* upng); 73 | 74 | unsigned upng_get_width(const upng_t* upng); 75 | unsigned upng_get_height(const upng_t* upng); 76 | unsigned upng_get_bpp(const upng_t* upng); 77 | unsigned upng_get_bitdepth(const upng_t* upng); 78 | unsigned upng_get_components(const upng_t* upng); 79 | unsigned upng_get_pixelsize(const upng_t* upng); 80 | upng_format upng_get_format(const upng_t* upng); 81 | 82 | const unsigned char* upng_get_buffer(const upng_t* upng); 83 | unsigned upng_get_size(const upng_t* upng); 84 | const unsigned char* upng_get_palette(const upng_t* upng); 85 | 86 | #endif /*defined(UPNG_H)*/ -------------------------------------------------------------------------------- /README_RU.md: -------------------------------------------------------------------------------- 1 | # 🐦 Flappy Bird на Си: Вес APK < 100 Килобайт! 🚀 2 | 3 | ## 📜 История: 4 | 5 | Всё началось в 2021 году, когда я наткнулся на [rawdrawandroid](https://github.com/cnlohr/rawdrawandroid). Моя цель была проста: создать игру с минимальным весом APK, но при этом, чтобы она была понятной и интересной. 🎯 6 | 7 | Идея сделать клон Flappy Bird казалась логичной, учитывая, что эта игра уже была портирована на множество языков. 🐤 8 | 9 | В 2021 году я также изучил [Raylib](https://github.com/raysan5/raylib), но первая попытка сделать игру на C++ с использованием [ImGui](https://github.com/ocornut/imgui/) потерпела неудачу. 💔 10 | 11 | Проблемы были везде: вес APK был около 1 МБ, игра вылетала, и в APK была только библиотека для armeabi-v7a (требования Google от 2022 года это наличие arm64-v8a библиотеки!). 🤯 12 | 13 | ## 💡 Мотивация: 14 | 15 | В сентябре 2024 года, увидев Flappy Bird на C# в дискорд-канале Raylib, я решил попробовать реализовать эту игру на Си для Android с весом APK менее 100 КБ. 🚀 16 | 17 | Идея казалась безумной, но спортивный интерес взял верх. 💪 18 | 19 | ## 🛠️ Реализация: 20 | 21 | Начал с компиляции "Hello World" на Си и упаковки библиотеки в APK. 📦 22 | 23 | Звуки были сжаты до формата MP3, а для их воспроизведения использовался OpenSLES. 🎵 24 | 25 | Для декодирования PNG файлов я выбрал [upng](https://github.com/elanthis/upng). 🖼️ 26 | 27 | Всё это было объединено с помощью OpenGL ES 2, шейдеров и Android Native Activity. 🎮 28 | 29 | ## 🔧 Сборка: 30 | 31 | ### Visual Studio (Windows) 32 | 33 | 1. Скачайте [Visual Studio 2022](https://visualstudio.microsoft.com/). 34 | 2. Установите компоненты: Разработка классических приложений на С++ и Разработка мобильных приложений на языке С++. 35 | 3. Скачайте Android Studio (для apktool, sdk, ndk). 36 | 4. Установите NDK 27.1.12297006 и Android SDK Платформа 30. 37 | 5. Настройте проект для "Debug ARM" и внесите изменения в build.bat. 38 | 6. Компилируйте через CTRL + B. 🛠️ 39 | 40 | ### Linux и macOS (Makefile) 41 | 42 | Для пользователей Linux и macOS можно собрать проект с помощью предоставленного Makefile: 43 | 44 | 1. Установите инструменты командной строки Android. 45 | 2. Настройте переменные окружения в файле `.env` в корневом каталоге проекта (смотрите `.env.example` для примера). 46 | 3. Соберите проект: 47 | ```bash 48 | cd FlappyBird 49 | make 50 | ``` 51 | 52 | Для более подробных инструкций, пожалуйста, обратитесь к [BUILDING.md](BUILDING.md). 53 | 54 | Подписанный APK будет сгенерирован в: 55 | `FlappyBird/app/build/outputs/apk/FlappyBird-signed.apk` 56 | 57 | ## 📄 Авторское право: 58 | 59 | Я не претендую на авторские права. Право на игру и ресурсы принадлежит **DotGEARS**. 📜 60 | 61 | ## 🌟 Вдохновение: 62 | 63 | - [rawdrawandroid](https://github.com/cnlohr/rawdrawandroid) 64 | - [Flapper](https://github.com/its-Lyn/Flapper) 65 | - [Raylib](https://github.com/raysan5/raylib) 66 | - [ImGui](https://github.com/ocornut/imgui/) 67 | 68 | ## 🌠 Star History 69 | 70 | [![Star History Chart](https://api.star-history.com/svg?repos=VadimBoev/FlappyBird&type=Timeline)](https://star-history.com/#VadimBoev/FlappyBird&Timeline) 71 | 72 | --- 73 | 74 | 🎉 Наслаждайтесь игрой и не забудьте поставить звезду! 🌟 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | game 2 | 3 | **[Readme на русском языке](README_RU.md)** 4 | 5 | [Dev blog in Telegram (ENG/RU)](https://t.me/boevdev) 6 | 7 | # 🐦 Flappy Bird in C: APK Size < 100 Kilobytes! 🚀 8 | 9 | ## 📜 History: 10 | 11 | It all started in 2021 when I stumbled upon [rawdrawandroid](https://github.com/cnlohr/rawdrawandroid). My goal was simple: to create a game with the minimal APK size, yet still be understandable and interesting. 🎯 12 | 13 | The idea of making a Flappy Bird clone seemed logical, given that this game had already been ported to many languages. 🐤 14 | 15 | In 2021, I also studied [Raylib](https://github.com/raysan5/raylib), but my first attempt to make a game in C++ using [ImGui](https://github.com/ocornut/imgui/) failed. 💔 16 | 17 | The problems were everywhere: the APK size was around 1 MB, the game crashed, and the APK only contained the armeabi-v7a library (Google's 2022 requirements state that the arm64-v8a library must be included!). 🤯 18 | 19 | ## 💡 Motivation: 20 | 21 | In September 2024, seeing Flappy Bird in C# in the Raylib Discord channel, I decided to try implementing this game in C for Android with an APK size of less than 100 KB. 🚀 22 | 23 | The idea seemed crazy, but the competitive interest took over. 💪 24 | 25 | ## 🛠️ Implementation: 26 | 27 | I started by compiling a "Hello World" in C and packaging the library into an APK. 📦 28 | 29 | Sounds were compressed to MP3 format, and OpenSLES was used for playback. 🎵 30 | 31 | For PNG file decoding, I chose [upng](https://github.com/elanthis/upng). 🖼️ 32 | 33 | All of this was combined using OpenGL ES 2, shaders, and Android Native Activity. 🎮 34 | 35 | ## 🔧 Build: 36 | 37 | ### Visual Studio (Windows) 38 | 39 | 1. Download [Visual Studio 2022](https://visualstudio.microsoft.com/). 40 | 2. Install components: C++ Desktop Development and C++ Mobile Development. 41 | 3. Download Android Studio (for apktool, sdk, ndk). 42 | 4. Install NDK 27.1.12297006 and Android SDK Platform 30. 43 | 5. Configure the project for "Debug ARM" and make changes to build.bat. 44 | 6. Compile via CTRL + B. 🛠️ 45 | 46 | ### Linux and macOS (Makefile) 47 | 48 | For Linux and macOS users, you can build the project using the provided Makefile: 49 | 50 | 1. Install the Android command-line tools. 51 | 2. Set up your environment variables in a `.env` file in the project root directory (see `.env.example` for an example). 52 | 3. Build the project: 53 | ```bash 54 | cd FlappyBird 55 | make 56 | ``` 57 | 58 | For more detailed instructions, please refer to [BUILDING.md](BUILDING.md). 59 | 60 | The signed APK will be generated at: 61 | `FlappyBird/app/build/outputs/apk/FlappyBird-signed.apk` 62 | 63 | ## 📄 Copyright: 64 | 65 | I do not claim any copyright. The rights to the game and resources belong to **DotGEARS**. 📜 66 | 67 | ## 🌟 Inspiration: 68 | 69 | - [rawdrawandroid](https://github.com/cnlohr/rawdrawandroid) 70 | - [Flapper](https://github.com/its-Lyn/Flapper) 71 | - [Raylib](https://github.com/raysan5/raylib) 72 | - [ImGui](https://github.com/ocornut/imgui/) 73 | 74 | ## 🌠 Star History 75 | 76 | [![Star History Chart](https://api.star-history.com/svg?repos=VadimBoev/FlappyBird&type=Timeline)](https://star-history.com/#VadimBoev/FlappyBird&Timeline) 77 | 78 | --- 79 | 80 | 🎉 Enjoy the game and don't forget to star it! 🌟 81 | -------------------------------------------------------------------------------- /FlappyBird/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for building FlappyBird on macOS 2 | 3 | # Load environment variables from .env file if it exists 4 | include ../.env 5 | 6 | # Set paths to your Android tools 7 | ANDROID_HOME := $(ANDROID_SDK_ROOT) 8 | ANDROID_SDK_ROOT := $(ANDROID_SDK_ROOT) 9 | ANDROID_NDK_ROOT := $(ANDROID_NDK_ROOT) 10 | 11 | # Tools 12 | ADB := $(ANDROID_SDK_ROOT)/platform-tools/adb 13 | NDK_BUILD := $(ANDROID_NDK_ROOT)/ndk-build 14 | AAPT := $(ANDROID_SDK_ROOT)/build-tools/$(BUILD_TOOLS_VERSION)/aapt 15 | ZIPALIGN := $(ANDROID_SDK_ROOT)/build-tools/$(BUILD_TOOLS_VERSION)/zipalign 16 | APKSIGNER := $(ANDROID_SDK_ROOT)/build-tools/$(BUILD_TOOLS_VERSION)/apksigner 17 | 18 | # Variables 19 | APKNAME := FlappyBird 20 | ANDROIDTARGET := 30 21 | KEYSTORE_PASSWORD := $(KEYSTORE_PASSWORD) 22 | PACKAGE_NAME := $(PACKAGE_NAME) 23 | BUILD_TOOLS_VERSION := $(BUILD_TOOLS_VERSION) 24 | 25 | # Directories 26 | BUILD_DIR := app/build 27 | INTERMEDIATES_DIR := $(BUILD_DIR)/intermediates 28 | APK_DIR := $(BUILD_DIR)/outputs/apk 29 | SRC_DIR := app/src/main 30 | JNI_DIR := $(SRC_DIR)/jni 31 | LIBS_DIR := $(SRC_DIR)/libs 32 | ASSETS_DIR := $(SRC_DIR)/assets 33 | RES_DIR := $(SRC_DIR)/res 34 | MANIFEST := $(SRC_DIR)/AndroidManifest.xml 35 | 36 | # Platform JAR 37 | ANDROID_JAR := $(ANDROID_SDK_ROOT)/platforms/android-$(ANDROIDTARGET)/android.jar 38 | 39 | .PHONY: all clean build-native build-apk sign-apk install run 40 | 41 | all: $(APK_DIR)/$(APKNAME)-signed.apk 42 | 43 | clean: 44 | rm -rf $(BUILD_DIR) 45 | 46 | build-native: 47 | mkdir -p $(INTERMEDIATES_DIR)/ndk 48 | cd $(JNI_DIR) && $(NDK_BUILD) 49 | 50 | $(APK_DIR)/$(APKNAME)-signed.apk: build-native 51 | mkdir -p $(APK_DIR) 52 | 53 | # Replace package name in AndroidManifest.xml 54 | sed -i.bak "s/your.app.package.name/$(PACKAGE_NAME)/g" $(MANIFEST) 55 | 56 | # Create empty APK (without native libraries) 57 | $(AAPT) package -f -M $(MANIFEST) -S $(RES_DIR) -A $(ASSETS_DIR) -I $(ANDROID_JAR) -F $(APK_DIR)/unaligned.apk 58 | 59 | # Add native libraries to the APK in the correct location 60 | # Create a temporary directory structure that mirrors the APK's lib structure 61 | mkdir -p $(INTERMEDIATES_DIR)/apk_add_libs/lib 62 | cp -r $(LIBS_DIR)/* $(INTERMEDIATES_DIR)/apk_add_libs/lib/ 2>/dev/null || echo "No libs to copy" 63 | # Check if the APK file exists and is not empty 64 | ls -l $(APK_DIR)/unaligned.apk || echo "APK file not found or empty" 65 | # Small delay to ensure file is written 66 | sleep 1 67 | # Use zip to add the libs to the APK (use absolute path for APK and ensure recursive add) 68 | cd $(INTERMEDIATES_DIR)/apk_add_libs && zip -r $(CURDIR)/$(APK_DIR)/unaligned.apk lib 2>/dev/null || echo "No libs to add" 69 | 70 | # Align APK 71 | $(ZIPALIGN) -f 4 $(APK_DIR)/unaligned.apk $(APK_DIR)/$(APKNAME).apk 72 | 73 | # Sign APK 74 | $(APKSIGNER) sign --ks mykeystore.jks --ks-pass pass:$(KEYSTORE_PASSWORD) --out $@ $(APK_DIR)/$(APKNAME).apk 75 | 76 | # Cleanup 77 | rm -rf $(INTERMEDIATES_DIR)/apk_add_libs 78 | rm -f $(APK_DIR)/$(APKNAME).apk 79 | rm -f $(APK_DIR)/$(APKNAME)-signed.apk.idsig 80 | rm -f $(APK_DIR)/unaligned.apk 81 | 82 | # Restore original AndroidManifest.xml 83 | mv $(MANIFEST).bak $(MANIFEST) 84 | 85 | install: $(APK_DIR)/$(APKNAME)-signed.apk 86 | $(ADB) install $(APK_DIR)/$(APKNAME)-signed.apk 87 | 88 | run: install 89 | $(ADB) shell am start -n $(PACKAGE_NAME)/android.app.NativeActivity 90 | 91 | debug: run 92 | $(ADB) logcat -s flappy -------------------------------------------------------------------------------- /FlappyBird/build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Load environment variables from .env file if it exists 4 | if [ -f "../.env" ]; then 5 | export $(cat ../.env | xargs) 6 | fi 7 | 8 | export PATH=$ANDROID_SDK_ROOT/tools:$ANDROID_SDK_ROOT/platform-tools:$PATH 9 | 10 | export ADB=$ANDROID_SDK_ROOT/platform-tools/adb 11 | 12 | export APKNAME=FlappyBird 13 | export ANDROIDVERSION=34 14 | export ANDROIDTARGET=34 15 | 16 | export KEYSTORE_PASSWORD=$KEYSTORE_PASSWORD 17 | 18 | echo Cleaning previous builds... 19 | rm -r app/build 20 | 21 | # Replace package name in AndroidManifest.xml 22 | sed -i.bak "s/your.app.package.name/$PACKAGE_NAME/g" app/src/main/AndroidManifest.xml 23 | 24 | echo Creating build directories... 25 | mkdir -p app/build/intermediates/ndk 26 | mkdir -p app/build/outputs/apk 27 | 28 | echo Building native code... 29 | cd app/src/main 30 | $ANDROID_NDK_ROOT/ndk-build 31 | ret_code=$? 32 | if [ $ret_code != 0 ]; then 33 | echo Error building native code! 34 | echo Error code: $ret_code 35 | exit $ret_code 36 | fi 37 | cd ../../.. 38 | 39 | echo Creating empty APK... 40 | $ANDROID_SDK_ROOT/build-tools/$BUILD_TOOLS_VERSION/aapt package -f -M app/src/main/AndroidManifest.xml -S app/src/main/res -A app/src/main/assets -I $ANDROID_SDK_ROOT/platforms/android-$ANDROIDTARGET/android.jar -F app/build/outputs/apk/unaligned.apk 41 | ret_code=$? 42 | if [ $ret_code != 0 ]; then 43 | echo Error creating empty APK! 44 | echo Error code: $ret_code 45 | exit $ret_code 46 | fi 47 | 48 | mkdir lib 49 | 50 | echo Copy files from libs to a temporary folder 51 | cp app/src/main/libs/* lib/ -R 52 | 53 | echo Add the contents of the temporary folder to the archive in the lib folder 54 | zip app/build/outputs/apk/unaligned.apk -r lib/* lib/ 55 | 56 | echo Aligning APK... 57 | $ANDROID_SDK_ROOT/build-tools/$BUILD_TOOLS_VERSION/zipalign -f 4 app/build/outputs/apk/unaligned.apk app/build/outputs/apk/$APKNAME.apk 58 | ret_code=$? 59 | if [ $ret_code != 0 ]; then 60 | echo Error aligning APK! 61 | echo Error code: $ret_code 62 | exit $ret_code 63 | fi 64 | 65 | # create a keystore if needed 66 | if [ 0 ]; then 67 | echo y | $JAVA_HOME/bin/keytool -genkeypair \ 68 | -dname "cn=Mark Jones, ou=JavaSoft, o=Sun, c=US" \ 69 | -alias business \ 70 | -keypass $KEYSTORE_PASSWORD \ 71 | -keystore mykeystore.jks \ 72 | -storepass $KEYSTORE_PASSWORD \ 73 | -validity 20000 74 | fi 75 | 76 | echo Signing APK... 77 | $ANDROID_SDK_ROOT/build-tools/$BUILD_TOOLS_VERSION/apksigner sign --ks mykeystore.jks --ks-pass pass:$KEYSTORE_PASSWORD --out app/build/outputs/apk/$APKNAME-signed.apk app/build/outputs/apk/$APKNAME.apk 78 | ret_code=$? 79 | if [ $ret_code != 0 ]; then 80 | echo Error signing APK! 81 | echo Error code: $ret_code 82 | exit $ret_code 83 | fi 84 | 85 | echo Delete temporary folder 86 | rm -r lib 87 | 88 | echo Deleting unnecessary files... 89 | rm app/build/outputs/apk/$APKNAME.apk 90 | rm app/build/outputs/apk/$APKNAME-signed.apk.idsig 91 | rm app/build/outputs/apk/unaligned.apk 92 | 93 | # Restore original AndroidManifest.xml 94 | mv app/src/main/AndroidManifest.xml.bak app/src/main/AndroidManifest.xml 95 | 96 | echo APK successfully created: app/build/outputs/apk/$APKNAME-signed.apk 97 | 98 | echo Debug moment 99 | echo Clear logcat 100 | $ADB logcat -c 101 | echo Installing APK 102 | $ADB install app/build/outputs/apk/$APKNAME-signed.apk 103 | echo Launching APK 104 | $ADB shell am start -n $PACKAGE_NAME/android.app.NativeActivity 105 | echo Starting logging 106 | $ADB logcat -s flappy 107 | 108 | exit 109 | -------------------------------------------------------------------------------- /FlappyBird/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | :: Load environment variables from .env file if it exists 5 | if exist ..\.env ( 6 | for /f "tokens=*" %%i in ('type ..\.env') do ( 7 | set %%i 8 | ) 9 | ) 10 | 11 | set PATH=%ANDROID_SDK_ROOT%\tools;%ANDROID_SDK_ROOT%\platform-tools;%PATH% 12 | 13 | set ADB=%ANDROID_SDK_ROOT%\platform-tools\adb.exe 14 | 15 | set APKNAME=FlappyBird 16 | set ANDROIDVERSION=30 17 | set ANDROIDTARGET=30 18 | 19 | set KEYSTORE_PASSWORD=%KEYSTORE_PASSWORD% 20 | 21 | echo Cleaning previous builds... 22 | rmdir /s /q app\build 23 | 24 | :: Backup original AndroidManifest.xml 25 | copy app\src\main\AndroidManifest.xml app\src\main\AndroidManifest.xml.bak 26 | 27 | :: Replace package name in AndroidManifest.xml 28 | powershell -Command "(gc app\src\main\AndroidManifest.xml) -replace 'your.app.package.name', '%PACKAGE_NAME%' | Out-File -encoding UTF8 app\src\main\AndroidManifest.xml" 29 | 30 | echo Creating build directories... 31 | mkdir app\build\intermediates\ndk 32 | mkdir app\build\outputs\apk 33 | 34 | echo Building native code... 35 | cd app\src\main 36 | call %ANDROID_NDK_ROOT%\ndk-build 37 | if %errorlevel% neq 0 ( 38 | echo Error building native code! 39 | echo Error code: %errorlevel% 40 | exit /b %errorlevel% 41 | ) 42 | cd ..\..\.. 43 | 44 | echo Creating empty APK... 45 | %ANDROID_SDK_ROOT%\build-tools\%BUILD_TOOLS_VERSION%\aapt package -f -M app\src\main\AndroidManifest.xml -S app\src\main\res -A app\src\main\assets -I %ANDROID_SDK_ROOT%\platforms\android-%ANDROIDTARGET%\android.jar -F app\build\outputs\apk\unaligned.apk 46 | if %errorlevel% neq 0 ( 47 | echo Error creating empty APK! 48 | echo Error code: %errorlevel% 49 | exit /b %errorlevel% 50 | ) 51 | 52 | mkdir lib 53 | 54 | :: Copy files from libs to a temporary folder 55 | xcopy "app\src\main\libs\*" "lib\" /E /I /Y 56 | 57 | :: Add the contents of the temporary folder to the archive in the lib folder 58 | start /min /wait WinRAR A -r app\build\outputs\apk\unaligned.apk "lib\*" "lib\" 59 | 60 | echo Aligning APK... 61 | %ANDROID_SDK_ROOT%\build-tools\%BUILD_TOOLS_VERSION%\zipalign -f 4 app\build\outputs\apk\unaligned.apk app\build\outputs\apk\%APKNAME%.apk 62 | if %errorlevel% neq 0 ( 63 | echo Error aligning APK! 64 | echo Error code: %errorlevel% 65 | exit /b %errorlevel% 66 | ) 67 | 68 | echo Signing APK... 69 | call %ANDROID_SDK_ROOT%\build-tools\%BUILD_TOOLS_VERSION%\apksigner sign --ks mykeystore.jks --ks-pass pass:%KEYSTORE_PASSWORD% --out app\build\outputs\apk\%APKNAME%-signed.apk app\build\outputs\apk\%APKNAME%.apk 70 | if %errorlevel% neq 0 ( 71 | echo Error signing APK! 72 | echo Error code: %errorlevel% 73 | exit /b %errorlevel% 74 | ) 75 | 76 | :: Delete temporary folder 77 | rmdir /S /Q lib 78 | 79 | echo Deleting unnecessary files... 80 | del /Q "app\build\outputs\apk\%APKNAME%.apk" 81 | del /Q "app\build\outputs\apk\%APKNAME%-signed.apk.idsig" 82 | del /Q "app\build\outputs\apk\unaligned.apk" 83 | 84 | echo APK successfully created: app\build\outputs\apk\%APKNAME%-signed.apk 85 | 86 | :: Debug moment 87 | echo Clear logcat 88 | %ADB% logcat -c 89 | echo Installing APK 90 | %ADB% install app\build\outputs\apk\%APKNAME%-signed.apk 91 | start /min timeout.exe 1 92 | echo Launching APK 93 | %ADB% shell am start -n %PACKAGE_NAME%/android.app.NativeActivity 94 | start /min timeout.exe 1 95 | echo Starting logging 96 | start %ADB% logcat -s flappy 97 | 98 | :: Restore original AndroidManifest.xml 99 | move /Y app\src\main\AndroidManifest.xml.bak app\src\main\AndroidManifest.xml 100 | 101 | exit 102 | 103 | endlocal -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/main.c: -------------------------------------------------------------------------------- 1 | #include "android_native_app_glue.h" 2 | #include "init.h" 3 | #include "utils.h" 4 | #include "mouse.h" 5 | #include 6 | 7 | 8 | #define TARGET_FPS 60 9 | #define TARGET_FRAME_TIME (1.0f / TARGET_FPS) 10 | 11 | double g_Time = 0.0; 12 | float DeltaTime = 0.0f; 13 | double g_LastFrameTime = 0.0; 14 | 15 | 16 | static void handleAppCmd(struct android_app* app, int32_t cmd) 17 | { 18 | switch (cmd) 19 | { 20 | case APP_CMD_INIT_WINDOW: 21 | Init(app); 22 | break; 23 | case APP_CMD_TERM_WINDOW: 24 | Shutdown(); 25 | break; 26 | case APP_CMD_GAINED_FOCUS: 27 | break; 28 | case APP_CMD_LOST_FOCUS: 29 | break; 30 | } 31 | } 32 | 33 | int32_t handle_input(struct android_app* app, AInputEvent* event) 34 | { 35 | int32_t eventType = AInputEvent_getType(event); 36 | 37 | if (eventType == AINPUT_EVENT_TYPE_MOTION) 38 | { 39 | int32_t action = AMotionEvent_getAction(event); 40 | action &= AMOTION_EVENT_ACTION_MASK; 41 | int whichsource = action >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; 42 | size_t pointerCount = AMotionEvent_getPointerCount(event); 43 | float x = 0.0f; 44 | float y = 0.0f; 45 | bool isDown = false; 46 | bool isReleased = false; 47 | bool isMoved = false; 48 | int index; 49 | 50 | for (size_t i = 0; i < pointerCount; ++i) 51 | { 52 | x = AMotionEvent_getX(event, i); 53 | y = AMotionEvent_getY(event, i); 54 | index = AMotionEvent_getPointerId(event, i); 55 | 56 | if (action == AMOTION_EVENT_ACTION_POINTER_DOWN || action == AMOTION_EVENT_ACTION_DOWN) 57 | { 58 | int id = index; 59 | if (action == AMOTION_EVENT_ACTION_POINTER_DOWN && id != whichsource) continue; 60 | 61 | mouse.x = x; 62 | mouse.y = y; 63 | mouse.isDown = true; 64 | } 65 | else if (action == AMOTION_EVENT_ACTION_POINTER_UP || action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL) 66 | { 67 | int id = index; 68 | if (action == AMOTION_EVENT_ACTION_POINTER_UP && id != whichsource) continue; 69 | 70 | mouse.x = x; 71 | mouse.y = y; 72 | mouse.isReleased = true; 73 | } 74 | else if (action == AMOTION_EVENT_ACTION_MOVE) 75 | { 76 | mouse.isMoved = true; 77 | 78 | mouse.x = x; 79 | mouse.y = y; 80 | } 81 | } 82 | 83 | return 1; 84 | } 85 | //else if (eventType == AINPUT_EVENT_TYPE_KEY) //Apparently, this applies to physical keys, including volume control keys. 86 | //{ 87 | // return 0; 88 | //} 89 | return 0; //Return 0 if you are not processing the event 90 | } 91 | 92 | void android_main(struct android_app* state) 93 | { 94 | state->onInputEvent = handle_input; 95 | state->onAppCmd = handleAppCmd; 96 | 97 | MouseInit(&mouse); 98 | 99 | while (1) 100 | { 101 | int ident; 102 | int events; 103 | struct android_poll_source* source; 104 | 105 | // Poll all events until we reach return value TIMEOUT, meaning no events left to process 106 | while ((ident = ALooper_pollOnce(0, NULL, &events, (void**)&source)) > ALOOPER_POLL_TIMEOUT) 107 | { 108 | if (source != NULL) { 109 | source->process(state, source); 110 | } 111 | 112 | if (state->destroyRequested != 0) { 113 | Shutdown(); 114 | return; 115 | } 116 | } 117 | 118 | if (g_Initialized) 119 | { 120 | struct timespec current_timespec; 121 | clock_gettime(CLOCK_MONOTONIC, ¤t_timespec); 122 | double current_time = (double)(current_timespec.tv_sec) + (current_timespec.tv_nsec / 1000000000.0); 123 | 124 | // calc delta time 125 | DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)TARGET_FRAME_TIME; 126 | 127 | // checking if enough time has passed for a new frame 128 | if (current_time - g_LastFrameTime >= TARGET_FRAME_TIME) 129 | { 130 | MainLoopStep(); 131 | MouseReset(&mouse); 132 | 133 | // update time last frame 134 | g_LastFrameTime = current_time; 135 | } 136 | 137 | g_Time = current_time; 138 | 139 | // timeout for thread to fix excessive CPU consumption 140 | usleep(1000); 141 | } 142 | } 143 | } -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/init.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "init.h" 6 | #include "utils.h" 7 | #include "texture.h" 8 | #include "shaders.h" 9 | #include "audio.h" 10 | #include "game.h" 11 | 12 | 13 | bool g_Initialized = false; 14 | EGLDisplay g_EglDisplay = EGL_NO_DISPLAY; 15 | EGLSurface g_EglSurface = EGL_NO_SURFACE; 16 | EGLContext g_EglContext = EGL_NO_CONTEXT; 17 | struct android_app* g_App = NULL; 18 | 19 | int32_t WindowSizeX = 0; 20 | int32_t WindowSizeY = 0; 21 | 22 | GLuint textureProgram; 23 | 24 | GLuint colorProgram; 25 | GLuint gPositionHandle; 26 | GLuint gColorHandle; 27 | 28 | //fix by Tempa 29 | const char* vertexShaderTexture = 30 | "attribute vec4 aPosition;" 31 | "attribute vec2 aTexCoord;" 32 | "varying vec2 vTexCoord;" 33 | "void main()" 34 | "{" 35 | "gl_Position = aPosition;" 36 | "vTexCoord = aTexCoord;" 37 | "}"; 38 | 39 | const char* fragmentShaderTexture = 40 | "precision mediump float;" 41 | "varying vec2 vTexCoord;" 42 | "uniform sampler2D uTexture;" 43 | "void main()" 44 | "{" 45 | "vec4 texColor = texture2D(uTexture, vTexCoord);" 46 | "if (texColor.rgb == vec3(0.0))" 47 | "{" 48 | "texColor.a = 0.0;" 49 | "}" 50 | "gl_FragColor = texColor;" 51 | "}"; 52 | 53 | //by vadim 54 | const char* vertexShaderColor = 55 | "attribute vec4 a_Position;\n" 56 | "void main() {\n" 57 | " gl_Position = a_Position;\n" 58 | "}\n"; 59 | 60 | const char* fragmentShaderColor = 61 | "precision mediump float;\n" 62 | "uniform vec4 u_Color;\n" 63 | "void main() {\n" 64 | " gl_FragColor = u_Color;\n" 65 | "}\n"; 66 | 67 | 68 | void Init(struct android_app* app) 69 | { 70 | if (g_Initialized) 71 | return; 72 | 73 | g_App = app; 74 | ANativeWindow_acquire(g_App->window); 75 | 76 | // Initialize EGL 77 | g_EglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); 78 | if (g_EglDisplay == EGL_NO_DISPLAY) 79 | { 80 | Log("eglGetDisplay(EGL_DEFAULT_DISPLAY) returned EGL_NO_DISPLAY"); 81 | return; 82 | } 83 | 84 | if (eglInitialize(g_EglDisplay, 0, 0) != EGL_TRUE) 85 | { 86 | Log("eglInitialize() returned with an error"); 87 | return; 88 | } 89 | 90 | const EGLint egl_attributes[] = { 91 | EGL_BLUE_SIZE, 8, 92 | EGL_GREEN_SIZE, 8, 93 | EGL_RED_SIZE, 8, 94 | EGL_DEPTH_SIZE, 24, 95 | EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 96 | EGL_NONE 97 | }; 98 | EGLint num_configs = 0; 99 | if (eglChooseConfig(g_EglDisplay, egl_attributes, NULL, 0, &num_configs) != EGL_TRUE) 100 | { 101 | Log("eglChooseConfig() returned with an error"); 102 | return; 103 | } 104 | if (num_configs == 0) 105 | { 106 | Log("eglChooseConfig() returned 0 matching config"); 107 | return; 108 | } 109 | 110 | EGLConfig egl_config; 111 | eglChooseConfig(g_EglDisplay, egl_attributes, &egl_config, 1, &num_configs); 112 | EGLint egl_format; 113 | eglGetConfigAttrib(g_EglDisplay, egl_config, EGL_NATIVE_VISUAL_ID, &egl_format); 114 | ANativeWindow_setBuffersGeometry(g_App->window, 0, 0, egl_format); 115 | 116 | const EGLint egl_context_attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; 117 | g_EglContext = eglCreateContext(g_EglDisplay, egl_config, EGL_NO_CONTEXT, egl_context_attributes); 118 | if (g_EglContext == EGL_NO_CONTEXT) 119 | { 120 | Log("eglCreateContext() returned EGL_NO_CONTEXT"); 121 | return; 122 | } 123 | 124 | g_EglSurface = eglCreateWindowSurface(g_EglDisplay, egl_config, g_App->window, NULL); 125 | if (g_EglSurface == EGL_NO_SURFACE) 126 | { 127 | Log("eglCreateWindowSurface() returned EGL_NO_SURFACE"); 128 | return; 129 | } 130 | 131 | if (eglMakeCurrent(g_EglDisplay, g_EglSurface, g_EglSurface, g_EglContext) != EGL_TRUE) 132 | { 133 | Log("eglMakeCurrent() returned with an error"); 134 | return; 135 | } 136 | 137 | // Set window size 138 | WindowSizeX = ANativeWindow_getWidth(g_App->window); 139 | WindowSizeY = ANativeWindow_getHeight(g_App->window); 140 | 141 | if (!InitGame()) 142 | { 143 | Log("Game not init!"); 144 | return; 145 | } 146 | 147 | CreateAudioEngine(); 148 | 149 | // Create shader program 150 | textureProgram = createProgram(vertexShaderTexture, fragmentShaderTexture); 151 | 152 | colorProgram = createProgram(vertexShaderColor, fragmentShaderColor); 153 | gPositionHandle = glGetAttribLocation(colorProgram, "a_Position"); 154 | gColorHandle = glGetUniformLocation(colorProgram, "u_Color"); 155 | 156 | 157 | Log("FlappyBird is loaded!"); 158 | 159 | g_Initialized = true; 160 | } 161 | 162 | void MainLoopStep() 163 | { 164 | if (g_EglDisplay == EGL_NO_DISPLAY) 165 | return; 166 | 167 | // Setup display size (every frame to accommodate for window resizing) 168 | glViewport(0, 0, (int)WindowSizeX, (int)WindowSizeY); 169 | glClearColor(1.0f, 1.0f, 1.0f, 1.0f); 170 | glClear(GL_COLOR_BUFFER_BIT); 171 | 172 | Render(); 173 | 174 | eglSwapBuffers(g_EglDisplay, g_EglSurface); 175 | } 176 | 177 | void Shutdown() 178 | { 179 | if (!g_Initialized) 180 | return; 181 | 182 | // Cleanup 183 | if (g_EglDisplay != EGL_NO_DISPLAY) 184 | { 185 | eglMakeCurrent(g_EglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 186 | 187 | if (g_EglContext != EGL_NO_CONTEXT) 188 | eglDestroyContext(g_EglDisplay, g_EglContext); 189 | 190 | if (g_EglSurface != EGL_NO_SURFACE) 191 | eglDestroySurface(g_EglDisplay, g_EglSurface); 192 | 193 | eglTerminate(g_EglDisplay); 194 | } 195 | 196 | g_EglDisplay = EGL_NO_DISPLAY; 197 | g_EglContext = EGL_NO_CONTEXT; 198 | g_EglSurface = EGL_NO_SURFACE; 199 | ANativeWindow_release(g_App->window); 200 | 201 | ShutdownGame(); 202 | glDeleteProgram(textureProgram); 203 | glDeleteProgram(colorProgram); 204 | 205 | g_Initialized = false; 206 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # FlappyBird build artifacts 20 | FlappyBird/app/build/ 21 | FlappyBird/app/src/main/libs/*/libflappybird.so 22 | FlappyBird/mykeystore.jks 23 | 24 | # Environment variables file 25 | .env 26 | 27 | # Build results 28 | [Dd]ebug/ 29 | [Dd]ebugPublic/ 30 | [Rr]elease/ 31 | [Rr]eleases/ 32 | x64/ 33 | x86/ 34 | [Ww][Ii][Nn]32/ 35 | [Aa][Rr][Mm]/ 36 | [Aa][Rr][Mm]64/ 37 | bld/ 38 | [Bb]in/ 39 | [Oo]bj/ 40 | [Oo]ut/ 41 | [Ll]og/ 42 | [Ll]ogs/ 43 | 44 | # Visual Studio 2015/2017 cache/options directory 45 | .vs/ 46 | # Uncomment if you have tasks that create the project's static files in wwwroot 47 | #wwwroot/ 48 | 49 | # Visual Studio 2017 auto generated files 50 | Generated\ Files/ 51 | 52 | # MSTest test Results 53 | [Tt]est[Rr]esult*/ 54 | [Bb]uild[Ll]og.* 55 | 56 | # NUnit 57 | *.VisualState.xml 58 | TestResult.xml 59 | nunit-*.xml 60 | 61 | # Build Results of an ATL Project 62 | [Dd]ebugPS/ 63 | [Rr]eleasePS/ 64 | dlldata.c 65 | 66 | # Benchmark Results 67 | BenchmarkDotNet.Artifacts/ 68 | 69 | # .NET Core 70 | project.lock.json 71 | project.fragment.lock.json 72 | artifacts/ 73 | 74 | # ASP.NET Scaffolding 75 | ScaffoldingReadMe.txt 76 | 77 | # StyleCop 78 | StyleCopReport.xml 79 | 80 | # Files built by Visual Studio 81 | *_i.c 82 | *_p.c 83 | *_h.h 84 | *.ilk 85 | *.meta 86 | *.obj 87 | *.iobj 88 | *.pch 89 | *.pdb 90 | *.ipdb 91 | *.pgc 92 | *.pgd 93 | *.rsp 94 | *.sbr 95 | *.tlb 96 | *.tli 97 | *.tlh 98 | *.tmp 99 | *.tmp_proj 100 | *_wpftmp.csproj 101 | *.log 102 | *.vspscc 103 | *.vssscc 104 | .builds 105 | *.pidb 106 | *.svclog 107 | *.scc 108 | 109 | # Chutzpah Test files 110 | _Chutzpah* 111 | 112 | # Visual C++ cache files 113 | ipch/ 114 | *.aps 115 | *.ncb 116 | *.opendb 117 | *.opensdf 118 | *.sdf 119 | *.cachefile 120 | *.VC.db 121 | *.VC.VC.opendb 122 | 123 | # Visual Studio profiler 124 | *.psess 125 | *.vsp 126 | *.vspx 127 | *.sap 128 | 129 | # Visual Studio Trace Files 130 | *.e2e 131 | 132 | # TFS 2012 Local Workspace 133 | $tf/ 134 | 135 | # Guidance Automation Toolkit 136 | *.gpState 137 | 138 | # ReSharper is a .NET coding add-in 139 | _ReSharper*/ 140 | *.[Rr]e[Ss]harper 141 | *.DotSettings.user 142 | 143 | # TeamCity is a build add-in 144 | _TeamCity* 145 | 146 | # DotCover is a Code Coverage Tool 147 | *.dotCover 148 | 149 | # AxoCover is a Code Coverage Tool 150 | .axoCover/* 151 | !.axoCover/settings.json 152 | 153 | # Coverlet is a free, cross platform Code Coverage Tool 154 | coverage*.json 155 | coverage*.xml 156 | coverage*.info 157 | 158 | # Visual Studio code coverage results 159 | *.coverage 160 | *.coveragexml 161 | 162 | # NCrunch 163 | _NCrunch_* 164 | .*crunch*.local.xml 165 | nCrunchTemp_* 166 | 167 | # MightyMoose 168 | *.mm.* 169 | AutoTest.Net/ 170 | 171 | # Web workbench (sass) 172 | .sass-cache/ 173 | 174 | # Installshield output folder 175 | [Ee]xpress/ 176 | 177 | # DocProject is a documentation generator add-in 178 | DocProject/buildhelp/ 179 | DocProject/Help/*.HxT 180 | DocProject/Help/*.HxC 181 | DocProject/Help/*.hhc 182 | DocProject/Help/*.hhk 183 | DocProject/Help/*.hhp 184 | DocProject/Help/Html2 185 | DocProject/Help/html 186 | 187 | # Click-Once directory 188 | publish/ 189 | 190 | # Publish Web Output 191 | *.[Pp]ublish.xml 192 | *.azurePubxml 193 | # Note: Comment the next line if you want to checkin your web deploy settings, 194 | # but database connection strings (with potential passwords) will be unencrypted 195 | *.pubxml 196 | *.publishproj 197 | 198 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 199 | # checkin your Azure Web App publish settings, but sensitive information contained 200 | # in these scripts will be unencrypted 201 | PublishScripts/ 202 | 203 | # NuGet Packages 204 | *.nupkg 205 | # NuGet Symbol Packages 206 | *.snupkg 207 | # The packages folder can be ignored because of Package Restore 208 | **/[Pp]ackages/* 209 | # except build/, which is used as an MSBuild target. 210 | !**/[Pp]ackages/build/ 211 | # Uncomment if necessary however generally it will be regenerated when needed 212 | #!**/[Pp]ackages/repositories.config 213 | # NuGet v3's project.json files produces more ignorable files 214 | *.nuget.props 215 | *.nuget.targets 216 | 217 | # Microsoft Azure Build Output 218 | csx/ 219 | *.build.csdef 220 | 221 | # Microsoft Azure Emulator 222 | ecf/ 223 | rcf/ 224 | 225 | # Windows Store app package directories and files 226 | AppPackages/ 227 | BundleArtifacts/ 228 | Package.StoreAssociation.xml 229 | _pkginfo.txt 230 | *.appx 231 | *.appxbundle 232 | *.appxupload 233 | 234 | # Visual Studio cache files 235 | # files ending in .cache can be ignored 236 | *.[Cc]ache 237 | # but keep track of directories ending in .cache 238 | !?*.[Cc]ache/ 239 | 240 | # Others 241 | ClientBin/ 242 | ~$* 243 | *~ 244 | *.dbmdl 245 | *.dbproj.schemaview 246 | *.jfm 247 | *.pfx 248 | *.publishsettings 249 | orleans.codegen.cs 250 | 251 | # Including strong name files can present a security risk 252 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 253 | #*.snk 254 | 255 | # Since there are multiple workflows, uncomment next line to ignore bower_components 256 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 257 | #bower_components/ 258 | 259 | # RIA/Silverlight projects 260 | Generated_Code/ 261 | 262 | # Backup & report files from converting an old project file 263 | # to a newer Visual Studio version. Backup files are not needed, 264 | # because we have git ;-) 265 | _UpgradeReport_Files/ 266 | Backup*/ 267 | UpgradeLog*.XML 268 | UpgradeLog*.htm 269 | ServiceFabricBackup/ 270 | *.rptproj.bak 271 | 272 | # SQL Server files 273 | *.mdf 274 | *.ldf 275 | *.ndf 276 | 277 | # Business Intelligence projects 278 | *.rdl.data 279 | *.bim.layout 280 | *.bim_*.settings 281 | *.rptproj.rsuser 282 | *- [Bb]ackup.rdl 283 | *- [Bb]ackup ([0-9]).rdl 284 | *- [Bb]ackup ([0-9][0-9]).rdl 285 | 286 | # Microsoft Fakes 287 | FakesAssemblies/ 288 | 289 | # GhostDoc plugin setting file 290 | *.GhostDoc.xml 291 | 292 | # Node.js Tools for Visual Studio 293 | .ntvs_analysis.dat 294 | node_modules/ 295 | 296 | # Visual Studio 6 build log 297 | *.plg 298 | 299 | # Visual Studio 6 workspace options file 300 | *.opt 301 | 302 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 303 | *.vbw 304 | 305 | # Visual Studio LightSwitch build output 306 | **/*.HTMLClient/GeneratedArtifacts 307 | **/*.DesktopClient/GeneratedArtifacts 308 | **/*.DesktopClient/ModelManifest.xml 309 | **/*.Server/GeneratedArtifacts 310 | **/*.Server/ModelManifest.xml 311 | _Pvt_Extensions 312 | 313 | # Paket dependency manager 314 | .paket/paket.exe 315 | paket-files/ 316 | 317 | # FAKE - F# Make 318 | .fake/ 319 | 320 | # CodeRush personal settings 321 | .cr/personal 322 | 323 | # Python Tools for Visual Studio (PTVS) 324 | __pycache__/ 325 | *.pyc 326 | 327 | # Cake - Uncomment if you are using it 328 | # tools/** 329 | # !tools/packages.config 330 | 331 | # Tabs Studio 332 | *.tss 333 | 334 | # Telerik's JustMock configuration file 335 | *.jmconfig 336 | 337 | # BizTalk build output 338 | *.btp.cs 339 | *.btm.cs 340 | *.odx.cs 341 | *.xsd.cs 342 | 343 | # OpenCover UI analysis results 344 | OpenCover/ 345 | 346 | # Azure Stream Analytics local run output 347 | ASALocalRun/ 348 | 349 | # MSBuild Binary and Structured Log 350 | *.binlog 351 | 352 | # NVidia Nsight GPU debugger configuration file 353 | *.nvuser 354 | 355 | # MFractors (Xamarin productivity tool) working folder 356 | .mfractor/ 357 | 358 | # Local History for Visual Studio 359 | .localhistory/ 360 | 361 | # BeatPulse healthcheck temp database 362 | healthchecksdb 363 | 364 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 365 | MigrationBackup/ 366 | 367 | # Ionide (cross platform F# VS Code tools) working folder 368 | .ionide/ 369 | 370 | # Fody - auto-generated XML schema 371 | FodyWeavers.xsd -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/texture.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "upng.h" 5 | #include "texture.h" 6 | #include "utils.h" 7 | #include "init.h" 8 | #include 9 | 10 | GLuint LoadTexture(const char* assetPath) 11 | { 12 | AAsset* file = AAssetManager_open(g_App->activity->assetManager, assetPath, AASSET_MODE_BUFFER); 13 | if (!file) 14 | { 15 | Log("Failed to open asset file: %s", assetPath); 16 | return 0; 17 | } 18 | 19 | unsigned char* buffer = (unsigned char*)AAsset_getBuffer(file); 20 | unsigned long len_file = AAsset_getLength(file); 21 | 22 | upng_t* png = upng_new_from_bytes(buffer, len_file); 23 | if (png == NULL) 24 | { 25 | Log("Error creating PNG from file: %s", assetPath); 26 | AAsset_close(file); 27 | return 0; 28 | } 29 | 30 | upng_decode(png); 31 | if (upng_get_error(png) != UPNG_EOK) 32 | { 33 | Log("Error decoding PNG from file: %s", assetPath); 34 | upng_free(png); 35 | AAsset_close(file); 36 | return 0; 37 | } 38 | 39 | unsigned width = upng_get_width(png); 40 | unsigned height = upng_get_height(png); 41 | 42 | //glEnable(GL_TEXTURE_2D); 43 | glEnable(GL_BLEND); 44 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 45 | GLuint texture; 46 | glGenTextures(1, &texture); 47 | glBindTexture(GL_TEXTURE_2D, texture); 48 | 49 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 50 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 51 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 52 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 53 | 54 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, upng_get_buffer(png)); 55 | 56 | upng_free(png); 57 | 58 | AAsset_close(file); 59 | 60 | if (!texture) 61 | { 62 | Log("Error load texture '%s'", assetPath); 63 | return 0; 64 | } 65 | else 66 | { 67 | Log("Texture '%s' is loaded!", assetPath); 68 | } 69 | 70 | return texture; 71 | } 72 | 73 | void RenderTexture(GLuint texture, float x, float y, float width, float height) 74 | { 75 | glUseProgram(textureProgram); 76 | 77 | // pixel coordinates to normal coordinates 78 | float normalized_x = (2.0f * x / WindowSizeX) - 1.0f; 79 | float normalized_y = 1.0f - (2.0f * y / WindowSizeY); 80 | float normalized_width = 2.0f * width / WindowSizeX; 81 | float normalized_height = 2.0f * height / WindowSizeY; 82 | 83 | GLfloat vertices[] = { 84 | normalized_x, normalized_y, 0.0f, 0.0f, 85 | normalized_x + normalized_width, normalized_y, 1.0f, 0.0f, 86 | normalized_x + normalized_width, normalized_y - normalized_height, 1.0f, 1.0f, 87 | normalized_x, normalized_y - normalized_height, 0.0f, 1.0f 88 | }; 89 | 90 | GLuint indices[] = { 91 | 0, 1, 2, 92 | 2, 3, 0 93 | }; 94 | 95 | GLuint vbo, ebo; 96 | glGenBuffers(1, &vbo); 97 | glGenBuffers(1, &ebo); 98 | 99 | glBindBuffer(GL_ARRAY_BUFFER, vbo); 100 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 101 | 102 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); 103 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); 104 | 105 | GLint positionAttrib = glGetAttribLocation(textureProgram, "aPosition"); 106 | glEnableVertexAttribArray(positionAttrib); 107 | glVertexAttribPointer(positionAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)0); 108 | 109 | GLint texCoordAttrib = glGetAttribLocation(textureProgram, "aTexCoord"); 110 | glEnableVertexAttribArray(texCoordAttrib); 111 | glVertexAttribPointer(texCoordAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat))); 112 | 113 | glActiveTexture(GL_TEXTURE0); 114 | glBindTexture(GL_TEXTURE_2D, texture); 115 | glUniform1i(glGetUniformLocation(textureProgram, "uTexture"), 0); 116 | 117 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); 118 | 119 | glDeleteBuffers(1, &vbo); 120 | glDeleteBuffers(1, &ebo); 121 | } 122 | 123 | void RenderTexturePro(GLuint texture, float x, float y, float width, float height, float angle) 124 | { 125 | glUseProgram(textureProgram); 126 | 127 | // calculate aspect ratio 128 | float aspect_ratio = (float)WindowSizeX / (float)WindowSizeY; 129 | 130 | // convert angle to radians 131 | float angle_rad = angle * M_PI / 180.0f; 132 | 133 | // calculate the rotation matrix 134 | float cos_angle = cos(angle_rad); 135 | float sin_angle = sin(angle_rad); 136 | 137 | // calculate the center of the texture in pixel coordinates 138 | float center_x = x + width / 2.0f; 139 | float center_y = y + height / 2.0f; 140 | 141 | // calculate the transformation matrix 142 | float transform[4][4] = { 143 | {cos_angle, -sin_angle, 0.0f, center_x * (1.0f - cos_angle) + center_y * sin_angle}, 144 | {sin_angle, cos_angle, 0.0f, center_y * (1.0f - cos_angle) - center_x * sin_angle}, 145 | {0.0f, 0.0f, 1.0f, 0.0f}, 146 | {0.0f, 0.0f, 0.0f, 1.0f} 147 | }; 148 | 149 | // calculate the vertices before transformation in pixel coordinates 150 | GLfloat vertices[] = { 151 | x, y, 0.0f, 0.0f, 152 | x + width, y, 1.0f, 0.0f, 153 | x + width, y + height, 1.0f, 1.0f, 154 | x, y + height, 0.0f, 1.0f 155 | }; 156 | 157 | // apply the transformation matrix to the vertices 158 | for (int i = 0; i < 4; ++i) { 159 | float x = vertices[i * 4]; 160 | float y = vertices[i * 4 + 1]; 161 | vertices[i * 4] = transform[0][0] * x + transform[0][1] * y + transform[0][3]; 162 | vertices[i * 4 + 1] = transform[1][0] * x + transform[1][1] * y + transform[1][3]; 163 | } 164 | 165 | // normalize the vertices to OpenGL coordinates 166 | for (int i = 0; i < 4; ++i) { 167 | vertices[i * 4] = (2.0f * vertices[i * 4] / WindowSizeX) - 1.0f; 168 | vertices[i * 4 + 1] = 1.0f - (2.0f * vertices[i * 4 + 1] / WindowSizeY); 169 | } 170 | 171 | GLuint indices[] = { 172 | 0, 1, 2, 173 | 2, 3, 0 174 | }; 175 | 176 | GLuint vbo, ebo; 177 | glGenBuffers(1, &vbo); 178 | glGenBuffers(1, &ebo); 179 | 180 | glBindBuffer(GL_ARRAY_BUFFER, vbo); 181 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 182 | 183 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); 184 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); 185 | 186 | GLint positionAttrib = glGetAttribLocation(textureProgram, "aPosition"); 187 | glEnableVertexAttribArray(positionAttrib); 188 | glVertexAttribPointer(positionAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)0); 189 | 190 | GLint texCoordAttrib = glGetAttribLocation(textureProgram, "aTexCoord"); 191 | glEnableVertexAttribArray(texCoordAttrib); 192 | glVertexAttribPointer(texCoordAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat))); 193 | 194 | glActiveTexture(GL_TEXTURE0); 195 | glBindTexture(GL_TEXTURE_2D, texture); 196 | glUniform1i(glGetUniformLocation(textureProgram, "uTexture"), 0); 197 | 198 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); 199 | 200 | glDeleteBuffers(1, &vbo); 201 | glDeleteBuffers(1, &ebo); 202 | } 203 | 204 | void CreateBox(uint32_t color, float posX, float posY, float width, float height) 205 | { 206 | float normalizedPosX = (2.0f * posX / WindowSizeX) - 1.0f; 207 | float normalizedPosY = 1.0f - (2.0f * posY / WindowSizeY); 208 | float normalizedWidth = 2.0f * width / WindowSizeX; 209 | float normalizedHeight = 2.0f * height / WindowSizeY; 210 | 211 | float vertices[] = { 212 | normalizedPosX, normalizedPosY, 213 | normalizedPosX + normalizedWidth, normalizedPosY, 214 | normalizedPosX, normalizedPosY - normalizedHeight, 215 | normalizedPosX + normalizedWidth, normalizedPosY - normalizedHeight 216 | }; 217 | 218 | glUseProgram(colorProgram); 219 | 220 | glVertexAttribPointer(gPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, vertices); 221 | glEnableVertexAttribArray(gPositionHandle); 222 | 223 | float r = ((color >> 16) & 0xFF) / 255.0f; 224 | float g = ((color >> 8) & 0xFF) / 255.0f; 225 | float b = (color & 0xFF) / 255.0f; 226 | float a = ((color >> 24) & 0xFF) / 255.0f; 227 | 228 | glUniform4f(gColorHandle, r, g, b, a); 229 | 230 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 231 | } -------------------------------------------------------------------------------- /FlappyBird/FlappyBird.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | ARM 7 | 8 | 9 | Release 10 | ARM 11 | 12 | 13 | Debug 14 | ARM64 15 | 16 | 17 | Release 18 | ARM64 19 | 20 | 21 | Debug 22 | x64 23 | 24 | 25 | Release 26 | x64 27 | 28 | 29 | Debug 30 | x86 31 | 32 | 33 | Release 34 | x86 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | {f1bb0f05-20a0-4ea7-a326-1516c80260d7} 66 | Android 67 | FlappyBird 68 | 14.0 69 | Android 70 | 3.0 71 | 72 | 73 | 74 | DynamicLibrary 75 | true 76 | Clang_5_0 77 | 78 | 79 | DynamicLibrary 80 | false 81 | Clang_5_0 82 | 83 | 84 | Makefile 85 | true 86 | Clang_5_0 87 | android-21 88 | 89 | 90 | DynamicLibrary 91 | false 92 | Clang_5_0 93 | 94 | 95 | DynamicLibrary 96 | true 97 | Clang_5_0 98 | 99 | 100 | DynamicLibrary 101 | false 102 | Clang_5_0 103 | 104 | 105 | DynamicLibrary 106 | true 107 | Clang_5_0 108 | 109 | 110 | DynamicLibrary 111 | false 112 | Clang_5_0 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | "$(MSBuildProjectDirectory)\build.bat" 133 | "$(MSBuildProjectDirectory)\build.bat" 134 | $(VS_NdkRoot)\toolchains\llvm\prebuilt\windows-x86_64\lib\clang\18\include;$(StlIncludeDirectories);$(Sysroot)\usr\include;$(VS_NdkRoot)\sources\android\ndk_helper;$(VS_NdkRoot)\toolchains\llvm\prebuilt\windows-x86_64\sysroot\usr\include;$(VS_NdkRoot)\sources\android\cpufeatures;$(VS_NdkRoot)\sources\android\native_app_glue 135 | 136 | 137 | __ANDROID_API__=30 138 | 139 | 140 | 141 | 142 | 143 | Use 144 | pch.h 145 | 146 | 147 | 148 | 149 | Use 150 | pch.h 151 | 152 | 153 | 154 | 155 | Use 156 | pch.h 157 | 158 | 159 | 160 | 161 | Use 162 | pch.h 163 | 164 | 165 | 166 | 167 | Use 168 | pch.h 169 | 170 | 171 | 172 | 173 | Use 174 | pch.h 175 | 176 | 177 | 178 | 179 | Use 180 | pch.h 181 | 182 | 183 | 184 | 185 | Use 186 | pch.h 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/audio.c: -------------------------------------------------------------------------------- 1 | #include "audio.h" 2 | #include "utils.h" 3 | #include "init.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include // close 9 | 10 | #define MAX_PLAYERS 5 11 | #define MAX_USES 5 12 | 13 | typedef struct { 14 | SLObjectItf playerObject; 15 | SLPlayItf playerPlay; 16 | SLSeekItf playerSeek; 17 | int fd; // file descriptor for the audio file 18 | int uses; 19 | } AudioPlayer; 20 | 21 | SLObjectItf engineObject = NULL; 22 | SLEngineItf engineEngine; 23 | SLObjectItf outputMixObject = NULL; 24 | AudioPlayer players[MAX_PLAYERS] = { { NULL, NULL, NULL, -1, 0 } }; 25 | int currentPlayerIndex = 0; 26 | 27 | void DestroyUnusedPlayers() 28 | { 29 | for (int i = 0; i < MAX_PLAYERS; ++i) 30 | { 31 | if (players[i].playerPlay) 32 | { 33 | SLuint32 state; 34 | SLresult result = (*players[i].playerPlay)->GetPlayState(players[i].playerPlay, &state); 35 | if (result == SL_RESULT_SUCCESS && state == SL_PLAYSTATE_STOPPED) 36 | { 37 | Log("Destroying unused player %d", i); 38 | (*players[i].playerObject)->Destroy(players[i].playerObject); 39 | close(players[i].fd); // close the file descriptor 40 | players[i].playerObject = NULL; 41 | players[i].playerPlay = NULL; 42 | players[i].playerSeek = NULL; 43 | players[i].fd = -1; 44 | players[i].uses = 0; 45 | } 46 | } 47 | } 48 | } 49 | 50 | void CreateAudioEngine() 51 | { 52 | SLresult result; 53 | 54 | // create engine 55 | result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); 56 | if (result != SL_RESULT_SUCCESS) 57 | { 58 | Log("Failed to create engine: %d", result); 59 | return; 60 | } 61 | 62 | // init engine 63 | result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); 64 | if (result != SL_RESULT_SUCCESS) 65 | { 66 | Log("Failed to realize engine: %d", result); 67 | return; 68 | } 69 | 70 | // get interface engine 71 | result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); 72 | if (result != SL_RESULT_SUCCESS) 73 | { 74 | Log("Failed to get engine interface: %d", result); 75 | return; 76 | } 77 | 78 | // create output sound 79 | const SLInterfaceID ids[1] = { SL_IID_ENVIRONMENTALREVERB }; 80 | const SLboolean req[1] = { SL_BOOLEAN_FALSE }; 81 | result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req); 82 | if (result != SL_RESULT_SUCCESS) 83 | { 84 | Log("Failed to create output mix: %d", result); 85 | return; 86 | } 87 | 88 | // init output 89 | result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); 90 | if (result != SL_RESULT_SUCCESS) 91 | { 92 | Log("Failed to realize output mix: %d", result); 93 | return; 94 | } 95 | } 96 | 97 | void CreateAudioPlayer(AudioPlayer* player, const char* assetPath) 98 | { 99 | SLresult result; 100 | 101 | AAsset* audioAsset = AAssetManager_open(g_App->activity->assetManager, assetPath, AASSET_MODE_BUFFER); 102 | if (!audioAsset) 103 | { 104 | Log("Failed to open asset file: %s", assetPath); 105 | return; 106 | } 107 | 108 | off_t start, length; 109 | int fd = AAsset_openFileDescriptor(audioAsset, &start, &length); 110 | AAsset_close(audioAsset); 111 | 112 | if (fd < 0) 113 | { 114 | Log("Failed to get file descriptor for asset: %s", assetPath); 115 | return; 116 | } 117 | 118 | // create source data 119 | SLDataLocator_AndroidFD loc_fd = { SL_DATALOCATOR_ANDROIDFD, fd, start, length }; 120 | SLDataFormat_MIME format_mime = { SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_MP3 }; 121 | SLDataSource audioSrc = { &loc_fd, &format_mime }; 122 | 123 | // setting receive data 124 | SLDataLocator_OutputMix loc_outmix = { SL_DATALOCATOR_OUTPUTMIX, outputMixObject }; 125 | SLDataSink audioSnk = { &loc_outmix, NULL }; 126 | 127 | // create player 128 | const SLInterfaceID ids[3] = { SL_IID_SEEK, SL_IID_MUTESOLO, SL_IID_VOLUME }; 129 | const SLboolean req[3] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; 130 | result = (*engineEngine)->CreateAudioPlayer(engineEngine, &player->playerObject, &audioSrc, &audioSnk, 3, ids, req); 131 | if (result != SL_RESULT_SUCCESS) 132 | { 133 | Log("Failed to create audio player: %d", result); 134 | close(fd); 135 | return; 136 | } 137 | 138 | // init player 139 | result = (*player->playerObject)->Realize(player->playerObject, SL_BOOLEAN_FALSE); 140 | if (result != SL_RESULT_SUCCESS) 141 | { 142 | Log("Failed to realize player: %d", result); 143 | close(fd); 144 | return; 145 | } 146 | 147 | // get interface player 148 | result = (*player->playerObject)->GetInterface(player->playerObject, SL_IID_PLAY, &player->playerPlay); 149 | if (result != SL_RESULT_SUCCESS) 150 | { 151 | Log("Failed to get play interface: %d", result); 152 | close(fd); 153 | return; 154 | } 155 | 156 | // get interface seek 157 | result = (*player->playerObject)->GetInterface(player->playerObject, SL_IID_SEEK, &player->playerSeek); 158 | if (result != SL_RESULT_SUCCESS) 159 | { 160 | Log("Failed to get seek interface: %d", result); 161 | close(fd); 162 | return; 163 | } 164 | 165 | player->fd = fd; // save descriptor 166 | player->uses = 0; // reset counter 167 | Log("Successfully created audio player for asset: %s", assetPath); 168 | } 169 | 170 | void PlayAudio(const char* assetPath) 171 | { 172 | SLresult result; 173 | 174 | DestroyUnusedPlayers(); 175 | 176 | // find an available player 177 | SLPlayItf playerPlay = NULL; 178 | int availableIndex = -1; 179 | for (int i = 0; i < MAX_PLAYERS; ++i) 180 | { 181 | if (players[i].playerPlay) 182 | { 183 | SLuint32 state; 184 | result = (*players[i].playerPlay)->GetPlayState(players[i].playerPlay, &state); 185 | if (result == SL_RESULT_SUCCESS && state == SL_PLAYSTATE_STOPPED) 186 | { 187 | playerPlay = players[i].playerPlay; 188 | availableIndex = i; 189 | Log("Reusing player %d for asset: %s", i, assetPath); 190 | break; 191 | } 192 | } 193 | } 194 | 195 | // if no available player, create a new one 196 | if (!playerPlay) 197 | { 198 | for (int i = 0; i < MAX_PLAYERS; ++i) 199 | { 200 | if (!players[i].playerPlay) 201 | { 202 | CreateAudioPlayer(&players[i], assetPath); 203 | playerPlay = players[i].playerPlay; 204 | availableIndex = i; 205 | Log("Created new player %d for asset: %s", i, assetPath); 206 | break; 207 | } 208 | } 209 | } 210 | 211 | // if still no available player, destroy the next player in the cycle and create a new one 212 | if (!playerPlay) 213 | { 214 | int oldestIndex = currentPlayerIndex; 215 | currentPlayerIndex = (currentPlayerIndex + 1) % MAX_PLAYERS; // move to the next player in the cycle 216 | 217 | Log("Destroying player %d to create a new one for asset: %s", oldestIndex, assetPath); 218 | (*players[oldestIndex].playerObject)->Destroy(players[oldestIndex].playerObject); 219 | close(players[oldestIndex].fd); // close the file descriptor 220 | players[oldestIndex].playerObject = NULL; 221 | players[oldestIndex].playerPlay = NULL; 222 | players[oldestIndex].playerSeek = NULL; 223 | players[oldestIndex].fd = -1; 224 | players[oldestIndex].uses = 0; 225 | 226 | CreateAudioPlayer(&players[oldestIndex], assetPath); 227 | playerPlay = players[oldestIndex].playerPlay; 228 | availableIndex = oldestIndex; 229 | Log("Created new player %d for asset: %s", oldestIndex, assetPath); 230 | } 231 | 232 | // start playing 233 | if (playerPlay) 234 | { 235 | result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING); 236 | if (result != SL_RESULT_SUCCESS) 237 | { 238 | Log("Failed to start playing: %d", result); 239 | return; 240 | } 241 | Log("Started playing asset: %s", assetPath); 242 | players[availableIndex].uses++; 243 | 244 | // check if the player has reached the maximum uses 245 | if (players[availableIndex].uses >= MAX_USES) 246 | { 247 | Log("Player %d has reached the maximum uses, destroying and creating a new one", availableIndex); 248 | (*players[availableIndex].playerObject)->Destroy(players[availableIndex].playerObject); 249 | close(players[availableIndex].fd); // close the file descriptor 250 | players[availableIndex].playerObject = NULL; 251 | players[availableIndex].playerPlay = NULL; 252 | players[availableIndex].playerSeek = NULL; 253 | players[availableIndex].fd = -1; 254 | players[availableIndex].uses = 0; 255 | 256 | CreateAudioPlayer(&players[availableIndex], assetPath); 257 | playerPlay = players[availableIndex].playerPlay; 258 | Log("Created new player %d for asset: %s", availableIndex, assetPath); 259 | } 260 | } 261 | else 262 | { 263 | Log("No available player to play asset: %s", assetPath); 264 | } 265 | } 266 | 267 | void PauseAudio() 268 | { 269 | SLresult result; 270 | for (int i = 0; i < MAX_PLAYERS; ++i) 271 | { 272 | if (players[i].playerPlay) 273 | { 274 | result = (*players[i].playerPlay)->SetPlayState(players[i].playerPlay, SL_PLAYSTATE_PAUSED); 275 | if (result != SL_RESULT_SUCCESS) 276 | { 277 | Log("Failed to pause playing for player %d: %d", i, result); 278 | } 279 | else 280 | { 281 | Log("Paused playing for player %d", i); 282 | } 283 | } 284 | } 285 | } 286 | 287 | void ResumeAudio() 288 | { 289 | SLresult result; 290 | for (int i = 0; i < MAX_PLAYERS; ++i) 291 | { 292 | if (players[i].playerPlay) 293 | { 294 | result = (*players[i].playerPlay)->SetPlayState(players[i].playerPlay, SL_PLAYSTATE_PLAYING); 295 | if (result != SL_RESULT_SUCCESS) 296 | { 297 | Log("Failed to resume playing for player %d: %d", i, result); 298 | } 299 | else 300 | { 301 | Log("Resumed playing for player %d", i); 302 | } 303 | } 304 | } 305 | } 306 | 307 | void StopAudio() 308 | { 309 | SLresult result; 310 | for (int i = 0; i < MAX_PLAYERS; ++i) 311 | { 312 | if (players[i].playerPlay) 313 | { 314 | result = (*players[i].playerPlay)->SetPlayState(players[i].playerPlay, SL_PLAYSTATE_STOPPED); 315 | if (result != SL_RESULT_SUCCESS) 316 | { 317 | Log("Failed to stop playing for player %d: %d", i, result); 318 | } 319 | else 320 | { 321 | Log("Stopped playing for player %d", i); 322 | } 323 | } 324 | } 325 | } 326 | 327 | void DestroyAudioPlayer() 328 | { 329 | for (int i = 0; i < MAX_PLAYERS; ++i) 330 | { 331 | if (players[i].playerObject != NULL) 332 | { 333 | (*players[i].playerObject)->Destroy(players[i].playerObject); 334 | close(players[i].fd); // close the file descriptor 335 | players[i].playerObject = NULL; 336 | players[i].playerPlay = NULL; 337 | players[i].playerSeek = NULL; 338 | players[i].fd = -1; 339 | players[i].uses = 0; 340 | Log("Destroyed player %d", i); 341 | } 342 | } 343 | } 344 | 345 | void DestroyAudioEngine() 346 | { 347 | if (outputMixObject != NULL) 348 | { 349 | (*outputMixObject)->Destroy(outputMixObject); 350 | outputMixObject = NULL; 351 | Log("Destroyed output mix object"); 352 | } 353 | 354 | if (engineObject != NULL) 355 | { 356 | (*engineObject)->Destroy(engineObject); 357 | engineObject = NULL; 358 | engineEngine = NULL; 359 | Log("Destroyed engine object"); 360 | } 361 | } -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/android_native_app_glue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | /** 32 | * The native activity interface provided by 33 | * is based on a set of application-provided callbacks that will be called 34 | * by the Activity's main thread when certain events occur. 35 | * 36 | * This means that each one of this callbacks _should_ _not_ block, or they 37 | * risk having the system force-close the application. This programming 38 | * model is direct, lightweight, but constraining. 39 | * 40 | * The 'android_native_app_glue' static library is used to provide a different 41 | * execution model where the application can implement its own main event 42 | * loop in a different thread instead. Here's how it works: 43 | * 44 | * 1/ The application must provide a function named "android_main()" that 45 | * will be called when the activity is created, in a new thread that is 46 | * distinct from the activity's main thread. 47 | * 48 | * 2/ android_main() receives a pointer to a valid "android_app" structure 49 | * that contains references to other important objects, e.g. the 50 | * ANativeActivity obejct instance the application is running in. 51 | * 52 | * 3/ the "android_app" object holds an ALooper instance that already 53 | * listens to two important things: 54 | * 55 | * - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX 56 | * declarations below. 57 | * 58 | * - input events coming from the AInputQueue attached to the activity. 59 | * 60 | * Each of these correspond to an ALooper identifier returned by 61 | * ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT, 62 | * respectively. 63 | * 64 | * Your application can use the same ALooper to listen to additional 65 | * file-descriptors. They can either be callback based, or with return 66 | * identifiers starting with LOOPER_ID_USER. 67 | * 68 | * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event, 69 | * the returned data will point to an android_poll_source structure. You 70 | * can call the process() function on it, and fill in android_app->onAppCmd 71 | * and android_app->onInputEvent to be called for your own processing 72 | * of the event. 73 | * 74 | * Alternatively, you can call the low-level functions to read and process 75 | * the data directly... look at the process_cmd() and process_input() 76 | * implementations in the glue to see how to do this. 77 | * 78 | * See the sample named "native-activity" that comes with the NDK with a 79 | * full usage example. Also look at the JavaDoc of NativeActivity. 80 | */ 81 | 82 | struct android_app; 83 | 84 | /** 85 | * Data associated with an ALooper fd that will be returned as the "outData" 86 | * when that source has data ready. 87 | */ 88 | struct android_poll_source { 89 | // The identifier of this source. May be LOOPER_ID_MAIN or 90 | // LOOPER_ID_INPUT. 91 | int32_t id; 92 | 93 | // The android_app this ident is associated with. 94 | struct android_app* app; 95 | 96 | // Function to call to perform the standard processing of data from 97 | // this source. 98 | void (*process)(struct android_app* app, struct android_poll_source* source); 99 | }; 100 | 101 | /** 102 | * This is the interface for the standard glue code of a threaded 103 | * application. In this model, the application's code is running 104 | * in its own thread separate from the main thread of the process. 105 | * It is not required that this thread be associated with the Java 106 | * VM, although it will need to be in order to make JNI calls any 107 | * Java objects. 108 | */ 109 | struct android_app { 110 | // The application can place a pointer to its own state object 111 | // here if it likes. 112 | void* userData; 113 | 114 | // Fill this in with the function to process main app commands (APP_CMD_*) 115 | void (*onAppCmd)(struct android_app* app, int32_t cmd); 116 | 117 | // Fill this in with the function to process input events. At this point 118 | // the event has already been pre-dispatched, and it will be finished upon 119 | // return. Return 1 if you have handled the event, 0 for any default 120 | // dispatching. 121 | int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event); 122 | 123 | // The ANativeActivity object instance that this app is running in. 124 | ANativeActivity* activity; 125 | 126 | // The current configuration the app is running in. 127 | AConfiguration* config; 128 | 129 | // This is the last instance's saved state, as provided at creation time. 130 | // It is NULL if there was no state. You can use this as you need; the 131 | // memory will remain around until you call android_app_exec_cmd() for 132 | // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL. 133 | // These variables should only be changed when processing a APP_CMD_SAVE_STATE, 134 | // at which point they will be initialized to NULL and you can malloc your 135 | // state and place the information here. In that case the memory will be 136 | // freed for you later. 137 | void* savedState; 138 | size_t savedStateSize; 139 | 140 | // The ALooper associated with the app's thread. 141 | ALooper* looper; 142 | 143 | // When non-NULL, this is the input queue from which the app will 144 | // receive user input events. 145 | AInputQueue* inputQueue; 146 | 147 | // When non-NULL, this is the window surface that the app can draw in. 148 | ANativeWindow* window; 149 | 150 | // Current content rectangle of the window; this is the area where the 151 | // window's content should be placed to be seen by the user. 152 | ARect contentRect; 153 | 154 | // Current state of the app's activity. May be either APP_CMD_START, 155 | // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. 156 | int activityState; 157 | 158 | // This is non-zero when the application's NativeActivity is being 159 | // destroyed and waiting for the app thread to complete. 160 | int destroyRequested; 161 | 162 | // ------------------------------------------------- 163 | // Below are "private" implementation of the glue code. 164 | 165 | pthread_mutex_t mutex; 166 | pthread_cond_t cond; 167 | 168 | int msgread; 169 | int msgwrite; 170 | 171 | pthread_t thread; 172 | 173 | struct android_poll_source cmdPollSource; 174 | struct android_poll_source inputPollSource; 175 | 176 | int running; 177 | int stateSaved; 178 | int destroyed; 179 | int redrawNeeded; 180 | AInputQueue* pendingInputQueue; 181 | ANativeWindow* pendingWindow; 182 | ARect pendingContentRect; 183 | }; 184 | 185 | enum { 186 | /** 187 | * Looper data ID of commands coming from the app's main thread, which 188 | * is returned as an identifier from ALooper_pollOnce(). The data for this 189 | * identifier is a pointer to an android_poll_source structure. 190 | * These can be retrieved and processed with android_app_read_cmd() 191 | * and android_app_exec_cmd(). 192 | */ 193 | LOOPER_ID_MAIN = 1, 194 | 195 | /** 196 | * Looper data ID of events coming from the AInputQueue of the 197 | * application's window, which is returned as an identifier from 198 | * ALooper_pollOnce(). The data for this identifier is a pointer to an 199 | * android_poll_source structure. These can be read via the inputQueue 200 | * object of android_app. 201 | */ 202 | LOOPER_ID_INPUT = 2, 203 | 204 | /** 205 | * Start of user-defined ALooper identifiers. 206 | */ 207 | LOOPER_ID_USER = 3, 208 | }; 209 | 210 | enum { 211 | /** 212 | * Command from main thread: the AInputQueue has changed. Upon processing 213 | * this command, android_app->inputQueue will be updated to the new queue 214 | * (or NULL). 215 | */ 216 | APP_CMD_INPUT_CHANGED, 217 | 218 | /** 219 | * Command from main thread: a new ANativeWindow is ready for use. Upon 220 | * receiving this command, android_app->window will contain the new window 221 | * surface. 222 | */ 223 | APP_CMD_INIT_WINDOW, 224 | 225 | /** 226 | * Command from main thread: the existing ANativeWindow needs to be 227 | * terminated. Upon receiving this command, android_app->window still 228 | * contains the existing window; after calling android_app_exec_cmd 229 | * it will be set to NULL. 230 | */ 231 | APP_CMD_TERM_WINDOW, 232 | 233 | /** 234 | * Command from main thread: the current ANativeWindow has been resized. 235 | * Please redraw with its new size. 236 | */ 237 | APP_CMD_WINDOW_RESIZED, 238 | 239 | /** 240 | * Command from main thread: the system needs that the current ANativeWindow 241 | * be redrawn. You should redraw the window before handing this to 242 | * android_app_exec_cmd() in order to avoid transient drawing glitches. 243 | */ 244 | APP_CMD_WINDOW_REDRAW_NEEDED, 245 | 246 | /** 247 | * Command from main thread: the content area of the window has changed, 248 | * such as from the soft input window being shown or hidden. You can 249 | * find the new content rect in android_app::contentRect. 250 | */ 251 | APP_CMD_CONTENT_RECT_CHANGED, 252 | 253 | /** 254 | * Command from main thread: the app's activity window has gained 255 | * input focus. 256 | */ 257 | APP_CMD_GAINED_FOCUS, 258 | 259 | /** 260 | * Command from main thread: the app's activity window has lost 261 | * input focus. 262 | */ 263 | APP_CMD_LOST_FOCUS, 264 | 265 | /** 266 | * Command from main thread: the current device configuration has changed. 267 | */ 268 | APP_CMD_CONFIG_CHANGED, 269 | 270 | /** 271 | * Command from main thread: the system is running low on memory. 272 | * Try to reduce your memory use. 273 | */ 274 | APP_CMD_LOW_MEMORY, 275 | 276 | /** 277 | * Command from main thread: the app's activity has been started. 278 | */ 279 | APP_CMD_START, 280 | 281 | /** 282 | * Command from main thread: the app's activity has been resumed. 283 | */ 284 | APP_CMD_RESUME, 285 | 286 | /** 287 | * Command from main thread: the app should generate a new saved state 288 | * for itself, to restore from later if needed. If you have saved state, 289 | * allocate it with malloc and place it in android_app.savedState with 290 | * the size in android_app.savedStateSize. The will be freed for you 291 | * later. 292 | */ 293 | APP_CMD_SAVE_STATE, 294 | 295 | /** 296 | * Command from main thread: the app's activity has been paused. 297 | */ 298 | APP_CMD_PAUSE, 299 | 300 | /** 301 | * Command from main thread: the app's activity has been stopped. 302 | */ 303 | APP_CMD_STOP, 304 | 305 | /** 306 | * Command from main thread: the app's activity is being destroyed, 307 | * and waiting for the app thread to clean up and exit before proceeding. 308 | */ 309 | APP_CMD_DESTROY, 310 | }; 311 | 312 | /** 313 | * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next 314 | * app command message. 315 | */ 316 | int8_t android_app_read_cmd(struct android_app* android_app); 317 | 318 | /** 319 | * Call with the command returned by android_app_read_cmd() to do the 320 | * initial pre-processing of the given command. You can perform your own 321 | * actions for the command after calling this function. 322 | */ 323 | void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd); 324 | 325 | /** 326 | * Call with the command returned by android_app_read_cmd() to do the 327 | * final post-processing of the given command. You must have done your own 328 | * actions for the command before calling this function. 329 | */ 330 | void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd); 331 | 332 | /** 333 | * No-op function that used to be used to prevent the linker from stripping app 334 | * glue code. No longer necessary, since __attribute__((visibility("default"))) 335 | * does this for us. 336 | */ 337 | __attribute__(( 338 | deprecated("Calls to app_dummy are no longer necessary. See " 339 | "https://github.com/android-ndk/ndk/issues/381."))) void 340 | app_dummy(); 341 | 342 | /** 343 | * This is the function that application code must implement, representing 344 | * the main entry to the app. 345 | */ 346 | extern void android_main(struct android_app* app); 347 | 348 | #ifdef __cplusplus 349 | } 350 | #endif 351 | -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/android_native_app_glue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "android_native_app_glue.h" 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__)) 29 | #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__)) 30 | 31 | /* For debug builds, always enable the debug traces in this library */ 32 | #ifndef NDEBUG 33 | # define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", __VA_ARGS__)) 34 | #else 35 | # define LOGV(...) ((void)0) 36 | #endif 37 | 38 | static void free_saved_state(struct android_app* android_app) { 39 | pthread_mutex_lock(&android_app->mutex); 40 | if (android_app->savedState != NULL) { 41 | free(android_app->savedState); 42 | android_app->savedState = NULL; 43 | android_app->savedStateSize = 0; 44 | } 45 | pthread_mutex_unlock(&android_app->mutex); 46 | } 47 | 48 | int8_t android_app_read_cmd(struct android_app* android_app) { 49 | int8_t cmd; 50 | if (read(android_app->msgread, &cmd, sizeof(cmd)) != sizeof(cmd)) { 51 | LOGE("No data on command pipe!"); 52 | return -1; 53 | } 54 | if (cmd == APP_CMD_SAVE_STATE) free_saved_state(android_app); 55 | return cmd; 56 | } 57 | 58 | static void print_cur_config(struct android_app* android_app) { 59 | char lang[2], country[2]; 60 | AConfiguration_getLanguage(android_app->config, lang); 61 | AConfiguration_getCountry(android_app->config, country); 62 | 63 | LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d " 64 | "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d " 65 | "modetype=%d modenight=%d", 66 | AConfiguration_getMcc(android_app->config), 67 | AConfiguration_getMnc(android_app->config), 68 | lang[0], lang[1], country[0], country[1], 69 | AConfiguration_getOrientation(android_app->config), 70 | AConfiguration_getTouchscreen(android_app->config), 71 | AConfiguration_getDensity(android_app->config), 72 | AConfiguration_getKeyboard(android_app->config), 73 | AConfiguration_getNavigation(android_app->config), 74 | AConfiguration_getKeysHidden(android_app->config), 75 | AConfiguration_getNavHidden(android_app->config), 76 | AConfiguration_getSdkVersion(android_app->config), 77 | AConfiguration_getScreenSize(android_app->config), 78 | AConfiguration_getScreenLong(android_app->config), 79 | AConfiguration_getUiModeType(android_app->config), 80 | AConfiguration_getUiModeNight(android_app->config)); 81 | } 82 | 83 | void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) { 84 | switch (cmd) { 85 | case APP_CMD_INPUT_CHANGED: 86 | LOGV("APP_CMD_INPUT_CHANGED"); 87 | pthread_mutex_lock(&android_app->mutex); 88 | if (android_app->inputQueue != NULL) { 89 | AInputQueue_detachLooper(android_app->inputQueue); 90 | } 91 | android_app->inputQueue = android_app->pendingInputQueue; 92 | if (android_app->inputQueue != NULL) { 93 | LOGV("Attaching input queue to looper"); 94 | AInputQueue_attachLooper(android_app->inputQueue, 95 | android_app->looper, LOOPER_ID_INPUT, NULL, 96 | &android_app->inputPollSource); 97 | } 98 | pthread_cond_broadcast(&android_app->cond); 99 | pthread_mutex_unlock(&android_app->mutex); 100 | break; 101 | 102 | case APP_CMD_INIT_WINDOW: 103 | LOGV("APP_CMD_INIT_WINDOW"); 104 | pthread_mutex_lock(&android_app->mutex); 105 | android_app->window = android_app->pendingWindow; 106 | pthread_cond_broadcast(&android_app->cond); 107 | pthread_mutex_unlock(&android_app->mutex); 108 | break; 109 | 110 | case APP_CMD_TERM_WINDOW: 111 | LOGV("APP_CMD_TERM_WINDOW"); 112 | pthread_cond_broadcast(&android_app->cond); 113 | break; 114 | 115 | case APP_CMD_RESUME: 116 | case APP_CMD_START: 117 | case APP_CMD_PAUSE: 118 | case APP_CMD_STOP: 119 | LOGV("activityState=%d", cmd); 120 | pthread_mutex_lock(&android_app->mutex); 121 | android_app->activityState = cmd; 122 | pthread_cond_broadcast(&android_app->cond); 123 | pthread_mutex_unlock(&android_app->mutex); 124 | break; 125 | 126 | case APP_CMD_CONFIG_CHANGED: 127 | LOGV("APP_CMD_CONFIG_CHANGED"); 128 | AConfiguration_fromAssetManager(android_app->config, 129 | android_app->activity->assetManager); 130 | print_cur_config(android_app); 131 | break; 132 | 133 | case APP_CMD_DESTROY: 134 | LOGV("APP_CMD_DESTROY"); 135 | android_app->destroyRequested = 1; 136 | break; 137 | } 138 | } 139 | 140 | void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) { 141 | switch (cmd) { 142 | case APP_CMD_TERM_WINDOW: 143 | LOGV("APP_CMD_TERM_WINDOW"); 144 | pthread_mutex_lock(&android_app->mutex); 145 | android_app->window = NULL; 146 | pthread_cond_broadcast(&android_app->cond); 147 | pthread_mutex_unlock(&android_app->mutex); 148 | break; 149 | 150 | case APP_CMD_SAVE_STATE: 151 | LOGV("APP_CMD_SAVE_STATE"); 152 | pthread_mutex_lock(&android_app->mutex); 153 | android_app->stateSaved = 1; 154 | pthread_cond_broadcast(&android_app->cond); 155 | pthread_mutex_unlock(&android_app->mutex); 156 | break; 157 | 158 | case APP_CMD_RESUME: 159 | free_saved_state(android_app); 160 | break; 161 | } 162 | } 163 | 164 | void app_dummy() { 165 | } 166 | 167 | static void android_app_destroy(struct android_app* android_app) { 168 | LOGV("android_app_destroy!"); 169 | free_saved_state(android_app); 170 | pthread_mutex_lock(&android_app->mutex); 171 | if (android_app->inputQueue != NULL) { 172 | AInputQueue_detachLooper(android_app->inputQueue); 173 | } 174 | AConfiguration_delete(android_app->config); 175 | android_app->destroyed = 1; 176 | pthread_cond_broadcast(&android_app->cond); 177 | pthread_mutex_unlock(&android_app->mutex); 178 | // Can't touch android_app object after this. 179 | } 180 | 181 | static void process_input(struct android_app* app, struct android_poll_source* source) { 182 | AInputEvent* event = NULL; 183 | while (AInputQueue_getEvent(app->inputQueue, &event) >= 0) { 184 | LOGV("New input event: type=%d", AInputEvent_getType(event)); 185 | if (AInputQueue_preDispatchEvent(app->inputQueue, event)) { 186 | continue; 187 | } 188 | int32_t handled = 0; 189 | if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event); 190 | AInputQueue_finishEvent(app->inputQueue, event, handled); 191 | } 192 | } 193 | 194 | static void process_cmd(struct android_app* app, struct android_poll_source* source) { 195 | int8_t cmd = android_app_read_cmd(app); 196 | android_app_pre_exec_cmd(app, cmd); 197 | if (app->onAppCmd != NULL) app->onAppCmd(app, cmd); 198 | android_app_post_exec_cmd(app, cmd); 199 | } 200 | 201 | static void* android_app_entry(void* param) { 202 | struct android_app* android_app = (struct android_app*)param; 203 | 204 | android_app->config = AConfiguration_new(); 205 | AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager); 206 | 207 | print_cur_config(android_app); 208 | 209 | android_app->cmdPollSource.id = LOOPER_ID_MAIN; 210 | android_app->cmdPollSource.app = android_app; 211 | android_app->cmdPollSource.process = process_cmd; 212 | android_app->inputPollSource.id = LOOPER_ID_INPUT; 213 | android_app->inputPollSource.app = android_app; 214 | android_app->inputPollSource.process = process_input; 215 | 216 | ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); 217 | ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL, 218 | &android_app->cmdPollSource); 219 | android_app->looper = looper; 220 | 221 | pthread_mutex_lock(&android_app->mutex); 222 | android_app->running = 1; 223 | pthread_cond_broadcast(&android_app->cond); 224 | pthread_mutex_unlock(&android_app->mutex); 225 | 226 | android_main(android_app); 227 | 228 | android_app_destroy(android_app); 229 | return NULL; 230 | } 231 | 232 | // -------------------------------------------------------------------- 233 | // Native activity interaction (called from main thread) 234 | // -------------------------------------------------------------------- 235 | 236 | static struct android_app* android_app_create(ANativeActivity* activity, 237 | void* savedState, size_t savedStateSize) { 238 | struct android_app* android_app = calloc(1, sizeof(struct android_app)); 239 | android_app->activity = activity; 240 | 241 | pthread_mutex_init(&android_app->mutex, NULL); 242 | pthread_cond_init(&android_app->cond, NULL); 243 | 244 | if (savedState != NULL) { 245 | android_app->savedState = malloc(savedStateSize); 246 | android_app->savedStateSize = savedStateSize; 247 | memcpy(android_app->savedState, savedState, savedStateSize); 248 | } 249 | 250 | int msgpipe[2]; 251 | if (pipe(msgpipe)) { 252 | LOGE("could not create pipe: %s", strerror(errno)); 253 | return NULL; 254 | } 255 | android_app->msgread = msgpipe[0]; 256 | android_app->msgwrite = msgpipe[1]; 257 | 258 | pthread_attr_t attr; 259 | pthread_attr_init(&attr); 260 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 261 | pthread_create(&android_app->thread, &attr, android_app_entry, android_app); 262 | 263 | // Wait for thread to start. 264 | pthread_mutex_lock(&android_app->mutex); 265 | while (!android_app->running) { 266 | pthread_cond_wait(&android_app->cond, &android_app->mutex); 267 | } 268 | pthread_mutex_unlock(&android_app->mutex); 269 | 270 | return android_app; 271 | } 272 | 273 | static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { 274 | if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { 275 | LOGE("Failure writing android_app cmd: %s", strerror(errno)); 276 | } 277 | } 278 | 279 | static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) { 280 | pthread_mutex_lock(&android_app->mutex); 281 | android_app->pendingInputQueue = inputQueue; 282 | android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED); 283 | while (android_app->inputQueue != android_app->pendingInputQueue) { 284 | pthread_cond_wait(&android_app->cond, &android_app->mutex); 285 | } 286 | pthread_mutex_unlock(&android_app->mutex); 287 | } 288 | 289 | static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) { 290 | pthread_mutex_lock(&android_app->mutex); 291 | if (android_app->pendingWindow != NULL) { 292 | android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW); 293 | } 294 | android_app->pendingWindow = window; 295 | if (window != NULL) { 296 | android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW); 297 | } 298 | while (android_app->window != android_app->pendingWindow) { 299 | pthread_cond_wait(&android_app->cond, &android_app->mutex); 300 | } 301 | pthread_mutex_unlock(&android_app->mutex); 302 | } 303 | 304 | static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) { 305 | pthread_mutex_lock(&android_app->mutex); 306 | android_app_write_cmd(android_app, cmd); 307 | while (android_app->activityState != cmd) { 308 | pthread_cond_wait(&android_app->cond, &android_app->mutex); 309 | } 310 | pthread_mutex_unlock(&android_app->mutex); 311 | } 312 | 313 | static void android_app_free(struct android_app* android_app) { 314 | pthread_mutex_lock(&android_app->mutex); 315 | android_app_write_cmd(android_app, APP_CMD_DESTROY); 316 | while (!android_app->destroyed) { 317 | pthread_cond_wait(&android_app->cond, &android_app->mutex); 318 | } 319 | pthread_mutex_unlock(&android_app->mutex); 320 | 321 | close(android_app->msgread); 322 | close(android_app->msgwrite); 323 | pthread_cond_destroy(&android_app->cond); 324 | pthread_mutex_destroy(&android_app->mutex); 325 | free(android_app); 326 | } 327 | 328 | static struct android_app* ToApp(ANativeActivity* activity) { 329 | return (struct android_app*) activity->instance; 330 | } 331 | 332 | static void onDestroy(ANativeActivity* activity) { 333 | LOGV("Destroy: %p", activity); 334 | android_app_free(ToApp(activity)); 335 | } 336 | 337 | static void onStart(ANativeActivity* activity) { 338 | LOGV("Start: %p", activity); 339 | android_app_set_activity_state(ToApp(activity), APP_CMD_START); 340 | } 341 | 342 | static void onResume(ANativeActivity* activity) { 343 | LOGV("Resume: %p", activity); 344 | android_app_set_activity_state(ToApp(activity), APP_CMD_RESUME); 345 | } 346 | 347 | static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) { 348 | LOGV("SaveInstanceState: %p", activity); 349 | 350 | struct android_app* android_app = ToApp(activity); 351 | void* savedState = NULL; 352 | pthread_mutex_lock(&android_app->mutex); 353 | android_app->stateSaved = 0; 354 | android_app_write_cmd(android_app, APP_CMD_SAVE_STATE); 355 | while (!android_app->stateSaved) { 356 | pthread_cond_wait(&android_app->cond, &android_app->mutex); 357 | } 358 | 359 | if (android_app->savedState != NULL) { 360 | savedState = android_app->savedState; 361 | *outLen = android_app->savedStateSize; 362 | android_app->savedState = NULL; 363 | android_app->savedStateSize = 0; 364 | } 365 | 366 | pthread_mutex_unlock(&android_app->mutex); 367 | 368 | return savedState; 369 | } 370 | 371 | static void onPause(ANativeActivity* activity) { 372 | LOGV("Pause: %p", activity); 373 | android_app_set_activity_state(ToApp(activity), APP_CMD_PAUSE); 374 | } 375 | 376 | static void onStop(ANativeActivity* activity) { 377 | LOGV("Stop: %p", activity); 378 | android_app_set_activity_state(ToApp(activity), APP_CMD_STOP); 379 | } 380 | 381 | static void onConfigurationChanged(ANativeActivity* activity) { 382 | LOGV("ConfigurationChanged: %p", activity); 383 | android_app_write_cmd(ToApp(activity), APP_CMD_CONFIG_CHANGED); 384 | } 385 | 386 | static void onContentRectChanged(ANativeActivity* activity, const ARect* r) { 387 | LOGV("ContentRectChanged: l=%d,t=%d,r=%d,b=%d", r->left, r->top, r->right, r->bottom); 388 | struct android_app* android_app = ToApp(activity); 389 | pthread_mutex_lock(&android_app->mutex); 390 | android_app->contentRect = *r; 391 | pthread_mutex_unlock(&android_app->mutex); 392 | android_app_write_cmd(ToApp(activity), APP_CMD_CONTENT_RECT_CHANGED); 393 | } 394 | 395 | static void onLowMemory(ANativeActivity* activity) { 396 | LOGV("LowMemory: %p", activity); 397 | android_app_write_cmd(ToApp(activity), APP_CMD_LOW_MEMORY); 398 | } 399 | 400 | static void onWindowFocusChanged(ANativeActivity* activity, int focused) { 401 | LOGV("WindowFocusChanged: %p -- %d", activity, focused); 402 | android_app_write_cmd(ToApp(activity), focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); 403 | } 404 | 405 | static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { 406 | LOGV("NativeWindowCreated: %p -- %p", activity, window); 407 | android_app_set_window(ToApp(activity), window); 408 | } 409 | 410 | static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { 411 | LOGV("NativeWindowDestroyed: %p -- %p", activity, window); 412 | android_app_set_window(ToApp(activity), NULL); 413 | } 414 | 415 | static void onNativeWindowRedrawNeeded(ANativeActivity* activity, ANativeWindow* window) { 416 | LOGV("NativeWindowRedrawNeeded: %p -- %p", activity, window); 417 | android_app_write_cmd(ToApp(activity), APP_CMD_WINDOW_REDRAW_NEEDED); 418 | } 419 | 420 | static void onNativeWindowResized(ANativeActivity* activity, ANativeWindow* window) { 421 | LOGV("NativeWindowResized: %p -- %p", activity, window); 422 | android_app_write_cmd(ToApp(activity), APP_CMD_WINDOW_RESIZED); 423 | } 424 | 425 | static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { 426 | LOGV("InputQueueCreated: %p -- %p", activity, queue); 427 | android_app_set_input(ToApp(activity), queue); 428 | } 429 | 430 | static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { 431 | LOGV("InputQueueDestroyed: %p -- %p", activity, queue); 432 | android_app_set_input(ToApp(activity), NULL); 433 | } 434 | 435 | JNIEXPORT 436 | void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize) { 437 | LOGV("Creating: %p", activity); 438 | 439 | activity->callbacks->onConfigurationChanged = onConfigurationChanged; 440 | activity->callbacks->onContentRectChanged = onContentRectChanged; 441 | activity->callbacks->onDestroy = onDestroy; 442 | activity->callbacks->onInputQueueCreated = onInputQueueCreated; 443 | activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; 444 | activity->callbacks->onLowMemory = onLowMemory; 445 | activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; 446 | activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; 447 | activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded; 448 | activity->callbacks->onNativeWindowResized = onNativeWindowResized; 449 | activity->callbacks->onPause = onPause; 450 | activity->callbacks->onResume = onResume; 451 | activity->callbacks->onSaveInstanceState = onSaveInstanceState; 452 | activity->callbacks->onStart = onStart; 453 | activity->callbacks->onStop = onStop; 454 | activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; 455 | 456 | activity->instance = android_app_create(activity, savedState, savedStateSize); 457 | } 458 | -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/game.c: -------------------------------------------------------------------------------- 1 | #include "android_native_app_glue.h" 2 | #include 3 | #include "game.h" 4 | #include "utils.h" 5 | #include "texture.h" 6 | #include "audio.h" 7 | #include "init.h" 8 | #include "mouse.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define SIZE_SPACE_PIPE 3.3f 15 | 16 | #define SPACE_BETWEEN_PIPES 5 17 | 18 | //buttons 19 | GLuint t_pause; 20 | GLuint t_ok; 21 | GLuint t_menu; 22 | GLuint t_resume; 23 | GLuint t_score; 24 | GLuint t_share; 25 | GLuint t_start; 26 | 27 | //sprites 28 | GLuint t_0; 29 | GLuint t_1; 30 | GLuint t_2; 31 | GLuint t_3; 32 | GLuint t_4; 33 | GLuint t_5; 34 | GLuint t_6; 35 | GLuint t_7; 36 | GLuint t_8; 37 | GLuint t_9; 38 | 39 | GLuint t_0_small; 40 | GLuint t_1_small; 41 | GLuint t_2_small; 42 | GLuint t_3_small; 43 | GLuint t_4_small; 44 | GLuint t_5_small; 45 | GLuint t_6_small; 46 | GLuint t_7_small; 47 | GLuint t_8_small; 48 | GLuint t_9_small; 49 | 50 | GLuint t_background_day; 51 | GLuint t_base; 52 | GLuint t_bronze_medal; 53 | GLuint t_gameover; 54 | GLuint t_gold_medal; 55 | GLuint t_logo; 56 | GLuint t_message; 57 | GLuint t_new; 58 | GLuint t_panel; 59 | GLuint t_pipe_green; 60 | GLuint t_platinum_medal; 61 | GLuint t_silver_medal; 62 | GLuint t_sparkle_sheet; 63 | GLuint t_yellowbird_downflap; 64 | GLuint t_yellowbird_midflap; 65 | GLuint t_yellowbird_upflap; 66 | 67 | // data 68 | int offsetBase = 0; 69 | int gameSpeed = 0; 70 | int score = 0; 71 | int bestScore = 0; 72 | bool newBestScore = false; 73 | 74 | int alpha = 0; 75 | bool fadeOut = false; 76 | 77 | enum GameState { 78 | IDLE, 79 | FADE_IN, 80 | FADE_OUT, 81 | READY_GAME, 82 | GO_GAME, 83 | STOP_GAME, 84 | FADE_OUT_GAMEOVER, 85 | FALL_BIRD, 86 | FADE_IN_PANEL 87 | }; 88 | 89 | enum GameState currentState = IDLE; 90 | 91 | typedef struct { 92 | float x, y; 93 | float velocity; 94 | float angle; 95 | float width; 96 | float height; 97 | GLuint currentTexture; 98 | int frame; 99 | uint64_t lastFrameTime; 100 | } Bird; 101 | 102 | typedef struct { 103 | float x, y; 104 | float w, h; 105 | float offset; 106 | } Pipe; 107 | 108 | Bird bird; 109 | Pipe pipes[2]; 110 | 111 | float ScaleX(float percent) 112 | { 113 | return (percent / 100.0f) * WindowSizeX; 114 | } 115 | 116 | float ScaleY(float percent) 117 | { 118 | return (percent / 100.0f) * WindowSizeY; 119 | } 120 | 121 | float logoY; 122 | float birdY; 123 | float logoVelocity; 124 | float birdVelocity; 125 | uint64_t timeAnimBirdForLogo; 126 | GLuint curTextureAnimBirdForLogo; 127 | 128 | GLuint birdTexturesForLogo[3]; 129 | int currentFrameForLogo = 0; 130 | 131 | int fadeOutAlpha = 255; 132 | float panelY = 0; 133 | GLuint medalTexture = 0; 134 | 135 | int Random(int min, int max) 136 | { 137 | return min + rand() / (RAND_MAX / (max - min + 1) + 1); 138 | } 139 | 140 | bool InitGame() 141 | { 142 | //buttons 143 | t_pause = LoadTexture("buttons/pause.png"); 144 | t_ok = LoadTexture("buttons/ok.png"); 145 | t_menu = LoadTexture("buttons/menu.png"); 146 | t_resume = LoadTexture("buttons/resume.png"); 147 | t_score = LoadTexture("buttons/score.png"); 148 | t_share = LoadTexture("buttons/share.png"); 149 | t_start = LoadTexture("buttons/start.png"); 150 | 151 | //sprites 152 | t_0 = LoadTexture("sprites/0.png"); 153 | t_1 = LoadTexture("sprites/1.png"); 154 | t_2 = LoadTexture("sprites/2.png"); 155 | t_3 = LoadTexture("sprites/3.png"); 156 | t_4 = LoadTexture("sprites/4.png"); 157 | t_5 = LoadTexture("sprites/5.png"); 158 | t_6 = LoadTexture("sprites/6.png"); 159 | t_7 = LoadTexture("sprites/7.png"); 160 | t_8 = LoadTexture("sprites/8.png"); 161 | t_9 = LoadTexture("sprites/9.png"); 162 | 163 | t_0_small = LoadTexture("sprites/0_small.png"); 164 | t_1_small = LoadTexture("sprites/1_small.png"); 165 | t_2_small = LoadTexture("sprites/2_small.png"); 166 | t_3_small = LoadTexture("sprites/3_small.png"); 167 | t_4_small = LoadTexture("sprites/4_small.png"); 168 | t_5_small = LoadTexture("sprites/5_small.png"); 169 | t_6_small = LoadTexture("sprites/6_small.png"); 170 | t_7_small = LoadTexture("sprites/7_small.png"); 171 | t_8_small = LoadTexture("sprites/8_small.png"); 172 | t_9_small = LoadTexture("sprites/9_small.png"); 173 | 174 | t_background_day = LoadTexture("sprites/background-day.png"); 175 | t_base = LoadTexture("sprites/base.png"); 176 | t_bronze_medal = LoadTexture("sprites/bronze-medal.png"); 177 | t_gameover = LoadTexture("sprites/gameover.png"); 178 | t_gold_medal = LoadTexture("sprites/gold-medal.png"); 179 | t_logo = LoadTexture("sprites/logo.png"); 180 | t_message = LoadTexture("sprites/message.png"); 181 | t_new = LoadTexture("sprites/new.png"); 182 | t_panel = LoadTexture("sprites/panel.png"); 183 | t_pipe_green = LoadTexture("sprites/pipe-green.png"); 184 | t_platinum_medal = LoadTexture("sprites/platinum-medal.png"); 185 | t_silver_medal = LoadTexture("sprites/silver-medal.png"); 186 | t_sparkle_sheet = LoadTexture("sprites/sparkle-sheet.png"); 187 | t_yellowbird_downflap = LoadTexture("sprites/yellowbird-downflap.png"); 188 | t_yellowbird_midflap = LoadTexture("sprites/yellowbird-midflap.png"); 189 | t_yellowbird_upflap = LoadTexture("sprites/yellowbird-upflap.png"); 190 | 191 | bird.x = ScaleX(18.52f); 192 | bird.y = ScaleY(20.f); 193 | bird.velocity = 0.0f; 194 | bird.angle = 0.0f; 195 | bird.width = ScaleX(11.11f); 196 | bird.height = ScaleY(4.17f); 197 | bird.currentTexture = t_yellowbird_midflap; 198 | bird.frame = 0; 199 | bird.lastFrameTime = 0; 200 | 201 | 202 | pipes[0].x = ScaleX(100.f); 203 | pipes[0].y = ScaleY(37.5f); 204 | pipes[0].w = ScaleX(15.f); 205 | pipes[0].h = ScaleY(37.5f); 206 | pipes[0].offset = Random(ScaleY(-SPACE_BETWEEN_PIPES), ScaleY(SPACE_BETWEEN_PIPES)); 207 | 208 | pipes[1].x = ScaleX(100.f) + ScaleX(60.f); 209 | pipes[1].y = ScaleY(37.5f); 210 | pipes[1].w = ScaleX(15.f); 211 | pipes[1].h = ScaleY(37.5f); 212 | pipes[1].offset = Random(ScaleY(-SPACE_BETWEEN_PIPES), ScaleY(SPACE_BETWEEN_PIPES)); 213 | 214 | 215 | logoY = ScaleY(20.83f); 216 | birdY = ScaleY(20.83f); 217 | logoVelocity = 1.1f; 218 | birdVelocity = 1.1f; 219 | 220 | timeAnimBirdForLogo = getTickCount(); 221 | curTextureAnimBirdForLogo = t_yellowbird_midflap; 222 | 223 | birdTexturesForLogo[0] = t_yellowbird_downflap; 224 | birdTexturesForLogo[1] = t_yellowbird_midflap; 225 | birdTexturesForLogo[2] = t_yellowbird_upflap; 226 | 227 | panelY = ScaleY(100.f); 228 | 229 | newBestScore = false; 230 | 231 | //game speed 232 | gameSpeed = WindowSizeX / 135; // 1080 / 135 = 8 233 | 234 | //load best score 235 | char filePath[256]; 236 | snprintf(filePath, sizeof(filePath), "%s/save.txt", g_App->activity->internalDataPath); 237 | 238 | FILE* file = fopen(filePath, "r"); 239 | if (file != NULL) 240 | { 241 | int loadbestscore; 242 | fscanf(file, "%d", &loadbestscore); 243 | fclose(file); 244 | 245 | bestScore = loadbestscore; 246 | } 247 | 248 | return true; 249 | } 250 | 251 | float MoveTowards(float current, float target, float maxDelta) 252 | { 253 | if (fabs(target - current) <= maxDelta) 254 | { 255 | return target; 256 | } 257 | return current + (target > current ? maxDelta : -maxDelta); 258 | } 259 | 260 | void AnimateBird() 261 | { 262 | uint64_t currentTime = getTickCount(); 263 | if (currentTime - bird.lastFrameTime > 100) 264 | { 265 | bird.lastFrameTime = currentTime; 266 | bird.frame = (bird.frame + 1) % 3; 267 | switch (bird.frame) 268 | { 269 | case 0: bird.currentTexture = t_yellowbird_downflap; break; 270 | case 1: bird.currentTexture = t_yellowbird_midflap; break; 271 | case 2: bird.currentTexture = t_yellowbird_upflap; break; 272 | } 273 | } 274 | } 275 | 276 | void ApplyGravity() 277 | { 278 | bird.velocity += 0.65f; 279 | bird.y += bird.velocity; 280 | 281 | float targetAngle = bird.velocity > 0 ? 90.0f : -30.0f; 282 | bird.angle = MoveTowards(bird.angle, targetAngle, 2.0f); 283 | 284 | if (bird.angle > 90.0f) bird.angle = 90.0f; 285 | } 286 | 287 | void Jump() 288 | { 289 | bird.velocity = -13.5f; 290 | bird.angle = -30.0f; 291 | } 292 | 293 | bool CheckCollision() 294 | { 295 | // detect collision with pipes 296 | for (int i = 0; i < 2; i++) 297 | { 298 | // upper pipe 299 | float topPipeX = pipes[i].x; 300 | float topPipeY = pipes[i].y + pipes[i].offset - (bird.height * SIZE_SPACE_PIPE); 301 | float topPipeWidth = pipes[i].w; 302 | float topPipeHeight = -(pipes[i].h + pipes[i].offset - (bird.height * SIZE_SPACE_PIPE)); 303 | 304 | // lower pipe 305 | float bottomPipeX = pipes[i].x; 306 | float bottomPipeY = pipes[i].y + pipes[i].offset; 307 | float bottomPipeWidth = pipes[i].w; 308 | float bottomPipeHeight = pipes[i].h - pipes[i].offset; 309 | 310 | // checking collision for upper pipe 311 | if (bird.x < topPipeX + topPipeWidth && 312 | bird.x + bird.width > topPipeX && 313 | bird.y < topPipeY && 314 | bird.y + bird.height > topPipeY + topPipeHeight) 315 | { 316 | return true; 317 | } 318 | 319 | // checking collision for lower pipe 320 | if (bird.x < bottomPipeX + bottomPipeWidth && 321 | bird.x + bird.width > bottomPipeX && 322 | bird.y < bottomPipeY + bottomPipeHeight && 323 | bird.y + bird.height > bottomPipeY) 324 | { 325 | return true; 326 | } 327 | 328 | // checking space between pipes 329 | float gapStartY = topPipeY + topPipeHeight; 330 | float gapEndY = bottomPipeY; 331 | 332 | if (bird.x < bottomPipeX + bottomPipeWidth && 333 | bird.x + bird.width > bottomPipeX && 334 | bird.y + bird.height > gapStartY && 335 | bird.y < gapEndY) 336 | { 337 | // collision not detect for bird 338 | continue; 339 | } 340 | } 341 | 342 | // ground collision 343 | float baseHeight = ScaleY(75.f); 344 | if (bird.y + bird.width > baseHeight) 345 | { 346 | return true; 347 | } 348 | 349 | // sky collision 350 | if (bird.y <= 0) 351 | { 352 | return true; 353 | } 354 | 355 | return false; 356 | } 357 | 358 | void RenderBird() 359 | { 360 | RenderTexturePro(bird.currentTexture, bird.x, bird.y, bird.width, bird.height, bird.angle); 361 | } 362 | 363 | void RenderPipes() 364 | { 365 | for (int i = 0; i < 2; i++) 366 | { 367 | RenderTexture(t_pipe_green, pipes[i].x, pipes[i].y + pipes[i].offset - (bird.height * SIZE_SPACE_PIPE), 368 | pipes[i].w, -(pipes[i].h + pipes[i].offset - (bird.height * SIZE_SPACE_PIPE))); 369 | 370 | RenderTexture(t_pipe_green, pipes[i].x, pipes[i].y + pipes[i].offset, pipes[i].w, pipes[i].h - pipes[i].offset); 371 | } 372 | } 373 | 374 | void UpdateBirdTextureForLogo() 375 | { 376 | uint64_t currentTime = getTickCount(); 377 | if (currentTime - timeAnimBirdForLogo > 100) 378 | { 379 | timeAnimBirdForLogo = currentTime; 380 | currentFrameForLogo = (currentFrameForLogo + 1) % 3; 381 | curTextureAnimBirdForLogo = birdTexturesForLogo[currentFrameForLogo]; 382 | } 383 | } 384 | 385 | void RenderScore(int score, float x, float y, float digitWidth, float digitHeight) 386 | { 387 | char scoreStr[10]; 388 | sprintf(scoreStr, "%d", score); 389 | int len = strlen(scoreStr); 390 | 391 | for (int i = 0; i < len; i++) 392 | { 393 | int digit = scoreStr[i] - '0'; 394 | GLuint texture = 0; 395 | 396 | switch (digit) 397 | { 398 | case 0: texture = t_0; break; 399 | case 1: texture = t_1; break; 400 | case 2: texture = t_2; break; 401 | case 3: texture = t_3; break; 402 | case 4: texture = t_4; break; 403 | case 5: texture = t_5; break; 404 | case 6: texture = t_6; break; 405 | case 7: texture = t_7; break; 406 | case 8: texture = t_8; break; 407 | case 9: texture = t_9; break; 408 | } 409 | 410 | RenderTexture(texture, x + i * digitWidth, y, digitWidth, digitHeight); 411 | } 412 | } 413 | 414 | void RenderSmallScore(int score, float x, float y, float digitWidth, float digitHeight) 415 | { 416 | char scoreStr[10]; 417 | sprintf(scoreStr, "%d", score); 418 | int len = strlen(scoreStr); 419 | 420 | for (int i = 0; i < len; i++) 421 | { 422 | int digit = scoreStr[i] - '0'; 423 | GLuint texture = 0; 424 | 425 | switch (digit) 426 | { 427 | case 0: texture = t_0_small; break; 428 | case 1: texture = t_1_small; break; 429 | case 2: texture = t_2_small; break; 430 | case 3: texture = t_3_small; break; 431 | case 4: texture = t_4_small; break; 432 | case 5: texture = t_5_small; break; 433 | case 6: texture = t_6_small; break; 434 | case 7: texture = t_7_small; break; 435 | case 8: texture = t_8_small; break; 436 | case 9: texture = t_9_small; break; 437 | } 438 | 439 | RenderTexture(texture, x + i * digitWidth, y, digitWidth, digitHeight); 440 | } 441 | } 442 | 443 | void Render() 444 | { 445 | //background 446 | RenderTexture(t_background_day, 0, 0, ScaleX(100.f), ScaleY(95.83f)); 447 | 448 | //cycle base texture 449 | if (currentState != STOP_GAME && currentState != FADE_OUT_GAMEOVER && currentState != FALL_BIRD && currentState != FADE_IN_PANEL) 450 | { 451 | offsetBase -= gameSpeed; 452 | } 453 | 454 | RenderTexture(t_base, offsetBase, ScaleY(75.f), ScaleX(100.f), ScaleY(25.f)); 455 | 456 | if (offsetBase < 0) 457 | { 458 | RenderTexture(t_base, ScaleX(100.f) + offsetBase, ScaleY(75.f), ScaleX(100.f), ScaleY(25.f)); 459 | } 460 | 461 | if (offsetBase <= -ScaleX(100.f)) 462 | { 463 | offsetBase = 0; 464 | } 465 | 466 | logoY += logoVelocity; 467 | birdY += birdVelocity; 468 | 469 | if (logoY > ScaleY(20.83f) + 25 || logoY < ScaleY(20.83f) - 25) { 470 | logoVelocity = -logoVelocity; 471 | } 472 | 473 | if (birdY > ScaleY(20.83f) + 25 || birdY < ScaleY(20.83f) - 25) { 474 | birdVelocity = -birdVelocity; 475 | } 476 | 477 | if (currentState == IDLE || currentState == FADE_IN) 478 | { 479 | RenderTexture(t_logo, ScaleX(15.f), logoY, ScaleX(55.56f), ScaleY(5.21f)); 480 | 481 | UpdateBirdTextureForLogo(); 482 | 483 | RenderTexture(curTextureAnimBirdForLogo, ScaleX(75), birdY, bird.width, bird.height); 484 | 485 | if (ButtonBump(t_start, ScaleX(10.f), ScaleY(65.f), ScaleX(35.f), ScaleY(6.f))) 486 | { 487 | //later 488 | //PlayAudio("audio/click_sound.ogg"); 489 | currentState = FADE_IN; 490 | } 491 | 492 | // button SCORE 493 | if (ButtonBump(t_score, ScaleX(55.f), ScaleY(65.f), ScaleX(35.f), ScaleY(6.f))) 494 | { 495 | //later 496 | //PlayAudio("audio/click_sound.ogg"); 497 | } 498 | } 499 | else if (currentState == FADE_OUT || currentState == READY_GAME) //Ready? 500 | { 501 | RenderBird(); 502 | 503 | RenderTexture(t_message, ScaleX(10.f), ScaleY(9.f), ScaleX(80.f), ScaleY(50.f)); 504 | if (Button(0, 0, ScaleX(100.f), ScaleY(100.f))) 505 | { 506 | currentState = GO_GAME; 507 | } 508 | } 509 | else if (currentState == GO_GAME) 510 | { 511 | ApplyGravity(); 512 | AnimateBird(); 513 | 514 | for (int i = 0; i < 2; i++) 515 | { 516 | pipes[i].x -= gameSpeed; 517 | if (pipes[i].x < -ScaleX(15.f)) 518 | { 519 | pipes[i].x = ScaleX(115.f); 520 | pipes[i].offset = Random(ScaleY(-SPACE_BETWEEN_PIPES), ScaleY(SPACE_BETWEEN_PIPES)); 521 | } 522 | 523 | if (bird.x + (bird.width / 2) >= pipes[i].x + pipes[i].w && 524 | bird.x + (bird.width / 2) <= pipes[i].x + pipes[i].w + gameSpeed) 525 | { 526 | score++; 527 | PlayAudio("audio/point.mp3"); 528 | } 529 | } 530 | 531 | if (CheckCollision()) 532 | { 533 | currentState = STOP_GAME; 534 | PlayAudio("audio/hit.mp3"); 535 | } 536 | 537 | if (IsClick(0, 0, ScaleX(100.f), ScaleY(100.f))) 538 | { 539 | Jump(); 540 | PlayAudio("audio/wing.mp3"); 541 | } 542 | 543 | RenderPipes(); 544 | RenderBird(); 545 | 546 | if (score > 0) 547 | RenderScore(score, ScaleX(45.f), ScaleY(7.f), ScaleX(8.f), ScaleY(5.f)); 548 | } 549 | else if (currentState == STOP_GAME) 550 | { 551 | if (score > bestScore) 552 | { 553 | bestScore = score; 554 | newBestScore = true; 555 | 556 | char filePath[256]; 557 | snprintf(filePath, sizeof(filePath), "%s/save.txt", g_App->activity->internalDataPath); 558 | 559 | FILE* file = fopen(filePath, "w"); 560 | if (file != NULL) 561 | { 562 | fprintf(file, "%d", bestScore); 563 | fclose(file); 564 | } 565 | } 566 | currentState = FADE_OUT_GAMEOVER; 567 | } 568 | else if (currentState == FADE_OUT_GAMEOVER) 569 | { 570 | fadeOutAlpha -= 5; 571 | if (fadeOutAlpha <= 0) 572 | { 573 | fadeOutAlpha = 0; 574 | currentState = FALL_BIRD; 575 | PlayAudio("audio/die.mp3"); 576 | } 577 | 578 | RenderPipes(); 579 | RenderBird(); 580 | 581 | uint32_t color = 0x00FFFFFF | (fadeOutAlpha << 24); 582 | CreateBox(color, 0, 0, ScaleX(100.f), ScaleY(100.f)); 583 | } 584 | else if (currentState == FALL_BIRD) 585 | { 586 | ApplyGravity(); 587 | RenderPipes(); 588 | RenderBird(); 589 | 590 | if (bird.y + bird.height >= ScaleY(75.f) - bird.height) 591 | { 592 | bird.y = ScaleY(75.f) - bird.height; 593 | currentState = FADE_IN_PANEL; 594 | } 595 | } 596 | else if (currentState == FADE_IN_PANEL) 597 | { 598 | RenderPipes(); 599 | RenderBird(); 600 | 601 | panelY = MoveTowards(panelY, ScaleY(30), 20.0f); 602 | RenderTexture(t_panel, ScaleX(15.f), panelY, ScaleX(70.f), ScaleY(17.5f)); 603 | 604 | // Render default score 605 | RenderSmallScore(score, ScaleX(70.f), panelY + ScaleY(5.f), ScaleX(4.f), ScaleY(3.f)); 606 | 607 | // Render best score 608 | RenderSmallScore(bestScore, ScaleX(70.f), panelY + ScaleY(11.5f), ScaleX(4.f), ScaleY(3.f)); 609 | 610 | if (newBestScore) 611 | { 612 | RenderTexture(t_new, ScaleX(56.f), panelY + ScaleY(9.f), ScaleX(10.f), ScaleY(1.8f)); 613 | } 614 | 615 | 616 | RenderTexture(t_gameover, ScaleX(17.5f), ScaleY(18.f), ScaleX(65.f), ScaleY(6.f)); 617 | 618 | // Render medal 619 | if (score >= 40) medalTexture = t_platinum_medal; 620 | else if (score >= 30) medalTexture = t_gold_medal; 621 | else if (score >= 20) medalTexture = t_silver_medal; 622 | else if (score >= 10) medalTexture = t_bronze_medal; 623 | else medalTexture = 0; 624 | 625 | if (medalTexture) 626 | { 627 | RenderTexture(medalTexture, ScaleX(22.f), panelY + ScaleY(6.f), ScaleX(15.f), ScaleY(7.f)); 628 | } 629 | 630 | // button OK 631 | if (ButtonBump(t_ok, ScaleX(10.f), ScaleY(65.f), ScaleX(35.f), ScaleY(6.f))) 632 | { 633 | //later 634 | //PlayAudio("audio/click_sound.ogg"); 635 | 636 | //Reset 637 | currentState = IDLE; 638 | score = 0; 639 | 640 | bird.x = ScaleX(18.52f); 641 | bird.y = ScaleY(20.f); 642 | bird.velocity = 0.0f; 643 | bird.angle = 0.0f; 644 | bird.width = ScaleX(11.11f); 645 | bird.height = ScaleY(4.17f); 646 | bird.currentTexture = t_yellowbird_midflap; 647 | bird.frame = 0; 648 | bird.lastFrameTime = 0; 649 | 650 | 651 | pipes[0].x = ScaleX(100.f); 652 | pipes[0].y = ScaleY(37.5f); 653 | pipes[0].w = ScaleX(15.f); 654 | pipes[0].h = ScaleY(37.5f); 655 | pipes[0].offset = Random(ScaleY(-SPACE_BETWEEN_PIPES), ScaleY(SPACE_BETWEEN_PIPES)); 656 | 657 | pipes[1].x = ScaleX(100.f) + ScaleX(60.f); 658 | pipes[1].y = ScaleY(37.5f); 659 | pipes[1].w = ScaleX(15.f); 660 | pipes[1].h = ScaleY(37.5f); 661 | pipes[1].offset = Random(ScaleY(-SPACE_BETWEEN_PIPES), ScaleY(SPACE_BETWEEN_PIPES)); 662 | 663 | panelY = ScaleY(100); 664 | 665 | fadeOutAlpha = 255; 666 | 667 | newBestScore = false; 668 | } 669 | 670 | // button SHARE 671 | if (ButtonBump(t_share, ScaleX(55), ScaleY(65), ScaleX(35), ScaleY(6))) 672 | { 673 | //later 674 | //PlayAudio("audio/click_sound.ogg"); 675 | } 676 | } 677 | 678 | if (currentState == FADE_IN) 679 | { 680 | alpha += 5; 681 | if (alpha >= 255) 682 | { 683 | alpha = 255; 684 | currentState = FADE_OUT; 685 | } 686 | } 687 | else if (currentState == FADE_OUT) 688 | { 689 | alpha -= 5; 690 | if (alpha <= 0) 691 | { 692 | alpha = 0; 693 | currentState = READY_GAME; 694 | } 695 | } 696 | 697 | // render black screen 698 | if (currentState == FADE_IN || currentState == FADE_OUT) 699 | { 700 | uint32_t color = 0x00000000 | (alpha << 24); 701 | CreateBox(color, 0, 0, ScaleX(100), ScaleY(100)); 702 | } 703 | } 704 | 705 | bool ButtonBump(GLuint textureid, float posX, float posY, float width, float height) 706 | { 707 | bool released = false; 708 | 709 | if (mouse.isReleased) 710 | { 711 | if (IsMouseInSquare(mouse.x, mouse.y, posX, posY, width, height)) 712 | { 713 | released = true; 714 | } 715 | } 716 | 717 | if (released) { posY += ScaleY(1); } 718 | 719 | RenderTexture(textureid, posX, posY, width, height); 720 | 721 | return released; 722 | } 723 | 724 | bool Button(float posX, float posY, float width, float height) 725 | { 726 | bool released = false; 727 | 728 | if (mouse.isReleased) 729 | { 730 | if (IsMouseInSquare(mouse.x, mouse.y, posX, posY, width, height)) 731 | { 732 | released = true; 733 | } 734 | } 735 | 736 | return released; 737 | } 738 | 739 | bool IsClick(float posX, float posY, float width, float height) 740 | { 741 | bool down = false; 742 | 743 | if (mouse.isDown) 744 | { 745 | if (IsMouseInSquare(mouse.x, mouse.y, posX, posY, width, height)) 746 | { 747 | down = true; 748 | } 749 | } 750 | 751 | return down; 752 | } 753 | 754 | void ShutdownGame() 755 | { 756 | DestroyAudioPlayer(); 757 | DestroyAudioEngine(); 758 | 759 | // Delete textures 760 | glDeleteTextures(1, &t_pause); 761 | glDeleteTextures(1, &t_ok); 762 | glDeleteTextures(1, &t_menu); 763 | glDeleteTextures(1, &t_resume); 764 | glDeleteTextures(1, &t_score); 765 | glDeleteTextures(1, &t_share); 766 | glDeleteTextures(1, &t_start); 767 | glDeleteTextures(1, &t_ok); 768 | 769 | glDeleteTextures(1, &t_0); 770 | glDeleteTextures(1, &t_1); 771 | glDeleteTextures(1, &t_2); 772 | glDeleteTextures(1, &t_3); 773 | glDeleteTextures(1, &t_4); 774 | glDeleteTextures(1, &t_5); 775 | glDeleteTextures(1, &t_6); 776 | glDeleteTextures(1, &t_7); 777 | glDeleteTextures(1, &t_8); 778 | glDeleteTextures(1, &t_9); 779 | 780 | glDeleteTextures(1, &t_0_small); 781 | glDeleteTextures(1, &t_1_small); 782 | glDeleteTextures(1, &t_2_small); 783 | glDeleteTextures(1, &t_3_small); 784 | glDeleteTextures(1, &t_4_small); 785 | glDeleteTextures(1, &t_5_small); 786 | glDeleteTextures(1, &t_6_small); 787 | glDeleteTextures(1, &t_7_small); 788 | glDeleteTextures(1, &t_8_small); 789 | glDeleteTextures(1, &t_9_small); 790 | 791 | glDeleteTextures(1, &t_background_day); 792 | glDeleteTextures(1, &t_base); 793 | glDeleteTextures(1, &t_bronze_medal); 794 | glDeleteTextures(1, &t_gameover); 795 | glDeleteTextures(1, &t_gold_medal); 796 | glDeleteTextures(1, &t_logo); 797 | glDeleteTextures(1, &t_message); 798 | glDeleteTextures(1, &t_new); 799 | glDeleteTextures(1, &t_panel); 800 | glDeleteTextures(1, &t_pipe_green); 801 | glDeleteTextures(1, &t_platinum_medal); 802 | glDeleteTextures(1, &t_silver_medal); 803 | glDeleteTextures(1, &t_sparkle_sheet); 804 | glDeleteTextures(1, &t_yellowbird_downflap); 805 | glDeleteTextures(1, &t_yellowbird_midflap); 806 | glDeleteTextures(1, &t_yellowbird_upflap); 807 | } -------------------------------------------------------------------------------- /FlappyBird/app/src/main/jni/upng.c: -------------------------------------------------------------------------------- 1 | /* 2 | uPNG -- derived from LodePNG version 20100808 3 | 4 | Copyright (c) 2005-2010 Lode Vandevenne 5 | Copyright (c) 2010 Sean Middleditch 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 20 | 2. Altered source versions must be plainly marked as such, and must not be 21 | misrepresented as being the original software. 22 | 23 | 3. This notice may not be removed or altered from any source 24 | distribution. 25 | */ 26 | 27 | // #include 28 | #include 29 | #include 30 | // #include 31 | 32 | #ifndef NULL 33 | #define NULL ((void *)0) 34 | #endif 35 | 36 | #ifndef INT_MAX 37 | // 32bit 0x7FFFFFFF = 2147483647 38 | // 64bit 0x7FFFFFFF FFFFFFFF = 9223372036854775807 39 | #define INT_MAX 2147483647 40 | #endif 41 | 42 | // for upng 43 | #ifndef FILE 44 | #define FILE void * 45 | #define fopen(fn, m) NULL 46 | #define fseek(f, n, s) NULL 47 | #define ftell(f) -1 48 | #define rewind(f) NULL 49 | #define fclose(f) NULL 50 | #define fread(b, s, n, f) NULL 51 | #endif 52 | 53 | #include "upng.h" 54 | 55 | #define MAKE_BYTE(b) ((b) & 0xFF) 56 | #define MAKE_DWORD(a,b,c,d) ((MAKE_BYTE(a) << 24) | (MAKE_BYTE(b) << 16) | (MAKE_BYTE(c) << 8) | MAKE_BYTE(d)) 57 | #define MAKE_DWORD_PTR(p) MAKE_DWORD((p)[0], (p)[1], (p)[2], (p)[3]) 58 | 59 | #define CHUNK_IHDR MAKE_DWORD('I','H','D','R') 60 | #define CHUNK_IDAT MAKE_DWORD('I','D','A','T') 61 | #define CHUNK_IEND MAKE_DWORD('I','E','N','D') 62 | #define CHUNK_PLTE MAKE_DWORD('P','L','T','E') 63 | 64 | #define FIRST_LENGTH_CODE_INDEX 257 65 | #define LAST_LENGTH_CODE_INDEX 285 66 | 67 | #define NUM_DEFLATE_CODE_SYMBOLS 288 /*256 literals, the end code, some length codes, and 2 unused codes */ 68 | #define NUM_DISTANCE_SYMBOLS 32 /*the distance codes have their own symbols, 30 used, 2 unused */ 69 | #define NUM_CODE_LENGTH_CODES 19 /*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros */ 70 | #define MAX_SYMBOLS 288 /* largest number of symbols used by any tree type */ 71 | 72 | #define DEFLATE_CODE_BITLEN 15 73 | #define DISTANCE_BITLEN 15 74 | #define CODE_LENGTH_BITLEN 7 75 | #define MAX_BIT_LENGTH 15 /* largest bitlen used by any tree type */ 76 | 77 | #define DEFLATE_CODE_BUFFER_SIZE (NUM_DEFLATE_CODE_SYMBOLS * 2) 78 | #define DISTANCE_BUFFER_SIZE (NUM_DISTANCE_SYMBOLS * 2) 79 | #define CODE_LENGTH_BUFFER_SIZE (NUM_DISTANCE_SYMBOLS * 2) 80 | 81 | #define SET_ERROR(upng,code) do { (upng)->error = (code); (upng)->error_line = __LINE__; } while (0) 82 | 83 | #define upng_chunk_length(chunk) MAKE_DWORD_PTR(chunk) 84 | #define upng_chunk_type(chunk) MAKE_DWORD_PTR((chunk) + 4) 85 | #define upng_chunk_critical(chunk) (((chunk)[4] & 32) == 0) 86 | 87 | typedef enum upng_state { 88 | UPNG_ERROR = -1, 89 | UPNG_DECODED = 0, 90 | UPNG_HEADER = 1, 91 | UPNG_NEW = 2 92 | } upng_state; 93 | 94 | typedef enum upng_color { 95 | UPNG_LUM = 0, 96 | UPNG_RGB = 2, 97 | UPNG_INDX = 3, 98 | UPNG_LUMA = 4, 99 | UPNG_RGBA = 6 100 | } upng_color; 101 | 102 | typedef struct upng_source { 103 | const unsigned char* buffer; 104 | unsigned long size; 105 | char owning; 106 | } upng_source; 107 | 108 | struct upng_t { 109 | unsigned width; 110 | unsigned height; 111 | 112 | upng_color color_type; 113 | unsigned color_depth; 114 | upng_format format; 115 | 116 | unsigned char* buffer; 117 | unsigned long size; 118 | 119 | unsigned char* palette; 120 | 121 | upng_error error; 122 | unsigned error_line; 123 | 124 | upng_state state; 125 | upng_source source; 126 | }; 127 | 128 | typedef struct huffman_tree { 129 | unsigned* tree2d; 130 | unsigned maxbitlen; /*maximum number of bits a single code can get */ 131 | unsigned numcodes; /*number of symbols in the alphabet = number of codes */ 132 | } huffman_tree; 133 | 134 | static const unsigned LENGTH_BASE[29] = { /*the base lengths represented by codes 257-285 */ 135 | 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 136 | 67, 83, 99, 115, 131, 163, 195, 227, 258 137 | }; 138 | 139 | static const unsigned LENGTH_EXTRA[29] = { /*the extra bits used by codes 257-285 (added to base length) */ 140 | 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 141 | 5, 5, 5, 0 142 | }; 143 | 144 | static const unsigned DISTANCE_BASE[30] = { /*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree) */ 145 | 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 146 | 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 147 | }; 148 | 149 | static const unsigned DISTANCE_EXTRA[30] = { /*the extra bits of backwards distances (added to base) */ 150 | 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 151 | 11, 11, 12, 12, 13, 13 152 | }; 153 | 154 | static const unsigned CLCL[NUM_CODE_LENGTH_CODES] /*the order in which "code length alphabet code lengths" are stored, out of this the huffman tree of the dynamic huffman tree lengths is generated */ 155 | = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; 156 | 157 | static const unsigned FIXED_DEFLATE_CODE_TREE[NUM_DEFLATE_CODE_SYMBOLS * 2] = { 158 | 289, 370, 290, 307, 546, 291, 561, 292, 293, 300, 294, 297, 295, 296, 0, 1, 159 | 2, 3, 298, 299, 4, 5, 6, 7, 301, 304, 302, 303, 8, 9, 10, 11, 305, 306, 12, 160 | 13, 14, 15, 308, 339, 309, 324, 310, 317, 311, 314, 312, 313, 16, 17, 18, 161 | 19, 315, 316, 20, 21, 22, 23, 318, 321, 319, 320, 24, 25, 26, 27, 322, 323, 162 | 28, 29, 30, 31, 325, 332, 326, 329, 327, 328, 32, 33, 34, 35, 330, 331, 36, 163 | 37, 38, 39, 333, 336, 334, 335, 40, 41, 42, 43, 337, 338, 44, 45, 46, 47, 164 | 340, 355, 341, 348, 342, 345, 343, 344, 48, 49, 50, 51, 346, 347, 52, 53, 165 | 54, 55, 349, 352, 350, 351, 56, 57, 58, 59, 353, 354, 60, 61, 62, 63, 356, 166 | 363, 357, 360, 358, 359, 64, 65, 66, 67, 361, 362, 68, 69, 70, 71, 364, 167 | 367, 365, 366, 72, 73, 74, 75, 368, 369, 76, 77, 78, 79, 371, 434, 372, 168 | 403, 373, 388, 374, 381, 375, 378, 376, 377, 80, 81, 82, 83, 379, 380, 84, 169 | 85, 86, 87, 382, 385, 383, 384, 88, 89, 90, 91, 386, 387, 92, 93, 94, 95, 170 | 389, 396, 390, 393, 391, 392, 96, 97, 98, 99, 394, 395, 100, 101, 102, 103, 171 | 397, 400, 398, 399, 104, 105, 106, 107, 401, 402, 108, 109, 110, 111, 404, 172 | 419, 405, 412, 406, 409, 407, 408, 112, 113, 114, 115, 410, 411, 116, 117, 173 | 118, 119, 413, 416, 414, 415, 120, 121, 122, 123, 417, 418, 124, 125, 126, 174 | 127, 420, 427, 421, 424, 422, 423, 128, 129, 130, 131, 425, 426, 132, 133, 175 | 134, 135, 428, 431, 429, 430, 136, 137, 138, 139, 432, 433, 140, 141, 142, 176 | 143, 435, 483, 436, 452, 568, 437, 438, 445, 439, 442, 440, 441, 144, 145, 177 | 146, 147, 443, 444, 148, 149, 150, 151, 446, 449, 447, 448, 152, 153, 154, 178 | 155, 450, 451, 156, 157, 158, 159, 453, 468, 454, 461, 455, 458, 456, 457, 179 | 160, 161, 162, 163, 459, 460, 164, 165, 166, 167, 462, 465, 463, 464, 168, 180 | 169, 170, 171, 466, 467, 172, 173, 174, 175, 469, 476, 470, 473, 471, 472, 181 | 176, 177, 178, 179, 474, 475, 180, 181, 182, 183, 477, 480, 478, 479, 184, 182 | 185, 186, 187, 481, 482, 188, 189, 190, 191, 484, 515, 485, 500, 486, 493, 183 | 487, 490, 488, 489, 192, 193, 194, 195, 491, 492, 196, 197, 198, 199, 494, 184 | 497, 495, 496, 200, 201, 202, 203, 498, 499, 204, 205, 206, 207, 501, 508, 185 | 502, 505, 503, 504, 208, 209, 210, 211, 506, 507, 212, 213, 214, 215, 509, 186 | 512, 510, 511, 216, 217, 218, 219, 513, 514, 220, 221, 222, 223, 516, 531, 187 | 517, 524, 518, 521, 519, 520, 224, 225, 226, 227, 522, 523, 228, 229, 230, 188 | 231, 525, 528, 526, 527, 232, 233, 234, 235, 529, 530, 236, 237, 238, 239, 189 | 532, 539, 533, 536, 534, 535, 240, 241, 242, 243, 537, 538, 244, 245, 246, 190 | 247, 540, 543, 541, 542, 248, 249, 250, 251, 544, 545, 252, 253, 254, 255, 191 | 547, 554, 548, 551, 549, 550, 256, 257, 258, 259, 552, 553, 260, 261, 262, 192 | 263, 555, 558, 556, 557, 264, 265, 266, 267, 559, 560, 268, 269, 270, 271, 193 | 562, 565, 563, 564, 272, 273, 274, 275, 566, 567, 276, 277, 278, 279, 569, 194 | 572, 570, 571, 280, 281, 282, 283, 573, 574, 284, 285, 286, 287, 0, 0 195 | }; 196 | 197 | static const unsigned FIXED_DISTANCE_TREE[NUM_DISTANCE_SYMBOLS * 2] = { 198 | 33, 48, 34, 41, 35, 38, 36, 37, 0, 1, 2, 3, 39, 40, 4, 5, 6, 7, 42, 45, 43, 199 | 44, 8, 9, 10, 11, 46, 47, 12, 13, 14, 15, 49, 56, 50, 53, 51, 52, 16, 17, 200 | 18, 19, 54, 55, 20, 21, 22, 23, 57, 60, 58, 59, 24, 25, 26, 27, 61, 62, 28, 201 | 29, 30, 31, 0, 0 202 | }; 203 | 204 | static unsigned char read_bit(unsigned long* bitpointer, const unsigned char* bitstream) 205 | { 206 | unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> ((*bitpointer) & 0x7)) & 1); 207 | (*bitpointer)++; 208 | return result; 209 | } 210 | 211 | static unsigned read_bits(unsigned long* bitpointer, const unsigned char* bitstream, unsigned long nbits) 212 | { 213 | unsigned result = 0, i; 214 | for (i = 0; i < nbits; i++) 215 | result |= ((unsigned)read_bit(bitpointer, bitstream)) << i; 216 | return result; 217 | } 218 | 219 | /* the buffer must be numcodes*2 in size! */ 220 | static void huffman_tree_init(huffman_tree* tree, unsigned* buffer, unsigned numcodes, unsigned maxbitlen) 221 | { 222 | tree->tree2d = buffer; 223 | 224 | tree->numcodes = numcodes; 225 | tree->maxbitlen = maxbitlen; 226 | } 227 | 228 | /*given the code lengths (as stored in the PNG file), generate the tree as defined by Deflate. maxbitlen is the maximum bits that a code in the tree can have. return value is error.*/ 229 | static void huffman_tree_create_lengths(upng_t* upng, huffman_tree* tree, const unsigned* bitlen) 230 | { 231 | unsigned tree1d[MAX_SYMBOLS]; 232 | unsigned blcount[MAX_BIT_LENGTH]; 233 | unsigned nextcode[MAX_BIT_LENGTH + 1]; 234 | unsigned bits, n, i; 235 | unsigned nodefilled = 0; /*up to which node it is filled */ 236 | unsigned treepos = 0; /*position in the tree (1 of the numcodes columns) */ 237 | 238 | /* initialize local vectors */ 239 | memset(blcount, 0, sizeof(blcount)); 240 | memset(nextcode, 0, sizeof(nextcode)); 241 | 242 | /*step 1: count number of instances of each code length */ 243 | for (bits = 0; bits < tree->numcodes; bits++) { 244 | blcount[bitlen[bits]]++; 245 | } 246 | 247 | /*step 2: generate the nextcode values */ 248 | for (bits = 1; bits <= tree->maxbitlen; bits++) { 249 | nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1; 250 | } 251 | 252 | /*step 3: generate all the codes */ 253 | for (n = 0; n < tree->numcodes; n++) { 254 | if (bitlen[n] != 0) { 255 | tree1d[n] = nextcode[bitlen[n]]++; 256 | } 257 | } 258 | 259 | /*convert tree1d[] to tree2d[][]. In the 2D array, a value of 32767 means uninited, a value >= numcodes is an address to another bit, a value < numcodes is a code. The 2 rows are the 2 possible bit values (0 or 1), there are as many columns as codes - 1 260 | a good huffmann tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. Here, the internal nodes are stored (what their 0 and 1 option point to). There is only memory for such good tree currently, if there are more nodes (due to too long length codes), error 55 will happen */ 261 | for (n = 0; n < tree->numcodes * 2; n++) { 262 | tree->tree2d[n] = 32767; /*32767 here means the tree2d isn't filled there yet */ 263 | } 264 | 265 | for (n = 0; n < tree->numcodes; n++) { /*the codes */ 266 | for (i = 0; i < bitlen[n]; i++) { /*the bits for this code */ 267 | unsigned char bit = (unsigned char)((tree1d[n] >> (bitlen[n] - i - 1)) & 1); 268 | /* check if oversubscribed */ 269 | if (treepos > tree->numcodes - 2) { 270 | SET_ERROR(upng, UPNG_EMALFORMED); 271 | return; 272 | } 273 | 274 | if (tree->tree2d[2 * treepos + bit] == 32767) { /*not yet filled in */ 275 | if (i + 1 == bitlen[n]) { /*last bit */ 276 | tree->tree2d[2 * treepos + bit] = n; /*put the current code in it */ 277 | treepos = 0; 278 | } 279 | else { /*put address of the next step in here, first that address has to be found of course (it's just nodefilled + 1)... */ 280 | nodefilled++; 281 | tree->tree2d[2 * treepos + bit] = nodefilled + tree->numcodes; /*addresses encoded with numcodes added to it */ 282 | treepos = nodefilled; 283 | } 284 | } 285 | else { 286 | treepos = tree->tree2d[2 * treepos + bit] - tree->numcodes; 287 | } 288 | } 289 | } 290 | 291 | for (n = 0; n < tree->numcodes * 2; n++) { 292 | if (tree->tree2d[n] == 32767) { 293 | tree->tree2d[n] = 0; /*remove possible remaining 32767's */ 294 | } 295 | } 296 | } 297 | 298 | static unsigned huffman_decode_symbol(upng_t* upng, const unsigned char* in, unsigned long* bp, const huffman_tree* codetree, unsigned long inlength) 299 | { 300 | unsigned treepos = 0, ct; 301 | unsigned char bit; 302 | for (;;) { 303 | /* error: end of input memory reached without endcode */ 304 | if (((*bp) & 0x07) == 0 && ((*bp) >> 3) > inlength) { 305 | SET_ERROR(upng, UPNG_EMALFORMED); 306 | return 0; 307 | } 308 | 309 | bit = read_bit(bp, in); 310 | 311 | ct = codetree->tree2d[(treepos << 1) | bit]; 312 | if (ct < codetree->numcodes) { 313 | return ct; 314 | } 315 | 316 | treepos = ct - codetree->numcodes; 317 | if (treepos >= codetree->numcodes) { 318 | SET_ERROR(upng, UPNG_EMALFORMED); 319 | return 0; 320 | } 321 | } 322 | } 323 | 324 | /* get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/ 325 | static void get_tree_inflate_dynamic(upng_t* upng, huffman_tree* codetree, huffman_tree* codetreeD, huffman_tree* codelengthcodetree, const unsigned char* in, unsigned long* bp, unsigned long inlength) 326 | { 327 | unsigned codelengthcode[NUM_CODE_LENGTH_CODES]; 328 | unsigned bitlen[NUM_DEFLATE_CODE_SYMBOLS]; 329 | unsigned bitlenD[NUM_DISTANCE_SYMBOLS]; 330 | unsigned n, hlit, hdist, hclen, i; 331 | 332 | /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated */ 333 | /*C-code note: use no "return" between ctor and dtor of an uivector! */ 334 | if ((*bp) >> 3 >= inlength - 2) { 335 | SET_ERROR(upng, UPNG_EMALFORMED); 336 | return; 337 | } 338 | 339 | /* clear bitlen arrays */ 340 | memset(bitlen, 0, sizeof(bitlen)); 341 | memset(bitlenD, 0, sizeof(bitlenD)); 342 | 343 | /*the bit pointer is or will go past the memory */ 344 | hlit = read_bits(bp, in, 5) + 257; /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already */ 345 | hdist = read_bits(bp, in, 5) + 1; /*number of distance codes. Unlike the spec, the value 1 is added to it here already */ 346 | hclen = read_bits(bp, in, 4) + 4; /*number of code length codes. Unlike the spec, the value 4 is added to it here already */ 347 | 348 | for (i = 0; i < NUM_CODE_LENGTH_CODES; i++) { 349 | if (i < hclen) { 350 | codelengthcode[CLCL[i]] = read_bits(bp, in, 3); 351 | } 352 | else { 353 | codelengthcode[CLCL[i]] = 0; /*if not, it must stay 0 */ 354 | } 355 | } 356 | 357 | huffman_tree_create_lengths(upng, codelengthcodetree, codelengthcode); 358 | 359 | /* bail now if we encountered an error earlier */ 360 | if (upng->error != UPNG_EOK) { 361 | return; 362 | } 363 | 364 | /*now we can use this tree to read the lengths for the tree that this function will return */ 365 | i = 0; 366 | while (i < hlit + hdist) { /*i is the current symbol we're reading in the part that contains the code lengths of lit/len codes and dist codes */ 367 | unsigned code = huffman_decode_symbol(upng, in, bp, codelengthcodetree, inlength); 368 | if (upng->error != UPNG_EOK) { 369 | break; 370 | } 371 | 372 | if (code <= 15) { /*a length code */ 373 | if (i < hlit) { 374 | bitlen[i] = code; 375 | } 376 | else { 377 | bitlenD[i - hlit] = code; 378 | } 379 | i++; 380 | } 381 | else if (code == 16) { /*repeat previous */ 382 | unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6) */ 383 | unsigned value; /*set value to the previous code */ 384 | 385 | if ((*bp) >> 3 >= inlength) { 386 | SET_ERROR(upng, UPNG_EMALFORMED); 387 | break; 388 | } 389 | /*error, bit pointer jumps past memory */ 390 | replength += read_bits(bp, in, 2); 391 | 392 | if ((i - 1) < hlit) { 393 | value = bitlen[i - 1]; 394 | } 395 | else { 396 | value = bitlenD[i - hlit - 1]; 397 | } 398 | 399 | /*repeat this value in the next lengths */ 400 | for (n = 0; n < replength; n++) { 401 | /* i is larger than the amount of codes */ 402 | if (i >= hlit + hdist) { 403 | SET_ERROR(upng, UPNG_EMALFORMED); 404 | break; 405 | } 406 | 407 | if (i < hlit) { 408 | bitlen[i] = value; 409 | } 410 | else { 411 | bitlenD[i - hlit] = value; 412 | } 413 | i++; 414 | } 415 | } 416 | else if (code == 17) { /*repeat "0" 3-10 times */ 417 | unsigned replength = 3; /*read in the bits that indicate repeat length */ 418 | if ((*bp) >> 3 >= inlength) { 419 | SET_ERROR(upng, UPNG_EMALFORMED); 420 | break; 421 | } 422 | 423 | /*error, bit pointer jumps past memory */ 424 | replength += read_bits(bp, in, 3); 425 | 426 | /*repeat this value in the next lengths */ 427 | for (n = 0; n < replength; n++) { 428 | /* error: i is larger than the amount of codes */ 429 | if (i >= hlit + hdist) { 430 | SET_ERROR(upng, UPNG_EMALFORMED); 431 | break; 432 | } 433 | 434 | if (i < hlit) { 435 | bitlen[i] = 0; 436 | } 437 | else { 438 | bitlenD[i - hlit] = 0; 439 | } 440 | i++; 441 | } 442 | } 443 | else if (code == 18) { /*repeat "0" 11-138 times */ 444 | unsigned replength = 11; /*read in the bits that indicate repeat length */ 445 | /* error, bit pointer jumps past memory */ 446 | if ((*bp) >> 3 >= inlength) { 447 | SET_ERROR(upng, UPNG_EMALFORMED); 448 | break; 449 | } 450 | 451 | replength += read_bits(bp, in, 7); 452 | 453 | /*repeat this value in the next lengths */ 454 | for (n = 0; n < replength; n++) { 455 | /* i is larger than the amount of codes */ 456 | if (i >= hlit + hdist) { 457 | SET_ERROR(upng, UPNG_EMALFORMED); 458 | break; 459 | } 460 | if (i < hlit) 461 | bitlen[i] = 0; 462 | else 463 | bitlenD[i - hlit] = 0; 464 | i++; 465 | } 466 | } 467 | else { 468 | /* somehow an unexisting code appeared. This can never happen. */ 469 | SET_ERROR(upng, UPNG_EMALFORMED); 470 | break; 471 | } 472 | } 473 | 474 | if (upng->error == UPNG_EOK && bitlen[256] == 0) { 475 | SET_ERROR(upng, UPNG_EMALFORMED); 476 | } 477 | 478 | /*the length of the end code 256 must be larger than 0 */ 479 | /*now we've finally got hlit and hdist, so generate the code trees, and the function is done */ 480 | if (upng->error == UPNG_EOK) { 481 | huffman_tree_create_lengths(upng, codetree, bitlen); 482 | } 483 | if (upng->error == UPNG_EOK) { 484 | huffman_tree_create_lengths(upng, codetreeD, bitlenD); 485 | } 486 | } 487 | 488 | /*inflate a block with dynamic of fixed Huffman tree*/ 489 | static void inflate_huffman(upng_t* upng, unsigned char* out, unsigned long outsize, const unsigned char* in, unsigned long* bp, unsigned long* pos, unsigned long inlength, unsigned btype) 490 | { 491 | unsigned codetree_buffer[DEFLATE_CODE_BUFFER_SIZE]; 492 | unsigned codetreeD_buffer[DISTANCE_BUFFER_SIZE]; 493 | unsigned done = 0; 494 | 495 | huffman_tree codetree; 496 | huffman_tree codetreeD; 497 | 498 | if (btype == 1) { 499 | /* fixed trees */ 500 | huffman_tree_init(&codetree, (unsigned*)FIXED_DEFLATE_CODE_TREE, NUM_DEFLATE_CODE_SYMBOLS, DEFLATE_CODE_BITLEN); 501 | huffman_tree_init(&codetreeD, (unsigned*)FIXED_DISTANCE_TREE, NUM_DISTANCE_SYMBOLS, DISTANCE_BITLEN); 502 | } 503 | else if (btype == 2) { 504 | /* dynamic trees */ 505 | unsigned codelengthcodetree_buffer[CODE_LENGTH_BUFFER_SIZE]; 506 | huffman_tree codelengthcodetree; 507 | 508 | huffman_tree_init(&codetree, codetree_buffer, NUM_DEFLATE_CODE_SYMBOLS, DEFLATE_CODE_BITLEN); 509 | huffman_tree_init(&codetreeD, codetreeD_buffer, NUM_DISTANCE_SYMBOLS, DISTANCE_BITLEN); 510 | huffman_tree_init(&codelengthcodetree, codelengthcodetree_buffer, NUM_CODE_LENGTH_CODES, CODE_LENGTH_BITLEN); 511 | get_tree_inflate_dynamic(upng, &codetree, &codetreeD, &codelengthcodetree, in, bp, inlength); 512 | } 513 | 514 | while (done == 0) { 515 | unsigned code = huffman_decode_symbol(upng, in, bp, &codetree, inlength); 516 | if (upng->error != UPNG_EOK) { 517 | return; 518 | } 519 | 520 | if (code == 256) { 521 | /* end code */ 522 | done = 1; 523 | } 524 | else if (code <= 255) { 525 | /* literal symbol */ 526 | if ((*pos) >= outsize) { 527 | SET_ERROR(upng, UPNG_EMALFORMED); 528 | return; 529 | } 530 | 531 | /* store output */ 532 | out[(*pos)++] = (unsigned char)(code); 533 | } 534 | else if (code >= FIRST_LENGTH_CODE_INDEX && code <= LAST_LENGTH_CODE_INDEX) { /*length code */ 535 | /* part 1: get length base */ 536 | unsigned long length = LENGTH_BASE[code - FIRST_LENGTH_CODE_INDEX]; 537 | unsigned codeD, distance, numextrabitsD; 538 | unsigned long start, forward, backward, numextrabits; 539 | 540 | /* part 2: get extra bits and add the value of that to length */ 541 | numextrabits = LENGTH_EXTRA[code - FIRST_LENGTH_CODE_INDEX]; 542 | 543 | /* error, bit pointer will jump past memory */ 544 | if (((*bp) >> 3) >= inlength) { 545 | SET_ERROR(upng, UPNG_EMALFORMED); 546 | return; 547 | } 548 | length += read_bits(bp, in, numextrabits); 549 | 550 | /*part 3: get distance code */ 551 | codeD = huffman_decode_symbol(upng, in, bp, &codetreeD, inlength); 552 | if (upng->error != UPNG_EOK) { 553 | return; 554 | } 555 | 556 | /* invalid distance code (30-31 are never used) */ 557 | if (codeD > 29) { 558 | SET_ERROR(upng, UPNG_EMALFORMED); 559 | return; 560 | } 561 | 562 | distance = DISTANCE_BASE[codeD]; 563 | 564 | /*part 4: get extra bits from distance */ 565 | numextrabitsD = DISTANCE_EXTRA[codeD]; 566 | 567 | /* error, bit pointer will jump past memory */ 568 | if (((*bp) >> 3) >= inlength) { 569 | SET_ERROR(upng, UPNG_EMALFORMED); 570 | return; 571 | } 572 | 573 | distance += read_bits(bp, in, numextrabitsD); 574 | 575 | /*part 5: fill in all the out[n] values based on the length and dist */ 576 | start = (*pos); 577 | backward = start - distance; 578 | 579 | if ((*pos) + length >= outsize) { 580 | SET_ERROR(upng, UPNG_EMALFORMED); 581 | return; 582 | } 583 | 584 | for (forward = 0; forward < length; forward++) { 585 | out[(*pos)++] = out[backward]; 586 | backward++; 587 | 588 | if (backward >= start) { 589 | backward = start - distance; 590 | } 591 | } 592 | } 593 | } 594 | } 595 | 596 | static void inflate_uncompressed(upng_t* upng, unsigned char* out, unsigned long outsize, const unsigned char* in, unsigned long* bp, unsigned long* pos, unsigned long inlength) 597 | { 598 | unsigned long p; 599 | unsigned len, nlen, n; 600 | 601 | /* go to first boundary of byte */ 602 | while (((*bp) & 0x7) != 0) { 603 | (*bp)++; 604 | } 605 | p = (*bp) / 8; /*byte position */ 606 | 607 | /* read len (2 bytes) and nlen (2 bytes) */ 608 | if (p >= inlength - 4) { 609 | SET_ERROR(upng, UPNG_EMALFORMED); 610 | return; 611 | } 612 | 613 | len = in[p] + 256 * in[p + 1]; 614 | p += 2; 615 | nlen = in[p] + 256 * in[p + 1]; 616 | p += 2; 617 | 618 | /* check if 16-bit nlen is really the one's complement of len */ 619 | if (len + nlen != 65535) { 620 | SET_ERROR(upng, UPNG_EMALFORMED); 621 | return; 622 | } 623 | 624 | if ((*pos) + len >= outsize) { 625 | SET_ERROR(upng, UPNG_EMALFORMED); 626 | return; 627 | } 628 | 629 | /* read the literal data: len bytes are now stored in the out buffer */ 630 | if (p + len > inlength) { 631 | SET_ERROR(upng, UPNG_EMALFORMED); 632 | return; 633 | } 634 | 635 | for (n = 0; n < len; n++) { 636 | out[(*pos)++] = in[p++]; 637 | } 638 | 639 | (*bp) = p * 8; 640 | } 641 | 642 | /*inflate the deflated data (cfr. deflate spec); return value is the error*/ 643 | static upng_error uz_inflate_data(upng_t* upng, unsigned char* out, unsigned long outsize, const unsigned char* in, unsigned long insize, unsigned long inpos) 644 | { 645 | unsigned long bp = 0; /*bit pointer in the "in" data, current byte is bp >> 3, current bit is bp & 0x7 (from lsb to msb of the byte) */ 646 | unsigned long pos = 0; /*byte position in the out buffer */ 647 | 648 | unsigned done = 0; 649 | 650 | while (done == 0) { 651 | unsigned btype; 652 | 653 | /* ensure next bit doesn't point past the end of the buffer */ 654 | if ((bp >> 3) >= insize) { 655 | SET_ERROR(upng, UPNG_EMALFORMED); 656 | return upng->error; 657 | } 658 | 659 | /* read block control bits */ 660 | done = read_bit(&bp, &in[inpos]); 661 | btype = read_bit(&bp, &in[inpos]) | (read_bit(&bp, &in[inpos]) << 1); 662 | 663 | /* process control type appropriateyly */ 664 | if (btype == 3) { 665 | SET_ERROR(upng, UPNG_EMALFORMED); 666 | return upng->error; 667 | } 668 | else if (btype == 0) { 669 | inflate_uncompressed(upng, out, outsize, &in[inpos], &bp, &pos, insize); /*no compression */ 670 | } 671 | else { 672 | inflate_huffman(upng, out, outsize, &in[inpos], &bp, &pos, insize, btype); /*compression, btype 01 or 10 */ 673 | } 674 | 675 | /* stop if an error has occured */ 676 | if (upng->error != UPNG_EOK) { 677 | return upng->error; 678 | } 679 | } 680 | 681 | return upng->error; 682 | } 683 | 684 | static upng_error uz_inflate(upng_t* upng, unsigned char* out, unsigned long outsize, const unsigned char* in, unsigned long insize) 685 | { 686 | /* we require two bytes for the zlib data header */ 687 | if (insize < 2) { 688 | SET_ERROR(upng, UPNG_EMALFORMED); 689 | return upng->error; 690 | } 691 | 692 | /* 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way */ 693 | if ((in[0] * 256 + in[1]) % 31 != 0) { 694 | SET_ERROR(upng, UPNG_EMALFORMED); 695 | return upng->error; 696 | } 697 | 698 | /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec */ 699 | if ((in[0] & 15) != 8 || ((in[0] >> 4) & 15) > 7) { 700 | SET_ERROR(upng, UPNG_EMALFORMED); 701 | return upng->error; 702 | } 703 | 704 | /* the specification of PNG says about the zlib stream: "The additional flags shall not specify a preset dictionary." */ 705 | if (((in[1] >> 5) & 1) != 0) { 706 | SET_ERROR(upng, UPNG_EMALFORMED); 707 | return upng->error; 708 | } 709 | 710 | /* create output buffer */ 711 | uz_inflate_data(upng, out, outsize, in, insize, 2); 712 | 713 | return upng->error; 714 | } 715 | 716 | /*Paeth predicter, used by PNG filter type 4*/ 717 | static int paeth_predictor(int a, int b, int c) 718 | { 719 | int p = a + b - c; 720 | int pa = p > a ? p - a : a - p; 721 | int pb = p > b ? p - b : b - p; 722 | int pc = p > c ? p - c : c - p; 723 | 724 | if (pa <= pb && pa <= pc) 725 | return a; 726 | else if (pb <= pc) 727 | return b; 728 | else 729 | return c; 730 | } 731 | 732 | static void unfilter_scanline(upng_t* upng, unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, unsigned long bytewidth, unsigned char filterType, unsigned long length) 733 | { 734 | /* 735 | For PNG filter method 0 736 | unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works byte per byte (bytewidth = 1) 737 | precon is the previous unfiltered scanline, recon the result, scanline the current one 738 | the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead 739 | recon and scanline MAY be the same memory address! precon must be disjoint. 740 | */ 741 | 742 | unsigned long i; 743 | switch (filterType) { 744 | case 0: 745 | for (i = 0; i < length; i++) 746 | recon[i] = scanline[i]; 747 | break; 748 | case 1: 749 | for (i = 0; i < bytewidth; i++) 750 | recon[i] = scanline[i]; 751 | for (i = bytewidth; i < length; i++) 752 | recon[i] = scanline[i] + recon[i - bytewidth]; 753 | break; 754 | case 2: 755 | if (precon) 756 | for (i = 0; i < length; i++) 757 | recon[i] = scanline[i] + precon[i]; 758 | else 759 | for (i = 0; i < length; i++) 760 | recon[i] = scanline[i]; 761 | break; 762 | case 3: 763 | if (precon) { 764 | for (i = 0; i < bytewidth; i++) 765 | recon[i] = scanline[i] + precon[i] / 2; 766 | for (i = bytewidth; i < length; i++) 767 | recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2); 768 | } 769 | else { 770 | for (i = 0; i < bytewidth; i++) 771 | recon[i] = scanline[i]; 772 | for (i = bytewidth; i < length; i++) 773 | recon[i] = scanline[i] + recon[i - bytewidth] / 2; 774 | } 775 | break; 776 | case 4: 777 | if (precon) { 778 | for (i = 0; i < bytewidth; i++) 779 | recon[i] = (unsigned char)(scanline[i] + paeth_predictor(0, precon[i], 0)); 780 | for (i = bytewidth; i < length; i++) 781 | recon[i] = (unsigned char)(scanline[i] + paeth_predictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); 782 | } 783 | else { 784 | for (i = 0; i < bytewidth; i++) 785 | recon[i] = scanline[i]; 786 | for (i = bytewidth; i < length; i++) 787 | recon[i] = (unsigned char)(scanline[i] + paeth_predictor(recon[i - bytewidth], 0, 0)); 788 | } 789 | break; 790 | default: 791 | SET_ERROR(upng, UPNG_EMALFORMED); 792 | break; 793 | } 794 | } 795 | 796 | static void unfilter(upng_t* upng, unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) 797 | { 798 | /* 799 | For PNG filter method 0 800 | this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 it's called 7 times) 801 | out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline 802 | w and h are image dimensions or dimensions of reduced image, bpp is bpp per pixel 803 | in and out are allowed to be the same memory address! 804 | */ 805 | 806 | unsigned y; 807 | unsigned char* prevline = 0; 808 | 809 | unsigned long bytewidth = (bpp + 7) / 8; /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise */ 810 | unsigned long linebytes = (w * bpp + 7) / 8; 811 | 812 | for (y = 0; y < h; y++) { 813 | unsigned long outindex = linebytes * y; 814 | unsigned long inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row */ 815 | unsigned char filterType = in[inindex]; 816 | 817 | unfilter_scanline(upng, &out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes); 818 | if (upng->error != UPNG_EOK) { 819 | return; 820 | } 821 | 822 | prevline = &out[outindex]; 823 | } 824 | } 825 | 826 | static void remove_padding_bits(unsigned char* out, const unsigned char* in, unsigned long olinebits, unsigned long ilinebits, unsigned h) 827 | { 828 | /* 829 | After filtering there are still padding bpp if scanlines have non multiple of 8 bit amounts. They need to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers for the Adam7 code, the color convert code and the output to the user. 830 | in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must have >= ilinebits*h bpp, out must have >= olinebits*h bpp, olinebits must be <= ilinebits 831 | also used to move bpp after earlier such operations happened, e.g. in a sequence of reduced images from Adam7 832 | only useful if (ilinebits - olinebits) is a value in the range 1..7 833 | */ 834 | unsigned y; 835 | unsigned long diff = ilinebits - olinebits; 836 | unsigned long obp = 0, ibp = 0; /*bit pointers */ 837 | for (y = 0; y < h; y++) { 838 | unsigned long x; 839 | for (x = 0; x < olinebits; x++) { 840 | unsigned char bit = (unsigned char)((in[(ibp) >> 3] >> (7 - ((ibp) & 0x7))) & 1); 841 | ibp++; 842 | 843 | if (bit == 0) 844 | out[(obp) >> 3] &= (unsigned char)(~(1 << (7 - ((obp) & 0x7)))); 845 | else 846 | out[(obp) >> 3] |= (1 << (7 - ((obp) & 0x7))); 847 | ++obp; 848 | } 849 | ibp += diff; 850 | } 851 | } 852 | 853 | /*out must be buffer big enough to contain full image, and in must contain the full decompressed data from the IDAT chunks*/ 854 | static void post_process_scanlines(upng_t* upng, unsigned char* out, unsigned char* in, const upng_t* info_png) 855 | { 856 | unsigned bpp = upng_get_bpp(info_png); 857 | unsigned w = info_png->width; 858 | unsigned h = info_png->height; 859 | 860 | if (bpp == 0) { 861 | SET_ERROR(upng, UPNG_EMALFORMED); 862 | return; 863 | } 864 | 865 | if (bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) { 866 | unfilter(upng, in, in, w, h, bpp); 867 | if (upng->error != UPNG_EOK) { 868 | return; 869 | } 870 | remove_padding_bits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h); 871 | } 872 | else { 873 | unfilter(upng, out, in, w, h, bpp); /*we can immediatly filter into the out buffer, no other steps needed */ 874 | } 875 | } 876 | 877 | static upng_format determine_format(upng_t* upng) { 878 | switch (upng->color_type) { 879 | case UPNG_LUM: 880 | switch (upng->color_depth) { 881 | case 1: 882 | return UPNG_LUMINANCE1; 883 | case 2: 884 | return UPNG_LUMINANCE2; 885 | case 4: 886 | return UPNG_LUMINANCE4; 887 | case 8: 888 | return UPNG_LUMINANCE8; 889 | default: 890 | return UPNG_BADFORMAT; 891 | } 892 | case UPNG_RGB: 893 | switch (upng->color_depth) { 894 | case 8: 895 | return UPNG_RGB8; 896 | case 16: 897 | return UPNG_RGB16; 898 | default: 899 | return UPNG_BADFORMAT; 900 | } 901 | case UPNG_LUMA: 902 | switch (upng->color_depth) { 903 | case 1: 904 | return UPNG_LUMINANCE_ALPHA1; 905 | case 2: 906 | return UPNG_LUMINANCE_ALPHA2; 907 | case 4: 908 | return UPNG_LUMINANCE_ALPHA4; 909 | case 8: 910 | return UPNG_LUMINANCE_ALPHA8; 911 | default: 912 | return UPNG_BADFORMAT; 913 | } 914 | case UPNG_RGBA: 915 | switch (upng->color_depth) { 916 | case 8: 917 | return UPNG_RGBA8; 918 | case 16: 919 | return UPNG_RGBA16; 920 | default: 921 | return UPNG_BADFORMAT; 922 | } 923 | case UPNG_INDX: 924 | switch (upng->color_depth) { 925 | case 1: 926 | return UPNG_INDEX1; 927 | case 2: 928 | return UPNG_INDEX2; 929 | case 4: 930 | return UPNG_INDEX4; 931 | case 8: 932 | return UPNG_INDEX8; 933 | default: 934 | return UPNG_BADFORMAT; 935 | } 936 | default: 937 | return UPNG_BADFORMAT; 938 | } 939 | } 940 | 941 | static void upng_free_source(upng_t* upng) 942 | { 943 | if (upng->source.owning != 0) { 944 | free((void*)upng->source.buffer); 945 | } 946 | 947 | upng->source.buffer = NULL; 948 | upng->source.size = 0; 949 | upng->source.owning = 0; 950 | } 951 | 952 | /*read the information from the header and store it in the upng_Info. return value is error*/ 953 | upng_error upng_header(upng_t* upng) 954 | { 955 | /* if we have an error state, bail now */ 956 | if (upng->error != UPNG_EOK) { 957 | return upng->error; 958 | } 959 | 960 | /* if the state is not NEW (meaning we are ready to parse the header), stop now */ 961 | if (upng->state != UPNG_NEW) { 962 | return upng->error; 963 | } 964 | 965 | /* minimum length of a valid PNG file is 29 bytes 966 | * FIXME: verify this against the specification, or 967 | * better against the actual code below */ 968 | if (upng->source.size < 29) { 969 | SET_ERROR(upng, UPNG_ENOTPNG); 970 | return upng->error; 971 | } 972 | 973 | /* check that PNG header matches expected value */ 974 | if (upng->source.buffer[0] != 137 || upng->source.buffer[1] != 80 || upng->source.buffer[2] != 78 || upng->source.buffer[3] != 71 || upng->source.buffer[4] != 13 || upng->source.buffer[5] != 10 || upng->source.buffer[6] != 26 || upng->source.buffer[7] != 10) { 975 | SET_ERROR(upng, UPNG_ENOTPNG); 976 | return upng->error; 977 | } 978 | 979 | /* check that the first chunk is the IHDR chunk */ 980 | if (MAKE_DWORD_PTR(upng->source.buffer + 12) != CHUNK_IHDR) { 981 | SET_ERROR(upng, UPNG_EMALFORMED); 982 | return upng->error; 983 | } 984 | 985 | /* read the values given in the header */ 986 | upng->width = MAKE_DWORD_PTR(upng->source.buffer + 16); 987 | upng->height = MAKE_DWORD_PTR(upng->source.buffer + 20); 988 | upng->color_depth = upng->source.buffer[24]; 989 | upng->color_type = (upng_color)upng->source.buffer[25]; 990 | 991 | /* determine our color format */ 992 | upng->format = determine_format(upng); 993 | if (upng->format == UPNG_BADFORMAT) { 994 | SET_ERROR(upng, UPNG_EUNFORMAT); 995 | return upng->error; 996 | } 997 | 998 | /* check that the compression method (byte 27) is 0 (only allowed value in spec) */ 999 | if (upng->source.buffer[26] != 0) { 1000 | SET_ERROR(upng, UPNG_EMALFORMED); 1001 | return upng->error; 1002 | } 1003 | 1004 | /* check that the compression method (byte 27) is 0 (only allowed value in spec) */ 1005 | if (upng->source.buffer[27] != 0) { 1006 | SET_ERROR(upng, UPNG_EMALFORMED); 1007 | return upng->error; 1008 | } 1009 | 1010 | /* check that the compression method (byte 27) is 0 (spec allows 1, but uPNG does not support it) */ 1011 | if (upng->source.buffer[28] != 0) { 1012 | SET_ERROR(upng, UPNG_EUNINTERLACED); 1013 | return upng->error; 1014 | } 1015 | 1016 | upng->state = UPNG_HEADER; 1017 | return upng->error; 1018 | } 1019 | 1020 | /*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ 1021 | upng_error upng_decode(upng_t* upng) 1022 | { 1023 | const unsigned char* chunk; 1024 | unsigned char* compressed; 1025 | unsigned char* inflated; 1026 | unsigned char* palette = NULL; 1027 | unsigned long compressed_size = 0, compressed_index = 0; 1028 | unsigned long palette_size = 0; 1029 | unsigned long inflated_size; 1030 | upng_error error; 1031 | 1032 | /* if we have an error state, bail now */ 1033 | if (upng->error != UPNG_EOK) { 1034 | return upng->error; 1035 | } 1036 | 1037 | /* parse the main header, if necessary */ 1038 | upng_header(upng); 1039 | if (upng->error != UPNG_EOK) { 1040 | return upng->error; 1041 | } 1042 | 1043 | /* if the state is not HEADER (meaning we are ready to decode the image), stop now */ 1044 | if (upng->state != UPNG_HEADER) { 1045 | return upng->error; 1046 | } 1047 | 1048 | /* release old result, if any */ 1049 | if (upng->buffer != 0) { 1050 | free(upng->buffer); 1051 | upng->buffer = 0; 1052 | upng->size = 0; 1053 | } 1054 | 1055 | /* first byte of the first chunk after the header */ 1056 | chunk = upng->source.buffer + 33; 1057 | 1058 | /* scan through the chunks, finding the size of all IDAT chunks, and also 1059 | * verify general well-formed-ness */ 1060 | while (chunk < upng->source.buffer + upng->source.size) { 1061 | unsigned long length; 1062 | // const unsigned char *data; /*the data in the chunk */ 1063 | 1064 | /* make sure chunk header is not larger than the total compressed */ 1065 | if ((unsigned long)(chunk - upng->source.buffer + 12) > upng->source.size) { 1066 | SET_ERROR(upng, UPNG_EMALFORMED); 1067 | return upng->error; 1068 | } 1069 | 1070 | /* get length; sanity check it */ 1071 | length = upng_chunk_length(chunk); 1072 | if (length > INT_MAX) { 1073 | SET_ERROR(upng, UPNG_EMALFORMED); 1074 | return upng->error; 1075 | } 1076 | 1077 | /* make sure chunk header+paylaod is not larger than the total compressed */ 1078 | if ((unsigned long)(chunk - upng->source.buffer + length + 12) > upng->source.size) { 1079 | SET_ERROR(upng, UPNG_EMALFORMED); 1080 | return upng->error; 1081 | } 1082 | 1083 | /* get pointer to payload */ 1084 | // data = chunk + 8; 1085 | 1086 | /* parse chunks */ 1087 | if (upng_chunk_type(chunk) == CHUNK_IDAT) { 1088 | compressed_size += length; 1089 | } 1090 | else if (upng_chunk_type(chunk) == CHUNK_PLTE) { 1091 | palette_size = length; 1092 | } 1093 | else if (upng_chunk_type(chunk) == CHUNK_IEND) { 1094 | break; 1095 | } 1096 | else if (upng_chunk_critical(chunk)) { 1097 | SET_ERROR(upng, UPNG_EUNSUPPORTED); 1098 | return upng->error; 1099 | } 1100 | 1101 | chunk += length + 12; 1102 | } 1103 | 1104 | /* allocate enough space for the (compressed and filtered) image data */ 1105 | compressed = (unsigned char*)malloc(compressed_size); 1106 | if (compressed == NULL) { 1107 | SET_ERROR(upng, UPNG_ENOMEM); 1108 | return upng->error; 1109 | } 1110 | 1111 | if (palette_size) { 1112 | palette = (unsigned char*)malloc(palette_size); 1113 | if (palette == NULL) { 1114 | free(compressed); 1115 | SET_ERROR(upng, UPNG_ENOMEM); 1116 | return upng->error; 1117 | } 1118 | } 1119 | 1120 | /* scan through the chunks again, this time copying the values into 1121 | * our compressed buffer. there's no reason to validate anything a second time. */ 1122 | chunk = upng->source.buffer + 33; 1123 | while (chunk < upng->source.buffer + upng->source.size) { 1124 | unsigned long length; 1125 | const unsigned char* data; /*the data in the chunk */ 1126 | 1127 | length = upng_chunk_length(chunk); 1128 | 1129 | /* parse chunks */ 1130 | if (upng_chunk_type(chunk) == CHUNK_IDAT) { 1131 | data = chunk + 8; 1132 | memcpy(compressed + compressed_index, data, length); 1133 | compressed_index += length; 1134 | } 1135 | else if (upng_chunk_type(chunk) == CHUNK_PLTE) { 1136 | data = chunk + 8; 1137 | memcpy(palette, data, palette_size); 1138 | } 1139 | else if (upng_chunk_type(chunk) == CHUNK_IEND) { 1140 | break; 1141 | } 1142 | 1143 | chunk += length + 12; 1144 | } 1145 | 1146 | /* allocate space to store inflated (but still filtered) data */ 1147 | inflated_size = ((upng->width * (upng->height * upng_get_bpp(upng) + 7)) / 8) + upng->height; 1148 | inflated = (unsigned char*)malloc(inflated_size); 1149 | if (inflated == NULL) { 1150 | free(palette); 1151 | free(compressed); 1152 | SET_ERROR(upng, UPNG_ENOMEM); 1153 | return upng->error; 1154 | } 1155 | 1156 | /* decompress image data */ 1157 | error = uz_inflate(upng, inflated, inflated_size, compressed, compressed_size); 1158 | if (error != UPNG_EOK) { 1159 | free(palette); 1160 | free(compressed); 1161 | free(inflated); 1162 | return upng->error; 1163 | } 1164 | 1165 | /* free the compressed compressed data */ 1166 | free(compressed); 1167 | 1168 | /* allocate final image buffer */ 1169 | upng->size = (upng->height * upng->width * upng_get_bpp(upng) + 7) / 8; 1170 | upng->buffer = (unsigned char*)malloc(upng->size); 1171 | if (upng->buffer == NULL) { 1172 | free(palette); 1173 | free(inflated); 1174 | upng->size = 0; 1175 | SET_ERROR(upng, UPNG_ENOMEM); 1176 | return upng->error; 1177 | } 1178 | 1179 | /* unfilter scanlines */ 1180 | post_process_scanlines(upng, upng->buffer, inflated, upng); 1181 | free(inflated); 1182 | 1183 | if (upng->error != UPNG_EOK) { 1184 | free(palette); 1185 | free(upng->buffer); 1186 | upng->buffer = NULL; 1187 | upng->size = 0; 1188 | } 1189 | else { 1190 | /* palette */ 1191 | upng->palette = palette; 1192 | upng->state = UPNG_DECODED; 1193 | } 1194 | 1195 | /* we are done with our input buffer; free it if we own it */ 1196 | upng_free_source(upng); 1197 | 1198 | return upng->error; 1199 | } 1200 | 1201 | static upng_t* upng_new(void) 1202 | { 1203 | upng_t* upng; 1204 | 1205 | upng = (upng_t*)malloc(sizeof(upng_t)); 1206 | if (upng == NULL) { 1207 | return NULL; 1208 | } 1209 | 1210 | upng->buffer = NULL; 1211 | upng->size = 0; 1212 | 1213 | upng->width = upng->height = 0; 1214 | 1215 | upng->color_type = UPNG_RGBA; 1216 | upng->color_depth = 8; 1217 | upng->format = UPNG_RGBA8; 1218 | 1219 | upng->state = UPNG_NEW; 1220 | 1221 | upng->error = UPNG_EOK; 1222 | upng->error_line = 0; 1223 | 1224 | upng->source.buffer = NULL; 1225 | upng->source.size = 0; 1226 | upng->source.owning = 0; 1227 | 1228 | return upng; 1229 | } 1230 | 1231 | upng_t* upng_new_from_bytes(const unsigned char* buffer, unsigned long size) 1232 | { 1233 | upng_t* upng = upng_new(); 1234 | if (upng == NULL) { 1235 | return NULL; 1236 | } 1237 | 1238 | upng->source.buffer = buffer; 1239 | upng->source.size = size; 1240 | upng->source.owning = 0; 1241 | 1242 | return upng; 1243 | } 1244 | 1245 | upng_t* upng_new_from_file(const char* filename) 1246 | { 1247 | upng_t* upng; 1248 | unsigned char* buffer; 1249 | FILE* file; 1250 | long size; 1251 | 1252 | upng = upng_new(); 1253 | if (upng == NULL) { 1254 | return NULL; 1255 | } 1256 | 1257 | file = fopen(filename, "rb"); 1258 | if (file == NULL) { 1259 | SET_ERROR(upng, UPNG_ENOTFOUND); 1260 | return upng; 1261 | } 1262 | 1263 | /* get filesize */ 1264 | fseek(file, 0, SEEK_END); 1265 | size = ftell(file); 1266 | rewind(file); 1267 | 1268 | /* read contents of the file into the vector */ 1269 | buffer = (unsigned char*)malloc((unsigned long)size); 1270 | if (buffer == NULL) { 1271 | fclose(file); 1272 | SET_ERROR(upng, UPNG_ENOMEM); 1273 | return upng; 1274 | } 1275 | fread(buffer, 1, (unsigned long)size, file); 1276 | fclose(file); 1277 | 1278 | /* set the read buffer as our source buffer, with owning flag set */ 1279 | upng->source.buffer = buffer; 1280 | upng->source.size = size; 1281 | upng->source.owning = 1; 1282 | 1283 | return upng; 1284 | } 1285 | 1286 | void upng_free(upng_t* upng) 1287 | { 1288 | /* deallocate palette */ 1289 | if (upng->palette != NULL) { 1290 | free(upng->palette); 1291 | } 1292 | 1293 | /* deallocate image buffer */ 1294 | if (upng->buffer != NULL) { 1295 | free(upng->buffer); 1296 | } 1297 | 1298 | /* deallocate source buffer, if necessary */ 1299 | upng_free_source(upng); 1300 | 1301 | /* deallocate struct itself */ 1302 | free(upng); 1303 | } 1304 | 1305 | upng_error upng_get_error(const upng_t* upng) 1306 | { 1307 | return upng->error; 1308 | } 1309 | 1310 | unsigned upng_get_error_line(const upng_t* upng) 1311 | { 1312 | return upng->error_line; 1313 | } 1314 | 1315 | unsigned upng_get_width(const upng_t* upng) 1316 | { 1317 | return upng->width; 1318 | } 1319 | 1320 | unsigned upng_get_height(const upng_t* upng) 1321 | { 1322 | return upng->height; 1323 | } 1324 | 1325 | unsigned upng_get_bpp(const upng_t* upng) 1326 | { 1327 | return upng_get_bitdepth(upng) * upng_get_components(upng); 1328 | } 1329 | 1330 | unsigned upng_get_components(const upng_t* upng) 1331 | { 1332 | switch (upng->color_type) { 1333 | case UPNG_LUM: 1334 | return 1; 1335 | case UPNG_RGB: 1336 | return 3; 1337 | case UPNG_LUMA: 1338 | return 2; 1339 | case UPNG_RGBA: 1340 | return 4; 1341 | case UPNG_INDX: 1342 | return 1; 1343 | default: 1344 | return 0; 1345 | } 1346 | } 1347 | 1348 | unsigned upng_get_bitdepth(const upng_t* upng) 1349 | { 1350 | return upng->color_depth; 1351 | } 1352 | 1353 | unsigned upng_get_pixelsize(const upng_t* upng) 1354 | { 1355 | unsigned bits = upng_get_bitdepth(upng) * upng_get_components(upng); 1356 | bits += bits % 8; 1357 | return bits; 1358 | } 1359 | 1360 | upng_format upng_get_format(const upng_t* upng) 1361 | { 1362 | return upng->format; 1363 | } 1364 | 1365 | const unsigned char* upng_get_buffer(const upng_t* upng) 1366 | { 1367 | return upng->buffer; 1368 | } 1369 | 1370 | unsigned upng_get_size(const upng_t* upng) 1371 | { 1372 | return upng->size; 1373 | } 1374 | 1375 | const unsigned char* upng_get_palette(const upng_t* upng) 1376 | { 1377 | return upng->palette; 1378 | } --------------------------------------------------------------------------------