diff --git a/README.md b/README.md index c3e0aedc..838d6fb4 100644 --- a/README.md +++ b/README.md @@ -12,4 +12,3 @@ This is a port of OpenLara (https://github.com/XProger/OpenLara) for the Sega Dr you can burn with [Image Burn](https://www.imgburn.com/index.php?act=download) see release page - require PS1 version of tomb raider disc. - \ No newline at end of file diff --git a/src/cache.h b/src/cache.h index 6605252d..e1968748 100644 --- a/src/cache.h +++ b/src/cache.h @@ -12,6 +12,10 @@ #define USE_SCREEN_TEX #endif +#if defined(_GAPI_D3D8) || defined(_GAPI_D3D9) || defined(_GAPI_D3D11) + #define EARLY_CLEAR +#endif + struct ShaderCache { enum Effect { FX_NONE = 0, FX_UNDERWATER = 1, FX_ALPHA_TEST = 2 }; @@ -86,8 +90,8 @@ struct ShaderCache { void prepareSky(int fx) { compile(Core::passSky, Shader::DEFAULT, fx, rsBase); if (Core::support.tex3D) { - compile(Core::passSky, Shader::SKY_CLOUDS, fx, rsBase); - compile(Core::passSky, Shader::SKY_CLOUDS_AZURE, fx, rsBase); + compile(Core::passSky, Shader::SKY_CLOUDS, fx, rsBase); + compile(Core::passSky, Shader::SKY_AZURE, fx, rsBase); } } @@ -123,7 +127,7 @@ struct ShaderCache { if (rs & RS_DISCARD) fx |= FX_ALPHA_TEST; - #ifndef FFP + #if !defined(FFP) && !defined(_GAPI_TA) if (shaders[pass][type][fx]) return shaders[pass][type][fx]; @@ -180,7 +184,7 @@ struct ShaderCache { Shader *getShader(Core::Pass pass, Shader::Type type, int fx) { Shader *shader = shaders[pass][type][fx]; - #ifndef FFP + #if !defined(FFP) && !defined(_GAPI_TA) if (shader == NULL) LOG("! NULL shader: %d %d %d\n", int(pass), int(type), int(fx)); ASSERT(shader != NULL); @@ -977,6 +981,10 @@ struct WaterCache { mat4 mProj = GAPI::ortho(0.0f, float(tex->origWidth), 0.0f, float(tex->origHeight), 0.0f, 1.0f); + #ifdef _OS_WP8 + mProj.unrot90(); + #endif + Core::active.shader->setParam(uViewProj, mProj); Core::active.shader->setParam(uMaterial, vec4(1.0f)); diff --git a/src/camera.h b/src/camera.h index db7700d1..524124a7 100644 --- a/src/camera.h +++ b/src/camera.h @@ -44,7 +44,7 @@ struct Camera : ICamera { Frustum *frustum; float fov, aspect, znear, zfar; - vec3 lookAngle, targetAngle; + vec3 lookAngle, targetAngle, viewAngle; mat4 mViewInv; float timer; @@ -69,6 +69,7 @@ struct Camera : ICamera { spectator = false; specTimer = 0.0f; + targetAngle = vec3(0.0f); } void reset() { @@ -366,6 +367,8 @@ struct Camera : ICamera { speed = CAM_SPEED_COMBAT; } + viewAngle = vec3(0.0f); + if (mode == MODE_CUTSCENE) { ASSERT(level->cameraFramesCount && level->cameraFrames); @@ -386,31 +389,33 @@ struct Camera : ICamera { } } - if (!firstPerson) { - TR::CameraFrame *frameA = &level->cameraFrames[indexA]; - TR::CameraFrame *frameB = &level->cameraFrames[indexB]; + if (!spectator) { + if (!firstPerson) { + TR::CameraFrame *frameA = &level->cameraFrames[indexA]; + TR::CameraFrame *frameB = &level->cameraFrames[indexB]; - const float maxDelta = 512 * 512; + const float maxDelta = 512 * 512; - float dp = (vec3(frameA->pos) - vec3(frameB->pos)).length2(); - float dt = (vec3(frameA->target) - vec3(frameB->target)).length2(); + float dp = (vec3(frameA->pos) - vec3(frameB->pos)).length2(); + float dt = (vec3(frameA->target) - vec3(frameB->target)).length2(); - if (dp > maxDelta || dt > maxDelta) { - eye.pos = frameA->pos; - target.pos = frameA->target; - fov = frameA->fov / 32767.0f * 120.0f; - } else { - eye.pos = vec3(frameA->pos).lerp(frameB->pos, t); - target.pos = vec3(frameA->target).lerp(frameB->target, t); - fov = lerp(frameA->fov / 32767.0f * 120.0f, frameB->fov / 32767.0f * 120.0f, t); - } + if (dp > maxDelta || dt > maxDelta) { + eye.pos = frameA->pos; + target.pos = frameA->target; + fov = frameA->fov / 32767.0f * 120.0f; + } else { + eye.pos = vec3(frameA->pos).lerp(frameB->pos, t); + target.pos = vec3(frameA->target).lerp(frameB->target, t); + fov = lerp(frameA->fov / 32767.0f * 120.0f, frameB->fov / 32767.0f * 120.0f, t); + } - eye.pos = level->cutMatrix * eye.pos; - target.pos = level->cutMatrix * target.pos; + eye.pos = level->cutMatrix * eye.pos; + target.pos = level->cutMatrix * target.pos; - mViewInv = mat4(eye.pos, target.pos, vec3(0, -1, 0)); - } else - updateFirstPerson(); + mViewInv = mat4(eye.pos, target.pos, vec3(0, -1, 0)); + } else + updateFirstPerson(); + } } else { if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) { lookAngle = vec3(0.0f); @@ -418,7 +423,7 @@ struct Camera : ICamera { if (mode == MODE_LOOK) { float d = 3.0f * Core::deltaTime; - vec2 L = Input::joy[cameraIndex].L; + vec2 L = Input::joy[Core::settings.controls[cameraIndex].joyIndex].L; L = L.normal() * max(0.0f, L.length() - INPUT_JOY_DZ_STICK) / (1.0f - INPUT_JOY_DZ_STICK); lookAngle.x += L.y * d; @@ -431,7 +436,7 @@ struct Camera : ICamera { lookAngle.x = clamp(lookAngle.x, CAM_LOOK_ANGLE_XMIN, CAM_LOOK_ANGLE_XMAX); lookAngle.y = clamp(lookAngle.y, -CAM_LOOK_ANGLE_Y, CAM_LOOK_ANGLE_Y); - } else + } else { if (lookAngle.x != CAM_FOLLOW_ANGLE || lookAngle.y != 0.0f) { float t = 10.0f * Core::deltaTime; lookAngle.x = lerp(clampAngle(lookAngle.x), CAM_FOLLOW_ANGLE, t); @@ -439,9 +444,20 @@ struct Camera : ICamera { if (fabsf(lookAngle.x - CAM_FOLLOW_ANGLE) < EPS) lookAngle.x = CAM_FOLLOW_ANGLE; if (lookAngle.y < EPS) lookAngle.y = 0.0f; } + + if (!spectator) { + vec2 R = Input::joy[Core::settings.controls[cameraIndex].joyIndex].R; + R.x = sign(R.x) * max(0.0f, (fabsf(R.x) - INPUT_JOY_DZ_STICK) / (1.0f - INPUT_JOY_DZ_STICK)); + R.y = sign(R.y) * max(0.0f, (fabsf(R.y) - INPUT_JOY_DZ_STICK) / (1.0f - INPUT_JOY_DZ_STICK)); + + viewAngle.x = -R.y * PI * 0.375f; + viewAngle.y = R.x * PI * 0.5f; + viewAngle.z = 0.0f; + } + } } - targetAngle = owner->angle + lookAngle; + targetAngle = owner->angle + lookAngle + viewAngle; targetAngle.x = clampAngle(targetAngle.x); targetAngle.y = clampAngle(targetAngle.y); @@ -503,7 +519,7 @@ struct Camera : ICamera { if (mode == MODE_LOOK) offset = CAM_OFFSET_LOOK; else - offset = (mode == MODE_COMBAT ? CAM_OFFSET_COMBAT : CAM_OFFSET_FOLLOW) * cosf(targetAngle.x); + offset = (mode == MODE_COMBAT ? CAM_OFFSET_COMBAT : CAM_OFFSET_FOLLOW); vec3 dir = vec3(targetAngle.x, targetAngle.y) * offset; to.pos = target.pos - dir; @@ -545,6 +561,7 @@ struct Camera : ICamera { if (specJoy.down[jkL] && specJoy.down[jkR]) { specTimer += Core::deltaTime; if (specTimer > SPECTATOR_TIMER) { + firstPerson = false; spectator = !spectator; specTimer = 0.0f; specPos = eye.pos; @@ -665,6 +682,10 @@ struct Camera : ICamera { } void setOblique(const vec4 &clipPlane) { // http://www.terathon.com/code/oblique.html + #ifdef _OS_WP8 + Core::mProj.unrot90(); + #endif + vec4 p = Core::mViewInv.transpose() * clipPlane; vec4 q; @@ -681,6 +702,10 @@ struct Camera : ICamera { Core::mProj.e21 = c.y; Core::mProj.e22 = c.z + (f - 1.0f); Core::mProj.e23 = c.w; + + #ifdef _OS_WP8 + Core::mProj.rot90(); + #endif } void changeView(bool firstPerson) { diff --git a/src/character.h b/src/character.h index 4e0ec19b..f3e954ff 100644 --- a/src/character.h +++ b/src/character.h @@ -308,6 +308,8 @@ struct Character : Controller { } void bakeEnvironment(Texture *&environment) { + Core::beginFrame(); + flags.invisible = true; if (!environment) { uint32 opt = OPT_CUBEMAP | OPT_TARGET; @@ -321,6 +323,8 @@ struct Character : Controller { environment->generateMipMap(); #endif flags.invisible = false; + + Core::endFrame(); } }; diff --git a/src/controller.h b/src/controller.h index bf1f56a0..fb1c5a3f 100644 --- a/src/controller.h +++ b/src/controller.h @@ -1054,17 +1054,17 @@ struct Controller { bool trace(const TR::Location &from, TR::Location &to) { int rx, rz; - if (fabsf(to.pos.x - from.pos.x) < fabsf(to.pos.z - from.pos.z)) { + if (fabsf(to.pos.x - from.pos.x) > fabsf(to.pos.z - from.pos.z)) { rz = traceZ(from, to); - if (!rz) return false; rx = traceX(from, to); + if (!rx) return false; } else { rx = traceX(from, to); - if (!rx) return false; rz = traceZ(from, to); + if (!rz) return false; } TR::Room::Sector *sector = level->getSector(to.room, to.pos); - return clipHeight(from, to, sector) && rx == 1 && rz == 1; + return !(!clipHeight(from, to, sector) || rx != 1 || rz != 1); } bool clipHeight(const TR::Location &from, TR::Location &to, TR::Room::Sector *sector) { diff --git a/src/core.h b/src/core.h index 94eaf5b3..09b98003 100644 --- a/src/core.h +++ b/src/core.h @@ -13,7 +13,27 @@ #define USE_CUBEMAP_MIPS -#ifdef WIN32 +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + #define _OS_WP8 1 + #define _GAPI_D3D11 1 + + #undef OS_PTHREAD_MT + + #define INV_SINGLE_PLAYER + #define INV_VIBRATION + #define INV_GAMEPAD_ONLY +#elif __UWP__ + #define _OS_UWP 1 + #define _GAPI_D3D11 1 + + #ifdef __XB1__ + #define _OS_XB1 + #define INV_VIBRATION + #define INV_GAMEPAD_ONLY + #endif + + #undef OS_PTHREAD_MT +#elif WIN32 #define _OS_WIN 1 #define _GAPI_GL 1 //#define _GAPI_D3D9 1 @@ -30,6 +50,9 @@ #ifdef _GAPI_GL #define VR_SUPPORT #endif + + #define INV_VIBRATION + #define INV_QUALITY #elif ANDROID #define _OS_ANDROID 1 #define _GAPI_GL 1 @@ -37,31 +60,43 @@ //#define _GAPI_VULKAN #define VR_SUPPORT + #define INV_QUALITY + #define INV_STEREO #elif __SDL2__ #define _GAPI_GL 1 #ifdef SDL2_GLES #define _GAPI_GLES 1 #define DYNGEOM_NO_VBO #endif + #define INV_QUALITY + #define INV_STEREO #elif __RPI__ #define _OS_RPI 1 #define _GAPI_GL 1 #define _GAPI_GLES 1 #define DYNGEOM_NO_VBO + #define INV_VIBRATION + #define INV_QUALITY + #define INV_STEREO #elif __CLOVER__ #define _OS_CLOVER 1 #define _GAPI_GL 1 #define _GAPI_GLES 1 #define DYNGEOM_NO_VBO + #define INV_GAMEPAD_NO_TRIGGER + #define INV_GAMEPAD_ONLY + #define INV_STEREO #elif __PSC__ #define _OS_PSC 1 #define _GAPI_GL 1 #define _GAPI_GLES 1 #define DYNGEOM_NO_VBO -#elif __BITTBOY__ + #define INV_GAMEPAD_ONLY + #define INV_STEREO +#elif __BITTBOY__ || __MIYOO__ #define _OS_BITTBOY 1 #define _OS_LINUX 1 #define _GAPI_SW 1 @@ -74,9 +109,17 @@ // etnaviv driver has a bug with cubemap mips generator #undef USE_CUBEMAP_MIPS + + #define INV_SINGLE_PLAYER + #define INV_VIBRATION + #define INV_GAMEPAD_ONLY #elif __linux__ #define _OS_LINUX 1 #define _GAPI_GL 1 + + #define INV_VIBRATION + #define INV_QUALITY + #define INV_STEREO #elif __APPLE__ #define _GAPI_GL 1 #include "TargetConditionals.h" @@ -86,7 +129,9 @@ #define _GAPI_GLES 1 #else #define _OS_MAC 1 + #define INV_STEREO #endif + #define INV_QUALITY #elif __EMSCRIPTEN__ #define _OS_WEB 1 #define _GAPI_GL 1 @@ -95,6 +140,9 @@ #undef OS_FILEIO_CACHE extern int WEBGL_VERSION; + + #define INV_QUALITY + #define INV_STEREO #elif _3DS #include <3ds.h> @@ -109,6 +157,11 @@ // stb_vorbis - 8 ms // libvorbis - 6 ms #define USE_LIBVORBIS + + #define INV_SINGLE_PLAYER + #define INV_GAMEPAD_NO_TRIGGER + #define INV_GAMEPAD_ONLY + //#define INV_STEREO // hardware switch #elif _PSP #define _OS_PSP 1 #define _GAPI_GU 1 @@ -118,35 +171,64 @@ #define EDRAM_TEX #undef OS_PTHREAD_MT + + #define INV_SINGLE_PLAYER + #define INV_GAMEPAD_NO_TRIGGER + #define INV_GAMEPAD_ONLY #elif __vita__ #define _OS_PSV 1 #define _GAPI_GXM 1 #undef OS_PTHREAD_MT + + //#define USE_LIBVORBIS // TODO crash + + #define INV_SINGLE_PLAYER + #define INV_GAMEPAD_NO_TRIGGER + #define INV_GAMEPAD_ONLY #elif __SWITCH__ #define _OS_SWITCH 1 #define _GAPI_GL 1 #undef OS_PTHREAD_MT + #define INV_QUALITY + #define INV_STEREO #elif _XBOX #define _OS_XBOX 1 #define _GAPI_D3D8 1 #undef OS_PTHREAD_MT + #undef USE_CUBEMAP_MIPS #define NOMINMAX #include #include + + #define INV_GAMEPAD_NO_TRIGGER + #define INV_GAMEPAD_ONLY + #define INV_VIBRATION +#elif _X360 + #define _OS_X360 1 + // TODO +#elif __NDLESS__ + #define _OS_TNS 1 + #define _GAPI_SW 1 + #include + + #undef OS_PTHREAD_MT #elif __DC__ #define _OS_DC 1 #define _GAPI_TA 1 + #define INV_SINGLE_PLAYER + #define INV_GAMEPAD_ONLY + #undef OS_FILEIO_CACHE #undef OS_PTHREAD_MT #undef USE_CUBEMAP_MIPS #endif -#if !defined(_OS_PSP) && !defined(_OS_DC) +#if !defined(_OS_PSP) && !defined(_OS_TNS) #define USE_INFLATE #endif @@ -154,7 +236,7 @@ #include "libs/tinf/tinf.h" #endif -#if defined(_GAPI_SW) || defined(_GAPI_GU) || defined(_GAPI_TA) +#if defined(_GAPI_SW) || defined(_GAPI_GU) #define FFP #endif @@ -163,6 +245,8 @@ #if defined(_GAPI_GU) #define SPLIT_BY_CLUT #endif +#elif _GAPI_TA + #define SPLIT_BY_TILE #else // current etnaviv driver implementation uses uncompatible Mesa GLSL compiler // it produce unimplemented TRUNC/ARL instructions instead of F2I @@ -182,6 +266,8 @@ #define SHADOW_TEX_SIZE 256 #elif defined(_OS_PSV) #define SHADOW_TEX_SIZE 1024 +#elif defined(_OS_DC) + #define SHADOW_TEX_SIZE 512 #else #define SHADOW_TEX_SIZE 2048 #endif @@ -247,10 +333,10 @@ namespace Core { struct Mutex { void *obj; - Mutex() { obj = osMutexInit(); } - ~Mutex() { osMutexFree(obj); } - void lock() { osMutexLock(obj); } - void unlock() { osMutexUnlock(obj); } + Mutex() { obj = osMutexInit(); } + ~Mutex() { if (obj) osMutexFree(obj); } + void lock() { if (obj) osMutexLock(obj); } + void unlock() { if (obj) osMutexUnlock(obj); } }; struct Lock { @@ -319,7 +405,7 @@ namespace Core { } void setLighting(Quality value) { - #if defined(_GAPI_SW) || defined(_GAPI_GU) || defined(_GAPI_TA) + #if defined(_GAPI_SW) || defined(_GAPI_GU) lighting = LOW; #else lighting = value; @@ -327,7 +413,7 @@ namespace Core { } void setShadows(Quality value) { - #if defined(_GAPI_SW) || defined(_GAPI_GU) || defined(_GAPI_TA) + #if defined(_GAPI_SW) || defined(_GAPI_GU) shadows = LOW; #else shadows = value; @@ -335,7 +421,7 @@ namespace Core { } void setWater(Quality value) { - #if defined(_GAPI_SW) || defined(_GAPI_GU) || defined(_GAPI_TA) + #if defined(_GAPI_SW) || defined(_GAPI_GU) water = LOW; #else if (value > LOW && !(support.texFloat || support.texHalf)) @@ -545,7 +631,6 @@ struct MeshRange { E( sNormal ) \ E( sReflect ) \ E( sShadow ) \ - E( sEnvironment ) \ E( sMask ) #define SHADER_UNIFORMS(E) \ @@ -701,7 +786,7 @@ namespace Core { void stop() { if (fpsTime < Core::getTime()) { - LOG("FPS: %d DIP: %d TRI: %d RT: %d CB: %d\n", fps, dips, tris, rt, cb); + LOG("FPS: %d DIP: %d TRI: %d RT: %d\n", fps, dips, tris, rt); #ifdef PROFILE LOG("frame time: %d mcs\n", tFrame / 1000); LOG("sound: mix %d rev %d ren %d/%d ogg %d\n", Sound::stats.mixer, Sound::stats.reverb, Sound::stats.render[0], Sound::stats.render[1], Sound::stats.ogg); @@ -997,6 +1082,13 @@ namespace Core { settings.audio.reverb = false; #endif + #ifdef _OS_PSV + settings.detail.setFilter (Core::Settings::HIGH); + settings.detail.setLighting (Core::Settings::LOW); + settings.detail.setShadows (Core::Settings::MEDIUM); + settings.detail.setWater (Core::Settings::MEDIUM); + #endif + #ifdef _OS_XBOX settings.detail.setFilter (Core::Settings::HIGH); settings.detail.setLighting (Core::Settings::LOW); @@ -1004,9 +1096,20 @@ namespace Core { settings.detail.setWater (Core::Settings::LOW); #endif + #ifdef _OS_WP8 + settings.detail.setFilter(Core::Settings::HIGH); + settings.detail.setLighting(Core::Settings::LOW); + settings.detail.setShadows(Core::Settings::LOW); + settings.detail.setWater(Core::Settings::LOW); + #endif + #ifdef _OS_DC + settings.detail.setFilter (Core::Settings::MEDIUM); + settings.detail.setLighting (Core::Settings::LOW); + settings.detail.setShadows (Core::Settings::LOW); + settings.detail.setWater (Core::Settings::LOW); + settings.audio.reverb = false; - settings.audio.music = false; #endif memset(&active, 0, sizeof(active)); @@ -1028,6 +1131,7 @@ namespace Core { GAPI::deinit(); NAPI::deinit(); Sound::deinit(); + Stream::deinit(); } void setVSync(bool enable) { @@ -1057,7 +1161,7 @@ namespace Core { GAPI::discardTarget(!(active.targetOp & RT_STORE_COLOR), !(active.targetOp & RT_STORE_DEPTH)); GAPI::Texture *target = reqTarget.texture; - uint32 face = reqTarget.face; + uint32 face = reqTarget.face; if (target != active.target || face != active.targetFace) { Core::stats.rt++; @@ -1222,7 +1326,7 @@ namespace Core { } void setFog(const vec4 ¶ms) { - #ifdef _XBOX + #if defined(_GAPI_D3D8) || defined(_GAPI_C3D) || defined(_GAPI_SW) || defined(FFP) GAPI::setFog(params); #else ASSERT(Core::active.shader); diff --git a/src/debug.h b/src/debug.h index e7583cf4..a82b1a49 100644 --- a/src/debug.h +++ b/src/debug.h @@ -548,6 +548,9 @@ namespace Debug { for (int i = 0; i < level.roomsCount; i++) for (int j = 0; j < level.rooms[i].lightsCount; j++) { TR::Room::Light &l = level.rooms[i].lights[j]; + + if (!level.rooms[i].flags.visible) continue; + vec3 p = vec3(float(l.x), float(l.y), float(l.z)); vec4 color = vec4(l.color.r, l.color.g, l.color.b, 255) * (1.0f / 255.0f); @@ -643,13 +646,36 @@ namespace Debug { } } - void dumpSample(TR::Level *level, int index) { + void dumpPalette(TR::Level *level, int index) + { char buf[255]; - sprintf(buf, "samples_PSX/%03d.wav", index); + sprintf(buf, "tiles_PC/%02d.pal", index); + FILE *f = fopen(buf, "wb"); + for (int i = 0; i < 256; i++) { + Color24 c = level->palette[i]; + uint16 res = (c.r >> 3) | ((c.g >> 3) << 5) | ((c.b >> 3) << 10); + fwrite(&res, 2, 1, f); + } + fclose(f); + } + + void dumpSample(TR::Level *level, int index, int id, int sample) { + char buf[255]; + sprintf(buf, "samples_PSX/%03d_%d.wav", id, sample); if (level->version == TR::VER_TR1_PSX) { uint32 dataSize = level->soundSize[index] / 16 * 28 * 2 * 4; + uint32 size = -1; + + FILE *f = fopen(buf, "rb"); + if (f) { + fseek(f, 0, SEEK_END); + size = ftell(f); + fclose(f); + } + + f = fopen(buf, "wb"); struct Header { uint32 RIFF; @@ -676,19 +702,30 @@ namespace Debug { fwrite(&header, sizeof(header), 1, f); - Sound::VAG vag(new Stream(NULL, &level->soundData[level->soundOffsets[index]], dataSize)); + Sound::VAG vag(level->getSampleStream(index)); Sound::Frame frames[4 * 28]; while (int count = vag.decode(frames, 4 * 28)) + { for (int i = 0; i < count; i++) - fwrite(&frames[i].L, 2, 1, f); + { + fwrite(&frames[i].L, 2, 1, f); + } + } + + dataSize = ftell(f); + if (size != -1 && size != dataSize) { + LOG("audio diff %03d_%d : %d -> %d\n", id, sample, size, dataSize); + } + + fclose(f); } if (level->version == TR::VER_TR1_PC) { uint32 *data = (uint32*)&level->soundData[level->soundOffsets[index]]; + FILE *f = fopen(buf, "wb"); fwrite(data, data[1] + 8, 1, f); + fclose(f); } - - fclose(f); } void info(IGame *game, Controller *controller, Animation &anim) { diff --git a/src/enemy.h b/src/enemy.h index edc9c022..d83afe67 100644 --- a/src/enemy.h +++ b/src/enemy.h @@ -1611,6 +1611,9 @@ struct Bat : Enemy { virtual void updatePosition() { turn(state == STATE_FLY || state == STATE_ATTACK, BAT_TURN_SPEED); + if (!target) + target = (Character*)game->getLara(pos); + if (flying) { float wy = waypoint.y - (target->stand != STAND_ONWATER ? 765.0f : 64.0f); lift(wy - pos.y, BAT_LIFT_SPEED); @@ -1910,7 +1913,7 @@ struct Mutant : Enemy { } virtual void setSaveData(const SaveEntity &data) { - Character::setSaveData(data); + Enemy::setSaveData(data); if (flags.invisible) deactivate(true); } @@ -2139,7 +2142,7 @@ struct GiantMutant : Enemy { } virtual void setSaveData(const SaveEntity &data) { - Character::setSaveData(data); + Enemy::setSaveData(data); if (flags.invisible) deactivate(true); } @@ -2226,7 +2229,7 @@ struct GiantMutant : Enemy { } break; case STATE_ATTACK_3 : - if (target->stand != STAND_HANG) { + if ((mask & HIT_MASK_HAND) && (target->stand != STAND_HANG)) { target->hit(GIANT_MUTANT_DAMAGE_FATAL, this, TR::HIT_GIANT_MUTANT); return STATE_FATAL; } @@ -2306,7 +2309,7 @@ struct Centaur : Enemy { } virtual void setSaveData(const SaveEntity &data) { - Character::setSaveData(data); + Enemy::setSaveData(data); if (flags.invisible) deactivate(true); } @@ -3503,12 +3506,14 @@ struct Winston : Enemy { virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { if (environment && (flags.unused & 4)) { game->setRoomParams(getRoomIndex(), Shader::MIRROR, 1.5f, 2.0f, 2.5f, 1.0f, false); - environment->bind(sEnvironment); + GAPI::Texture *dtex = Core::active.textures[sDiffuse]; + environment->bind(sDiffuse); Controller::render(frustum, mesh, type, caustics); + if (dtex) dtex->bind(sDiffuse); } else { Enemy::render(frustum, mesh, type, caustics); } } }; -#endif \ No newline at end of file +#endif diff --git a/src/extension.h b/src/extension.h index d44dea38..b7878429 100644 --- a/src/extension.h +++ b/src/extension.h @@ -12,35 +12,226 @@ namespace Extension { + const float MESH_SCALE = 1.0f / 256.0f; + #ifdef GEOMETRY_EXPORT - void exportTexture(const char *name, Texture *tex) { + void exportTexture(const char *dir, const char *name, Texture *tex) { char *data32 = new char[tex->width * tex->height * 4]; tex->bind(sDiffuse); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data32); - Texture::SaveBMP(name, data32, tex->width, tex->height); + char path[256]; + sprintf(path, "%s/%s", dir, name); + Texture::SaveBMP(path, data32, tex->width, tex->height); delete[] data32; } - void exportModel(IGame *game, TR::Model &model) { + void exportRooms(IGame *game, const char *dir) { TR::Level *level = game->getLevel(); MeshBuilder *mesh = game->getMesh(); - struct ModelVertex { + typedef uint32 MeshIndex; + + struct MeshVertex { vec3 coord; vec3 normal; vec2 texCoord; ubyte4 color; + }; + + char name[256]; + sprintf(name, "%s/rooms.glb", dir); + + LOG("export rooms: %s\n", name); + + FILE *file = fopen(name, "wb"); + if (!file) { + LOG("dump: can't dump rooms to file \"%s\"!\n", name); + return; + } + + Index *indices = new Index[1024 * 1024]; + Vertex *vertices = new Vertex[1024 * 1024]; + + mat4 flip = mat4( + -1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ); + mat4 flipInv = flip.inverseOrtho(); + + struct RoomParams { + int iStart; + int vStart; + int iCount; + int vCount; + } roomParams[1024]; + + int iCount = 0, vCount = 0; + + // get geometry data + for (int roomIndex = 0; roomIndex < level->roomsCount; roomIndex++) { + RoomParams ¶ms = roomParams[roomIndex]; + params.iStart = iCount; + params.vStart = vCount; + + MeshBuilder::Geometry geom; + + for (int transp = 0; transp < 3; transp++) { + int blendMask = mesh->getBlendMask(transp); + + TR::Room &room = level->rooms[roomIndex]; + + // room geometry + mesh->buildRoom(geom, NULL, blendMask, room, level, indices, vertices, iCount, vCount, params.vStart); + + // static meshes + for (int j = 0; j < room.meshesCount; j++) { + TR::Room::Mesh &m = room.meshes[j]; + TR::StaticMesh *s = &level->staticMeshes[m.meshIndex]; + if (!level->meshOffsets[s->mesh]) continue; + TR::Mesh &staticMesh = level->meshes[level->meshOffsets[s->mesh]]; + + int x = m.x - room.info.x; + int y = m.y; + int z = m.z - room.info.z; + int d = m.rotation.value / 0x4000; + mesh->buildMesh(geom, blendMask, staticMesh, level, indices, vertices, iCount, vCount, params.vStart, 0, x, y, z, d, m.color, true, false); + } + } + + params.iCount = iCount - params.iStart; + params.vCount = vCount - params.vStart; + } + + for (int i = 0; i < iCount; i += 3) { // CCW -> CW + swap(indices[i], indices[i + 2]); + } + + // get model geometry + int verticesOffset = 0; + int indicesOffset = verticesOffset + vCount * sizeof(MeshVertex); + int bufferSize = indicesOffset + iCount * sizeof(MeshIndex); + + char *bufferData = new char[bufferSize]; + + MeshVertex *gVertices = (MeshVertex*)(bufferData + verticesOffset); + for (int i = 0; i < vCount; i++) { + Vertex &src = vertices[i]; + MeshVertex &dst = gVertices[i]; + + dst.coord = src.coord; + + dst.normal = src.normal; + dst.texCoord = src.texCoord; + dst.normal = dst.normal.normal(); + + dst.coord = flip * (dst.coord * MESH_SCALE); + dst.normal = flip * dst.normal; + + dst.texCoord *= (1.0f / 32767.0f); + dst.color = src.light; + } + + for (int i = 0; i < iCount; i++) { + MeshIndex &dst = ((MeshIndex*)(bufferData + indicesOffset))[i]; + dst = indices[i]; + } + + GLTF *gltf = new GLTF(); + + JSON *nodes; + gltf->addScene("Scene", &nodes); + + int accessorIndex = 0; + int nodeIndex = 0; + + for (int roomIndex = 0; roomIndex < level->roomsCount; roomIndex++) { + TR::Room &room = level->rooms[roomIndex]; + + RoomParams ¶ms = roomParams[roomIndex]; + if (params.iCount == 0) { + continue; + } + + sprintf(name, "%d_mesh", roomIndex); + gltf->addMesh(name, 0, accessorIndex + 0, accessorIndex + 1, accessorIndex + 2, accessorIndex + 3, accessorIndex + 4, -1, -1); + + sprintf(name, "%d", roomIndex); + gltf->addNode(name, nodeIndex, -1, vec3(float(-room.info.x), 0, float(room.info.z)) * MESH_SCALE, quat(0, 0, 0, 1)); + nodes->add(NULL, nodeIndex); // mesh + nodeIndex++; + + vec4 vMin(+INF), vMax(-INF); + + for (int i = params.vStart; i < params.vStart + params.vCount; i++) { + MeshVertex &v = gVertices[i]; + + vMin.x = min(vMin.x, v.coord.x); + vMin.y = min(vMin.y, v.coord.y); + vMin.z = min(vMin.z, v.coord.z); + + vMax.x = max(vMax.x, v.coord.x); + vMax.y = max(vMax.y, v.coord.y); + vMax.z = max(vMax.z, v.coord.z); + } + + gltf->addAccessor(0, sizeof(MeshIndex), params.iStart * sizeof(MeshIndex), params.iCount, GLTF::SCALAR, GL_UNSIGNED_INT); // 0 + gltf->addAccessor(1, sizeof(MeshVertex), params.vStart * sizeof(MeshVertex) + (int)OFFSETOF(MeshVertex, coord), params.vCount, GLTF::VEC3, GL_FLOAT, false, vMin, vMax); // 1 + gltf->addAccessor(1, sizeof(MeshVertex), params.vStart * sizeof(MeshVertex) + (int)OFFSETOF(MeshVertex, normal), params.vCount, GLTF::VEC3, GL_FLOAT); // 2 + gltf->addAccessor(1, sizeof(MeshVertex), params.vStart * sizeof(MeshVertex) + (int)OFFSETOF(MeshVertex, texCoord), params.vCount, GLTF::VEC2, GL_FLOAT); // 3 + gltf->addAccessor(1, sizeof(MeshVertex), params.vStart * sizeof(MeshVertex) + (int)OFFSETOF(MeshVertex, color), params.vCount, GLTF::VEC4, GL_UNSIGNED_BYTE, true); // 4 + + accessorIndex += 5; + } + + gltf->addSampler(GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT); + gltf->addImage("rooms.bmp"); + gltf->addTexture("texture", 0, 0); + gltf->addMaterial("material", 0, 0, 1.0f, 0.0f); + + delete[] vertices; + delete[] indices; + + gltf->addBufferView(0, 0, indicesOffset, sizeof(MeshIndex) * iCount); // 0 + gltf->addBufferView(0, sizeof(MeshVertex), verticesOffset, sizeof(MeshVertex) * vCount); // 1 + + gltf->addBuffer(bufferData, bufferSize); // 0 + + delete[] bufferData; + + char *buffer = new char[gltf->getBufferSize()]; + int size = gltf->save(buffer); + delete gltf; + + fwrite(buffer, 1, size, file); + + delete[] buffer; + + fclose(file); + } + + void exportModel(IGame *game, const char *dir, TR::Model &model) { + TR::Level *level = game->getLevel(); + MeshBuilder *mesh = game->getMesh(); + + typedef uint32 MeshIndex; + + struct ModelVertex { + vec3 coord; + vec3 normal; + vec2 texCoord; ubyte4 joints; ubyte4 weights; }; - typedef uint32 ModelIndex; - char name[256]; - sprintf(name, "models/dump/%d.glb", int(model.type)); + sprintf(name, "%s/model_%d.glb", dir, int(model.type)); + + LOG("export model: %s\n", name); FILE *file = fopen(name, "wb"); if (!file) { @@ -61,6 +252,7 @@ namespace Extension { anim->setAnim(0); animRate = max((int)(anim->anims + anim->index)->frameRate, 1); animFrames = anim->framesCount / animRate; + ASSERT(animFrames > 0); } // get model geometry @@ -89,7 +281,7 @@ namespace Extension { int rotationOffset = translationOffset + animFrames * sizeof(vec3); int verticesOffset = rotationOffset + model.mCount * animFrames * sizeof(quat); int indicesOffset = verticesOffset + vCount * sizeof(ModelVertex); - int bufferSize = indicesOffset + iCount * sizeof(ModelIndex); + int bufferSize = indicesOffset + iCount * sizeof(MeshIndex); char *bufferData = new char[bufferSize]; @@ -113,11 +305,10 @@ namespace Extension { dst.texCoord = src.texCoord; dst.normal = dst.normal.normal(); - dst.coord = flip * dst.coord; + dst.coord = flip * (dst.coord * MESH_SCALE); dst.normal = flip * dst.normal; dst.texCoord *= (1.0f / 32767.0f); - dst.color = src.color; dst.joints = ubyte4(uint8(src.coord.w / 2), 0, 0, 0); dst.weights = ubyte4(255, 0, 0, 0); @@ -131,29 +322,28 @@ namespace Extension { } for (int i = 0; i < iCount; i++) { - ModelIndex &dst = ((ModelIndex*)(bufferData + indicesOffset))[i]; + MeshIndex &dst = ((MeshIndex*)(bufferData + indicesOffset))[i]; dst = indices[i]; } GLTF *gltf = new GLTF(); gltf->addSampler(GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, GL_REPEAT, GL_REPEAT); - gltf->addImage("objects.bmp"); + gltf->addImage("models.bmp"); gltf->addTexture("texture", 0, 0); gltf->addMaterial("material", 0, 0, 1.0f, 0.0f); delete[] vertices; delete[] indices; - gltf->addAccessor(0, 0, iCount, GLTF::SCALAR, GL_UNSIGNED_INT); // 0 - gltf->addAccessor(1, (int)OFFSETOF(ModelVertex, coord), vCount, GLTF::VEC3, GL_FLOAT, false, vMin, vMax); // 1 - gltf->addAccessor(1, (int)OFFSETOF(ModelVertex, normal), vCount, GLTF::VEC3, GL_FLOAT); // 2 - gltf->addAccessor(1, (int)OFFSETOF(ModelVertex, texCoord), vCount, GLTF::VEC2, GL_FLOAT); // 3 - //gltf->addAccessor(1, (int)OFFSETOF(ModelVertex, color), vCount, GLTF::VEC4, GL_UNSIGNED_BYTE); // TODO - gltf->addAccessor(1, (int)OFFSETOF(ModelVertex, joints), vCount, GLTF::VEC4, GL_UNSIGNED_BYTE); // 4 - gltf->addAccessor(1, (int)OFFSETOF(ModelVertex, weights), vCount, GLTF::VEC4, GL_UNSIGNED_BYTE, true); // 5 + gltf->addAccessor(0, sizeof(MeshIndex), 0, iCount, GLTF::SCALAR, GL_UNSIGNED_INT); // 0 + gltf->addAccessor(1, sizeof(ModelVertex), (int)OFFSETOF(ModelVertex, coord), vCount, GLTF::VEC3, GL_FLOAT, false, vMin, vMax); // 1 + gltf->addAccessor(1, sizeof(ModelVertex), (int)OFFSETOF(ModelVertex, normal), vCount, GLTF::VEC3, GL_FLOAT); // 2 + gltf->addAccessor(1, sizeof(ModelVertex), (int)OFFSETOF(ModelVertex, texCoord), vCount, GLTF::VEC2, GL_FLOAT); // 3 + gltf->addAccessor(1, sizeof(ModelVertex), (int)OFFSETOF(ModelVertex, joints), vCount, GLTF::VEC4, GL_UNSIGNED_BYTE); // 4 + gltf->addAccessor(1, sizeof(ModelVertex), (int)OFFSETOF(ModelVertex, weights), vCount, GLTF::VEC4, GL_UNSIGNED_BYTE, true); // 5 - gltf->addBufferView(0, 0, indicesOffset, sizeof(ModelIndex) * iCount); // 0 + gltf->addBufferView(0, 0, indicesOffset, sizeof(MeshIndex) * iCount); // 0 gltf->addBufferView(0, sizeof(ModelVertex), verticesOffset, sizeof(ModelVertex) * vCount); // 1 sprintf(name, "%d_mesh", int(model.type)); @@ -182,13 +372,13 @@ namespace Extension { timeline[i] = i * animRate / 30.0f; } gltf->addBufferView(0, 0, timelineOffset, animFrames * sizeof(float)); // 2 - gltf->addAccessor(2, 0, animFrames, GLTF::SCALAR, GL_FLOAT, false, vec4(timeline[0], 0, 0, 0), vec4(timeline[animFrames - 1], 0, 0, 1)); // 6 + gltf->addAccessor(2, 0, 0, animFrames, GLTF::SCALAR, GL_FLOAT, false, vec4(timeline[0], 0, 0, 0), vec4(timeline[animFrames - 1], 0, 0, 1)); // 6 } { // root translation (output) vec3 *translation = (vec3*)(bufferData + translationOffset); gltf->addBufferView(0, 0, translationOffset, animFrames * sizeof(vec3)); // 3 - gltf->addAccessor(3, 0, animFrames, GLTF::VEC3, GL_FLOAT); // 7+ + gltf->addAccessor(3, 0, 0, animFrames, GLTF::VEC3, GL_FLOAT); // 7+ JSON *sampler = samplers->add(JSON::OBJECT); // 0 sampler->add("input", 6); // timeline @@ -203,8 +393,7 @@ namespace Extension { for (int j = 0; j < animFrames; j++) { TR::AnimFrame *frame = anim->getFrame(anim->anims + anim->index, j); - translation[j] = frame->pos; - translation[j] = flip * translation[j]; + translation[j] = flip * (vec3((float)frame->pos.x, (float)frame->pos.y, (float)frame->pos.z) * MESH_SCALE); } } @@ -227,7 +416,7 @@ namespace Extension { *ptr = matrix.getRot(); ptr++; } - gltf->addAccessor(4, i * animFrames * sizeof(quat), animFrames, GLTF::VEC4, GL_FLOAT); // 8+ + gltf->addAccessor(4, 0, i * animFrames * sizeof(quat), animFrames, GLTF::VEC4, GL_FLOAT); // 8+ JSON *sampler = samplers->add(JSON::OBJECT); // 1+ sampler->add("input", 6); // timeline @@ -271,7 +460,7 @@ namespace Extension { for (int i = 0; i < model.mCount; i++) { sprintf(name, "joint_%d", i + 1); - JSON* node = gltf->addNode(name, -1, -1, jointPos, quat(0, 0, 0, 1)); + JSON* node = gltf->addNode(name, -1, -1, jointPos * MESH_SCALE, quat(0, 0, 0, 1)); JSON* children = new JSON(JSON::ARRAY, "children"); for (int j = 0; j < model.mCount; j++) { @@ -287,13 +476,13 @@ namespace Extension { } joints[i] = i + 1; + TR::Node &t = *((TR::Node*)&level->nodesData[model.node] + i); jointPos = flip * vec3((float)t.x, (float)t.y, (float)t.z); } gltf->addSkin("skin", -1, 1, joints, model.mCount); - char *buffer = new char[gltf->getBufferSize()]; int size = gltf->save(buffer); delete gltf; @@ -302,24 +491,27 @@ namespace Extension { delete[] buffer; - LOG("export model: %s\n", name); - fclose(file); } void exportGeometry(IGame *game, Texture *atlasRooms, Texture *atlasObjects, Texture *atlasSprites) { - CreateDirectory("models", NULL); - CreateDirectory("models/dump/", NULL); - exportTexture("models/dump/rooms", atlasRooms); - exportTexture("models/dump/objects", atlasObjects); - exportTexture("models/dump/sprites", atlasSprites); + char dir[256]; + sprintf(dir, "dump/%s", TR::LEVEL_INFO[game->getLevel()->id].name); + + CreateDirectory("dump", NULL); + CreateDirectory(dir, NULL); + + exportTexture(dir, "rooms", atlasRooms); + exportTexture(dir, "models", atlasObjects); TR::Level *level = game->getLevel(); MeshBuilder *mesh = game->getMesh(); + exportRooms(game, dir); + for (int i = 0; i < level->modelsCount; i++) { - exportModel(game, level->models[i]); + exportModel(game, dir, level->models[i]); } } #endif diff --git a/src/fixed/camera.h b/src/fixed/camera.h new file mode 100644 index 00000000..8f822b79 --- /dev/null +++ b/src/fixed/camera.h @@ -0,0 +1,449 @@ +#ifndef H_CAMERA +#define H_CAMERA + +#include "common.h" + +#define CAM_SPEED (1 << 3) +#define CAM_ROT_SPEED (1 << 9) +#define CAM_DIST_FOLLOW 1536 +#define CAM_DIST_LOOK 768 +#define CAM_DIST_COMBAT 2048 +#define CAM_RADIUS 255 +#define CAM_ANGLE_FOLLOW ANGLE(-10) +#define CAM_ANGLE_COMBAT ANGLE(-10) +#define CAM_ANGLE_MAX ANGLE(85) + +void Camera::init(ItemObj* lara) +{ + ASSERT(lara->extraL); + + target.pos = lara->pos; + target.pos.y -= 1024; + target.room = lara->room; + + view = target; + view.pos.z -= 100; + + targetDist = CAM_DIST_FOLLOW; + targetAngle = _vec3s(0, 0, 0); + angle = _vec3s(0, 0, 0); + + laraItem = lara; + lastItem = NULL; + lookAtItem = NULL; + + speed = 1; + timer = 0; + index = -1; + lastIndex = -1; + + mode = CAMERA_MODE_FOLLOW; + + lastFixed = false; + center = false; +} + +Location Camera::getLocationForAngle(int32 angle, int32 distH, int32 distV) +{ + int32 s, c; + sincos(angle, s, c); + + Location res; + res.pos.x = target.pos.x - (distH * s >> FIXED_SHIFT); + res.pos.y = target.pos.y + (distV); + res.pos.z = target.pos.z - (distH * c >> FIXED_SHIFT); + res.room = target.room; + return res; +} + +bool checkWall(Room* room, int32 x, int32 y, int32 z) +{ + Room* nextRoom = room->getRoom(x, y, z); + + const Sector* sector = nextRoom->getSector(x, z); + int32 floor = sector->getFloor(x, y, z); + int32 ceiling = sector->getCeiling(x, y, z); + return (floor == WALL || ceiling == WALL || ceiling >= floor || y > floor || y < ceiling); +} + +int32 checkHeight(Room* room, int32 x, int32 y, int32 z) +{ + Room* nextRoom = room->getRoom(x, y, z); + const Sector* sector = nextRoom->getSector(x, z); + int32 floor = sector->getFloor(x, y, z); + int32 ceiling = sector->getCeiling(x, y, z); + + if (floor != WALL && ceiling != WALL && ceiling < floor) + { + if (y - CAM_RADIUS < ceiling && y + CAM_RADIUS > floor) + return (floor + ceiling) >> 1; + + if (y + CAM_RADIUS > floor) + return floor - CAM_RADIUS; + + if (y - CAM_RADIUS < ceiling) + return ceiling + CAM_RADIUS; + } + + return y; +} + +void Camera::clip(Location &loc) +{ + loc.pos.y = checkHeight(loc.room, loc.pos.x, loc.pos.y, loc.pos.z); + + if (checkWall(loc.room, loc.pos.x - CAM_RADIUS, loc.pos.y, loc.pos.z)) { + loc.pos.x = (loc.pos.x & (~1023)) + CAM_RADIUS; + } + + if (checkWall(loc.room, loc.pos.x + CAM_RADIUS, loc.pos.y, loc.pos.z)) { + loc.pos.x = (loc.pos.x | 1023) - CAM_RADIUS; + } + + if (checkWall(loc.room, loc.pos.x, loc.pos.y, loc.pos.z - CAM_RADIUS)) { + loc.pos.z = (loc.pos.z & (~1023)) + CAM_RADIUS; + } + + if (checkWall(loc.room, loc.pos.x, loc.pos.y, loc.pos.z + CAM_RADIUS)) { + loc.pos.z = (loc.pos.z | 1023) - CAM_RADIUS; + } + + loc.room = loc.room->getRoom(loc.pos.x, loc.pos.y, loc.pos.z); +} + +Location Camera::getBestLocation(bool clip) +{ + int32 s, c; + sincos(targetAngle.x, s, c); + + int32 distH = targetDist * c >> FIXED_SHIFT; + int32 distV = targetDist * s >> FIXED_SHIFT; + + Location best = getLocationForAngle(targetAngle.y, distH, distV); + + if (trace(target, best, true)) + return best; + + if (clip && best.pos != target.pos) + return best; + + int32 dist = fastLength(target.pos.x - best.pos.x, target.pos.z - best.pos.z); + + if (dist > 768) + return best; + + int32 minDist = INT_MAX; + + for (int32 i = 0; i < 4; i++) + { + Location tmpDest = getLocationForAngle(i * ANGLE_90, distH, distV); + Location tmpView = view; + + if (!trace(target, tmpDest, true) || !trace(tmpDest, tmpView, false)) + continue; + + dist = fastLength(view.pos.x - tmpDest.pos.x, view.pos.z - tmpDest.pos.z); + + if (dist < minDist) + { + minDist = dist; + best = tmpDest; + } + } + + return best; +} + +void Camera::move(Location &to, int32 speed) +{ + clip(to); + + vec3i d = to.pos - view.pos; + + if (speed > 1) { + d /= speed; + } + + view.pos += d; + view.room = to.room->getRoom(view.pos.x, view.pos.y, view.pos.z); + + const Sector* sector = view.room->getSector(view.pos.x, view.pos.z); + + int32 floor = sector->getFloor(view.pos.x, view.pos.y, view.pos.z) - 256; + if (view.pos.y >= floor && to.pos.y >= floor) + { + trace(target, view, true); + view.room = view.room->getRoom(view.pos.x, view.pos.y, view.pos.z); + } +} + +void Camera::updateFree() +{ + matrixSetView(view.pos, angle.x, angle.y); + + Matrix &m = matrixGet(); + + if (keys & IK_UP) angle.x -= CAM_ROT_SPEED; + if (keys & IK_DOWN) angle.x += CAM_ROT_SPEED; + if (keys & IK_LEFT) angle.y -= CAM_ROT_SPEED; + if (keys & IK_RIGHT) angle.y += CAM_ROT_SPEED; + + angle.x = X_CLAMP(angle.x, -CAM_ANGLE_MAX, CAM_ANGLE_MAX); + + if (keys & IK_A) + { + view.pos.x += m.e20 * CAM_SPEED >> 10; + view.pos.y += m.e21 * CAM_SPEED >> 10; + view.pos.z += m.e22 * CAM_SPEED >> 10; + } + + if (keys & IK_B) + { + view.pos.x -= m.e20 * CAM_SPEED >> 10; + view.pos.y -= m.e21 * CAM_SPEED >> 10; + view.pos.z -= m.e22 * CAM_SPEED >> 10; + } + + if (keys & IK_R) + { + view.pos.x += m.e00 * CAM_SPEED >> 10; + view.pos.y += m.e01 * CAM_SPEED >> 10; + view.pos.z += m.e02 * CAM_SPEED >> 10; + } + + if (keys & IK_L) + { + view.pos.x -= m.e00 * CAM_SPEED >> 10; + view.pos.y -= m.e01 * CAM_SPEED >> 10; + view.pos.z -= m.e02 * CAM_SPEED >> 10; + } + + view.room = view.room->getRoom(view.pos.x, view.pos.y, view.pos.z); +} + +void Camera::updateFollow(ItemObj* item) +{ + if (targetAngle.x == 0) { + targetAngle.x = CAM_ANGLE_FOLLOW; + } + + targetAngle.x = X_CLAMP(targetAngle.x + item->angle.x, -CAM_ANGLE_MAX, CAM_ANGLE_MAX); + targetAngle.y += item->angle.y; + + Location best = getBestLocation(false); + + move(best, lastFixed ? speed : 12); +} + +void Camera::updateCombat(ItemObj* item) +{ + ASSERT(item->type == ITEM_LARA); + + targetAngle.x = item->angle.x + CAM_ANGLE_COMBAT; + targetAngle.y = item->angle.y; + + if (item->extraL->armR.target || item->extraL->armL.target) + { + int32 aX = item->extraL->armR.angleAim.x + item->extraL->armL.angleAim.x; + int32 aY = item->extraL->armR.angleAim.y + item->extraL->armL.angleAim.y; + + if (item->extraL->armR.target && item->extraL->armL.target) { + targetAngle.x += aX >> 1; + targetAngle.y += aY >> 1; + } else { + targetAngle.x += aX; + targetAngle.y += aY; + } + } else { + targetAngle.x += item->extraL->head.angle.x + item->extraL->torso.angle.x; + targetAngle.y += item->extraL->head.angle.y + item->extraL->torso.angle.y; + } + + targetDist = CAM_DIST_COMBAT; + + Location best = getBestLocation(true); + + move(best, speed); +} + +void Camera::updateLook(ItemObj* item) +{ + ASSERT(item->type == ITEM_LARA); + + targetAngle.x = item->extraL->head.angle.x + item->extraL->torso.angle.x + item->angle.x; + targetAngle.y = item->extraL->head.angle.y + item->extraL->torso.angle.y + item->angle.y; + targetDist = lookAtItem ? CAM_DIST_FOLLOW : CAM_DIST_LOOK; + + Location best = getBestLocation(true); + + move(best, speed); +} + +void Camera::updateFixed() +{ + const FixedCamera* cam = level.cameras + index; + + Location best; + best.pos = cam->pos; + best.room = rooms + cam->roomIndex; + + lastFixed = true; + move(best, 1); + + if (timer != 0) + { + timer--; + if (timer == 0) { + timer = -1; + } + } +} + +void Camera::lookAt(int32 offset) +{ + int32 dx = lookAtItem->pos.x - laraItem->pos.x; + int32 dz = lookAtItem->pos.z - laraItem->pos.z; + + int16 ay = int16(phd_atan(dz, dx) - laraItem->angle.y) >> 1; + + if (abs(ay) >= LARA_LOOK_ANGLE_Y) + { + lookAtItem = NULL; + return; + } + + const AABBs& box = lookAtItem->getBoundingBox(true); + + offset -= lookAtItem->pos.y + ((box.minY + box.maxY) >> 1); + + int16 ax = int16(phd_atan(phd_sqrt(X_SQR(dx) + X_SQR(dz)), offset)) >> 1; + + if (ax < LARA_LOOK_ANGLE_MIN || ax > LARA_LOOK_ANGLE_MAX) + { + lookAtItem = NULL; + return; + } + + laraItem->extraL->head.angle.x = angleLerp(laraItem->extraL->head.angle.x, ax, LARA_LOOK_TURN_SPEED); + laraItem->extraL->head.angle.y = angleLerp(laraItem->extraL->head.angle.y, ay, LARA_LOOK_TURN_SPEED); + + laraItem->extraL->torso.angle = laraItem->extraL->head.angle; + + lookAtItem->flags |= ITEM_FLAG_ANIMATED; // use as once flag + mode = CAMERA_MODE_LOOK; +} + +void Camera::update() +{ + if (mode == CAMERA_MODE_FREE) + { + updateFree(); + matrixSetView(view.pos, angle.x, angle.y); + return; + } + + bool isFixed = false; + ItemObj* item = laraItem; + + if (lookAtItem && (mode == CAMERA_MODE_FIXED || mode == CAMERA_MODE_OBJECT)) + { + isFixed = true; + item = lookAtItem; + } + + ASSERT(item); + + target.room = item->room; + target.pos.x = item->pos.x; + target.pos.z = item->pos.z; + + const AABBs &box = item->getBoundingBox(true); + + int32 y = item->pos.y; + if (isFixed) { + y += (box.minY + box.maxY) >> 1; + } else { + y += box.maxY + ((box.minY - box.maxY) * 3 >> 2); + } + + if (!isFixed && lookAtItem) { + lookAt(y); + } + + if (mode == CAMERA_MODE_LOOK || mode == CAMERA_MODE_COMBAT) + { + y -= 256; + + if (lastFixed) { + target.pos.y = 0; + speed = 1; + } else { + target.pos.y += (y - target.pos.y) >> 2; + speed = (mode == CAMERA_MODE_LOOK) ? 4 : 8; + } + + } else { + + if (center) + { + int32 offset = (box.minZ + box.maxZ) >> 1; + int32 s, c; + sincos(item->angle.y, s, c); + target.pos.x += (s * offset) >> FIXED_SHIFT; + target.pos.z += (c * offset) >> FIXED_SHIFT; + } + + lastFixed = (int32(lastFixed) ^ int32(isFixed)) != 0; // armcpp 3DO compiler (lastFixed ^= isFixed) + + if (lastFixed) { + target.pos.y = y; + speed = 1; + } else { + target.pos.y += (y - target.pos.y) >> 2; + } + } + + switch (mode) + { + case CAMERA_MODE_FOLLOW : updateFollow(item); break; + case CAMERA_MODE_COMBAT : updateCombat(item); break; + case CAMERA_MODE_LOOK : updateLook(item); break; + default : updateFixed(); + } + + lastFixed = isFixed; + lastIndex = index; + + if (mode != CAMERA_MODE_OBJECT || timer == -1) + { + mode = CAMERA_MODE_FOLLOW; + index = -1; + lastItem = lookAtItem; + lookAtItem = NULL; + targetAngle.x = 0; + targetAngle.y = 0; + targetDist = CAM_DIST_FOLLOW; + center = false; + } + + vec3i dir = target.pos - view.pos; + anglesFromVector(dir.x, dir.y, dir.z, angle.x, angle.y); + + matrixSetView(view.pos, angle.x, angle.y); +} + +void Camera::toCombat() +{ + if (mode == CAMERA_MODE_FREE) + return; + + if (mode == CAMERA_MODE_CUTSCENE) + return; + + if (mode == CAMERA_MODE_LOOK) + return; + + mode = CAMERA_MODE_COMBAT; +} + +#endif diff --git a/src/fixed/common.cpp b/src/fixed/common.cpp new file mode 100644 index 00000000..ff6f1576 --- /dev/null +++ b/src/fixed/common.cpp @@ -0,0 +1,1595 @@ +#include "common.h" +#include "lang/en.h" + +EWRAM_DATA uint32 keys; +EWRAM_DATA RectMinMax viewport; +vec3i gCameraViewPos; +Matrix gMatrixStack[MAX_MATRICES]; +Matrix* gMatrixPtr = gMatrixStack; + +EWRAM_DATA Sphere gSpheres[2][MAX_SPHERES]; + +const FloorData* gLastFloorData; +FloorData gLastFloorSlant; +EWRAM_DATA TargetInfo tinfo; + +EWRAM_DATA Settings gSettings; +EWRAM_DATA SaveGame gSaveGame; +EWRAM_DATA uint8 gSaveData[SAVEGAME_SIZE - sizeof(SaveGame)]; + +EWRAM_DATA int32 gCurTrack; +EWRAM_DATA int32 gAnimTexFrame; + +int32 gLightAmbient; +int32 gRandTable[MAX_RAND_TABLE]; +int32 gCaustics[MAX_CAUSTICS]; +int32 gCausticsFrame; + +EWRAM_DATA const char* const* STR = STR_EN; + +EWRAM_DATA ExtraInfoLara playersExtra[MAX_PLAYERS]; + +#if defined(__GBA__) + #include "TRACKS_IMA.h" + #include "TITLE_SCR.h" + #include "TITLE_PKD.h" + #include "GYM_PKD.h" + #include "LEVEL1_PKD.h" + #include "LEVEL2_PKD.h" + + #define LEVEL_INFO(name, title, track, secrets) { #name, name##_PKD, title, track, secrets } +#else + #define LEVEL_INFO(name, title, track, secrets) { #name, NULL, title, track, secrets } +#endif + +#ifdef __3DO__ // TODO fix the title scren on 3DO +EWRAM_DATA LevelID gLevelID = LVL_TR1_1; +#else +EWRAM_DATA LevelID gLevelID = LVL_TR1_TITLE; +#endif + +const LevelInfo gLevelInfo[LVL_MAX] = { +// TR1 + LEVEL_INFO( TITLE , STR_EMPTY , TRACK_TR1_TITLE , 0 ), + LEVEL_INFO( GYM , STR_TR1_GYM , TRACK_NONE , 0 ), + LEVEL_INFO( LEVEL1 , STR_TR1_LEVEL1 , TRACK_TR1_CAVES , 3 ), + LEVEL_INFO( LEVEL2 , STR_TR1_LEVEL2 , TRACK_TR1_CAVES , 3 ), + //LEVEL_INFO( LEVEL3A , STR_TR1_LEVEL3A , TRACK_TR1_CAVES , 5 ), + //LEVEL_INFO( LEVEL3B , STR_TR1_LEVEL3B , TRACK_TR1_CAVES , 3 ), + //LEVEL_INFO( CUT1 , STR_EMPTY , TRACK_TR1_CUT_1 , 0 ), + //LEVEL_INFO( LEVEL4 , STR_TR1_LEVEL4 , TRACK_TR1_WIND , 4 ), + //LEVEL_INFO( LEVEL5 , STR_TR1_LEVEL5 , TRACK_TR1_WIND , 3 ), + //LEVEL_INFO( LEVEL6 , STR_TR1_LEVEL6 , TRACK_TR1_WIND , 3 ), + //LEVEL_INFO( LEVEL7A , STR_TR1_LEVEL7A , TRACK_TR1_CISTERN , 3 ), + //LEVEL_INFO( LEVEL7B , STR_TR1_LEVEL7B , TRACK_TR1_CISTERN , 2 ), + //LEVEL_INFO( CUT2 , STR_EMPTY , TRACK_TR1_CUT_2 , 0 ), + //LEVEL_INFO( LEVEL8A , STR_TR1_LEVEL8A , TRACK_TR1_WIND , 3 ), + //LEVEL_INFO( LEVEL8B , STR_TR1_LEVEL8B , TRACK_TR1_WIND , 3 ), + //LEVEL_INFO( LEVEL8C , STR_TR1_LEVEL8C , TRACK_TR1_WIND , 1 ), + //LEVEL_INFO( LEVEL10A , STR_TR1_LEVEL10A , TRACK_TR1_CISTERN , 3 ), + //LEVEL_INFO( CUT3 , STR_EMPTY , TRACK_TR1_CUT_3 , 0 ), + //LEVEL_INFO( LEVEL10B , STR_TR1_LEVEL10B , TRACK_TR1_PYRAMID , 3 ), + //LEVEL_INFO( CUT4 , STR_EMPTY , TRACK_TR1_CUT_4 , 0 ), + //LEVEL_INFO( LEVEL10C , STR_TR1_LEVEL10C , TRACK_TR1_PYRAMID , 3 ), + //LEVEL_INFO( EGYPT , STR_TR1_EGYPT , TRACK_TR1_WIND , 3 ), + //LEVEL_INFO( CAT , STR_TR1_CAT , TRACK_TR1_WIND , 4 ), + //LEVEL_INFO( END , STR_TR1_END , TRACK_TR1_WIND , 2 ) +}; + +#ifdef PROFILING + uint32 gCounters[CNT_MAX]; +#endif + +int32 gRandSeedLogic; +int32 gRandSeedDraw; + +#define X_RAND(seed) (((seed = 0x3039 + seed * 0x41C64E6D) >> 10) & 0x7FFF); + +int32 rand_logic() +{ + return X_RAND(gRandSeedLogic); +} + +int32 rand_draw() +{ + return X_RAND(gRandSeedDraw); +} + +#ifdef USE_DIV_TABLE +EWRAM_DATA divTableInt divTable[DIV_TABLE_SIZE] = { // must be at EWRAM start + 0xFFFF, 0xFFFF, 0x8000, 0x5555, 0x4000, 0x3333, 0x2AAA, 0x2492, + 0x2000, 0x1C71, 0x1999, 0x1745, 0x1555, 0x13B1, 0x1249, 0x1111, + 0x1000, 0x0F0F, 0x0E38, 0x0D79, 0x0CCC, 0x0C30, 0x0BA2, 0x0B21, + 0x0AAA, 0x0A3D, 0x09D8, 0x097B, 0x0924, 0x08D3, 0x0888, 0x0842, + 0x0800, 0x07C1, 0x0787, 0x0750, 0x071C, 0x06EB, 0x06BC, 0x0690, + 0x0666, 0x063E, 0x0618, 0x05F4, 0x05D1, 0x05B0, 0x0590, 0x0572, + 0x0555, 0x0539, 0x051E, 0x0505, 0x04EC, 0x04D4, 0x04BD, 0x04A7, + 0x0492, 0x047D, 0x0469, 0x0456, 0x0444, 0x0432, 0x0421, 0x0410, + 0x0400, 0x03F0, 0x03E0, 0x03D2, 0x03C3, 0x03B5, 0x03A8, 0x039B, + 0x038E, 0x0381, 0x0375, 0x0369, 0x035E, 0x0353, 0x0348, 0x033D, + 0x0333, 0x0329, 0x031F, 0x0315, 0x030C, 0x0303, 0x02FA, 0x02F1, + 0x02E8, 0x02E0, 0x02D8, 0x02D0, 0x02C8, 0x02C0, 0x02B9, 0x02B1, + 0x02AA, 0x02A3, 0x029C, 0x0295, 0x028F, 0x0288, 0x0282, 0x027C, + 0x0276, 0x0270, 0x026A, 0x0264, 0x025E, 0x0259, 0x0253, 0x024E, + 0x0249, 0x0243, 0x023E, 0x0239, 0x0234, 0x0230, 0x022B, 0x0226, + 0x0222, 0x021D, 0x0219, 0x0214, 0x0210, 0x020C, 0x0208, 0x0204, + 0x0200, 0x01FC, 0x01F8, 0x01F4, 0x01F0, 0x01EC, 0x01E9, 0x01E5, + 0x01E1, 0x01DE, 0x01DA, 0x01D7, 0x01D4, 0x01D0, 0x01CD, 0x01CA, + 0x01C7, 0x01C3, 0x01C0, 0x01BD, 0x01BA, 0x01B7, 0x01B4, 0x01B2, + 0x01AF, 0x01AC, 0x01A9, 0x01A6, 0x01A4, 0x01A1, 0x019E, 0x019C, + 0x0199, 0x0197, 0x0194, 0x0192, 0x018F, 0x018D, 0x018A, 0x0188, + 0x0186, 0x0183, 0x0181, 0x017F, 0x017D, 0x017A, 0x0178, 0x0176, + 0x0174, 0x0172, 0x0170, 0x016E, 0x016C, 0x016A, 0x0168, 0x0166, + 0x0164, 0x0162, 0x0160, 0x015E, 0x015C, 0x015A, 0x0158, 0x0157, + 0x0155, 0x0153, 0x0151, 0x0150, 0x014E, 0x014C, 0x014A, 0x0149, + 0x0147, 0x0146, 0x0144, 0x0142, 0x0141, 0x013F, 0x013E, 0x013C, + 0x013B, 0x0139, 0x0138, 0x0136, 0x0135, 0x0133, 0x0132, 0x0130, + 0x012F, 0x012E, 0x012C, 0x012B, 0x0129, 0x0128, 0x0127, 0x0125, + 0x0124, 0x0123, 0x0121, 0x0120, 0x011F, 0x011E, 0x011C, 0x011B, + 0x011A, 0x0119, 0x0118, 0x0116, 0x0115, 0x0114, 0x0113, 0x0112, + 0x0111, 0x010F, 0x010E, 0x010D, 0x010C, 0x010B, 0x010A, 0x0109, + 0x0108, 0x0107, 0x0106, 0x0105, 0x0104, 0x0103, 0x0102, 0x0101, + 0x0100, 0x00FF, 0x00FE, 0x00FD, 0x00FC, 0x00FB, 0x00FA, 0x00F9, + 0x00F8, 0x00F7, 0x00F6, 0x00F5, 0x00F4, 0x00F3, 0x00F2, 0x00F1, + 0x00F0, 0x00F0, 0x00EF, 0x00EE, 0x00ED, 0x00EC, 0x00EB, 0x00EA, + 0x00EA, 0x00E9, 0x00E8, 0x00E7, 0x00E6, 0x00E5, 0x00E5, 0x00E4, + 0x00E3, 0x00E2, 0x00E1, 0x00E1, 0x00E0, 0x00DF, 0x00DE, 0x00DE, + 0x00DD, 0x00DC, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D8, + 0x00D7, 0x00D6, 0x00D6, 0x00D5, 0x00D4, 0x00D4, 0x00D3, 0x00D2, + 0x00D2, 0x00D1, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CD, + 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C9, 0x00C8, + 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C4, 0x00C3, + 0x00C3, 0x00C2, 0x00C1, 0x00C1, 0x00C0, 0x00C0, 0x00BF, 0x00BF, + 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BC, 0x00BB, 0x00BB, 0x00BA, + 0x00BA, 0x00B9, 0x00B9, 0x00B8, 0x00B8, 0x00B7, 0x00B7, 0x00B6, + 0x00B6, 0x00B5, 0x00B5, 0x00B4, 0x00B4, 0x00B3, 0x00B3, 0x00B2, + 0x00B2, 0x00B1, 0x00B1, 0x00B0, 0x00B0, 0x00AF, 0x00AF, 0x00AE, + 0x00AE, 0x00AD, 0x00AD, 0x00AC, 0x00AC, 0x00AC, 0x00AB, 0x00AB, + 0x00AA, 0x00AA, 0x00A9, 0x00A9, 0x00A8, 0x00A8, 0x00A8, 0x00A7, + 0x00A7, 0x00A6, 0x00A6, 0x00A5, 0x00A5, 0x00A5, 0x00A4, 0x00A4, + 0x00A3, 0x00A3, 0x00A3, 0x00A2, 0x00A2, 0x00A1, 0x00A1, 0x00A1, + 0x00A0, 0x00A0, 0x009F, 0x009F, 0x009F, 0x009E, 0x009E, 0x009D, + 0x009D, 0x009D, 0x009C, 0x009C, 0x009C, 0x009B, 0x009B, 0x009A, + 0x009A, 0x009A, 0x0099, 0x0099, 0x0099, 0x0098, 0x0098, 0x0098, + 0x0097, 0x0097, 0x0097, 0x0096, 0x0096, 0x0095, 0x0095, 0x0095, + 0x0094, 0x0094, 0x0094, 0x0093, 0x0093, 0x0093, 0x0092, 0x0092, + 0x0092, 0x0091, 0x0091, 0x0091, 0x0090, 0x0090, 0x0090, 0x0090, + 0x008F, 0x008F, 0x008F, 0x008E, 0x008E, 0x008E, 0x008D, 0x008D, + 0x008D, 0x008C, 0x008C, 0x008C, 0x008C, 0x008B, 0x008B, 0x008B, + 0x008A, 0x008A, 0x008A, 0x0089, 0x0089, 0x0089, 0x0089, 0x0088, + 0x0088, 0x0088, 0x0087, 0x0087, 0x0087, 0x0087, 0x0086, 0x0086, + 0x0086, 0x0086, 0x0085, 0x0085, 0x0085, 0x0084, 0x0084, 0x0084, + 0x0084, 0x0083, 0x0083, 0x0083, 0x0083, 0x0082, 0x0082, 0x0082, + 0x0082, 0x0081, 0x0081, 0x0081, 0x0081, 0x0080, 0x0080, 0x0080, + 0x0080, 0x007F, 0x007F, 0x007F, 0x007F, 0x007E, 0x007E, 0x007E, + 0x007E, 0x007D, 0x007D, 0x007D, 0x007D, 0x007C, 0x007C, 0x007C, + 0x007C, 0x007B, 0x007B, 0x007B, 0x007B, 0x007A, 0x007A, 0x007A, + 0x007A, 0x007A, 0x0079, 0x0079, 0x0079, 0x0079, 0x0078, 0x0078, + 0x0078, 0x0078, 0x0078, 0x0077, 0x0077, 0x0077, 0x0077, 0x0076, + 0x0076, 0x0076, 0x0076, 0x0076, 0x0075, 0x0075, 0x0075, 0x0075, + 0x0075, 0x0074, 0x0074, 0x0074, 0x0074, 0x0073, 0x0073, 0x0073, + 0x0073, 0x0073, 0x0072, 0x0072, 0x0072, 0x0072, 0x0072, 0x0071, + 0x0071, 0x0071, 0x0071, 0x0071, 0x0070, 0x0070, 0x0070, 0x0070, + 0x0070, 0x0070, 0x006F, 0x006F, 0x006F, 0x006F, 0x006F, 0x006E, + 0x006E, 0x006E, 0x006E, 0x006E, 0x006D, 0x006D, 0x006D, 0x006D, + 0x006D, 0x006D, 0x006C, 0x006C, 0x006C, 0x006C, 0x006C, 0x006B, + 0x006B, 0x006B, 0x006B, 0x006B, 0x006B, 0x006A, 0x006A, 0x006A, + 0x006A, 0x006A, 0x006A, 0x0069, 0x0069, 0x0069, 0x0069, 0x0069, + 0x0069, 0x0068, 0x0068, 0x0068, 0x0068, 0x0068, 0x0068, 0x0067, + 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0066, 0x0066, 0x0066, + 0x0066, 0x0066, 0x0066, 0x0065, 0x0065, 0x0065, 0x0065, 0x0065, + 0x0065, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064, + 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0062, 0x0062, + 0x0062, 0x0062, 0x0062, 0x0062, 0x0062, 0x0061, 0x0061, 0x0061, + 0x0061, 0x0061, 0x0061, 0x0061, 0x0060, 0x0060, 0x0060, 0x0060, + 0x0060, 0x0060, 0x0060, 0x005F, 0x005F, 0x005F, 0x005F, 0x005F, + 0x005F, 0x005F, 0x005E, 0x005E, 0x005E, 0x005E, 0x005E, 0x005E, + 0x005E, 0x005E, 0x005D, 0x005D, 0x005D, 0x005D, 0x005D, 0x005D, + 0x005D, 0x005C, 0x005C, 0x005C, 0x005C, 0x005C, 0x005C, 0x005C, + 0x005C, 0x005B, 0x005B, 0x005B, 0x005B, 0x005B, 0x005B, 0x005B, + 0x005B, 0x005A, 0x005A, 0x005A, 0x005A, 0x005A, 0x005A, 0x005A, + 0x005A, 0x0059, 0x0059, 0x0059, 0x0059, 0x0059, 0x0059, 0x0059, + 0x0059, 0x0058, 0x0058, 0x0058, 0x0058, 0x0058, 0x0058, 0x0058, + 0x0058, 0x0057, 0x0057, 0x0057, 0x0057, 0x0057, 0x0057, 0x0057, + 0x0057, 0x0057, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, + 0x0056, 0x0056, 0x0056, 0x0055, 0x0055, 0x0055, 0x0055, 0x0055, + 0x0055, 0x0055, 0x0055, 0x0055, 0x0054, 0x0054, 0x0054, 0x0054, + 0x0054, 0x0054, 0x0054, 0x0054, 0x0054, 0x0053, 0x0053, 0x0053, + 0x0053, 0x0053, 0x0053, 0x0053, 0x0053, 0x0053, 0x0052, 0x0052, + 0x0052, 0x0052, 0x0052, 0x0052, 0x0052, 0x0052, 0x0052, 0x0052, + 0x0051, 0x0051, 0x0051, 0x0051, 0x0051, 0x0051, 0x0051, 0x0051, + 0x0051, 0x0051, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, + 0x0050, 0x0050, 0x0050, 0x0050, 0x004F, 0x004F, 0x004F, 0x004F, + 0x004F, 0x004F, 0x004F, 0x004F, 0x004F, 0x004F, 0x004E, 0x004E, + 0x004E, 0x004E, 0x004E, 0x004E, 0x004E, 0x004E, 0x004E, 0x004E, + 0x004E, 0x004D, 0x004D, 0x004D, 0x004D, 0x004D, 0x004D, 0x004D, + 0x004D, 0x004D, 0x004D, 0x004D, 0x004C, 0x004C, 0x004C, 0x004C, + 0x004C, 0x004C, 0x004C, 0x004C, 0x004C, 0x004C, 0x004C, 0x004B, + 0x004B, 0x004B, 0x004B, 0x004B, 0x004B, 0x004B, 0x004B, 0x004B, + 0x004B, 0x004B, 0x004A, 0x004A, 0x004A, 0x004A, 0x004A, 0x004A, + 0x004A, 0x004A, 0x004A, 0x004A, 0x004A, 0x004A, 0x0049, 0x0049, + 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, 0x0049, + 0x0049, 0x0049, 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, + 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, 0x0048, 0x0047, + 0x0047, 0x0047, 0x0047, 0x0047, 0x0047, 0x0047, 0x0047, 0x0047, + 0x0047, 0x0047, 0x0047, 0x0047, 0x0046, 0x0046, 0x0046, 0x0046, + 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, + 0x0046, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, + 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0044, 0x0044, + 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, 0x0044, + 0x0044, 0x0044, 0x0044, 0x0044, 0x0043, 0x0043, 0x0043, 0x0043, + 0x0043, 0x0043, 0x0043, 0x0043, 0x0043, 0x0043, 0x0043, 0x0043, + 0x0043, 0x0043, 0x0043, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, + 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, + 0x0042, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, + 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, + 0x0041, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, + 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040, 0x0040 +}; +#endif + +const uint32 gSinCosTable[4096] = { // ROM + 0x00004000, 0x00194000, 0x00324000, 0x004B4000, 0x00654000, 0x007E4000, 0x00973FFF, 0x00B03FFF, + 0x00C93FFF, 0x00E23FFE, 0x00FB3FFE, 0x01143FFE, 0x012E3FFD, 0x01473FFD, 0x01603FFC, 0x01793FFC, + 0x01923FFB, 0x01AB3FFA, 0x01C43FFA, 0x01DD3FF9, 0x01F73FF8, 0x02103FF7, 0x02293FF7, 0x02423FF6, + 0x025B3FF5, 0x02743FF4, 0x028D3FF3, 0x02A63FF2, 0x02C03FF1, 0x02D93FF0, 0x02F23FEF, 0x030B3FED, + 0x03243FEC, 0x033D3FEB, 0x03563FEA, 0x036F3FE8, 0x03883FE7, 0x03A13FE6, 0x03BB3FE4, 0x03D43FE3, + 0x03ED3FE1, 0x04063FE0, 0x041F3FDE, 0x04383FDC, 0x04513FDB, 0x046A3FD9, 0x04833FD7, 0x049C3FD5, + 0x04B53FD4, 0x04CE3FD2, 0x04E73FD0, 0x05003FCE, 0x051A3FCC, 0x05333FCA, 0x054C3FC8, 0x05653FC6, + 0x057E3FC4, 0x05973FC1, 0x05B03FBF, 0x05C93FBD, 0x05E23FBB, 0x05FB3FB8, 0x06143FB6, 0x062D3FB4, + 0x06463FB1, 0x065F3FAF, 0x06783FAC, 0x06913FAA, 0x06AA3FA7, 0x06C33FA4, 0x06DC3FA2, 0x06F53F9F, + 0x070E3F9C, 0x07273F99, 0x07403F97, 0x07593F94, 0x07723F91, 0x078B3F8E, 0x07A43F8B, 0x07BD3F88, + 0x07D63F85, 0x07EF3F82, 0x08073F7F, 0x08203F7B, 0x08393F78, 0x08523F75, 0x086B3F72, 0x08843F6E, + 0x089D3F6B, 0x08B63F68, 0x08CF3F64, 0x08E83F61, 0x09013F5D, 0x09193F5A, 0x09323F56, 0x094B3F52, + 0x09643F4F, 0x097D3F4B, 0x09963F47, 0x09AF3F43, 0x09C73F40, 0x09E03F3C, 0x09F93F38, 0x0A123F34, + 0x0A2B3F30, 0x0A443F2C, 0x0A5C3F28, 0x0A753F24, 0x0A8E3F20, 0x0AA73F1C, 0x0AC03F17, 0x0AD83F13, + 0x0AF13F0F, 0x0B0A3F0A, 0x0B233F06, 0x0B3B3F02, 0x0B543EFD, 0x0B6D3EF9, 0x0B853EF4, 0x0B9E3EF0, + 0x0BB73EEB, 0x0BD03EE7, 0x0BE83EE2, 0x0C013EDD, 0x0C1A3ED8, 0x0C323ED4, 0x0C4B3ECF, 0x0C643ECA, + 0x0C7C3EC5, 0x0C953EC0, 0x0CAE3EBB, 0x0CC63EB6, 0x0CDF3EB1, 0x0CF83EAC, 0x0D103EA7, 0x0D293EA2, + 0x0D413E9D, 0x0D5A3E98, 0x0D723E92, 0x0D8B3E8D, 0x0DA43E88, 0x0DBC3E82, 0x0DD53E7D, 0x0DED3E77, + 0x0E063E72, 0x0E1E3E6C, 0x0E373E67, 0x0E4F3E61, 0x0E683E5C, 0x0E803E56, 0x0E993E50, 0x0EB13E4A, + 0x0ECA3E45, 0x0EE23E3F, 0x0EFB3E39, 0x0F133E33, 0x0F2B3E2D, 0x0F443E27, 0x0F5C3E21, 0x0F753E1B, + 0x0F8D3E15, 0x0FA53E0F, 0x0FBE3E09, 0x0FD63E03, 0x0FEE3DFC, 0x10073DF6, 0x101F3DF0, 0x10373DE9, + 0x10503DE3, 0x10683DDD, 0x10803DD6, 0x10993DD0, 0x10B13DC9, 0x10C93DC2, 0x10E13DBC, 0x10FA3DB5, + 0x11123DAF, 0x112A3DA8, 0x11423DA1, 0x115A3D9A, 0x11733D93, 0x118B3D8D, 0x11A33D86, 0x11BB3D7F, + 0x11D33D78, 0x11EB3D71, 0x12043D6A, 0x121C3D63, 0x12343D5B, 0x124C3D54, 0x12643D4D, 0x127C3D46, + 0x12943D3F, 0x12AC3D37, 0x12C43D30, 0x12DC3D28, 0x12F43D21, 0x130C3D1A, 0x13243D12, 0x133C3D0B, + 0x13543D03, 0x136C3CFB, 0x13843CF4, 0x139C3CEC, 0x13B43CE4, 0x13CC3CDD, 0x13E43CD5, 0x13FB3CCD, + 0x14133CC5, 0x142B3CBD, 0x14433CB5, 0x145B3CAD, 0x14733CA5, 0x148B3C9D, 0x14A23C95, 0x14BA3C8D, + 0x14D23C85, 0x14EA3C7D, 0x15013C74, 0x15193C6C, 0x15313C64, 0x15493C5B, 0x15603C53, 0x15783C4B, + 0x15903C42, 0x15A73C3A, 0x15BF3C31, 0x15D73C29, 0x15EE3C20, 0x16063C17, 0x161D3C0F, 0x16353C06, + 0x164C3BFD, 0x16643BF5, 0x167C3BEC, 0x16933BE3, 0x16AB3BDA, 0x16C23BD1, 0x16DA3BC8, 0x16F13BBF, + 0x17093BB6, 0x17203BAD, 0x17373BA4, 0x174F3B9B, 0x17663B92, 0x177E3B88, 0x17953B7F, 0x17AC3B76, + 0x17C43B6D, 0x17DB3B63, 0x17F23B5A, 0x180A3B50, 0x18213B47, 0x18383B3E, 0x184F3B34, 0x18673B2A, + 0x187E3B21, 0x18953B17, 0x18AC3B0E, 0x18C33B04, 0x18DB3AFA, 0x18F23AF0, 0x19093AE6, 0x19203ADD, + 0x19373AD3, 0x194E3AC9, 0x19653ABF, 0x197C3AB5, 0x19933AAB, 0x19AA3AA1, 0x19C13A97, 0x19D83A8D, + 0x19EF3A82, 0x1A063A78, 0x1A1D3A6E, 0x1A343A64, 0x1A4B3A59, 0x1A623A4F, 0x1A793A45, 0x1A903A3A, + 0x1AA73A30, 0x1ABE3A25, 0x1AD43A1B, 0x1AEB3A10, 0x1B023A06, 0x1B1939FB, 0x1B3039F0, 0x1B4639E6, + 0x1B5D39DB, 0x1B7439D0, 0x1B8A39C5, 0x1BA139BB, 0x1BB839B0, 0x1BCE39A5, 0x1BE5399A, 0x1BFC398F, + 0x1C123984, 0x1C293979, 0x1C3F396E, 0x1C563963, 0x1C6C3958, 0x1C83394C, 0x1C993941, 0x1CB03936, + 0x1CC6392B, 0x1CDD391F, 0x1CF33914, 0x1D0A3909, 0x1D2038FD, 0x1D3638F2, 0x1D4D38E6, 0x1D6338DB, + 0x1D7938CF, 0x1D9038C3, 0x1DA638B8, 0x1DBC38AC, 0x1DD338A1, 0x1DE93895, 0x1DFF3889, 0x1E15387D, + 0x1E2B3871, 0x1E423866, 0x1E58385A, 0x1E6E384E, 0x1E843842, 0x1E9A3836, 0x1EB0382A, 0x1EC6381E, + 0x1EDC3812, 0x1EF23805, 0x1F0837F9, 0x1F1E37ED, 0x1F3437E1, 0x1F4A37D5, 0x1F6037C8, 0x1F7637BC, + 0x1F8C37B0, 0x1FA237A3, 0x1FB73797, 0x1FCD378A, 0x1FE3377E, 0x1FF93771, 0x200F3765, 0x20243758, + 0x203A374B, 0x2050373F, 0x20653732, 0x207B3725, 0x20913718, 0x20A6370C, 0x20BC36FF, 0x20D136F2, + 0x20E736E5, 0x20FD36D8, 0x211236CB, 0x212836BE, 0x213D36B1, 0x215336A4, 0x21683697, 0x217D368A, + 0x2193367D, 0x21A8366F, 0x21BE3662, 0x21D33655, 0x21E83648, 0x21FE363A, 0x2213362D, 0x22283620, + 0x223D3612, 0x22533605, 0x226835F7, 0x227D35EA, 0x229235DC, 0x22A735CE, 0x22BC35C1, 0x22D235B3, + 0x22E735A5, 0x22FC3598, 0x2311358A, 0x2326357C, 0x233B356E, 0x23503561, 0x23653553, 0x237A3545, + 0x238E3537, 0x23A33529, 0x23B8351B, 0x23CD350D, 0x23E234FF, 0x23F734F1, 0x240B34E2, 0x242034D4, + 0x243534C6, 0x244A34B8, 0x245E34AA, 0x2473349B, 0x2488348D, 0x249C347F, 0x24B13470, 0x24C53462, + 0x24DA3453, 0x24EF3445, 0x25033436, 0x25183428, 0x252C3419, 0x2541340B, 0x255533FC, 0x256933ED, + 0x257E33DF, 0x259233D0, 0x25A633C1, 0x25BB33B2, 0x25CF33A3, 0x25E33395, 0x25F83386, 0x260C3377, + 0x26203368, 0x26343359, 0x2648334A, 0x265C333B, 0x2671332C, 0x2685331D, 0x2699330D, 0x26AD32FE, + 0x26C132EF, 0x26D532E0, 0x26E932D0, 0x26FD32C1, 0x271132B2, 0x272432A3, 0x27383293, 0x274C3284, + 0x27603274, 0x27743265, 0x27883255, 0x279B3246, 0x27AF3236, 0x27C33227, 0x27D63217, 0x27EA3207, + 0x27FE31F8, 0x281131E8, 0x282531D8, 0x283831C8, 0x284C31B9, 0x286031A9, 0x28733199, 0x28863189, + 0x289A3179, 0x28AD3169, 0x28C13159, 0x28D43149, 0x28E73139, 0x28FB3129, 0x290E3119, 0x29213109, + 0x293530F9, 0x294830E8, 0x295B30D8, 0x296E30C8, 0x298130B8, 0x299430A7, 0x29A73097, 0x29BB3087, + 0x29CE3076, 0x29E13066, 0x29F43055, 0x2A073045, 0x2A1A3034, 0x2A2C3024, 0x2A3F3013, 0x2A523002, + 0x2A652FF2, 0x2A782FE1, 0x2A8B2FD0, 0x2A9D2FC0, 0x2AB02FAF, 0x2AC32F9E, 0x2AD62F8D, 0x2AE82F7D, + 0x2AFB2F6C, 0x2B0D2F5B, 0x2B202F4A, 0x2B332F39, 0x2B452F28, 0x2B582F17, 0x2B6A2F06, 0x2B7D2EF5, + 0x2B8F2EE4, 0x2BA12ED3, 0x2BB42EC2, 0x2BC62EB0, 0x2BD82E9F, 0x2BEB2E8E, 0x2BFD2E7D, 0x2C0F2E6B, + 0x2C212E5A, 0x2C342E49, 0x2C462E37, 0x2C582E26, 0x2C6A2E15, 0x2C7C2E03, 0x2C8E2DF2, 0x2CA02DE0, + 0x2CB22DCF, 0x2CC42DBD, 0x2CD62DAB, 0x2CE82D9A, 0x2CFA2D88, 0x2D0C2D76, 0x2D1E2D65, 0x2D2F2D53, + 0x2D412D41, 0x2D532D2F, 0x2D652D1E, 0x2D762D0C, 0x2D882CFA, 0x2D9A2CE8, 0x2DAB2CD6, 0x2DBD2CC4, + 0x2DCF2CB2, 0x2DE02CA0, 0x2DF22C8E, 0x2E032C7C, 0x2E152C6A, 0x2E262C58, 0x2E372C46, 0x2E492C34, + 0x2E5A2C21, 0x2E6B2C0F, 0x2E7D2BFD, 0x2E8E2BEB, 0x2E9F2BD8, 0x2EB02BC6, 0x2EC22BB4, 0x2ED32BA1, + 0x2EE42B8F, 0x2EF52B7D, 0x2F062B6A, 0x2F172B58, 0x2F282B45, 0x2F392B33, 0x2F4A2B20, 0x2F5B2B0D, + 0x2F6C2AFB, 0x2F7D2AE8, 0x2F8D2AD6, 0x2F9E2AC3, 0x2FAF2AB0, 0x2FC02A9D, 0x2FD02A8B, 0x2FE12A78, + 0x2FF22A65, 0x30022A52, 0x30132A3F, 0x30242A2C, 0x30342A1A, 0x30452A07, 0x305529F4, 0x306629E1, + 0x307629CE, 0x308729BB, 0x309729A7, 0x30A72994, 0x30B82981, 0x30C8296E, 0x30D8295B, 0x30E82948, + 0x30F92935, 0x31092921, 0x3119290E, 0x312928FB, 0x313928E7, 0x314928D4, 0x315928C1, 0x316928AD, + 0x3179289A, 0x31892886, 0x31992873, 0x31A92860, 0x31B9284C, 0x31C82838, 0x31D82825, 0x31E82811, + 0x31F827FE, 0x320727EA, 0x321727D6, 0x322727C3, 0x323627AF, 0x3246279B, 0x32552788, 0x32652774, + 0x32742760, 0x3284274C, 0x32932738, 0x32A32724, 0x32B22711, 0x32C126FD, 0x32D026E9, 0x32E026D5, + 0x32EF26C1, 0x32FE26AD, 0x330D2699, 0x331D2685, 0x332C2671, 0x333B265C, 0x334A2648, 0x33592634, + 0x33682620, 0x3377260C, 0x338625F8, 0x339525E3, 0x33A325CF, 0x33B225BB, 0x33C125A6, 0x33D02592, + 0x33DF257E, 0x33ED2569, 0x33FC2555, 0x340B2541, 0x3419252C, 0x34282518, 0x34362503, 0x344524EF, + 0x345324DA, 0x346224C5, 0x347024B1, 0x347F249C, 0x348D2488, 0x349B2473, 0x34AA245E, 0x34B8244A, + 0x34C62435, 0x34D42420, 0x34E2240B, 0x34F123F7, 0x34FF23E2, 0x350D23CD, 0x351B23B8, 0x352923A3, + 0x3537238E, 0x3545237A, 0x35532365, 0x35612350, 0x356E233B, 0x357C2326, 0x358A2311, 0x359822FC, + 0x35A522E7, 0x35B322D2, 0x35C122BC, 0x35CE22A7, 0x35DC2292, 0x35EA227D, 0x35F72268, 0x36052253, + 0x3612223D, 0x36202228, 0x362D2213, 0x363A21FE, 0x364821E8, 0x365521D3, 0x366221BE, 0x366F21A8, + 0x367D2193, 0x368A217D, 0x36972168, 0x36A42153, 0x36B1213D, 0x36BE2128, 0x36CB2112, 0x36D820FD, + 0x36E520E7, 0x36F220D1, 0x36FF20BC, 0x370C20A6, 0x37182091, 0x3725207B, 0x37322065, 0x373F2050, + 0x374B203A, 0x37582024, 0x3765200F, 0x37711FF9, 0x377E1FE3, 0x378A1FCD, 0x37971FB7, 0x37A31FA2, + 0x37B01F8C, 0x37BC1F76, 0x37C81F60, 0x37D51F4A, 0x37E11F34, 0x37ED1F1E, 0x37F91F08, 0x38051EF2, + 0x38121EDC, 0x381E1EC6, 0x382A1EB0, 0x38361E9A, 0x38421E84, 0x384E1E6E, 0x385A1E58, 0x38661E42, + 0x38711E2B, 0x387D1E15, 0x38891DFF, 0x38951DE9, 0x38A11DD3, 0x38AC1DBC, 0x38B81DA6, 0x38C31D90, + 0x38CF1D79, 0x38DB1D63, 0x38E61D4D, 0x38F21D36, 0x38FD1D20, 0x39091D0A, 0x39141CF3, 0x391F1CDD, + 0x392B1CC6, 0x39361CB0, 0x39411C99, 0x394C1C83, 0x39581C6C, 0x39631C56, 0x396E1C3F, 0x39791C29, + 0x39841C12, 0x398F1BFC, 0x399A1BE5, 0x39A51BCE, 0x39B01BB8, 0x39BB1BA1, 0x39C51B8A, 0x39D01B74, + 0x39DB1B5D, 0x39E61B46, 0x39F01B30, 0x39FB1B19, 0x3A061B02, 0x3A101AEB, 0x3A1B1AD4, 0x3A251ABE, + 0x3A301AA7, 0x3A3A1A90, 0x3A451A79, 0x3A4F1A62, 0x3A591A4B, 0x3A641A34, 0x3A6E1A1D, 0x3A781A06, + 0x3A8219EF, 0x3A8D19D8, 0x3A9719C1, 0x3AA119AA, 0x3AAB1993, 0x3AB5197C, 0x3ABF1965, 0x3AC9194E, + 0x3AD31937, 0x3ADD1920, 0x3AE61909, 0x3AF018F2, 0x3AFA18DB, 0x3B0418C3, 0x3B0E18AC, 0x3B171895, + 0x3B21187E, 0x3B2A1867, 0x3B34184F, 0x3B3E1838, 0x3B471821, 0x3B50180A, 0x3B5A17F2, 0x3B6317DB, + 0x3B6D17C4, 0x3B7617AC, 0x3B7F1795, 0x3B88177E, 0x3B921766, 0x3B9B174F, 0x3BA41737, 0x3BAD1720, + 0x3BB61709, 0x3BBF16F1, 0x3BC816DA, 0x3BD116C2, 0x3BDA16AB, 0x3BE31693, 0x3BEC167C, 0x3BF51664, + 0x3BFD164C, 0x3C061635, 0x3C0F161D, 0x3C171606, 0x3C2015EE, 0x3C2915D7, 0x3C3115BF, 0x3C3A15A7, + 0x3C421590, 0x3C4B1578, 0x3C531560, 0x3C5B1549, 0x3C641531, 0x3C6C1519, 0x3C741501, 0x3C7D14EA, + 0x3C8514D2, 0x3C8D14BA, 0x3C9514A2, 0x3C9D148B, 0x3CA51473, 0x3CAD145B, 0x3CB51443, 0x3CBD142B, + 0x3CC51413, 0x3CCD13FB, 0x3CD513E4, 0x3CDD13CC, 0x3CE413B4, 0x3CEC139C, 0x3CF41384, 0x3CFB136C, + 0x3D031354, 0x3D0B133C, 0x3D121324, 0x3D1A130C, 0x3D2112F4, 0x3D2812DC, 0x3D3012C4, 0x3D3712AC, + 0x3D3F1294, 0x3D46127C, 0x3D4D1264, 0x3D54124C, 0x3D5B1234, 0x3D63121C, 0x3D6A1204, 0x3D7111EB, + 0x3D7811D3, 0x3D7F11BB, 0x3D8611A3, 0x3D8D118B, 0x3D931173, 0x3D9A115A, 0x3DA11142, 0x3DA8112A, + 0x3DAF1112, 0x3DB510FA, 0x3DBC10E1, 0x3DC210C9, 0x3DC910B1, 0x3DD01099, 0x3DD61080, 0x3DDD1068, + 0x3DE31050, 0x3DE91037, 0x3DF0101F, 0x3DF61007, 0x3DFC0FEE, 0x3E030FD6, 0x3E090FBE, 0x3E0F0FA5, + 0x3E150F8D, 0x3E1B0F75, 0x3E210F5C, 0x3E270F44, 0x3E2D0F2B, 0x3E330F13, 0x3E390EFB, 0x3E3F0EE2, + 0x3E450ECA, 0x3E4A0EB1, 0x3E500E99, 0x3E560E80, 0x3E5C0E68, 0x3E610E4F, 0x3E670E37, 0x3E6C0E1E, + 0x3E720E06, 0x3E770DED, 0x3E7D0DD5, 0x3E820DBC, 0x3E880DA4, 0x3E8D0D8B, 0x3E920D72, 0x3E980D5A, + 0x3E9D0D41, 0x3EA20D29, 0x3EA70D10, 0x3EAC0CF8, 0x3EB10CDF, 0x3EB60CC6, 0x3EBB0CAE, 0x3EC00C95, + 0x3EC50C7C, 0x3ECA0C64, 0x3ECF0C4B, 0x3ED40C32, 0x3ED80C1A, 0x3EDD0C01, 0x3EE20BE8, 0x3EE70BD0, + 0x3EEB0BB7, 0x3EF00B9E, 0x3EF40B85, 0x3EF90B6D, 0x3EFD0B54, 0x3F020B3B, 0x3F060B23, 0x3F0A0B0A, + 0x3F0F0AF1, 0x3F130AD8, 0x3F170AC0, 0x3F1C0AA7, 0x3F200A8E, 0x3F240A75, 0x3F280A5C, 0x3F2C0A44, + 0x3F300A2B, 0x3F340A12, 0x3F3809F9, 0x3F3C09E0, 0x3F4009C7, 0x3F4309AF, 0x3F470996, 0x3F4B097D, + 0x3F4F0964, 0x3F52094B, 0x3F560932, 0x3F5A0919, 0x3F5D0901, 0x3F6108E8, 0x3F6408CF, 0x3F6808B6, + 0x3F6B089D, 0x3F6E0884, 0x3F72086B, 0x3F750852, 0x3F780839, 0x3F7B0820, 0x3F7F0807, 0x3F8207EF, + 0x3F8507D6, 0x3F8807BD, 0x3F8B07A4, 0x3F8E078B, 0x3F910772, 0x3F940759, 0x3F970740, 0x3F990727, + 0x3F9C070E, 0x3F9F06F5, 0x3FA206DC, 0x3FA406C3, 0x3FA706AA, 0x3FAA0691, 0x3FAC0678, 0x3FAF065F, + 0x3FB10646, 0x3FB4062D, 0x3FB60614, 0x3FB805FB, 0x3FBB05E2, 0x3FBD05C9, 0x3FBF05B0, 0x3FC10597, + 0x3FC4057E, 0x3FC60565, 0x3FC8054C, 0x3FCA0533, 0x3FCC051A, 0x3FCE0500, 0x3FD004E7, 0x3FD204CE, + 0x3FD404B5, 0x3FD5049C, 0x3FD70483, 0x3FD9046A, 0x3FDB0451, 0x3FDC0438, 0x3FDE041F, 0x3FE00406, + 0x3FE103ED, 0x3FE303D4, 0x3FE403BB, 0x3FE603A1, 0x3FE70388, 0x3FE8036F, 0x3FEA0356, 0x3FEB033D, + 0x3FEC0324, 0x3FED030B, 0x3FEF02F2, 0x3FF002D9, 0x3FF102C0, 0x3FF202A6, 0x3FF3028D, 0x3FF40274, + 0x3FF5025B, 0x3FF60242, 0x3FF70229, 0x3FF70210, 0x3FF801F7, 0x3FF901DD, 0x3FFA01C4, 0x3FFA01AB, + 0x3FFB0192, 0x3FFC0179, 0x3FFC0160, 0x3FFD0147, 0x3FFD012E, 0x3FFE0114, 0x3FFE00FB, 0x3FFE00E2, + 0x3FFF00C9, 0x3FFF00B0, 0x3FFF0097, 0x4000007E, 0x40000065, 0x4000004B, 0x40000032, 0x40000019, + 0x40000000, 0x4000FFE7, 0x4000FFCE, 0x4000FFB5, 0x4000FF9B, 0x4000FF82, 0x3FFFFF69, 0x3FFFFF50, + 0x3FFFFF37, 0x3FFEFF1E, 0x3FFEFF05, 0x3FFEFEEC, 0x3FFDFED2, 0x3FFDFEB9, 0x3FFCFEA0, 0x3FFCFE87, + 0x3FFBFE6E, 0x3FFAFE55, 0x3FFAFE3C, 0x3FF9FE23, 0x3FF8FE09, 0x3FF7FDF0, 0x3FF7FDD7, 0x3FF6FDBE, + 0x3FF5FDA5, 0x3FF4FD8C, 0x3FF3FD73, 0x3FF2FD5A, 0x3FF1FD40, 0x3FF0FD27, 0x3FEFFD0E, 0x3FEDFCF5, + 0x3FECFCDC, 0x3FEBFCC3, 0x3FEAFCAA, 0x3FE8FC91, 0x3FE7FC78, 0x3FE6FC5F, 0x3FE4FC45, 0x3FE3FC2C, + 0x3FE1FC13, 0x3FE0FBFA, 0x3FDEFBE1, 0x3FDCFBC8, 0x3FDBFBAF, 0x3FD9FB96, 0x3FD7FB7D, 0x3FD5FB64, + 0x3FD4FB4B, 0x3FD2FB32, 0x3FD0FB19, 0x3FCEFB00, 0x3FCCFAE6, 0x3FCAFACD, 0x3FC8FAB4, 0x3FC6FA9B, + 0x3FC4FA82, 0x3FC1FA69, 0x3FBFFA50, 0x3FBDFA37, 0x3FBBFA1E, 0x3FB8FA05, 0x3FB6F9EC, 0x3FB4F9D3, + 0x3FB1F9BA, 0x3FAFF9A1, 0x3FACF988, 0x3FAAF96F, 0x3FA7F956, 0x3FA4F93D, 0x3FA2F924, 0x3F9FF90B, + 0x3F9CF8F2, 0x3F99F8D9, 0x3F97F8C0, 0x3F94F8A7, 0x3F91F88E, 0x3F8EF875, 0x3F8BF85C, 0x3F88F843, + 0x3F85F82A, 0x3F82F811, 0x3F7FF7F9, 0x3F7BF7E0, 0x3F78F7C7, 0x3F75F7AE, 0x3F72F795, 0x3F6EF77C, + 0x3F6BF763, 0x3F68F74A, 0x3F64F731, 0x3F61F718, 0x3F5DF6FF, 0x3F5AF6E7, 0x3F56F6CE, 0x3F52F6B5, + 0x3F4FF69C, 0x3F4BF683, 0x3F47F66A, 0x3F43F651, 0x3F40F639, 0x3F3CF620, 0x3F38F607, 0x3F34F5EE, + 0x3F30F5D5, 0x3F2CF5BC, 0x3F28F5A4, 0x3F24F58B, 0x3F20F572, 0x3F1CF559, 0x3F17F540, 0x3F13F528, + 0x3F0FF50F, 0x3F0AF4F6, 0x3F06F4DD, 0x3F02F4C5, 0x3EFDF4AC, 0x3EF9F493, 0x3EF4F47B, 0x3EF0F462, + 0x3EEBF449, 0x3EE7F430, 0x3EE2F418, 0x3EDDF3FF, 0x3ED8F3E6, 0x3ED4F3CE, 0x3ECFF3B5, 0x3ECAF39C, + 0x3EC5F384, 0x3EC0F36B, 0x3EBBF352, 0x3EB6F33A, 0x3EB1F321, 0x3EACF308, 0x3EA7F2F0, 0x3EA2F2D7, + 0x3E9DF2BF, 0x3E98F2A6, 0x3E92F28E, 0x3E8DF275, 0x3E88F25C, 0x3E82F244, 0x3E7DF22B, 0x3E77F213, + 0x3E72F1FA, 0x3E6CF1E2, 0x3E67F1C9, 0x3E61F1B1, 0x3E5CF198, 0x3E56F180, 0x3E50F167, 0x3E4AF14F, + 0x3E45F136, 0x3E3FF11E, 0x3E39F105, 0x3E33F0ED, 0x3E2DF0D5, 0x3E27F0BC, 0x3E21F0A4, 0x3E1BF08B, + 0x3E15F073, 0x3E0FF05B, 0x3E09F042, 0x3E03F02A, 0x3DFCF012, 0x3DF6EFF9, 0x3DF0EFE1, 0x3DE9EFC9, + 0x3DE3EFB0, 0x3DDDEF98, 0x3DD6EF80, 0x3DD0EF67, 0x3DC9EF4F, 0x3DC2EF37, 0x3DBCEF1F, 0x3DB5EF06, + 0x3DAFEEEE, 0x3DA8EED6, 0x3DA1EEBE, 0x3D9AEEA6, 0x3D93EE8D, 0x3D8DEE75, 0x3D86EE5D, 0x3D7FEE45, + 0x3D78EE2D, 0x3D71EE15, 0x3D6AEDFC, 0x3D63EDE4, 0x3D5BEDCC, 0x3D54EDB4, 0x3D4DED9C, 0x3D46ED84, + 0x3D3FED6C, 0x3D37ED54, 0x3D30ED3C, 0x3D28ED24, 0x3D21ED0C, 0x3D1AECF4, 0x3D12ECDC, 0x3D0BECC4, + 0x3D03ECAC, 0x3CFBEC94, 0x3CF4EC7C, 0x3CECEC64, 0x3CE4EC4C, 0x3CDDEC34, 0x3CD5EC1C, 0x3CCDEC05, + 0x3CC5EBED, 0x3CBDEBD5, 0x3CB5EBBD, 0x3CADEBA5, 0x3CA5EB8D, 0x3C9DEB75, 0x3C95EB5E, 0x3C8DEB46, + 0x3C85EB2E, 0x3C7DEB16, 0x3C74EAFF, 0x3C6CEAE7, 0x3C64EACF, 0x3C5BEAB7, 0x3C53EAA0, 0x3C4BEA88, + 0x3C42EA70, 0x3C3AEA59, 0x3C31EA41, 0x3C29EA29, 0x3C20EA12, 0x3C17E9FA, 0x3C0FE9E3, 0x3C06E9CB, + 0x3BFDE9B4, 0x3BF5E99C, 0x3BECE984, 0x3BE3E96D, 0x3BDAE955, 0x3BD1E93E, 0x3BC8E926, 0x3BBFE90F, + 0x3BB6E8F7, 0x3BADE8E0, 0x3BA4E8C9, 0x3B9BE8B1, 0x3B92E89A, 0x3B88E882, 0x3B7FE86B, 0x3B76E854, + 0x3B6DE83C, 0x3B63E825, 0x3B5AE80E, 0x3B50E7F6, 0x3B47E7DF, 0x3B3EE7C8, 0x3B34E7B1, 0x3B2AE799, + 0x3B21E782, 0x3B17E76B, 0x3B0EE754, 0x3B04E73D, 0x3AFAE725, 0x3AF0E70E, 0x3AE6E6F7, 0x3ADDE6E0, + 0x3AD3E6C9, 0x3AC9E6B2, 0x3ABFE69B, 0x3AB5E684, 0x3AABE66D, 0x3AA1E656, 0x3A97E63F, 0x3A8DE628, + 0x3A82E611, 0x3A78E5FA, 0x3A6EE5E3, 0x3A64E5CC, 0x3A59E5B5, 0x3A4FE59E, 0x3A45E587, 0x3A3AE570, + 0x3A30E559, 0x3A25E542, 0x3A1BE52C, 0x3A10E515, 0x3A06E4FE, 0x39FBE4E7, 0x39F0E4D0, 0x39E6E4BA, + 0x39DBE4A3, 0x39D0E48C, 0x39C5E476, 0x39BBE45F, 0x39B0E448, 0x39A5E432, 0x399AE41B, 0x398FE404, + 0x3984E3EE, 0x3979E3D7, 0x396EE3C1, 0x3963E3AA, 0x3958E394, 0x394CE37D, 0x3941E367, 0x3936E350, + 0x392BE33A, 0x391FE323, 0x3914E30D, 0x3909E2F6, 0x38FDE2E0, 0x38F2E2CA, 0x38E6E2B3, 0x38DBE29D, + 0x38CFE287, 0x38C3E270, 0x38B8E25A, 0x38ACE244, 0x38A1E22D, 0x3895E217, 0x3889E201, 0x387DE1EB, + 0x3871E1D5, 0x3866E1BE, 0x385AE1A8, 0x384EE192, 0x3842E17C, 0x3836E166, 0x382AE150, 0x381EE13A, + 0x3812E124, 0x3805E10E, 0x37F9E0F8, 0x37EDE0E2, 0x37E1E0CC, 0x37D5E0B6, 0x37C8E0A0, 0x37BCE08A, + 0x37B0E074, 0x37A3E05E, 0x3797E049, 0x378AE033, 0x377EE01D, 0x3771E007, 0x3765DFF1, 0x3758DFDC, + 0x374BDFC6, 0x373FDFB0, 0x3732DF9B, 0x3725DF85, 0x3718DF6F, 0x370CDF5A, 0x36FFDF44, 0x36F2DF2F, + 0x36E5DF19, 0x36D8DF03, 0x36CBDEEE, 0x36BEDED8, 0x36B1DEC3, 0x36A4DEAD, 0x3697DE98, 0x368ADE83, + 0x367DDE6D, 0x366FDE58, 0x3662DE42, 0x3655DE2D, 0x3648DE18, 0x363ADE02, 0x362DDDED, 0x3620DDD8, + 0x3612DDC3, 0x3605DDAD, 0x35F7DD98, 0x35EADD83, 0x35DCDD6E, 0x35CEDD59, 0x35C1DD44, 0x35B3DD2E, + 0x35A5DD19, 0x3598DD04, 0x358ADCEF, 0x357CDCDA, 0x356EDCC5, 0x3561DCB0, 0x3553DC9B, 0x3545DC86, + 0x3537DC72, 0x3529DC5D, 0x351BDC48, 0x350DDC33, 0x34FFDC1E, 0x34F1DC09, 0x34E2DBF5, 0x34D4DBE0, + 0x34C6DBCB, 0x34B8DBB6, 0x34AADBA2, 0x349BDB8D, 0x348DDB78, 0x347FDB64, 0x3470DB4F, 0x3462DB3B, + 0x3453DB26, 0x3445DB11, 0x3436DAFD, 0x3428DAE8, 0x3419DAD4, 0x340BDABF, 0x33FCDAAB, 0x33EDDA97, + 0x33DFDA82, 0x33D0DA6E, 0x33C1DA5A, 0x33B2DA45, 0x33A3DA31, 0x3395DA1D, 0x3386DA08, 0x3377D9F4, + 0x3368D9E0, 0x3359D9CC, 0x334AD9B8, 0x333BD9A4, 0x332CD98F, 0x331DD97B, 0x330DD967, 0x32FED953, + 0x32EFD93F, 0x32E0D92B, 0x32D0D917, 0x32C1D903, 0x32B2D8EF, 0x32A3D8DC, 0x3293D8C8, 0x3284D8B4, + 0x3274D8A0, 0x3265D88C, 0x3255D878, 0x3246D865, 0x3236D851, 0x3227D83D, 0x3217D82A, 0x3207D816, + 0x31F8D802, 0x31E8D7EF, 0x31D8D7DB, 0x31C8D7C8, 0x31B9D7B4, 0x31A9D7A0, 0x3199D78D, 0x3189D77A, + 0x3179D766, 0x3169D753, 0x3159D73F, 0x3149D72C, 0x3139D719, 0x3129D705, 0x3119D6F2, 0x3109D6DF, + 0x30F9D6CB, 0x30E8D6B8, 0x30D8D6A5, 0x30C8D692, 0x30B8D67F, 0x30A7D66C, 0x3097D659, 0x3087D645, + 0x3076D632, 0x3066D61F, 0x3055D60C, 0x3045D5F9, 0x3034D5E6, 0x3024D5D4, 0x3013D5C1, 0x3002D5AE, + 0x2FF2D59B, 0x2FE1D588, 0x2FD0D575, 0x2FC0D563, 0x2FAFD550, 0x2F9ED53D, 0x2F8DD52A, 0x2F7DD518, + 0x2F6CD505, 0x2F5BD4F3, 0x2F4AD4E0, 0x2F39D4CD, 0x2F28D4BB, 0x2F17D4A8, 0x2F06D496, 0x2EF5D483, + 0x2EE4D471, 0x2ED3D45F, 0x2EC2D44C, 0x2EB0D43A, 0x2E9FD428, 0x2E8ED415, 0x2E7DD403, 0x2E6BD3F1, + 0x2E5AD3DF, 0x2E49D3CC, 0x2E37D3BA, 0x2E26D3A8, 0x2E15D396, 0x2E03D384, 0x2DF2D372, 0x2DE0D360, + 0x2DCFD34E, 0x2DBDD33C, 0x2DABD32A, 0x2D9AD318, 0x2D88D306, 0x2D76D2F4, 0x2D65D2E2, 0x2D53D2D1, + 0x2D41D2BF, 0x2D2FD2AD, 0x2D1ED29B, 0x2D0CD28A, 0x2CFAD278, 0x2CE8D266, 0x2CD6D255, 0x2CC4D243, + 0x2CB2D231, 0x2CA0D220, 0x2C8ED20E, 0x2C7CD1FD, 0x2C6AD1EB, 0x2C58D1DA, 0x2C46D1C9, 0x2C34D1B7, + 0x2C21D1A6, 0x2C0FD195, 0x2BFDD183, 0x2BEBD172, 0x2BD8D161, 0x2BC6D150, 0x2BB4D13E, 0x2BA1D12D, + 0x2B8FD11C, 0x2B7DD10B, 0x2B6AD0FA, 0x2B58D0E9, 0x2B45D0D8, 0x2B33D0C7, 0x2B20D0B6, 0x2B0DD0A5, + 0x2AFBD094, 0x2AE8D083, 0x2AD6D073, 0x2AC3D062, 0x2AB0D051, 0x2A9DD040, 0x2A8BD030, 0x2A78D01F, + 0x2A65D00E, 0x2A52CFFE, 0x2A3FCFED, 0x2A2CCFDC, 0x2A1ACFCC, 0x2A07CFBB, 0x29F4CFAB, 0x29E1CF9A, + 0x29CECF8A, 0x29BBCF79, 0x29A7CF69, 0x2994CF59, 0x2981CF48, 0x296ECF38, 0x295BCF28, 0x2948CF18, + 0x2935CF07, 0x2921CEF7, 0x290ECEE7, 0x28FBCED7, 0x28E7CEC7, 0x28D4CEB7, 0x28C1CEA7, 0x28ADCE97, + 0x289ACE87, 0x2886CE77, 0x2873CE67, 0x2860CE57, 0x284CCE47, 0x2838CE38, 0x2825CE28, 0x2811CE18, + 0x27FECE08, 0x27EACDF9, 0x27D6CDE9, 0x27C3CDD9, 0x27AFCDCA, 0x279BCDBA, 0x2788CDAB, 0x2774CD9B, + 0x2760CD8C, 0x274CCD7C, 0x2738CD6D, 0x2724CD5D, 0x2711CD4E, 0x26FDCD3F, 0x26E9CD30, 0x26D5CD20, + 0x26C1CD11, 0x26ADCD02, 0x2699CCF3, 0x2685CCE3, 0x2671CCD4, 0x265CCCC5, 0x2648CCB6, 0x2634CCA7, + 0x2620CC98, 0x260CCC89, 0x25F8CC7A, 0x25E3CC6B, 0x25CFCC5D, 0x25BBCC4E, 0x25A6CC3F, 0x2592CC30, + 0x257ECC21, 0x2569CC13, 0x2555CC04, 0x2541CBF5, 0x252CCBE7, 0x2518CBD8, 0x2503CBCA, 0x24EFCBBB, + 0x24DACBAD, 0x24C5CB9E, 0x24B1CB90, 0x249CCB81, 0x2488CB73, 0x2473CB65, 0x245ECB56, 0x244ACB48, + 0x2435CB3A, 0x2420CB2C, 0x240BCB1E, 0x23F7CB0F, 0x23E2CB01, 0x23CDCAF3, 0x23B8CAE5, 0x23A3CAD7, + 0x238ECAC9, 0x237ACABB, 0x2365CAAD, 0x2350CA9F, 0x233BCA92, 0x2326CA84, 0x2311CA76, 0x22FCCA68, + 0x22E7CA5B, 0x22D2CA4D, 0x22BCCA3F, 0x22A7CA32, 0x2292CA24, 0x227DCA16, 0x2268CA09, 0x2253C9FB, + 0x223DC9EE, 0x2228C9E0, 0x2213C9D3, 0x21FEC9C6, 0x21E8C9B8, 0x21D3C9AB, 0x21BEC99E, 0x21A8C991, + 0x2193C983, 0x217DC976, 0x2168C969, 0x2153C95C, 0x213DC94F, 0x2128C942, 0x2112C935, 0x20FDC928, + 0x20E7C91B, 0x20D1C90E, 0x20BCC901, 0x20A6C8F4, 0x2091C8E8, 0x207BC8DB, 0x2065C8CE, 0x2050C8C1, + 0x203AC8B5, 0x2024C8A8, 0x200FC89B, 0x1FF9C88F, 0x1FE3C882, 0x1FCDC876, 0x1FB7C869, 0x1FA2C85D, + 0x1F8CC850, 0x1F76C844, 0x1F60C838, 0x1F4AC82B, 0x1F34C81F, 0x1F1EC813, 0x1F08C807, 0x1EF2C7FB, + 0x1EDCC7EE, 0x1EC6C7E2, 0x1EB0C7D6, 0x1E9AC7CA, 0x1E84C7BE, 0x1E6EC7B2, 0x1E58C7A6, 0x1E42C79A, + 0x1E2BC78F, 0x1E15C783, 0x1DFFC777, 0x1DE9C76B, 0x1DD3C75F, 0x1DBCC754, 0x1DA6C748, 0x1D90C73D, + 0x1D79C731, 0x1D63C725, 0x1D4DC71A, 0x1D36C70E, 0x1D20C703, 0x1D0AC6F7, 0x1CF3C6EC, 0x1CDDC6E1, + 0x1CC6C6D5, 0x1CB0C6CA, 0x1C99C6BF, 0x1C83C6B4, 0x1C6CC6A8, 0x1C56C69D, 0x1C3FC692, 0x1C29C687, + 0x1C12C67C, 0x1BFCC671, 0x1BE5C666, 0x1BCEC65B, 0x1BB8C650, 0x1BA1C645, 0x1B8AC63B, 0x1B74C630, + 0x1B5DC625, 0x1B46C61A, 0x1B30C610, 0x1B19C605, 0x1B02C5FA, 0x1AEBC5F0, 0x1AD4C5E5, 0x1ABEC5DB, + 0x1AA7C5D0, 0x1A90C5C6, 0x1A79C5BB, 0x1A62C5B1, 0x1A4BC5A7, 0x1A34C59C, 0x1A1DC592, 0x1A06C588, + 0x19EFC57E, 0x19D8C573, 0x19C1C569, 0x19AAC55F, 0x1993C555, 0x197CC54B, 0x1965C541, 0x194EC537, + 0x1937C52D, 0x1920C523, 0x1909C51A, 0x18F2C510, 0x18DBC506, 0x18C3C4FC, 0x18ACC4F2, 0x1895C4E9, + 0x187EC4DF, 0x1867C4D6, 0x184FC4CC, 0x1838C4C2, 0x1821C4B9, 0x180AC4B0, 0x17F2C4A6, 0x17DBC49D, + 0x17C4C493, 0x17ACC48A, 0x1795C481, 0x177EC478, 0x1766C46E, 0x174FC465, 0x1737C45C, 0x1720C453, + 0x1709C44A, 0x16F1C441, 0x16DAC438, 0x16C2C42F, 0x16ABC426, 0x1693C41D, 0x167CC414, 0x1664C40B, + 0x164CC403, 0x1635C3FA, 0x161DC3F1, 0x1606C3E9, 0x15EEC3E0, 0x15D7C3D7, 0x15BFC3CF, 0x15A7C3C6, + 0x1590C3BE, 0x1578C3B5, 0x1560C3AD, 0x1549C3A5, 0x1531C39C, 0x1519C394, 0x1501C38C, 0x14EAC383, + 0x14D2C37B, 0x14BAC373, 0x14A2C36B, 0x148BC363, 0x1473C35B, 0x145BC353, 0x1443C34B, 0x142BC343, + 0x1413C33B, 0x13FBC333, 0x13E4C32B, 0x13CCC323, 0x13B4C31C, 0x139CC314, 0x1384C30C, 0x136CC305, + 0x1354C2FD, 0x133CC2F5, 0x1324C2EE, 0x130CC2E6, 0x12F4C2DF, 0x12DCC2D8, 0x12C4C2D0, 0x12ACC2C9, + 0x1294C2C1, 0x127CC2BA, 0x1264C2B3, 0x124CC2AC, 0x1234C2A5, 0x121CC29D, 0x1204C296, 0x11EBC28F, + 0x11D3C288, 0x11BBC281, 0x11A3C27A, 0x118BC273, 0x1173C26D, 0x115AC266, 0x1142C25F, 0x112AC258, + 0x1112C251, 0x10FAC24B, 0x10E1C244, 0x10C9C23E, 0x10B1C237, 0x1099C230, 0x1080C22A, 0x1068C223, + 0x1050C21D, 0x1037C217, 0x101FC210, 0x1007C20A, 0x0FEEC204, 0x0FD6C1FD, 0x0FBEC1F7, 0x0FA5C1F1, + 0x0F8DC1EB, 0x0F75C1E5, 0x0F5CC1DF, 0x0F44C1D9, 0x0F2BC1D3, 0x0F13C1CD, 0x0EFBC1C7, 0x0EE2C1C1, + 0x0ECAC1BB, 0x0EB1C1B6, 0x0E99C1B0, 0x0E80C1AA, 0x0E68C1A4, 0x0E4FC19F, 0x0E37C199, 0x0E1EC194, + 0x0E06C18E, 0x0DEDC189, 0x0DD5C183, 0x0DBCC17E, 0x0DA4C178, 0x0D8BC173, 0x0D72C16E, 0x0D5AC168, + 0x0D41C163, 0x0D29C15E, 0x0D10C159, 0x0CF8C154, 0x0CDFC14F, 0x0CC6C14A, 0x0CAEC145, 0x0C95C140, + 0x0C7CC13B, 0x0C64C136, 0x0C4BC131, 0x0C32C12C, 0x0C1AC128, 0x0C01C123, 0x0BE8C11E, 0x0BD0C119, + 0x0BB7C115, 0x0B9EC110, 0x0B85C10C, 0x0B6DC107, 0x0B54C103, 0x0B3BC0FE, 0x0B23C0FA, 0x0B0AC0F6, + 0x0AF1C0F1, 0x0AD8C0ED, 0x0AC0C0E9, 0x0AA7C0E4, 0x0A8EC0E0, 0x0A75C0DC, 0x0A5CC0D8, 0x0A44C0D4, + 0x0A2BC0D0, 0x0A12C0CC, 0x09F9C0C8, 0x09E0C0C4, 0x09C7C0C0, 0x09AFC0BD, 0x0996C0B9, 0x097DC0B5, + 0x0964C0B1, 0x094BC0AE, 0x0932C0AA, 0x0919C0A6, 0x0901C0A3, 0x08E8C09F, 0x08CFC09C, 0x08B6C098, + 0x089DC095, 0x0884C092, 0x086BC08E, 0x0852C08B, 0x0839C088, 0x0820C085, 0x0807C081, 0x07EFC07E, + 0x07D6C07B, 0x07BDC078, 0x07A4C075, 0x078BC072, 0x0772C06F, 0x0759C06C, 0x0740C069, 0x0727C067, + 0x070EC064, 0x06F5C061, 0x06DCC05E, 0x06C3C05C, 0x06AAC059, 0x0691C056, 0x0678C054, 0x065FC051, + 0x0646C04F, 0x062DC04C, 0x0614C04A, 0x05FBC048, 0x05E2C045, 0x05C9C043, 0x05B0C041, 0x0597C03F, + 0x057EC03C, 0x0565C03A, 0x054CC038, 0x0533C036, 0x051AC034, 0x0500C032, 0x04E7C030, 0x04CEC02E, + 0x04B5C02C, 0x049CC02B, 0x0483C029, 0x046AC027, 0x0451C025, 0x0438C024, 0x041FC022, 0x0406C020, + 0x03EDC01F, 0x03D4C01D, 0x03BBC01C, 0x03A1C01A, 0x0388C019, 0x036FC018, 0x0356C016, 0x033DC015, + 0x0324C014, 0x030BC013, 0x02F2C011, 0x02D9C010, 0x02C0C00F, 0x02A6C00E, 0x028DC00D, 0x0274C00C, + 0x025BC00B, 0x0242C00A, 0x0229C009, 0x0210C009, 0x01F7C008, 0x01DDC007, 0x01C4C006, 0x01ABC006, + 0x0192C005, 0x0179C004, 0x0160C004, 0x0147C003, 0x012EC003, 0x0114C002, 0x00FBC002, 0x00E2C002, + 0x00C9C001, 0x00B0C001, 0x0097C001, 0x007EC000, 0x0065C000, 0x004BC000, 0x0032C000, 0x0019C000, + 0x0000C000, 0xFFE7C000, 0xFFCEC000, 0xFFB5C000, 0xFF9BC000, 0xFF82C000, 0xFF69C001, 0xFF50C001, + 0xFF37C001, 0xFF1EC002, 0xFF05C002, 0xFEECC002, 0xFED2C003, 0xFEB9C003, 0xFEA0C004, 0xFE87C004, + 0xFE6EC005, 0xFE55C006, 0xFE3CC006, 0xFE23C007, 0xFE09C008, 0xFDF0C009, 0xFDD7C009, 0xFDBEC00A, + 0xFDA5C00B, 0xFD8CC00C, 0xFD73C00D, 0xFD5AC00E, 0xFD40C00F, 0xFD27C010, 0xFD0EC011, 0xFCF5C013, + 0xFCDCC014, 0xFCC3C015, 0xFCAAC016, 0xFC91C018, 0xFC78C019, 0xFC5FC01A, 0xFC45C01C, 0xFC2CC01D, + 0xFC13C01F, 0xFBFAC020, 0xFBE1C022, 0xFBC8C024, 0xFBAFC025, 0xFB96C027, 0xFB7DC029, 0xFB64C02B, + 0xFB4BC02C, 0xFB32C02E, 0xFB19C030, 0xFB00C032, 0xFAE6C034, 0xFACDC036, 0xFAB4C038, 0xFA9BC03A, + 0xFA82C03C, 0xFA69C03F, 0xFA50C041, 0xFA37C043, 0xFA1EC045, 0xFA05C048, 0xF9ECC04A, 0xF9D3C04C, + 0xF9BAC04F, 0xF9A1C051, 0xF988C054, 0xF96FC056, 0xF956C059, 0xF93DC05C, 0xF924C05E, 0xF90BC061, + 0xF8F2C064, 0xF8D9C067, 0xF8C0C069, 0xF8A7C06C, 0xF88EC06F, 0xF875C072, 0xF85CC075, 0xF843C078, + 0xF82AC07B, 0xF811C07E, 0xF7F9C081, 0xF7E0C085, 0xF7C7C088, 0xF7AEC08B, 0xF795C08E, 0xF77CC092, + 0xF763C095, 0xF74AC098, 0xF731C09C, 0xF718C09F, 0xF6FFC0A3, 0xF6E7C0A6, 0xF6CEC0AA, 0xF6B5C0AE, + 0xF69CC0B1, 0xF683C0B5, 0xF66AC0B9, 0xF651C0BD, 0xF639C0C0, 0xF620C0C4, 0xF607C0C8, 0xF5EEC0CC, + 0xF5D5C0D0, 0xF5BCC0D4, 0xF5A4C0D8, 0xF58BC0DC, 0xF572C0E0, 0xF559C0E4, 0xF540C0E9, 0xF528C0ED, + 0xF50FC0F1, 0xF4F6C0F6, 0xF4DDC0FA, 0xF4C5C0FE, 0xF4ACC103, 0xF493C107, 0xF47BC10C, 0xF462C110, + 0xF449C115, 0xF430C119, 0xF418C11E, 0xF3FFC123, 0xF3E6C128, 0xF3CEC12C, 0xF3B5C131, 0xF39CC136, + 0xF384C13B, 0xF36BC140, 0xF352C145, 0xF33AC14A, 0xF321C14F, 0xF308C154, 0xF2F0C159, 0xF2D7C15E, + 0xF2BFC163, 0xF2A6C168, 0xF28EC16E, 0xF275C173, 0xF25CC178, 0xF244C17E, 0xF22BC183, 0xF213C189, + 0xF1FAC18E, 0xF1E2C194, 0xF1C9C199, 0xF1B1C19F, 0xF198C1A4, 0xF180C1AA, 0xF167C1B0, 0xF14FC1B6, + 0xF136C1BB, 0xF11EC1C1, 0xF105C1C7, 0xF0EDC1CD, 0xF0D5C1D3, 0xF0BCC1D9, 0xF0A4C1DF, 0xF08BC1E5, + 0xF073C1EB, 0xF05BC1F1, 0xF042C1F7, 0xF02AC1FD, 0xF012C204, 0xEFF9C20A, 0xEFE1C210, 0xEFC9C217, + 0xEFB0C21D, 0xEF98C223, 0xEF80C22A, 0xEF67C230, 0xEF4FC237, 0xEF37C23E, 0xEF1FC244, 0xEF06C24B, + 0xEEEEC251, 0xEED6C258, 0xEEBEC25F, 0xEEA6C266, 0xEE8DC26D, 0xEE75C273, 0xEE5DC27A, 0xEE45C281, + 0xEE2DC288, 0xEE15C28F, 0xEDFCC296, 0xEDE4C29D, 0xEDCCC2A5, 0xEDB4C2AC, 0xED9CC2B3, 0xED84C2BA, + 0xED6CC2C1, 0xED54C2C9, 0xED3CC2D0, 0xED24C2D8, 0xED0CC2DF, 0xECF4C2E6, 0xECDCC2EE, 0xECC4C2F5, + 0xECACC2FD, 0xEC94C305, 0xEC7CC30C, 0xEC64C314, 0xEC4CC31C, 0xEC34C323, 0xEC1CC32B, 0xEC05C333, + 0xEBEDC33B, 0xEBD5C343, 0xEBBDC34B, 0xEBA5C353, 0xEB8DC35B, 0xEB75C363, 0xEB5EC36B, 0xEB46C373, + 0xEB2EC37B, 0xEB16C383, 0xEAFFC38C, 0xEAE7C394, 0xEACFC39C, 0xEAB7C3A5, 0xEAA0C3AD, 0xEA88C3B5, + 0xEA70C3BE, 0xEA59C3C6, 0xEA41C3CF, 0xEA29C3D7, 0xEA12C3E0, 0xE9FAC3E9, 0xE9E3C3F1, 0xE9CBC3FA, + 0xE9B4C403, 0xE99CC40B, 0xE984C414, 0xE96DC41D, 0xE955C426, 0xE93EC42F, 0xE926C438, 0xE90FC441, + 0xE8F7C44A, 0xE8E0C453, 0xE8C9C45C, 0xE8B1C465, 0xE89AC46E, 0xE882C478, 0xE86BC481, 0xE854C48A, + 0xE83CC493, 0xE825C49D, 0xE80EC4A6, 0xE7F6C4B0, 0xE7DFC4B9, 0xE7C8C4C2, 0xE7B1C4CC, 0xE799C4D6, + 0xE782C4DF, 0xE76BC4E9, 0xE754C4F2, 0xE73DC4FC, 0xE725C506, 0xE70EC510, 0xE6F7C51A, 0xE6E0C523, + 0xE6C9C52D, 0xE6B2C537, 0xE69BC541, 0xE684C54B, 0xE66DC555, 0xE656C55F, 0xE63FC569, 0xE628C573, + 0xE611C57E, 0xE5FAC588, 0xE5E3C592, 0xE5CCC59C, 0xE5B5C5A7, 0xE59EC5B1, 0xE587C5BB, 0xE570C5C6, + 0xE559C5D0, 0xE542C5DB, 0xE52CC5E5, 0xE515C5F0, 0xE4FEC5FA, 0xE4E7C605, 0xE4D0C610, 0xE4BAC61A, + 0xE4A3C625, 0xE48CC630, 0xE476C63B, 0xE45FC645, 0xE448C650, 0xE432C65B, 0xE41BC666, 0xE404C671, + 0xE3EEC67C, 0xE3D7C687, 0xE3C1C692, 0xE3AAC69D, 0xE394C6A8, 0xE37DC6B4, 0xE367C6BF, 0xE350C6CA, + 0xE33AC6D5, 0xE323C6E1, 0xE30DC6EC, 0xE2F6C6F7, 0xE2E0C703, 0xE2CAC70E, 0xE2B3C71A, 0xE29DC725, + 0xE287C731, 0xE270C73D, 0xE25AC748, 0xE244C754, 0xE22DC75F, 0xE217C76B, 0xE201C777, 0xE1EBC783, + 0xE1D5C78F, 0xE1BEC79A, 0xE1A8C7A6, 0xE192C7B2, 0xE17CC7BE, 0xE166C7CA, 0xE150C7D6, 0xE13AC7E2, + 0xE124C7EE, 0xE10EC7FB, 0xE0F8C807, 0xE0E2C813, 0xE0CCC81F, 0xE0B6C82B, 0xE0A0C838, 0xE08AC844, + 0xE074C850, 0xE05EC85D, 0xE049C869, 0xE033C876, 0xE01DC882, 0xE007C88F, 0xDFF1C89B, 0xDFDCC8A8, + 0xDFC6C8B5, 0xDFB0C8C1, 0xDF9BC8CE, 0xDF85C8DB, 0xDF6FC8E8, 0xDF5AC8F4, 0xDF44C901, 0xDF2FC90E, + 0xDF19C91B, 0xDF03C928, 0xDEEEC935, 0xDED8C942, 0xDEC3C94F, 0xDEADC95C, 0xDE98C969, 0xDE83C976, + 0xDE6DC983, 0xDE58C991, 0xDE42C99E, 0xDE2DC9AB, 0xDE18C9B8, 0xDE02C9C6, 0xDDEDC9D3, 0xDDD8C9E0, + 0xDDC3C9EE, 0xDDADC9FB, 0xDD98CA09, 0xDD83CA16, 0xDD6ECA24, 0xDD59CA32, 0xDD44CA3F, 0xDD2ECA4D, + 0xDD19CA5B, 0xDD04CA68, 0xDCEFCA76, 0xDCDACA84, 0xDCC5CA92, 0xDCB0CA9F, 0xDC9BCAAD, 0xDC86CABB, + 0xDC72CAC9, 0xDC5DCAD7, 0xDC48CAE5, 0xDC33CAF3, 0xDC1ECB01, 0xDC09CB0F, 0xDBF5CB1E, 0xDBE0CB2C, + 0xDBCBCB3A, 0xDBB6CB48, 0xDBA2CB56, 0xDB8DCB65, 0xDB78CB73, 0xDB64CB81, 0xDB4FCB90, 0xDB3BCB9E, + 0xDB26CBAD, 0xDB11CBBB, 0xDAFDCBCA, 0xDAE8CBD8, 0xDAD4CBE7, 0xDABFCBF5, 0xDAABCC04, 0xDA97CC13, + 0xDA82CC21, 0xDA6ECC30, 0xDA5ACC3F, 0xDA45CC4E, 0xDA31CC5D, 0xDA1DCC6B, 0xDA08CC7A, 0xD9F4CC89, + 0xD9E0CC98, 0xD9CCCCA7, 0xD9B8CCB6, 0xD9A4CCC5, 0xD98FCCD4, 0xD97BCCE3, 0xD967CCF3, 0xD953CD02, + 0xD93FCD11, 0xD92BCD20, 0xD917CD30, 0xD903CD3F, 0xD8EFCD4E, 0xD8DCCD5D, 0xD8C8CD6D, 0xD8B4CD7C, + 0xD8A0CD8C, 0xD88CCD9B, 0xD878CDAB, 0xD865CDBA, 0xD851CDCA, 0xD83DCDD9, 0xD82ACDE9, 0xD816CDF9, + 0xD802CE08, 0xD7EFCE18, 0xD7DBCE28, 0xD7C8CE38, 0xD7B4CE47, 0xD7A0CE57, 0xD78DCE67, 0xD77ACE77, + 0xD766CE87, 0xD753CE97, 0xD73FCEA7, 0xD72CCEB7, 0xD719CEC7, 0xD705CED7, 0xD6F2CEE7, 0xD6DFCEF7, + 0xD6CBCF07, 0xD6B8CF18, 0xD6A5CF28, 0xD692CF38, 0xD67FCF48, 0xD66CCF59, 0xD659CF69, 0xD645CF79, + 0xD632CF8A, 0xD61FCF9A, 0xD60CCFAB, 0xD5F9CFBB, 0xD5E6CFCC, 0xD5D4CFDC, 0xD5C1CFED, 0xD5AECFFE, + 0xD59BD00E, 0xD588D01F, 0xD575D030, 0xD563D040, 0xD550D051, 0xD53DD062, 0xD52AD073, 0xD518D083, + 0xD505D094, 0xD4F3D0A5, 0xD4E0D0B6, 0xD4CDD0C7, 0xD4BBD0D8, 0xD4A8D0E9, 0xD496D0FA, 0xD483D10B, + 0xD471D11C, 0xD45FD12D, 0xD44CD13E, 0xD43AD150, 0xD428D161, 0xD415D172, 0xD403D183, 0xD3F1D195, + 0xD3DFD1A6, 0xD3CCD1B7, 0xD3BAD1C9, 0xD3A8D1DA, 0xD396D1EB, 0xD384D1FD, 0xD372D20E, 0xD360D220, + 0xD34ED231, 0xD33CD243, 0xD32AD255, 0xD318D266, 0xD306D278, 0xD2F4D28A, 0xD2E2D29B, 0xD2D1D2AD, + 0xD2BFD2BF, 0xD2ADD2D1, 0xD29BD2E2, 0xD28AD2F4, 0xD278D306, 0xD266D318, 0xD255D32A, 0xD243D33C, + 0xD231D34E, 0xD220D360, 0xD20ED372, 0xD1FDD384, 0xD1EBD396, 0xD1DAD3A8, 0xD1C9D3BA, 0xD1B7D3CC, + 0xD1A6D3DF, 0xD195D3F1, 0xD183D403, 0xD172D415, 0xD161D428, 0xD150D43A, 0xD13ED44C, 0xD12DD45F, + 0xD11CD471, 0xD10BD483, 0xD0FAD496, 0xD0E9D4A8, 0xD0D8D4BB, 0xD0C7D4CD, 0xD0B6D4E0, 0xD0A5D4F3, + 0xD094D505, 0xD083D518, 0xD073D52A, 0xD062D53D, 0xD051D550, 0xD040D563, 0xD030D575, 0xD01FD588, + 0xD00ED59B, 0xCFFED5AE, 0xCFEDD5C1, 0xCFDCD5D4, 0xCFCCD5E6, 0xCFBBD5F9, 0xCFABD60C, 0xCF9AD61F, + 0xCF8AD632, 0xCF79D645, 0xCF69D659, 0xCF59D66C, 0xCF48D67F, 0xCF38D692, 0xCF28D6A5, 0xCF18D6B8, + 0xCF07D6CB, 0xCEF7D6DF, 0xCEE7D6F2, 0xCED7D705, 0xCEC7D719, 0xCEB7D72C, 0xCEA7D73F, 0xCE97D753, + 0xCE87D766, 0xCE77D77A, 0xCE67D78D, 0xCE57D7A0, 0xCE47D7B4, 0xCE38D7C8, 0xCE28D7DB, 0xCE18D7EF, + 0xCE08D802, 0xCDF9D816, 0xCDE9D82A, 0xCDD9D83D, 0xCDCAD851, 0xCDBAD865, 0xCDABD878, 0xCD9BD88C, + 0xCD8CD8A0, 0xCD7CD8B4, 0xCD6DD8C8, 0xCD5DD8DC, 0xCD4ED8EF, 0xCD3FD903, 0xCD30D917, 0xCD20D92B, + 0xCD11D93F, 0xCD02D953, 0xCCF3D967, 0xCCE3D97B, 0xCCD4D98F, 0xCCC5D9A4, 0xCCB6D9B8, 0xCCA7D9CC, + 0xCC98D9E0, 0xCC89D9F4, 0xCC7ADA08, 0xCC6BDA1D, 0xCC5DDA31, 0xCC4EDA45, 0xCC3FDA5A, 0xCC30DA6E, + 0xCC21DA82, 0xCC13DA97, 0xCC04DAAB, 0xCBF5DABF, 0xCBE7DAD4, 0xCBD8DAE8, 0xCBCADAFD, 0xCBBBDB11, + 0xCBADDB26, 0xCB9EDB3B, 0xCB90DB4F, 0xCB81DB64, 0xCB73DB78, 0xCB65DB8D, 0xCB56DBA2, 0xCB48DBB6, + 0xCB3ADBCB, 0xCB2CDBE0, 0xCB1EDBF5, 0xCB0FDC09, 0xCB01DC1E, 0xCAF3DC33, 0xCAE5DC48, 0xCAD7DC5D, + 0xCAC9DC72, 0xCABBDC86, 0xCAADDC9B, 0xCA9FDCB0, 0xCA92DCC5, 0xCA84DCDA, 0xCA76DCEF, 0xCA68DD04, + 0xCA5BDD19, 0xCA4DDD2E, 0xCA3FDD44, 0xCA32DD59, 0xCA24DD6E, 0xCA16DD83, 0xCA09DD98, 0xC9FBDDAD, + 0xC9EEDDC3, 0xC9E0DDD8, 0xC9D3DDED, 0xC9C6DE02, 0xC9B8DE18, 0xC9ABDE2D, 0xC99EDE42, 0xC991DE58, + 0xC983DE6D, 0xC976DE83, 0xC969DE98, 0xC95CDEAD, 0xC94FDEC3, 0xC942DED8, 0xC935DEEE, 0xC928DF03, + 0xC91BDF19, 0xC90EDF2F, 0xC901DF44, 0xC8F4DF5A, 0xC8E8DF6F, 0xC8DBDF85, 0xC8CEDF9B, 0xC8C1DFB0, + 0xC8B5DFC6, 0xC8A8DFDC, 0xC89BDFF1, 0xC88FE007, 0xC882E01D, 0xC876E033, 0xC869E049, 0xC85DE05E, + 0xC850E074, 0xC844E08A, 0xC838E0A0, 0xC82BE0B6, 0xC81FE0CC, 0xC813E0E2, 0xC807E0F8, 0xC7FBE10E, + 0xC7EEE124, 0xC7E2E13A, 0xC7D6E150, 0xC7CAE166, 0xC7BEE17C, 0xC7B2E192, 0xC7A6E1A8, 0xC79AE1BE, + 0xC78FE1D5, 0xC783E1EB, 0xC777E201, 0xC76BE217, 0xC75FE22D, 0xC754E244, 0xC748E25A, 0xC73DE270, + 0xC731E287, 0xC725E29D, 0xC71AE2B3, 0xC70EE2CA, 0xC703E2E0, 0xC6F7E2F6, 0xC6ECE30D, 0xC6E1E323, + 0xC6D5E33A, 0xC6CAE350, 0xC6BFE367, 0xC6B4E37D, 0xC6A8E394, 0xC69DE3AA, 0xC692E3C1, 0xC687E3D7, + 0xC67CE3EE, 0xC671E404, 0xC666E41B, 0xC65BE432, 0xC650E448, 0xC645E45F, 0xC63BE476, 0xC630E48C, + 0xC625E4A3, 0xC61AE4BA, 0xC610E4D0, 0xC605E4E7, 0xC5FAE4FE, 0xC5F0E515, 0xC5E5E52C, 0xC5DBE542, + 0xC5D0E559, 0xC5C6E570, 0xC5BBE587, 0xC5B1E59E, 0xC5A7E5B5, 0xC59CE5CC, 0xC592E5E3, 0xC588E5FA, + 0xC57EE611, 0xC573E628, 0xC569E63F, 0xC55FE656, 0xC555E66D, 0xC54BE684, 0xC541E69B, 0xC537E6B2, + 0xC52DE6C9, 0xC523E6E0, 0xC51AE6F7, 0xC510E70E, 0xC506E725, 0xC4FCE73D, 0xC4F2E754, 0xC4E9E76B, + 0xC4DFE782, 0xC4D6E799, 0xC4CCE7B1, 0xC4C2E7C8, 0xC4B9E7DF, 0xC4B0E7F6, 0xC4A6E80E, 0xC49DE825, + 0xC493E83C, 0xC48AE854, 0xC481E86B, 0xC478E882, 0xC46EE89A, 0xC465E8B1, 0xC45CE8C9, 0xC453E8E0, + 0xC44AE8F7, 0xC441E90F, 0xC438E926, 0xC42FE93E, 0xC426E955, 0xC41DE96D, 0xC414E984, 0xC40BE99C, + 0xC403E9B4, 0xC3FAE9CB, 0xC3F1E9E3, 0xC3E9E9FA, 0xC3E0EA12, 0xC3D7EA29, 0xC3CFEA41, 0xC3C6EA59, + 0xC3BEEA70, 0xC3B5EA88, 0xC3ADEAA0, 0xC3A5EAB7, 0xC39CEACF, 0xC394EAE7, 0xC38CEAFF, 0xC383EB16, + 0xC37BEB2E, 0xC373EB46, 0xC36BEB5E, 0xC363EB75, 0xC35BEB8D, 0xC353EBA5, 0xC34BEBBD, 0xC343EBD5, + 0xC33BEBED, 0xC333EC05, 0xC32BEC1C, 0xC323EC34, 0xC31CEC4C, 0xC314EC64, 0xC30CEC7C, 0xC305EC94, + 0xC2FDECAC, 0xC2F5ECC4, 0xC2EEECDC, 0xC2E6ECF4, 0xC2DFED0C, 0xC2D8ED24, 0xC2D0ED3C, 0xC2C9ED54, + 0xC2C1ED6C, 0xC2BAED84, 0xC2B3ED9C, 0xC2ACEDB4, 0xC2A5EDCC, 0xC29DEDE4, 0xC296EDFC, 0xC28FEE15, + 0xC288EE2D, 0xC281EE45, 0xC27AEE5D, 0xC273EE75, 0xC26DEE8D, 0xC266EEA6, 0xC25FEEBE, 0xC258EED6, + 0xC251EEEE, 0xC24BEF06, 0xC244EF1F, 0xC23EEF37, 0xC237EF4F, 0xC230EF67, 0xC22AEF80, 0xC223EF98, + 0xC21DEFB0, 0xC217EFC9, 0xC210EFE1, 0xC20AEFF9, 0xC204F012, 0xC1FDF02A, 0xC1F7F042, 0xC1F1F05B, + 0xC1EBF073, 0xC1E5F08B, 0xC1DFF0A4, 0xC1D9F0BC, 0xC1D3F0D5, 0xC1CDF0ED, 0xC1C7F105, 0xC1C1F11E, + 0xC1BBF136, 0xC1B6F14F, 0xC1B0F167, 0xC1AAF180, 0xC1A4F198, 0xC19FF1B1, 0xC199F1C9, 0xC194F1E2, + 0xC18EF1FA, 0xC189F213, 0xC183F22B, 0xC17EF244, 0xC178F25C, 0xC173F275, 0xC16EF28E, 0xC168F2A6, + 0xC163F2BF, 0xC15EF2D7, 0xC159F2F0, 0xC154F308, 0xC14FF321, 0xC14AF33A, 0xC145F352, 0xC140F36B, + 0xC13BF384, 0xC136F39C, 0xC131F3B5, 0xC12CF3CE, 0xC128F3E6, 0xC123F3FF, 0xC11EF418, 0xC119F430, + 0xC115F449, 0xC110F462, 0xC10CF47B, 0xC107F493, 0xC103F4AC, 0xC0FEF4C5, 0xC0FAF4DD, 0xC0F6F4F6, + 0xC0F1F50F, 0xC0EDF528, 0xC0E9F540, 0xC0E4F559, 0xC0E0F572, 0xC0DCF58B, 0xC0D8F5A4, 0xC0D4F5BC, + 0xC0D0F5D5, 0xC0CCF5EE, 0xC0C8F607, 0xC0C4F620, 0xC0C0F639, 0xC0BDF651, 0xC0B9F66A, 0xC0B5F683, + 0xC0B1F69C, 0xC0AEF6B5, 0xC0AAF6CE, 0xC0A6F6E7, 0xC0A3F6FF, 0xC09FF718, 0xC09CF731, 0xC098F74A, + 0xC095F763, 0xC092F77C, 0xC08EF795, 0xC08BF7AE, 0xC088F7C7, 0xC085F7E0, 0xC081F7F9, 0xC07EF811, + 0xC07BF82A, 0xC078F843, 0xC075F85C, 0xC072F875, 0xC06FF88E, 0xC06CF8A7, 0xC069F8C0, 0xC067F8D9, + 0xC064F8F2, 0xC061F90B, 0xC05EF924, 0xC05CF93D, 0xC059F956, 0xC056F96F, 0xC054F988, 0xC051F9A1, + 0xC04FF9BA, 0xC04CF9D3, 0xC04AF9EC, 0xC048FA05, 0xC045FA1E, 0xC043FA37, 0xC041FA50, 0xC03FFA69, + 0xC03CFA82, 0xC03AFA9B, 0xC038FAB4, 0xC036FACD, 0xC034FAE6, 0xC032FB00, 0xC030FB19, 0xC02EFB32, + 0xC02CFB4B, 0xC02BFB64, 0xC029FB7D, 0xC027FB96, 0xC025FBAF, 0xC024FBC8, 0xC022FBE1, 0xC020FBFA, + 0xC01FFC13, 0xC01DFC2C, 0xC01CFC45, 0xC01AFC5F, 0xC019FC78, 0xC018FC91, 0xC016FCAA, 0xC015FCC3, + 0xC014FCDC, 0xC013FCF5, 0xC011FD0E, 0xC010FD27, 0xC00FFD40, 0xC00EFD5A, 0xC00DFD73, 0xC00CFD8C, + 0xC00BFDA5, 0xC00AFDBE, 0xC009FDD7, 0xC009FDF0, 0xC008FE09, 0xC007FE23, 0xC006FE3C, 0xC006FE55, + 0xC005FE6E, 0xC004FE87, 0xC004FEA0, 0xC003FEB9, 0xC003FED2, 0xC002FEEC, 0xC002FF05, 0xC002FF1E, + 0xC001FF37, 0xC001FF50, 0xC001FF69, 0xC000FF82, 0xC000FF9B, 0xC000FFB5, 0xC000FFCE, 0xC000FFE7, + 0xC0000000, 0xC0000019, 0xC0000032, 0xC000004B, 0xC0000065, 0xC000007E, 0xC0010097, 0xC00100B0, + 0xC00100C9, 0xC00200E2, 0xC00200FB, 0xC0020114, 0xC003012E, 0xC0030147, 0xC0040160, 0xC0040179, + 0xC0050192, 0xC00601AB, 0xC00601C4, 0xC00701DD, 0xC00801F7, 0xC0090210, 0xC0090229, 0xC00A0242, + 0xC00B025B, 0xC00C0274, 0xC00D028D, 0xC00E02A6, 0xC00F02C0, 0xC01002D9, 0xC01102F2, 0xC013030B, + 0xC0140324, 0xC015033D, 0xC0160356, 0xC018036F, 0xC0190388, 0xC01A03A1, 0xC01C03BB, 0xC01D03D4, + 0xC01F03ED, 0xC0200406, 0xC022041F, 0xC0240438, 0xC0250451, 0xC027046A, 0xC0290483, 0xC02B049C, + 0xC02C04B5, 0xC02E04CE, 0xC03004E7, 0xC0320500, 0xC034051A, 0xC0360533, 0xC038054C, 0xC03A0565, + 0xC03C057E, 0xC03F0597, 0xC04105B0, 0xC04305C9, 0xC04505E2, 0xC04805FB, 0xC04A0614, 0xC04C062D, + 0xC04F0646, 0xC051065F, 0xC0540678, 0xC0560691, 0xC05906AA, 0xC05C06C3, 0xC05E06DC, 0xC06106F5, + 0xC064070E, 0xC0670727, 0xC0690740, 0xC06C0759, 0xC06F0772, 0xC072078B, 0xC07507A4, 0xC07807BD, + 0xC07B07D6, 0xC07E07EF, 0xC0810807, 0xC0850820, 0xC0880839, 0xC08B0852, 0xC08E086B, 0xC0920884, + 0xC095089D, 0xC09808B6, 0xC09C08CF, 0xC09F08E8, 0xC0A30901, 0xC0A60919, 0xC0AA0932, 0xC0AE094B, + 0xC0B10964, 0xC0B5097D, 0xC0B90996, 0xC0BD09AF, 0xC0C009C7, 0xC0C409E0, 0xC0C809F9, 0xC0CC0A12, + 0xC0D00A2B, 0xC0D40A44, 0xC0D80A5C, 0xC0DC0A75, 0xC0E00A8E, 0xC0E40AA7, 0xC0E90AC0, 0xC0ED0AD8, + 0xC0F10AF1, 0xC0F60B0A, 0xC0FA0B23, 0xC0FE0B3B, 0xC1030B54, 0xC1070B6D, 0xC10C0B85, 0xC1100B9E, + 0xC1150BB7, 0xC1190BD0, 0xC11E0BE8, 0xC1230C01, 0xC1280C1A, 0xC12C0C32, 0xC1310C4B, 0xC1360C64, + 0xC13B0C7C, 0xC1400C95, 0xC1450CAE, 0xC14A0CC6, 0xC14F0CDF, 0xC1540CF8, 0xC1590D10, 0xC15E0D29, + 0xC1630D41, 0xC1680D5A, 0xC16E0D72, 0xC1730D8B, 0xC1780DA4, 0xC17E0DBC, 0xC1830DD5, 0xC1890DED, + 0xC18E0E06, 0xC1940E1E, 0xC1990E37, 0xC19F0E4F, 0xC1A40E68, 0xC1AA0E80, 0xC1B00E99, 0xC1B60EB1, + 0xC1BB0ECA, 0xC1C10EE2, 0xC1C70EFB, 0xC1CD0F13, 0xC1D30F2B, 0xC1D90F44, 0xC1DF0F5C, 0xC1E50F75, + 0xC1EB0F8D, 0xC1F10FA5, 0xC1F70FBE, 0xC1FD0FD6, 0xC2040FEE, 0xC20A1007, 0xC210101F, 0xC2171037, + 0xC21D1050, 0xC2231068, 0xC22A1080, 0xC2301099, 0xC23710B1, 0xC23E10C9, 0xC24410E1, 0xC24B10FA, + 0xC2511112, 0xC258112A, 0xC25F1142, 0xC266115A, 0xC26D1173, 0xC273118B, 0xC27A11A3, 0xC28111BB, + 0xC28811D3, 0xC28F11EB, 0xC2961204, 0xC29D121C, 0xC2A51234, 0xC2AC124C, 0xC2B31264, 0xC2BA127C, + 0xC2C11294, 0xC2C912AC, 0xC2D012C4, 0xC2D812DC, 0xC2DF12F4, 0xC2E6130C, 0xC2EE1324, 0xC2F5133C, + 0xC2FD1354, 0xC305136C, 0xC30C1384, 0xC314139C, 0xC31C13B4, 0xC32313CC, 0xC32B13E4, 0xC33313FB, + 0xC33B1413, 0xC343142B, 0xC34B1443, 0xC353145B, 0xC35B1473, 0xC363148B, 0xC36B14A2, 0xC37314BA, + 0xC37B14D2, 0xC38314EA, 0xC38C1501, 0xC3941519, 0xC39C1531, 0xC3A51549, 0xC3AD1560, 0xC3B51578, + 0xC3BE1590, 0xC3C615A7, 0xC3CF15BF, 0xC3D715D7, 0xC3E015EE, 0xC3E91606, 0xC3F1161D, 0xC3FA1635, + 0xC403164C, 0xC40B1664, 0xC414167C, 0xC41D1693, 0xC42616AB, 0xC42F16C2, 0xC43816DA, 0xC44116F1, + 0xC44A1709, 0xC4531720, 0xC45C1737, 0xC465174F, 0xC46E1766, 0xC478177E, 0xC4811795, 0xC48A17AC, + 0xC49317C4, 0xC49D17DB, 0xC4A617F2, 0xC4B0180A, 0xC4B91821, 0xC4C21838, 0xC4CC184F, 0xC4D61867, + 0xC4DF187E, 0xC4E91895, 0xC4F218AC, 0xC4FC18C3, 0xC50618DB, 0xC51018F2, 0xC51A1909, 0xC5231920, + 0xC52D1937, 0xC537194E, 0xC5411965, 0xC54B197C, 0xC5551993, 0xC55F19AA, 0xC56919C1, 0xC57319D8, + 0xC57E19EF, 0xC5881A06, 0xC5921A1D, 0xC59C1A34, 0xC5A71A4B, 0xC5B11A62, 0xC5BB1A79, 0xC5C61A90, + 0xC5D01AA7, 0xC5DB1ABE, 0xC5E51AD4, 0xC5F01AEB, 0xC5FA1B02, 0xC6051B19, 0xC6101B30, 0xC61A1B46, + 0xC6251B5D, 0xC6301B74, 0xC63B1B8A, 0xC6451BA1, 0xC6501BB8, 0xC65B1BCE, 0xC6661BE5, 0xC6711BFC, + 0xC67C1C12, 0xC6871C29, 0xC6921C3F, 0xC69D1C56, 0xC6A81C6C, 0xC6B41C83, 0xC6BF1C99, 0xC6CA1CB0, + 0xC6D51CC6, 0xC6E11CDD, 0xC6EC1CF3, 0xC6F71D0A, 0xC7031D20, 0xC70E1D36, 0xC71A1D4D, 0xC7251D63, + 0xC7311D79, 0xC73D1D90, 0xC7481DA6, 0xC7541DBC, 0xC75F1DD3, 0xC76B1DE9, 0xC7771DFF, 0xC7831E15, + 0xC78F1E2B, 0xC79A1E42, 0xC7A61E58, 0xC7B21E6E, 0xC7BE1E84, 0xC7CA1E9A, 0xC7D61EB0, 0xC7E21EC6, + 0xC7EE1EDC, 0xC7FB1EF2, 0xC8071F08, 0xC8131F1E, 0xC81F1F34, 0xC82B1F4A, 0xC8381F60, 0xC8441F76, + 0xC8501F8C, 0xC85D1FA2, 0xC8691FB7, 0xC8761FCD, 0xC8821FE3, 0xC88F1FF9, 0xC89B200F, 0xC8A82024, + 0xC8B5203A, 0xC8C12050, 0xC8CE2065, 0xC8DB207B, 0xC8E82091, 0xC8F420A6, 0xC90120BC, 0xC90E20D1, + 0xC91B20E7, 0xC92820FD, 0xC9352112, 0xC9422128, 0xC94F213D, 0xC95C2153, 0xC9692168, 0xC976217D, + 0xC9832193, 0xC99121A8, 0xC99E21BE, 0xC9AB21D3, 0xC9B821E8, 0xC9C621FE, 0xC9D32213, 0xC9E02228, + 0xC9EE223D, 0xC9FB2253, 0xCA092268, 0xCA16227D, 0xCA242292, 0xCA3222A7, 0xCA3F22BC, 0xCA4D22D2, + 0xCA5B22E7, 0xCA6822FC, 0xCA762311, 0xCA842326, 0xCA92233B, 0xCA9F2350, 0xCAAD2365, 0xCABB237A, + 0xCAC9238E, 0xCAD723A3, 0xCAE523B8, 0xCAF323CD, 0xCB0123E2, 0xCB0F23F7, 0xCB1E240B, 0xCB2C2420, + 0xCB3A2435, 0xCB48244A, 0xCB56245E, 0xCB652473, 0xCB732488, 0xCB81249C, 0xCB9024B1, 0xCB9E24C5, + 0xCBAD24DA, 0xCBBB24EF, 0xCBCA2503, 0xCBD82518, 0xCBE7252C, 0xCBF52541, 0xCC042555, 0xCC132569, + 0xCC21257E, 0xCC302592, 0xCC3F25A6, 0xCC4E25BB, 0xCC5D25CF, 0xCC6B25E3, 0xCC7A25F8, 0xCC89260C, + 0xCC982620, 0xCCA72634, 0xCCB62648, 0xCCC5265C, 0xCCD42671, 0xCCE32685, 0xCCF32699, 0xCD0226AD, + 0xCD1126C1, 0xCD2026D5, 0xCD3026E9, 0xCD3F26FD, 0xCD4E2711, 0xCD5D2724, 0xCD6D2738, 0xCD7C274C, + 0xCD8C2760, 0xCD9B2774, 0xCDAB2788, 0xCDBA279B, 0xCDCA27AF, 0xCDD927C3, 0xCDE927D6, 0xCDF927EA, + 0xCE0827FE, 0xCE182811, 0xCE282825, 0xCE382838, 0xCE47284C, 0xCE572860, 0xCE672873, 0xCE772886, + 0xCE87289A, 0xCE9728AD, 0xCEA728C1, 0xCEB728D4, 0xCEC728E7, 0xCED728FB, 0xCEE7290E, 0xCEF72921, + 0xCF072935, 0xCF182948, 0xCF28295B, 0xCF38296E, 0xCF482981, 0xCF592994, 0xCF6929A7, 0xCF7929BB, + 0xCF8A29CE, 0xCF9A29E1, 0xCFAB29F4, 0xCFBB2A07, 0xCFCC2A1A, 0xCFDC2A2C, 0xCFED2A3F, 0xCFFE2A52, + 0xD00E2A65, 0xD01F2A78, 0xD0302A8B, 0xD0402A9D, 0xD0512AB0, 0xD0622AC3, 0xD0732AD6, 0xD0832AE8, + 0xD0942AFB, 0xD0A52B0D, 0xD0B62B20, 0xD0C72B33, 0xD0D82B45, 0xD0E92B58, 0xD0FA2B6A, 0xD10B2B7D, + 0xD11C2B8F, 0xD12D2BA1, 0xD13E2BB4, 0xD1502BC6, 0xD1612BD8, 0xD1722BEB, 0xD1832BFD, 0xD1952C0F, + 0xD1A62C21, 0xD1B72C34, 0xD1C92C46, 0xD1DA2C58, 0xD1EB2C6A, 0xD1FD2C7C, 0xD20E2C8E, 0xD2202CA0, + 0xD2312CB2, 0xD2432CC4, 0xD2552CD6, 0xD2662CE8, 0xD2782CFA, 0xD28A2D0C, 0xD29B2D1E, 0xD2AD2D2F, + 0xD2BF2D41, 0xD2D12D53, 0xD2E22D65, 0xD2F42D76, 0xD3062D88, 0xD3182D9A, 0xD32A2DAB, 0xD33C2DBD, + 0xD34E2DCF, 0xD3602DE0, 0xD3722DF2, 0xD3842E03, 0xD3962E15, 0xD3A82E26, 0xD3BA2E37, 0xD3CC2E49, + 0xD3DF2E5A, 0xD3F12E6B, 0xD4032E7D, 0xD4152E8E, 0xD4282E9F, 0xD43A2EB0, 0xD44C2EC2, 0xD45F2ED3, + 0xD4712EE4, 0xD4832EF5, 0xD4962F06, 0xD4A82F17, 0xD4BB2F28, 0xD4CD2F39, 0xD4E02F4A, 0xD4F32F5B, + 0xD5052F6C, 0xD5182F7D, 0xD52A2F8D, 0xD53D2F9E, 0xD5502FAF, 0xD5632FC0, 0xD5752FD0, 0xD5882FE1, + 0xD59B2FF2, 0xD5AE3002, 0xD5C13013, 0xD5D43024, 0xD5E63034, 0xD5F93045, 0xD60C3055, 0xD61F3066, + 0xD6323076, 0xD6453087, 0xD6593097, 0xD66C30A7, 0xD67F30B8, 0xD69230C8, 0xD6A530D8, 0xD6B830E8, + 0xD6CB30F9, 0xD6DF3109, 0xD6F23119, 0xD7053129, 0xD7193139, 0xD72C3149, 0xD73F3159, 0xD7533169, + 0xD7663179, 0xD77A3189, 0xD78D3199, 0xD7A031A9, 0xD7B431B9, 0xD7C831C8, 0xD7DB31D8, 0xD7EF31E8, + 0xD80231F8, 0xD8163207, 0xD82A3217, 0xD83D3227, 0xD8513236, 0xD8653246, 0xD8783255, 0xD88C3265, + 0xD8A03274, 0xD8B43284, 0xD8C83293, 0xD8DC32A3, 0xD8EF32B2, 0xD90332C1, 0xD91732D0, 0xD92B32E0, + 0xD93F32EF, 0xD95332FE, 0xD967330D, 0xD97B331D, 0xD98F332C, 0xD9A4333B, 0xD9B8334A, 0xD9CC3359, + 0xD9E03368, 0xD9F43377, 0xDA083386, 0xDA1D3395, 0xDA3133A3, 0xDA4533B2, 0xDA5A33C1, 0xDA6E33D0, + 0xDA8233DF, 0xDA9733ED, 0xDAAB33FC, 0xDABF340B, 0xDAD43419, 0xDAE83428, 0xDAFD3436, 0xDB113445, + 0xDB263453, 0xDB3B3462, 0xDB4F3470, 0xDB64347F, 0xDB78348D, 0xDB8D349B, 0xDBA234AA, 0xDBB634B8, + 0xDBCB34C6, 0xDBE034D4, 0xDBF534E2, 0xDC0934F1, 0xDC1E34FF, 0xDC33350D, 0xDC48351B, 0xDC5D3529, + 0xDC723537, 0xDC863545, 0xDC9B3553, 0xDCB03561, 0xDCC5356E, 0xDCDA357C, 0xDCEF358A, 0xDD043598, + 0xDD1935A5, 0xDD2E35B3, 0xDD4435C1, 0xDD5935CE, 0xDD6E35DC, 0xDD8335EA, 0xDD9835F7, 0xDDAD3605, + 0xDDC33612, 0xDDD83620, 0xDDED362D, 0xDE02363A, 0xDE183648, 0xDE2D3655, 0xDE423662, 0xDE58366F, + 0xDE6D367D, 0xDE83368A, 0xDE983697, 0xDEAD36A4, 0xDEC336B1, 0xDED836BE, 0xDEEE36CB, 0xDF0336D8, + 0xDF1936E5, 0xDF2F36F2, 0xDF4436FF, 0xDF5A370C, 0xDF6F3718, 0xDF853725, 0xDF9B3732, 0xDFB0373F, + 0xDFC6374B, 0xDFDC3758, 0xDFF13765, 0xE0073771, 0xE01D377E, 0xE033378A, 0xE0493797, 0xE05E37A3, + 0xE07437B0, 0xE08A37BC, 0xE0A037C8, 0xE0B637D5, 0xE0CC37E1, 0xE0E237ED, 0xE0F837F9, 0xE10E3805, + 0xE1243812, 0xE13A381E, 0xE150382A, 0xE1663836, 0xE17C3842, 0xE192384E, 0xE1A8385A, 0xE1BE3866, + 0xE1D53871, 0xE1EB387D, 0xE2013889, 0xE2173895, 0xE22D38A1, 0xE24438AC, 0xE25A38B8, 0xE27038C3, + 0xE28738CF, 0xE29D38DB, 0xE2B338E6, 0xE2CA38F2, 0xE2E038FD, 0xE2F63909, 0xE30D3914, 0xE323391F, + 0xE33A392B, 0xE3503936, 0xE3673941, 0xE37D394C, 0xE3943958, 0xE3AA3963, 0xE3C1396E, 0xE3D73979, + 0xE3EE3984, 0xE404398F, 0xE41B399A, 0xE43239A5, 0xE44839B0, 0xE45F39BB, 0xE47639C5, 0xE48C39D0, + 0xE4A339DB, 0xE4BA39E6, 0xE4D039F0, 0xE4E739FB, 0xE4FE3A06, 0xE5153A10, 0xE52C3A1B, 0xE5423A25, + 0xE5593A30, 0xE5703A3A, 0xE5873A45, 0xE59E3A4F, 0xE5B53A59, 0xE5CC3A64, 0xE5E33A6E, 0xE5FA3A78, + 0xE6113A82, 0xE6283A8D, 0xE63F3A97, 0xE6563AA1, 0xE66D3AAB, 0xE6843AB5, 0xE69B3ABF, 0xE6B23AC9, + 0xE6C93AD3, 0xE6E03ADD, 0xE6F73AE6, 0xE70E3AF0, 0xE7253AFA, 0xE73D3B04, 0xE7543B0E, 0xE76B3B17, + 0xE7823B21, 0xE7993B2A, 0xE7B13B34, 0xE7C83B3E, 0xE7DF3B47, 0xE7F63B50, 0xE80E3B5A, 0xE8253B63, + 0xE83C3B6D, 0xE8543B76, 0xE86B3B7F, 0xE8823B88, 0xE89A3B92, 0xE8B13B9B, 0xE8C93BA4, 0xE8E03BAD, + 0xE8F73BB6, 0xE90F3BBF, 0xE9263BC8, 0xE93E3BD1, 0xE9553BDA, 0xE96D3BE3, 0xE9843BEC, 0xE99C3BF5, + 0xE9B43BFD, 0xE9CB3C06, 0xE9E33C0F, 0xE9FA3C17, 0xEA123C20, 0xEA293C29, 0xEA413C31, 0xEA593C3A, + 0xEA703C42, 0xEA883C4B, 0xEAA03C53, 0xEAB73C5B, 0xEACF3C64, 0xEAE73C6C, 0xEAFF3C74, 0xEB163C7D, + 0xEB2E3C85, 0xEB463C8D, 0xEB5E3C95, 0xEB753C9D, 0xEB8D3CA5, 0xEBA53CAD, 0xEBBD3CB5, 0xEBD53CBD, + 0xEBED3CC5, 0xEC053CCD, 0xEC1C3CD5, 0xEC343CDD, 0xEC4C3CE4, 0xEC643CEC, 0xEC7C3CF4, 0xEC943CFB, + 0xECAC3D03, 0xECC43D0B, 0xECDC3D12, 0xECF43D1A, 0xED0C3D21, 0xED243D28, 0xED3C3D30, 0xED543D37, + 0xED6C3D3F, 0xED843D46, 0xED9C3D4D, 0xEDB43D54, 0xEDCC3D5B, 0xEDE43D63, 0xEDFC3D6A, 0xEE153D71, + 0xEE2D3D78, 0xEE453D7F, 0xEE5D3D86, 0xEE753D8D, 0xEE8D3D93, 0xEEA63D9A, 0xEEBE3DA1, 0xEED63DA8, + 0xEEEE3DAF, 0xEF063DB5, 0xEF1F3DBC, 0xEF373DC2, 0xEF4F3DC9, 0xEF673DD0, 0xEF803DD6, 0xEF983DDD, + 0xEFB03DE3, 0xEFC93DE9, 0xEFE13DF0, 0xEFF93DF6, 0xF0123DFC, 0xF02A3E03, 0xF0423E09, 0xF05B3E0F, + 0xF0733E15, 0xF08B3E1B, 0xF0A43E21, 0xF0BC3E27, 0xF0D53E2D, 0xF0ED3E33, 0xF1053E39, 0xF11E3E3F, + 0xF1363E45, 0xF14F3E4A, 0xF1673E50, 0xF1803E56, 0xF1983E5C, 0xF1B13E61, 0xF1C93E67, 0xF1E23E6C, + 0xF1FA3E72, 0xF2133E77, 0xF22B3E7D, 0xF2443E82, 0xF25C3E88, 0xF2753E8D, 0xF28E3E92, 0xF2A63E98, + 0xF2BF3E9D, 0xF2D73EA2, 0xF2F03EA7, 0xF3083EAC, 0xF3213EB1, 0xF33A3EB6, 0xF3523EBB, 0xF36B3EC0, + 0xF3843EC5, 0xF39C3ECA, 0xF3B53ECF, 0xF3CE3ED4, 0xF3E63ED8, 0xF3FF3EDD, 0xF4183EE2, 0xF4303EE7, + 0xF4493EEB, 0xF4623EF0, 0xF47B3EF4, 0xF4933EF9, 0xF4AC3EFD, 0xF4C53F02, 0xF4DD3F06, 0xF4F63F0A, + 0xF50F3F0F, 0xF5283F13, 0xF5403F17, 0xF5593F1C, 0xF5723F20, 0xF58B3F24, 0xF5A43F28, 0xF5BC3F2C, + 0xF5D53F30, 0xF5EE3F34, 0xF6073F38, 0xF6203F3C, 0xF6393F40, 0xF6513F43, 0xF66A3F47, 0xF6833F4B, + 0xF69C3F4F, 0xF6B53F52, 0xF6CE3F56, 0xF6E73F5A, 0xF6FF3F5D, 0xF7183F61, 0xF7313F64, 0xF74A3F68, + 0xF7633F6B, 0xF77C3F6E, 0xF7953F72, 0xF7AE3F75, 0xF7C73F78, 0xF7E03F7B, 0xF7F93F7F, 0xF8113F82, + 0xF82A3F85, 0xF8433F88, 0xF85C3F8B, 0xF8753F8E, 0xF88E3F91, 0xF8A73F94, 0xF8C03F97, 0xF8D93F99, + 0xF8F23F9C, 0xF90B3F9F, 0xF9243FA2, 0xF93D3FA4, 0xF9563FA7, 0xF96F3FAA, 0xF9883FAC, 0xF9A13FAF, + 0xF9BA3FB1, 0xF9D33FB4, 0xF9EC3FB6, 0xFA053FB8, 0xFA1E3FBB, 0xFA373FBD, 0xFA503FBF, 0xFA693FC1, + 0xFA823FC4, 0xFA9B3FC6, 0xFAB43FC8, 0xFACD3FCA, 0xFAE63FCC, 0xFB003FCE, 0xFB193FD0, 0xFB323FD2, + 0xFB4B3FD4, 0xFB643FD5, 0xFB7D3FD7, 0xFB963FD9, 0xFBAF3FDB, 0xFBC83FDC, 0xFBE13FDE, 0xFBFA3FE0, + 0xFC133FE1, 0xFC2C3FE3, 0xFC453FE4, 0xFC5F3FE6, 0xFC783FE7, 0xFC913FE8, 0xFCAA3FEA, 0xFCC33FEB, + 0xFCDC3FEC, 0xFCF53FED, 0xFD0E3FEF, 0xFD273FF0, 0xFD403FF1, 0xFD5A3FF2, 0xFD733FF3, 0xFD8C3FF4, + 0xFDA53FF5, 0xFDBE3FF6, 0xFDD73FF7, 0xFDF03FF7, 0xFE093FF8, 0xFE233FF9, 0xFE3C3FFA, 0xFE553FFA, + 0xFE6E3FFB, 0xFE873FFC, 0xFEA03FFC, 0xFEB93FFD, 0xFED23FFD, 0xFEEC3FFE, 0xFF053FFE, 0xFF1E3FFE, + 0xFF373FFF, 0xFF503FFF, 0xFF693FFF, 0xFF824000, 0xFF9B4000, 0xFFB54000, 0xFFCE4000, 0xFFE74000, +}; + +const int16 atanTable[2050] = { // ROM + 0x0000, 0x0005, 0x000A, 0x000F, 0x0014, 0x0019, 0x001F, 0x0024, + 0x0029, 0x002E, 0x0033, 0x0038, 0x003D, 0x0042, 0x0047, 0x004C, + 0x0051, 0x0057, 0x005C, 0x0061, 0x0066, 0x006B, 0x0070, 0x0075, + 0x007A, 0x007F, 0x0084, 0x008A, 0x008F, 0x0094, 0x0099, 0x009E, + 0x00A3, 0x00A8, 0x00AD, 0x00B2, 0x00B7, 0x00BC, 0x00C2, 0x00C7, + 0x00CC, 0x00D1, 0x00D6, 0x00DB, 0x00E0, 0x00E5, 0x00EA, 0x00EF, + 0x00F4, 0x00FA, 0x00FF, 0x0104, 0x0109, 0x010E, 0x0113, 0x0118, + 0x011D, 0x0122, 0x0127, 0x012C, 0x0131, 0x0137, 0x013C, 0x0141, + 0x0146, 0x014B, 0x0150, 0x0155, 0x015A, 0x015F, 0x0164, 0x0169, + 0x016F, 0x0174, 0x0179, 0x017E, 0x0183, 0x0188, 0x018D, 0x0192, + 0x0197, 0x019C, 0x01A1, 0x01A6, 0x01AC, 0x01B1, 0x01B6, 0x01BB, + 0x01C0, 0x01C5, 0x01CA, 0x01CF, 0x01D4, 0x01D9, 0x01DE, 0x01E3, + 0x01E9, 0x01EE, 0x01F3, 0x01F8, 0x01FD, 0x0202, 0x0207, 0x020C, + 0x0211, 0x0216, 0x021B, 0x0220, 0x0226, 0x022B, 0x0230, 0x0235, + 0x023A, 0x023F, 0x0244, 0x0249, 0x024E, 0x0253, 0x0258, 0x025D, + 0x0262, 0x0268, 0x026D, 0x0272, 0x0277, 0x027C, 0x0281, 0x0286, + 0x028B, 0x0290, 0x0295, 0x029A, 0x029F, 0x02A4, 0x02A9, 0x02AF, + 0x02B4, 0x02B9, 0x02BE, 0x02C3, 0x02C8, 0x02CD, 0x02D2, 0x02D7, + 0x02DC, 0x02E1, 0x02E6, 0x02EB, 0x02F0, 0x02F6, 0x02FB, 0x0300, + 0x0305, 0x030A, 0x030F, 0x0314, 0x0319, 0x031E, 0x0323, 0x0328, + 0x032D, 0x0332, 0x0337, 0x033C, 0x0341, 0x0347, 0x034C, 0x0351, + 0x0356, 0x035B, 0x0360, 0x0365, 0x036A, 0x036F, 0x0374, 0x0379, + 0x037E, 0x0383, 0x0388, 0x038D, 0x0392, 0x0397, 0x039C, 0x03A2, + 0x03A7, 0x03AC, 0x03B1, 0x03B6, 0x03BB, 0x03C0, 0x03C5, 0x03CA, + 0x03CF, 0x03D4, 0x03D9, 0x03DE, 0x03E3, 0x03E8, 0x03ED, 0x03F2, + 0x03F7, 0x03FC, 0x0401, 0x0407, 0x040C, 0x0411, 0x0416, 0x041B, + 0x0420, 0x0425, 0x042A, 0x042F, 0x0434, 0x0439, 0x043E, 0x0443, + 0x0448, 0x044D, 0x0452, 0x0457, 0x045C, 0x0461, 0x0466, 0x046B, + 0x0470, 0x0475, 0x047A, 0x047F, 0x0484, 0x0489, 0x048E, 0x0494, + 0x0499, 0x049E, 0x04A3, 0x04A8, 0x04AD, 0x04B2, 0x04B7, 0x04BC, + 0x04C1, 0x04C6, 0x04CB, 0x04D0, 0x04D5, 0x04DA, 0x04DF, 0x04E4, + 0x04E9, 0x04EE, 0x04F3, 0x04F8, 0x04FD, 0x0502, 0x0507, 0x050C, + 0x0511, 0x0516, 0x051B, 0x0520, 0x0525, 0x052A, 0x052F, 0x0534, + 0x0539, 0x053E, 0x0543, 0x0548, 0x054D, 0x0552, 0x0557, 0x055C, + 0x0561, 0x0566, 0x056B, 0x0570, 0x0575, 0x057A, 0x057F, 0x0584, + 0x0589, 0x058E, 0x0593, 0x0598, 0x059D, 0x05A2, 0x05A7, 0x05AC, + 0x05B1, 0x05B6, 0x05BB, 0x05C0, 0x05C5, 0x05CA, 0x05CF, 0x05D4, + 0x05D9, 0x05DE, 0x05E3, 0x05E8, 0x05ED, 0x05F2, 0x05F7, 0x05FC, + 0x0601, 0x0606, 0x060B, 0x0610, 0x0615, 0x061A, 0x061F, 0x0624, + 0x0629, 0x062E, 0x0633, 0x0638, 0x063D, 0x0642, 0x0647, 0x064C, + 0x0651, 0x0656, 0x065B, 0x0660, 0x0665, 0x066A, 0x066E, 0x0673, + 0x0678, 0x067D, 0x0682, 0x0687, 0x068C, 0x0691, 0x0696, 0x069B, + 0x06A0, 0x06A5, 0x06AA, 0x06AF, 0x06B4, 0x06B9, 0x06BE, 0x06C3, + 0x06C8, 0x06CD, 0x06D2, 0x06D7, 0x06DC, 0x06E1, 0x06E5, 0x06EA, + 0x06EF, 0x06F4, 0x06F9, 0x06FE, 0x0703, 0x0708, 0x070D, 0x0712, + 0x0717, 0x071C, 0x0721, 0x0726, 0x072B, 0x0730, 0x0735, 0x0739, + 0x073E, 0x0743, 0x0748, 0x074D, 0x0752, 0x0757, 0x075C, 0x0761, + 0x0766, 0x076B, 0x0770, 0x0775, 0x077A, 0x077E, 0x0783, 0x0788, + 0x078D, 0x0792, 0x0797, 0x079C, 0x07A1, 0x07A6, 0x07AB, 0x07B0, + 0x07B5, 0x07B9, 0x07BE, 0x07C3, 0x07C8, 0x07CD, 0x07D2, 0x07D7, + 0x07DC, 0x07E1, 0x07E6, 0x07EB, 0x07EF, 0x07F4, 0x07F9, 0x07FE, + 0x0803, 0x0808, 0x080D, 0x0812, 0x0817, 0x081C, 0x0820, 0x0825, + 0x082A, 0x082F, 0x0834, 0x0839, 0x083E, 0x0843, 0x0848, 0x084C, + 0x0851, 0x0856, 0x085B, 0x0860, 0x0865, 0x086A, 0x086F, 0x0873, + 0x0878, 0x087D, 0x0882, 0x0887, 0x088C, 0x0891, 0x0896, 0x089A, + 0x089F, 0x08A4, 0x08A9, 0x08AE, 0x08B3, 0x08B8, 0x08BD, 0x08C1, + 0x08C6, 0x08CB, 0x08D0, 0x08D5, 0x08DA, 0x08DF, 0x08E3, 0x08E8, + 0x08ED, 0x08F2, 0x08F7, 0x08FC, 0x0901, 0x0905, 0x090A, 0x090F, + 0x0914, 0x0919, 0x091E, 0x0922, 0x0927, 0x092C, 0x0931, 0x0936, + 0x093B, 0x093F, 0x0944, 0x0949, 0x094E, 0x0953, 0x0958, 0x095C, + 0x0961, 0x0966, 0x096B, 0x0970, 0x0975, 0x0979, 0x097E, 0x0983, + 0x0988, 0x098D, 0x0992, 0x0996, 0x099B, 0x09A0, 0x09A5, 0x09AA, + 0x09AE, 0x09B3, 0x09B8, 0x09BD, 0x09C2, 0x09C6, 0x09CB, 0x09D0, + 0x09D5, 0x09DA, 0x09DE, 0x09E3, 0x09E8, 0x09ED, 0x09F2, 0x09F6, + 0x09FB, 0x0A00, 0x0A05, 0x0A0A, 0x0A0E, 0x0A13, 0x0A18, 0x0A1D, + 0x0A22, 0x0A26, 0x0A2B, 0x0A30, 0x0A35, 0x0A39, 0x0A3E, 0x0A43, + 0x0A48, 0x0A4D, 0x0A51, 0x0A56, 0x0A5B, 0x0A60, 0x0A64, 0x0A69, + 0x0A6E, 0x0A73, 0x0A77, 0x0A7C, 0x0A81, 0x0A86, 0x0A8B, 0x0A8F, + 0x0A94, 0x0A99, 0x0A9E, 0x0AA2, 0x0AA7, 0x0AAC, 0x0AB1, 0x0AB5, + 0x0ABA, 0x0ABF, 0x0AC4, 0x0AC8, 0x0ACD, 0x0AD2, 0x0AD7, 0x0ADB, + 0x0AE0, 0x0AE5, 0x0AE9, 0x0AEE, 0x0AF3, 0x0AF8, 0x0AFC, 0x0B01, + 0x0B06, 0x0B0B, 0x0B0F, 0x0B14, 0x0B19, 0x0B1E, 0x0B22, 0x0B27, + 0x0B2C, 0x0B30, 0x0B35, 0x0B3A, 0x0B3F, 0x0B43, 0x0B48, 0x0B4D, + 0x0B51, 0x0B56, 0x0B5B, 0x0B60, 0x0B64, 0x0B69, 0x0B6E, 0x0B72, + 0x0B77, 0x0B7C, 0x0B80, 0x0B85, 0x0B8A, 0x0B8F, 0x0B93, 0x0B98, + 0x0B9D, 0x0BA1, 0x0BA6, 0x0BAB, 0x0BAF, 0x0BB4, 0x0BB9, 0x0BBD, + 0x0BC2, 0x0BC7, 0x0BCB, 0x0BD0, 0x0BD5, 0x0BD9, 0x0BDE, 0x0BE3, + 0x0BE7, 0x0BEC, 0x0BF1, 0x0BF5, 0x0BFA, 0x0BFF, 0x0C03, 0x0C08, + 0x0C0D, 0x0C11, 0x0C16, 0x0C1B, 0x0C1F, 0x0C24, 0x0C29, 0x0C2D, + 0x0C32, 0x0C37, 0x0C3B, 0x0C40, 0x0C45, 0x0C49, 0x0C4E, 0x0C53, + 0x0C57, 0x0C5C, 0x0C60, 0x0C65, 0x0C6A, 0x0C6E, 0x0C73, 0x0C78, + 0x0C7C, 0x0C81, 0x0C86, 0x0C8A, 0x0C8F, 0x0C93, 0x0C98, 0x0C9D, + 0x0CA1, 0x0CA6, 0x0CAB, 0x0CAF, 0x0CB4, 0x0CB8, 0x0CBD, 0x0CC2, + 0x0CC6, 0x0CCB, 0x0CCF, 0x0CD4, 0x0CD9, 0x0CDD, 0x0CE2, 0x0CE6, + 0x0CEB, 0x0CF0, 0x0CF4, 0x0CF9, 0x0CFD, 0x0D02, 0x0D07, 0x0D0B, + 0x0D10, 0x0D14, 0x0D19, 0x0D1E, 0x0D22, 0x0D27, 0x0D2B, 0x0D30, + 0x0D34, 0x0D39, 0x0D3E, 0x0D42, 0x0D47, 0x0D4B, 0x0D50, 0x0D54, + 0x0D59, 0x0D5E, 0x0D62, 0x0D67, 0x0D6B, 0x0D70, 0x0D74, 0x0D79, + 0x0D7D, 0x0D82, 0x0D87, 0x0D8B, 0x0D90, 0x0D94, 0x0D99, 0x0D9D, + 0x0DA2, 0x0DA6, 0x0DAB, 0x0DAF, 0x0DB4, 0x0DB9, 0x0DBD, 0x0DC2, + 0x0DC6, 0x0DCB, 0x0DCF, 0x0DD4, 0x0DD8, 0x0DDD, 0x0DE1, 0x0DE6, + 0x0DEA, 0x0DEF, 0x0DF3, 0x0DF8, 0x0DFC, 0x0E01, 0x0E05, 0x0E0A, + 0x0E0F, 0x0E13, 0x0E18, 0x0E1C, 0x0E21, 0x0E25, 0x0E2A, 0x0E2E, + 0x0E33, 0x0E37, 0x0E3C, 0x0E40, 0x0E45, 0x0E49, 0x0E4E, 0x0E52, + 0x0E56, 0x0E5B, 0x0E5F, 0x0E64, 0x0E68, 0x0E6D, 0x0E71, 0x0E76, + 0x0E7A, 0x0E7F, 0x0E83, 0x0E88, 0x0E8C, 0x0E91, 0x0E95, 0x0E9A, + 0x0E9E, 0x0EA3, 0x0EA7, 0x0EAC, 0x0EB0, 0x0EB4, 0x0EB9, 0x0EBD, + 0x0EC2, 0x0EC6, 0x0ECB, 0x0ECF, 0x0ED4, 0x0ED8, 0x0EDC, 0x0EE1, + 0x0EE5, 0x0EEA, 0x0EEE, 0x0EF3, 0x0EF7, 0x0EFC, 0x0F00, 0x0F04, + 0x0F09, 0x0F0D, 0x0F12, 0x0F16, 0x0F1B, 0x0F1F, 0x0F23, 0x0F28, + 0x0F2C, 0x0F31, 0x0F35, 0x0F3A, 0x0F3E, 0x0F42, 0x0F47, 0x0F4B, + 0x0F50, 0x0F54, 0x0F58, 0x0F5D, 0x0F61, 0x0F66, 0x0F6A, 0x0F6E, + 0x0F73, 0x0F77, 0x0F7C, 0x0F80, 0x0F84, 0x0F89, 0x0F8D, 0x0F91, + 0x0F96, 0x0F9A, 0x0F9F, 0x0FA3, 0x0FA7, 0x0FAC, 0x0FB0, 0x0FB5, + 0x0FB9, 0x0FBD, 0x0FC2, 0x0FC6, 0x0FCA, 0x0FCF, 0x0FD3, 0x0FD7, + 0x0FDC, 0x0FE0, 0x0FE5, 0x0FE9, 0x0FED, 0x0FF2, 0x0FF6, 0x0FFA, + 0x0FFF, 0x1003, 0x1007, 0x100C, 0x1010, 0x1014, 0x1019, 0x101D, + 0x1021, 0x1026, 0x102A, 0x102E, 0x1033, 0x1037, 0x103B, 0x1040, + 0x1044, 0x1048, 0x104D, 0x1051, 0x1055, 0x105A, 0x105E, 0x1062, + 0x1067, 0x106B, 0x106F, 0x1073, 0x1078, 0x107C, 0x1080, 0x1085, + 0x1089, 0x108D, 0x1092, 0x1096, 0x109A, 0x109E, 0x10A3, 0x10A7, + 0x10AB, 0x10B0, 0x10B4, 0x10B8, 0x10BC, 0x10C1, 0x10C5, 0x10C9, + 0x10CE, 0x10D2, 0x10D6, 0x10DA, 0x10DF, 0x10E3, 0x10E7, 0x10EB, + 0x10F0, 0x10F4, 0x10F8, 0x10FD, 0x1101, 0x1105, 0x1109, 0x110E, + 0x1112, 0x1116, 0x111A, 0x111F, 0x1123, 0x1127, 0x112B, 0x1130, + 0x1134, 0x1138, 0x113C, 0x1140, 0x1145, 0x1149, 0x114D, 0x1151, + 0x1156, 0x115A, 0x115E, 0x1162, 0x1166, 0x116B, 0x116F, 0x1173, + 0x1177, 0x117C, 0x1180, 0x1184, 0x1188, 0x118C, 0x1191, 0x1195, + 0x1199, 0x119D, 0x11A1, 0x11A6, 0x11AA, 0x11AE, 0x11B2, 0x11B6, + 0x11BB, 0x11BF, 0x11C3, 0x11C7, 0x11CB, 0x11CF, 0x11D4, 0x11D8, + 0x11DC, 0x11E0, 0x11E4, 0x11E9, 0x11ED, 0x11F1, 0x11F5, 0x11F9, + 0x11FD, 0x1202, 0x1206, 0x120A, 0x120E, 0x1212, 0x1216, 0x121A, + 0x121F, 0x1223, 0x1227, 0x122B, 0x122F, 0x1233, 0x1237, 0x123C, + 0x1240, 0x1244, 0x1248, 0x124C, 0x1250, 0x1254, 0x1259, 0x125D, + 0x1261, 0x1265, 0x1269, 0x126D, 0x1271, 0x1275, 0x127A, 0x127E, + 0x1282, 0x1286, 0x128A, 0x128E, 0x1292, 0x1296, 0x129A, 0x129F, + 0x12A3, 0x12A7, 0x12AB, 0x12AF, 0x12B3, 0x12B7, 0x12BB, 0x12BF, + 0x12C3, 0x12C7, 0x12CC, 0x12D0, 0x12D4, 0x12D8, 0x12DC, 0x12E0, + 0x12E4, 0x12E8, 0x12EC, 0x12F0, 0x12F4, 0x12F8, 0x12FC, 0x1301, + 0x1305, 0x1309, 0x130D, 0x1311, 0x1315, 0x1319, 0x131D, 0x1321, + 0x1325, 0x1329, 0x132D, 0x1331, 0x1335, 0x1339, 0x133D, 0x1341, + 0x1345, 0x1349, 0x134D, 0x1351, 0x1355, 0x135A, 0x135E, 0x1362, + 0x1366, 0x136A, 0x136E, 0x1372, 0x1376, 0x137A, 0x137E, 0x1382, + 0x1386, 0x138A, 0x138E, 0x1392, 0x1396, 0x139A, 0x139E, 0x13A2, + 0x13A6, 0x13AA, 0x13AE, 0x13B2, 0x13B6, 0x13BA, 0x13BE, 0x13C2, + 0x13C6, 0x13CA, 0x13CE, 0x13D2, 0x13D6, 0x13DA, 0x13DE, 0x13E2, + 0x13E6, 0x13E9, 0x13ED, 0x13F1, 0x13F5, 0x13F9, 0x13FD, 0x1401, + 0x1405, 0x1409, 0x140D, 0x1411, 0x1415, 0x1419, 0x141D, 0x1421, + 0x1425, 0x1429, 0x142D, 0x1431, 0x1435, 0x1439, 0x143D, 0x1440, + 0x1444, 0x1448, 0x144C, 0x1450, 0x1454, 0x1458, 0x145C, 0x1460, + 0x1464, 0x1468, 0x146C, 0x1470, 0x1473, 0x1477, 0x147B, 0x147F, + 0x1483, 0x1487, 0x148B, 0x148F, 0x1493, 0x1497, 0x149B, 0x149E, + 0x14A2, 0x14A6, 0x14AA, 0x14AE, 0x14B2, 0x14B6, 0x14BA, 0x14BE, + 0x14C1, 0x14C5, 0x14C9, 0x14CD, 0x14D1, 0x14D5, 0x14D9, 0x14DD, + 0x14E0, 0x14E4, 0x14E8, 0x14EC, 0x14F0, 0x14F4, 0x14F8, 0x14FB, + 0x14FF, 0x1503, 0x1507, 0x150B, 0x150F, 0x1513, 0x1516, 0x151A, + 0x151E, 0x1522, 0x1526, 0x152A, 0x152D, 0x1531, 0x1535, 0x1539, + 0x153D, 0x1541, 0x1544, 0x1548, 0x154C, 0x1550, 0x1554, 0x1558, + 0x155B, 0x155F, 0x1563, 0x1567, 0x156B, 0x156E, 0x1572, 0x1576, + 0x157A, 0x157E, 0x1581, 0x1585, 0x1589, 0x158D, 0x1591, 0x1594, + 0x1598, 0x159C, 0x15A0, 0x15A4, 0x15A7, 0x15AB, 0x15AF, 0x15B3, + 0x15B7, 0x15BA, 0x15BE, 0x15C2, 0x15C6, 0x15C9, 0x15CD, 0x15D1, + 0x15D5, 0x15D8, 0x15DC, 0x15E0, 0x15E4, 0x15E8, 0x15EB, 0x15EF, + 0x15F3, 0x15F7, 0x15FA, 0x15FE, 0x1602, 0x1606, 0x1609, 0x160D, + 0x1611, 0x1614, 0x1618, 0x161C, 0x1620, 0x1623, 0x1627, 0x162B, + 0x162F, 0x1632, 0x1636, 0x163A, 0x163E, 0x1641, 0x1645, 0x1649, + 0x164C, 0x1650, 0x1654, 0x1658, 0x165B, 0x165F, 0x1663, 0x1666, + 0x166A, 0x166E, 0x1671, 0x1675, 0x1679, 0x167D, 0x1680, 0x1684, + 0x1688, 0x168B, 0x168F, 0x1693, 0x1696, 0x169A, 0x169E, 0x16A1, + 0x16A5, 0x16A9, 0x16AC, 0x16B0, 0x16B4, 0x16B7, 0x16BB, 0x16BF, + 0x16C2, 0x16C6, 0x16CA, 0x16CD, 0x16D1, 0x16D5, 0x16D8, 0x16DC, + 0x16E0, 0x16E3, 0x16E7, 0x16EB, 0x16EE, 0x16F2, 0x16F6, 0x16F9, + 0x16FD, 0x1700, 0x1704, 0x1708, 0x170B, 0x170F, 0x1713, 0x1716, + 0x171A, 0x171D, 0x1721, 0x1725, 0x1728, 0x172C, 0x1730, 0x1733, + 0x1737, 0x173A, 0x173E, 0x1742, 0x1745, 0x1749, 0x174C, 0x1750, + 0x1754, 0x1757, 0x175B, 0x175E, 0x1762, 0x1766, 0x1769, 0x176D, + 0x1770, 0x1774, 0x1778, 0x177B, 0x177F, 0x1782, 0x1786, 0x1789, + 0x178D, 0x1791, 0x1794, 0x1798, 0x179B, 0x179F, 0x17A2, 0x17A6, + 0x17AA, 0x17AD, 0x17B1, 0x17B4, 0x17B8, 0x17BB, 0x17BF, 0x17C2, + 0x17C6, 0x17C9, 0x17CD, 0x17D1, 0x17D4, 0x17D8, 0x17DB, 0x17DF, + 0x17E2, 0x17E6, 0x17E9, 0x17ED, 0x17F0, 0x17F4, 0x17F7, 0x17FB, + 0x17FE, 0x1802, 0x1806, 0x1809, 0x180D, 0x1810, 0x1814, 0x1817, + 0x181B, 0x181E, 0x1822, 0x1825, 0x1829, 0x182C, 0x1830, 0x1833, + 0x1837, 0x183A, 0x183E, 0x1841, 0x1845, 0x1848, 0x184C, 0x184F, + 0x1853, 0x1856, 0x185A, 0x185D, 0x1860, 0x1864, 0x1867, 0x186B, + 0x186E, 0x1872, 0x1875, 0x1879, 0x187C, 0x1880, 0x1883, 0x1887, + 0x188A, 0x188E, 0x1891, 0x1894, 0x1898, 0x189B, 0x189F, 0x18A2, + 0x18A6, 0x18A9, 0x18AD, 0x18B0, 0x18B3, 0x18B7, 0x18BA, 0x18BE, + 0x18C1, 0x18C5, 0x18C8, 0x18CC, 0x18CF, 0x18D2, 0x18D6, 0x18D9, + 0x18DD, 0x18E0, 0x18E3, 0x18E7, 0x18EA, 0x18EE, 0x18F1, 0x18F5, + 0x18F8, 0x18FB, 0x18FF, 0x1902, 0x1906, 0x1909, 0x190C, 0x1910, + 0x1913, 0x1917, 0x191A, 0x191D, 0x1921, 0x1924, 0x1928, 0x192B, + 0x192E, 0x1932, 0x1935, 0x1938, 0x193C, 0x193F, 0x1943, 0x1946, + 0x1949, 0x194D, 0x1950, 0x1953, 0x1957, 0x195A, 0x195D, 0x1961, + 0x1964, 0x1968, 0x196B, 0x196E, 0x1972, 0x1975, 0x1978, 0x197C, + 0x197F, 0x1982, 0x1986, 0x1989, 0x198C, 0x1990, 0x1993, 0x1996, + 0x199A, 0x199D, 0x19A0, 0x19A4, 0x19A7, 0x19AA, 0x19AE, 0x19B1, + 0x19B4, 0x19B8, 0x19BB, 0x19BE, 0x19C2, 0x19C5, 0x19C8, 0x19CC, + 0x19CF, 0x19D2, 0x19D5, 0x19D9, 0x19DC, 0x19DF, 0x19E3, 0x19E6, + 0x19E9, 0x19ED, 0x19F0, 0x19F3, 0x19F6, 0x19FA, 0x19FD, 0x1A00, + 0x1A04, 0x1A07, 0x1A0A, 0x1A0D, 0x1A11, 0x1A14, 0x1A17, 0x1A1B, + 0x1A1E, 0x1A21, 0x1A24, 0x1A28, 0x1A2B, 0x1A2E, 0x1A31, 0x1A35, + 0x1A38, 0x1A3B, 0x1A3E, 0x1A42, 0x1A45, 0x1A48, 0x1A4B, 0x1A4F, + 0x1A52, 0x1A55, 0x1A58, 0x1A5C, 0x1A5F, 0x1A62, 0x1A65, 0x1A69, + 0x1A6C, 0x1A6F, 0x1A72, 0x1A76, 0x1A79, 0x1A7C, 0x1A7F, 0x1A83, + 0x1A86, 0x1A89, 0x1A8C, 0x1A8F, 0x1A93, 0x1A96, 0x1A99, 0x1A9C, + 0x1A9F, 0x1AA3, 0x1AA6, 0x1AA9, 0x1AAC, 0x1AB0, 0x1AB3, 0x1AB6, + 0x1AB9, 0x1ABC, 0x1AC0, 0x1AC3, 0x1AC6, 0x1AC9, 0x1ACC, 0x1ACF, + 0x1AD3, 0x1AD6, 0x1AD9, 0x1ADC, 0x1ADF, 0x1AE3, 0x1AE6, 0x1AE9, + 0x1AEC, 0x1AEF, 0x1AF2, 0x1AF6, 0x1AF9, 0x1AFC, 0x1AFF, 0x1B02, + 0x1B05, 0x1B09, 0x1B0C, 0x1B0F, 0x1B12, 0x1B15, 0x1B18, 0x1B1C, + 0x1B1F, 0x1B22, 0x1B25, 0x1B28, 0x1B2B, 0x1B2E, 0x1B32, 0x1B35, + 0x1B38, 0x1B3B, 0x1B3E, 0x1B41, 0x1B44, 0x1B48, 0x1B4B, 0x1B4E, + 0x1B51, 0x1B54, 0x1B57, 0x1B5A, 0x1B5D, 0x1B61, 0x1B64, 0x1B67, + 0x1B6A, 0x1B6D, 0x1B70, 0x1B73, 0x1B76, 0x1B79, 0x1B7D, 0x1B80, + 0x1B83, 0x1B86, 0x1B89, 0x1B8C, 0x1B8F, 0x1B92, 0x1B95, 0x1B98, + 0x1B9C, 0x1B9F, 0x1BA2, 0x1BA5, 0x1BA8, 0x1BAB, 0x1BAE, 0x1BB1, + 0x1BB4, 0x1BB7, 0x1BBA, 0x1BBD, 0x1BC1, 0x1BC4, 0x1BC7, 0x1BCA, + 0x1BCD, 0x1BD0, 0x1BD3, 0x1BD6, 0x1BD9, 0x1BDC, 0x1BDF, 0x1BE2, + 0x1BE5, 0x1BE8, 0x1BEB, 0x1BEE, 0x1BF2, 0x1BF5, 0x1BF8, 0x1BFB, + 0x1BFE, 0x1C01, 0x1C04, 0x1C07, 0x1C0A, 0x1C0D, 0x1C10, 0x1C13, + 0x1C16, 0x1C19, 0x1C1C, 0x1C1F, 0x1C22, 0x1C25, 0x1C28, 0x1C2B, + 0x1C2E, 0x1C31, 0x1C34, 0x1C37, 0x1C3A, 0x1C3D, 0x1C40, 0x1C43, + 0x1C46, 0x1C49, 0x1C4C, 0x1C4F, 0x1C52, 0x1C55, 0x1C58, 0x1C5B, + 0x1C5E, 0x1C61, 0x1C64, 0x1C67, 0x1C6A, 0x1C6D, 0x1C70, 0x1C73, + 0x1C76, 0x1C79, 0x1C7C, 0x1C7F, 0x1C82, 0x1C85, 0x1C88, 0x1C8B, + 0x1C8E, 0x1C91, 0x1C94, 0x1C97, 0x1C9A, 0x1C9D, 0x1CA0, 0x1CA3, + 0x1CA6, 0x1CA9, 0x1CAC, 0x1CAF, 0x1CB2, 0x1CB5, 0x1CB8, 0x1CBB, + 0x1CBE, 0x1CC1, 0x1CC3, 0x1CC6, 0x1CC9, 0x1CCC, 0x1CCF, 0x1CD2, + 0x1CD5, 0x1CD8, 0x1CDB, 0x1CDE, 0x1CE1, 0x1CE4, 0x1CE7, 0x1CEA, + 0x1CED, 0x1CF0, 0x1CF3, 0x1CF5, 0x1CF8, 0x1CFB, 0x1CFE, 0x1D01, + 0x1D04, 0x1D07, 0x1D0A, 0x1D0D, 0x1D10, 0x1D13, 0x1D16, 0x1D18, + 0x1D1B, 0x1D1E, 0x1D21, 0x1D24, 0x1D27, 0x1D2A, 0x1D2D, 0x1D30, + 0x1D33, 0x1D35, 0x1D38, 0x1D3B, 0x1D3E, 0x1D41, 0x1D44, 0x1D47, + 0x1D4A, 0x1D4D, 0x1D4F, 0x1D52, 0x1D55, 0x1D58, 0x1D5B, 0x1D5E, + 0x1D61, 0x1D64, 0x1D66, 0x1D69, 0x1D6C, 0x1D6F, 0x1D72, 0x1D75, + 0x1D78, 0x1D7B, 0x1D7D, 0x1D80, 0x1D83, 0x1D86, 0x1D89, 0x1D8C, + 0x1D8E, 0x1D91, 0x1D94, 0x1D97, 0x1D9A, 0x1D9D, 0x1DA0, 0x1DA2, + 0x1DA5, 0x1DA8, 0x1DAB, 0x1DAE, 0x1DB1, 0x1DB3, 0x1DB6, 0x1DB9, + 0x1DBC, 0x1DBF, 0x1DC2, 0x1DC4, 0x1DC7, 0x1DCA, 0x1DCD, 0x1DD0, + 0x1DD3, 0x1DD5, 0x1DD8, 0x1DDB, 0x1DDE, 0x1DE1, 0x1DE3, 0x1DE6, + 0x1DE9, 0x1DEC, 0x1DEF, 0x1DF1, 0x1DF4, 0x1DF7, 0x1DFA, 0x1DFD, + 0x1DFF, 0x1E02, 0x1E05, 0x1E08, 0x1E0B, 0x1E0D, 0x1E10, 0x1E13, + 0x1E16, 0x1E19, 0x1E1B, 0x1E1E, 0x1E21, 0x1E24, 0x1E26, 0x1E29, + 0x1E2C, 0x1E2F, 0x1E32, 0x1E34, 0x1E37, 0x1E3A, 0x1E3D, 0x1E3F, + 0x1E42, 0x1E45, 0x1E48, 0x1E4A, 0x1E4D, 0x1E50, 0x1E53, 0x1E55, + 0x1E58, 0x1E5B, 0x1E5E, 0x1E60, 0x1E63, 0x1E66, 0x1E69, 0x1E6B, + 0x1E6E, 0x1E71, 0x1E74, 0x1E76, 0x1E79, 0x1E7C, 0x1E7F, 0x1E81, + 0x1E84, 0x1E87, 0x1E8A, 0x1E8C, 0x1E8F, 0x1E92, 0x1E94, 0x1E97, + 0x1E9A, 0x1E9D, 0x1E9F, 0x1EA2, 0x1EA5, 0x1EA8, 0x1EAA, 0x1EAD, + 0x1EB0, 0x1EB2, 0x1EB5, 0x1EB8, 0x1EBA, 0x1EBD, 0x1EC0, 0x1EC3, + 0x1EC5, 0x1EC8, 0x1ECB, 0x1ECD, 0x1ED0, 0x1ED3, 0x1ED5, 0x1ED8, + 0x1EDB, 0x1EDE, 0x1EE0, 0x1EE3, 0x1EE6, 0x1EE8, 0x1EEB, 0x1EEE, + 0x1EF0, 0x1EF3, 0x1EF6, 0x1EF8, 0x1EFB, 0x1EFE, 0x1F00, 0x1F03, + 0x1F06, 0x1F08, 0x1F0B, 0x1F0E, 0x1F10, 0x1F13, 0x1F16, 0x1F18, + 0x1F1B, 0x1F1E, 0x1F20, 0x1F23, 0x1F26, 0x1F28, 0x1F2B, 0x1F2E, + 0x1F30, 0x1F33, 0x1F36, 0x1F38, 0x1F3B, 0x1F3D, 0x1F40, 0x1F43, + 0x1F45, 0x1F48, 0x1F4B, 0x1F4D, 0x1F50, 0x1F53, 0x1F55, 0x1F58, + 0x1F5A, 0x1F5D, 0x1F60, 0x1F62, 0x1F65, 0x1F68, 0x1F6A, 0x1F6D, + 0x1F6F, 0x1F72, 0x1F75, 0x1F77, 0x1F7A, 0x1F7C, 0x1F7F, 0x1F82, + 0x1F84, 0x1F87, 0x1F8A, 0x1F8C, 0x1F8F, 0x1F91, 0x1F94, 0x1F97, + 0x1F99, 0x1F9C, 0x1F9E, 0x1FA1, 0x1FA4, 0x1FA6, 0x1FA9, 0x1FAB, + 0x1FAE, 0x1FB0, 0x1FB3, 0x1FB6, 0x1FB8, 0x1FBB, 0x1FBD, 0x1FC0, + 0x1FC3, 0x1FC5, 0x1FC8, 0x1FCA, 0x1FCD, 0x1FCF, 0x1FD2, 0x1FD5, + 0x1FD7, 0x1FDA, 0x1FDC, 0x1FDF, 0x1FE1, 0x1FE4, 0x1FE6, 0x1FE9, + 0x1FEC, 0x1FEE, 0x1FF1, 0x1FF3, 0x1FF6, 0x1FF8, 0x1FFB, 0x1FFD, + 0x2000, 0x2000, +}; + +const int32 atanOctant[] = { + 0, -16384, -65535, 49152, -32768, 16384, 32768, -49152 +}; + +int32 phd_atan(int32 x, int32 y) +{ + if (x == 0 && y == 0) + return 0; + + int32 o = 0; + + if (x < 0) { + o += 4; + x = -x; + } + + if (y < 0) { + o += 2; + y = -y; + } + + if (y > x) { + o++; + swap(x, y); + } + + return abs(atanTable[(y << 11) / x] + atanOctant[o]); +} + +uint32 phd_sqrt(uint32 x) +{ + uint32 m = 0x40000000; + uint32 y = 0; + uint32 z = 0; + + do { + y += m; + + if (y > x) { + y = z; + } else { + x -= y; + y = z + m; + } + + z = y >> 1; + } while (m >>= 2); + + return y; +} + +void anglesFromVector(int32 x, int32 y, int32 z, int16 &angleX, int16 &angleY) +{ + angleY = phd_atan(z, x); + angleX = phd_atan(phd_sqrt(x * x + z * z), -y); +} + +bool boxIntersect(const AABBi &a, const AABBi &b) +{ + return !(a.maxX <= b.minX || a.minX >= b.maxX || + a.maxY <= b.minY || a.minY >= b.maxY || + a.maxZ <= b.minZ || a.minZ >= b.maxZ); +} + +bool boxContains(const AABBi &a, const vec3i &p) +{ + return !(a.minX > p.x || a.maxX < p.x || + a.minY > p.y || a.maxY < p.y || + a.minZ > p.z || a.maxZ < p.z); +} + +vec3i boxPushOut(const AABBi &a, const AABBi &b) +{ + int32 ax = b.maxX - a.minX; + int32 bx = a.maxX - b.minX; + int32 az = b.maxZ - a.minZ; + int32 bz = a.maxZ - b.minZ; + + vec3i p; + p.y = 0; + p.x = (ax < bx) ? -ax : bx; + p.z = (az < bz) ? -az : bz; + + return p; +} + +/* +#ifdef USE_DIV_TABLE +void initDivTable() +{ + uint16 divTable[DIV_TABLE_SIZE]; + + divTable[0] = 0xFFFF; + divTable[1] = 0xFFFF; + for (uint32 i = 2; i < DIV_TABLE_SIZE; i++) { + divTable[i] = (1 << 16) / i; + } + + ASSERT((DIV_TABLE_SIZE & 8) == 0) + + printf("const uint16 divTable[DIV_TABLE_SIZE] = {\n"); + for (int i = 0; i < DIV_TABLE_SIZE; i += 8) { + printf(" 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X,\n", + divTable[i + 0], divTable[i + 1], divTable[i + 2], divTable[i + 3], + divTable[i + 4], divTable[i + 5], divTable[i + 6], divTable[i + 7]); + } + printf("};\n"); +} +#endif +*/ + +X_INLINE int16 lerpAngle(int16 a, int16 b, int32 t) +{ + int32 d = b - a; + + if (d > 0x8000) { + d -= 0x10000; + } else if (d < -0x8000) { + d += 0x10000; + } + + return a + ((d * t) >> FIXED_SHIFT); +} + +X_INLINE int16 lerpAngleSlow(int16 a, int16 b, int32 mul, int32 div) +{ + int32 d = b - a; + + if (d > 0x8000) { + d -= 0x10000; + } else if (d < -0x8000) { + d += 0x10000; + } + + return a + d * mul / div; +} + +#ifndef USE_ASM +void matrixPush_c() +{ + ASSERT(gMatrixPtr - gMatrixStack < MAX_MATRICES); + memcpy(gMatrixPtr + 1, gMatrixPtr, sizeof(Matrix)); + gMatrixPtr++; +} + +void matrixSetIdentity_c() +{ + Matrix &m = matrixGet(); + + m.e00 = 0x4000; + m.e01 = 0; + m.e02 = 0; + m.e03 = 0; + + m.e10 = 0; + m.e11 = 0x4000; + m.e12 = 0; + m.e13 = 0; + + m.e20 = 0; + m.e21 = 0; + m.e22 = 0x4000; + m.e23 = 0; +} + +void matrixSetBasis_c(Matrix &dst, const Matrix &src) +{ + dst.e00 = src.e00; + dst.e01 = src.e01; + dst.e02 = src.e02; + + dst.e10 = src.e10; + dst.e11 = src.e11; + dst.e12 = src.e12; + + dst.e10 = src.e10; + dst.e11 = src.e11; + dst.e12 = src.e12; +} + +#define LERP_1_2(a, b) a = (b + a) >> 1 +#define LERP_1_3(a, b) a = a + (b - a) / 3 +#define LERP_2_3(a, b) a = b - (b - a) / 3 +#define LERP_1_4(a, b) a = a + ((b - a) >> 2) +#define LERP_3_4(a, b) a = b - ((b - a) >> 2) +#define LERP_1_5(a, b) a = a + (b - a) / 5 +#define LERP_2_5(a, b) a = a + ((b - a) << 1) / 5 +#define LERP_3_5(a, b) a = b - ((b - a) << 1) / 5 +#define LERP_4_5(a, b) a = b - (b - a) / 5 +#define LERP_SLOW(a, b) a = a + ((b - a) * t >> 8) + +#define LERP_ROW(lerp_func, a, b, row) \ + lerp_func(a.e##row##0, b.e##row##0); \ + lerp_func(a.e##row##1, b.e##row##1); \ + lerp_func(a.e##row##2, b.e##row##2); \ + lerp_func(a.e##row##3, b.e##row##3); + +#define LERP_MATRIX(lerp_func) \ + LERP_ROW(lerp_func, m, n, 0); \ + LERP_ROW(lerp_func, m, n, 1); \ + LERP_ROW(lerp_func, m, n, 2); + +void matrixLerp_c(const Matrix &n, int32 pmul, int32 pdiv) +{ + Matrix &m = matrixGet(); + + if ((pdiv == 2) || ((pdiv == 4) && (pmul == 2))) { + LERP_MATRIX(LERP_1_2); + } else if (pdiv == 4) { + + if (pmul == 1) { + LERP_MATRIX(LERP_1_4); + } else { + LERP_MATRIX(LERP_3_4); + } + + } else { + int32 t = pmul * FixedInvU(pdiv) >> 8; + LERP_MATRIX(LERP_SLOW); + } +} + +#define MATRIX_TRANS(x,y,z)\ + Matrix &m = matrixGet();\ + int32 tx = DP33(m.e00, m.e01, m.e02, x, y, z);\ + int32 ty = DP33(m.e10, m.e11, m.e12, x, y, z);\ + int32 tz = DP33(m.e20, m.e21, m.e22, x, y, z); + +void matrixTranslateRel_c(int32 x, int32 y, int32 z) +{ + MATRIX_TRANS(x, y, z); + m.e03 += tx; + m.e13 += ty; + m.e23 += tz; +} + +void matrixTranslateAbs_c(int32 x, int32 y, int32 z) +{ + x -= gCameraViewPos.x; + y -= gCameraViewPos.y; + z -= gCameraViewPos.z; + MATRIX_TRANS(x, y, z); + m.e03 = tx; + m.e13 = ty; + m.e23 = tz; +} + +void matrixTranslateSet_c(int32 x, int32 y, int32 z) +{ + MATRIX_TRANS(x, y, z); + m.e03 = tx; + m.e13 = ty; + m.e23 = tz; +} + +void matrixRotateX_c(int32 angle) +{ + int32 s, c; + sincos(angle, s, c); + + Matrix &m = matrixGet(); + X_ROTXY(m.e02, m.e01, s, c); + X_ROTXY(m.e12, m.e11, s, c); + X_ROTXY(m.e22, m.e21, s, c); +} + +void matrixRotateY_c(int32 angle) +{ + int32 s, c; + sincos(angle, s, c); + + Matrix &m = matrixGet(); + X_ROTXY(m.e00, m.e02, s, c); + X_ROTXY(m.e10, m.e12, s, c); + X_ROTXY(m.e20, m.e22, s, c); +} + +void matrixRotateZ_c(int32 angle) +{ + int32 s, c; + sincos(angle, s, c); + + Matrix &m = matrixGet(); + X_ROTXY(m.e01, m.e00, s, c); + X_ROTXY(m.e11, m.e10, s, c); + X_ROTXY(m.e21, m.e20, s, c); +} + +void matrixRotateYQ_c(int32 quadrant) +{ + if (quadrant == 2) + return; + + Matrix &m = matrixGet(); + + if (quadrant == 0) { + m.e00 = -m.e00; + m.e10 = -m.e10; + m.e20 = -m.e20; + m.e02 = -m.e02; + m.e12 = -m.e12; + m.e22 = -m.e22; + } else if (quadrant == 1) { + int32 e0 = m.e02; + int32 e1 = m.e12; + int32 e2 = m.e22; + + m.e02 = -m.e00; + m.e12 = -m.e10; + m.e22 = -m.e20; + + m.e00 = e0; + m.e10 = e1; + m.e20 = e2; + } else { + int32 e0 = m.e02; + int32 e1 = m.e12; + int32 e2 = m.e22; + + m.e02 = m.e00; + m.e12 = m.e10; + m.e22 = m.e20; + + m.e00 = -e0; + m.e10 = -e1; + m.e20 = -e2; + } +} + +void matrixRotateYXZ_c(int32 angleX, int32 angleY, int32 angleZ) +{ + if (angleY) matrixRotateY(angleY); + if (angleX) matrixRotateX(angleX); + if (angleZ) matrixRotateZ(angleZ); +} + +void boxTranslate_c(AABBi &box, int32 x, int32 y, int32 z) +{ + box.minX += x; + box.maxX += x; + box.minY += y; + box.maxY += y; + box.minZ += z; + box.maxZ += z; +} + +void boxRotateYQ_c(AABBi &box, int32 quadrant) +{ + if (quadrant == 2) + return; + + int32 minX = box.minX; + int32 maxX = box.maxX; + int32 minZ = box.minZ; + int32 maxZ = box.maxZ; + + if (quadrant == 3) { + box.minX = minZ; + box.maxX = maxZ; + box.minZ = -maxX; + box.maxZ = -minX; + } else if (quadrant == 1) { + box.minX = -maxZ; + box.maxX = -minZ; + box.minZ = minX; + box.maxZ = maxX; + } else if (quadrant == 0) { + box.minX = -maxX; + box.maxX = -minX; + box.minZ = -maxZ; + box.maxZ = -minZ; + } +} +#endif + +void matrixFrame(const void* pos, const void* angles) +{ + int16 aX, aY, aZ; + DECODE_ANGLES(*(uint32*)angles, aX, aY, aZ); + + uint32 xy = ((uint32*)pos)[0]; + uint32 zu = ((uint32*)pos)[1]; + +#ifdef CPU_BIG_ENDIAN + int32 posX = int16(xy >> 16); + int32 posY = int16(xy & 0xFFFF); + int32 posZ = int16(zu >> 16); +#else + int32 posX = int16(xy & 0xFFFF); + int32 posY = int16(xy >> 16); + int32 posZ = int16(zu & 0xFFFF); +#endif + + matrixTranslateRel(posX, posY, posZ); + matrixRotateYXZ(aX, aY, aZ); +} + +void matrixFrameLerp(const void* pos, const void* anglesA, const void* anglesB, int32 delta, int32 rate) +{ + int16 aX, aY, aZ; + int16 bX, bY, bZ; + + DECODE_ANGLES(*(uint32*)anglesA, aX, aY, aZ); + DECODE_ANGLES(*(uint32*)anglesB, bX, bY, bZ); + + uint32 xy = ((uint32*)pos)[0]; + uint32 zu = ((uint32*)pos)[1]; + +#ifdef CPU_BIG_ENDIAN + int32 posX = int16(xy >> 16); + int32 posY = int16(xy & 0xFFFF); + int32 posZ = int16(zu >> 16); +#else + int32 posX = int16(xy & 0xFFFF); + int32 posY = int16(xy >> 16); + int32 posZ = int16(zu & 0xFFFF); +#endif + + matrixTranslateRel(posX, posY, posZ); + + matrixPush(); + matrixRotateYXZ(bX, bY, bZ); + matrixPop(); + + matrixRotateYXZ(aX, aY, aZ); + + matrixLerp(*(gMatrixPtr + 1), delta, rate); +} + +void matrixSetView(const vec3i &pos, int32 angleX, int32 angleY) +{ + int32 sx, cx; + int32 sy, cy; + + sincos(angleX, sx, cx); + sincos(angleY, sy, cy); + + Matrix &m = matrixGet(); + + m.e00 = cy; + m.e01 = 0; + m.e02 = -sy; + m.e03 = 0; + + m.e10 = (sx * sy) >> FIXED_SHIFT; + m.e11 = cx; + m.e12 = (sx * cy) >> FIXED_SHIFT; + m.e13 = 0; + + m.e20 = (cx * sy) >> FIXED_SHIFT; + m.e21 = -sx; + m.e22 = (cx * cy) >> FIXED_SHIFT; + m.e23 = 0; + + gCameraViewPos = pos; +} + +void CollisionInfo::setSide(CollisionInfo::SideType st, int32 floor, int32 ceiling) +{ + SlantType slantType; + + if (FD_SLANT_X(gLastFloorSlant) == 0 && FD_SLANT_Z(gLastFloorSlant) == 0) { + slantType = SLANT_NONE; + } else if (abs(FD_SLANT_X(gLastFloorSlant)) < 3 && abs(FD_SLANT_Z(gLastFloorSlant)) < 3) { + slantType = SLANT_LOW; + } else { + slantType = SLANT_HIGH; + } + + if (st != ST_MIDDLE) { + if (stopOnSlant && floor < 0 && slantType == SLANT_HIGH) { + floor = -0x7FFF; + } else if (stopOnSlant && floor > 0 && slantType == SLANT_HIGH) { + floor = 512; + }/* TODO lava else if (stopOnLava && floor > 0 && trigger && FloorData(*(uint16*)trigger).cmd.func == FloorData::LAVA) { + floor = 512; + }*/ + } + + Side *s = &m + st; + s->slantType = slantType; + s->floor = floor; + s->ceiling = ceiling; +} + +void palGamma(const uint16* srcPal, uint16* dstPal, int32 value) +{ + for (int32 i = 0; i < 256; i++) + { + uint16 src = *srcPal++; + int32 r = 31 & (src); + int32 g = 31 & (src >> 5); + int32 b = 31 & (src >> 10); + + r = X_MIN(31, r + (((r * r >> 2) - r) * value >> 10)); + g = X_MIN(31, g + (((g * g >> 2) - g) * value >> 10)); + b = X_MIN(31, b + (((b * b >> 2) - b) * value >> 10)); + + *dstPal++ = r | (g << 5) | (b << 10); + } +} + +void palBright(const uint16* srcPal, uint16* dstPal, int32 value) +{ + value >>= 2; + + for (int32 i = 0; i < 256; i++) + { + uint16 src = *srcPal++; + int32 r = 31 & (src); + int32 g = 31 & (src >> 5); + int32 b = 31 & (src >> 10); + + r = X_CLAMP(r + value, 0, 31); + g = X_CLAMP(g + value, 0, 31); + b = X_CLAMP(b + value, 0, 31); + + *dstPal++ = r | (g << 5) | (b << 10); + } +} + +void palGrayRemap(uint8* data, int32 size) +{ + static const uint8 grad[8] = { + 1, 22, 21, 20, 19, 18, 17, 33 + }; + + uint8 remap[256]; + + for (int32 i = 0; i < 256; i++) + { + uint16 p = level.palette[i]; + uint8 r = (p & 31); + uint8 g = ((p >> 5) & 31); + uint8 b = ((p >> 10) & 31); + + int32 lum = (r * 77 + g * 150 + b * 29) >> (8 + 2); + + remap[i] = grad[lum]; + } + + for (int32 i = 0; i < size; i++) + { + data[i] = remap[data[i]]; + } +} + +void palSet(const uint16* palette, int32 gamma, int32 bright) +{ + const uint16* pal = palette; + + if (gamma || bright) + { + uint16 tmp[256]; + if (gamma) { + palGamma(pal, tmp, gamma); + pal = tmp; + } + + if (bright) { + palBright(pal, tmp, bright); + pal = tmp; + } + } + + osSetPalette(pal); +} + +void dmaFill(void* dst, uint8 value, uint32 count) +{ + ASSERT((count & 3) == 0); +#ifdef __GBA__ + vu32 v = value; + dma3_fill(dst, v, count); +#else + memset(dst, value, count); +#endif +} + +#ifndef __NDS__ +void dmaCopy(const void* src, void* dst, uint32 size) +{ + ASSERT((size & 3) == 0); +#ifdef __GBA__ + dma3_cpy(dst, src, size); +#else + memcpy(dst, src, size); +#endif +} +#endif diff --git a/src/fixed/common.h b/src/fixed/common.h new file mode 100644 index 00000000..d2c86e35 --- /dev/null +++ b/src/fixed/common.h @@ -0,0 +1,2723 @@ +#ifndef H_COMMON +#define H_COMMON +//#define STATIC_ITEMS +//#define PROFILING +#ifdef PROFILING + #define STATIC_ITEMS +// #define PROFILE_FRAMETIME +// #define PROFILE_SOUNDTIME +#endif + +#if defined(_WIN32) + #define MODE4 + //#define MODE13 + #define USE_DIV_TABLE + + #define _CRT_SECURE_NO_WARNINGS + #include +#elif defined(__GBA__) + #define MODE4 + #define USE_DIV_TABLE + #define ROM_READ + #define USE_ASM + #define ALIGNED_LIGHTMAP + + #include +#elif defined(__NDS__) + #define MODEHW + #define USE_DIV_TABLE + + #include + #include + #include +#elif defined(__TNS__) + #define MODE13 + #define USE_DIV_TABLE + + #include +#elif defined(__DOS__) + #define MODE13 + #define USE_DIV_TABLE + + #include + #include + #include + #include + #include +#elif defined(__3DO__) + #define MODEHW + #define USE_DIV_TABLE // 4k of DRAM + #define CPU_BIG_ENDIAN + #define USE_ASM + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#else + #error unsupported platform +#endif + +#if !defined(__3DO__) + #include +#endif + +#include "stdio.h" // TODO_3DO armcpp bug? + +#include +#include +#include + +#if defined(MODEHW) + #if defined(__3DO__) + #define FRAME_WIDTH 320 + #define FRAME_HEIGHT 240 + #elif defined(__NDS__) + #define FRAME_WIDTH 256 + #define FRAME_HEIGHT 192 + #endif +#elif defined(MODE4) + #define VRAM_WIDTH 120 // in shorts (240 bytes) + #define FRAME_WIDTH 240 + #define FRAME_HEIGHT 160 +#elif defined(MODE13) + #define VRAM_WIDTH 160 // in shorts (320 bytes) + #define FRAME_WIDTH 320 + + #if defined(__TNS__) + #define FRAME_HEIGHT 240 // MODE X? + #else + #define FRAME_HEIGHT 200 + #endif +#endif + +// Optimization flags ========================================================= +#ifdef __GBA__ +// hide dead enemies after a while to reduce the number of polygons on the screen + #define HIDE_CORPSES (30*10) // 10 sec +// replace trap flor geometry by two flat quads in the static state + #define LOD_TRAP_FLOOR +// disable some plants environment to reduce overdraw of transparent geometry + #define NO_STATIC_MESH_PLANTS +// use IWRAM_CODE section that faster for matrix interpolation (GBA only) + #define IWRAM_MATRIX_LERP +// the maximum of active enemies + #define MAX_ENEMIES 3 +// visibility distance + #define VIEW_DIST (1024 * 10) +// skip collideSpheres for enemies + #define FAST_HITMASK +#endif + +#ifdef __3DO__ +// hide dead enemies after a while to reduce the number of polygons on the screen + #define HIDE_CORPSES (30*10) // 10 sec +// replace trap flor geometry by two flat quads in the static state + #define LOD_TRAP_FLOOR +// disable matrix interpolation + //#define NO_ANIM_LERP +// the maximum navigation iterations per simulation tick + #define NAV_STEPS 1 +// the maximum of active enemies + #define MAX_ENEMIES 3 +// set the maximum number of simultaneously played channels + #define SND_CHANNELS 4 +// visibility distance + #define VIEW_DIST (1024 * 10) +// skip collideSpheres for enemies + #define FAST_HITMASK +#endif + +#ifndef NAV_STEPS + #define NAV_STEPS 5 +#endif + +#ifndef MAX_ENEMIES + #define MAX_ENEMIES 8 +#endif + +// ============================================================================ + +#if defined(_MSC_VER) + #define X_INLINE inline + #define X_NOINLINE __declspec(noinline) + #define ALIGN4 __declspec(align(4)) + #define ALIGN8 __declspec(align(8)) + #define ALIGN16 __declspec(align(16)) +#elif defined(__WATCOMC__) || defined(__3DO__) + #define X_INLINE inline + #define X_NOINLINE + #define ALIGN4 + #define ALIGN8 + #define ALIGN16 +#else + #define X_INLINE __attribute__((always_inline)) inline + #define X_NOINLINE __attribute__((noinline)) + #define ALIGN4 __attribute__((aligned(4))) + #define ALIGN8 __attribute__((aligned(8))) + #define ALIGN16 __attribute__((aligned(16))) +#endif + +#if defined(__3DO__) +typedef size_t intptr_t; +typedef uint32 divTableInt; +#else +typedef signed char int8; +typedef signed short int16; +#if !defined(__NDS__) +typedef signed int int32; +#endif +typedef unsigned char uint8; +typedef unsigned short uint16; +#if !defined(__NDS__) +typedef unsigned int uint32; +#endif +typedef uint16 divTableInt; +#endif + +#if defined(__3DO__) +X_INLINE int32 abs(int32 x) { + return (x >= 0) ? x : -x; +} +#endif + +#if defined(__GBA__) || defined(__NDS__) + #define int2str(x,str) itoa(x, str, 10) +#elif defined(__3DO__) + #define int2str(x,str) sprintf(str, "%d", x) +#else + #define int2str(x,str) _itoa(x, str, 10) +#endif + +#ifdef __GBA__ + #define ARM_CODE __attribute__((target("arm"))) +#else + #define ARM_CODE + #define THUMB_CODE + #define IWRAM_DATA + #define EWRAM_DATA + #define EWRAM_BSS + #define IWRAM_CODE + #define EWRAM_CODE +#endif + +#if defined(_WIN32) + #define ASSERT(x) { if (!(x)) { DebugBreak(); } } +#else + #define ASSERT(x) +#endif + +#if defined(__GBA__) + #define IME_DISABLE() u16 origIME = REG_IME; REG_IME = 0 + #define IME_ENABLE() REG_IME = origIME; +#else + #define IME_DISABLE() + #define IME_ENABLE() +#endif + +#if defined(_WIN32) + extern uint16 fb[VRAM_WIDTH * FRAME_HEIGHT]; +#elif defined(__GBA__) + extern uint32 fb; +#elif defined(__TNS__) + extern uint16 fb[VRAM_WIDTH * FRAME_HEIGHT]; +#elif defined(__DOS__) + extern uint16 fb[VRAM_WIDTH * FRAME_HEIGHT]; +#endif + +// system +extern int32 osGetSystemTimeMS(); +extern bool osSaveSettings(); +extern bool osLoadSettings(); +extern bool osCheckSave(); +extern bool osSaveGame(); +extern bool osLoadGame(); +extern void osJoyVibrate(int32 index, int32 L, int32 R); +extern void osSetPalette(const uint16* palette); +extern void* osLoadLevel(const char* name); + +#ifdef PROFILING + #define PROFILE_FRAME\ + CNT_UPDATE,\ + CNT_RENDER + + #define PROFILE_STAGES\ + CNT_TRANSFORM,\ + CNT_ADD,\ + CNT_FLUSH,\ + CNT_VERT,\ + CNT_POLY + + #define PROFILE_SOUND\ + CNT_SOUND + + #if defined(PROFILE_FRAMETIME) + enum ProfileCounterId { + PROFILE_FRAME, + CNT_MAX, + PROFILE_STAGES, + PROFILE_SOUND + }; + #elif defined(PROFILE_SOUNDTIME) + enum ProfileCounterId { + PROFILE_SOUND, + CNT_MAX, + PROFILE_FRAME, + PROFILE_STAGES + }; + #else + enum ProfileCounterId { + PROFILE_STAGES, + CNT_MAX, + PROFILE_FRAME, + PROFILE_SOUND + }; + #endif + + extern uint32 gCounters[CNT_MAX]; + + #if defined(__3DO__) // should be first, armcpp bug (#elif) + extern int32 g_timer; + + #define PROFILE_START() {\ + g_timer = osGetSystemTimeMS();\ + } + + #define PROFILE_STOP(value) {\ + value += (osGetSystemTimeMS() - g_timer);\ + } + #elif defined(_WIN32) + extern LARGE_INTEGER g_timer; + extern LARGE_INTEGER g_current; + + #define PROFILE_START() {\ + QueryPerformanceCounter(&g_timer);\ + } + + #define PROFILE_STOP(value) {\ + QueryPerformanceCounter(&g_current);\ + value += (g_current.QuadPart - g_timer.QuadPart);\ + } + #elif defined(__GBA__) + #ifdef PROFILE_SOUNDTIME + #define TIMER_FREQ_DIV 1 + #else + #define TIMER_FREQ_DIV 3 + #endif + + #define PROFILE_START() {\ + REG_TM2CNT_L = 0;\ + REG_TM2CNT_H = (1 << 7) | TIMER_FREQ_DIV;\ + } + + #define PROFILE_STOP(value) {\ + value += REG_TM2CNT_L;\ + REG_TM2CNT_H = 0;\ + } + #else + #define PROFILE_START() aaa + #define PROFILE_STOP(value) bbb + #endif + + struct ProfileCounter + { + ProfileCounterId cnt; + + ProfileCounter(ProfileCounterId cnt) : cnt(cnt) { + if (cnt < CNT_MAX) { + PROFILE_START() + } + } + + ~ProfileCounter() { + if (cnt < CNT_MAX) { + PROFILE_STOP(gCounters[cnt]); + } + } + }; + + #define PROFILE(cnt) ProfileCounter profileCounter(cnt) + #define PROFILE_CLEAR() memset(gCounters, 0, sizeof(gCounters)); +#else + #define PROFILE(cnt) + #define PROFILE_CLEAR() +#endif + +#ifdef __TNS__ + void osSetPalette(uint16* palette); +#endif + +#define STATIC_MESH_FLAG_NO_COLLISION 1 +#define STATIC_MESH_FLAG_VISIBLE 2 +#define MAX_STATIC_MESH_RADIUS (5 * 1024) + +extern int32 fps; + +#define FIXED_SHIFT 14 + +#define SND_MAX_DIST (8 * 1024) + +#ifndef SND_CHANNELS + #define SND_CHANNELS 6 +#endif + +#define SND_FIXED_SHIFT 8 +#define SND_VOL_SHIFT 6 +#define SND_PITCH_SHIFT 7 + +#if defined(_WIN32) + #define SND_SAMPLES 1024 + #define SND_OUTPUT_FREQ 22050 + #define SND_SAMPLE_FREQ 22050 + #define SND_ENCODE(x) ((x) + 128) + #define SND_DECODE(x) ((x) - 128) + #define SND_MIN -128 + #define SND_MAX 127 +#elif defined(__GBA__) + #define SND_SAMPLES 176 + #define SND_OUTPUT_FREQ 10512 + #define SND_SAMPLE_FREQ 22050 + #define SND_ENCODE(x) (x) + #define SND_DECODE(x) ((x) - 128) + #define SND_MIN -128 + #define SND_MAX 127 +#elif defined(__DOS__) + #define SND_SAMPLES 1024 + #define SND_OUTPUT_FREQ 11025 + #define SND_SAMPLE_FREQ 11025 + #define SND_ENCODE(x) ((x) + 128) + #define SND_DECODE(x) ((x) - 128) + #define SND_MIN -128 + #define SND_MAX 127 +#elif defined(__3DO__) + #define SND_SAMPLES 1024 + #define SND_OUTPUT_FREQ 11025 + #define SND_SAMPLE_FREQ 11025 + #define SND_ENCODE(x) ((x) + 128) + #define SND_DECODE(x) ((x) - 128) + #define SND_MIN -128 + #define SND_MAX 127 +#endif + +#if defined(__3DO__) + #define MAX_VERTICES (1024 + 32) // for mesh (max = LEVEL10A room:58) +#elif defined(__GBA__) + #define MAX_VERTICES (5*1024) // for frame (max is 8191 - check the assumption in flush.s) +#else + #define MAX_VERTICES (5*1024) // for frame +#endif + +#define MAX_UPDATE_FRAMES 10 +#define MAX_PLAYERS 1 // TODO 2 players for non-potato platforms +#define MAX_SPHERES 32 +#define MAX_MATRICES 8 +#define MAX_ROOMS 139 // LEVEL7A +#define MAX_ITEMS 256 +#define MAX_MODELS ITEM_MAX +#define MAX_MESHES 512 +#define MAX_STATIC_MESHES 50 +#define MAX_CAMERAS 16 +#define MAX_BOXES 1024 +#define MAX_TEXTURES 1536 +#define MAX_SPRITES 180 +#define MAX_FACES 1920 +#define MAX_ROOM_LIST 16 +#define MAX_PORTALS 16 +#define MAX_CAUSTICS 32 +#define MAX_RAND_TABLE 32 +#define MAX_DYN_SECTORS (1024*3) +#define MAX_SAMPLES 180 + +#ifndef VIEW_DIST + #define VIEW_DIST (1024 * 10) +#endif + +#define FOV_SHIFT 3 +#define FOG_SHIFT 1 +#define FOG_MAX VIEW_DIST +#define FOG_MIN (FOG_MAX - (8192 >> FOG_SHIFT)) +#define VIEW_MIN_F (64 << FIXED_SHIFT) +#define VIEW_MAX_F (VIEW_DIST << FIXED_SHIFT) + +#define TEX_ATTR_AKILL 1 + +#define NOT_ENEMY -0x4000 // default hp for non enemies +#define NO_ROOM 0xFF +#define NO_MODEL 0xFF +#define NO_BOX 0xFFFF +#define NO_FLOOR -127 +#define WALL (NO_FLOOR * 256) + +#define ANGLE_360 0x10000 +#define ANGLE_0 0 +#define ANGLE_1 (ANGLE_360 / 360) +#define ANGLE_45 (ANGLE_360 / 8) // != 45 * ANGLE_1 !!! +#define ANGLE_90 (ANGLE_360 / 4) // != 90 * ANGLE_1 !!! +#define ANGLE_180 -(ANGLE_360 / 2) // INT16_MIN +#define ANGLE(x) ((x) * ANGLE_1) +#define ANGLE_SHIFT_45 13 +#define ANGLE_SHIFT_90 14 +#define ANGLE_SHIFT_180 15 + +#define LARA_MAX_HEALTH 1000 +#define LARA_MAX_OXYGEN 1800 // TODO +30 sec for TR5 + +#define X_CLAMP(x, a, b) ((x) < (a) ? (a) : ((x) > (b) ? (b) : (x))) +#define X_MIN(a,b) ((a) < (b) ? (a) : (b)) +#define X_MAX(a,b) ((a) > (b) ? (a) : (b)) +#define X_SQR(x) ((x) * (x)) +#define X_COUNT(x) int32(sizeof(x) / sizeof(x[0])) + +#define X_ROTX(x,y,s,c) (((x) * (c) - (y) * (s)) >> FIXED_SHIFT) +#define X_ROTY(x,y,s,c) (((y) * (c) + (x) * (s)) >> FIXED_SHIFT) +#define X_ROTXY(x,y,s,c) {\ + int32 _x = X_ROTX(x,y,s,c);\ + int32 _y = X_ROTY(x,y,s,c);\ + x = _x;\ + y = _y;\ +} + +#define DP43(ax,ay,az,aw,bx,by,bz) (ax * bx + ay * by + az * bz + aw) +#define DP33(ax,ay,az,bx,by,bz) (ax * bx + ay * by + az * bz) + +#ifdef USE_DIV_TABLE + #define DIV_TABLE_SIZE 1025 // to compare with #1024 without extra LDR + #define FixedInvS(x) ((x < 0) ? -divTable[abs(x)] : divTable[x]) + #define FixedInvU(x) divTable[x] + extern divTableInt divTable[DIV_TABLE_SIZE]; + + #define GET_FRAME_T(x,n) (FixedInvU(n) * x) +#else + #define GET_FRAME_T(x,n) ((x << 16) / n) +#endif + +#define OT_SHIFT 4 +#define OT_SIZE ((VIEW_MAX_F >> (FIXED_SHIFT + OT_SHIFT)) + 1) + +// system keys (keys) +enum InputKey { + IK_NONE = 0, + IK_UP = (1 << 0), + IK_RIGHT = (1 << 1), + IK_DOWN = (1 << 2), + IK_LEFT = (1 << 3), + IK_A = (1 << 4), + IK_B = (1 << 5), + IK_C = (1 << 6), + IK_X = (1 << 7), + IK_Y = (1 << 8), + IK_L = (1 << 9), + IK_R = (1 << 10), + IK_START = (1 << 11), + IK_SELECT = (1 << 12) +}; + +// action keys (ItemObj::input) +enum InputState { + IN_LEFT = (1 << 1), + IN_RIGHT = (1 << 2), + IN_UP = (1 << 3), + IN_DOWN = (1 << 4), + IN_JUMP = (1 << 5), + IN_WALK = (1 << 6), + IN_ACTION = (1 << 7), + IN_WEAPON = (1 << 8), + IN_LOOK = (1 << 9), + IN_START = (1 << 10), + IN_SELECT = (1 << 11) +}; + +struct vec3s { + int16 x, y, z; + + X_INLINE static vec3s create(int16 x, int16 y, int16 z) { + vec3s r; + r.x = x; + r.y = y; + r.z = z; + return r; + } + + X_INLINE vec3s operator + (const vec3s &v) const { return create(x + v.x, y + v.y, z + v.z); } + X_INLINE vec3s operator - (const vec3s &v) const { return create(x - v.x, y - v.y, z - v.z); } + X_INLINE bool operator == (const vec3s &v) { return x == v.x && y == v.y && z == v.z; } + X_INLINE bool operator != (const vec3s &v) { return x != v.x || y != v.y || z != v.z; } + X_INLINE vec3s& operator += (const vec3s &v) { x += v.x; y += v.y; z += v.z; return *this; } + X_INLINE vec3s& operator -= (const vec3s &v) { x -= v.x; y -= v.y; z -= v.z; return *this; } +}; + +#define _vec3s(x,y,z) vec3s::create(x, y, z) + +struct vec4s { + int16 x, y, z, w; +}; + +struct vec3i { + int32 x, y, z; + + X_INLINE static vec3i create(int32 x, int32 y, int32 z) { + vec3i r; + r.x = x; + r.y = y; + r.z = z; + return r; + } + + X_INLINE vec3i operator + (const vec3i &v) const { return create(x + v.x, y + v.y, z + v.z); } + X_INLINE vec3i operator - (const vec3i &v) const { return create(x - v.x, y - v.y, z - v.z); } + X_INLINE vec3i operator * (int32 s) const { return create(x * s, y * s, z * s); } + X_INLINE vec3i operator / (int32 s) const { return create(x / s, y / s, z / s); } + X_INLINE bool operator == (const vec3i &v) const { return x == v.x && y == v.y && z == v.z; } + X_INLINE bool operator != (const vec3i &v) const { return x != v.x || y != v.y || z != v.z; } + X_INLINE vec3i& operator += (const vec3i &v) { x += v.x; y += v.y; z += v.z; return *this; } + X_INLINE vec3i& operator -= (const vec3i &v) { x -= v.x; y -= v.y; z -= v.z; return *this; } + X_INLINE vec3i& operator *= (int32 s) { x *= s; y *= s; z *= s; return *this; } + X_INLINE vec3i& operator /= (int32 s) { x /= s; y /= s; z /= s; return *this; } +}; + +#define _vec3i(x,y,z) vec3i::create(x, y, z) + +struct vec4i { + int32 x, y, z, w; + + X_INLINE int32& operator [] (int32 index) const { + ASSERT(index >= 0 && index <= 3); + return ((int32*)this)[index]; + } +}; + +struct Matrix +{ +#ifdef __3DO__ + int32 e00, e10, e20; + int32 e01, e11, e21; + int32 e02, e12, e22; + int32 e03, e13, e23; +#else + int32 e00, e01, e02, e03; + int32 e10, e11, e12, e13; + int32 e20, e21, e22, e23; +#endif +}; + +struct RoomQuad +{ +#ifdef __3DO__ + uint32 flags; + uint16 indices[4]; +#else + uint16 flags; + uint16 indices[4]; +#endif +}; + +struct RoomTriangle +{ +#ifdef __3DO__ + uint32 flags; + uint16 indices[4]; +#else + uint16 flags; + uint16 indices[3]; +#endif +}; + +struct MeshQuad +{ +#ifdef __3DO__ + uint32 flags; + uint32 indices; +#else + uint16 flags; + uint8 indices[4]; +#endif +}; + +struct MeshTriangle +{ +#ifdef __3DO__ + uint32 flags; + uint32 indices; +#else + uint16 flags; + uint8 indices[4]; +#endif +}; + +struct RectMinMax +{ + int32 x0; + int32 y0; + int32 x1; + int32 y1; + + RectMinMax() {} + RectMinMax(int32 x0, int32 y0, int32 x1, int32 y1) : x0(x0), y0(y0), x1(x1), y1(y1) {} +}; + +union TexCoord +{ + struct { uint16 v, u; } uv; + uint32 t; +}; + +#ifdef __3DO__ +struct Face +{ + uint32 ccb_Flags; + Face* ccb_NextPtr; + CelData* ccb_SourcePtr; + void* ccb_PLUTPtr; + Coord ccb_XPos; + Coord ccb_YPos; + int32 ccb_HDX; + int32 ccb_HDY; + int32 ccb_VDX; + int32 ccb_VDY; + int32 ccb_HDDX; + int32 ccb_HDDY; + uint32 ccb_PIXC; + // TODO use 1x1 textures instead of colored faces to remove preamble words (8 bytes per face - 15k) + uint32 ccb_PRE0; + uint32 ccb_PRE1; + //int32 ccb_Width; + //int32 ccb_Height; +}; + + #define BLOCK_SIZE_DRAM (32 * 1024) + #define BLOCK_SIZE_VRAM (16 * 1024) + #define BLOCK_SIZE_CD (2 * 1024) + + #define SND_BUFFER_SIZE (4 * BLOCK_SIZE_CD) + #define SND_BUFFERS 4 + + #define MAX_RAM_LVL (BLOCK_SIZE_DRAM * 29) // 34 for LEVEL10C! >_< + #define MAX_RAM_TEX (BLOCK_SIZE_VRAM * 44) + #define MAX_RAM_CEL (MAX_FACES * sizeof(Face)) + #define MAX_RAM_SND (SND_BUFFERS * SND_BUFFER_SIZE) + + extern void* RAM_LVL; + extern void* RAM_TEX; + extern void* RAM_CEL; + extern void* RAM_SND; +#else +struct Face +{ + uint32 flags; + Face* next; + uint16 indices[4]; +}; +#endif + +struct AABBs +{ + int16 minX; + int16 maxX; + int16 minY; + int16 maxY; + int16 minZ; + int16 maxZ; + + X_INLINE AABBs() {} + X_INLINE AABBs(int16 minX, int16 maxX, int16 minY, int16 maxY, int16 minZ, int16 maxZ) : + minX(minX), maxX(maxX), minY(minY), maxY(maxY), minZ(minZ), maxZ(maxZ) {} + + X_INLINE vec3i getCenter() const { + return _vec3i((maxX + minX) >> 1, (maxY + minY) >> 1, (maxZ + minZ) >> 1); + } +}; + +struct AABBi +{ + int32 minX; + int32 maxX; + int32 minY; + int32 maxY; + int32 minZ; + int32 maxZ; + + X_INLINE AABBi() {} + X_INLINE AABBi(const AABBs &b) : + minX(b.minX), maxX(b.maxX), minY(b.minY), maxY(b.maxY), minZ(b.minZ), maxZ(b.maxZ) {} + X_INLINE AABBi(int32 minX, int32 maxX, int32 minY, int32 maxY, int32 minZ, int32 maxZ) : + minX(minX), maxX(maxX), minY(minY), maxY(maxY), minZ(minZ), maxZ(maxZ) {} + + X_INLINE vec3i getCenter() const { + return _vec3i((maxX + minX) >> 1, (maxY + minY) >> 1, (maxZ + minZ) >> 1); + } +}; + +struct Sphere +{ + vec3i center; + int32 radius; +}; + +struct Room; + +struct RoomVertex +{ +#ifdef __3DO__ + uint16 xyz565; +#else + uint8 x, y, z, g; +#endif +}; + +struct RoomSprite +{ + vec3s pos; + uint8 g; + uint8 index; +}; + +struct MeshVertex +{ + int16 x, y, z; +}; + +struct Portal +{ + uint16 roomIndex; + vec3s n; + vec3s v[4]; +}; + +struct Sector +{ + uint16 floorIndex; + uint16 boxIndex; + uint8 roomBelow; + int8 floor; + uint8 roomAbove; + int8 ceiling; + + const Sector* getSectorBelow(int32 posX, int32 posZ) const; + const Sector* getSectorAbove(int32 posX, int32 posZ) const; + int32 getFloor(int32 x, int32 y, int32 z) const; + int32 getCeiling(int32 x, int32 y, int32 z) const; + Room* getNextRoom() const; + void getTriggerFloorCeiling(int32 x, int32 y, int32 z, int32* floor, int32* ceiling) const; +}; + +struct Light +{ + vec3s pos; + uint8 radius; + uint8 intensity; +}; + +#define STATIC_MESH_ID(flags) ((flags) & 0x3F) +#define STATIC_MESH_QUADRANT(flags) (((flags) >> 6) & 3) +#define STATIC_MESH_ROT(flags) ((STATIC_MESH_QUADRANT(flags) - 2) * ANGLE_90) +#define STATIC_MESH_INTENSITY(flags) ((((flags) >> 8) & 0xFF) << 5) + +struct RoomMesh +{ + uint32 xy; // (x << 16) | y + uint32 zf; // (z << 16) | (intensity << 8) | flags +}; + +struct ItemObj; + +struct RoomData +{ + const RoomQuad* quads; + const RoomTriangle* triangles; + const RoomVertex* vertices; + const RoomSprite* sprites; + const Portal* portals; + const Sector* sectors; + const Light* lights; + const RoomMesh* meshes; +}; + +#define ROOM_FLAG_WATER(x) ((x) & 1) + +struct RoomInfo +{ + int16 x; + int16 z; + + int16 yBottom; + int16 yTop; + + uint16 quadsCount; + uint16 trianglesCount; + + uint16 verticesCount; + uint16 spritesCount; + + uint8 portalsCount; + uint8 lightsCount; + uint8 meshesCount; + uint8 ambient; + + uint8 xSectors; + uint8 zSectors; + uint8 alternateRoom; + uint8 flags; + + RoomData data; +}; + +struct CollisionInfo; + +struct Room { + ItemObj* firstItem; + const RoomInfo* info; + const Sector* sectors; // == info->sectors (const) by default (see roomModify) + + RoomData data; + + RectMinMax clip; + uint8 _reserved; + bool visible; + + void modify(); + void reset(); + + void add(ItemObj* item); + void remove(ItemObj* item); + + const Sector* getSector(int32 x, int32 z) const; + const Sector* getWaterSector(int32 x, int32 z) const; + Room* getRoom(int32 x, int32 y, int32 z); + bool collideStatic(CollisionInfo &cinfo, const vec3i &p, int32 height); + bool checkPortal(const Portal* portal); + + Room** addVisibleRoom(Room** list); + Room** addNearRoom(Room** list, int32 x, int32 y, int32 z); + Room** getNearRooms(const vec3i &pos, int32 radius, int32 height); + Room** getAdjRooms(); + Room** getVisibleRooms(); +}; + +enum NodeFlag { + NODE_FLAG_POP = (1 << 0), + NODE_FLAG_PUSH = (1 << 1), + NODE_FLAG_ROTX = (1 << 2), + NODE_FLAG_ROTY = (1 << 3), + NODE_FLAG_ROTZ = (1 << 4) +}; + +struct ModelNode { + vec3s pos; + uint16 flags; +}; + +struct Model { + uint8 type; + int8 count; + uint16 start; + uint16 nodeIndex; + uint16 animIndex; +}; + +#define FILE_MODEL_SIZE (sizeof(Model) - 2) // -padding + +struct Mesh { + vec3s center; + int16 radius; + uint16 intensity; + int16 vCount; + int16 rCount; + int16 tCount; + int16 crCount; + int16 ctCount; + // data... +}; + +struct Sphere16 { + uint32 xy; + uint32 zr; +}; + +struct StaticMesh { + uint16 id; + uint16 meshIndex; + uint32 flags; + Sphere16 vs; + AABBs vbox; + AABBs cbox; +}; + +#define SI_MODE(x) (x & 3) +#define SI_COUNT(x) ((x >> 2) & 15) +#define SI_CAMERA(x) ((x >> 12) & 1) +#define SI_PITCH(x) ((x >> 13) & 1) +#define SI_GAIN(x) ((x >> 14) & 1) + +struct SoundInfo +{ + uint16 index; + uint16 volume; + uint16 chance; + uint16 flags; +}; + +struct Anim { + uint32 frameOffset; + uint8 frameRate; + uint8 frameSize; + uint16 state; + int32 speed; + int32 accel; + uint16 frameBegin; + uint16 frameEnd; + uint16 nextAnimIndex; + uint16 nextFrameIndex; + uint16 statesCount; + uint16 statesStart; + uint16 commandsCount; + uint16 commandsStart; +}; + +struct AnimState { + uint8 state; + uint8 rangesCount; + uint16 rangesStart; +}; + +struct AnimRange { + uint16 frameBegin; + uint16 frameEnd; + uint16 nextAnimIndex; + uint16 nextFrameIndex; +}; + +struct AnimFrame { + AABBs box; + vec3s pos; + uint16 angles[1]; +}; + +struct Texture +{ +#ifdef __3DO__ + uint8* data; + uint32 shift; +#else + uint32 tile; + uint32 uv01; + uint32 uv23; +#endif +}; + +struct Sprite +{ +#ifdef __3DO__ + uint32 texture; + int32 l, t, r, b; +#else + uint32 tile; + uint32 uwvh; + int16 l, t, r, b; +#endif +}; + +struct SpriteSeq { + uint16 type; + uint16 unused; + int16 count; + uint16 start; +}; + + +#define FIXED_CAMERA_FLAG_TIMER 0xFF +#define FIXED_CAMERA_FLAG_ONCE (1 << 8) +#define FIXED_CAMERA_FLAG_SPEED_SHIFT 9 +#define FIXED_CAMERA_FLAG_SPEED (31 << FIXED_CAMERA_FLAG_SPEED_SHIFT) + +struct FixedCamera { + vec3i pos; + int16 roomIndex; + uint16 flags; +}; + +#define ITEM_FLAGS_STATUS_SHIFT 3 +#define ITEM_FLAGS_MASK_SHIFT 9 +#define ITEM_FLAGS_MASK_ALL 31 + +#define ITEM_FLAG_GRAVITY (1 << 1) +#define ITEM_FLAG_ACTIVE (1 << 2) +#define ITEM_FLAG_STATUS (3 << ITEM_FLAGS_STATUS_SHIFT) +#define ITEM_FLAG_STATUS_ACTIVE (1 << ITEM_FLAGS_STATUS_SHIFT) +#define ITEM_FLAG_STATUS_INACTIVE (2 << ITEM_FLAGS_STATUS_SHIFT) +#define ITEM_FLAG_STATUS_INVISIBLE (3 << ITEM_FLAGS_STATUS_SHIFT) +#define ITEM_FLAG_COLLISION (1 << 5) +#define ITEM_FLAG_INJURED (1 << 6) +#define ITEM_FLAG_ANIMATED (1 << 7) +#define ITEM_FLAG_ONCE (1 << 8) +#define ITEM_FLAG_MASK (ITEM_FLAGS_MASK_ALL << ITEM_FLAGS_MASK_SHIFT) +#define ITEM_FLAG_REVERSE (1 << 14) +#define ITEM_FLAG_SHADOW (1 << 15) + +struct ItemObjInfo { + uint8 type; + uint8 roomIndex; + vec3s pos; + uint16 intensity; + uint16 flags; +}; + +#define FILE_ITEM_SIZE (sizeof(ItemObjInfo) - 2) + +struct CollisionInfo; +struct Lara; + +#define ITEM_TYPES(E) \ + E( LARA ) \ + E( LARA_PISTOLS ) \ + E( LARA_SHOTGUN ) \ + E( LARA_MAGNUMS ) \ + E( LARA_UZIS ) \ + E( LARA_SPEC ) \ + E( DOPPELGANGER ) \ + E( WOLF ) \ + E( BEAR ) \ + E( BAT ) \ + E( CROCODILE_LAND ) \ + E( CROCODILE_WATER ) \ + E( LION_MALE ) \ + E( LION_FEMALE ) \ + E( PUMA ) \ + E( GORILLA ) \ + E( RAT_LAND ) \ + E( RAT_WATER ) \ + E( REX ) \ + E( RAPTOR ) \ + E( MUTANT_1 ) \ + E( MUTANT_2 ) \ + E( MUTANT_3 ) \ + E( CENTAUR ) \ + E( MUMMY ) \ + E( UNUSED_1 ) \ + E( UNUSED_2 ) \ + E( LARSON ) \ + E( PIERRE ) \ + E( SKATEBOARD ) \ + E( SKATER ) \ + E( COWBOY ) \ + E( MR_T ) \ + E( NATLA ) \ + E( ADAM ) \ + E( TRAP_FLOOR ) \ + E( TRAP_SWING_BLADE ) \ + E( TRAP_SPIKES ) \ + E( TRAP_BOULDER ) \ + E( DART ) \ + E( TRAP_DART_EMITTER ) \ + E( DRAWBRIDGE ) \ + E( TRAP_SLAM ) \ + E( TRAP_SWORD ) \ + E( HAMMER_HANDLE ) \ + E( HAMMER_BLOCK ) \ + E( LIGHTNING ) \ + E( MOVING_OBJECT ) \ + E( BLOCK_1 ) \ + E( BLOCK_2 ) \ + E( BLOCK_3 ) \ + E( BLOCK_4 ) \ + E( MOVING_BLOCK ) \ + E( TRAP_CEILING ) \ + E( TRAP_FLOOR_LOD ) \ + E( SWITCH ) \ + E( SWITCH_WATER ) \ + E( DOOR_1 ) \ + E( DOOR_2 ) \ + E( DOOR_3 ) \ + E( DOOR_4 ) \ + E( DOOR_5 ) \ + E( DOOR_6 ) \ + E( DOOR_7 ) \ + E( DOOR_8 ) \ + E( TRAP_DOOR_1 ) \ + E( TRAP_DOOR_2 ) \ + E( TRAP_DOOR_LOD ) \ + E( BRIDGE_FLAT ) \ + E( BRIDGE_TILT_1 ) \ + E( BRIDGE_TILT_2 ) \ + E( INV_PASSPORT ) \ + E( INV_COMPASS ) \ + E( INV_HOME ) \ + E( GEARS_1 ) \ + E( GEARS_2 ) \ + E( GEARS_3 ) \ + E( CUT_1 ) \ + E( CUT_2 ) \ + E( CUT_3 ) \ + E( CUT_4 ) \ + E( INV_PASSPORT_CLOSED ) \ + E( INV_MAP ) \ + E( CRYSTAL ) \ + E( PISTOLS ) \ + E( SHOTGUN ) \ + E( MAGNUMS ) \ + E( UZIS ) \ + E( AMMO_PISTOLS ) \ + E( AMMO_SHOTGUN ) \ + E( AMMO_MAGNUMS ) \ + E( AMMO_UZIS ) \ + E( EXPLOSIVE ) \ + E( MEDIKIT_SMALL ) \ + E( MEDIKIT_BIG ) \ + E( INV_DETAIL ) \ + E( INV_SOUND ) \ + E( INV_CONTROLS ) \ + E( INV_GAMMA ) \ + E( INV_PISTOLS ) \ + E( INV_SHOTGUN ) \ + E( INV_MAGNUMS ) \ + E( INV_UZIS ) \ + E( INV_AMMO_PISTOLS ) \ + E( INV_AMMO_SHOTGUN ) \ + E( INV_AMMO_MAGNUMS ) \ + E( INV_AMMO_UZIS ) \ + E( INV_EXPLOSIVE ) \ + E( INV_MEDIKIT_SMALL ) \ + E( INV_MEDIKIT_BIG ) \ + E( PUZZLE_1 ) \ + E( PUZZLE_2 ) \ + E( PUZZLE_3 ) \ + E( PUZZLE_4 ) \ + E( INV_PUZZLE_1 ) \ + E( INV_PUZZLE_2 ) \ + E( INV_PUZZLE_3 ) \ + E( INV_PUZZLE_4 ) \ + E( PUZZLEHOLE_1 ) \ + E( PUZZLEHOLE_2 ) \ + E( PUZZLEHOLE_3 ) \ + E( PUZZLEHOLE_4 ) \ + E( PUZZLEHOLE_DONE_1 ) \ + E( PUZZLEHOLE_DONE_2 ) \ + E( PUZZLEHOLE_DONE_3 ) \ + E( PUZZLEHOLE_DONE_4 ) \ + E( LEADBAR ) \ + E( INV_LEADBAR ) \ + E( MIDAS_HAND ) \ + E( KEY_ITEM_1 ) \ + E( KEY_ITEM_2 ) \ + E( KEY_ITEM_3 ) \ + E( KEY_ITEM_4 ) \ + E( INV_KEY_ITEM_1 ) \ + E( INV_KEY_ITEM_2 ) \ + E( INV_KEY_ITEM_3 ) \ + E( INV_KEY_ITEM_4 ) \ + E( KEYHOLE_1 ) \ + E( KEYHOLE_2 ) \ + E( KEYHOLE_3 ) \ + E( KEYHOLE_4 ) \ + E( UNUSED_5 ) \ + E( UNUSED_6 ) \ + E( SCION_PICKUP_QUALOPEC ) \ + E( SCION_PICKUP_DROP ) \ + E( SCION_TARGET ) \ + E( SCION_PICKUP_HOLDER ) \ + E( SCION_HOLDER ) \ + E( UNUSED_7 ) \ + E( UNUSED_8 ) \ + E( INV_SCION ) \ + E( EXPLOSION ) \ + E( UNUSED_9 ) \ + E( SPLASH ) \ + E( UNUSED_10 ) \ + E( BUBBLE ) \ + E( UNUSED_11 ) \ + E( UNUSED_12 ) \ + E( BLOOD ) \ + E( UNUSED_13 ) \ + E( SMOKE ) \ + E( CENTAUR_STATUE ) \ + E( CABIN ) \ + E( MUTANT_EGG_SMALL ) \ + E( RICOCHET ) \ + E( SPARKLES ) \ + E( MUZZLE_FLASH ) \ + E( UNUSED_14 ) \ + E( UNUSED_15 ) \ + E( VIEW_TARGET ) \ + E( WATERFALL ) \ + E( NATLA_BULLET ) \ + E( MUTANT_BULLET ) \ + E( CENTAUR_BULLET ) \ + E( UNUSED_16 ) \ + E( UNUSED_17 ) \ + E( LAVA_PARTICLE ) \ + E( LAVA_EMITTER ) \ + E( FLAME ) \ + E( FLAME_EMITTER ) \ + E( TRAP_LAVA ) \ + E( MUTANT_EGG_BIG ) \ + E( BOAT ) \ + E( EARTHQUAKE ) \ + E( UNUSED_18 ) \ + E( UNUSED_19 ) \ + E( UNUSED_20 ) \ + E( UNUSED_21 ) \ + E( UNUSED_22 ) \ + E( LARA_BRAID ) \ + E( GLYPHS ) + +#define DECL_ENUM(v) ITEM_##v, + +enum ItemType { + ITEM_TYPES(DECL_ENUM) + TR1_ITEM_MAX, + ITEM_MAX = TR1_ITEM_MAX +}; + +#undef DECL_ENUM + +struct Location { + Room* room; + vec3i pos; +}; + +enum CameraMode { + CAMERA_MODE_FREE, + CAMERA_MODE_FOLLOW, + CAMERA_MODE_COMBAT, + CAMERA_MODE_LOOK, + CAMERA_MODE_FIXED, + CAMERA_MODE_OBJECT, + CAMERA_MODE_CUTSCENE +}; + +struct Camera { + Location view; + Location target; + + int32 targetDist; + vec3s targetAngle; + + vec3s angle; + + ItemObj* laraItem; + ItemObj* lastItem; + ItemObj* lookAtItem; + + int32 speed; + int32 timer; + int32 index; + int32 lastIndex; + + CameraMode mode; + + bool lastFixed; + bool center; + + void init(ItemObj* lara); + Location getLocationForAngle(int32 angle, int32 distH, int32 distV); + void clip(Location &loc); + Location getBestLocation(bool clip); + void move(Location &to, int32 speed); + void updateFree(); + void updateFollow(ItemObj* item); + void updateCombat(ItemObj* item); + void updateLook(ItemObj* item); + void updateFixed(); + void lookAt(int32 offset); + void update(); + void toCombat(); +}; + +enum ZoneType +{ + ZONE_GROUND_1, + ZONE_GROUND_2, + ZONE_FLY, + ZONE_MAX +}; + +struct Nav +{ + struct Cell + { + uint16 boxIndex; + uint16 weight; + uint16 end; + uint16 next; + }; + + Cell cells[MAX_BOXES]; + uint32 cellsCount; + + uint32 zoneType; + uint32 weight; + + uint32 endBox; + uint32 nextBox; + uint32 headBox; + uint32 tailBox; + int32 stepHeight; + int32 dropHeight; + int32 vSpeed; + uint32 mask; + + vec3i pos; + + void init(uint32 boxIndex); + void search(uint16 zone, const uint16* zones); + vec3i getWaypoint(uint32 boxIndex, const vec3i &from); +}; + +enum WeaponState +{ + WEAPON_STATE_FREE, + WEAPON_STATE_BUSY, + WEAPON_STATE_DRAW, + WEAPON_STATE_HOLSTER, + WEAPON_STATE_READY +}; + +enum Weapon +{ + WEAPON_PISTOLS, + WEAPON_MAGNUMS, + WEAPON_UZIS, + WEAPON_SHOTGUN, + // WEAPON_DESERT_EAGLE, + // WEAPON_REVOLVER, + // WEAPON_M16 + // WEAPON_MP5 + // WEAPON_HK + // WEAPON_ROCKET + // WEAPON_GRENADE + // WEAPON_HARPOON + // WEAPON_CROSSBOW + // WEAPON_GRAPPLING + // WEAPON_FLARE + WEAPON_NONE, + WEAPON_MAX +}; + +struct WeaponParams +{ + ItemType modelType; + ItemType animType; + uint16 damage; + uint16 spread; + uint16 range; + int16 height; + int16 soundId; + uint8 reloadTimer; + uint8 flashOffset; + uint8 flashTimer; + uint8 flashIntensity; + int16 aimX; + int16 aimY; + int16 armX; + int16 armMinY; + int16 armMaxY; +}; + +enum LaraArm +{ + LARA_ARM_R, + LARA_ARM_L, + LARA_ARM_MAX +}; + +enum LaraJoint +{ + JOINT_HIPS = 0, + JOINT_LEG_L1, + JOINT_LEG_L2, + JOINT_LEG_L3, + JOINT_LEG_R1, + JOINT_LEG_R2, + JOINT_LEG_R3, + JOINT_TORSO, + JOINT_ARM_R1, + JOINT_ARM_R2, + JOINT_ARM_R3, + JOINT_ARM_L1, + JOINT_ARM_L2, + JOINT_ARM_L3, + JOINT_HEAD, + JOINT_MAX +}; + +struct ExtraInfoLara +{ + int16 swimTimer; + uint8 weaponState; + uint8 vSpeedHack; + + int16 moveAngle; + int16 hitFrame; + + int8 hitTimer; + int8 hitQuadrant; + + uint8 weapon; + uint8 goalWeapon; + + struct Head { + vec3s angle; + } head; + + struct Torso { + vec3s angle; + } torso; + + struct Arm + { + vec3s angle; + vec3s angleAim; + + uint16 animIndex; + uint16 frameIndex; + + struct Flash { + int16 timer; + int16 angle; + int16 offset; + int16 intensity; + } flash; + + ItemObj* target; + + bool aim; + bool useBasis; + }; + + Arm armR; + Arm armL; + + Camera camera; + + uint16 meshes[JOINT_MAX]; + + int16 ammo[WEAPON_MAX]; // TODO make global + + Nav nav; + + uint16 lastInput; + int8 healthTimer; + + bool dozy; +}; + +extern ExtraInfoLara playersExtra[MAX_PLAYERS]; + +struct Enemy; + +enum EnemyMood +{ + MOOD_SLEEP, + MOOD_STALK, + MOOD_ATTACK, + MOOD_ESCAPE +}; + +struct ExtraInfoEnemy +{ + int16 rotHead; + int16 rotNeck; + + int16 maxTurn; + int16 _reserved; + + Enemy* enemy; + + Nav nav; +}; + +struct ItemObj +{ + Room* room; + + vec3i pos; + vec3s angle; + + uint16 flags; + + int16 vSpeed; + int16 hSpeed; + + union { + uint16 animIndex; + int16 tick; // effects only + }; + + uint16 frameIndex; + + uint8 state; + uint8 nextState; // enemies only + uint8 goalState; + uint8 waterState; + + uint16 headOffset; // enemies only + union { + uint16 gymTimer; // lara only + uint16 aggression; // enemies only + }; + + int16 health; + union { + int16 timer; + int16 oxygen; // Lara only + int16 radius; // enemies only TODO + }; + + union { + uint16 input; // Lara only + uint16 mood; // enemies only + int16 corpseTimer; // enemies only + }; + int16 turnSpeed; + + uint8 type; + uint8 intensity; + int16 roomFloor; + + uint32 hitMask; + uint32 visibleMask; + + union { + uint8* extra; + ExtraInfoLara* extraL; + ExtraInfoEnemy* extraE; + }; + + ItemObj* nextItem; + ItemObj* nextActive; + + static ItemObj* sFirstActive; + static ItemObj* sFirstFree; + + static ItemObj* add(ItemType type, Room* room, const vec3i &pos, int32 angleY); + void remove(); + + void fxBubbles(Room *fxRoom, int32 fxJoint, const vec3i &fxOffset); + void fxRicochet(Room *fxRoom, const vec3i &fxPos, bool fxSound); + void fxBlood(const vec3i &fxPos, int16 fxAngleY, int16 fxSpeed); + void fxSmoke(const vec3i &fxPos); + void fxSplash(); + + int32 getFrames(const AnimFrame* &frameA, const AnimFrame* &frameB, int32 &animFrameRate) const; + const AnimFrame* getFrame() const; + const AABBs& getBoundingBox(bool lerp) const; + void move(); + const Anim* animSet(int32 newAnimIndex, bool resetState, int32 frameOffset = 0); + const Anim* animChange(const Anim* anim); + void animCmd(bool fx, const Anim* anim); + void animSkip(int32 stateBefore, int32 stateAfter, bool advance = false); + void animProcess(bool movement = true); + bool animIsEnd(int32 offset) const; + void animHit(int32 dirX, int32 dirZ, int32 hitTimer); + bool moveTo(const vec3i &point, ItemObj* item, bool lerp); + + void updateRoom(int32 offset = 0); + + bool isKeyHit(InputState state) const; // Lara only + + vec3i getRelative(const vec3i &point) const; + + int32 getWaterLevel() const; + int32 getWaterDepth() const; + int32 getBridgeFloor(int32 x, int32 z) const; + int32 getTrapDoorFloor(int32 x, int32 z) const; + int32 getDrawBridgeFloor(int32 x, int32 z) const; + void getItemFloorCeiling(int32 x, int32 y, int32 z, int32* floor, int32* ceiling) const; + + vec3i getJoint(int32 jointIndex, const vec3i &offset) const; + int32 getSpheres(Sphere* spheres, bool flag) const; + + uint32 collideSpheres(Lara* lara) const; + bool collideBounds(Lara* lara, CollisionInfo* cinfo) const; + void collidePush(Lara* lara, CollisionInfo* cinfo, bool enemyHit) const; + void collideRoom(int32 height, int32 yOffset) const; + + uint32 updateHitMask(Lara* lara, CollisionInfo* cinfo); + + ItemObj* init(Room* room); + + X_INLINE ItemObj() {} + ItemObj(Room* room); + virtual void activate(); + virtual void deactivate(); + virtual void hit(int32 damage, const vec3i &point, int32 soundId); + virtual void collide(Lara* lara, CollisionInfo* cinfo); + virtual void update(); + virtual void draw(); + virtual uint8* save(uint8* data); + virtual uint8* load(uint8* data); +}; + +#define TRACK_FLAG_ONCE 32 +#define TRACK_FLAG_MASK 31 + +#define SAVEGAME_VER 2 +#define SAVEGAME_SIZE (8 * 1024) // 8k EWRAM + +struct SaveGame +{ + uint32 version; + uint32 dataSize; + + uint8 level; + int8 track; + uint8 secrets; + uint8 pickups; + uint32 time; + uint32 distance; + uint32 randSeedLogic; + uint32 randSeedDraw; + uint16 mediUsed; + uint16 ammoUsed; + uint16 kills; + uint16 flipped; + uint8 tracks[64]; + uint16 invSlots[64]; +}; + +#define SETTINGS_VER 3 +#define SETTINGS_SIZE 128 + +struct Settings +{ + uint8 version; + uint8 controls_vibration:1; + uint8 controls_swap:1; + uint8 audio_sfx:1; + uint8 audio_music:1; + uint8 video_gamma:5; + uint8 video_fps:1; + uint8 video_vsync:1; +}; + +#define FD_SET_END(x,end) ((x) |= ((end) << 15)) +#define FD_END(x) ((x) >> 15) +#define FD_FLOOR_TYPE(x) ((x) & 0x1F) +#define FD_TRIGGER_TYPE(x) (((x) >> 8) & 0x7F) +#define FD_TIMER(x) ((x) & 0xFF) +#define FD_ONCE(x) (((x) >> 8) & 1) +#define FD_SPEED(x) (((x) >> 9) & 0x1F) +#define FD_MASK(x) (((x) >> 9) & 0x1F) +#define FD_ACTION(x) (((x) >> 10) & 0x1F) +#define FD_ARGS(x) ((x) & 0x03FF) +#define FD_SLANT_X(x) int8((x) & 0xFF) +#define FD_SLANT_Z(x) int8((x) >> 8) + +typedef uint16 FloorData; + +enum FloorType +{ + FLOOR_TYPE_NONE, + FLOOR_TYPE_PORTAL, + FLOOR_TYPE_FLOOR, + FLOOR_TYPE_CEILING, + FLOOR_TYPE_TRIGGER, + FLOOR_TYPE_LAVA +}; + +enum TriggerType +{ + TRIGGER_TYPE_ACTIVATE, + TRIGGER_TYPE_PAD, + TRIGGER_TYPE_SWITCH, + TRIGGER_TYPE_KEY, + TRIGGER_TYPE_PICKUP, + TRIGGER_TYPE_OBJECT, + TRIGGER_TYPE_ANTIPAD, + TRIGGER_TYPE_COMBAT, + TRIGGER_TYPE_DUMMY +}; + +enum TriggerAction +{ + TRIGGER_ACTION_ACTIVATE_OBJECT, + TRIGGER_ACTION_ACTIVATE_CAMERA, + TRIGGER_ACTION_FLOW, + TRIGGER_ACTION_FLIP, + TRIGGER_ACTION_FLIP_ON, + TRIGGER_ACTION_FLIP_OFF, + TRIGGER_ACTION_CAMERA_TARGET, + TRIGGER_ACTION_END, + TRIGGER_ACTION_SOUNDTRACK, + TRIGGER_ACTION_EFFECT, + TRIGGER_ACTION_SECRET, + TRIGGER_ACTION_CLEAR_BODIES, + TRIGGER_ACTION_FLYBY, + TRIGGER_ACTION_CUTSCENE +}; + +enum SlantType +{ + SLANT_NONE, + SLANT_LOW, + SLANT_HIGH +}; + +enum WaterState +{ + WATER_STATE_ABOVE, + WATER_STATE_WADE, + WATER_STATE_SURFACE, + WATER_STATE_UNDER +}; + +enum AnimCommand +{ + ANIM_CMD_NONE, + ANIM_CMD_OFFSET, + ANIM_CMD_JUMP, + ANIM_CMD_EMPTY, + ANIM_CMD_KILL, + ANIM_CMD_SOUND, + ANIM_CMD_EFFECT +}; + +enum EffectType +{ + FX_NONE = -1, + FX_ROTATE_180 , + FX_FLOOR_SHAKE , + FX_LARA_NORMAL , + FX_LARA_BUBBLES , + FX_FINISH_LEVEL , + FX_EARTHQUAKE , + FX_FLOOD , + FX_UNK1 , + FX_STAIRS2SLOPE , + FX_UNK3 , + FX_UNK4 , + FX_EXPLOSION , + FX_LARA_HANDSFREE , + FX_FLIP_MAP , + FX_DRAW_RIGHTGUN , + FX_DRAW_LEFTGUN , + FX_SHOT_RIGHTGUN , + FX_SHOT_LEFTGUN , + FX_MESH_SWAP_1 , + FX_MESH_SWAP_2 , + FX_MESH_SWAP_3 , + FX_INV_ON , + FX_INV_OFF , + FX_DYN_ON , + FX_DYN_OFF , + FX_STATUE_FX , + FX_RESET_HAIR , + FX_BOILER_FX , + FX_ASSAULT_RESET , + FX_ASSAULT_STOP , + FX_ASSAULT_START , + FX_ASSAULT_FINISH , + FX_FOOTPRINT , +// specific + FX_TR1_FLICKER = 16 +}; + +enum SoundMode { + UNIQUE, + REPLAY, + LOOP +}; + +enum SoundID +{ + SND_NO = 2, + + SND_LANDING = 4, + + SND_DRAW = 6, + SND_HOLSTER = 7, + SND_PISTOLS_SHOT = 8, + SND_SHOTGUN_RELOAD = 9, + SND_RICOCHET = 10, + + SND_HIT_BEAR = 16, + SND_HIT_WOLF = 20, + + SND_SCREAM = 30, + SND_HIT = 27, + SND_DAMAGE = 31, + + SND_SPLASH = 33, + + SND_BUBBLE = 37, + + SND_UZIS_SHOT = 43, + SND_MAGNUMS_SHOT = 44, + SND_SHOTGUN_SHOT = 45, + SND_EMPTY = 48, + SND_HIT_UNDERWATER = 50, + + SND_UNDERWATER = 60, + + SND_BOULDER = 70, + + SND_FLOOD = 81, + + SND_HIT_LION = 85, + + SND_HIT_RAT = 95, + + SND_LIGHTNING = 98, + SND_ROCK = 99, + + SND_SWORD = 103, + SND_EXPLOSION = 104, + + SND_INV_SPIN = 108, + SND_INV_HOME = 109, + SND_INV_CONTROLS = 110, + SND_INV_SHOW = 111, + SND_INV_HIDE = 112, + SND_INV_COMPASS = 113, + SND_INV_WEAPON = 114, + SND_INV_PAGE = 115, + SND_HEALTH = 116, + + SND_STAIRS2SLOPE = 119, + + SND_NATLA_SHOT = 123, + + SND_HIT_SKATER = 132, + + SND_HIT_ADAM = 142, + SND_STOMP = 147, + + SND_LAVA = 149, + SND_FLAME = 150, + SND_DART = 151, + + SND_TNT = 170, + SND_MUTANT_DEATH = 171, + SND_SECRET = 173, + + SND_HELICOPTER = 297, + + SND_WINSTON_SCARED = 344, + SND_WINSTON_WALK = 345, + SND_WINSTON_PUSH = 346, + SND_WINSTON_TRAY = 347 +}; + +#define LARA_LOOK_ANGLE_MAX ANGLE(22) +#define LARA_LOOK_ANGLE_MIN ANGLE(-42) +#define LARA_LOOK_ANGLE_Y ANGLE(44) +#define LARA_LOOK_TURN_SPEED ANGLE(2) + +enum CollisionType +{ + CT_NONE = 0, + CT_FRONT = (1 << 0), + CT_LEFT = (1 << 1), + CT_RIGHT = (1 << 2), + CT_CEILING = (1 << 3), + CT_FRONT_CEILING = (1 << 4), + CT_FLOOR_CEILING = (1 << 5) +}; + +struct CollisionInfo +{ + enum SideType + { + ST_MIDDLE, + ST_FRONT, + ST_LEFT, + ST_RIGHT, + ST_MAX + }; + + struct Side + { + int32 floor; + int32 ceiling; + SlantType slantType; + }; + + const FloorData* trigger; + + Side m; + Side f; + Side l; + Side r; + + int32 radius; + + int32 gapPos; + int32 gapNeg; + int32 gapCeiling; + + vec3i offset; + vec3i pos; + + int16 angle; + uint16 quadrant; + + CollisionType type; + + int8 slantX; + int8 slantZ; + + bool enemyPush; + bool enemyHit; + bool staticHit; + bool stopOnSlant; + bool stopOnLava; + + void setSide(SideType st, int32 floor, int32 ceiling); + + X_INLINE void setAngle(int16 value) + { + angle = value; + quadrant = uint16(value + ANGLE_45) >> ANGLE_SHIFT_90; + } +}; + +struct Box +{ + uint8 minZ, maxZ; + uint8 minX, maxX; + int16 floor; + uint16 overlap; +}; + +struct Level +{ + uint32 magic; + + uint16 tilesCount; + uint16 roomsCount; + uint16 modelsCount; + uint16 meshesCount; + uint16 staticMeshesCount; + uint16 spriteSequencesCount; + uint16 soundSourcesCount; + uint16 boxesCount; + uint16 texturesCount; + uint16 spritesCount; + uint16 itemsCount; + uint16 camerasCount; + uint16 cameraFramesCount; + uint16 soundOffsetsCount; + + const uint16* palette; + const uint8* lightmap; + const uint8* tiles; + const RoomInfo* roomsInfo; + const FloorData* floors; + const Mesh** meshes; + const int32* meshOffsets; + const Anim* anims; + const AnimState* animStates; + const AnimRange* animRanges; + const int16* animCommands; + const ModelNode* nodes; + const uint16* animFrames; + const Model* models; + const StaticMesh* staticMeshes; + Texture* textures; + Sprite* sprites; + const SpriteSeq* spriteSequences; + FixedCamera* cameras; + uint32 soundSources; + Box* boxes; + const uint16* overlaps; + const uint16* zones[2][ZONE_MAX]; + const int16* animTexData; + const ItemObjInfo* itemsInfo; + uint32 cameraFrames; + const uint16* soundMap; + const SoundInfo* soundsInfo; + const uint8* soundData; + const int32* soundOffsets; +}; + +// used by enemies +struct TargetInfo +{ + ItemObj* target; + vec3i waypoint; + vec3i pos; + int16 angle; + int16 rotHead; + int16 tilt; + int16 turn; + uint32 dist; + uint16 boxIndex; + uint16 boxIndexTarget; + uint16 zoneIndex; + uint16 zoneIndexTarget; + bool aim; + bool canAttack; +}; + +extern TargetInfo tinfo; + +extern Level level; + +struct IMA_STATE +{ + int32 smp; + int32 idx; +}; + +#if defined(MODEHW) || defined(MODE13) + #define PROJ_SHIFT 4 + + #define PERSPECTIVE_DZ(z) (z >> PROJ_SHIFT) + + #define PERSPECTIVE(x, y, z) {\ + int32 dz = PERSPECTIVE_DZ(z);\ + if (dz >= DIV_TABLE_SIZE) dz = DIV_TABLE_SIZE - 1;\ + int32 d = FixedInvU(dz);\ + x = (x * d) >> (16 - PROJ_SHIFT);\ + y = (y * d) >> (16 - PROJ_SHIFT);\ + } +#elif defined(MODE4) + #define PERSPECTIVE_DZ(z) ((z >> 4) + (z >> 6)) + + #define PERSPECTIVE(x, y, z) {\ + int32 dz = PERSPECTIVE_DZ(z);\ + if (dz >= DIV_TABLE_SIZE) dz = DIV_TABLE_SIZE - 1;\ + int32 d = FixedInvU(dz);\ + x = (x * d) >> 12;\ + y = (y * d) >> 12;\ + } +#else + #define PERSPECTIVE(x, y, z) {\ + int32 dz = (z >> (FIXED_SHIFT + FOV_SHIFT - 1)) / 3;\ + if (dz >= DIV_TABLE_SIZE) dz = DIV_TABLE_SIZE - 1;\ + int32 d = FixedInvU(dz);\ + x = d * (x >> FIXED_SHIFT) >> 13;\ + y = d * (y >> FIXED_SHIFT) >> 13;\ + } +#endif + +#define STR_LANGUAGES \ + "English" \ + , "Fran|cais" \ + , "Deutsch" + +#define STR_SCALE "25", "50", "75", "100" + +enum StringID { + STR_EMPTY + , STR_ALPHA_END_1 + , STR_ALPHA_END_2 + , STR_ALPHA_END_3 + , STR_ALPHA_END_4 + , STR_ALPHA_END_5 + , STR_ALPHA_END_6 + , STR_GBA_SAVE_WARNING_1 + , STR_GBA_SAVE_WARNING_2 + , STR_GBA_SAVE_WARNING_3 +// common + , STR_LOADING + , STR_LEVEL_STATS + , STR_HINT_SAVING + , STR_HINT_SAVING_DONE + , STR_HINT_SAVING_ERROR + , STR_YES + , STR_NO + , STR_OFF + , STR_ON + , STR_OK + , STR_SBS + , STR_ANAGLYPH + , STR_SPLIT + , STR_VR + , STR_QUALITY_LOW + , STR_QUALITY_MEDIUM + , STR_QUALITY_HIGH + , STR_LANG_EN + , STR_LANG_FR + , STR_LANG_DE +// , STR_LANG_ES +// , STR_LANG_IT +// , STR_LANG_PL +// , STR_LANG_PT +// , STR_LANG_RU +// , STR_LANG_JA +// , STR_LANG_GR +// , STR_LANG_FI +// , STR_LANG_CZ +// , STR_LANG_CN +// , STR_LANG_HU +// , STR_LANG_SV + , STR_APPLY + , STR_GAMEPAD_1 + , STR_GAMEPAD_2 + , STR_GAMEPAD_3 + , STR_GAMEPAD_4 + , STR_NOT_READY + , STR_PLAYER_1 + , STR_PLAYER_2 + , STR_PRESS_ANY_KEY + , STR_HELP_SELECT + , STR_HELP_BACK +// inventory pages + , STR_INV_TITLE_OPTIONS + , STR_INV_TITLE_MAIN + , STR_INV_TITLE_KEYS +// save game page + , STR_SAVEGAME + , STR_CURRENT_POSITION +// inventory option + , STR_GAME + , STR_MAP + , STR_COMPASS + , STR_STOPWATCH + , STR_HOME + , STR_DETAIL + , STR_SOUND + , STR_CONTROLS + , STR_GAMMA +// passport menu + , STR_LOAD_GAME + , STR_SAVE_GAME + , STR_START_GAME + , STR_RESTART_LEVEL + , STR_EXIT_TO_TITLE + , STR_EXIT_GAME + , STR_SELECT_LEVEL +// detail options + , STR_SELECT_DETAIL + , STR_OPT_DETAIL_GAMMA + , STR_OPT_DETAIL_FPS + , STR_OPT_DETAIL_FILTER + , STR_OPT_DETAIL_LIGHTING + , STR_OPT_DETAIL_SHADOWS + , STR_OPT_DETAIL_WATER + , STR_OPT_DETAIL_VSYNC + , STR_OPT_DETAIL_STEREO + , STR_OPT_SIMPLE_ITEMS + , STR_OPT_RESOLUTION + , STR_SCALE_25 + , STR_SCALE_50 + , STR_SCALE_75 + , STR_SCALE_100 +// sound options + , STR_SET_VOLUMES + , STR_REVERBERATION + , STR_OPT_SUBTITLES + , STR_OPT_LANGUAGE + , STR_OPT_SOUND_SFX + , STR_OPT_SOUND_MUSIC +// controls options + , STR_SET_CONTROLS + , STR_OPT_CONTROLS_KEYBOARD + , STR_OPT_CONTROLS_GAMEPAD + , STR_OPT_CONTROLS_VIBRATION + , STR_OPT_CONTROLS_RETARGET + , STR_OPT_CONTROLS_MULTIAIM + , STR_OPT_CONTROLS_SWAP + // controls + , STR_CTRL_RUN + , STR_CTRL_BACK + , STR_CTRL_RIGHT + , STR_CTRL_LEFT + , STR_CTRL_WALK + , STR_CTRL_JUMP + , STR_CTRL_ACTION + , STR_CTRL_WEAPON + , STR_CTRL_LOOK + , STR_CTRL_ROLL + , STR_CTRL_INVENTORY + , STR_CTRL_PAUSE + // control keys + , STR_KEY_UP + , STR_KEY_DOWN + , STR_KEY_RIGHT + , STR_KEY_LEFT + , STR_KEY_A + , STR_KEY_B + , STR_KEY_L + , STR_KEY_R + , STR_KEY_SELECT + , STR_KEY_START + , STR_KEY_L_R + , STR_KEY_L_A + , STR_KEY_L_B +// inventory items + , STR_UNKNOWN + , STR_EXPLOSIVE + , STR_PISTOLS + , STR_SHOTGUN + , STR_MAGNUMS + , STR_UZIS + , STR_AMMO_PISTOLS + , STR_AMMO_SHOTGUN + , STR_AMMO_MAGNUMS + , STR_AMMO_UZIS + , STR_MEDI_SMALL + , STR_MEDI_BIG + , STR_LEAD_BAR + , STR_SCION +// keys + , STR_KEY + , STR_KEY_SILVER + , STR_KEY_RUSTY + , STR_KEY_GOLD + , STR_KEY_SAPPHIRE + , STR_KEY_NEPTUNE + , STR_KEY_ATLAS + , STR_KEY_DAMOCLES + , STR_KEY_THOR + , STR_KEY_ORNATE +// puzzles + , STR_PUZZLE + , STR_PUZZLE_GOLD_IDOL + , STR_PUZZLE_GOLD_BAR + , STR_PUZZLE_COG + , STR_PUZZLE_FUSE + , STR_PUZZLE_ANKH + , STR_PUZZLE_HORUS + , STR_PUZZLE_ANUBIS + , STR_PUZZLE_SCARAB + , STR_PUZZLE_PYRAMID +#ifdef USE_SUBTITLES +// TR1 subtitles + , STR_TR1_SUB_CAFE + , STR_TR1_SUB_LIFT + , STR_TR1_SUB_CANYON + , STR_TR1_SUB_PRISON + , STR_TR1_SUB_22 // CUT4 + , STR_TR1_SUB_23 // CUT1 + , STR_TR1_SUB_24 + , STR_TR1_SUB_25 // CUT3 + , STR_TR1_SUB_26 + , STR_TR1_SUB_27 + , STR_TR1_SUB_28 + , STR_TR1_SUB_29 + , STR_TR1_SUB_30 + , STR_TR1_SUB_31 + , STR_TR1_SUB_32 + , STR_TR1_SUB_33 + , STR_TR1_SUB_34 + , STR_TR1_SUB_35 + , STR_TR1_SUB_36 + , STR_TR1_SUB_37 + , STR_TR1_SUB_38 + , STR_TR1_SUB_39 + , STR_TR1_SUB_40 + , STR_TR1_SUB_41 + , STR_TR1_SUB_42 + , STR_TR1_SUB_43 + , STR_TR1_SUB_44 + , STR_TR1_SUB_45 + , STR_TR1_SUB_46 + , STR_TR1_SUB_47 + , STR_TR1_SUB_48 + , STR_TR1_SUB_49 + , STR_TR1_SUB_50 + , STR_TR1_SUB_51 + , STR_TR1_SUB_52 + , STR_TR1_SUB_53 + , STR_TR1_SUB_54 + , STR_TR1_SUB_55 + , STR_TR1_SUB_56 +#endif +// TR1 levels + , STR_TR1_GYM + , STR_TR1_LEVEL1 + , STR_TR1_LEVEL2 + , STR_TR1_LEVEL3A + , STR_TR1_LEVEL3B + , STR_TR1_LEVEL4 + , STR_TR1_LEVEL5 + , STR_TR1_LEVEL6 + , STR_TR1_LEVEL7A + , STR_TR1_LEVEL7B + , STR_TR1_LEVEL8A + , STR_TR1_LEVEL8B + , STR_TR1_LEVEL8C + , STR_TR1_LEVEL10A + , STR_TR1_LEVEL10B + , STR_TR1_LEVEL10C + , STR_TR1_EGYPT + , STR_TR1_CAT + , STR_TR1_END + , STR_TR1_END2 +// TR2 levels + , STR_TR2_ASSAULT + , STR_TR2_WALL + , STR_TR2_BOAT + , STR_TR2_VENICE + , STR_TR2_OPERA + , STR_TR2_RIG + , STR_TR2_PLATFORM + , STR_TR2_UNWATER + , STR_TR2_KEEL + , STR_TR2_LIVING + , STR_TR2_DECK + , STR_TR2_SKIDOO + , STR_TR2_MONASTRY + , STR_TR2_CATACOMB + , STR_TR2_ICECAVE + , STR_TR2_EMPRTOMB + , STR_TR2_FLOATING + , STR_TR2_XIAN + , STR_TR2_HOUSE +// TR3 levels + , STR_TR3_HOUSE + , STR_TR3_JUNGLE + , STR_TR3_TEMPLE + , STR_TR3_QUADCHAS + , STR_TR3_TONYBOSS + , STR_TR3_SHORE + , STR_TR3_CRASH + , STR_TR3_RAPIDS + , STR_TR3_TRIBOSS + , STR_TR3_ROOFS + , STR_TR3_SEWER + , STR_TR3_TOWER + , STR_TR3_OFFICE + , STR_TR3_NEVADA + , STR_TR3_COMPOUND + , STR_TR3_AREA51 + , STR_TR3_ANTARC + , STR_TR3_MINES + , STR_TR3_CITY + , STR_TR3_CHAMBER + , STR_TR3_STPAUL + + , STR_MAX +}; + +extern const char* const* STR; + +enum TrackID +{ + TRACK_NONE = -1, +// TR1 + TRACK_TR1_TITLE = 4, // TODO 2 + TRACK_TR1_CAVES = 5, + TRACK_TR1_SECRET = 13, + TRACK_TR1_CISTERN = 57, + TRACK_TR1_WIND = 58, + TRACK_TR1_PYRAMID = 59, + TRACK_TR1_CUT_1 = 23, + TRACK_TR1_CUT_2 = 25, + TRACK_TR1_CUT_3 = 24, + TRACK_TR1_CUT_4 = 22 +}; + +struct LevelInfo +{ + const char* name; + const void* data; + StringID title; + TrackID track; + uint8 secrets; +}; + +enum LevelID +{ + LVL_TR1_TITLE, + LVL_TR1_GYM, + LVL_TR1_1, + LVL_TR1_2, + LVL_TR1_3A, + LVL_TR1_3B, + LVL_TR1_CUT_1, + LVL_TR1_4, + LVL_TR1_5, + LVL_TR1_6, + LVL_TR1_7A, + LVL_TR1_7B, + LVL_TR1_CUT_2, + LVL_TR1_8A, + LVL_TR1_8B, + LVL_TR1_8C, + LVL_TR1_10A, + LVL_TR1_CUT_3, + LVL_TR1_10B, + LVL_TR1_CUT_4, + LVL_TR1_10C, + LVL_TR1_EGYPT, + LVL_TR1_CAT, + LVL_TR1_END, + LVL_TR1_END2, + LVL_LOAD, + LVL_MAX +}; + +extern const LevelInfo gLevelInfo[LVL_MAX]; +extern LevelID gLevelID; + +enum BarType { + BAR_HEALTH, + BAR_OXYGEN, + BAR_DASH, + BAR_OPTION, + BAR_MAX +}; + +enum TextAlign { + TEXT_ALIGN_LEFT, + TEXT_ALIGN_RIGHT, + TEXT_ALIGN_CENTER +}; + +// renderer internal +extern uint32 keys; +extern RectMinMax viewport; +extern vec3i gCameraViewPos; +extern Matrix* gMatrixPtr; +extern Matrix gMatrixStack[MAX_MATRICES]; +extern const uint32 gSinCosTable[4096]; + +extern Sphere gSpheres[2][MAX_SPHERES]; + +extern Settings gSettings; +extern SaveGame gSaveGame; +extern uint8 gSaveData[SAVEGAME_SIZE - sizeof(SaveGame)]; + +extern int32 gCurTrack; +extern int32 gAnimTexFrame; +extern int32 gBrightness; +extern int32 gLightAmbient; +extern int32 gRandTable[MAX_RAND_TABLE]; +extern int32 gCaustics[MAX_CAUSTICS]; +extern int32 gCausticsFrame; + +extern const FloorData* gLastFloorData; +extern FloorData gLastFloorSlant; + +extern Room rooms[MAX_ROOMS]; +extern ItemObj items[MAX_ITEMS]; + +template +X_INLINE void swap(T &a, T &b) { + T tmp = a; + a = b; + b = tmp; +} + +extern int32 gRandSeedLogic; +extern int32 gRandSeedDraw; + +int32 rand_logic(); +int32 rand_draw(); + +#define RAND_LOGIC(r) (rand_logic() * (r) >> 15) +#define RAND_DRAW(r) (rand_draw() * (r) >> 15) + +#define sincos(x,s,c) {\ + uint32 sc = gSinCosTable[uint32(x << 16) >> 20];\ + s = int32(sc) >> 16;\ + c = int32(sc) << 16 >> 16;\ +} + +#define sin(x) (int32(gSinCosTable[uint32(x << 16) >> 20]) >> 16) + +int32 phd_atan(int32 x, int32 y); +uint32 phd_sqrt(uint32 x); + +X_INLINE int32 dot(const vec3i &a, const vec3i &b) +{ + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +X_INLINE int32 fastLength(int32 dx, int32 dy) +{ + dx = abs(dx); + dy = abs(dy); + return (dx > dy) ? (dx + (dy >> 1)) : (dy + (dx >> 1)); +} + +void anglesFromVector(int32 x, int32 y, int32 z, int16 &angleX, int16 &angleY); + +X_INLINE int16 angleLerp(int16 a, int16 b, int32 w) +{ + int16 d = b - a; + if (d > +w) return a + w; + if (d < -w) return a - w; + return b; +} + +#define angleDec(angle, value) angleLerp(angle, 0, value) + +bool boxIntersect(const AABBi &i, const AABBi &b); +bool boxContains(const AABBi &a, const vec3i &p); +vec3i boxPushOut(const AABBi &a, const AABBi &b); + +#ifdef CPU_BIG_ENDIAN +#define DECODE_ANGLES(a,x,y,z)\ + x = (a & 0x3FF0) << 2;\ + y = (a & 0x000F) << 12 | ((a >> 16) & 0xFC00) >> 4;\ + z = ((a >> 16) & 0x03FF) << 6; +#else +#define DECODE_ANGLES(a,x,y,z)\ + x = ((a >> 16) & 0x3FF0) << 2;\ + y = ((a >> 16) & 0x000F) << 12 | (a & 0xFC00) >> 4;\ + z = (a & 0x03FF) << 6; +#endif + +#define matrixGet() *gMatrixPtr + +#ifdef USE_ASM + extern "C" { + void matrixPush_asm(); + void matrixSetIdentity_asm(); + void matrixSetBasis_asm(Matrix &dst, const Matrix &src); + void matrixLerp_asm(const Matrix &n, int32 pmul, int32 pdiv); + void matrixTranslateRel_asm(int32 x, int32 y, int32 z); + void matrixTranslateAbs_asm(int32 x, int32 y, int32 z); + void matrixTranslateSet_asm(int32 x, int32 y, int32 z); + void matrixRotateX_asm(int32 angle); + void matrixRotateY_asm(int32 angle); + void matrixRotateZ_asm(int32 angle); + void matrixRotateYQ_asm(int32 quadrant); + void matrixRotateYXZ_asm(int32 angleX, int32 angleY, int32 angleZ); + void boxTranslate_asm(AABBi &box, int32 x, int32 y, int32 z); + void boxRotateYQ_asm(AABBi &box, int32 quadrant); + int32 boxIsVisible_asm(const AABBs* box); + int32 sphereIsVisible_asm(int32 x, int32 y, int32 z, int32 r); + void flush_asm(); + } + + #define matrixPush matrixPush_asm + #define matrixSetIdentity matrixSetIdentity_asm + #define matrixSetBasis matrixSetBasis_asm + #define matrixLerp matrixLerp_asm + #define matrixTranslateRel matrixTranslateRel_asm + #define matrixTranslateAbs matrixTranslateAbs_asm + #define matrixTranslateSet matrixTranslateSet_asm + #define matrixRotateX matrixRotateX_asm + #define matrixRotateY matrixRotateY_asm + #define matrixRotateZ matrixRotateZ_asm + #define matrixRotateYXZ matrixRotateYXZ_asm + #define matrixRotateYQ matrixRotateYQ_asm + #define boxTranslate boxTranslate_asm + #define boxRotateYQ boxRotateYQ_asm + #define boxIsVisible boxIsVisible_asm + #define sphereIsVisible sphereIsVisible_asm + #define flush flush_asm +#else + #define matrixPush matrixPush_c + #define matrixSetIdentity matrixSetIdentity_c + #define matrixSetBasis matrixSetBasis_c + #define matrixLerp matrixLerp_c + #define matrixTranslateRel matrixTranslateRel_c + #define matrixTranslateAbs matrixTranslateAbs_c + #define matrixTranslateSet matrixTranslateSet_c + #define matrixRotateX matrixRotateX_c + #define matrixRotateY matrixRotateY_c + #define matrixRotateZ matrixRotateZ_c + #define matrixRotateYXZ matrixRotateYXZ_c + #define matrixRotateYQ matrixRotateYQ_c + #define boxTranslate boxTranslate_c + #define boxRotateYQ boxRotateYQ_c + #define boxIsVisible boxIsVisible_c + #define sphereIsVisible sphereIsVisible_c + #define flush flush_c + + void matrixPush_c(); + void matrixSetIdentity_c(); + void matrixSetBasis_c(Matrix &dst, const Matrix &src); + void matrixLerp_c(const Matrix &n, int32 pmul, int32 pdiv); + void matrixTranslateRel_c(int32 x, int32 y, int32 z); + void matrixTranslateAbs_c(int32 x, int32 y, int32 z); + void matrixTranslateSet_c(int32 x, int32 y, int32 z); + void matrixRotateX_c(int32 angle); + void matrixRotateY_c(int32 angle); + void matrixRotateZ_c(int32 angle); + void matrixRotateYQ_c(int32 quadrant); + void matrixRotateYXZ_c(int32 angleX, int32 angleY, int32 angleZ); + + void boxTranslate_c(AABBi &box, int32 x, int32 y, int32 z); + void boxRotateYQ_c(AABBi &box, int32 quadrant); + int32 boxIsVisible_c(const AABBs* box); + int32 sphereIsVisible_c(int32 x, int32 y, int32 z, int32 r); + void flush_c(); +#endif + +#define matrixPop() gMatrixPtr-- + +X_INLINE vec3i matrixGetDir(const Matrix &m) +{ + return _vec3i(m.e20, m.e21, m.e22); +} + +void matrixFrame(const void* pos, const void* angles); +void matrixFrameLerp(const void* pos, const void* anglesA, const void* anglesB, int32 delta, int32 rate); +void matrixSetView(const vec3i &pos, int32 angleX, int32 angleY); + +void renderInit(); +void setViewport(const RectMinMax &vp); +void setPaletteIndex(int32 index); +void clear(); +void renderRoom(const Room* room); +void renderMesh(const Mesh* mesh); +void renderShadow(int32 x, int32 z, int32 sx, int32 sz); +void renderSprite(int32 vx, int32 vy, int32 vz, int32 vg, int32 index); +void renderGlyph(int32 vx, int32 vy, int32 index); +void renderBorder(int32 x, int32 y, int32 width, int32 height, int32 shade, int32 color1, int32 color2, int32 z); +void renderBar(int32 x, int32 y, int32 width, int32 value, BarType type); +void renderBackground(const void* background); +void* copyBackground(); + +int32 getTextWidth(const char* text); + +void drawInit(); +void drawFree(); +void drawText(int32 x, int32 y, const char* text, TextAlign align); +void drawModel(const ItemObj* item); +void drawItem(const ItemObj* item); +void drawRooms(Camera* camera); +void drawHUD(Lara* lara); +void drawNodesLerp(const ItemObj* item, const AnimFrame* frameA, const AnimFrame* frameB, int32 frameDelta, int32 frameRate); + +void calcLightingDynamic(const Room* room, const vec3i &point); +void calcLightingStatic(int32 intensity); + +void checkTrigger(const FloorData* fd, ItemObj* lara); +void readLevel(const uint8 *data); +bool trace(const Location &from, Location &to, bool accurate); + +Lara* getLara(const vec3i &pos); + +bool useSwitch(ItemObj* item, int32 timer); +bool useKey(ItemObj* item, ItemObj* lara); +bool usePickup(ItemObj* item); + +void startLevel(const char* name); +void nextLevel(LevelID next); +bool gameSave(); +bool gameLoad(); + +int32 doTutorial(ItemObj* lara, int32 track); + +void sndInit(); +void sndInitSamples(); +void sndFreeSamples(); +void sndFill(uint8* buffer, int32 count); +void* sndPlaySample(int32 index, int32 volume, int32 pitch, int32 mode); +void sndPlayTrack(int32 track); +bool sndTrackIsPlaying(); +void sndStopTrack(); +void sndStopSample(int32 index); +void sndStop(); + +void palGrayRemap(uint8* data, int32 size); +void palSet(const uint16* palette, int32 gamma, int32 bright); + +void updateFading(int32 frames); + +void dmaFill(void* dst, uint8 value, uint32 count); +void dmaCopy(const void* src, void* dst, uint32 size); + +#endif diff --git a/src/fixed/draw.h b/src/fixed/draw.h new file mode 100644 index 00000000..9a712a8b --- /dev/null +++ b/src/fixed/draw.h @@ -0,0 +1,826 @@ +#ifndef H_DRAW +#define H_DRAW + +#include "common.h" +#include "item.h" + +void drawInit() +{ + for (int32 i = 0; i < MAX_RAND_TABLE; i++) + { + gRandTable[i] = (rand_draw() >> 5) - 511; + } + + for (int32 i = 0; i < MAX_CAUSTICS; i++) + { + int16 rot = i * (ANGLE_90 * 4) / MAX_CAUSTICS; + gCaustics[i] = sin(rot) * 768 >> FIXED_SHIFT; + } +} + +void drawFree() +{ + // +} + +void calcLightingDynamic(const Room* room, const vec3i &point) +{ + const RoomInfo* info = room->info; + + gLightAmbient = (info->ambient << 5); + + if (!info->lightsCount) + return; + + gLightAmbient = 8191 - gLightAmbient; + int32 maxLum = 0; + + for (int i = 0; i < info->lightsCount; i++) + { + const Light* light = room->data.lights + i; + + vec3i pos; + pos.x = light->pos.x + (info->x << 8); + pos.y = light->pos.y; + pos.z = light->pos.z + (info->z << 8); + + int32 radius = light->radius << 8; + int32 intensity = light->intensity << 5; + + vec3i d = point - pos; + int32 dist = dot(d, d) >> 12; + int32 att = X_SQR(radius) >> 12; + + int32 lum = (intensity * att) / (dist + att) + gLightAmbient; + + if (lum > maxLum) { + maxLum = lum; + } + } + + gLightAmbient = 8191 - ((maxLum + gLightAmbient) >> 1); + + Matrix &m = matrixGet(); + + int32 fogZ = m.e23 >> FIXED_SHIFT; + if (fogZ > FOG_MIN) { + gLightAmbient += (fogZ - FOG_MIN) << FOG_SHIFT; + gLightAmbient = X_MIN(gLightAmbient, 8191); + } +} + +void calcLightingStatic(int32 intensity) +{ + gLightAmbient = intensity - 4096; + + Matrix &m = matrixGet(); + + int32 fogZ = m.e23 >> FIXED_SHIFT; + if (fogZ > FOG_MIN) { + gLightAmbient += (fogZ - FOG_MIN) << FOG_SHIFT; + gLightAmbient = X_MIN(gLightAmbient, 8191); + } +} + +const static uint8 char_width[110] = { + 14, 11, 11, 11, 11, 11, 11, 13, 8, 11, 12, 11, 13, 13, 12, 11, 12, 12, 11, 12, 13, 13, 13, 12, 12, 11, // A-Z + 9, 9, 9, 9, 9, 9, 9, 9, 5, 9, 9, 5, 12, 10, 9, 9, 9, 8, 9, 8, 9, 9, 11, 9, 9, 9, // a-z + 12, 8, 10, 10, 10, 10, 10, 9, 10, 10, // 0-9 + 5, 5, 5, 11, 9, 7, 8, 6, 0, 7, 7, 3, 8, 8, 13, 7, 9, 4, 12, 12, + 7, 5, 7, 7, 7, 7, 7, 7, 7, 7, 16, 14, 14, 14, 16, 16, 16, 16, 16, 12, 14, 8, 8, 8, 8, 8, 8, 8 +}; + +static const uint8 char_map[102] = { + 0, 64, 66, 78, 77, 74, 78, 79, 69, 70, 92, 72, 63, 71, 62, 68, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 73, 73, 66, 74, 75, 65, 0, 0, 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, 80, 76, 81, 97, 98, 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 100, 101, 102, 67, 0, 0, 0, 0, 0, 0, 0 +}; + +X_INLINE int32 charRemap(char c) +{ + if (c < 11) + return c + 81; + if (c < 16) + return c + 91; + return char_map[c - 32]; +} + +int32 getTextWidth(const char* text) +{ + int32 w = 0; + + char c; + while ((c = *text++)) + { + if (c == ' ') { + w += 6; + continue; + } + w += char_width[charRemap(c)] + 1; + } + + return w; +} + +void drawText(int32 x, int32 y, const char* text, TextAlign align) +{ + if (!text || !*text) + return; + + if (align == TEXT_ALIGN_CENTER) { + x += (FRAME_WIDTH - getTextWidth(text)) >> 1; + } + + if (align == TEXT_ALIGN_RIGHT) { + x += FRAME_WIDTH - getTextWidth(text); + } + + int32 index; + char c; + while ((c = *text++)) + { + if (c == ' ') { + x += 6; + continue; + } + + if (c == '$') { // special char + index = *text++; + } else { + index = charRemap(c); + } + + int32 iy = y; + + if (c == 'p') { // TODO investigate! + iy--; + } + + renderGlyph(x, iy, level.models[ITEM_GLYPHS].start + index); + x += char_width[index] + 1; + } +} + +void drawMesh(int32 meshIndex) +{ + renderMesh(level.meshes[meshIndex]); +} + +void drawShadow(const ItemObj* item, int32 size) +{ + const Sector* sector = item->room->getSector(item->pos.x, item->pos.z); + int32 floor = sector->getFloor(item->pos.x, item->pos.y, item->pos.z); + + if (floor == WALL) + return; + + const AABBs& box = item->getBoundingBox(true); + int32 x = (box.maxX + box.minX) >> 1; + int32 z = (box.maxZ + box.minZ) >> 1; + int32 sx = (box.maxX - box.minX) * size >> 10; + int32 sz = (box.maxZ - box.minZ) * size >> 10; + + matrixPush(); + matrixTranslateAbs(item->pos.x, floor, item->pos.z); + matrixRotateY(item->angle.y); + + renderShadow(x, z, sx, sz); + + matrixPop(); +} + +void drawSprite(const ItemObj* item) +{ + renderSprite(item->pos.x, item->pos.y, item->pos.z, item->intensity << 5, level.models[item->type].start + item->frameIndex); +} + +void drawFlash(const ExtraInfoLara::Arm::Flash &flash) +{ + matrixPush(); + matrixTranslateRel(0, flash.offset, 55); + matrixRotateYXZ(-ANGLE_90, 0, flash.angle); + + int32 tmp = gLightAmbient; + calcLightingStatic(flash.intensity); + + drawMesh(level.models[ITEM_MUZZLE_FLASH].start); + + gLightAmbient = tmp; + + matrixPop(); +} + +void drawNodes(const ItemObj* item, const AnimFrame* frameA) +{ + const Model* model = level.models + item->type; + const ModelNode* node = level.nodes + model->nodeIndex; + int32 meshIndex = model->start; + int32 meshCount = model->count; + uint32 visibleMask = item->visibleMask; + + const uint32* angles = (uint32*)(frameA->angles + 1); + const int16* extraAngles = (int16*)item->extra; + + matrixFrame(&frameA->pos, angles); + if (visibleMask & 1) { + drawMesh(meshIndex); + } + + while (meshCount > 1) + { + meshIndex++; + visibleMask >>= 1; + angles++; + + if (node->flags & NODE_FLAG_POP) matrixPop(); + if (node->flags & NODE_FLAG_PUSH) matrixPush(); + + matrixFrame(&node->pos, angles); + + if (extraAngles) + { + if (node->flags & NODE_FLAG_ROTY) matrixRotateY(*extraAngles++); + if (node->flags & NODE_FLAG_ROTX) matrixRotateX(*extraAngles++); + if (node->flags & NODE_FLAG_ROTZ) matrixRotateZ(*extraAngles++); + } + + if (visibleMask & 1) { + drawMesh(meshIndex); + } + + meshCount--; + node++; + } +} + +void drawNodesLerp(const ItemObj* item, const AnimFrame* frameA, const AnimFrame* frameB, int32 frameDelta, int32 frameRate) +{ + if (frameDelta == 0) + { + drawNodes(item, frameA); + return; + } + + const Model* model = level.models + item->type; + const ModelNode* node = level.nodes + model->nodeIndex; + int32 meshIndex = model->start; + int32 meshCount = model->count; + uint32 visibleMask = item->visibleMask; + + const uint32* anglesA = (uint32*)(frameA->angles + 1); + const uint32* anglesB = (uint32*)(frameB->angles + 1); + const int16* extraAngles = (int16*)item->extra; + + int32 t = GET_FRAME_T(frameDelta, frameRate); + + vec4s posLerp; + posLerp.x = frameA->pos.x + ((frameB->pos.x - frameA->pos.x) * t >> 16); + posLerp.y = frameA->pos.y + ((frameB->pos.y - frameA->pos.y) * t >> 16); + posLerp.z = frameA->pos.z + ((frameB->pos.z - frameA->pos.z) * t >> 16); + + matrixFrameLerp(&posLerp, anglesA, anglesB, frameDelta, frameRate); + if (visibleMask & 1) { + drawMesh(meshIndex); + } + + while (meshCount > 1) + { + meshIndex++; + visibleMask >>= 1; + anglesA++; + anglesB++; + + if (node->flags & NODE_FLAG_POP) matrixPop(); + if (node->flags & NODE_FLAG_PUSH) matrixPush(); + + matrixFrameLerp(&node->pos, anglesA, anglesB, frameDelta, frameRate); + + if (extraAngles) + { + if (node->flags & NODE_FLAG_ROTY) matrixRotateY(*extraAngles++); + if (node->flags & NODE_FLAG_ROTX) matrixRotateX(*extraAngles++); + if (node->flags & NODE_FLAG_ROTZ) matrixRotateZ(*extraAngles++); + } + + if (visibleMask & 1) { + drawMesh(meshIndex); + } + + meshCount--; + node++; + } +} + +#define DEF_TORSO_ANGLE_X 1216 +#define DEF_TORSO_ANGLE_Y -832 +#define DEF_TORSO_ANGLE_Z -192 + +const uint32 ZERO_POS[2] = { 0, 0 }; + +void drawLaraNodes(const ItemObj* lara, const AnimFrame* frameA) +{ + const Model* model = level.models + lara->type; + const ModelNode* node = level.nodes + model->nodeIndex; + const ExtraInfoLara* extraL = lara->extraL; + + const uint16* mesh = extraL->meshes; + + const uint32* anglesArm[LARA_ARM_MAX]; + const uint32* angles = anglesArm[LARA_ARM_R] = anglesArm[LARA_ARM_L] = (uint32*)(frameA->angles + 1); + int32 frameSize = (sizeof(AnimFrame) >> 1) + (model->count << 1); + + vec3s torsoAngle = extraL->torso.angle; + + for (int32 i = 0; i < LARA_ARM_MAX; i++) + { + const ExtraInfoLara::Arm* arm = &extraL->armR + i; + + if (arm->animIndex) + { + const Anim* anim = level.anims + arm->animIndex; + const AnimFrame* frame = (AnimFrame*)(level.animFrames + (anim->frameOffset >> 1) + arm->frameIndex * frameSize); + anglesArm[i] = (uint32*)(frame->angles + 1); + + // additional torso animation for shotgun + if (extraL->weapon == WEAPON_SHOTGUN && i == LARA_ARM_R) + { + int32 aX, aY, aZ; + DECODE_ANGLES(*((uint32*)(frame->angles + 1) + JOINT_TORSO), aX, aY, aZ); + torsoAngle.x = torsoAngle.x + aX - DEF_TORSO_ANGLE_X; + torsoAngle.y = torsoAngle.y + aY - DEF_TORSO_ANGLE_Y; + torsoAngle.z = torsoAngle.z + aZ - DEF_TORSO_ANGLE_Z; + } + } + } + + anglesArm[LARA_ARM_R] += JOINT_ARM_R1; + anglesArm[LARA_ARM_L] += JOINT_ARM_L1; + + const Matrix& basis = matrixGet(); + + matrixPush(); + { // JOINT_HIPS + matrixFrame(&frameA->pos, angles++); + drawMesh(*mesh++); + + for (int32 i = 0; i < 2; i++) // draw Left & Right legs + { + matrixPush(); + { // JOINT_LEG_1 + matrixFrame(&(node++)->pos, angles++); + drawMesh(*mesh++); + + { // JOINT_LEG_2 + matrixFrame(&(node++)->pos, angles++); + drawMesh(*mesh++); + + { // JOINT_LEG_3 + matrixFrame(&(node++)->pos, angles++); + drawMesh(*mesh++); + } + } + } + matrixPop(); + } + + { // JOINT_TORSO + matrixFrame(&(node++)->pos, angles++); + matrixRotateYXZ(torsoAngle.x, torsoAngle.y, torsoAngle.z); + drawMesh(*mesh++); + + for (int32 i = 0; i < LARA_ARM_MAX; i++) // draw Right & Left arms + { + const ExtraInfoLara::Arm* arm = &extraL->armR + i; + + matrixPush(); + // JOINT_ARM_1 + matrixTranslateRel(node->pos.x, node->pos.y, node->pos.z); + node++; + if (arm->useBasis) { // hands are rotated relative to the basis + matrixSetBasis(matrixGet(), basis); + matrixRotateYXZ(arm->angle.x, arm->angle.y, arm->angle.z); + } + matrixFrame(&ZERO_POS, anglesArm[i]++); + drawMesh(*mesh++); + + { // JOINT_ARM_2 + matrixFrame(&(node++)->pos, anglesArm[i]++); + drawMesh(*mesh++); + + { // JOINT_ARM_3 + matrixFrame(&(node++)->pos, anglesArm[i]); + drawMesh(*mesh++); + + if (arm->flash.timer) { // muzzle flash + drawFlash(arm->flash); + } + } + } + matrixPop(); + } + + { // JOINT_HEAD + matrixFrame(&(node++)->pos, angles + 3 * LARA_ARM_MAX); + matrixRotateYXZ(extraL->head.angle.x, extraL->head.angle.y, extraL->head.angle.z); + drawMesh(*mesh++); + } + } + } + matrixPop(); +} + +void drawLaraNodesLerp(const ItemObj* lara, const AnimFrame* frameA, const AnimFrame* frameB, int32 frameDelta, int32 frameRate) +{ + if (frameDelta == 0) + { + drawLaraNodes(lara, frameA); + return; + } + + const Model* model = level.models + lara->type; + const ModelNode* node = level.nodes + model->nodeIndex; + const ExtraInfoLara* extraL = lara->extraL; + + const uint16* mesh = extraL->meshes; + + const uint32* anglesArmA[LARA_ARM_MAX]; + const uint32* anglesArmB[LARA_ARM_MAX]; + const uint32* anglesA = anglesArmA[LARA_ARM_R] = anglesArmA[LARA_ARM_L] = (uint32*)(frameA->angles + 1); + const uint32* anglesB = anglesArmB[LARA_ARM_R] = anglesArmB[LARA_ARM_L] = (uint32*)(frameB->angles + 1); + int32 frameRateArm[2]; + frameRateArm[0] = frameRateArm[1] = frameRate; + + int32 frameSize = (sizeof(AnimFrame) >> 1) + (model->count << 1); + + vec3s torsoAngle = extraL->torso.angle; + + for (int32 i = 0; i < LARA_ARM_MAX; i++) + { + const ExtraInfoLara::Arm* arm = &extraL->armR + i; + + if (arm->animIndex) + { + const Anim* anim = level.anims + arm->animIndex; + const AnimFrame* frame = (AnimFrame*)(level.animFrames + (anim->frameOffset >> 1) + arm->frameIndex * frameSize); + anglesArmA[i] = anglesArmB[i] = (uint32*)(frame->angles + 1); // no lerp for armed hands (frameRate == 1) + ASSERT(anim->frameRate == 1); + frameRateArm[i] = anim->frameRate; + + // additional torso animation for shotgun + if (extraL->weapon == WEAPON_SHOTGUN && i == LARA_ARM_R) + { + int32 aX, aY, aZ; + DECODE_ANGLES(*((uint32*)(frame->angles + 1) + JOINT_TORSO), aX, aY, aZ); + torsoAngle.x = torsoAngle.x + aX - DEF_TORSO_ANGLE_X; + torsoAngle.y = torsoAngle.y + aY - DEF_TORSO_ANGLE_Y; + torsoAngle.z = torsoAngle.z + aZ - DEF_TORSO_ANGLE_Z; + } + } + } + + anglesArmA[LARA_ARM_R] += JOINT_ARM_R1; + anglesArmB[LARA_ARM_R] += JOINT_ARM_R1; + anglesArmA[LARA_ARM_L] += JOINT_ARM_L1; + anglesArmB[LARA_ARM_L] += JOINT_ARM_L1; + + const Matrix& basis = matrixGet(); + + matrixPush(); + { // JOINT_HIPS + int32 t = GET_FRAME_T(frameDelta, frameRate); + + vec4s posLerp; + posLerp.x = frameA->pos.x + ((frameB->pos.x - frameA->pos.x) * t >> 16); + posLerp.y = frameA->pos.y + ((frameB->pos.y - frameA->pos.y) * t >> 16); + posLerp.z = frameA->pos.z + ((frameB->pos.z - frameA->pos.z) * t >> 16); + + matrixFrameLerp(&posLerp, anglesA++, anglesB++, frameDelta, frameRate); + drawMesh(*mesh++); + + for (int32 i = 0; i < 2; i++) // draw Left & Right legs + { + matrixPush(); + { // JOINT_LEG_1 + matrixFrameLerp(&(node++)->pos, anglesA++, anglesB++, frameDelta, frameRate); + drawMesh(*mesh++); + + { // JOINT_LEG_2 + matrixFrameLerp(&(node++)->pos, anglesA++, anglesB++, frameDelta, frameRate); + drawMesh(*mesh++); + + { // JOINT_LEG_3 + matrixFrameLerp(&(node++)->pos, anglesA++, anglesB++, frameDelta, frameRate); + drawMesh(*mesh++); + } + } + } + matrixPop(); + } + + { // JOINT_TORSO + matrixFrameLerp(&(node++)->pos, anglesA++, anglesB++, frameDelta, frameRate); + matrixRotateYXZ(torsoAngle.x, torsoAngle.y, torsoAngle.z); + drawMesh(*mesh++); + + for (int32 i = 0; i < LARA_ARM_MAX; i++) // draw Right & Left arms + { + const ExtraInfoLara::Arm* arm = &extraL->armR + i; + + matrixPush(); + // JOINT_ARM_1 + matrixTranslateRel(node->pos.x, node->pos.y, node->pos.z); + node++; + if (arm->useBasis) { // hands are rotated relative to the basis + matrixSetBasis(matrixGet(), basis); + matrixRotateYXZ(arm->angle.x, arm->angle.y, arm->angle.z); + } + + bool useLerp = frameRateArm[i] > 1; // armed hands always use frameRate == 1 (i.e. useLerp == false) + + if (useLerp) { + matrixFrameLerp(&ZERO_POS, anglesArmA[i]++, anglesArmB[i]++, frameDelta, frameRate); + } else { + matrixFrame(&ZERO_POS, anglesArmA[i]++); + } + drawMesh(*mesh++); + + { // JOINT_ARM_2 + if (useLerp) { + matrixFrameLerp(&(node++)->pos, anglesArmA[i]++, anglesArmB[i]++, frameDelta, frameRate); + } else { + matrixFrame(&(node++)->pos, anglesArmA[i]++); + } + drawMesh(*mesh++); + + { // JOINT_ARM_3 + if (useLerp) { + matrixFrameLerp(&(node++)->pos, anglesArmA[i], anglesArmB[i], frameDelta, frameRate); + } else { + matrixFrame(&(node++)->pos, anglesArmA[i]); + } + drawMesh(*mesh++); + + if (arm->flash.timer) { // muzzle flash + drawFlash(arm->flash); + } + } + } + matrixPop(); + } + + { // JOINT_HEAD + matrixFrameLerp(&(node++)->pos, anglesA + 3 * LARA_ARM_MAX, anglesB + 3 * LARA_ARM_MAX, frameDelta, frameRate); + matrixRotateYXZ(extraL->head.angle.x, extraL->head.angle.y, extraL->head.angle.z); + drawMesh(*mesh++); + } + } + } + matrixPop(); +} + +void drawModel(const ItemObj* item) +{ + const AnimFrame *frameA, *frameB; + + int32 frameRate; + int32 frameDelta = item->getFrames(frameA, frameB, frameRate); + +#ifdef NO_ANIM_LERP + frameDelta = 0; +#endif + + matrixPush(); + matrixTranslateAbs(item->pos.x, item->pos.y, item->pos.z); + matrixRotateYXZ(item->angle.x, item->angle.y, item->angle.z); + + int32 vis = boxIsVisible(&frameA->box); + + if (vis) + { + int32 intensity = item->intensity << 5; + + if (intensity == 0) { + vec3i point = item->getRelative(frameA->box.getCenter()); + calcLightingDynamic(item->room, point); + } else { + calcLightingStatic(intensity); + } + + // skip rooms portal clipping for objects + if (item->type == ITEM_LARA) { + drawLaraNodesLerp(item, frameA, frameB, frameDelta, frameRate); + } else { + drawNodesLerp(item, frameA, frameB, frameDelta, frameRate); + } + } + + matrixPop(); + +// shadow + if (vis && (item->flags & ITEM_FLAG_SHADOW)) { + drawShadow(item, 160); // TODO per item shadow size + } +} + +void drawItem(const ItemObj* item) +{ + if (level.models[item->type].count > 0) { + drawModel(item); + } else { + drawSprite(item); + } +} + +void drawRoom(const Room* room) +{ + setViewport(room->clip); + + const RoomInfo* info = room->info; + const RoomData& data = room->data; + + int32 rx = info->x << 8; + int32 ry = info->yTop; + int32 rz = info->z << 8; + + matrixPush(); + matrixTranslateAbs(rx, ry, rz); + + setPaletteIndex(ROOM_FLAG_WATER(info->flags) ? 1 : 0); + + renderRoom(room); + + matrixPop(); + + for (int32 i = 0; i < info->spritesCount; i++) + { + const RoomSprite* sprite = data.sprites + i; + renderSprite(sprite->pos.x + rx, sprite->pos.y, sprite->pos.z + rz, sprite->g << 5, sprite->index); + } + + rx -= gCameraViewPos.x; + ry = -gCameraViewPos.y; + rz -= gCameraViewPos.z; + + for (int32 i = 0; i < info->meshesCount; i++) + { + const RoomMesh* mesh = data.meshes + i; + + #ifdef NO_STATIC_MESH_PLANTS + if (STATIC_MESH_ID(mesh->zf) < 10) continue; + #endif + + const StaticMesh* staticMesh = level.staticMeshes + STATIC_MESH_ID(mesh->zf); + + if (!(staticMesh->flags & STATIC_MESH_FLAG_VISIBLE)) continue; // invisible + + int32 px = rx + (int32(mesh->xy) >> 16); + int32 py = ry + (int32(mesh->xy) << 16 >> 16); + int32 pz = rz + (int32(mesh->zf) >> 16); + + int32 sx = (int32(staticMesh->vs.xy) >> 16); + int32 sy = (int32(staticMesh->vs.xy) << 16 >> 16); + int32 sz = (int32(staticMesh->vs.zr) >> 16); + int32 sr = (int32(staticMesh->vs.zr) << 16 >> 16); + + // rotate visible sphere offset + int32 q = STATIC_MESH_QUADRANT(mesh->zf); + if (q == 0) { + sx = -sx; + sz = -sz; + } else if (q == 1) { + int32 t = sx; + sx = -sz; + sz = t; + } else if (q == 3) { + int32 t = sz; + sz = -sx; + sx = t; + } + + sx += px; + sy += py; + sz += pz; + + if (!sphereIsVisible(sx, sy, sz, sr)) + continue; + + matrixPush(); + matrixTranslateSet(px, py, pz); + matrixRotateYQ(q); + + int32 vis = boxIsVisible(&staticMesh->vbox); + if (vis) { + calcLightingStatic(STATIC_MESH_INTENSITY(mesh->zf)); + drawMesh(staticMesh->meshIndex); + } + + matrixPop(); + } + + ItemObj* item = room->firstItem; + while (item) + { + if ((item->flags & ITEM_FLAG_STATUS) != ITEM_FLAG_STATUS_INVISIBLE) { + item->draw(); + } + item = item->nextItem; + } +} + +void drawRooms(Camera* camera) +{ + RectMinMax vp = viewport; + + camera->view.room->clip = viewport; + + Room** visRoom = camera->view.room->getVisibleRooms(); + + // draw Lara first + for (int32 i = 0; i < MAX_PLAYERS; i++) + { + Lara* lara = players[i]; + if (lara) + { + lara->flags &= ~ITEM_FLAG_STATUS; + setPaletteIndex(ROOM_FLAG_WATER(lara->room->info->flags) ? 1 : 0); + lara->draw(); + lara->flags |= ITEM_FLAG_STATUS_INVISIBLE; // skip drawing in the general pass + } + } + + // draw rooms and objects + while (*visRoom) + { + Room* room = *visRoom++; + drawRoom(room); + room->reset(); + } + + // reset visibility flags for Lara + for (int32 i = 0; i < MAX_PLAYERS; i++) + { + Lara* lara = players[i]; + if (lara) + { + lara->flags &= ~ITEM_FLAG_STATUS; + } + } + + setPaletteIndex(0); + setViewport(vp); +} + +void drawHUD(Lara* lara) +{ + int32 x = (FRAME_WIDTH - (100 + 2 + 2)) - 4; + int32 y = 4; + + if (lara->waterState == WATER_STATE_SURFACE || lara->waterState == WATER_STATE_UNDER) + { + int32 v = (lara->oxygen << 8) / LARA_MAX_OXYGEN; + renderBar(x, y, 100, v, BAR_OXYGEN); + y += 10; + } + + if (lara->extraL->healthTimer || lara->extraL->weaponState == WEAPON_STATE_READY) + { + int32 v = (lara->health << 8) / LARA_MAX_HEALTH; + renderBar(x, y, 100, v, BAR_HEALTH); + } +} + +void drawFPS() +{ +#ifdef __GBA__ + if (gLevelID == LVL_TR1_TITLE) + { + if (REG_WSCNT != (WS_ROM0_N2 | WS_ROM0_S1 | WS_PREFETCH)) + { + drawText(0, 15, "! slow cartridge !", TEXT_ALIGN_CENTER); + } + } +#endif + + if (!gSettings.video_fps) + return; + + char buf[32]; + int2str(fps, buf); + drawText(2, 16, buf, TEXT_ALIGN_LEFT); +} + +void drawProfiling() +{ +#ifdef PROFILING + for (int32 i = 0; i < CNT_MAX; i++) + { + char buf[32]; + int2str(gCounters[i], buf); + drawText(2, 16 + 32 + i * 16, buf, TEXT_ALIGN_LEFT); + } + flush(); +#endif +} + +#endif diff --git a/src/fixed/enemy.h b/src/fixed/enemy.h new file mode 100644 index 00000000..7d1047ff --- /dev/null +++ b/src/fixed/enemy.h @@ -0,0 +1,1285 @@ +#ifndef H_ENEMY +#define H_ENEMY + +#include "common.h" +#include "item.h" + +#define ENEMY_TURN_FAST ANGLE(5) +#define ENEMY_TURN_SLOW ANGLE(2) + +EWRAM_DATA ExtraInfoEnemy enemiesExtra[MAX_ENEMIES]; + +enum AggressionLevel +{ + AGGRESSION_LVL_1 = 0x400, + AGGRESSION_LVL_2 = 0x2000, + AGGRESSION_LVL_3 = 0x4000, + AGGRESSION_LVL_MAX = 0x7FFF +}; + +struct Enemy : ItemObj +{ + Enemy(Room* room, int32 _health, int32 _radius, int32 _headOffset, int32 _aggression) : ItemObj(room) + { + flags |= ITEM_FLAG_SHADOW; + + #ifndef STATIC_ITEMS + angle.y += (rand_logic() - 0x4000) >> 1; + #endif + + ASSERT(_radius < 512); + + health = _health; + radius = _radius >> 1; + headOffset = _headOffset; + aggression = _aggression; + } + + void setExtra(ExtraInfoEnemy* extra) + { + ASSERT(!extraE); + ASSERT(extra); + + if (extra->enemy) + { + extra->enemy->flags &= ~ITEM_FLAG_STATUS; + extra->enemy->flags |= ITEM_FLAG_STATUS_INVISIBLE; + extra->enemy->disable(); + } + + extra->enemy = this; + extra->rotHead = extra->rotNeck = 0; + extra->maxTurn = 0; + extra->nav.stepHeight = 256; + extra->nav.dropHeight = -256; + extra->nav.vSpeed = 0; + extraE = extra; + + initExtra(); + + const Sector* sector = room->getSector(pos.x, pos.z); + ASSERT(sector->boxIndex != NO_BOX); + extraE->nav.init(sector->boxIndex); + } + + bool enable(bool forced) + { + if (extraE) + return true; + + int32 index = -1; + + for (int32 i = 0; i < MAX_ENEMIES; i++) + { + if (!enemiesExtra[i].enemy) + { + index = i; + break; + } + } + + if (index == -1) + { + for (int32 i = 0; i < MAX_PLAYERS; i++) + { + if (!players[i]) + continue; + + vec3i &viewPos = players[i]->extraL->camera.view.pos; + + int32 maxDistQ = 0; + + if (!forced) + { + vec3i d = pos - viewPos; + maxDistQ = X_SQR(d.x >> 8) + X_SQR(d.y >> 8) + X_SQR(d.z >> 8); + } + + for (int32 j = 0; j < MAX_ENEMIES; j++) + { + vec3i d = enemiesExtra[j].enemy->pos - viewPos; + int32 distQ = X_SQR(d.x >> 8) + X_SQR(d.y >> 8) + X_SQR(d.z >> 8); + if (distQ > maxDistQ) + { + maxDistQ = distQ; + index = j; + } + } + } + } + + if (index == -1) + return false; + + setExtra(enemiesExtra + index); + return true; + } + + void disable() + { + ASSERT(extraE); + extraE->enemy = NULL; + extraE = NULL; + } + + void updateTargetInfo() + { + tinfo.target = getLara(pos); + + if (tinfo.target->health <= 0) { + hitMask = 0; + } + + tinfo.tilt = 0; + tinfo.turn = 0; + tinfo.pos = pos; + + if (health <= 0) + { + tinfo.angle = 0; + tinfo.rotHead = 0; + tinfo.aim = false; + tinfo.rotHead = 0; + return; + } + + int32 dx = tinfo.target->pos.x - pos.x; + int32 dz = tinfo.target->pos.z - pos.z; + + if (headOffset) + { + int32 s, c; + sincos(angle.y, s, c); + dx -= s * headOffset >> FIXED_SHIFT; + dz -= c * headOffset >> FIXED_SHIFT; + } + + int16 rot = phd_atan(dz, dx); + + tinfo.dist = fastLength(dx, dz); + tinfo.angle = rot - angle.y; + tinfo.aim = (tinfo.angle > -ANGLE_90) && (tinfo.angle < ANGLE_90); + tinfo.canAttack = tinfo.aim && (abs(tinfo.target->pos.y - pos.y) < 256); + tinfo.rotHead = (tinfo.aim && mood != MOOD_SLEEP) ? tinfo.angle : 0; + + // update navigation target + const uint16* zones = getZones(); + + tinfo.boxIndex = room->getSector(pos.x, pos.z)->boxIndex; + tinfo.boxIndexTarget = tinfo.target->room->getSector(tinfo.target->pos.x, tinfo.target->pos.z)->boxIndex; + tinfo.zoneIndex = zones[tinfo.boxIndex]; + tinfo.zoneIndexTarget = zones[tinfo.boxIndexTarget]; + + //@TODO blocking + } + + void updateMood() + { + //if (extraE->nav.cells[extraE->boxIndex].weight == ) //@TODO blocking + + if (mood != MOOD_ATTACK && extraE->nav.nextBox != NO_BOX && !checkZone(extraE->nav.endBox)) + { + bool inZone = checkZone(); + if (!inZone) { + mood = MOOD_SLEEP; + } + extraE->nav.nextBox = NO_BOX; + } + + uint32 nextMood = tinfo.target->health <= 0 ? MOOD_SLEEP : ((flags & ITEM_FLAG_ANIMATED) ? getMoodWild() : getMoodNormal()); + + flags &= ~ITEM_FLAG_INJURED; + + if (mood != nextMood) + { + if (mood == MOOD_ATTACK) { + setNextBox(extraE->nav.endBox); + } + extraE->nav.nextBox = NO_BOX; + mood = nextMood; + } + + switch (mood) + { + case MOOD_SLEEP : moodSleep(); break; + case MOOD_STALK : moodStalk(); break; + case MOOD_ATTACK : moodAttack(); break; + case MOOD_ESCAPE : moodEscape(); break; + } + + if (extraE->nav.endBox == NO_BOX) { + setNextBox(tinfo.boxIndex); + } + } + + void moodSleep() + { + int32 boxIndex = getRandomBox(); + + if (!checkZone(boxIndex)) + return; + + if (checkStalk(boxIndex)) + { + mood = MOOD_STALK; + setNextBox(boxIndex); + return; + } + + if (extraE->nav.nextBox != NO_BOX) + return; + + setNextBox(boxIndex); + } + + void moodStalk() + { + if (extraE->nav.nextBox != NO_BOX && checkStalk(extraE->nav.nextBox)) + return; + + int32 boxIndex = getRandomBox(); + + if (!checkZone(boxIndex)) + return; + + if (checkStalk(boxIndex)) + { + setNextBox(boxIndex); + return; + } + + if (extraE->nav.nextBox != NO_BOX) + return; + + setNextBox(boxIndex); + + if (tinfo.zoneIndex != tinfo.zoneIndexTarget) { + mood = MOOD_SLEEP; + } + } + + void moodAttack() + { + if (rand_logic() >= aggression) + return; + + extraE->nav.pos = tinfo.target->pos; + extraE->nav.nextBox = tinfo.boxIndexTarget; + + if (extraE->nav.zoneType == ZONE_FLY) { + extraE->nav.pos.y += tinfo.target->getFrame()->box.minY; // attack Laras head + } + } + + void moodEscape() + { + int32 boxIndex = getRandomBox(); + + if (!checkZone(boxIndex) || (extraE->nav.nextBox != NO_BOX)) + return; + + if (checkEscape(boxIndex)) + { + setNextBox(boxIndex); + return; + } + + if ((tinfo.zoneIndex != tinfo.zoneIndexTarget) || !checkStalk(boxIndex)) + return; + + mood = MOOD_STALK; + setNextBox(boxIndex); + } + + void updateNavigation() + { + tinfo.waypoint = extraE->nav.getWaypoint(tinfo.boxIndex, pos); + + int16 maxTurn = extraE->maxTurn; + + if (health <= 0 || !hSpeed || !maxTurn) + return; + + int32 dx = tinfo.waypoint.x - pos.x; + int32 dz = tinfo.waypoint.z - pos.z; + int16 turn = phd_atan(dz, dx) - angle.y; + int32 r = (hSpeed << FIXED_SHIFT) / maxTurn; //@DIV + int32 d = fastLength(dx, dz); + bool aim = (turn > -ANGLE_90) && (turn < ANGLE_90); + + if (!aim && (d < r)) + maxTurn >>= 1; + + tinfo.turn = X_CLAMP(turn, -maxTurn, maxTurn); + } + + void setNextBox(int16 boxIndex) + { + boxIndex &= 0x7FFF; + const Box* box = level.boxes + boxIndex; + + int32 bMinX = (box->minX << 10) + 512; + int32 bMaxX = (box->maxX << 10) - 512; + int32 bMinZ = (box->minZ << 10) + 512; + int32 bMaxZ = (box->maxZ << 10) - 512; + + extraE->nav.nextBox = boxIndex; + extraE->nav.pos.x = bMinX + RAND_LOGIC(bMaxX - bMinX); + extraE->nav.pos.z = bMinZ + RAND_LOGIC(bMaxZ - bMinZ); + extraE->nav.pos.y = box->floor; + if (extraE->nav.zoneType != ZONE_FLY) + extraE->nav.pos.y -= 384; + } + + uint32 getMoodWild() + { + bool inZone = checkZone(); + + if (mood == MOOD_SLEEP || mood == MOOD_STALK) + return inZone ? MOOD_ATTACK : ((flags & ITEM_FLAG_INJURED) ? MOOD_ESCAPE : mood); + + if (mood == MOOD_ATTACK) + return inZone ? mood : MOOD_SLEEP; + + return inZone ? MOOD_ATTACK : mood; + } + + uint32 getMoodNormal() + { + bool inZone = checkZone(); + + if (mood == MOOD_SLEEP || mood == MOOD_STALK) + { + if ((flags & ITEM_FLAG_INJURED) && (rand_logic() < 0x800 || !inZone)) + return MOOD_ESCAPE; + if (inZone) + return ((tinfo.dist < 3072) || (mood == MOOD_STALK && extraE->nav.nextBox == NO_BOX)) ? MOOD_ATTACK : MOOD_STALK; + return mood; + } + + if (mood == MOOD_ATTACK) + return ((flags & ITEM_FLAG_INJURED) && rand_logic() < 0x800) ? MOOD_ESCAPE : (!inZone ? MOOD_SLEEP : mood); + + return (inZone && rand_logic() < 0x100) ? MOOD_STALK : mood; + } + + X_INLINE bool checkZone() + { + return tinfo.zoneIndex == tinfo.zoneIndexTarget; + } + + X_INLINE int32 getRandomBox() + { + return extraE->nav.cells[RAND_LOGIC(extraE->nav.cellsCount)].boxIndex; + } + + bool checkZone(int16 boxIndex) + { + if (getZones()[boxIndex] != tinfo.zoneIndex) + return false; + + const Box &b = level.boxes[boxIndex]; + + //@TODO blocking + + return (pos.x <= (b.minX << 10)) || (pos.x >= ((b.maxX << 10) - 1)) || + (pos.z <= (b.minZ << 10)) || (pos.z >= ((b.maxZ << 10) - 1)); + } + + bool checkStalk(int16 boxIndex) + { + const Box &b = level.boxes[boxIndex]; + + int32 dx = (((b.minX + b.maxX) << 10) >> 1) - tinfo.target->pos.x; + int32 dz = (((b.minZ + b.maxZ) << 10) >> 1) - tinfo.target->pos.z; + + if (X_MAX(abs(dx), abs(dz)) > 3072) + return false; + + int32 qLara = (tinfo.target->angle.y >> 14) + 2; + int32 qBox = (dz > 0) ? ((dx > 0) ? 2 : 1) : ((dx > 0) ? 3 : 0); + + if (qLara == qBox) // Lara looks at this box + return false; + + if (abs(qLara - qBox) != 2) // Lara looks in the opposite direction + return true; + + dx = pos.x - tinfo.target->pos.x; + dz = pos.z - tinfo.target->pos.z; + + int32 qEnemy = (dz > 0) ? ((dx > 0) ? 2 : 1) : ((dx > 0) ? 3 : 0); + + return qLara != qEnemy; // Lara looks at enemy + } + + bool checkEscape(int16 boxIndex) + { + const Box &b = level.boxes[boxIndex]; + + int32 dx = (((b.minX + b.maxX) << 10) >> 1) - tinfo.target->pos.x; + int32 dz = (((b.minZ + b.maxZ) << 10) >> 1) - tinfo.target->pos.z; + + if (X_MAX(abs(dx), abs(dz)) < 5120) + return false; + + int32 px = pos.x - tinfo.target->pos.x; + int32 pz = pos.z - tinfo.target->pos.z; + + return !(((dx ^ px) & 0x80000000) && ((dz ^ pz) & 0x80000000)); + } + + const uint16* getZones() + { + return level.zones[gSaveGame.flipped][extraE->nav.zoneType]; + } + + void bite(int32 joint, const vec3i &offset, int32 damage) + { + if (!tinfo.target) + return; + + vec3i fxPos = pos + getJoint(joint, offset); + fxBlood(fxPos, 0, 0); + + tinfo.target->hit(damage, offset, 0); + } + + void updateLocation() + { + Room* oldRoom = room; + + updateRoom(); + + int32 h = pos.y - roomFloor; + bool badPos = (h > extraE->nav.stepHeight) || (h < extraE->nav.dropHeight); + + if (!badPos) + { + if (extraE->nav.zoneType == ZONE_FLY) + { + int32 dy = X_CLAMP(tinfo.waypoint.y - pos.y, -extraE->nav.vSpeed, extraE->nav.vSpeed); + int32 y = pos.y + dy; + + const Sector* sector = room->getSector(pos.x, pos.z); + + int32 floor = sector->getFloor(pos.x, y, pos.z); + if (y > floor) + { + if (pos.y > floor) + { + pos.x = tinfo.pos.x; + pos.z = tinfo.pos.z; + dy = -extraE->nav.vSpeed; // go up + } else { + pos.y = floor; + dy = 0; + } + } + + int32 ceiling = sector->getCeiling(pos.x, y, pos.z); + if (y < ceiling) + { + if (pos.y < ceiling) + { + pos.x = tinfo.pos.x; + pos.z = tinfo.pos.z; + dy = extraE->nav.vSpeed; // go down + } else { + pos.y = ceiling; + dy = 0; + } + } + + // update vertical position + pos.y += dy; + + // update pitch + int32 pitch = 0; + if (hSpeed) { + pitch = phd_atan(hSpeed, -dy); + } + angle.x = angleLerp(angle.x, pitch, ANGLE_1); + + updateRoom(); + } else { + if (pos.y > roomFloor) { + pos.y = roomFloor; + } else if (pos.y < roomFloor - 64) { + pos.y += 64; + } else { + pos.y = roomFloor; + } + } + } + + if (!badPos) // check for enemy vs enemy collision + { + ItemObj* item = room->firstItem; + while (item) + { + if (item->type != ITEM_LARA && item != this && item->health > 0 && ((item->flags & ITEM_FLAG_STATUS) == ITEM_FLAG_STATUS_ACTIVE)) + { + int32 dx = item->pos.x - pos.x; + int32 dz = item->pos.z - pos.z; + int32 d = fastLength(dx, dz); + + if ((d < (radius << 1)) && hSpeed) + { + tinfo.turn = ANGLE(8); + badPos = true; + break; + } + } + item = item->nextItem; + } + } + + if (!badPos) // check for wrong zone or wall + { + const uint16* zones = getZones(); + uint32 boxIndex = room->getSector(pos.x, pos.z)->boxIndex; + badPos = (boxIndex == NO_BOX) || (tinfo.zoneIndex != zones[boxIndex]); + } + + if (badPos) + { + angle.y += tinfo.turn; // apply turn again for double rotation speed if blocked by something + pos = tinfo.pos; + + if (room != oldRoom) + { + room->remove(this); + oldRoom->add(this); + } + } + + #ifdef _DEBUG + {// TODO investigate enemies respawn + const Sector* sector = room->getSector(pos.x, pos.z); + ASSERT(sector->boxIndex != NO_BOX); + } + #endif + } + + virtual void activate() + { + if (health <= 0) + return; + + ItemObj::activate(); + + if (!enable(!(flags & ITEM_FLAG_STATUS))) { + flags &= ~ITEM_FLAG_STATUS; + flags |= ITEM_FLAG_STATUS_INVISIBLE; + } + } + + virtual void hit(int32 damage, const vec3i &point, int32 soundId) + { + ASSERT(health > 0); + + health -= damage; + + if (health > 0) + { + if (soundId) { + soundPlay(soundId, &pos); + } + } else { + gSaveGame.kills++; + #ifdef HIDE_CORPSES + corpseTimer = HIDE_CORPSES; + #endif + } + + if (type != ITEM_MUMMY) { + fxBlood(point, 0, 0); + } + + flags |= ITEM_FLAG_INJURED; + } + + virtual void collide(Lara* lara, CollisionInfo* cinfo) + { + if (!extraE) // disabled + return; + + if (!updateHitMask(lara, cinfo)) + return; + + if (!cinfo->enemyPush) + return; + + int32 r = cinfo->radius; + cinfo->radius = 0; + collidePush(lara, cinfo, true); + cinfo->radius = r; + } + + virtual void update() + { + if ((flags & ITEM_FLAG_STATUS) == ITEM_FLAG_STATUS_INVISIBLE) + { + if (!enable(false)) + return; + flags &= ~ITEM_FLAG_STATUS; + flags |= ITEM_FLAG_STATUS_ACTIVE; + } + + #ifdef HIDE_CORPSES + if ((health <= 0) && !corpseTimer--) + { + deactivate(); + flags &= ~ITEM_FLAG_STATUS; + flags |= ITEM_FLAG_STATUS_INVISIBLE; + } + #endif + + if (!extraE) + return; + + updateTargetInfo(); + + if (health > 0) + { + updateMood(); + updateNavigation(); + } + + goalState = updateState(); + + animProcess(); + + updateLocation(); + + extraE->rotHead = angleLerp(extraE->rotHead, tinfo.rotHead, ANGLE(5)); + angle.z += X_CLAMP((tinfo.tilt << 2) - angle.z, -ANGLE(3), ANGLE(3)); + angle.y += tinfo.turn; + + if ((flags & ITEM_FLAG_STATUS) == ITEM_FLAG_STATUS_INACTIVE) + { + flags &= ~ITEM_FLAG_COLLISION; + health = NOT_ENEMY; + disable(); + #ifndef HIDE_CORPSES + deactivate(); + #endif + } + } + + virtual int32 updateState() + { + return goalState; + } + + virtual void initExtra() + { + // empty + } + + struct EnemySave { + int16 vSpeed; + int16 hSpeed; + int16 health; + uint8 mood; + uint8 waterState; + }; + + virtual uint8* save(uint8* data) + { + data = ItemObj::save(data); + + EnemySave* sg = (EnemySave*)data; + + sg->vSpeed = vSpeed; + sg->hSpeed = hSpeed; + sg->health = health; + sg->mood = mood; + sg->waterState = waterState; + + return data + sizeof(EnemySave); + } + + virtual uint8* load(uint8* data) + { + data = ItemObj::load(data); + + EnemySave* sg = (EnemySave*)data; + + vSpeed = sg->vSpeed; + hSpeed = sg->hSpeed; + health = sg->health; + mood = sg->mood; + waterState = sg->waterState; + + return data + sizeof(EnemySave); + } +}; + + +struct Doppelganger : Enemy +{ + Doppelganger(Room* room) : Enemy(room, LARA_MAX_HEALTH, 10, 0, 0) {} +}; + + +struct Wolf : Enemy +{ + enum { + HIT_MASK = 0x774F, // body, head, front legs + DIST_STALK = 1023 * 3, + DIST_BITE = 345, + DIST_ATTACK = 1024 + 512 + }; + + enum { + ANIM_DEATH = 20, + ANIM_DEATH_RUN, + ANIM_DEATH_JUMP + }; + + enum { + STATE_NONE, + STATE_STOP, + STATE_WALK, + STATE_RUN, + STATE_JUMP, // unused + STATE_STALK, + STATE_ATTACK, + STATE_HOWL, + STATE_SLEEP, + STATE_GROWL, + STATE_TURN, // unused + STATE_DEATH, + STATE_BITE + }; + + Wolf(Room* room) : Enemy(room, 6, 341, 375, AGGRESSION_LVL_2) + { + frameIndex = level.anims[animIndex].frameEnd - 1; + } + + virtual void hit(int32 damage, const vec3i &point, int32 soundId) + { + Enemy::hit(damage, point, SND_HIT_WOLF); + + if (health <= 0) { + animSet(level.models[type].animIndex + ANIM_DEATH + RAND_LOGIC(3), true); + } + } + + virtual int32 updateState() + { + if (health <= 0) + return goalState; + + switch (state) + { + case STATE_STOP: { + return (nextState != STATE_NONE) ? nextState : STATE_WALK; + } + case STATE_WALK: { + extraE->maxTurn = ENEMY_TURN_SLOW; + tinfo.tilt = tinfo.turn >> 1; + + if (mood != MOOD_SLEEP) { + nextState = STATE_NONE; + return STATE_STALK; + } + if (rand_logic() < 0x20) { + nextState = STATE_SLEEP; + return STATE_STOP; + } + break; + } + case STATE_RUN: { + extraE->maxTurn = ENEMY_TURN_FAST; + tinfo.tilt = tinfo.turn; + + if (tinfo.aim && tinfo.dist < DIST_ATTACK) + { + if (tinfo.dist <= (DIST_ATTACK >> 1)) + { + nextState = STATE_NONE; + return STATE_ATTACK; + } + nextState = STATE_STALK; + return STATE_GROWL; + } + if (mood == MOOD_STALK && tinfo.dist < DIST_STALK) + { + nextState = STATE_STALK; + return STATE_GROWL; + } + if (mood == MOOD_SLEEP) + return STATE_GROWL; + break; + } + case STATE_STALK: { + extraE->maxTurn = ENEMY_TURN_SLOW; + + if (mood == MOOD_ESCAPE) + return STATE_RUN; + if (tinfo.dist < DIST_BITE && tinfo.canAttack && (tinfo.target->health > 0)) + return STATE_BITE; + if (tinfo.dist > DIST_STALK) + return STATE_RUN; + if (mood == MOOD_ATTACK && (!tinfo.aim || tinfo.dist > DIST_ATTACK)) + return STATE_RUN; + if (rand_logic() < 0x180) + { + nextState = STATE_HOWL; + return STATE_GROWL; + } + if (mood == MOOD_SLEEP) + return STATE_GROWL; + break; + } + case STATE_SLEEP: { + if ((mood == MOOD_ESCAPE) || checkZone()) + nextState = STATE_GROWL; + else if (rand_logic() < 0x20) + nextState = STATE_WALK; + else + break; + return STATE_STOP; + } + case STATE_GROWL: { + if (nextState != STATE_NONE) + return nextState; + if (mood == MOOD_ESCAPE) + return STATE_RUN; + if (tinfo.dist < DIST_BITE && tinfo.canAttack && (tinfo.target->health > 0)) + return STATE_BITE; + if (mood == MOOD_STALK) + return STATE_STALK; + if (mood == MOOD_SLEEP) + return STATE_STOP; + return STATE_RUN; + } + case STATE_ATTACK: { + tinfo.tilt = tinfo.turn; + + if (nextState == STATE_NONE && (hitMask & HIT_MASK)) { + bite(6, _vec3i(0, -14, 174), 50); + nextState = STATE_RUN; + } + return STATE_RUN; + } + case STATE_BITE: { + if (nextState == STATE_NONE && (hitMask & HIT_MASK)) { + bite(6, _vec3i(0, -14, 174), 100); + nextState = STATE_GROWL; + } + return STATE_RUN; + } + } + + return goalState; + } + + virtual void initExtra() + { + extraE->nav.stepHeight = 256; + extraE->nav.dropHeight = -1024; + } +}; + + +struct Bear : Enemy +{ + enum { + HIT_MASK = 0x2406C // front legs and head + }; + + enum { + STATE_NONE = 0, // WTF? + STATE_WALK = 0, + STATE_STOP , + STATE_HIND , + STATE_RUN , + STATE_HOWL , + STATE_GROWL , + STATE_BITE , + STATE_ATTACK , + STATE_EAT , + STATE_DEATH + }; + + Bear(Room* room) : Enemy(room, 20, 341, 500, AGGRESSION_LVL_3) + { + flags |= ITEM_FLAG_ANIMATED; // TODO WILD flag + } + + virtual void hit(int32 damage, const vec3i &point, int32 soundId) + { + Enemy::hit(damage, point, SND_HIT_BEAR); + } + + virtual int32 updateState() + { + if (health <= 0) + { + switch (state) + { + case STATE_HIND: + return STATE_HOWL; + case STATE_WALK: + case STATE_RUN: + return STATE_STOP; + case STATE_STOP: + flags &= ~ITEM_FLAG_REVERSE; // TODO special flag + return STATE_DEATH; + case STATE_HOWL: + flags |= ITEM_FLAG_REVERSE; + return STATE_DEATH; + case STATE_DEATH: + if ((flags & ITEM_FLAG_REVERSE) && (hitMask & HIT_MASK) && tinfo.target) // fall on Lara + { + tinfo.target->hit(200, _vec3i(0, 0, 0), 0); + flags &= ~ITEM_FLAG_REVERSE; + } + return STATE_DEATH; + default: + return goalState; + } + } + + // hold the injured flag until death + if (flags & ITEM_FLAG_INJURED) { + flags |= ITEM_FLAG_REVERSE; + } else { + if (flags & ITEM_FLAG_REVERSE) { + flags |= ITEM_FLAG_INJURED; + } + } + + switch (state) + { + case STATE_WALK: { + extraE->maxTurn = ENEMY_TURN_SLOW; + + if (tinfo.target->health <= 0 && tinfo.aim && (hitMask & HIT_MASK)) + return nextState = STATE_STOP; // eat lara! >:E + + if (mood != MOOD_SLEEP) + { + if (mood == MOOD_ESCAPE) { + nextState = STATE_NONE; + } + return STATE_STOP; + } + + if (rand_logic() < 0x50) + { + nextState = STATE_GROWL; + return STATE_STOP; + } + break; + } + case STATE_STOP: { + if (tinfo.target->health <= 0) + return (tinfo.canAttack && tinfo.dist < 768) ? STATE_EAT : STATE_WALK; + if (nextState != STATE_NONE) + return nextState; + return (mood == MOOD_SLEEP) ? STATE_WALK : STATE_RUN; + } + case STATE_HIND: { + if (flags & ITEM_FLAG_INJURED) { + nextState = STATE_NONE; + return STATE_HOWL; + } + if (tinfo.aim && (hitMask & HIT_MASK)) + return STATE_HOWL; + if (mood == MOOD_ESCAPE) { + nextState = STATE_NONE; + return STATE_HOWL; + } + if (mood == MOOD_SLEEP || rand_logic() < 0x50) { + nextState = STATE_GROWL; + return STATE_HOWL; + } + if (tinfo.dist > 2048 || rand_logic() < 0x600) { + nextState = STATE_STOP; + return STATE_HOWL; + } + return STATE_HOWL; + } + case STATE_RUN: { + extraE->maxTurn = ENEMY_TURN_FAST; + + if (hitMask & HIT_MASK) { + tinfo.target->hit(3, pos, 0); + if (health >= 0) { + nextState = STATE_NONE; + } + } + + if (tinfo.target->health <= 0 || mood == MOOD_SLEEP) + return STATE_STOP; + + if (tinfo.aim && nextState == STATE_NONE) + { + if (tinfo.dist < 2048) + { + if (!(flags & ITEM_FLAG_INJURED) && (rand_logic() < 0x300)) + { + nextState = STATE_HOWL; + return STATE_STOP; + } + if (tinfo.dist < 1024) + return STATE_BITE; + } + } + break; + } + case STATE_HOWL: { + if (flags & ITEM_FLAG_INJURED) + { + nextState = STATE_NONE; + return STATE_STOP; + } + + if (nextState != STATE_NONE) + return nextState; + + if (mood == MOOD_SLEEP || mood == MOOD_ESCAPE) + return STATE_STOP; + + if (tinfo.canAttack && tinfo.dist < 600) + return STATE_ATTACK; + + return STATE_HIND; + } + case STATE_BITE: { + if ((nextState == STATE_NONE) && (hitMask & HIT_MASK)) { + bite(14, _vec3i(0, 96, 335), 200); + nextState = STATE_STOP; + } + break; + } + case STATE_ATTACK: { + if ((nextState == STATE_NONE) && (hitMask & HIT_MASK) && tinfo.target) { + tinfo.target->hit(400, _vec3i(0, 0, 0), 0); + nextState = STATE_HOWL; + } + break; + } + } + + return goalState; + } +}; + + +struct Bat : Enemy +{ + enum { + STATE_NONE, + STATE_AWAKE, + STATE_FLY, + STATE_ATTACK, + STATE_CIRCLING, + STATE_DEATH + }; + + Bat(Room* room) : Enemy(room, 1, 102, 0, AGGRESSION_LVL_1) {} + + virtual int32 updateState() + { + if (health <= 0) + { + hSpeed = 0; + if (pos.y < roomFloor) { + flags |= ITEM_FLAG_GRAVITY; + } else { + flags &= ~ITEM_FLAG_GRAVITY; + } + + if (flags & ITEM_FLAG_GRAVITY) { + return STATE_CIRCLING; + } + + pos.y = roomFloor; + return STATE_DEATH; + } + + switch (state) + { + case STATE_AWAKE: + return STATE_FLY; + case STATE_FLY: + if (hitMask) { + return STATE_ATTACK; + } + break; + case STATE_ATTACK: + if (!hitMask) { + mood = MOOD_SLEEP; + return STATE_FLY; + } + bite(4, _vec3i(0, 16, 45), 2); + break; + } + + return goalState; + } + + virtual void initExtra() + { + extraE->nav.stepHeight = 20 * 1024; + extraE->nav.dropHeight = -20 * 1024; + extraE->nav.vSpeed = 16; + extraE->maxTurn = ANGLE(20); + } +}; + + +struct Crocodile : Enemy +{ + Crocodile(Room* room) : Enemy(room, 20, 341, 600, AGGRESSION_LVL_2) {} + + virtual void initExtra() + { + if (type == ITEM_CROCODILE_LAND) { + extraE->nav.stepHeight = 256; + extraE->nav.dropHeight = -256; + } else { + extraE->nav.stepHeight = 20 * 1024; + extraE->nav.dropHeight = -20 * 1024; + } + } +}; + + +struct Lion : Enemy +{ + Lion(Room* room) : Enemy(room, 1, 341, 400, AGGRESSION_LVL_2) + { + switch (type) + { + case ITEM_LION_MALE : health = 30; aggression = AGGRESSION_LVL_MAX; break; + case ITEM_LION_FEMALE : health = 25; break; + case ITEM_PUMA : health = 40; break; + } + } + + virtual void hit(int32 damage, const vec3i &point, int32 soundId) + { + Enemy::hit(damage, point, (type == ITEM_PUMA) ? 0 : SND_HIT_LION); + } + + virtual void initExtra() + { + extraE->nav.stepHeight = 256; + extraE->nav.dropHeight = -1024; + } +}; + + +struct Gorilla : Enemy +{ + Gorilla(Room* room) : Enemy(room, 22, 341, 250, AGGRESSION_LVL_MAX) {} + + virtual void initExtra() + { + extraE->nav.stepHeight = 512; + extraE->nav.dropHeight = -1024; + } +}; + + +struct Rat : Enemy +{ + Rat(Room* room) : Enemy(room, 5, 204, 200, AGGRESSION_LVL_2) {} + + virtual void hit(int32 damage, const vec3i &point, int32 soundId) + { + Enemy::hit(damage, point, SND_HIT_RAT); + } +}; + + +struct Rex : Enemy +{ + Rex(Room* room) : Enemy(room, 100, 341, 2000, AGGRESSION_LVL_MAX) {} +}; + + +struct Raptor : Enemy +{ + Raptor(Room* room) : Enemy(room, 20, 341, 400, AGGRESSION_LVL_3) {} +}; + + +struct Mutant : Enemy +{ + Mutant(Room* room) : Enemy(room, 50, 341, 150, AGGRESSION_LVL_MAX) {} +}; + + +struct Centaur : Enemy +{ + Centaur(Room* room) : Enemy(room, 120, 341, 400, AGGRESSION_LVL_MAX) {} +}; + + +struct Mummy : Enemy +{ + Mummy(Room* room) : Enemy(room, 18, 10, 0, AGGRESSION_LVL_MAX) {} +}; + + +struct Larson : Enemy +{ + Larson(Room* room) : Enemy(room, 50, 102, 0, AGGRESSION_LVL_MAX) {} +}; + + +struct Pierre : Enemy +{ + Pierre(Room* room) : Enemy(room, 70, 102, 0, AGGRESSION_LVL_MAX) {} +}; + + +struct Skater : Enemy +{ + Skater(Room* room) : Enemy(room, 125, 204, 0, AGGRESSION_LVL_MAX) {} + + virtual void hit(int32 damage, const vec3i &point, int32 soundId) + { + Enemy::hit(damage, point, SND_HIT_SKATER); + } +}; + + +struct Cowboy : Enemy +{ + Cowboy(Room* room) : Enemy(room, 150, 102, 0, AGGRESSION_LVL_MAX) {} +}; + + +struct MrT : Enemy +{ + MrT(Room* room) : Enemy(room, 200, 102, 0, AGGRESSION_LVL_MAX) {} +}; + + +struct Natla : Enemy +{ + Natla(Room* room) : Enemy(room, 400, 204, 0, AGGRESSION_LVL_MAX) {} +}; + + +struct Adam : Enemy +{ + Adam(Room* room) : Enemy(room, 500, 341, 0, AGGRESSION_LVL_MAX) {} + + virtual void hit(int32 damage, const vec3i &point, int32 soundId) + { + Enemy::hit(damage, point, SND_HIT_ADAM); + } +}; + +#endif diff --git a/src/fixed/game.h b/src/fixed/game.h new file mode 100644 index 00000000..095d93b5 --- /dev/null +++ b/src/fixed/game.h @@ -0,0 +1,332 @@ +#ifndef H_GAME +#define H_GAME + +#include "common.h" +#include "room.h" +#include "camera.h" +#include "item.h" +#include "draw.h" +#include "nav.h" +#include "level.h" +#include "inventory.h" + +EWRAM_DATA LevelID gNextLevel = LVL_MAX; + +void nextLevel(LevelID next) +{ + if ((next == LVL_TR1_3A) && (inventory.state == INV_STATE_NONE)) // alpha version + { + inventory.open(players[0], INV_PAGE_END); + return; + } + gNextLevel = next; +} + +bool gameSave() +{ + gSaveGame.version = SAVEGAME_VER; + gSaveGame.level = gLevelID; + gSaveGame.track = gCurTrack; + gSaveGame.randSeedLogic = gRandSeedLogic; + gSaveGame.randSeedDraw = gRandSeedDraw; + + memset(gSaveGame.invSlots, 0, sizeof(gSaveGame.invSlots)); + memcpy(gSaveGame.invSlots, inventory.counts, sizeof(inventory.counts)); + + uint8* ptr = gSaveData; + ItemObj* item = items; + for (int32 i = 0; i < level.itemsCount; i++, item++) + { + ptr = item->save(ptr); + } + gSaveGame.dataSize = ptr - gSaveData; + + return osSaveGame(); +} + +bool gameLoad() +{ + if (!osLoadGame()) + { + if (gSaveGame.dataSize == 0) + return false; + } + + SaveGame tmp = gSaveGame; + gLevelID = (LevelID)gSaveGame.level; + startLevel(gLevelInfo[gLevelID].name); + gSaveGame = tmp; + + inventory.setSlots(gSaveGame.invSlots); + + ItemObj::sFirstActive = NULL; + ItemObj::sFirstFree = items + level.itemsCount; + + uint8* ptr = gSaveData; + ItemObj* item = items; + for (int32 i = 0; i < level.itemsCount; i++, item++) + { + ptr = item->load(ptr); + + if (item->flags & ITEM_FLAG_ACTIVE) { + item->activate(); + } + } + + if (gSaveGame.track != -1) { + sndPlayTrack(gSaveGame.track); + } + + gRandSeedLogic = gSaveGame.randSeedLogic; + gRandSeedDraw = gSaveGame.randSeedDraw; + + return true; +} + +void gameInit(const char* name) +{ + renderInit(); + + gSaveGame.dataSize = 0; + + gSettings.version = SETTINGS_VER; + gSettings.controls_vibration = 1; + gSettings.controls_swap = 0; + gSettings.audio_sfx = 1; + gSettings.audio_music = 1; + gSettings.video_gamma = 0; + gSettings.video_fps = 1; + gSettings.video_vsync = 0; + osLoadSettings(); + + inventory.init(); + + startLevel(name); +} + +void resetLara(int32 index, int32 roomIndex, const vec3i &pos, int32 angleY) +{ + Lara* lara = players[index]; + + lara->room->remove(lara); + + lara->pos = pos; + lara->angle.y = angleY; + lara->health = LARA_MAX_HEALTH; + + lara->extraL->camera.target.pos = lara->pos; + lara->extraL->camera.target.room = lara->room; + lara->extraL->camera.view = lara->extraL->camera.target; + + rooms[roomIndex].add(lara); +} + +void gameLoadLevel(const void* data) +{ + drawFree(); + + memset(&gSaveGame, 0, sizeof(gSaveGame)); + memset(enemiesExtra, 0, sizeof(enemiesExtra)); + + ItemObj::sFirstActive = NULL; + ItemObj::sFirstFree = NULL; + + gCurTrack = -1; + + readLevel((uint8*)data); + + // prepare rooms + for (int32 i = 0; i < level.roomsCount; i++) + { + rooms[i].reset(); + } + + // prepare items free list + items[MAX_ITEMS - 1].nextItem = NULL; + for (int32 i = MAX_ITEMS - 2; i >= level.itemsCount; i--) + { + items[i].nextItem = items + i + 1; + } + ItemObj::sFirstFree = items + level.itemsCount; + + if (gLevelID == LVL_TR1_TITLE) { + // init dummy Lara for updateInput() + items->extraL = playersExtra; + items->extraL->camera.mode = CAMERA_MODE_FOLLOW; + inventory.open(items, INV_PAGE_TITLE); + } else { + inventory.page = INV_PAGE_MAIN; + + // init items + for (int32 i = 0; i < level.itemsCount; i++) + { + const ItemObjInfo* info = level.itemsInfo + i; + ItemObj* item = items + i; + + item->type = info->type; + item->intensity = uint8(info->intensity); + + item->pos.x = info->pos.x + (rooms[info->roomIndex].info->x << 8); + item->pos.y = info->pos.y; + item->pos.z = info->pos.z + (rooms[info->roomIndex].info->z << 8); + + item->angle.y = ((info->flags >> 14) - 2) * ANGLE_90; + item->flags = info->flags; + + if (item->type == ITEM_LARA || item->type == ITEM_CUT_1) { + players[0] = (Lara*)item; + } + + item->init(rooms + info->roomIndex); + } + + // gym + //resetLara(0, 7, _vec3i(39038, -1280, 51712), ANGLE_90); // start + //resetLara(0, 8, _vec3i(55994, 0, 52603), ANGLE_90); // piano + //resetLara(0, 9, _vec3i(47672, 256, 40875), ANGLE_90); // hall + //resetLara(0, 13, _vec3i(38953, 3328, 63961), ANGLE_90 + ANGLE_45); // pool + // level 1 + //resetLara(0, 0, _vec3i(74588, 3072, 19673), ANGLE_0); // first darts + //resetLara(0, 9, _vec3i(49669, 7680, 57891), ANGLE_0); // first door + //resetLara(0, 10, _vec3i(43063, 7168, 61198), ANGLE_0); // transp + //resetLara(0, 14, _vec3i(20215, 6656, 52942), ANGLE_90 + ANGLE_45); // bridge + //resetLara(0, 17, _vec3i(16475, 6656, 59845), ANGLE_90); // bear + //resetLara(0, 26, _vec3i(24475, 6912, 83505), ANGLE_90); // switch timer 1 + //resetLara(0, 35, _vec3i(35149, 2048, 74189), ANGLE_90); // switch timer 2 + // level 2 + //resetLara(0, 15, _vec3i(66179, 0, 25920), -ANGLE_90 - ANGLE_45); // sprites + //resetLara(0, 19, _vec3i(61018, 1024, 31214), ANGLE_180); // block + //resetLara(0, 14, _vec3i(64026, 512, 20806), ANGLE_0); // key and puzzle + //resetLara(0, 5, _vec3i(55644, 0, 29155), -ANGLE_90); // keyhole + //resetLara(0, 71, _vec3i(12705, -768, 30195), -ANGLE_90); // puzzle + //resetLara(0, 63, _vec3i(31055, -2048, 33406), ANGLE_0); // right room + //resetLara(0, 44, _vec3i(27868, -1024, 29191), -ANGLE_90); // swing blades + // level 3a + //resetLara(0, 44, _vec3i(73798, 2304, 9819), ANGLE_90); // uw gears + } + + drawInit(); +} + +void startLevel(const char* name) +{ + gRandSeedLogic = osGetSystemTimeMS() * 3; + gRandSeedDraw = osGetSystemTimeMS() * 7; + + sndStop(); + sndFreeSamples(); + + void* data = osLoadLevel(name); + gameLoadLevel(data); + + sndInitSamples(); + sndPlayTrack(getAmbientTrack()); +} + +void updateItems() +{ + ItemObj* item = ItemObj::sFirstActive; + while (item) + { + ItemObj* next = item->nextActive; + item->update(); + item = next; + } + + for (int32 i = 0; i < MAX_PLAYERS; i++) + { + if (players[i]) { + players[i]->update(); + } + } +} + +void gameUpdate(int32 frames) +{ + PROFILE(CNT_UPDATE); + + if (frames > MAX_UPDATE_FRAMES) { + frames = MAX_UPDATE_FRAMES; + } + + if (!sndTrackIsPlaying()) { + gCurTrack = -1; + sndPlayTrack(getAmbientTrack()); + } + + if (inventory.state != INV_STATE_NONE) + { + Lara* lara = (Lara*)inventory.lara; + ASSERT(lara); + lara->updateInput(); + inventory.update(frames); + + if ((inventory.page != INV_PAGE_TITLE) && (inventory.state == INV_STATE_NONE)) + { + if (lara->useItem(inventory.useSlot)) { + inventory.useSlot = SLOT_MAX; + } + } + } + + if ((inventory.page != INV_PAGE_TITLE) && (inventory.state == INV_STATE_NONE) && (gNextLevel == LVL_MAX)) + { + for (int32 i = 0; i < frames; i++) + { + updateItems(); + } + updateLevel(frames); + } + + if ((gNextLevel != LVL_MAX) && (inventory.state == INV_STATE_NONE)) + { + gLevelID = gNextLevel; + gNextLevel = LVL_MAX; + if (gLevelID == LVL_LOAD) { + gameLoad(); + } else { + startLevel(gLevelInfo[gLevelID].name); + } + gameUpdate(1); + } +} + +void gameRender() +{ + { + PROFILE(CNT_RENDER); + + setViewport(RectMinMax(0, 0, FRAME_WIDTH, FRAME_HEIGHT)); + + if (inventory.state == INV_STATE_NONE) + { + clear(); + + for (int32 i = 0; i < MAX_PLAYERS; i++) + { + // TODO set viewports for coop + #ifndef PROFILE_SOUNDTIME + drawRooms(&players[i]->extraL->camera); + #endif + drawHUD(players[i]); + } + } else { + inventory.draw(); + } + + //if (inventory.state == INV_STATE_NONE) + { + drawFPS(); + } + + flush(); + } + + drawProfiling(); + +#ifndef PROFILE_SOUNDTIME + PROFILE_CLEAR(); +#endif +} + +#endif diff --git a/src/fixed/inventory.h b/src/fixed/inventory.h new file mode 100644 index 00000000..4c9800c8 --- /dev/null +++ b/src/fixed/inventory.h @@ -0,0 +1,1529 @@ +#ifndef H_INVENTORY +#define H_INVENTORY + +#include "common.h" + +#ifdef __GBA__ +extern const uint8_t TITLE_SCR[]; +#else +extern const void* TITLE_SCR; +#endif + +#define INV_CAMERA_HEIGHT -1536 +#define INV_CAMERA_Y 96 +#define INV_CAMERA_Z 768 +#define INV_RING_RADIUS 688 +#define INV_TILT ANGLE(-45) + +enum InvState { + INV_STATE_NONE, + INV_STATE_OPENING, + INV_STATE_CLOSING, + INV_STATE_CLOSE, + INV_STATE_READY, + INV_STATE_DEATH, + INV_STATE_SPIN, + INV_STATE_SELECT, + INV_STATE_DESELECT, + INV_STATE_SHOW, + INV_STATE_PAGE_MAIN, + INV_STATE_PAGE_KEYS, + INV_STATE_PAGE_OPTIONS +}; + +enum InvPage { + INV_PAGE_TITLE, + INV_PAGE_DEATH, + INV_PAGE_END, + INV_PAGE_USE, + INV_PAGE_MAIN, + INV_PAGE_KEYS, + INV_PAGE_OPTIONS +}; + +enum InvSlot { +// Items + SLOT_LEADBAR, + SLOT_KEY_ITEM_1, + SLOT_KEY_ITEM_2, + SLOT_KEY_ITEM_3, + SLOT_KEY_ITEM_4, + SLOT_PUZZLE_4, + SLOT_PUZZLE_3, + SLOT_PUZZLE_2, + SLOT_PUZZLE_1, + SLOT_SCION, +// Inventory + SLOT_COMPASS, + SLOT_PISTOLS, + SLOT_AMMO_PISTOLS, + SLOT_SHOTGUN, + SLOT_AMMO_SHOTGUN, + SLOT_MAGNUMS, + SLOT_AMMO_MAGNUMS, + SLOT_UZIS, + SLOT_AMMO_UZIS, + SLOT_MEDIKIT_BIG, + SLOT_MEDIKIT_SMALL, +// Options + SLOT_PASSPORT, + SLOT_DETAIL, + SLOT_SOUND, + SLOT_CONTROLS, + SLOT_HOME, + SLOT_MAX, +}; + +enum PassportPage { + PASSPORT_PAGE_LOAD_GAME, + PASSPORT_PAGE_SAVE_GAME, + PASSPORT_PAGE_EXIT_TO_TITLE +}; + +enum OptionID +{ +// controls + OPT_ID_RUMBLE = 0, + OPT_ID_SWAP = 1, +// audio + OPT_ID_SFX = 0, + OPT_ID_MUSIC = 1, +// video + OPT_ID_GAMMA = 0, + OPT_ID_FPS = 1, + OPT_ID_VSYNC = 2, +// passport + OPT_ID_OK = 5 +}; + +struct InvItem +{ + uint8 type; + uint8 snd; + int16 selDist; + int16 selRotPre; + int16 selRotX; + int16 selRotY; + StringID str; +}; + +const InvItem INV_SLOTS[SLOT_MAX] = { + { ITEM_INV_LEADBAR , SND_INV_SHOW , 256 , ANGLE(20) , ANGLE(44) , ANGLE(67) , STR_LEAD_BAR }, + { ITEM_INV_KEY_ITEM_1 , SND_INV_SHOW , 296 , ANGLE(20) , ANGLE(15) , ANGLE(0) , STR_KEY }, + { ITEM_INV_KEY_ITEM_2 , SND_INV_SHOW , 296 , ANGLE(20) , ANGLE(15) , ANGLE(0) , STR_KEY }, + { ITEM_INV_KEY_ITEM_3 , SND_INV_SHOW , 296 , ANGLE(20) , ANGLE(15) , ANGLE(0) , STR_KEY }, + { ITEM_INV_KEY_ITEM_4 , SND_INV_SHOW , 296 , ANGLE(20) , ANGLE(15) , ANGLE(0) , STR_KEY }, + { ITEM_INV_PUZZLE_4 , SND_INV_SHOW , 296 , ANGLE(20) , ANGLE(15) , ANGLE(0) , STR_PUZZLE }, + { ITEM_INV_PUZZLE_3 , SND_INV_SHOW , 296 , ANGLE(20) , ANGLE(15) , ANGLE(0) , STR_PUZZLE }, + { ITEM_INV_PUZZLE_2 , SND_INV_SHOW , 296 , ANGLE(20) , ANGLE(15) , ANGLE(0) , STR_PUZZLE }, + { ITEM_INV_PUZZLE_1 , SND_INV_SHOW , 296 , ANGLE(20) , ANGLE(15) , ANGLE(0) , STR_PUZZLE }, + { ITEM_INV_SCION , SND_INV_SHOW , 296 , ANGLE(20) , ANGLE(15) , ANGLE(45) , STR_SCION }, + { ITEM_INV_COMPASS , SND_INV_COMPASS , 512 , ANGLE(20) , ANGLE(70) , ANGLE(0) , STR_COMPASS }, + { ITEM_INV_PISTOLS , SND_INV_WEAPON , 296 , ANGLE(17) , ANGLE(20) , ANGLE(0) , STR_PISTOLS }, + { ITEM_INV_AMMO_PISTOLS , SND_INV_SHOW , 296 , ANGLE(17) , ANGLE(20) , ANGLE(0) , STR_AMMO_PISTOLS }, + { ITEM_INV_SHOTGUN , SND_INV_WEAPON , 296 , ANGLE(17) , ANGLE(20) , ANGLE(0) , STR_SHOTGUN }, + { ITEM_INV_AMMO_SHOTGUN , SND_INV_SHOW , 296 , ANGLE(17) , ANGLE(20) , ANGLE(0) , STR_AMMO_SHOTGUN }, + { ITEM_INV_MAGNUMS , SND_INV_WEAPON , 296 , ANGLE(17) , ANGLE(20) , ANGLE(0) , STR_MAGNUMS }, + { ITEM_INV_AMMO_MAGNUMS , SND_INV_SHOW , 296 , ANGLE(17) , ANGLE(20) , ANGLE(0) , STR_AMMO_MAGNUMS }, + { ITEM_INV_UZIS , SND_INV_WEAPON , 296 , ANGLE(17) , ANGLE(20) , ANGLE(0) , STR_UZIS }, + { ITEM_INV_AMMO_UZIS , SND_INV_SHOW , 296 , ANGLE(17) , ANGLE(20) , ANGLE(0) , STR_AMMO_UZIS }, + { ITEM_INV_MEDIKIT_BIG , SND_INV_SHOW , 352 , ANGLE(20) , ANGLE(44) , ANGLE(22) , STR_MEDI_BIG }, + { ITEM_INV_MEDIKIT_SMALL , SND_INV_SHOW , 216 , ANGLE(22) , ANGLE(40) , ANGLE(22) , STR_MEDI_SMALL }, + { ITEM_INV_PASSPORT , SND_INV_SHOW , 384 , ANGLE(25) , ANGLE(23) , ANGLE(0) , STR_GAME }, + { ITEM_INV_DETAIL , SND_INV_SHOW , 424 , ANGLE(22) , ANGLE(36) , ANGLE(0) , STR_DETAIL }, + { ITEM_INV_SOUND , SND_INV_SHOW , 368 , ANGLE(26) , ANGLE(12) , ANGLE(0) , STR_SOUND }, + { ITEM_INV_CONTROLS , SND_INV_CONTROLS , 352 , ANGLE(30) , ANGLE(67) , ANGLE(0) , STR_CONTROLS }, + { ITEM_INV_HOME , SND_INV_HOME , 384 , ANGLE(25) , ANGLE(23) , ANGLE(0) , STR_HOME }, +}; + +#define FRAME_PASSPORT 14 +#define FRAME_COMPASS 10 + +struct Inventory +{ + InvSlot useSlot; + + int32 numKeys; + + int16 counts[SLOT_MAX]; + + int32 height; + int32 radius; + int16 pitch; + int16 rot; + int16 rotItem; + int32 selDist; + int16 selRotPre; + int16 selRotX; + int16 selRotY; + + ItemObj* lara; + InvPage page; + InvState state; + InvState nextState; + int32 timer; + + int32 heightTarget; + int32 heightInc; + int32 radiusTarget; + int32 radiusInc; + int32 pitchTarget; + int32 pitchInc; + int32 rotTarget; + int32 rotInc; + int32 rotItemInc; + int32 selDistTarget; + int32 selDistInc; + int32 selRotPreTarget; + int32 selRotPreInc; + int32 selRotXTarget; + int32 selRotXInc; + int32 selRotYTarget; + int32 selRotYInc; + + InvSlot itemsList[SLOT_MAX]; + int32 itemsCount; + uint32 itemVisMask; + int32 itemIndex; + int32 nextIndex; + int32 optionIndex; + int32 optionsCount; + int32 optionsWidth; + int32 optionsHeight; + + PassportPage passportPage; + + int32 frameIndex; + int32 frameTarget; + + const void* background; + + enum OptionType { + OPT_SPACE, + OPT_BUTTON, + OPT_BAR, + OPT_SWITCH, + OPT_VALUE, + OPT_TEXT + }; + + #define OPTION(t,s,v)\ + options[optionsCount].type = t;\ + options[optionsCount].str = s;\ + options[optionsCount].value = v;\ + optionsCount++ + + #define OPTION_SPACE() optionsHeight += 8; OPTION(OPT_SPACE, STR_EMPTY, 0) + #define OPTION_BTN(s,v) optionsHeight += 18; OPTION(OPT_BUTTON, s, v) + #define OPTION_BAR(s,v) optionsHeight += 18; OPTION(OPT_BAR, s, v) + #define OPTION_SWITCH(s,v) optionsHeight += 18; OPTION(OPT_SWITCH, s, v) + #define OPTION_TEXT(s) optionsHeight += 18; OPTION(OPT_TEXT, s, 0) + + struct Option + { + OptionType type; + StringID str; + int32 value; + }; + + Option options[32]; + + void init() + { + memset(counts, 0, sizeof(counts)); + + useSlot = SLOT_MAX; + numKeys = 0; + + add(ITEM_INV_PASSPORT); + add(ITEM_INV_DETAIL); + add(ITEM_INV_SOUND); + add(ITEM_INV_CONTROLS); + add(ITEM_INV_HOME); + + add(ITEM_INV_COMPASS); + + if (gLevelID != LVL_TR1_GYM) + { + add(ITEM_INV_PISTOLS); + add(ITEM_INV_SHOTGUN); + add(ITEM_INV_MAGNUMS); + add(ITEM_INV_UZIS); + add(ITEM_INV_MEDIKIT_SMALL, 10); + add(ITEM_INV_MEDIKIT_BIG, 5); + } + + //add(ITEM_INV_KEY_ITEM_1); + //add(ITEM_INV_PUZZLE_1); + } + + ItemType remapToInv(ItemType type) + { + switch (type) + { + case ITEM_PISTOLS : return ITEM_INV_PISTOLS; + case ITEM_SHOTGUN : return ITEM_INV_SHOTGUN; + case ITEM_MAGNUMS : return ITEM_INV_MAGNUMS; + case ITEM_UZIS : return ITEM_INV_UZIS; + case ITEM_AMMO_PISTOLS : return ITEM_INV_AMMO_PISTOLS; + case ITEM_AMMO_SHOTGUN : return ITEM_INV_AMMO_SHOTGUN; + case ITEM_AMMO_MAGNUMS : return ITEM_INV_AMMO_MAGNUMS; + case ITEM_AMMO_UZIS : return ITEM_INV_AMMO_UZIS; + case ITEM_MEDIKIT_SMALL : return ITEM_INV_MEDIKIT_SMALL; + case ITEM_MEDIKIT_BIG : return ITEM_INV_MEDIKIT_BIG; + case ITEM_PUZZLE_1 : return ITEM_INV_PUZZLE_1; + case ITEM_PUZZLE_2 : return ITEM_INV_PUZZLE_2; + case ITEM_PUZZLE_3 : return ITEM_INV_PUZZLE_3; + case ITEM_PUZZLE_4 : return ITEM_INV_PUZZLE_4; + case ITEM_LEADBAR : return ITEM_INV_LEADBAR; + case ITEM_KEY_ITEM_1 : return ITEM_INV_KEY_ITEM_1; + case ITEM_KEY_ITEM_2 : return ITEM_INV_KEY_ITEM_2; + case ITEM_KEY_ITEM_3 : return ITEM_INV_KEY_ITEM_3; + case ITEM_KEY_ITEM_4 : return ITEM_INV_KEY_ITEM_4; + case ITEM_SCION_PICKUP_QUALOPEC : + case ITEM_SCION_PICKUP_DROP : + case ITEM_SCION_PICKUP_HOLDER : return ITEM_INV_SCION; + default : return type; + } + } + + InvSlot remapToSlot(ItemType type) + { + type = remapToInv(type); + + for (int32 i = 0; i < X_COUNT(INV_SLOTS); i++) + { + if (INV_SLOTS[i].type == type) + return (InvSlot)i; + } + + ASSERT(false); + return SLOT_COMPASS; + } + + InvSlot remapHoleToSlot(ItemType type) + { + switch (type) + { + case ITEM_PUZZLEHOLE_1 : return SLOT_PUZZLE_1; + case ITEM_PUZZLEHOLE_2 : return SLOT_PUZZLE_2; + case ITEM_PUZZLEHOLE_3 : return SLOT_PUZZLE_3; + case ITEM_PUZZLEHOLE_4 : return SLOT_PUZZLE_4; + case ITEM_KEYHOLE_1 : return SLOT_KEY_ITEM_1; + case ITEM_KEYHOLE_2 : return SLOT_KEY_ITEM_2; + case ITEM_KEYHOLE_3 : return SLOT_KEY_ITEM_3; + case ITEM_KEYHOLE_4 : return SLOT_KEY_ITEM_4; + default : return SLOT_MAX; + } + } + + void add(ItemType type, int32 count = 1) + { + InvSlot slot = remapToSlot(type); + counts[slot] += count; + // TODO check max + + if (slot < SLOT_COMPASS) { + numKeys += count; + } + } + + void remove(InvSlot slot, int32 count) + { + counts[slot] -= count; + + if (slot < SLOT_COMPASS) { + numKeys -= count; + } + } + + void setSlots(uint16* invSlots) + { + numKeys = 0; + + memcpy(counts, invSlots, sizeof(counts)); + + for (int32 i = 0; i < SLOT_COMPASS; i++) + { + numKeys += counts[i]; + } + } + + bool applyItem(ItemObj* hole) + { + #define CHECK_CASE(A, B) case A: { if (useSlot != B) return false; break; } + + switch (hole->type) + { + CHECK_CASE(ITEM_PUZZLEHOLE_1, SLOT_PUZZLE_1); + CHECK_CASE(ITEM_PUZZLEHOLE_2, SLOT_PUZZLE_2); + CHECK_CASE(ITEM_PUZZLEHOLE_3, SLOT_PUZZLE_3); + CHECK_CASE(ITEM_PUZZLEHOLE_4, SLOT_PUZZLE_4); + CHECK_CASE(ITEM_KEYHOLE_1, SLOT_KEY_ITEM_1); + CHECK_CASE(ITEM_KEYHOLE_2, SLOT_KEY_ITEM_2); + CHECK_CASE(ITEM_KEYHOLE_3, SLOT_KEY_ITEM_3); + CHECK_CASE(ITEM_KEYHOLE_4, SLOT_KEY_ITEM_4); + default: return false; + } + + remove(useSlot, 1); + useSlot = SLOT_MAX; + + return true; + } + + void setState(InvState state, InvState nextState, int32 timer) + { + this->state = state; + this->nextState = nextState; + this->timer = timer; + + rotItemInc = -rotItem / timer; + } + + void setHeight(int32 target) + { + heightTarget = target; + heightInc = (target - height) / timer; + } + + void setRadius(int32 target) + { + radiusTarget = target; + radiusInc = (target - radius) / timer; + } + + void setPitch(int32 target) + { + pitchTarget = target; + pitchInc = (target - pitch) / timer; + } + + void setRot(int32 delta, int32 target) + { + rotTarget = target; + rotInc = delta / timer; + } + + void setSelection(bool selected) + { + InvSlot slot = itemsList[itemIndex]; + + if (selected) { + const InvItem &item = INV_SLOTS[slot]; + selDistTarget = item.selDist; + selRotPreTarget = item.selRotPre; + selRotXTarget = item.selRotX; + selRotYTarget = item.selRotY; + } else { + selDistTarget = 0; + selRotPreTarget = 0; + selRotXTarget = 0; + selRotYTarget = 0; + } + + selDistInc = (selDistTarget - selDist) / timer; + selRotPreInc = (selRotPreTarget - selRotPre) / timer; + selRotXInc = (selRotXTarget - selRotX) / timer; + selRotYInc = (selRotYTarget - selRotY) / timer; + + frameTarget = (selected) ? getAnimLength() : 0; + itemVisMask = 0xFFFFFFFF; + + if (selected && slot == SLOT_PASSPORT) { + passportPage = (page == INV_PAGE_OPTIONS) ? PASSPORT_PAGE_SAVE_GAME : PASSPORT_PAGE_LOAD_GAME; + frameTarget += passportPage * 5; + } + } + + void setPage(InvPage page) + { + this->page = page; + + itemsCount = 0; + + #define ADD_SLOT(slot) if (counts[slot]) itemsList[itemsCount++] = slot; + + switch (page) + { + case INV_PAGE_TITLE: + case INV_PAGE_DEATH: + case INV_PAGE_OPTIONS: + { + ADD_SLOT(SLOT_PASSPORT); + ADD_SLOT(SLOT_CONTROLS); + ADD_SLOT(SLOT_DETAIL); + ADD_SLOT(SLOT_SOUND); + if (page == INV_PAGE_TITLE) + { + ADD_SLOT(SLOT_HOME); + } + break; + } + + case INV_PAGE_MAIN: + { + ADD_SLOT(SLOT_COMPASS); + + if (gLevelID != LVL_TR1_GYM) + { + ADD_SLOT(SLOT_PISTOLS); + ADD_SLOT(SLOT_SHOTGUN); + ADD_SLOT(SLOT_MAGNUMS); + ADD_SLOT(SLOT_UZIS); + ADD_SLOT(SLOT_MEDIKIT_BIG); + ADD_SLOT(SLOT_MEDIKIT_SMALL); + } + break; + } + + case INV_PAGE_USE: + case INV_PAGE_KEYS: + { + ADD_SLOT(SLOT_LEADBAR); + ADD_SLOT(SLOT_KEY_ITEM_1); + ADD_SLOT(SLOT_KEY_ITEM_2); + ADD_SLOT(SLOT_KEY_ITEM_3); + ADD_SLOT(SLOT_KEY_ITEM_4); + ADD_SLOT(SLOT_PUZZLE_4); + ADD_SLOT(SLOT_PUZZLE_3); + ADD_SLOT(SLOT_PUZZLE_2); + ADD_SLOT(SLOT_PUZZLE_1); + ADD_SLOT(SLOT_SCION); + break; + } + default: ; + } + + itemIndex = nextIndex = 0; + + if (itemsCount > 0) { + setRot(ANGLE_180, itemIndex * ANGLE_360 / itemsCount); + } + + rot = rotTarget - ANGLE_180; + } + + int32 getItemIndexForSlot(InvSlot slot) + { + for (int32 i = 0; i < itemsCount; i++) + { + if (itemsList[i] == slot) + return i; + } + return 0; + } + + void open(ItemObj* lara, InvPage page, int32 arg = 0) + { + this->lara = lara; + + if (page == INV_PAGE_TITLE) { + background = TITLE_SCR; + } else { + background = copyBackground(); + } + + height = INV_CAMERA_HEIGHT; + pitch = (page == INV_PAGE_TITLE) ? 1024 : 0; + radius = 0; + frameIndex = 0; + useSlot = SLOT_MAX; + itemVisMask = 0xFFFFFFFF; + + if (page == INV_PAGE_END) + { + sndPlayTrack(3); + soundPlay(SND_HEALTH, NULL); + this->page = page; + this->state = INV_STATE_READY; + return; + } + + setState(INV_STATE_OPENING, (page == INV_PAGE_DEATH) ? INV_STATE_DEATH : INV_STATE_READY, 16); + setHeight(-256); + setRadius(INV_RING_RADIUS); + setPage(page); + + if (page == INV_PAGE_USE) + { + itemIndex = nextIndex = getItemIndexForSlot(remapHoleToSlot((ItemType)arg)); + setRot(ANGLE_180, itemIndex * ANGLE_360 / itemsCount); + rot = rotTarget - ANGLE_180; + } + + soundPlay(SND_INV_SHOW, NULL); + + update(1); + } + + void close() + { + setState(INV_STATE_CLOSING, INV_STATE_NONE, 16); + setHeight(-1536); + setRadius(0); + setRot(ANGLE_180, rot - ANGLE_180); + } + + StringID getTitleStr() + { + StringID title = STR_EMPTY; + + switch (page) + { + case INV_PAGE_MAIN: + { + title = STR_INV_TITLE_MAIN; + break; + } + + case INV_PAGE_USE: + case INV_PAGE_KEYS: + { + title = STR_INV_TITLE_KEYS; + break; + } + + case INV_PAGE_OPTIONS: + { + title = STR_INV_TITLE_OPTIONS; + break; + } + default: ; + } + return title; + } + + StringID getItemStr() + { + const InvItem &item = INV_SLOTS[itemsList[itemIndex]]; + int32 type = item.type; + + #define LVLCHECK(L, T, S) if (gLevelID == L && type == ITEM_INV_##T) return S; + + LVLCHECK(LVL_TR1_2, KEY_ITEM_1, STR_KEY_SILVER); + LVLCHECK(LVL_TR1_2, PUZZLE_1, STR_PUZZLE_GOLD_IDOL); + + LVLCHECK(LVL_TR1_3A, PUZZLE_1, STR_PUZZLE_COG); + + LVLCHECK(LVL_TR1_4, KEY_ITEM_1, STR_KEY_NEPTUNE); + LVLCHECK(LVL_TR1_4, KEY_ITEM_2, STR_KEY_ATLAS); + LVLCHECK(LVL_TR1_4, KEY_ITEM_3, STR_KEY_DAMOCLES); + LVLCHECK(LVL_TR1_4, KEY_ITEM_4, STR_KEY_THOR); + + LVLCHECK(LVL_TR1_5, KEY_ITEM_1, STR_KEY_RUSTY); + + LVLCHECK(LVL_TR1_6, PUZZLE_1, STR_PUZZLE_GOLD_BAR); + + LVLCHECK(LVL_TR1_7A, KEY_ITEM_1, STR_KEY_GOLD); + LVLCHECK(LVL_TR1_7A, KEY_ITEM_2, STR_KEY_SILVER); + LVLCHECK(LVL_TR1_7A, KEY_ITEM_3, STR_KEY_RUSTY); + + LVLCHECK(LVL_TR1_7B, KEY_ITEM_1, STR_KEY_GOLD); + LVLCHECK(LVL_TR1_7B, KEY_ITEM_2, STR_KEY_RUSTY); + LVLCHECK(LVL_TR1_7B, KEY_ITEM_3, STR_KEY_RUSTY); + + LVLCHECK(LVL_TR1_8A, KEY_ITEM_1, STR_KEY_SAPPHIRE); + + LVLCHECK(LVL_TR1_8B, KEY_ITEM_1, STR_KEY_SAPPHIRE); + LVLCHECK(LVL_TR1_8B, PUZZLE_2, STR_PUZZLE_SCARAB); + LVLCHECK(LVL_TR1_8B, PUZZLE_3, STR_PUZZLE_HORUS); + LVLCHECK(LVL_TR1_8B, PUZZLE_4, STR_PUZZLE_ANKH); + LVLCHECK(LVL_TR1_8B, PUZZLE_1, STR_PUZZLE_HORUS); + + LVLCHECK(LVL_TR1_8C, KEY_ITEM_1, STR_KEY_GOLD); + LVLCHECK(LVL_TR1_8C, PUZZLE_1, STR_PUZZLE_ANKH); + LVLCHECK(LVL_TR1_8C, PUZZLE_2, STR_PUZZLE_SCARAB); + + LVLCHECK(LVL_TR1_10A, PUZZLE_1, STR_PUZZLE_FUSE); + LVLCHECK(LVL_TR1_10A, PUZZLE_2, STR_PUZZLE_PYRAMID); + + LVLCHECK(LVL_TR1_EGYPT, KEY_ITEM_1, STR_KEY_GOLD); + LVLCHECK(LVL_TR1_CAT, KEY_ITEM_1, STR_KEY_ORNATE); + + if (state == INV_STATE_SHOW) + { + if (type == ITEM_INV_PASSPORT) + { + if (passportPage == PASSPORT_PAGE_LOAD_GAME) + return STR_LOAD_GAME; + if (passportPage == PASSPORT_PAGE_SAVE_GAME) + return STR_SAVE_GAME; + if (passportPage == PASSPORT_PAGE_EXIT_TO_TITLE) + return STR_EXIT_TO_TITLE; + } + } + + return item.str; + } + + int32 getAnimLength() + { + int32 type = INV_SLOTS[itemsList[itemIndex]].type; + + // HACK! override max animation length + if (type == ITEM_INV_PASSPORT) + { + return FRAME_PASSPORT; + } + + if (type == ITEM_INV_COMPASS) + { + return FRAME_COMPASS; + } + + const Anim &anim = level.anims[level.models[type].animIndex]; + + return anim.frameEnd - anim.frameBegin; + } + + bool animate(int32 frames) + { + if (frameIndex == frameTarget) + return false; + + if (frameIndex < frameTarget) + { + frameIndex += frames; + if (frameIndex >= frameTarget) + { + frameIndex = frameTarget; + updateVisMask(); + return false; + } + } + + if (frameIndex > frameTarget) + { + frameIndex -= frames; + if (frameIndex <= frameTarget) + { + frameIndex = frameTarget; + updateVisMask(); + return false; + } + } + + updateVisMask(); + return true; + } + + void updateVisMask() + { + #define PM(x) (1 << x) + + if (itemsList[itemIndex] == SLOT_PASSPORT) + { + itemVisMask = PM(0) | PM(1) | PM(4); + if (frameIndex <= 14) { + itemVisMask |= PM(2) | PM(6); + } else if (frameIndex > 14 && frameIndex < 19) { + itemVisMask |= PM(2) | PM(3) | PM(6); + } else if (frameIndex == 19) { + itemVisMask |= PM(3) | PM(6); + } else if (frameIndex > 19 && frameIndex < 24) { + itemVisMask |= PM(3) | PM(5) | PM(6); + } else if (frameIndex >= 24 && frameIndex < 29) { + itemVisMask |= PM(3) | PM(5); + } + } + } + + void onOption() + { + if (optionsCount == 0) + return; + + if (lara->isKeyHit(IN_DOWN)) + { + for (int32 i = 0; i < optionsCount; i++) + { + optionIndex++; + + if (optionIndex >= optionsCount) { + optionIndex = 0; + } + + if ((options[optionIndex].type == OPT_SPACE) || (options[optionIndex].type == OPT_TEXT)) { + continue; + } + + break; + } + } + + if (lara->isKeyHit(IN_UP)) + { + for (int32 i = 0; i < optionsCount; i++) + { + optionIndex--; + + if (optionIndex < 0) { + optionIndex = optionsCount - 1; + } + + if ((options[optionIndex].type == OPT_SPACE) || (options[optionIndex].type == OPT_TEXT)) { + continue; + } + + break; + } + } + + Option &opt = options[optionIndex]; + + switch (opt.type) + { + case OPT_BAR: + { + if (lara->isKeyHit(IN_LEFT)) + { + opt.value -= 16; + if (opt.value < 0) { + opt.value = 0; + } + } + + if (lara->isKeyHit(IN_RIGHT)) + { + opt.value += 16; + if (opt.value > 256) { + opt.value = 256; + } + } + break; + } + + case OPT_SWITCH: + { + if (lara->isKeyHit(IN_LEFT) || lara->isKeyHit(IN_RIGHT)) + { + opt.value = !opt.value; + } + break; + } + + default: ; + } + } + + void onKey() + { + if (lara->input & IN_ACTION) + { + nextState = INV_STATE_CLOSE; + useSlot = itemsList[itemIndex]; + } + } + + void onCompass() + { + if (lara->input & IN_ACTION) + { + frameTarget = 0; + nextState = INV_STATE_CLOSE; + } + } + + void onPassport() + { + if ((passportPage == PASSPORT_PAGE_SAVE_GAME) && (optionsCount > 0)) // error message + { + if (lara->isKeyHit(IN_ACTION) || lara->isKeyHit(IN_JUMP)) + { + frameTarget = 0; + nextState = INV_STATE_CLOSE; + } + return; + } + + if ((page == INV_PAGE_OPTIONS) || (page == INV_PAGE_DEATH)) + { + if ((passportPage == PASSPORT_PAGE_LOAD_GAME) && lara->isKeyHit(IN_RIGHT)) { + passportPage = PASSPORT_PAGE_SAVE_GAME; + frameTarget += 5; + initOptions(); + soundPlay(SND_INV_PAGE, NULL); + } else if ((passportPage == PASSPORT_PAGE_SAVE_GAME) && lara->isKeyHit(IN_RIGHT)) { + passportPage = PASSPORT_PAGE_EXIT_TO_TITLE; + frameTarget += 5; + initOptions(); + soundPlay(SND_INV_PAGE, NULL); + } else if ((passportPage == PASSPORT_PAGE_EXIT_TO_TITLE) && lara->isKeyHit(IN_LEFT)) { + passportPage = PASSPORT_PAGE_SAVE_GAME; + frameTarget -= 5; + initOptions(); + soundPlay(SND_INV_PAGE, NULL); + } else if ((passportPage == PASSPORT_PAGE_SAVE_GAME) && lara->isKeyHit(IN_LEFT)) { + passportPage = PASSPORT_PAGE_LOAD_GAME; + frameTarget -= 5; + initOptions(); + soundPlay(SND_INV_PAGE, NULL); + } + } + + if (lara->isKeyHit(IN_ACTION)) + { + Option &opt = options[optionIndex]; + + if (passportPage == PASSPORT_PAGE_LOAD_GAME) + { + nextLevel(LevelID(opt.value)); + frameTarget = 0; + nextState = INV_STATE_CLOSE; + } + + if (passportPage == PASSPORT_PAGE_SAVE_GAME) + { + if (!gameSave()) + { + optionsHeight = 4; + optionsCount = 0; + optionIndex = OPT_ID_OK; + OPTION_SPACE(); + OPTION_TEXT(STR_GBA_SAVE_WARNING_1); + OPTION_TEXT(STR_GBA_SAVE_WARNING_2); + OPTION_TEXT(STR_GBA_SAVE_WARNING_3); + OPTION_SPACE(); + OPTION_BTN(STR_OK, 0); + } else { + frameTarget = 0; + nextState = INV_STATE_CLOSE; + } + } + + if (passportPage == PASSPORT_PAGE_EXIT_TO_TITLE) + { + nextLevel(LVL_TR1_TITLE); + frameTarget = 0; + nextState = INV_STATE_CLOSE; + } + } + } + + void onDetail() + { + Option &opt = options[optionIndex]; + + if (optionIndex == OPT_ID_GAMMA) + { + int32 gamma = opt.value >> 4; + + if (gSettings.video_gamma != gamma) + { + gSettings.video_gamma = gamma; + palSet(level.palette, gamma << 4, gBrightness); + osSaveSettings(); + } + } + + if (optionIndex == OPT_ID_FPS) + { + if (gSettings.video_fps != opt.value) + { + gSettings.video_fps = opt.value; + osSaveSettings(); + } + } + + if (optionIndex == OPT_ID_VSYNC) + { + if (gSettings.video_vsync != opt.value) + { + gSettings.video_vsync = opt.value; + osSaveSettings(); + } + } + } + + void onSound() + { + Option &opt = options[optionIndex]; + + if ((optionIndex == OPT_ID_SFX) && (gSettings.audio_sfx != opt.value)) + { + gSettings.audio_sfx = opt.value; + osSaveSettings(); + } + + if ((optionIndex == OPT_ID_MUSIC) && (gSettings.audio_music != opt.value)) + { + gSettings.audio_music = opt.value; + osSaveSettings(); + } + } + + void onControls() + { + Option &opt = options[optionIndex]; + + if ((optionIndex == OPT_ID_RUMBLE) && (gSettings.controls_vibration != opt.value)) + { + gSettings.controls_vibration = opt.value; + osJoyVibrate(0, 0xFF, 0xFF); + osSaveSettings(); + } + + if ((optionIndex == OPT_ID_SWAP) && (gSettings.controls_swap != opt.value)) + { + gSettings.controls_swap = opt.value; + osSaveSettings(); + } + } + + void onHome() + { + if (lara->input & IN_ACTION) + { + nextState = INV_STATE_CLOSE; + useSlot = itemsList[itemIndex]; + nextLevel(LVL_TR1_GYM); + } + } + + void onEnd() + { + if (lara->isKeyHit(IN_ACTION) || lara->isKeyHit(IN_JUMP) || lara->isKeyHit(IN_SELECT)) + { + nextLevel(LVL_TR1_TITLE); + state = INV_STATE_NONE; + } + } + + bool onItem() + { + onOption(); + + if (lara->input & (IN_JUMP | IN_SELECT)) + { + frameTarget = 0; + nextState = INV_STATE_READY; + } + + InvSlot slot = itemsList[itemIndex]; + + switch (slot) + { + case SLOT_LEADBAR: + case SLOT_KEY_ITEM_1: + case SLOT_KEY_ITEM_2: + case SLOT_KEY_ITEM_3: + case SLOT_KEY_ITEM_4: + case SLOT_PUZZLE_4: + case SLOT_PUZZLE_3: + case SLOT_PUZZLE_2: + case SLOT_PUZZLE_1: + case SLOT_SCION: + case SLOT_PISTOLS: + case SLOT_SHOTGUN: + case SLOT_MAGNUMS: + case SLOT_UZIS: + case SLOT_MEDIKIT_BIG: + case SLOT_MEDIKIT_SMALL: + nextState = INV_STATE_CLOSE; + useSlot = slot; + break; + case SLOT_COMPASS: + onCompass(); + break; + case SLOT_PASSPORT: + onPassport(); + break; + case SLOT_DETAIL: + onDetail(); + break; + case SLOT_SOUND: + onSound(); + break; + case SLOT_CONTROLS: + onControls(); + break; + case SLOT_HOME: + onHome(); + break; + default: ; + } + + return nextState == INV_STATE_NONE; + } + + void update(int32 frames) + { + updateFading(frames); + + if (page == INV_PAGE_END) + { + onEnd(); + return; + } + + if (state != INV_STATE_SHOW) + { + rotItem += rotItemInc * frames; + } + + if (timer > 0) + { + timer -= frames; + + height += heightInc * frames; + radius += radiusInc * frames; + pitch += pitchInc * frames; + rot += rotInc * frames; + selDist += selDistInc * frames; + selRotPre += selRotPreInc * frames; + selRotX += selRotXInc * frames; + selRotY += selRotYInc * frames; + + if (timer <= 0) + { + timer = 0; + state = nextState; + nextState = INV_STATE_NONE; + height = heightTarget; + radius = radiusTarget; + pitch = pitchTarget; + rot = rotTarget; + selDist = selDistTarget; + selRotPre = selRotPreTarget; + selRotX = selRotXTarget; + selRotY = selRotYTarget; + + heightInc = 0; + radiusInc = 0; + pitchInc = 0; + rotInc = 0; + rotItem = 0; + rotItemInc = 512; + selDistInc = 0; + selRotPreInc = 0; + selRotXInc = 0; + selRotYInc = 0; + + itemIndex = nextIndex; + } + } + + switch (state) + { + case INV_STATE_CLOSE: + { + close(); + break; + } + + case INV_STATE_READY: + { + if ((lara->input & IN_LEFT) && (itemsCount > 1)) { + soundPlay(SND_INV_SPIN, NULL); + nextIndex = itemIndex + 1; + if (nextIndex >= itemsCount) { + nextIndex -= itemsCount; + } + setState(INV_STATE_SPIN, INV_STATE_READY, 12); + setRot(ANGLE_360 / itemsCount, nextIndex * ANGLE_360 / itemsCount); + } else if ((lara->input & IN_RIGHT) && (itemsCount > 1)) { + soundPlay(SND_INV_SPIN, NULL); + nextIndex = itemIndex - 1; + if (nextIndex < 0) { + nextIndex += itemsCount; + } + setState(INV_STATE_SPIN, INV_STATE_READY, 12); + setRot(-ANGLE_360 / itemsCount, nextIndex * ANGLE_360 / itemsCount); + } else { + if (lara->input & IN_UP) { + if (page == INV_PAGE_OPTIONS) { + setState(INV_STATE_CLOSING, INV_STATE_PAGE_MAIN, 12); + setRadius(0); + setRot(ANGLE_180, rot - ANGLE_180); + setPitch(ANGLE_45); + } else if ((page == INV_PAGE_MAIN) && (numKeys > 0)) { + setState(INV_STATE_CLOSING, INV_STATE_PAGE_KEYS, 12); + setRadius(0); + setRot(ANGLE_180, rot - ANGLE_180); + setPitch(ANGLE_45); + } + } else if (lara->input & IN_DOWN) { + if (page == INV_PAGE_KEYS) { + setState(INV_STATE_CLOSING, INV_STATE_PAGE_MAIN, 12); + setRadius(0); + setRot(ANGLE_180, rot - ANGLE_180); + setPitch(-ANGLE_45); + } else if (page == INV_PAGE_MAIN) { + setState(INV_STATE_CLOSING, INV_STATE_PAGE_OPTIONS, 12); + setRadius(0); + setRot(ANGLE_180, rot - ANGLE_180); + setPitch(-ANGLE_45); + } + } else if ((lara->isKeyHit(IN_SELECT) || lara->isKeyHit(IN_JUMP)) && (page != INV_PAGE_TITLE)) { + soundPlay(SND_INV_HIDE, NULL); + useSlot = SLOT_MAX; + close(); + } else if (lara->input & IN_ACTION) { + soundPlay(INV_SLOTS[itemsList[itemIndex]].snd, NULL); + setState(INV_STATE_SELECT, INV_STATE_SHOW, 8); + setSelection(true); + initOptions(); + } + } + + break; + } + + case INV_STATE_DEATH: + { + soundPlay(INV_SLOTS[itemsList[itemIndex]].snd, NULL); + setState(INV_STATE_SELECT, INV_STATE_SHOW, 8); + setSelection(true); + initOptions(); + break; + } + + case INV_STATE_SHOW: + { + rotItem = 0; + + if (animate(frames)) + break; + + if (!onItem() && (frameIndex == frameTarget)) + { + setState(INV_STATE_DESELECT, nextState, 8); + setSelection(false); + break; + } + + break; + } + + case INV_STATE_PAGE_MAIN: + { + pitch = -pitch; + setState(INV_STATE_OPENING, INV_STATE_READY, 12); + setRadius(INV_RING_RADIUS); + setPitch(0); + setPage(INV_PAGE_MAIN); + break; + } + + case INV_STATE_PAGE_KEYS: + { + pitch = -pitch; + setState(INV_STATE_OPENING, INV_STATE_READY, 12); + setRadius(INV_RING_RADIUS); + setPitch(0); + setPage(INV_PAGE_KEYS); + break; + } + + case INV_STATE_PAGE_OPTIONS: + { + pitch = -pitch; + setState(INV_STATE_OPENING, INV_STATE_READY, 12); + setRadius(INV_RING_RADIUS); + setPitch(0); + setPage(INV_PAGE_OPTIONS); + break; + } + + default: ; + } + } + + void initOptions() + { + optionsCount = 0; + optionIndex = 0; + optionsWidth = 216; + optionsHeight = 4; + + InvSlot slot = itemsList[itemIndex]; + + switch (slot) + { + case SLOT_PASSPORT: + { + if (passportPage == PASSPORT_PAGE_LOAD_GAME) + { + OPTION_BTN(STR_TR1_LEVEL1, LVL_TR1_1); + OPTION_BTN(STR_TR1_LEVEL2, LVL_TR1_2); + if (osCheckSave()) + { + OPTION_SPACE(); + OPTION_BTN(STR_CURRENT_POSITION, LVL_LOAD); + optionIndex = optionsCount - 1; + } + } + break; + } + case SLOT_DETAIL: + { + OPTION_BAR(STR_OPT_DETAIL_GAMMA, gSettings.video_gamma << 4); + OPTION_SWITCH(STR_OPT_DETAIL_FPS, gSettings.video_fps); + OPTION_SWITCH(STR_OPT_DETAIL_VSYNC, gSettings.video_vsync); + break; + } + case SLOT_SOUND: + { + OPTION_SWITCH(STR_OPT_SOUND_SFX, gSettings.audio_sfx); + OPTION_SWITCH(STR_OPT_SOUND_MUSIC, gSettings.audio_music); + break; + } + case SLOT_CONTROLS: + { + OPTION_SWITCH(STR_OPT_CONTROLS_VIBRATION, gSettings.controls_vibration); + OPTION_SWITCH(STR_OPT_CONTROLS_SWAP, gSettings.controls_swap); + /* + OPTION_SPACE(); + OPTION_CTRL(STR_CTRL_RUN, 0); + OPTION_CTRL(STR_CTRL_BACK, 0); + OPTION_CTRL(STR_CTRL_RIGHT, 0); + OPTION_CTRL(STR_CTRL_LEFT, 0); + OPTION_CTRL(STR_CTRL_WALK, 0); + OPTION_CTRL(STR_CTRL_JUMP, 0); + OPTION_CTRL(STR_CTRL_ACTION, 0); + OPTION_CTRL(STR_CTRL_WEAPON, 0); + OPTION_CTRL(STR_CTRL_LOOK, 0); + OPTION_CTRL(STR_CTRL_ROLL, 0); + OPTION_CTRL(STR_CTRL_INVENTORY, 0); + OPTION_CTRL(STR_CTRL_PAUSE, 0); + */ + break; + } + default: ; + } + } + + void drawOptions() + { + if (state != INV_STATE_SHOW) + return; + + if (optionsCount == 0) + return; + + int32 w = optionsWidth; + int32 h = optionsHeight; + int32 y = (FRAME_HEIGHT - h) / 2 - 12; + + renderBorder((FRAME_WIDTH - w) / 2, y, w, h, 25, 14, 10, 2); + + w -= 4; + h = 18; + y += 2; + + for (int32 i = 0; i < optionsCount; i++) + { + const Option &opt = options[i]; + + if (optionIndex == i) { + renderBorder((FRAME_WIDTH - w) / 2, y, w, h, -1, 15, 15, 1); + } + + switch (opt.type) + { + case OPT_SPACE: + { + y -= 10; + break; + } + + case OPT_BUTTON: + { + drawText(0, y + 16, STR[opt.str], TEXT_ALIGN_CENTER); + break; + } + + case OPT_BAR: + { + renderBar(FRAME_WIDTH / 2, y + 6, 80, opt.value, BAR_OPTION); + drawText(-FRAME_WIDTH / 2 - 8, y + 16, STR[opt.str], TEXT_ALIGN_RIGHT); + break; + } + + case OPT_SWITCH: + { + drawText(-FRAME_WIDTH / 2 - 8, y + 16, STR[opt.str], TEXT_ALIGN_RIGHT); + drawText(44, y + 16, STR[opt.value ? STR_ON : STR_OFF], TEXT_ALIGN_CENTER); + break; + } + + case OPT_VALUE: + { + break; + } + + case OPT_TEXT: + { + drawText(0, y + 16, STR[opt.str], TEXT_ALIGN_CENTER); + break; + } + } + + y += 18; + } + } + + void drawSlot(InvSlot slot) + { + int32 type = INV_SLOTS[slot].type; + + bool current = itemsList[itemIndex] == slot; + bool selected = current && ((state == INV_STATE_SHOW) || (frameTarget != frameIndex)); + + if ((type == ITEM_INV_PASSPORT) && !selected) { + type = ITEM_INV_PASSPORT_CLOSED; + } + + ItemObj item; + memset(&item, 0, sizeof(item)); + item.type = type; + item.intensity = 255; + item.visibleMask = itemVisMask; + item.animIndex = level.models[type].animIndex; + item.frameIndex = level.anims[item.animIndex].frameBegin + (selected ? frameIndex : 0); + + const AnimFrame *frameA, *frameB; + + int32 frameRate; + int32 frameDelta = item.getFrames(frameA, frameB, frameRate); + + calcLightingStatic(255 << 5); + drawNodesLerp(&item, frameA, frameB, frameDelta, frameRate); + + if ((state == INV_STATE_READY) && current && (counts[slot] > 1)) + { + char buf[32]; + int2str(counts[slot], buf); + + // convert ASCII to small digits (TR glyph) + char* ptr = buf; + while (*ptr) + { + *ptr -= 47; + ptr++; + } + + drawText(FRAME_WIDTH / 2 + 32, FRAME_HEIGHT - 32, buf, TEXT_ALIGN_LEFT); + + if (slot == SLOT_MEDIKIT_SMALL || slot == SLOT_MEDIKIT_BIG) + { + int32 v = (lara->health << 8) / LARA_MAX_HEALTH; + renderBar((FRAME_WIDTH - 104) / 2, 24, 100, v, BAR_HEALTH); + } + } + } + + void drawPage() + { + int16 angleX, angleY; + + anglesFromVector(0, -(height + INV_CAMERA_Y), -INV_CAMERA_Z, angleX, angleY); + + vec3i pos = _vec3i(0, height, radius + INV_CAMERA_Z); + + matrixSetView(pos, angleX + pitch, angleY); + matrixTranslateAbs(0, 0, 0); + + for (int32 i = itemsCount - 1; i >= 0; i--) + { + matrixPush(); + matrixRotateY(i * ANGLE_360 / itemsCount - rot - ANGLE_90); + matrixTranslateRel(radius, 0, 0); + matrixRotateYXZ(0, ANGLE_90, 0); + + if (itemIndex == i) + { + matrixRotateX(selRotPre); + matrixTranslateRel(0, 0, selDist); + matrixRotateYXZ(-selRotX, -selRotY, 0); + matrixRotateY(rotItem); + } + + drawSlot(itemsList[i]); + + matrixPop(); + } + + if (frameIndex == frameTarget) { + drawOptions(); + } + } + + void drawEndPage() + { + int32 y = 48; + for (int32 i = 0; i <= STR_ALPHA_END_6 - STR_ALPHA_END_1; i++) + { + drawText(0, y, STR[STR_ALPHA_END_1 + i], TEXT_ALIGN_CENTER); + y += 16; + } + } + + void draw() + { + //clear(); + ASSERT(background); + renderBackground(background); + + if (page == INV_PAGE_END) + { + drawEndPage(); + return; + } + + drawPage(); + + if (state == INV_STATE_READY || + state == INV_STATE_SPIN || + state == INV_STATE_SELECT || + state == INV_STATE_DESELECT || + state == INV_STATE_SHOW) + { + if (state != INV_STATE_SHOW && + state != INV_STATE_SELECT && + state != INV_STATE_DESELECT) + { + drawText(0, 20, STR[getTitleStr()], TEXT_ALIGN_CENTER); + + if ((page == INV_PAGE_OPTIONS) || (page == INV_PAGE_MAIN && numKeys)) + { + drawText(4, 4 + 16, "[", TEXT_ALIGN_LEFT); + drawText(-6, 4 + 16, "[", TEXT_ALIGN_RIGHT); + } + + if (page == INV_PAGE_MAIN || page == INV_PAGE_KEYS) + { + drawText(4, FRAME_HEIGHT - 5, "]", TEXT_ALIGN_LEFT); + drawText(-6, FRAME_HEIGHT - 5, "]", TEXT_ALIGN_RIGHT); + } + } + + if ((frameIndex == frameTarget) && (state != INV_STATE_SPIN)) + { + const char* str = STR[getItemStr()]; + + drawText(0, FRAME_HEIGHT - 8, str, TEXT_ALIGN_CENTER); + + if ((state == INV_STATE_SHOW) && (itemsList[itemIndex] == SLOT_PASSPORT)) + { + if ((page == INV_PAGE_OPTIONS) || (page == INV_PAGE_DEATH)) + { + int32 len = getTextWidth(str); + if ((passportPage == PASSPORT_PAGE_LOAD_GAME) || (passportPage == PASSPORT_PAGE_SAVE_GAME)) { + drawText((FRAME_WIDTH + len) / 2 + 4, FRAME_HEIGHT - 8, "$\x6D", TEXT_ALIGN_LEFT); + } + if ((passportPage == PASSPORT_PAGE_SAVE_GAME) || (passportPage == PASSPORT_PAGE_EXIT_TO_TITLE)) { + drawText((FRAME_WIDTH - len) / 2 - 18, FRAME_HEIGHT - 8, "$\x6C", TEXT_ALIGN_LEFT); + } + } + } + } + + } + } +}; + +EWRAM_DATA Inventory inventory; + +#endif diff --git a/src/fixed/item.h b/src/fixed/item.h new file mode 100644 index 00000000..6cd4e775 --- /dev/null +++ b/src/fixed/item.h @@ -0,0 +1,1594 @@ +#ifndef H_ITEM +#define H_ITEM + +#include "common.h" +#include "camera.h" +#include "room.h" + +EWRAM_DATA AABBs tmpBox; + +#define GRAVITY 6 + +int32 alignOffset(int32 a, int32 b) +{ + int32 ca = a >> 10; + int32 cb = b >> 10; + + if (ca == cb) { + return 0; + } + + a &= 1023; + + if (ca < cb) { + return 1025 - a; + } + + return -(a + 1); +} + +void* soundPlay(int16 id, const vec3i* pos) +{ + if (!gSettings.audio_sfx) + return NULL; + + if (id < 0) + return NULL; + + // TODO gym + // 0 -> 200 + // 4 -> 204 + + int16 a = level.soundMap[id]; + + if (a == -1) + return NULL; + + const SoundInfo* b = level.soundsInfo + a; + + if (b->chance && b->chance < rand_draw()) + return NULL; + + int32 volume = b->volume; + + if (pos) + { + vec3i d = *pos - playersExtra[0].camera.target.pos; // TODO find nearest camera for coop + + if (abs(d.x) >= SND_MAX_DIST || abs(d.y) >= SND_MAX_DIST || abs(d.z) >= SND_MAX_DIST) + return NULL; + + volume -= (phd_sqrt(dot(d, d)) << 2); + } + + if (SI_GAIN(b->flags)) { + volume -= rand_draw() >> 2; + } + + volume = X_MIN(volume, 0x7FFF) >> 9; + + if (volume <= 0) + return NULL; + + volume += 1; // 63 to 64 (1 << SND_VOL_SHIFT) for 100% vol samples + + int32 pitch = 128; + + if (SI_PITCH(b->flags)) { + pitch += ((rand_draw() * 13) >> 14) - 13; + } + + int32 index = b->index; + if (SI_COUNT(b->flags) > 1) { + index += (rand_draw() * SI_COUNT(b->flags)) >> 15; + } + + return sndPlaySample(index, volume, pitch, SI_MODE(b->flags)); +} + +void soundStop(int16 id) +{ + int16 a = level.soundMap[id]; + + if (a == -1) + return; + + const SoundInfo* b = level.soundsInfo + a; + + for (int32 i = 0; i < SI_COUNT(b->flags); i++) + { + sndStopSample(b->index + i); + } +} + +int32 ItemObj::getFrames(const AnimFrame* &frameA, const AnimFrame* &frameB, int32 &animFrameRate) const +{ + const Anim* anim = level.anims + animIndex; + + animFrameRate = anim->frameRate; + + int32 frameSize = (sizeof(AnimFrame) >> 1) + (level.models[type].count << 1); + + int32 frame = frameIndex - anim->frameBegin; + +// int32 d = FixedInvU(animFrameRate); +// int32 indexA = frame * d >> 16; + + int32 indexA = frame / animFrameRate; + int32 frameDelta = frame - indexA * animFrameRate; + int32 indexB = indexA + 1; + + if (indexB * animFrameRate >= anim->frameEnd) + { + indexB = indexA; + } + + frameA = (AnimFrame*)(level.animFrames + (anim->frameOffset >> 1) + indexA * frameSize); + frameB = (AnimFrame*)(level.animFrames + (anim->frameOffset >> 1) + indexB * frameSize); + + if (!frameDelta || frameA == frameB) + return 0; + + indexB *= animFrameRate; + if (indexB > anim->frameEnd) { + animFrameRate -= indexB - anim->frameEnd; + } + + return frameDelta; +} + +const AnimFrame* ItemObj::getFrame() const +{ + const AnimFrame *frameA, *frameB; + + int32 frameRate; + int32 frameDelta = getFrames(frameA, frameB, frameRate); + + return (frameDelta <= (frameRate >> 1)) ? frameA : frameB; +} + +const AABBs& ItemObj::getBoundingBox(bool lerp) const +{ + if (!lerp) + return getFrame()->box; + + const AnimFrame *frameA, *frameB; + + int32 frameRate; + int32 frameDelta = getFrames(frameA, frameB, frameRate); + + if (!frameDelta) + return frameA->box; + + int32 d = GET_FRAME_T(frameDelta, frameRate); + + #define COMP_LERP(COMP) tmpBox.COMP = frameA->box.COMP + ((frameB->box.COMP - frameA->box.COMP) * d >> 16); + + COMP_LERP(minX); + COMP_LERP(maxX); + COMP_LERP(minY); + COMP_LERP(maxY); + COMP_LERP(minZ); + COMP_LERP(maxZ); + + #undef COMP_LERP + + return tmpBox; +} + +void ItemObj::move() +{ + const Anim* anim = level.anims + animIndex; + + int32 sp = anim->speed; + + if (flags & ITEM_FLAG_GRAVITY) + { + sp += anim->accel * (frameIndex - anim->frameBegin - 1); + hSpeed -= sp >> 16; + sp += anim->accel; + hSpeed += sp >> 16; + + vSpeed += (vSpeed < 128) ? GRAVITY : 1; + + pos.y += vSpeed; + } else { + sp += anim->accel * (frameIndex - anim->frameBegin); + + hSpeed = sp >> 16; + } + + int16 realAngle = (type == ITEM_LARA) ? extraL->moveAngle : angle.y; + + int32 s, c; + sincos(realAngle, s, c); + + pos.x += s * hSpeed >> FIXED_SHIFT; + pos.z += c * hSpeed >> FIXED_SHIFT; +} + +const Anim* ItemObj::animSet(int32 newAnimIndex, bool resetState, int32 frameOffset) +{ + const Anim* anim = level.anims + newAnimIndex; + + animIndex = newAnimIndex; + frameIndex = anim->frameBegin + frameOffset; + + if (resetState) { + state = goalState = uint8(anim->state); + } + + return anim; +} + +const Anim* ItemObj::animChange(const Anim* anim) +{ + if (goalState == state || !anim->statesCount) + return anim; + + const AnimState* animState = level.animStates + anim->statesStart; + + for (int32 i = 0; i < anim->statesCount; i++) + { + if (goalState == animState->state) + { + const AnimRange* animRange = level.animRanges + animState->rangesStart; + + for (int32 j = 0; j < animState->rangesCount; j++) + { + if ((frameIndex >= animRange->frameBegin) && (frameIndex <= animRange->frameEnd)) + { + frameIndex = animRange->nextFrameIndex; + animIndex = animRange->nextAnimIndex; + anim = level.anims + animRange->nextAnimIndex; + state = uint8(anim->state); + return anim; + } + animRange++; + } + } + animState++; + } + + return anim; +} + +void ItemObj::animCmd(bool fx, const Anim* anim) +{ + if (!anim->commandsCount) return; + + const int16 *ptr = level.animCommands + anim->commandsStart; + + for (int32 i = 0; i < anim->commandsCount; i++) + { + int32 cmd = *ptr++; + + switch (cmd) + { + case ANIM_CMD_NONE: + break; + + case ANIM_CMD_OFFSET: + { + if (!fx) + { + int32 s, c; + sincos(angle.y, s, c); + int32 x = ptr[0]; + int32 z = ptr[2]; + pos.x += X_ROTX(x, z, -s, c); + pos.y += ptr[1]; + pos.z += X_ROTY(x, z, -s, c); + } + ptr += 3; + break; + } + + case ANIM_CMD_JUMP: + { + if (!fx) + { + if (type == ITEM_LARA && extraL->vSpeedHack) { + vSpeed = -extraL->vSpeedHack; + extraL->vSpeedHack = 0; + } else { + vSpeed = ptr[0]; + } + hSpeed = ptr[1]; + flags |= ITEM_FLAG_GRAVITY; + } + ptr += 2; + break; + } + + case ANIM_CMD_EMPTY: + { + if (!fx) { + ASSERT(type == ITEM_LARA); + extraL->weaponState = WEAPON_STATE_FREE; + } + break; + } + + case ANIM_CMD_KILL: + { + if (!fx) { + flags &= ~ITEM_FLAG_STATUS; + flags |= ITEM_FLAG_STATUS_INACTIVE; + } + break; + } + + case ANIM_CMD_SOUND: + { + if (fx && frameIndex == ptr[0]) { + soundPlay(ptr[1] & 0x03FFF, &pos); + } + ptr += 2; + break; + } + + case ANIM_CMD_EFFECT: + { + if (fx && frameIndex == ptr[0]) + { + switch (ptr[1]) { + case FX_ROTATE_180 : + { + angle.y += ANGLE_180; + break; + } + + /* + case FX_FLOOR_SHAKE : ASSERT(false); + */ + + case FX_LARA_NORMAL : + { + ASSERT(type == ITEM_LARA); + animSet(11, true); // Lara::ANIM_STAND + break; + } + + case FX_LARA_BUBBLES : + { + fxBubbles(room, JOINT_HEAD, _vec3i(0, 0, 50)); + break; + } + + case FX_LARA_HANDSFREE : + { + ASSERT(type == ITEM_LARA && extraL); + extraL->weaponState = WEAPON_STATE_FREE; + break; + } + /* + case FX_DRAW_RIGHTGUN : drawGun(true); break; + case FX_DRAW_LEFTGUN : drawGun(false); break; + case FX_SHOT_RIGHTGUN : game->addMuzzleFlash(this, LARA_RGUN_JOINT, LARA_RGUN_OFFSET, 1 + camera->cameraIndex); break; + case FX_SHOT_LEFTGUN : game->addMuzzleFlash(this, LARA_LGUN_JOINT, LARA_LGUN_OFFSET, 1 + camera->cameraIndex); break; + case FX_MESH_SWAP_1 : + case FX_MESH_SWAP_2 : + case FX_MESH_SWAP_3 : Character::cmdEffect(fx); + case 26 : break; // TODO TR2 reset_hair + case 32 : break; // TODO TR3 footprint + default : LOG("unknown effect command %d (anim %d)\n", fx, animation.index); ASSERT(false); + */ + default : ; + } + } + ptr += 2; + break; + } + } + } +} + +void ItemObj::animSkip(int32 stateBefore, int32 stateAfter, bool advance) +{ + goalState = stateBefore; + + vec3i p = pos; + + while (state != goalState) + { + animProcess(false); + } + + if (advance) { + animProcess(); + } + + pos = p; + vSpeed = 0; + hSpeed = 0; + + goalState = stateAfter; +} + +#define ANIM_MOVE_LERP_POS (16) +#define ANIM_MOVE_LERP_ROT ANGLE(2) + +void ItemObj::animProcess(bool movement) +{ + ASSERT(level.models[type].count > 0); + + const Anim* anim = level.anims + animIndex; + +#ifndef STATIC_ITEMS + frameIndex++; +#endif + + anim = animChange(anim); + + if ((type != ITEM_LARA) && (nextState == state)) { + nextState = 0; + } + + if (frameIndex > anim->frameEnd) + { + animCmd(false, anim); + + frameIndex = anim->nextFrameIndex; + animIndex = anim->nextAnimIndex; + anim = level.anims + anim->nextAnimIndex; + state = uint8(anim->state); + + if (type != ITEM_LARA) + { + goalState = state; + if (nextState == state) { + nextState = 0; + } + } + } + + animCmd(true, anim); + +#ifndef STATIC_ITEMS + if (movement) { + move(); + } +#endif +} + +bool ItemObj::animIsEnd(int32 offset) const +{ + return frameIndex == level.anims[animIndex].frameEnd - offset; +} + +void ItemObj::animHit(int32 dirX, int32 dirZ, int32 hitTimer) +{ + ASSERT(type == ITEM_LARA); + ASSERT(extraL != NULL); + + extraL->hitQuadrant = uint16(angle.y - phd_atan(dirZ, dirX) + ANGLE_180 + ANGLE_45) >> ANGLE_SHIFT_90; + extraL->hitTimer = hitTimer; +} + +bool ItemObj::moveTo(const vec3i &point, ItemObj* item, bool lerp) +{ + // lerp position + vec3i p = item->getRelative(point); + + if (!lerp) + { + pos = p; + angle = item->angle; + return true; + } + + vec3i posDelta = p - pos; + + int32 dist = phd_sqrt(X_SQR(posDelta.x) + X_SQR(posDelta.y) + X_SQR(posDelta.z)); + + if (dist > ANIM_MOVE_LERP_POS) { + pos += (posDelta * ANIM_MOVE_LERP_POS) / dist; + } else { + pos = p; + } + + // lerp rotation + angle.x = angleLerp(angle.x, item->angle.x, ANIM_MOVE_LERP_ROT); + angle.y = angleLerp(angle.y, item->angle.y, ANIM_MOVE_LERP_ROT); + angle.z = angleLerp(angle.z, item->angle.z, ANIM_MOVE_LERP_ROT); + + return (pos == p && angle == item->angle); +} + +ItemObj* ItemObj::add(ItemType type, Room* room, const vec3i &pos, int32 angleY) +{ + if (!ItemObj::sFirstFree) { + ASSERT(false); + return NULL; + } + + ItemObj* item = ItemObj::sFirstFree; + ItemObj::sFirstFree = item->nextItem; + + item->type = type; + item->pos = pos; + item->angle.y = angleY; + item->intensity = 128; + + item->init(room); + + return item; +} + +void ItemObj::remove() +{ + deactivate(); + room->remove(this); + + for (int32 i = 0; i < MAX_PLAYERS; i++) + { + if (playersExtra[i].armR.target == this) playersExtra[i].armR.target = NULL; + if (playersExtra[i].armL.target == this) playersExtra[i].armL.target = NULL; + } + + nextItem = ItemObj::sFirstFree; + ItemObj::sFirstFree = this; +} + +void ItemObj::activate() +{ + //ASSERT(!flags.active) TODO check LEVEL3B + + flags |= ITEM_FLAG_ACTIVE; + + nextActive = ItemObj::sFirstActive; + ItemObj::sFirstActive = this; +} + +void ItemObj::deactivate() +{ + ItemObj* prev = NULL; + ItemObj* curr = ItemObj::sFirstActive; + + while (curr) + { + ItemObj* next = curr->nextActive; + + if (curr == this) + { + flags &= ~ITEM_FLAG_ACTIVE; + nextActive = NULL; + + if (prev) { + prev->nextActive = next; + } else { + ItemObj::sFirstActive = next; + } + + break; + } + + prev = curr; + curr = next; + } +} + +void ItemObj::hit(int32 damage, const vec3i &point, int32 soundId) +{ + // +} + +void ItemObj::fxBubbles(Room *fxRoom, int32 fxJoint, const vec3i &fxOffset) +{ + int32 count = rand_draw() % 3; + + if (!count) + return; + + vec3i fxPos = pos + getJoint(fxJoint, fxOffset); + + for (int32 i = 0; i < count; i++) + { + ItemObj::add(ITEM_BUBBLE, fxRoom, fxPos, 0); + } +} + +void ItemObj::fxRicochet(Room *fxRoom, const vec3i &fxPos, bool fxSound) +{ + ItemObj* ricochet = ItemObj::add(ITEM_RICOCHET, fxRoom, fxPos, 0); + + if (!ricochet) + return; + + ricochet->timer = 4; + ricochet->frameIndex = rand_draw() % (-level.models[ricochet->type].count); + + if (fxSound) { + soundPlay(SND_RICOCHET, &ricochet->pos); + } +} + +void ItemObj::fxBlood(const vec3i &fxPos, int16 fxAngleY, int16 fxSpeed) +{ + ItemObj* blood = ItemObj::add(ITEM_BLOOD, room, fxPos, fxAngleY); + + if (!blood) + return; + + blood->hSpeed = fxSpeed; + blood->timer = 4; + blood->flags |= ITEM_FLAG_ANIMATED; +} + +void ItemObj::fxSmoke(const vec3i &fxPos) +{ + ItemObj* smoke = ItemObj::add(ITEM_SMOKE, room, fxPos, 0); + + if (!smoke) + return; + + smoke->timer = 3; + smoke->flags |= ITEM_FLAG_ANIMATED; +} + +void ItemObj::fxSplash() +{ + vec3i fxPos = pos; + fxPos.y = getWaterLevel(); + + // TODO TR3+ + for (int32 i = 0; i < 10; i++) + { + ItemObj* splash = ItemObj::add(ITEM_SPLASH, room, fxPos, int16(rand_draw() - ANGLE_90) << 1); + + if (!splash) + return; + + splash->hSpeed = int16(rand_draw() >> 8); + splash->flags |= ITEM_FLAG_ANIMATED; + } +} + +void ItemObj::updateRoom(int32 offset) +{ + Room* nextRoom = room->getRoom(pos.x, pos.y + offset, pos.z); + + if (room != nextRoom) + { + room->remove(this); + nextRoom->add(this); + } + + const Sector* sector = room->getSector(pos.x, pos.z); + roomFloor = sector->getFloor(pos.x, pos.y, pos.z); +} + +bool ItemObj::isKeyHit(InputState state) const +{ + return (input & state) && !(extraL->lastInput & state); +} + +vec3i ItemObj::getRelative(const vec3i &point) const +{ + matrixPush(); + + Matrix &m = matrixGet(); + + matrixSetIdentity(); + matrixRotateYXZ(angle.x, angle.y, angle.z); + + vec3i p; + p.x = pos.x + (DP33(m.e00, m.e01, m.e02, point.x, point.y, point.z) >> FIXED_SHIFT); + p.y = pos.y + (DP33(m.e10, m.e11, m.e12, point.x, point.y, point.z) >> FIXED_SHIFT); + p.z = pos.z + (DP33(m.e20, m.e21, m.e22, point.x, point.y, point.z) >> FIXED_SHIFT); + + matrixPop(); + + return p; +} + +int32 ItemObj::getWaterLevel() const +{ + const Sector* sector = room->getWaterSector(pos.x, pos.z); + if (sector) { + if (sector->roomAbove == NO_ROOM) { + return sector->getCeiling(pos.x, pos.y, pos.z); + } else { + return sector->ceiling << 8; + } + } + + return WALL; +} + +int32 ItemObj::getWaterDepth() const +{ + const Sector* sector = room->getWaterSector(pos.x, pos.z); + + if (sector) + return sector->getFloor(pos.x, pos.y, pos.z) - (sector->ceiling * 256); + + return WALL; +} + +int32 ItemObj::getBridgeFloor(int32 x, int32 z) const +{ + if (type == ITEM_BRIDGE_FLAT) + return pos.y; + + int32 h; + if (angle.y == ANGLE_0) { + h = 1024 - x; + } else if (angle.y == ANGLE_180) { + h = x; + } else if (angle.y == ANGLE_90) { + h = z; + } else { + h = 1024 - z; + } + + h &= 1023; + + return pos.y + ((type == ITEM_BRIDGE_TILT_1) ? (h >> 2) : (h >> 1)); +} + +int32 ItemObj::getTrapDoorFloor(int32 x, int32 z) const +{ + int32 dx = (pos.x >> 10) - (x >> 10); + int32 dz = (pos.z >> 10) - (z >> 10); + + if (((dx == 0) && (dz == 0)) || + ((dx == 0) && (dz == 1) && (angle.y == ANGLE_0)) || + ((dx == 0) && (dz == -1) && (angle.y == ANGLE_180)) || + ((dx == 1) && (dz == 0) && (angle.y == ANGLE_90)) || + ((dx == -1) && (dz == 0) && (angle.y == -ANGLE_90))) + { + return pos.y; + } + + return WALL; +} + +int32 ItemObj::getDrawBridgeFloor(int32 x, int32 z) const +{ + int32 dx = (pos.x >> 10) - (x >> 10); + int32 dz = (pos.z >> 10) - (z >> 10); + + if (((dx == 0) && ((dz == -1) || (dz == -2)) && (angle.y == ANGLE_0)) || + ((dx == 0) && ((dz == 1) || (dz == 2)) && (angle.y == ANGLE_180)) || + ((dz == 0) && ((dx == -1) || (dz == -2)) && (angle.y == ANGLE_90)) || + ((dz == 0) && ((dx == 1) || (dz == 2)) && (angle.y == -ANGLE_90))) + { + return pos.y; + } + + return WALL; +} + +void ItemObj::getItemFloorCeiling(int32 x, int32 y, int32 z, int32* floor, int32* ceiling) const +{ + int32 h = WALL; + + switch (type) + { + case ITEM_TRAP_FLOOR: + { + if (state == 0 || state == 1) { + h = pos.y - 512; + } + break; + } + case ITEM_DRAWBRIDGE: + { + if (state == 1) { + h = getDrawBridgeFloor(x, z); + } + break; + } + case ITEM_BRIDGE_FLAT: + case ITEM_BRIDGE_TILT_1: + case ITEM_BRIDGE_TILT_2: + { + h = getBridgeFloor(x, z); + break; + } + case ITEM_TRAP_DOOR_1: + case ITEM_TRAP_DOOR_2: + { + if (state != 0) + return; + + h = getTrapDoorFloor(x, z); + + if ((floor && (h >= *floor)) || (ceiling && (h <= *ceiling))) + return; + } + } + + if (h == WALL) + return; + + if (floor && (y <= h)) + { + *floor = h; + } + + if (ceiling && (y > h)) + { + *ceiling = h + 256; + } +} + +vec3i ItemObj::getJoint(int32 jointIndex, const vec3i &offset) const +{ + const Model* model = level.models + type; + + const AnimFrame* frame = getFrame(); + + const uint32* frameAngles = (uint32*)(frame->angles + 1); + + Matrix* oldMatrixPtr = gMatrixPtr; + + matrixPush(); + matrixSetIdentity(); + matrixRotateYXZ(angle.x, angle.y, angle.z); + + const ModelNode* node = level.nodes + model->nodeIndex; + + matrixFrame(&frame->pos, frameAngles); + + ASSERT(jointIndex < model->count); + + for (int32 i = 0; i < jointIndex; i++) + { + if (node->flags & 1) matrixPop(); + if (node->flags & 2) matrixPush(); + + matrixFrame(&node->pos, ++frameAngles); + + // TODO extra rotations + + node++; + } + + matrixTranslateRel(offset.x, offset.y, offset.z); + + Matrix &m = matrixGet(); + vec3i result = _vec3i(m.e03 >> FIXED_SHIFT, m.e13 >> FIXED_SHIFT, m.e23 >> FIXED_SHIFT); + + gMatrixPtr = oldMatrixPtr; + + return result; +} + +int32 ItemObj::getSpheres(Sphere* spheres, bool flag) const +{ + const Model* model = level.models + type; + + const AnimFrame* frame = getFrame(); + const uint32* frameAngles = (uint32*)(frame->angles + 1); + + const Mesh** meshPtr = level.meshes + model->start; + + int32 x, y, z; + + if (flag) { + x = pos.x; + y = pos.y; + z = pos.z; + matrixPush(); + matrixSetIdentity(); + } else { + x = y = z = 0; + matrixPush(); + matrixTranslateAbs(pos.x, pos.y, pos.z); + } + + matrixRotateYXZ(angle.x, angle.y, angle.z); + + const ModelNode* node = level.nodes + model->nodeIndex; + + matrixFrame(&frame->pos, frameAngles); + + Sphere* sphere = spheres; + + matrixPush(); + { + const Mesh* mesh = *meshPtr; + matrixTranslateRel(mesh->center.x, mesh->center.y, mesh->center.z); + Matrix &m = matrixGet(); + sphere->center.x = x + (m.e03 >> FIXED_SHIFT); + sphere->center.y = y + (m.e13 >> FIXED_SHIFT); + sphere->center.z = z + (m.e23 >> FIXED_SHIFT); + sphere->radius = mesh->radius; + sphere++; + meshPtr++; + } + matrixPop(); + + for (int32 i = 1; i < model->count; i++) + { + if (node->flags & 1) matrixPop(); + if (node->flags & 2) matrixPush(); + + matrixFrame(&node->pos, ++frameAngles); + + // TODO extra rotations + + matrixPush(); + { + const Mesh* mesh = *meshPtr; + matrixTranslateRel(mesh->center.x, mesh->center.y, mesh->center.z); + Matrix &m = matrixGet(); + sphere->center.x = x + (m.e03 >> FIXED_SHIFT); + sphere->center.y = y + (m.e13 >> FIXED_SHIFT); + sphere->center.z = z + (m.e23 >> FIXED_SHIFT); + sphere->radius = mesh->radius; + sphere++; + meshPtr++; + } + matrixPop(); + + node++; + } + + matrixPop(); + + return sphere - spheres; +} + +#include "lara.h" +#include "enemy.h" +#include "object.h" + +#ifdef __GBA__ +// ItemObj ctor is called on existing and pre-filled memory +#pragma GCC diagnostic ignored "-Wuninitialized" +#endif + +ItemObj::ItemObj(Room* room) +{ + angle.x = 0; + angle.z = 0; + vSpeed = 0; + hSpeed = 0; + nextItem = NULL; + nextActive = NULL; + animIndex = level.models[type].animIndex; + frameIndex = level.anims[animIndex].frameBegin; + state = uint8(level.anims[animIndex].state); + nextState = state; + goalState = state; + extra = NULL; + health = NOT_ENEMY; + hitMask = 0; + visibleMask = 0xFFFFFFFF; + + flags &= (ITEM_FLAG_ONCE | ITEM_FLAG_MASK); + + if ((type == ITEM_BRIDGE_FLAT) || + (type == ITEM_BRIDGE_TILT_1) || + (type == ITEM_BRIDGE_TILT_2) || + (type == ITEM_TRAP_FLOOR)) + { + // no collision + } else { + flags |= ITEM_FLAG_COLLISION; + } + + if (flags & ITEM_FLAG_ONCE) // once -> invisible + { + flags &= ~ITEM_FLAG_ONCE; + flags |= ITEM_FLAG_STATUS_INVISIBLE; + } + + if ((flags & ITEM_FLAG_MASK) == ITEM_FLAG_MASK) // full set of mask -> reverse + { + flags &= ~ITEM_FLAG_MASK; + flags |= ITEM_FLAG_REVERSE; + activate(); + } + + ASSERT(type <= ITEM_MAX); + + ASSERT(room); + + room->add(this); +} + +#ifdef __GBA__ +#pragma GCC diagnostic warning "-Wuninitialized" +#endif + +void ItemObj::update() +{ + // +} + +void ItemObj::draw() +{ + drawItem(this); +} + +struct ItemSave { + int16 x; + int16 y; + int16 z; + int16 ax; + int16 ay; + uint16 animIndex; + uint16 frameIndex; + uint16 flags; + uint16 timer; + uint8 state; + uint8 nextState; + uint8 goalState; + uint8 roomIndex; +}; + +uint8* ItemObj::save(uint8* data) +{ + ItemSave* sg = (ItemSave*)data; + + sg->x = pos.x - (room->info->x << 8); + sg->y = pos.y - (room->info->yTop); + sg->z = pos.z - (room->info->z << 8); + sg->ax = angle.x; + sg->ay = angle.y; + sg->animIndex = animIndex; + sg->frameIndex = frameIndex; + sg->flags = flags; + sg->timer = timer; + sg->state = state; + sg->nextState = nextState; + sg->goalState = goalState; + sg->roomIndex = room - rooms; + + return data + sizeof(ItemSave); +} + +uint8* ItemObj::load(uint8* data) +{ + ItemSave* sg = (ItemSave*)data; + + if (room) { + room->remove(this); + } + + room = rooms + sg->roomIndex; + room->add(this); + + pos.x = sg->x + (room->info->x << 8); + pos.y = sg->y + (room->info->yTop); + pos.z = sg->z + (room->info->z << 8); + angle.x = sg->ax; + angle.y = sg->ay; + animIndex = sg->animIndex; + frameIndex = sg->frameIndex; + flags = sg->flags; + timer = sg->timer; + state = sg->state; + nextState = sg->nextState; + goalState = sg->goalState; + + return data + sizeof(ItemSave); +} + +void ItemObj::collide(Lara* lara, CollisionInfo* cinfo) +{ + // empty +} + +uint32 ItemObj::collideSpheres(Lara* lara) const +{ +#ifdef FAST_HITMASK + if (type != ITEM_TRAP_SWING_BLADE) + return 0xFFFFFFFF; +#endif + + Sphere *a = gSpheres[0]; + Sphere *b = gSpheres[1]; + + int32 aCount = getSpheres(a, true); + int32 bCount = lara->getSpheres(b, true); + + uint32 mask = 0; + + for (int32 i = 0; i < aCount; i++) + { + if (a[i].radius <= 0) + continue; + + for (int32 j = 0; j < bCount; j++) + { + if (b[j].radius <= 0) + continue; + + vec3i d = b[j].center - a[i].center; + int32 r = b[j].radius + a[i].radius; + + if (X_SQR(d.x) + X_SQR(d.y) + X_SQR(d.z) < X_SQR(r)) + { + mask |= (1 << i); + } + } + } + + return mask; +} + +bool ItemObj::collideBounds(Lara* lara, CollisionInfo* cinfo) const +{ + const AABBs &a = getBoundingBox(false); + const AABBs &b = lara->getBoundingBox(false); + + int32 dy = lara->pos.y - pos.y; + + if ((a.maxY - b.minY <= dy) || + (a.minY - b.maxY >= dy)) + return false; + + int32 dx = lara->pos.x - pos.x; + int32 dz = lara->pos.z - pos.z; + + int32 s, c; + sincos(angle.y, s, c); + + int32 px = X_ROTX(dx, dz, s, c); + int32 pz = X_ROTY(dx, dz, s, c); + + int32 r = cinfo->radius; + + return (px >= a.minX - r) && + (px <= a.maxX + r) && + (pz >= a.minZ - r) && + (pz <= a.maxZ + r); +} + +void ItemObj::collidePush(Lara* lara, CollisionInfo* cinfo, bool enemyHit) const +{ + int32 dx = lara->pos.x - pos.x; + int32 dz = lara->pos.z - pos.z; + + int32 s, c; + sincos(angle.y, s, c); + + int32 px = X_ROTX(dx, dz, s, c); + int32 pz = X_ROTY(dx, dz, s, c); + + const AABBs &box = getBoundingBox(false); + int32 minX = box.minX - cinfo->radius; + int32 maxX = box.maxX + cinfo->radius; + int32 minZ = box.minZ - cinfo->radius; + int32 maxZ = box.maxZ + cinfo->radius; + + if ((px < minX) || (px > maxX) || (pz < minZ) || (pz > maxZ)) + return; + + enemyHit = enemyHit && cinfo->enemyHit && ((box.maxY - box.minY) > 256); + + int32 ax = px - minX; + int32 bx = maxX - px; + int32 az = pz - minZ; + int32 bz = maxZ - pz; + + if (ax <= bx && ax <= az && ax <= bz) { + px -= ax; + } else if (bx <= ax && bx <= az && bx <= bz) { + px += bx; + } else if (az <= ax && az <= bx && az <= bz) { + pz -= az; + } else { + pz += bz; + } + + s = -s; + + lara->pos.x = pos.x + X_ROTX(px, pz, s, c); + lara->pos.z = pos.z + X_ROTY(px, pz, s, c); + + if (enemyHit) + { + int32 cx = (minX + maxX) >> 1; + int32 cz = (minZ + maxZ) >> 1; + dx -= X_ROTX(cx, cz, s, c); + dz -= X_ROTY(cx, cz, s, c); + lara->animHit(dx, dz, 5); + } + + int32 tmpAngle = cinfo->angle; + + cinfo->gapPos = -WALL; + cinfo->gapNeg = -LARA_STEP_HEIGHT; + cinfo->gapCeiling = 0; + + cinfo->setAngle(phd_atan(lara->pos.z - cinfo->pos.z, lara->pos.x - cinfo->pos.x)); + + collideRoom(LARA_HEIGHT, 0); + + cinfo->setAngle(tmpAngle); + + if (cinfo->type != CT_NONE) { + lara->pos.x = cinfo->pos.x; + lara->pos.z = cinfo->pos.z; + } else { + cinfo->pos = lara->pos; + lara->updateRoom(-10); + } +} + +void ItemObj::collideRoom(int32 height, int32 yOffset) const +{ + cinfo.type = CT_NONE; + cinfo.offset = _vec3i(0, 0, 0); + + vec3i p = pos; + p.y += yOffset; + + int32 y = p.y - height; + + int32 cy = y - 160; + + int32 floor, ceiling; + + Room* nextRoom = room; + + #define CHECK_HEIGHT(v) {\ + nextRoom = nextRoom->getRoom(v.x, cy, v.z);\ + const Sector* sector = nextRoom->getSector(v.x, v.z);\ + floor = sector->getFloor(v.x, cy, v.z);\ + if (floor != WALL) floor -= p.y;\ + ceiling = sector->getCeiling(v.x, cy, v.z);\ + if (ceiling != WALL) ceiling -= y;\ + } + +// middle + CHECK_HEIGHT(p); + + cinfo.trigger = gLastFloorData; + cinfo.slantX = FD_SLANT_X(gLastFloorSlant); + cinfo.slantZ = FD_SLANT_Z(gLastFloorSlant); + + cinfo.setSide(CollisionInfo::ST_MIDDLE, floor, ceiling); + + vec3i f, l, r; + int32 R = cinfo.radius; + + int32 s, c; + sincos(cinfo.angle, s, c); + + switch (cinfo.quadrant) { + case 0 : { + f = _vec3i((R * s) >> FIXED_SHIFT, 0, R); + l = _vec3i(-R, 0, R); + r = _vec3i( R, 0, R); + break; + } + case 1 : { + f = _vec3i( R, 0, (R * c) >> FIXED_SHIFT); + l = _vec3i( R, 0, R); + r = _vec3i( R, 0, -R); + break; + } + case 2 : { + f = _vec3i((R * s) >> FIXED_SHIFT, 0, -R); + l = _vec3i( R, 0, -R); + r = _vec3i(-R, 0, -R); + break; + } + case 3 : { + f = _vec3i(-R, 0, (R * c) >> FIXED_SHIFT); + l = _vec3i(-R, 0, -R); + r = _vec3i(-R, 0, R); + break; + } + default : { + f.x = f.y = f.z = 0; + l.x = l.y = l.z = 0; + r.x = r.y = r.z = 0; + ASSERT(false); + } + } + + f += p; + l += p; + r += p; + + vec3i delta; + delta.x = cinfo.pos.x - p.x; + delta.y = cinfo.pos.y - p.y; + delta.z = cinfo.pos.z - p.z; + +// front + CHECK_HEIGHT(f); + cinfo.setSide(CollisionInfo::ST_FRONT, floor, ceiling); + +// left + CHECK_HEIGHT(l); + cinfo.setSide(CollisionInfo::ST_LEFT, floor, ceiling); + +// right + CHECK_HEIGHT(r); + cinfo.setSide(CollisionInfo::ST_RIGHT, floor, ceiling); + +// static objects + room->collideStatic(cinfo, p, height); + +// check middle + if (cinfo.m.floor == WALL) + { + cinfo.offset = delta; + cinfo.type = CT_FRONT; + return; + } + + if (cinfo.m.floor <= cinfo.m.ceiling) + { + cinfo.offset = delta; + cinfo.type = CT_FLOOR_CEILING; + return; + } + + if (cinfo.m.ceiling >= 0) + { + cinfo.offset.y = cinfo.m.ceiling; + cinfo.type = CT_CEILING; + } + +// front + if (cinfo.f.floor > cinfo.gapPos || + cinfo.f.floor < cinfo.gapNeg || + cinfo.f.ceiling > cinfo.gapCeiling) + { + if (cinfo.quadrant & 1) + { + cinfo.offset.x = alignOffset(f.x, p.x); + cinfo.offset.z = delta.z; + } else { + cinfo.offset.x = delta.x; + cinfo.offset.z = alignOffset(f.z, p.z); + } + + cinfo.type = CT_FRONT; + return; + } + +// front ceiling + if (cinfo.f.ceiling >= cinfo.gapCeiling) + { + cinfo.offset = delta; + cinfo.type = CT_FRONT_CEILING; + return; + } + +// left + if (cinfo.l.floor > cinfo.gapPos || cinfo.l.floor < cinfo.gapNeg) + { + if (cinfo.quadrant & 1) { + cinfo.offset.z = alignOffset(l.z, f.z); + } else { + cinfo.offset.x = alignOffset(l.x, f.x); + } + cinfo.type = CT_LEFT; + return; + } + +// right + if (cinfo.r.floor > cinfo.gapPos || cinfo.r.floor < cinfo.gapNeg) + { + if (cinfo.quadrant & 1) { + cinfo.offset.z = alignOffset(r.z, f.z); + } else { + cinfo.offset.x = alignOffset(r.x, f.x); + } + cinfo.type = CT_RIGHT; + return; + } +} + +uint32 ItemObj::updateHitMask(Lara* lara, CollisionInfo* cinfo) +{ + hitMask = 0; + + if (!collideBounds(lara, cinfo)) // check bound box intersection + return false; + + hitMask = collideSpheres(lara); // get hitMask = spheres collision mask + + return hitMask; +} + +ItemObj* ItemObj::init(Room* room) +{ + #define INIT_ITEM(type, className) case ITEM_##type : return new (this) className(room) + + switch (type) + { + INIT_ITEM( LARA , Lara ); + INIT_ITEM( DOPPELGANGER , Doppelganger ); + INIT_ITEM( WOLF , Wolf ); + INIT_ITEM( BEAR , Bear ); + INIT_ITEM( BAT , Bat ); + INIT_ITEM( CROCODILE_LAND , Crocodile ); + INIT_ITEM( CROCODILE_WATER , Crocodile ); + INIT_ITEM( LION_MALE , Lion ); + INIT_ITEM( LION_FEMALE , Lion ); + INIT_ITEM( PUMA , Lion ); + INIT_ITEM( GORILLA , Gorilla ); + INIT_ITEM( RAT_LAND , Rat ); + INIT_ITEM( RAT_WATER , Rat ); + INIT_ITEM( REX , Rex ); + INIT_ITEM( RAPTOR , Raptor ); + INIT_ITEM( MUTANT_1 , Mutant ); + INIT_ITEM( MUTANT_2 , Mutant ); + INIT_ITEM( MUTANT_3 , Mutant ); + INIT_ITEM( CENTAUR , Centaur ); + INIT_ITEM( MUMMY , Mummy ); + // INIT_ITEM( UNUSED_1 , ??? ); + // INIT_ITEM( UNUSED_2 , ??? ); + INIT_ITEM( LARSON , Larson ); + INIT_ITEM( PIERRE , Pierre ); + // INIT_ITEM( SKATEBOARD , ??? ); + INIT_ITEM( SKATER , Skater ); + INIT_ITEM( COWBOY , Cowboy ); + INIT_ITEM( MR_T , MrT ); + INIT_ITEM( NATLA , Natla ); + INIT_ITEM( ADAM , Adam ); + INIT_ITEM( TRAP_FLOOR , TrapFloor ); + INIT_ITEM( TRAP_SWING_BLADE , TrapSwingBlade ); + // INIT_ITEM( TRAP_SPIKES , ??? ); + // INIT_ITEM( TRAP_BOULDER , ??? ); + INIT_ITEM( DART , Dart ); + INIT_ITEM( TRAP_DART_EMITTER , TrapDartEmitter ); + // INIT_ITEM( DRAWBRIDGE , ??? ); + // INIT_ITEM( TRAP_SLAM , ??? ); + // INIT_ITEM( TRAP_SWORD , ??? ); + // INIT_ITEM( HAMMER_HANDLE , ??? ); + // INIT_ITEM( HAMMER_BLOCK , ??? ); + // INIT_ITEM( LIGHTNING , ??? ); + // INIT_ITEM( MOVING_OBJECT , ??? ); + INIT_ITEM( BLOCK_1 , Block ); + INIT_ITEM( BLOCK_2 , Block ); + INIT_ITEM( BLOCK_3 , Block ); + INIT_ITEM( BLOCK_4 , Block ); + // INIT_ITEM( MOVING_BLOCK , ??? ); + // INIT_ITEM( TRAP_CEILING , ??? ); + // INIT_ITEM( TRAP_FLOOR_LOD , ??? ); + INIT_ITEM( SWITCH , Switch ); + INIT_ITEM( SWITCH_WATER , SwitchWater ); + INIT_ITEM( DOOR_1 , Door ); + INIT_ITEM( DOOR_2 , Door ); + INIT_ITEM( DOOR_3 , Door ); + INIT_ITEM( DOOR_4 , Door ); + INIT_ITEM( DOOR_5 , Door ); + INIT_ITEM( DOOR_6 , Door ); + INIT_ITEM( DOOR_7 , Door ); + INIT_ITEM( DOOR_8 , Door ); + INIT_ITEM( TRAP_DOOR_1 , TrapDoor ); + INIT_ITEM( TRAP_DOOR_2 , TrapDoor ); + // INIT_ITEM( TRAP_DOOR_LOD , ??? ); + // INIT_ITEM( BRIDGE_FLAT , ??? ); + // INIT_ITEM( BRIDGE_TILT_1 , ??? ); + // INIT_ITEM( BRIDGE_TILT_2 , ??? ); + // INIT_ITEM( INV_PASSPORT , ??? ); + // INIT_ITEM( INV_COMPASS , ??? ); + // INIT_ITEM( INV_HOME , ??? ); + // INIT_ITEM( GEARS_1 , ??? ); + // INIT_ITEM( GEARS_2 , ??? ); + // INIT_ITEM( GEARS_3 , ??? ); + // INIT_ITEM( CUT_1 , ??? ); + // INIT_ITEM( CUT_2 , ??? ); + // INIT_ITEM( CUT_3 , ??? ); + // INIT_ITEM( CUT_4 , ??? ); + // INIT_ITEM( INV_PASSPORT_CLOSED , ??? ); + // INIT_ITEM( INV_MAP , ??? ); + INIT_ITEM( CRYSTAL , Crystal ); + INIT_ITEM( PISTOLS , Pickup ); + INIT_ITEM( SHOTGUN , Pickup ); + INIT_ITEM( MAGNUMS , Pickup ); + INIT_ITEM( UZIS , Pickup ); + INIT_ITEM( AMMO_PISTOLS , Pickup ); + INIT_ITEM( AMMO_SHOTGUN , Pickup ); + INIT_ITEM( AMMO_MAGNUMS , Pickup ); + INIT_ITEM( AMMO_UZIS , Pickup ); + INIT_ITEM( EXPLOSIVE , Pickup ); + INIT_ITEM( MEDIKIT_SMALL , Pickup ); + INIT_ITEM( MEDIKIT_BIG , Pickup ); + // INIT_ITEM( INV_DETAIL , ??? ); + // INIT_ITEM( INV_SOUND , ??? ); + // INIT_ITEM( INV_CONTROLS , ??? ); + // INIT_ITEM( INV_GAMMA , ??? ); + // INIT_ITEM( INV_PISTOLS , ??? ); + // INIT_ITEM( INV_SHOTGUN , ??? ); + // INIT_ITEM( INV_MAGNUMS , ??? ); + // INIT_ITEM( INV_UZIS , ??? ); + // INIT_ITEM( INV_AMMO_PISTOLS , ??? ); + // INIT_ITEM( INV_AMMO_SHOTGUN , ??? ); + // INIT_ITEM( INV_AMMO_MAGNUMS , ??? ); + // INIT_ITEM( INV_AMMO_UZIS , ??? ); + // INIT_ITEM( INV_EXPLOSIVE , ??? ); + // INIT_ITEM( INV_MEDIKIT_SMALL , ??? ); + // INIT_ITEM( INV_MEDIKIT_BIG , ??? ); + INIT_ITEM( PUZZLE_1 , Pickup ); + INIT_ITEM( PUZZLE_2 , Pickup ); + INIT_ITEM( PUZZLE_3 , Pickup ); + INIT_ITEM( PUZZLE_4 , Pickup ); + // INIT_ITEM( INV_PUZZLE_1 , ??? ); + // INIT_ITEM( INV_PUZZLE_2 , ??? ); + // INIT_ITEM( INV_PUZZLE_3 , ??? ); + // INIT_ITEM( INV_PUZZLE_4 , ??? ); + INIT_ITEM( PUZZLEHOLE_1 , PuzzleHole ); + INIT_ITEM( PUZZLEHOLE_2 , PuzzleHole ); + INIT_ITEM( PUZZLEHOLE_3 , PuzzleHole ); + INIT_ITEM( PUZZLEHOLE_4 , PuzzleHole ); + // INIT_ITEM( PUZZLE_DONE_1 , ??? ); + // INIT_ITEM( PUZZLE_DONE_2 , ??? ); + // INIT_ITEM( PUZZLE_DONE_3 , ??? ); + // INIT_ITEM( PUZZLE_DONE_4 , ??? ); + INIT_ITEM( LEADBAR , Pickup ); + // INIT_ITEM( INV_LEADBAR , ??? ); + // INIT_ITEM( MIDAS_HAND , ??? ); + INIT_ITEM( KEY_ITEM_1 , Pickup ); + INIT_ITEM( KEY_ITEM_2 , Pickup ); + INIT_ITEM( KEY_ITEM_3 , Pickup ); + INIT_ITEM( KEY_ITEM_4 , Pickup ); + // INIT_ITEM( INV_KEY_ITEM_1 , ??? ); + // INIT_ITEM( INV_KEY_ITEM_2 , ??? ); + // INIT_ITEM( INV_KEY_ITEM_3 , ??? ); + // INIT_ITEM( INV_KEY_ITEM_4 , ??? ); + INIT_ITEM( KEYHOLE_1 , KeyHole ); + INIT_ITEM( KEYHOLE_2 , KeyHole ); + INIT_ITEM( KEYHOLE_3 , KeyHole ); + INIT_ITEM( KEYHOLE_4 , KeyHole ); + // INIT_ITEM( UNUSED_4 , ??? ); + // INIT_ITEM( UNUSED_5 , ??? ); + // INIT_ITEM( SCION_PICKUP_QUALOPEC , ??? ); + INIT_ITEM( SCION_PICKUP_DROP , Pickup ); + INIT_ITEM( SCION_TARGET , ViewTarget ); + // INIT_ITEM( SCION_PICKUP_HOLDER , ??? ); + // INIT_ITEM( SCION_HOLDER , ??? ); + // INIT_ITEM( UNUSED_6 , ??? ); + // INIT_ITEM( UNUSED_7 , ??? ); + // INIT_ITEM( INV_SCION , ??? ); + // INIT_ITEM( EXPLOSION , ??? ); + // INIT_ITEM( UNUSED_8 , ??? ); + INIT_ITEM( SPLASH , SpriteEffect ); + // INIT_ITEM( UNUSED_9 , ??? ); + INIT_ITEM( BUBBLE , Bubble ); + // INIT_ITEM( UNUSED_10 , ??? ); + // INIT_ITEM( UNUSED_11 , ??? ); + INIT_ITEM( BLOOD , SpriteEffect ); + // INIT_ITEM( UNUSED_12 , ??? ); + INIT_ITEM( SMOKE , SpriteEffect ); + // INIT_ITEM( CENTAUR_STATUE , ??? ); + // INIT_ITEM( CABIN , ??? ); + // INIT_ITEM( MUTANT_EGG_SMALL , ??? ); + INIT_ITEM( RICOCHET , SpriteEffect ); + INIT_ITEM( SPARKLES , SpriteEffect ); + // INIT_ITEM( MUZZLE_FLASH , ??? ); + // INIT_ITEM( UNUSED_13 , ??? ); + // INIT_ITEM( UNUSED_14 , ??? ); + INIT_ITEM( VIEW_TARGET , ViewTarget ); + INIT_ITEM( WATERFALL , Waterfall ); + // INIT_ITEM( NATLA_BULLET , ??? ); + // INIT_ITEM( MUTANT_BULLET , ??? ); + // INIT_ITEM( CENTAUR_BULLET , ??? ); + // INIT_ITEM( UNUSED_15 , ??? ); + // INIT_ITEM( UNUSED_16 , ??? ); + // INIT_ITEM( LAVA_PARTICLE , ??? ); + INIT_ITEM( LAVA_EMITTER , LavaEmitter ); + // INIT_ITEM( FLAME , ??? ); + // INIT_ITEM( FLAME_EMITTER , ??? ); + // INIT_ITEM( TRAP_LAVA , ??? ); + // INIT_ITEM( MUTANT_EGG_BIG , ??? ); + // INIT_ITEM( BOAT , ??? ); + // INIT_ITEM( EARTHQUAKE , ??? ); + // INIT_ITEM( UNUSED_17 , ??? ); + // INIT_ITEM( UNUSED_18 , ??? ); + // INIT_ITEM( UNUSED_19 , ??? ); + // INIT_ITEM( UNUSED_20 , ??? ); + // INIT_ITEM( UNUSED_21 , ??? ); + // INIT_ITEM( LARA_BRAID , ??? ); + // INIT_ITEM( GLYPHS , ??? ); + } + + return new (this) ItemObj(room); +} + +#endif diff --git a/src/fixed/lang/en.h b/src/fixed/lang/en.h new file mode 100644 index 00000000..e9eeb6df --- /dev/null +++ b/src/fixed/lang/en.h @@ -0,0 +1,394 @@ +#ifndef H_LANG_EN +#define H_LANG_EN + +// Thanks: Nancy Charlton, Vague Rant + +const char* const STR_EN[STR_MAX] = { "" + , "Thanks for playing the" + , "OpenLara alpha demo!" + , "" + , "follow the project news:" + , "discord.gg/EF8JaQB" + , "t.me/openlara" + , "No SRAM or FRAM" + , "Saves will be lost" + , "after GBA reboot!" +// help + , "Loading..." + , "%s@@@" + "KILLS %d@@" + "PICKUPS %d@@" + "SECRETS %d of %d@@" + "TIME TAKEN %s" + , "Saving game..." + , "Saving done!" + , "SAVING ERROR!" + , "YES" + , "NO" + , "Off" + , "On" + , "OK" + , "Side-By-Side" + , "Anaglyph" + , "Split Screen" + , "VR" + , "Low" + , "Medium" + , "High" + , STR_LANGUAGES + , "Apply" + , "Gamepad 1" + , "Gamepad 2" + , "Gamepad 3" + , "Gamepad 4" + , "Not Ready" + , "Player 1" + , "Player 2" + , "Press Any Key" + , "%s - Select" + , "%s - Go Back" +// inventory pages + , "OPTIONS" + , "INVENTORY" + , "ITEMS" +// save game page + , "Save Game?" + , "Current Position" +// inventory option + , "Game" + , "Map" + , "Compass" + , "Statistics" + , "Lara's Home" + , "Graphics" //, "Detail Levels" + , "Sound" + , "Controls" + , "Gamma" +// passport menu + , "Load Game" + , "Save Game" + , "New Game" + , "Restart Level" + , "Exit to Title" + , "Exit Game" + , "Select Level" +// detail options + , "Select Detail" + , "Gamma" + , "Show FPS" + , "Filtering" + , "Lighting" + , "Shadows" + , "Water" + , "VSync" + , "Stereo" + , "Simple Items" + , "Resolution" + , STR_SCALE +// sound options + , "Set Volumes" + , "Reverberation" + , "Subtitles" + , "Language" + , "SFX" + , "Music" +// controls options + , "Set Controls" + , "Keyboard" + , "Gamepad" + , "Rumble" + , "Retargeting" + , "Multi-aiming" + , "Swap A B" + // controls + , "Run" + , "Back" + , "Right" + , "Left" + , "Walk" + , "Jump" + , "Action" + , "Weapon" + , "Look" + , "Roll" + , "Inventory" + , "Pause" + // control keys + , "Up" + , "Down" + , "Right" + , "Left" + , "A" + , "B" + , "L" + , "R" + , "Select" + , "Start" + , "L+R" + , "L+A" + , "L+B" +// inventory items + , "Unknown" + , "Explosive" + , "Pistols" + , "Shotgun" + , "Magnums" + , "Uzis" + , "Pistol Clips" + , "Shotgun Shells" + , "Magnum Clips" + , "Uzi Clips" + , "Small Medi Pack" + , "Large Medi Pack" + , "Lead Bar" + , "Scion" +// keys + , "Key" + , "Silver Key" + , "Rusty Key" + , "Gold Key" + , "Sapphire Key" + , "Neptune Key" + , "Atlas Key" + , "Damocles Key" + , "Thor Key" + , "Ornate Key" +// puzzles + , "Puzzle" + , "Gold Idol" + , "Gold Bar" + , "Machine Cog" + , "Fuse" + , "Ankh" + , "Eye of Horus" + , "Seal of Anubis" + , "Scarab" + , "Pyramid Key" +#ifdef USE_SUBTITLES +// TR1 subtitles + /* CAFE */ , + "[43500]What's a man gotta do to@get that kinda attention from ye?" + "[47500]It's hard to say exactly,@but you seem to be doing fine." + "[50000]Well, great. Though, truth is,@it ain't me that wants ye." + "[54500]No?" + "[55000]No. Miss Jacqueline Natla does,@from Natla Technologies." + "[59000]You know, creator of@all things bright and beautiful?" + "[64500]Seal it, Larson." + "[66000]Ma'am." + "[68000]Feast your eyes on this, Lara." + "[70500]How does that make your wallet rumble?" + "[73500]I'm sorry. I only play for sport." + "[76000]Then you'll like a big park." + "[78000]Peru. Vast mountain ranges to cover.@Sheer walls of ice. Rocky crags. Savage winds." + "[87500]And there's this little trinket:@an age old artefact of mystical powers" + "[92500]buried in the unfound tomb of Qualopec." + "[96000]That's my interest." + "[98000]You could leave tomorrow.@Are you busy tomorrow?" + /* LIFT */ , + "[49000]Relocated now to St. Francis' Folly, new temptations torment me." + "[53500]Rumour amongst my fellow brothers is that entombed@beneath our monastery lies the body of Tihocan," + "[60000]one of the three legendary rulers of@the lost continent, Atlantis," + "[64500]and that within lies his piece@of the Atlantean Scion." + "[68000]The pendant divided and shared between the three rulers@" + "[72500]which curbs tremendous powers.@Powers beyond the creator himself." + "[79000]My toes sweat at such possibilities@lying so close to my mortal self." + "[85500]Each night, I bid myself rid of these@fantasies, but it is indeed a test." + "[92000]" + "[93500]Pierre. Tsk. You litterbug." + /* CANYON */ , + "[13500]You just pulled the tough end of a wishbone." + "[16500]Howdy." + "[17500]Afternoon." + "[20000]Left Larson sucking wind then, eh?" + "[22500]If that is the phrase." + "[24000]Well, your little vacation riot's over now." + "[27000]Time to give back what you've hijacked off me." + "[30000]Let's try the lunch-box." + "[32000]" + "[42500]Well? Kill her!" + "[45000]Hey!" + "[48000]" + "[50500]You morons!" + "[53000]" + "[62500]Let's go." + "[65000]" + "[136000]What the heck was that?" + "[138000]What?" + "[138500]That-a-way." + "[140500]Probably just a fish." + "[142500]That's some fish, kid." + "[145000]Man, you have got to learn to chill.@I'm going back inside. You coming?" + "[152000]" + "[158000]Steady..." + "[160000]Here she goes." + "[161500]You ready yet?" + /* PRISON */ , + "[00001]You can't do this!" + "[01500]We condemn you, Natla of Atlantis, for your crimes." + "[06000]For the flagrant misuse of your powers@and for robbing us of our..." + "[11500]You can't! I..." + "[12500]Breaking the free bond of consent that our@people are ruled and secured under," + "[18500]and invading Tihocan and myself with our army." + "[23500]Our warriors emptied from our pyramid" + "[27000]so that you could use the pyramid - its powers@of creation - for your own mindless destruction." + "[33500]Mindless!? Look at you!" + "[35500]Neither of you have one squirt of@inventive juice in your heads." + "[40500]Wasters!" + "[41500]Let's just do it." + "[44000]Tihocan!" + "[45000]You used the sacramental place as a source@of individual pleasure," + "[49500]as some freak factory." + "[51000]They're survivalists. A new generation." + "[54000]A slaughter heap now." + "[56000]And you. We're going to lock you in limbo." + "[60000]Make your veins, heart, feet," + "[64000]and that diseased brain stick solid with frozen blood." + "[70000]Greet your eternal unrest, Natla." + "[73000]You won't rest either, or your@damned continent of Atlantis!" + /* 22 */ , + "[04000]Back again?" + "[05500]And you - for a grand re-opening, I assume." + "[09500]Evolution's in a rut - natural selection at an all time low..." + "[13500]shipping out fresh meat will incite territorial rages again" + "[17500] - will strengthen and advance us..." + "[20500]Even create new breeds." + "[22500]Kind of evolution on steroids, then." + "[24500]A kick in the pants...@those runts Qualopec and Tihocan had no idea" + "[29500] - the cataclysm of Atlantis struck a race of langouring wimps..." + "[33500]plummeted them to the very basics of survival again..." + "[37000]It shouldn't happen like that." + "[39000]Or like this." + "[40000]Hatching commences in 15 seconds." + "[43000]Too late for abortions now!" + "[45000]Not without the heart of the operation!" + "[47000]Noooo!" + "[50000]TEN" + "[54000]FIVE..." + "[55500]4...3...2..." + "[60000]ONE..." + /* 23 */ , + "[00001]Well, you have my total attention now" + "[02500]- I'm not quite sure if I've got yours, though." + "[05000]Hello?" + "[06000]I'll heel an' hide ye to a barn door yit." + "[09000]Of course." + "[10000]Ye and that drivelin' piece of the Scion." + "[13000]Ye want to keep it so bad, I'll harness it right up y..." + "[17000]Wait... we're talking about the artifact here?" + "[20000]Damn straight we are ... right up y ..." + "[22000]Hold on - I'm sorry" + "[24000]- this piece, you say - where's the rest?" + "[26500]Ms. Natla put Pierre Dupont on that trail." + "[29500]And where is that?" + "[30500]Hah. Ye ain't fast enough fer him." + "[34000]So you think all this talking is just holding me up?" + "[37000]I don't know where his little jackrabbit-frog-legs are runnin' him to." + "[42000]You'll have to ask Ms. Natla." + "[46000]" + "[51000]Thank you. I will." + /* 24 */ , "" + /* 25 */ , + "[03500]Here lies Tihocan" + "[05000]...one of the two just rulers of Atlantis..." + "[10000]Who even after the curse of the continent..." + "[13000]...had tried to keep rule here in these barren other-lands..." + "[19000]He died without child and his knowledge has no heritage..." + "[25500]Look over us kindly, Tihocan." + /* 26 */ , "Welcome to my home!@I'll take you on a guided tour." + /* 27 */ , "Use the directional keys to go into the music room." + /* 28 */ , "OK. Let's do some tumbling.@Press the jump button." + /* 29 */ , "Now press it again and quickly press one of@the directions and I'll jump that way." + /* 30 */ , "Ah, the main hall.@Sorry about the crates, I'm having some things put@ into storage and the delivery people haven't been yet." + /* 31 */ , "Run up to a crate, and while still pressing forward,@press action, and I'll vault up onto it." + /* 32 */ , "This used to be the ballroom, but I've@converted it into my own personal gym.@What do you think?@Well, let's do some exercises." + /* 33 */ , "I don't actually run everywhere.@When I want to be careful, I walk.@Hold down the walk button, and walk to the white line." + /* 34 */ , "With the walk button down, I won't fall off even if you try to make me.@Go on, try it." + /* 35 */ , "If you want look around, press and hold the look button.@Then press in the direction you want to look." + /* 36 */ , "If a jump is too far for me, I can grab the ledge and save myself@from a nasty fall. Walk to the edge with the white line until I@won't go any further. Then press jump, immediately followed by@forward and while I'm in the air, press and hold the action button." + /* 37 */ , "Press forward and I'll climb up." + /* 38 */ , "If I do a running jump, I can make a jump like that, no problem." + /* 39 */ , "Walk to the edge with the white line until I stop.@Then let go of walk and tap backwards to give me a run up.@Press forward, and almost immediately press and hold the jump button.@I won't actually jump until the last minute." + /* 40 */ , "Right. This is a really big one.@So do a running jump exactly as before except while I'm in the air@press and hold the action button to make me grab the ledge." + /* 41 */ , "Nice." + /* 42 */ , "Try to vault up here.@Press forward and hold action." + /* 43 */ , "I can't climb up because the gap is too small.@But press right and I'll shimmy sideways@until there is room, then press forward." + /* 44 */ , "Great!@If there is a long drop and I don't want to@hurt myself jumping off, I can let myself down carefully." + /* 45 */ , "Tap backwards, and I'll jump off backwards.@Immediately press and hold the action button,@and I'll grab the ledge on the way down." + /* 46 */ , "Then let go." + /* 47 */ , "Let's go for a swim." + /* 48 */ , "The jump button and the directions@move me around underwater." + /* 49 */ , "Ah! Air!@Just use forward and left and right@to manoeuvre around on the surface.@Press jump to dive down for another swim about.@Or go to the edge and press action to climb out." + /* 50 */ , "Right. Now I'd better take off these wet clothes." + /* 51 */ , "Say cheese!" + /* 52 */ , "Ain't nothin' personal." + /* 53 */ , "I still git a pain in my brain from ye.@An' it's tellin' me funny ideas now.@Like to shoot you to hell!" + /* 54 */ , "You can't bump off me and my brood so easy, Lara." + /* 55 */ , "A leetle late for ze prize giving - non?@Still, it is ze taking-part wheech counts." + /* 56 */ , "You firin' at me?@You firin' at me, huh?@Ain't nobody else here, you must be firin' at me!" +#endif +// TR1 levels + , "Lara's Home" + , "Caves" + , "City of Vilcabamba" + , "Lost Valley" + , "Tomb of Qualopec" + , "St. Francis' Folly" + , "Colosseum" + , "Palace Midas" + , "The Cistern" + , "Tomb of Tihocan" + , "City of Khamoon" + , "Obelisk of Khamoon" + , "Sanctuary of the Scion" + , "Natla's Mines" + , "Atlantis" + , "The Great Pyramid" + , "Return to Egypt" + , "Temple of the Cat" + , "Atlantean Stronghold" + , "The Hive" +// TR2 levels + , "Lara's Home" + , "The Great Wall" + , "Venice" + , "Bartoli's Hideout" + , "Opera House" + , "Offshore Rig" + , "Diving Area" + , "40 Fathoms" + , "Wreck of the Maria Doria" + , "Living Quarters" + , "The Deck" + , "Tibetan Foothills" + , "Barkhang Monastery" + , "Catacombs of the Talion" + , "Ice Palace" + , "Temple of Xian" + , "Floating Islands" + , "The Dragon's Lair" + , "Home Sweet Home" +// TR3 levels + , "Lara's House" + , "Jungle" + , "Temple Ruins" + , "The River Ganges" + , "Caves Of Kaliya" + , "Coastal Village" + , "Crash Site" + , "Madubu Gorge" + , "Temple Of Puna" + , "Thames Wharf" + , "Aldwych" + , "Lud's Gate" + , "City" + , "Nevada Desert" + , "High Security Compound" + , "Area 51" + , "Antarctica" + , "RX-Tech Mines" + , "Lost City Of Tinnos" + , "Meteorite Cavern" + , "All Hallows" +}; + +#endif \ No newline at end of file diff --git a/src/fixed/lara.h b/src/fixed/lara.h new file mode 100644 index 00000000..d33c8d5e --- /dev/null +++ b/src/fixed/lara.h @@ -0,0 +1,4144 @@ +#ifndef H_LARA +#define H_LARA + +#include "common.h" +#include "item.h" +#include "camera.h" +#include "inventory.h" + +#define LARA_STATES(E) \ + E( STATE_WALK ) \ + E( STATE_RUN ) \ + E( STATE_STOP ) \ + E( STATE_JUMP ) \ + E( STATE_POSE ) \ + E( STATE_BACK_FAST ) \ + E( STATE_TURN_RIGHT ) \ + E( STATE_TURN_LEFT ) \ + E( STATE_DEATH ) \ + E( STATE_FALL ) \ + E( STATE_HANG ) \ + E( STATE_REACH ) \ + E( STATE_SPLAT ) \ + E( STATE_UW_TREAD ) \ + E( STATE_LAND ) \ + E( STATE_COMPRESS ) \ + E( STATE_BACK ) \ + E( STATE_UW_SWIM ) \ + E( STATE_UW_GLIDE ) \ + E( STATE_HANG_UP ) \ + E( STATE_TURN_FAST ) \ + E( STATE_STEP_RIGHT ) \ + E( STATE_STEP_LEFT ) \ + E( STATE_ROLL_END ) \ + E( STATE_SLIDE ) \ + E( STATE_JUMP_BACK ) \ + E( STATE_JUMP_RIGHT ) \ + E( STATE_JUMP_LEFT ) \ + E( STATE_JUMP_UP ) \ + E( STATE_FALL_BACK ) \ + E( STATE_HANG_LEFT ) \ + E( STATE_HANG_RIGHT ) \ + E( STATE_SLIDE_BACK ) \ + E( STATE_SURF_TREAD ) \ + E( STATE_SURF_SWIM ) \ + E( STATE_UW_DIVE ) \ + E( STATE_BLOCK_PUSH ) \ + E( STATE_BLOCK_PULL ) \ + E( STATE_BLOCK_READY ) \ + E( STATE_PICKUP ) \ + E( STATE_SWITCH_DOWN ) \ + E( STATE_SWITCH_UP ) \ + E( STATE_USE_KEY ) \ + E( STATE_USE_PUZZLE ) \ + E( STATE_DEATH_UW ) \ + E( STATE_ROLL_START ) \ + E( STATE_SPECIAL ) \ + E( STATE_SURF_BACK ) \ + E( STATE_SURF_LEFT ) \ + E( STATE_SURF_RIGHT ) \ + E( STATE_USE_MIDAS ) \ + E( STATE_DEATH_MIDAS ) \ + E( STATE_SWAN_DIVE ) \ + E( STATE_FAST_DIVE ) \ + E( STATE_HANDSTAND ) \ + E( STATE_WATER_OUT ) \ + E( STATE_CLIMB_START ) \ + E( STATE_CLIMB_UP ) \ + E( STATE_CLIMB_LEFT ) \ + E( STATE_CLIMB_END ) \ + E( STATE_CLIMB_RIGHT ) \ + E( STATE_CLIMB_DOWN ) \ + E( STATE_UNUSED_1 ) \ + E( STATE_UNUSED_2 ) \ + E( STATE_UNUSED_3 ) \ + E( STATE_WADE ) \ + E( STATE_ROLL_UW ) \ + E( STATE_PICKUP_FLARE ) \ + E( STATE_ROLL_AIR ) \ + E( STATE_UNUSED_4 ) \ + E( STATE_ZIPLINE ) + +#define DECL_ENUM(v) v, +#define DECL_S_HANDLER(v) &Lara::s_##v, +#define DECL_C_HANDLER(v) &Lara::c_##v, +#define S_HANDLER(state) void s_##state() +#define C_HANDLER(state) void c_##state() + +#define LARA_HANG_SLANT 60 +#define LARA_HANG_OFFSET 724 +#define LARA_HEIGHT 762 +#define LARA_HEIGHT_JUMP 870 // LARA_HEIGHT + hands up +#define LARA_HEIGHT_UW 400 +#define LARA_HEIGHT_SURF 700 +#define LARA_RADIUS 100 +#define LARA_RADIUS_WATER 300 +#define LARA_RADIUS_CLIMB 220 +#define LARA_HEIGHT 762 +#define LARA_TURN_ACCEL (ANGLE(9) / 4) +#define LARA_TURN_JUMP ANGLE(3) +#define LARA_TURN_VERY_SLOW ANGLE(2) +#define LARA_TURN_SLOW ANGLE(4) +#define LARA_TURN_MED ANGLE(6) +#define LARA_TURN_FAST ANGLE(8) +#define LARA_TILT_ACCEL (ANGLE(3) / 2) +#define LARA_TILT_MAX ANGLE(11) +#define LARA_STEP_HEIGHT 384 +#define LARA_SMASH_HEIGHT 640 +#define LARA_FLOAT_UP_SPEED 5 +#define LARA_SWIM_FRICTION 6 +#define LARA_SWIM_ACCEL 8 +#define LARA_SWIM_SPEED_MIN 133 +#define LARA_SWIM_SPEED_MAX 200 +#define LARA_SWIM_TIMER 10 // 1/3 sec +#define LARA_SURF_FRICTION 4 +#define LARA_SURF_ACCEL 8 +#define LARA_SURF_SPEED_MAX 60 +#define LARA_DIVE_SPEED 80 +#define LARA_WADE_MIN_DEPTH 384 +#define LARA_WADE_MAX_DEPTH 730 +#define LARA_SWIM_MIN_DEPTH 512 + +enum { + JOINT_MASK_HIPS = 1 << JOINT_HIPS, + JOINT_MASK_LEG_L1 = 1 << JOINT_LEG_L1, + JOINT_MASK_LEG_L2 = 1 << JOINT_LEG_L2, + JOINT_MASK_LEG_L3 = 1 << JOINT_LEG_L3, + JOINT_MASK_LEG_R1 = 1 << JOINT_LEG_R1, + JOINT_MASK_LEG_R2 = 1 << JOINT_LEG_R2, + JOINT_MASK_LEG_R3 = 1 << JOINT_LEG_R3, + JOINT_MASK_TORSO = 1 << JOINT_TORSO, + JOINT_MASK_ARM_R1 = 1 << JOINT_ARM_R1, + JOINT_MASK_ARM_R2 = 1 << JOINT_ARM_R2, + JOINT_MASK_ARM_R3 = 1 << JOINT_ARM_R3, + JOINT_MASK_ARM_L1 = 1 << JOINT_ARM_L1, + JOINT_MASK_ARM_L2 = 1 << JOINT_ARM_L2, + JOINT_MASK_ARM_L3 = 1 << JOINT_ARM_L3, + JOINT_MASK_HEAD = 1 << JOINT_HEAD, + JOINT_MASK_ARM_L = JOINT_MASK_ARM_L1 | JOINT_MASK_ARM_L2 | JOINT_MASK_ARM_L3, + JOINT_MASK_ARM_R = JOINT_MASK_ARM_R1 | JOINT_MASK_ARM_R2 | JOINT_MASK_ARM_R3, + JOINT_MASK_LEG_L = JOINT_MASK_LEG_L1 | JOINT_MASK_LEG_L2 | JOINT_MASK_LEG_L3, + JOINT_MASK_LEG_R = JOINT_MASK_LEG_R1 | JOINT_MASK_LEG_R2 | JOINT_MASK_LEG_R3, + JOINT_MASK_UPPER = JOINT_MASK_TORSO | JOINT_MASK_ARM_L | JOINT_MASK_ARM_R, // without head + JOINT_MASK_LOWER = JOINT_MASK_HIPS | JOINT_MASK_LEG_L | JOINT_MASK_LEG_R, + JOINT_MASK_BRAID = JOINT_MASK_HEAD | JOINT_MASK_TORSO | JOINT_MASK_ARM_L1 | JOINT_MASK_ARM_L2 | JOINT_MASK_ARM_R1 | JOINT_MASK_ARM_R2 +}; + +EWRAM_DATA Lara* players[MAX_PLAYERS]; +EWRAM_DATA CollisionInfo cinfo; + +const WeaponParams weaponParams[WEAPON_MAX] = { + { // WEAPON_PISTOLS + ITEM_LARA_PISTOLS, // modelType + ITEM_LARA_PISTOLS, // animType + 1, // damage + ANGLE(8), // spread + 8192, // range + 650, // height + SND_PISTOLS_SHOT, // soundId + 9, // reloadTimer + 155, // flashOffset + 3, // flashTimer + 20, // flashIntensity + ANGLE(60), // aimX + ANGLE(60), // aimY + ANGLE(80), // armX + ANGLE(-60), // armMinY + ANGLE(170), // armMaxY + }, + { // WEAPON_MAGNUMS + ITEM_LARA_MAGNUMS, // modelType + ITEM_LARA_PISTOLS, // animType + 2, // damage + ANGLE(8), // spread + 8192, // range + 650, // height + SND_MAGNUMS_SHOT, // soundId + 9, // reloadTimer + 155, // flashOffset + 3, // flashTimer + 16, // flashIntensity + ANGLE(60), // aimX + ANGLE(60), // aimY + ANGLE(80), // armX + ANGLE(-60), // armMinY + ANGLE(170), // armMaxY + }, + { // WEAPON_UZIS + ITEM_LARA_UZIS, // modelType + ITEM_LARA_PISTOLS, // animType + 1, // damage + ANGLE(8), // spread + 8192, // range + 650, // height + SND_UZIS_SHOT, // soundId + 3, // reloadTimer + 180, // flashOffset + 2, // flashTimer + 10, // flashIntensity + ANGLE(60), // aimX + ANGLE(60), // aimY + ANGLE(80), // armX + ANGLE(-60), // armMinY + ANGLE(170), // armMaxY + }, + { // WEAPON_SHOTGUN + ITEM_LARA_SHOTGUN, // modelType + ITEM_LARA_SHOTGUN, // animType + 4, // damage + ANGLE(20), // spread + 8192, // range + 500, // height + SND_SHOTGUN_SHOT, // soundId + 26, // reloadTimer + 0, // flashOffset + 0, // flashTimer + 0, // flashIntensity + ANGLE(55), // aimX + ANGLE(60), // aimY + ANGLE(65), // armX + ANGLE(-80), // armMinY + ANGLE(80), // armMaxY + }, +}; + +struct Lara : ItemObj +{ + enum State { + LARA_STATES(DECL_ENUM) + X_MAX + }; + + enum { + ANIM_PISTOLS_AIM = 0, + ANIM_PISTOLS_PICK, + ANIM_PISTOLS_DRAW, + ANIM_PISTOLS_FIRE, + + ANIM_SHOTGUN_AIM = 0, + ANIM_SHOTGUN_DRAW, + ANIM_SHOTGUN_FIRE + }; + + enum { + ANIM_RUN = 0, + + ANIM_STAND_LEFT = 2, + ANIM_STAND_RIGHT = 3, + + ANIM_RUN_START = 6, + + ANIM_STAND = 11, + + ANIM_LANDING = 24, + + ANIM_CLIMB_JUMP = 26, + + ANIM_FALL_HANG = 28, + + ANIM_SMASH_JUMP = 32, + + ANIM_FALL_FORTH = 34, + + ANIM_BACK = 41, + ANIM_CLIMB_3 = 42, + + ANIM_CLIMB_2 = 50, + + ANIM_SMASH_RUN_LEFT = 53, + ANIM_SMASH_RUN_RIGHT = 54, + ANIM_RUN_ASCEND_LEFT = 55, + ANIM_RUN_ASCEND_RIGHT = 56, + ANIM_WALK_ASCEND_LEFT = 57, + ANIM_WALK_ASCEND_RIGHT = 58, + ANIM_WALK_DESCEND_RIGHT = 59, + ANIM_WALK_DESCEND_LEFT = 60, + ANIM_BACK_DESCEND_LEFT = 61, + ANIM_BACK_DESCEND_RIGHT = 62, + + ANIM_SLIDE_FORTH = 70, + + ANIM_UW_GLIDE = 87, + + ANIM_FALL_BACK = 93, + + ANIM_HANG = 96, + + ANIM_STAND_NORMAL = 103, + + ANIM_SLIDE_BACK = 104, + + ANIM_UNDERWATER = 108, + + ANIM_WATER_OUT = 111, + ANIM_WATER_FALL = 112, + ANIM_SURF = 114, + ANIM_SURF_SWIM = 116, + ANIM_SURF_DIVE = 119, + ANIM_BLOCK_READY = 120, + + ANIM_HIT_FRONT = 125, + ANIM_HIT_BACK = 126, + ANIM_HIT_LEFT = 127, + ANIM_HIT_RIGHT = 128, + + ANIM_DEATH_BOULDER = 139, + ANIM_SURF_BACK = 140, + + ANIM_SURF_LEFT = 143, + ANIM_SURF_RIGHT = 144, + + ANIM_STAND_ROLL_BEGIN = 146, + ANIM_STAND_ROLL_END = 147, + + ANIM_DEATH_SPIKES = 149, + ANIM_HANG_SWING = 150, + + ANIM_CLIMB_START = 164, + + ANIM_WADE_SWIM = 176, + ANIM_WADE = 177, + ANIM_WADE_RUN_LEFT = 178, + ANIM_WADE_RUN_RIGHT = 179, + ANIM_WADE_STAND = 186, + ANIM_WADE_ASCEND = 190, + ANIM_SURF_OUT = 191, + ANIM_SWIM_STAND = 192, + ANIM_SURF_STAND = 193, + + ANIM_SWITCH_BIG_DOWN = 195, + ANIM_SWITCH_BIG_UP = 196, + ANIM_PUSH_BUTTON = 197, + + ANIM_UW_ROLL = 203 + }; + + typedef void (Lara::*Handler)(); + + static const Handler sHandlers[X_MAX]; + static const Handler cHandlers[X_MAX]; + + void updateState() + { + (this->*sHandlers[state])(); + } + + void updateObjectsCollision() + { + if (health <= 0) + { + extraL->hitQuadrant = -1; + return; + } + + Room** adjRoom = room->getAdjRooms(); + while (*adjRoom) + { + ItemObj* item = (*adjRoom++)->firstItem; + + while (item) + { + if ((item->flags & ITEM_FLAG_STATUS) != ITEM_FLAG_STATUS_INVISIBLE) + { + if (item->flags & ITEM_FLAG_COLLISION) + { + vec3i d = pos - item->pos; + + if (abs(d.x) < 4096 && abs(d.y) < 4096 && abs(d.z) < 4096) + { + item->collide(this, &cinfo); + } + } + } + item = item->nextItem; + } + } + } + + void updateCollision() + { + #ifndef __NDS__ // TODO + updateObjectsCollision(); + #endif + + (this->*cHandlers[state])(); + + // control hit animation + if (extraL->hitTimer <= 0) + return; + + if (!extraL->hitFrame) { + soundPlay(SND_HIT, &pos); + } + + extraL->hitFrame++; + if (extraL->hitFrame > 34) { + extraL->hitFrame = 34; + } + + extraL->hitTimer--; + if (extraL->hitTimer == 0) + { + extraL->hitQuadrant = -1; + extraL->hitFrame = 0; + } + } + + void startScreaming() + { + soundPlay(SND_SCREAM, &pos); + } + + void stopScreaming() + { + soundStop(SND_SCREAM); + } + + void restore() + { + if (health > 0) + return; + + health = LARA_MAX_HEALTH; + oxygen = LARA_MAX_OXYGEN; + animSet(ROOM_FLAG_WATER(room->info->flags) ? Lara::ANIM_UNDERWATER : Lara::ANIM_STAND, true, 0); + } + +// common + bool alignAngle(int16 &angle, int16 threshold) + { + if (angle >= -threshold && angle <= threshold) { + angle = 0; + } else if (angle >= ANGLE_90 - threshold && angle <= ANGLE_90 + threshold) { + angle = ANGLE_90; + } else if (angle >= -ANGLE_90 - threshold && angle <= -ANGLE_90 + threshold) { + angle = -ANGLE_90; + } else if (angle >= -(ANGLE_180 + 1 + threshold) || angle <= (ANGLE_180 + 1 + threshold)) { + angle = ANGLE_180; + } + + return (angle & (ANGLE_90 - 1)) > 0; + } + + void alignWall(int32 radius) + { + int x = pos.x & ~1023; + int z = pos.z & ~1023; + + switch (angle.y) + { + case ANGLE_0 : pos.z = z + 1024 - radius; break; + case ANGLE_90 : pos.x = x + 1024 - radius; break; + case -ANGLE_90 : pos.x = x + radius; break; + case ANGLE_180 : pos.z = z + radius; break; + default : ASSERT(false); + } + } + + bool s_getFront(int16 rot) + { + rot += angle.y; + + int32 s, c; + sincos(rot, s, c); + + int32 x = pos.x + (s >> (FIXED_SHIFT - 8)); + int32 y = pos.y - LARA_HEIGHT; + int32 z = pos.z + (c >> (FIXED_SHIFT - 8)); + + Room* roomFront = room->getRoom(x, y, z); + const Sector* sector = roomFront->getSector(x, z); + int32 floor = sector->getFloor(x, y, z); + + if (floor != WALL) { + floor -= pos.y; + } + + return floor >= -LARA_STEP_HEIGHT; + } + + bool s_checkDeath(int32 deathState) + { + if (health <= 0) { + goalState = deathState; + return true; + } + return false; + } + +// state control + bool s_checkFront(int16 angleDelta, int32 radius) + { + CollisionInfo tmpInfo = cinfo; + int16 tmpAngle = extraL->moveAngle; + + c_angle(angleDelta); + cinfo.radius = radius; + cinfo.type = CT_NONE; + cinfo.gapPos = -WALL; + cinfo.gapNeg = -LARA_STEP_HEIGHT; + cinfo.stopOnSlant = true; + cinfo.gapCeiling = 0; + + if ((angleDelta == ANGLE_180) && ((input & IN_WALK) || (waterState == WATER_STATE_WADE))) { + cinfo.gapPos = LARA_STEP_HEIGHT; + cinfo.stopOnLava = true; + } + + collideRoom(LARA_HEIGHT, 0); + + bool collide = (cinfo.type == CT_FRONT) || (cinfo.type == CT_FRONT_CEILING); + + cinfo = tmpInfo; + extraL->moveAngle = tmpAngle; + + return !collide; + } + + void s_ignoreEnemy() + { + cinfo.enemyPush = false; + cinfo.enemyHit = false; + } + + void s_rotate(int32 maxSpeed, int32 tilt) + { + tilt *= LARA_TILT_ACCEL; + + if (input & IN_LEFT) { + turnSpeed = X_MAX(turnSpeed - LARA_TURN_ACCEL, -maxSpeed); + angle.z = X_MAX(angle.z - tilt, -LARA_TILT_MAX); + } else if (input & IN_RIGHT) { + turnSpeed = X_MIN(turnSpeed + LARA_TURN_ACCEL, maxSpeed); + angle.z = X_MIN(angle.z + tilt, LARA_TILT_MAX); + } + } + + bool s_checkFall() + { + if (vSpeed > 131) + { + if (state == STATE_SWAN_DIVE) { + goalState = STATE_FAST_DIVE; + } else { + goalState = STATE_FALL; + } + return true; + } + return false; + } + + void s_checkWalk(int32 stopState) + { + if ((input & IN_UP) && s_checkFront(ANGLE_0, LARA_RADIUS + 4)) { + if (input & IN_WALK) { + goalState = STATE_WALK; + } else { + goalState = STATE_RUN; + } + } else { + goalState = stopState; + } + } + + bool s_checkRoll() + { + if ((waterState != WATER_STATE_ABOVE) && (waterState != WATER_STATE_UNDER)) { + return false; + } + + bool roll = (input & (IN_UP | IN_DOWN)) == (IN_UP | IN_DOWN); + + if ((waterState == WATER_STATE_ABOVE) && roll) + { + if ((state == STATE_RUN) || (state == STATE_STOP)) + { + animSet(ANIM_STAND_ROLL_BEGIN, true, 2); + goalState = STATE_STOP; + return true; + } + } + + return false; + } + + void s_turnUW() + { + if (input & IN_UP) { + angle.x -= ANGLE(2); + } else if (input & IN_DOWN) { + angle.x += ANGLE(2); + } + + if (input & IN_LEFT) { + turnSpeed = X_MAX(turnSpeed - LARA_TURN_ACCEL, -LARA_TURN_MED); + angle.z -= LARA_TILT_ACCEL * 2; + } else if (input & IN_RIGHT) { + turnSpeed = X_MIN(turnSpeed + LARA_TURN_ACCEL, LARA_TURN_MED); + angle.z += LARA_TILT_ACCEL * 2; + } + } + + void s_dive() + { + animSet(ANIM_SURF_DIVE, true); + angle.x = ANGLE(-45); + vSpeed = LARA_DIVE_SPEED; + waterState = WATER_STATE_UNDER; + } + + bool s_checkLook() + { + if (input & IN_LOOK) { + extraL->camera.mode = CAMERA_MODE_LOOK; + return true; + } + + if (extraL->camera.mode == CAMERA_MODE_LOOK) { + extraL->camera.mode = CAMERA_MODE_FOLLOW; + } + + return false; + } + + S_HANDLER( STATE_WALK ) + { + if (s_checkDeath(STATE_STOP)) + return; + + s_rotate(LARA_TURN_SLOW, 0); + + s_checkWalk(STATE_STOP); + } + + S_HANDLER( STATE_RUN ) + { + if (s_checkDeath(STATE_DEATH)) + return; + + if (s_checkRoll()) + return; + + s_rotate(LARA_TURN_FAST, 1); + + if ((input & IN_JUMP) && !(flags & ITEM_FLAG_GRAVITY)) { + goalState = STATE_JUMP; + } else { + s_checkWalk(STATE_STOP); + } + } + + S_HANDLER( STATE_STOP ) + { + if (s_checkDeath(STATE_DEATH)) + return; + + if (s_checkRoll()) + return; + + goalState = STATE_STOP; + + if (s_checkLook()) + return; + + if (input & IN_WALK) { + if ((input & IN_LEFT) && s_checkFront(-ANGLE_90, LARA_RADIUS + 16)) { + goalState = STATE_STEP_LEFT; + } else if ((input & IN_RIGHT) && s_checkFront(ANGLE_90, LARA_RADIUS + 16)) { + goalState = STATE_STEP_RIGHT; + } + } else { + if (input & IN_LEFT) { + goalState = STATE_TURN_LEFT; + } else if (input & IN_RIGHT) { + goalState = STATE_TURN_RIGHT; + } + } + + if (input & IN_JUMP) { + goalState = STATE_COMPRESS; + } else if ((input & IN_UP) && s_checkFront(ANGLE_0, LARA_RADIUS + 4)) { + if (input & IN_WALK) { + s_STATE_WALK(); + } else { + s_STATE_RUN(); + } + } else if ((input & IN_DOWN) && s_checkFront(ANGLE_180, LARA_RADIUS + 4)) { + if (input & IN_WALK) { + s_STATE_BACK(); + } else { + goalState = STATE_BACK_FAST; + } + } + } + + + S_HANDLER( STATE_JUMP ) + { + if (goalState == STATE_SWAN_DIVE || + goalState == STATE_REACH) + { + goalState = STATE_JUMP; + } + + if (goalState != STATE_DEATH && + goalState != STATE_STOP && + goalState != STATE_RUN) + { + if (extraL->weaponState == WEAPON_STATE_FREE) + { + if (input & IN_ACTION) + { + goalState = STATE_REACH; + } + + if (input & IN_WALK) + { + goalState = STATE_SWAN_DIVE; + } + } + + s_checkRoll(); + s_checkFall(); + } + + s_rotate(LARA_TURN_JUMP, 0); + } + + S_HANDLER( STATE_POSE ) + { + // empty + } + + S_HANDLER( STATE_BACK_FAST ) + { + s_rotate(LARA_TURN_MED, 0); + goalState = STATE_STOP; + } + + S_HANDLER( STATE_TURN_RIGHT ) + { + if (s_checkDeath(STATE_STOP)) + return; + + if (input & IN_LOOK) + { + goalState = STATE_STOP; + return; + } + + turnSpeed += LARA_TURN_ACCEL; + + if ((turnSpeed > LARA_TURN_SLOW || extraL->weaponState == WEAPON_STATE_READY) && (waterState != WATER_STATE_WADE) && !(input & IN_WALK)) + { + goalState = STATE_TURN_FAST; + } + + if (goalState == state) { + turnSpeed = X_MIN(turnSpeed, LARA_TURN_SLOW); + } + + s_checkWalk((input & IN_RIGHT) ? goalState : STATE_STOP); + } + + S_HANDLER( STATE_TURN_LEFT ) + { + if (s_checkDeath(STATE_STOP)) + return; + + if (input & IN_LOOK) + { + goalState = STATE_STOP; + return; + } + + turnSpeed -= LARA_TURN_ACCEL; + + if ((turnSpeed < -LARA_TURN_SLOW || extraL->weaponState == WEAPON_STATE_READY) && (waterState != WATER_STATE_WADE) && !(input & IN_WALK)) + { + goalState = STATE_TURN_FAST; + } + + if (goalState == state) { + turnSpeed = X_MAX(turnSpeed, -LARA_TURN_SLOW); + } + + s_checkWalk((input & IN_LEFT) ? goalState : STATE_STOP); + } + + S_HANDLER( STATE_DEATH ) + { + s_ignoreEnemy(); + } + + S_HANDLER( STATE_FALL ) + { + hSpeed = (hSpeed * 95) / 100; + + if (vSpeed >= 154) { + startScreaming(); + } + } + + S_HANDLER( STATE_HANG ) + { + extraL->camera.targetAngle.x = ANGLE(-60); + + s_ignoreEnemy(); + if (input & IN_LEFT) { + goalState = STATE_HANG_LEFT; + } else if (input & IN_RIGHT) { + goalState = STATE_HANG_RIGHT; + } + } + + S_HANDLER( STATE_REACH ) + { + extraL->camera.targetAngle.y = ANGLE(85); + + s_checkFall(); + } + + S_HANDLER( STATE_SPLAT ) + { + // empty + } + + S_HANDLER( STATE_UW_TREAD ) + { + if (s_checkDeath(STATE_DEATH_UW)) + return; + + if (s_checkRoll()) + return; + + s_turnUW(); + + if (input & IN_JUMP) { + goalState = STATE_UW_SWIM; + } + + vSpeed = X_MAX(vSpeed - LARA_SWIM_FRICTION, 0); + } + + S_HANDLER( STATE_LAND ) + { + // empty + } + + S_HANDLER( STATE_COMPRESS ) + { + if ((input & IN_UP) && s_getFront(ANGLE_0)) { + goalState = STATE_JUMP; + } else if ((input & IN_LEFT) && s_getFront(-ANGLE_90)) { + goalState = STATE_JUMP_LEFT; + } else if ((input & IN_RIGHT) && s_getFront(ANGLE_90)) { + goalState = STATE_JUMP_RIGHT; + } else if ((input & IN_DOWN) && s_getFront(ANGLE_180)) { + goalState = STATE_JUMP_BACK; + } + s_checkFall(); + } + + S_HANDLER( STATE_BACK ) + { + if (s_checkDeath(STATE_STOP)) + return; + + if ((input & (IN_WALK | IN_DOWN)) != (IN_WALK | IN_DOWN)) { + goalState = STATE_STOP; + } else { + goalState = STATE_BACK; + } + + s_rotate(LARA_TURN_SLOW, 0); + } + + S_HANDLER( STATE_UW_SWIM ) + { + if (s_checkDeath(STATE_DEATH_UW)) + return; + + if (s_checkRoll()) + return; + + s_turnUW(); + + vSpeed = X_MIN(vSpeed + LARA_SWIM_ACCEL, LARA_SWIM_SPEED_MAX); + + if (!(input & IN_JUMP)) { + goalState = STATE_UW_GLIDE; + } + } + + S_HANDLER( STATE_UW_GLIDE ) + { + if (s_checkDeath(STATE_DEATH_UW)) + return; + + if (s_checkRoll()) + return; + + s_turnUW(); + + if (input & IN_JUMP) { + goalState = STATE_UW_SWIM; + } + + vSpeed = X_MAX(vSpeed - LARA_SWIM_FRICTION, 0); + + if (vSpeed <= LARA_SWIM_SPEED_MIN) { + goalState = STATE_UW_TREAD; + } + } + + S_HANDLER( STATE_HANG_UP ) + { + s_ignoreEnemy(); + } + + S_HANDLER( STATE_TURN_FAST ) + { + if (s_checkDeath(STATE_STOP)) + return; + + if (input & IN_LOOK) + { + goalState = STATE_STOP; + return; + } + + if (turnSpeed < 0) { + turnSpeed = -LARA_TURN_FAST; + if (!(input & IN_LEFT)) { + goalState = STATE_STOP; + } + } else { + turnSpeed = LARA_TURN_FAST; + if (!(input & IN_RIGHT)) { + goalState = STATE_STOP; + } + } + } + + S_HANDLER( STATE_STEP_RIGHT ) + { + if (s_checkDeath(STATE_STOP)) + return; + + if ((input & (IN_WALK | IN_RIGHT)) != (IN_WALK | IN_RIGHT)) + { + goalState = STATE_STOP; + } + } + + S_HANDLER( STATE_STEP_LEFT ) + { + if (s_checkDeath(STATE_STOP)) + return; + + if ((input & (IN_WALK | IN_LEFT)) != (IN_WALK | IN_LEFT)) + { + goalState = STATE_STOP; + } + } + + S_HANDLER( STATE_ROLL_END ) + { + // empty + } + + S_HANDLER( STATE_SLIDE ) + { + extraL->camera.targetAngle.x = ANGLE(-45); + + if (input & IN_JUMP) { + goalState = STATE_JUMP; + } + } + + S_HANDLER( STATE_JUMP_BACK ) + { + extraL->camera.targetAngle.y = ANGLE(135); + + if (s_checkFall()) + return; + + if (goalState == STATE_RUN) { + goalState = STATE_STOP; + } + } + + S_HANDLER( STATE_JUMP_RIGHT ) + { + s_checkFall(); + } + + S_HANDLER( STATE_JUMP_LEFT ) + { + s_checkFall(); + } + + S_HANDLER( STATE_JUMP_UP ) + { + s_checkFall(); + } + + S_HANDLER( STATE_FALL_BACK ) + { + s_checkFall(); + + if ((input & IN_ACTION) && (extraL->weaponState == WEAPON_STATE_FREE)) { + goalState = STATE_REACH; + } + } + + S_HANDLER( STATE_HANG_LEFT ) + { + extraL->camera.targetAngle.x = ANGLE(-60); + + s_ignoreEnemy(); + + if (!(input & IN_LEFT)) { + goalState = STATE_HANG; + } + } + + S_HANDLER( STATE_HANG_RIGHT ) + { + extraL->camera.targetAngle.x = ANGLE(-60); + + s_ignoreEnemy(); + + if (!(input & IN_RIGHT)) { + goalState = STATE_HANG; + } + } + + S_HANDLER( STATE_SLIDE_BACK ) + { + if (input & IN_JUMP) { + goalState = STATE_JUMP_BACK; + } + } + + S_HANDLER( STATE_SURF_TREAD ) + { + vSpeed = X_MAX(vSpeed - LARA_SURF_FRICTION, 0); + + if (s_checkDeath(STATE_DEATH_UW)) + return; + + if (s_checkLook()) + return; + + if (input & IN_LEFT) { + angle.y -= LARA_TURN_SLOW; + } else if (input & IN_RIGHT) { + angle.y += LARA_TURN_SLOW; + } + + if (input & IN_UP) { + goalState = STATE_SURF_SWIM; + } else if (input & IN_DOWN) { + goalState = STATE_SURF_BACK; + } else if ((input & (IN_WALK | IN_LEFT)) == (IN_WALK | IN_LEFT)) { + goalState = STATE_SURF_LEFT; + } else if ((input & (IN_WALK | IN_RIGHT)) == (IN_WALK | IN_RIGHT)) { + goalState = STATE_SURF_RIGHT; + } + + if (input & IN_JUMP) { + extraL->swimTimer++; + if (extraL->swimTimer == LARA_SWIM_TIMER) { + s_dive(); + } + } else { + extraL->swimTimer = 0; + } + } + + S_HANDLER( STATE_SURF_SWIM ) + { + if (s_checkDeath(STATE_DEATH_UW)) + return; + + extraL->swimTimer = 0; + + if (input & IN_LEFT) { + angle.y -= LARA_TURN_SLOW; + } else if (input & IN_RIGHT) { + angle.y += LARA_TURN_SLOW; + } + + if (!(input & IN_UP) || (input & IN_JUMP)) { + goalState = STATE_SURF_TREAD; + } + + vSpeed = X_MIN(vSpeed + LARA_SURF_ACCEL, LARA_SURF_SPEED_MAX); + } + + S_HANDLER( STATE_UW_DIVE ) + { + if (input & IN_UP) { + angle.x -= ANGLE_1; + } + } + + S_HANDLER( STATE_BLOCK_PUSH ) + { + extraL->camera.targetAngle.x = ANGLE(-25); + extraL->camera.targetAngle.y = ANGLE(35); + extraL->camera.center = true; + + s_ignoreEnemy(); + } + + S_HANDLER( STATE_BLOCK_PULL ) + { + extraL->camera.targetAngle.x = ANGLE(-25); + extraL->camera.targetAngle.y = ANGLE(35); + extraL->camera.center = true; + + s_ignoreEnemy(); + } + + S_HANDLER( STATE_BLOCK_READY ) + { + extraL->camera.targetAngle.y = ANGLE(75); + + s_ignoreEnemy(); + + if (!(input & IN_ACTION)) { + goalState = STATE_STOP; + } + } + + S_HANDLER( STATE_PICKUP ) + { + extraL->camera.targetAngle.x = ANGLE(-15); + extraL->camera.targetAngle.y = ANGLE(-130); + extraL->camera.targetDist = 1024; + + s_ignoreEnemy(); + } + + S_HANDLER( STATE_SWITCH_DOWN ) + { + extraL->camera.targetAngle.x = ANGLE(-25); + extraL->camera.targetAngle.y = ANGLE(80); + extraL->camera.targetDist = 1024; + + s_ignoreEnemy(); + } + + S_HANDLER( STATE_SWITCH_UP ) + { + extraL->camera.targetAngle.x = ANGLE(-25); + extraL->camera.targetAngle.y = ANGLE(80); + extraL->camera.targetDist = 1024; + + s_ignoreEnemy(); + } + + S_HANDLER( STATE_USE_KEY ) + { + extraL->camera.targetAngle.x = ANGLE(-25); + extraL->camera.targetAngle.y = ANGLE(-80); + extraL->camera.targetDist = 1024; + + s_ignoreEnemy(); + } + + S_HANDLER( STATE_USE_PUZZLE ) + { + extraL->camera.targetAngle.x = ANGLE(-25); + extraL->camera.targetAngle.y = ANGLE(-80); + extraL->camera.targetDist = 1024; + + s_ignoreEnemy(); + } + + S_HANDLER( STATE_DEATH_UW ) + { + vSpeed = X_MAX(vSpeed - LARA_SWIM_ACCEL, 0); + angle.x = angleDec(angle.x, ANGLE(2)); + } + + S_HANDLER( STATE_ROLL_START ) + { + // empty + } + + S_HANDLER( STATE_SPECIAL ) + { + extraL->camera.targetAngle.x = ANGLE(-25); + extraL->camera.targetAngle.y = ANGLE(170); + extraL->camera.center = true; + } + + S_HANDLER( STATE_SURF_BACK ) + { + if (s_checkDeath(STATE_DEATH_UW)) + return; + + extraL->swimTimer = 0; + + if (input & IN_LEFT) { + angle.y -= LARA_TURN_VERY_SLOW; + } else if (input & IN_RIGHT) { + angle.y += LARA_TURN_VERY_SLOW; + } + + if (!(input & IN_DOWN)) { + goalState = STATE_SURF_TREAD; + } + + vSpeed = X_MIN(vSpeed + LARA_SURF_ACCEL, LARA_SURF_SPEED_MAX); + } + + S_HANDLER( STATE_SURF_LEFT ) + { + if (s_checkDeath(STATE_DEATH_UW)) + return; + + extraL->swimTimer = 0; + + if ((input & (IN_WALK | IN_LEFT)) != (IN_WALK | IN_LEFT)) { + goalState = STATE_SURF_TREAD; + } + + vSpeed = X_MIN(vSpeed + LARA_SURF_ACCEL, LARA_SURF_SPEED_MAX); + } + + S_HANDLER( STATE_SURF_RIGHT ) + { + if (s_checkDeath(STATE_DEATH_UW)) + return; + + extraL->swimTimer = 0; + + if ((input & (IN_WALK | IN_RIGHT)) != (IN_WALK | IN_RIGHT)) { + goalState = STATE_SURF_TREAD; + } + + vSpeed = X_MIN(vSpeed + LARA_SURF_ACCEL, LARA_SURF_SPEED_MAX); + } + + S_HANDLER( STATE_USE_MIDAS ) + { + s_ignoreEnemy(); + } + + S_HANDLER( STATE_DEATH_MIDAS ) + { + s_ignoreEnemy(); + flags &= ~ITEM_FLAG_GRAVITY; + } + + S_HANDLER( STATE_SWAN_DIVE ) + { + cinfo.enemyPush = true; + cinfo.enemyHit = false; + + s_checkFall(); + } + + S_HANDLER( STATE_FAST_DIVE ) + { + cinfo.enemyPush = true; + cinfo.enemyHit = false; + hSpeed = (hSpeed * 95) / 100; + + s_checkRoll(); + } + + S_HANDLER( STATE_HANDSTAND ) + { + s_ignoreEnemy(); + } + + S_HANDLER( STATE_WATER_OUT ) + { + s_ignoreEnemy(); + extraL->camera.center = true; + } + + S_HANDLER( STATE_CLIMB_START ) {} + S_HANDLER( STATE_CLIMB_UP ) {} + S_HANDLER( STATE_CLIMB_LEFT ) {} + S_HANDLER( STATE_CLIMB_END ) {} + S_HANDLER( STATE_CLIMB_RIGHT ) {} + S_HANDLER( STATE_CLIMB_DOWN ) {} + S_HANDLER( STATE_UNUSED_1 ) {} + S_HANDLER( STATE_UNUSED_2 ) {} + S_HANDLER( STATE_UNUSED_3 ) {} + S_HANDLER( STATE_WADE ) {} + S_HANDLER( STATE_ROLL_UW ) {} + S_HANDLER( STATE_PICKUP_FLARE ) {} + S_HANDLER( STATE_ROLL_AIR ) {} + S_HANDLER( STATE_UNUSED_4 ) {} + S_HANDLER( STATE_ZIPLINE ) {} + + +// collision control + void c_applyOffset() + { + pos += cinfo.offset; + cinfo.offset = _vec3i(0, 0, 0); + } + + void c_angle(int16 angleDelta) + { + angleDelta += angle.y; + + extraL->moveAngle = angleDelta; + cinfo.setAngle(angleDelta); + } + + bool c_checkCeiling() + { + if (cinfo.type != CT_CEILING && cinfo.type != CT_FLOOR_CEILING) { + return false; + } + + animSet(ANIM_STAND, true); + goalState = state; + hSpeed = 0; + vSpeed = 0; + flags &= ~ITEM_FLAG_GRAVITY; + pos = cinfo.pos; + + return true; + } + + bool c_checkWall() + { + if (cinfo.type == CT_FRONT || cinfo.type == CT_FRONT_CEILING) + { + c_applyOffset(); + goalState = STATE_STOP; + hSpeed = 0; + flags &= ~ITEM_FLAG_GRAVITY; + return true; + } + + if (cinfo.type == CT_LEFT) { + c_applyOffset(); + angle.y += ANGLE(5); + angle.z = angleDec(angle.z, ANGLE(2)); + } else if (cinfo.type == CT_RIGHT) { + c_applyOffset(); + angle.y -= ANGLE(5); + angle.z = angleDec(angle.z, ANGLE(2)); + } + + return false; + } + + bool c_checkWallUW() + { + if (cinfo.type == CT_FRONT) { + if (angle.x > ANGLE(35)) { + angle.x += ANGLE(2); + } else if (angle.x < ANGLE(-35)) { + angle.x -= ANGLE(2); + } else { + vSpeed = 0; + } + } else if (cinfo.type == CT_CEILING) { + if (angle.x >= ANGLE(-45)) { + angle.x -= ANGLE(2); + } + } else if (cinfo.type == CT_FRONT_CEILING) { + vSpeed = 0; + } else if (cinfo.type == CT_LEFT) { + angle.y += ANGLE(5); + } else if (cinfo.type == CT_RIGHT) { + angle.y -= ANGLE(5); + } else if (cinfo.type == CT_FLOOR_CEILING) { + pos = cinfo.pos; + vSpeed = 0; + return true; + } + + if (cinfo.m.floor < 0) { + pos.y += cinfo.m.floor; + angle.x += ANGLE(2); + } + + int32 waterDepth = getWaterDepth(); + + if (waterDepth == WALL) { + vSpeed = 0; + pos = cinfo.pos; + } else if (waterDepth <= 512) { + waterState = WATER_STATE_WADE; + + animSet(ANIM_SWIM_STAND, true); + goalState = STATE_STOP; + + angle.x = 0; + angle.z = 0; + hSpeed = 0; + vSpeed = 0; + flags &= ~ITEM_FLAG_GRAVITY; + } + + return false; + } + + bool c_checkWallSurf() + { + if ((cinfo.m.floor < 0 && cinfo.m.slantType == SLANT_HIGH) || (cinfo.type & (CT_FRONT | CT_CEILING | CT_FRONT_CEILING | CT_FLOOR_CEILING))) { + pos = cinfo.pos; + vSpeed = 0; + } else if (cinfo.type == CT_LEFT) { + angle.y += ANGLE(5); + } else if (cinfo.type == CT_RIGHT) { + angle.y -= ANGLE(5); + } + + return true; + } + + bool c_checkSlide() + { + if (waterState == WATER_STATE_WADE) + return false; + + if (cinfo.m.slantType != SLANT_HIGH) + return false; + + c_applyOffset(); + + int16 realAngle; + + if (cinfo.slantX > 2) { + realAngle = -ANGLE_90; + } else if (cinfo.slantX < -2) { + realAngle = ANGLE_90; + } else if (cinfo.slantZ > 2) { + realAngle = ANGLE_180; + } else { + realAngle = 0; + } + + if (abs(realAngle - angle.y) <= ANGLE_90) { + if (state != STATE_SLIDE) { + animSet(ANIM_SLIDE_FORTH, true); + } + extraL->moveAngle = realAngle; + angle.y = realAngle; + } else { + if (state != STATE_SLIDE_BACK) { + animSet(ANIM_SLIDE_BACK, true); + } + extraL->moveAngle = realAngle; + angle.y = realAngle + ANGLE_180; + } + + return true; + } + + bool c_checkFall(int32 height, int32 fallAnimIndex = ANIM_FALL_FORTH) + { + if (waterState == WATER_STATE_WADE) + return false; + + if (cinfo.m.floor <= height) + return false; + + animSet(fallAnimIndex, true); + + vSpeed = 0; + flags |= ITEM_FLAG_GRAVITY; + + return true; + } + + bool c_checkLanding() + { + if ((state == STATE_FAST_DIVE || state == STATE_ROLL_AIR) && vSpeed > 133) + { + hit(LARA_MAX_HEALTH, pos, 0); + return true; + } + + int32 y = pos.y; + pos.y += cinfo.m.floor; + roomFloor = pos.y; + + checkTrigger(cinfo.trigger, this); + + pos.y = y; + + if (vSpeed <= 140) + return false; + + if (vSpeed > 154) { + hit(LARA_MAX_HEALTH, pos, 0); + } else { + hit((X_SQR(vSpeed - 140) * LARA_MAX_HEALTH) / 196, pos, 0); + } + + return health <= 0; + } + + bool c_checkSwing() + { + int32 x = pos.x; + int32 y = pos.y; + int32 z = pos.z; + + switch (angle.y) { + case ANGLE_0 : z += 256; break; + case ANGLE_90 : x += 256; break; + case -ANGLE_90 : x -= 256; break; + case ANGLE_180 : z -= 256; break; + } + + Room* roomBelow = room->getRoom(x, y, z); + const Sector* sector = roomBelow->getSector(x, z); + int32 floor = sector->getFloor(x, y, z); + + if (floor != WALL) + { + int32 ceiling = sector->getCeiling(x, y, z); + + floor -= y; + ceiling -= y; + + if (floor > 0 && ceiling < -400) + return true; + } + + return false; + } + + bool c_checkGrab() + { + return (extraL->weaponState != WEAPON_STATE_FREE) || !(input & IN_ACTION) || (cinfo.type != CT_FRONT) || (abs(cinfo.r.floor - cinfo.l.floor) >= LARA_HANG_SLANT); + } + + bool c_checkSpace() + { + return (cinfo.f.floor < cinfo.f.ceiling || + cinfo.l.floor < cinfo.l.ceiling || + cinfo.r.floor < cinfo.r.ceiling); + } + + bool c_checkClimbStart() + { + return false; + } + + bool c_checkClimbUp() + { + if (cinfo.f.floor == WALL) + return false; + + if (c_checkGrab()) + return false; + + int16 realAngle = angle.y; + if (alignAngle(realAngle, ANGLE(30))) + return false; + + if (cinfo.f.floor >= -640 && cinfo.f.floor <= -384) { + if (c_checkSpace()) + return false; + + setWeaponState(WEAPON_STATE_BUSY); + animSet(ANIM_CLIMB_2, true); + state = STATE_HANG_UP; + + pos.y += 512 + cinfo.f.floor; + } else if (cinfo.f.floor >= -896 && cinfo.f.floor <= -640) { + if (c_checkSpace()) + return false; + + setWeaponState(WEAPON_STATE_BUSY); + animSet(ANIM_CLIMB_3, true); + state = STATE_HANG_UP; + + pos.y += 768 + cinfo.f.floor; + } else if (cinfo.f.floor >= -1920 && cinfo.f.floor <= -896) { + animSet(ANIM_STAND, true); + goalState = STATE_JUMP_UP; + extraL->vSpeedHack = int32(phd_sqrt(-2 * GRAVITY * (cinfo.f.floor + 800)) + 3); + animProcess(); + /*} TODO climb + else if ((waterState != WATER_STATE_WADE) && (cinfo.f.floor <= -1920) && (cinfo.l.floor <= -1920) && (cinfo.r.floor <= -1920) && (cinfo.m.ceiling <= -1158)) { + animSet(ANIM_STAND, true); + goalState = STATE_JUMP_UP; + vSpeedHack = 116; + animProcess(); + } else if (((cinfo.f.floor < -1024) && (cinfo.f.ceiling >= 506)) || ((cinfo.m.ceiling <= -518) && c_checkClimbStart())) { + animSet(ANIM_STAND, true); + goalState = STATE_CLIMB_START; + animProcess();*/ + } else { + return false; + } + + angle.y = realAngle; + c_applyOffset(); + + return true; + } + + bool c_checkHang() + { + if (c_checkGrab()) + return false; + + if ((cinfo.f.ceiling > 0) || + (cinfo.m.ceiling > -LARA_STEP_HEIGHT) || + (cinfo.m.floor < 200 && state == STATE_REACH)) + { + return false; + } + + int32 h = cinfo.f.floor - getBoundingBox(true).minY; + int32 v = h + vSpeed; + + if ((h < 0 && v < 0) || (h > 0 && v > 0)) + return false; + + if (alignAngle(angle.y, ANGLE(35))) + return false; + + if (state == STATE_REACH) + { + if (c_checkSwing()) { + animSet(ANIM_HANG_SWING, true); + } else { + animSet(ANIM_HANG, true); + } + } else { + animSet(ANIM_HANG, true, 12); + } + + setWeaponState(WEAPON_STATE_BUSY); + cinfo.offset.y = cinfo.f.floor - getBoundingBox(true).minY; + + c_applyOffset(); + + flags &= ~ITEM_FLAG_GRAVITY; + hSpeed = 0; + vSpeed = 0; + + return true; + } + + bool c_checkDrop() + { + // TODO getTrigger here + + if ((health > 0) && (input & IN_ACTION)) + { + flags &= ~ITEM_FLAG_GRAVITY; + vSpeed = 0; + return false; + } + + flags |= ITEM_FLAG_GRAVITY; + hSpeed = 2; + vSpeed = 1; + + animSet(ANIM_FALL_FORTH, true); + + return true; + } + + bool c_checkWaterOut() + { + if (!(input & IN_ACTION) || + (cinfo.type != CT_FRONT) || + (cinfo.f.ceiling > 0) || + (cinfo.m.ceiling > -LARA_STEP_HEIGHT) || + (abs(cinfo.r.floor - cinfo.l.floor) >= LARA_HANG_SLANT)) + { + return false; + } + + int32 h = cinfo.f.floor + LARA_HEIGHT_SURF; + + if (h <= -512 || h > 316) + return false; + + if (alignAngle(angle.y, ANGLE(35))) + return false; + + pos.y += h - 5; + + updateRoom(-LARA_HEIGHT / 2); + + alignWall(-LARA_RADIUS); + + if ((h < -128)) { // TODO || (level->version & TR::VER_TR1)) { + animSet(ANIM_WATER_OUT, true); + // specular = LARA_WET_SPECULAR; + } else if (h < 128) { + animSet(ANIM_SURF_OUT, true); + } else { + animSet(ANIM_SURF_STAND, true); + } + + //game->waterDrop(pos, 128.0f, 0.2f); + + animSet(ANIM_WATER_OUT, true); + setWeaponState(WEAPON_STATE_BUSY); + + waterState = WATER_STATE_ABOVE; + goalState = STATE_STOP; + angle.x = 0; + angle.z = 0; + hSpeed = 0; + vSpeed = 0; + flags &= ~ITEM_FLAG_GRAVITY; + + return true; + } + + void c_default() + { + cinfo.gapPos = LARA_STEP_HEIGHT; + cinfo.gapNeg = -LARA_STEP_HEIGHT; + cinfo.gapCeiling = 0; + cinfo.stopOnSlant = true; + + collideRoom(LARA_HEIGHT, 0); + } + + void c_step() + { + cinfo.gapPos = (waterState == WATER_STATE_WADE) ? -WALL : 128; + cinfo.gapNeg = -128; + cinfo.gapCeiling = 0; + cinfo.stopOnSlant = true; + + collideRoom(LARA_HEIGHT, 0); + + if (c_checkCeiling()) + return; + + if (c_checkWall()) { + animSet(ANIM_STAND, true); + } + + if (c_checkSlide()) + return; + + pos.y += cinfo.m.floor; + } + + void c_fall() + { + if (vSpeed <= 0 || cinfo.m.floor > 0) + return; + + if (c_checkLanding()) { + goalState = STATE_DEATH; + } else if (state == STATE_JUMP && (input & IN_UP) && !(input & IN_WALK)) { + goalState = STATE_RUN; + } else if (state == STATE_FALL) { + animSet(ANIM_LANDING, true); + } else { + goalState = STATE_STOP; + } + + stopScreaming(); + + pos.y += cinfo.m.floor; + vSpeed = 0; + flags &= ~ITEM_FLAG_GRAVITY; + + if (state == STATE_JUMP) { + animProcess(); + } + } + + void c_jump() + { + cinfo.gapPos = -WALL; + cinfo.gapNeg = (state == STATE_REACH) ? 0 : -LARA_STEP_HEIGHT; + cinfo.gapCeiling = 192; + + collideRoom(state == STATE_JUMP_UP ? LARA_HEIGHT_JUMP : LARA_HEIGHT, 0); + + if ((state == STATE_REACH || state == STATE_JUMP_UP) && c_checkHang()) + return; + + c_applyOffset(); + + // TODO long up jump + // TODO can't side jump near walls + bool slide = (state == STATE_FALL) || (state == STATE_REACH) || (state == STATE_JUMP_UP); + + if ((cinfo.type == CT_CEILING) || (slide && (cinfo.type == CT_FRONT_CEILING))) { + if (vSpeed <= 0) { + vSpeed = 1; + } + } else if (!slide && ((cinfo.type == CT_FRONT) || (cinfo.type == CT_FRONT_CEILING))) { + osJoyVibrate(0, 0xFF, 0xFF); + animSet(ANIM_SMASH_JUMP, true, 1); + extraL->moveAngle += ANGLE_180; + hSpeed >>= 2; + if (vSpeed <= 0) { + vSpeed = 1; + } + } else if (cinfo.type == CT_FLOOR_CEILING) { + int32 s, c; + sincos(cinfo.angle, s, c); + pos.x -= (s * LARA_RADIUS) >> FIXED_SHIFT; + pos.z -= (c * LARA_RADIUS) >> FIXED_SHIFT; + cinfo.m.floor = 0; + hSpeed = 0; + if (vSpeed <= 0) { + vSpeed = 16; + } + } else if (cinfo.type == CT_LEFT) { + angle.y += ANGLE(5); + } else if (cinfo.type == CT_RIGHT) { + angle.y -= ANGLE(5); + } + + c_fall(); + } + + void c_slide() + { + cinfo.gapPos = -WALL; + cinfo.gapNeg = -512; + cinfo.gapCeiling = 0; + cinfo.stopOnSlant = true; + + collideRoom(LARA_HEIGHT, 0); + + if (c_checkCeiling()) + return; + + c_checkWall(); + + if (c_checkFall(200, state == STATE_SLIDE ? ANIM_FALL_FORTH : ANIM_FALL_BACK)) + return; + + c_checkSlide(); + + pos.y += cinfo.m.floor; + + if (cinfo.m.slantType != SLANT_HIGH) { + goalState = STATE_STOP; + } + } + + void c_roll() + { + vSpeed = 0; + flags &= ~ITEM_FLAG_GRAVITY; + + cinfo.gapPos = -WALL; + cinfo.gapNeg = -LARA_STEP_HEIGHT; + cinfo.gapCeiling = 0; + cinfo.stopOnSlant = true; + + collideRoom(LARA_HEIGHT, 0); + + if (c_checkCeiling()) + return; + + if (c_checkSlide()) + return; + + if (c_checkFall(200, state == STATE_ROLL_START ? ANIM_FALL_FORTH : ANIM_FALL_BACK)) + return; + + c_applyOffset(); + + pos.y += cinfo.m.floor; + } + + void c_hang(int32 angleDelta) + { + c_angle(angleDelta); + cinfo.gapPos = -WALL; + cinfo.gapNeg = WALL; + cinfo.gapCeiling = 0; + collideRoom(LARA_HEIGHT, 0); + + bool noFloor = cinfo.f.floor < 200; + + c_angle(ANGLE_0); + cinfo.gapPos = -WALL; + cinfo.gapNeg = -LARA_STEP_HEIGHT; + cinfo.gapCeiling = 0; + + switch (cinfo.quadrant) + { + case 0 : pos.z += 2; break; + case 1 : pos.x += 2; break; + case 2 : pos.z -= 2; break; + case 3 : pos.x -= 2; break; + } + + collideRoom(LARA_HEIGHT, 0); + + extraL->moveAngle = angle.y + angleDelta; + + if (health <= 0 || !(input & IN_ACTION)) + { + animSet(ANIM_FALL_HANG, true, 9); + + cinfo.offset.y = cinfo.f.floor - getBoundingBox(true).minY + 2; + c_applyOffset(); + + hSpeed = 2; + vSpeed = 1; + flags |= ITEM_FLAG_GRAVITY; + setWeaponState(WEAPON_STATE_FREE); + return; + } + + vSpeed = 0; + flags &= ~ITEM_FLAG_GRAVITY; + + if (noFloor || (cinfo.type != CT_FRONT) || (cinfo.m.ceiling >= 0) || abs(cinfo.r.floor - cinfo.l.floor) >= LARA_HANG_SLANT) + { + if (state != STATE_HANG) { + animSet(ANIM_HANG, true, 21); + } + pos = cinfo.pos; + return; + } + + if (cinfo.quadrant & 1) { + pos.x += cinfo.offset.x; + } else { + pos.z += cinfo.offset.z; + } + + int32 h = cinfo.f.floor - getBoundingBox(true).minY; + if (abs(h) <= 256) { + pos.y += h; + } + } + + enum ClimbState { + CLIMB_HANG, + CLIMB_COLLIDE, + CLIMB_OK + }; + + ClimbState c_climbCollide(int32 width) + { + /* TODO + int32 dx = 0, dz = 0; + + int32 x = pos.x; + int32 y = pos.y - 512; + int32 z = pos.z; + + switch (cinfo.quadrant) { + case 0 : + x += width; + z += cinfo.radius; + dz = 4; + break; + case 1 : + x += cinfo.radius; + z -= width; + dx = 4; + break; + case 2 : + x -= width; + z -= cinfo.radius; + dz = -4; + break; + case 3 : + x -= cinfo.radius; + z += width; + dx = -4; + break; + } + + // TODO + cinfo.offset.y = 0; + */ + return CLIMB_OK; + } + + void c_climbSide(int32 width) + { + if (c_checkDrop()) + return; + + switch (c_climbCollide(width)) + { + case CLIMB_HANG: + { + pos.x = cinfo.pos.x; + pos.z = cinfo.pos.z; + goalState = STATE_HANG; + break; + } + + case CLIMB_COLLIDE: + { + pos.x = cinfo.pos.x; + pos.z = cinfo.pos.z; + animSet(ANIM_CLIMB_START, true); + break; + } + + case CLIMB_OK: + { + if (input & IN_LEFT) { + goalState = STATE_CLIMB_LEFT; + } else if (input & IN_RIGHT) { + goalState = STATE_CLIMB_RIGHT; + } else { + goalState = STATE_CLIMB_START; + } + pos.y += cinfo.offset.y; + break; + } + } + } + + void c_swim() + { + c_angle(ANGLE_0); + + collideRoom(LARA_HEIGHT_UW, LARA_HEIGHT_UW / 2); + + c_applyOffset(); + + if (c_checkWallUW()) + return; + } + + void c_surf() + { + collideRoom(LARA_HEIGHT_SURF + 100, LARA_HEIGHT_SURF); + + c_applyOffset(); + + c_checkWallSurf(); + + if (state == STATE_SURF_TREAD) { + if (frameIndex == 0) { + //game->waterDrop(getJoint(jointHead).pos, 96.0f, 0.03f); + } + } else { + if (frameIndex % 4 == 0) { + //game->waterDrop(getJoint(jointHead).pos, 96.0f, 0.02f); + } + } + + int32 waterLevel = getWaterLevel(); + if (waterLevel - pos.y <= -100) { + s_dive(); + return; + } + + /* TODO + if (level->version & TR::VER_TR1) { + return; + } + + if ((cinfo.type == CT_FRONT) || (cinfo.m.slantType == TR::SLANT_HIGH) || (cinfo.m.floor >= 0)) { + return; + } + + if (cinfo.m.floor >= -128) { + if (targetState == STATE_SURF_LEFT) { + targetState = STATE_STEP_LEFT; + } else if (targetState == STATE_SURF_RIGHT) { + targetState = STATE_STEP_RIGHT; + } else { + setAnimV2(ANIM_WADE, true); + } + } else { + setAnimV2(ANIM_WADE_ASCEND, true); + targetState = STATE_STOP; + } + + v2pos.y += cinfo.f.floor + LARA_HEIGHT - 5; + updateRoomV2(-LARA_HEIGHT / 2); + + gravity = false; + v2rot.x = 0; + v2rot.z = 0; + hSpeed = 0; + vSpeed = 0; + waterState = WATER_STATE_WADE; + */ + } + + C_HANDLER( STATE_WALK ) + { + vSpeed = 0; + flags &= ~ITEM_FLAG_GRAVITY; + cinfo.stopOnLava = true; + + c_angle(ANGLE_0); + c_default(); + + if (c_checkCeiling()) + return; + + if (c_checkClimbUp()) + return; + + if (c_checkWall()) + { + if (frameIndex >= 29 && frameIndex <= 47) { + animSet(ANIM_STAND_RIGHT, false); + } else if ((frameIndex >= 22 && frameIndex <= 28) || (frameIndex >= 48 && frameIndex <= 57)) { + animSet(ANIM_STAND_LEFT, false); + } else { + animSet(ANIM_STAND, false); + } + } + + if (c_checkFall(LARA_STEP_HEIGHT)) + return; + + // descend + if (cinfo.m.floor > 128) + { + if (frameIndex >= 28 && frameIndex <= 45) { + animSet(ANIM_WALK_DESCEND_RIGHT, false); + } else { + animSet(ANIM_WALK_DESCEND_LEFT, false); + } + } + + // ascend + if (cinfo.m.floor >= -LARA_STEP_HEIGHT && cinfo.m.floor < -128) + { + if (frameIndex >= 27 && frameIndex <= 44) { + animSet(ANIM_WALK_ASCEND_RIGHT, false); + } else { + animSet(ANIM_WALK_ASCEND_LEFT, false); + } + } + + if (c_checkSlide()) + return; + + pos.y += cinfo.m.floor; + } + + C_HANDLER( STATE_RUN ) + { + c_angle(ANGLE_0); + + cinfo.gapPos = -WALL; + cinfo.gapNeg = -LARA_STEP_HEIGHT; + cinfo.gapCeiling = 0; + cinfo.stopOnSlant = true; + + collideRoom(LARA_HEIGHT, 0); + + if (c_checkCeiling()) + return; + + if (c_checkClimbUp()) + return; + + if (c_checkWall()) { + angle.z = 0; + + if (cinfo.f.slantType == SLANT_NONE && cinfo.f.floor < -LARA_SMASH_HEIGHT && frameIndex < 22) + { + animSet(frameIndex < 10 ? ANIM_SMASH_RUN_LEFT : ANIM_SMASH_RUN_RIGHT, false); + state = STATE_SPLAT; + return; + } + + animSet(ANIM_STAND, true); + } + + if (c_checkFall(LARA_STEP_HEIGHT)) + return; + + // ascend + if (cinfo.m.floor >= -LARA_STEP_HEIGHT && cinfo.m.floor < -128) + { + if (frameIndex >= 3 && frameIndex <= 14) { + animSet(ANIM_RUN_ASCEND_RIGHT, false); + } else { + animSet(ANIM_RUN_ASCEND_LEFT, false); + } + } + + if (c_checkSlide()) + return; + + if (cinfo.m.floor >= 50) + { + pos.y += 50; + return; + } + + pos.y += cinfo.m.floor; + } + + C_HANDLER( STATE_STOP ) + { + vSpeed = 0; + flags &= ~ITEM_FLAG_GRAVITY; + + c_angle(ANGLE_0); + + if (input == (IN_UP | IN_ACTION)) // to check front climb up from STOP state + { + int32 s, c; + sincos(cinfo.angle, s, c); + pos.x += (s * 4) >> FIXED_SHIFT; + pos.z += (c * 4) >> FIXED_SHIFT; + } + + c_default(); + + if (c_checkClimbUp()) + return; + + if (c_checkFall(100)) + return; + + if (c_checkSlide()) + return; + + c_applyOffset(); + + pos.y += cinfo.m.floor; + } + + C_HANDLER( STATE_JUMP ) + { + c_angle(ANGLE_0); + c_jump(); + } + + C_HANDLER( STATE_POSE ) + { + c_STATE_STOP(); + } + + C_HANDLER( STATE_BACK_FAST ) + { + vSpeed = 0; + flags &= ~ITEM_FLAG_GRAVITY; + + c_angle(ANGLE_180); + + cinfo.gapPos = -WALL; + cinfo.gapNeg = -LARA_STEP_HEIGHT; + cinfo.gapCeiling = 0; + cinfo.stopOnSlant = true; + + collideRoom(LARA_HEIGHT, 0); + + if (c_checkCeiling()) + return; + + if (c_checkFall(200, ANIM_FALL_BACK)) + return; + + if (c_checkWall()) { + animSet(ANIM_STAND, false); + } + + pos.y += cinfo.m.floor; + } + + C_HANDLER( STATE_TURN_RIGHT ) + { + c_angle(ANGLE_0); + c_default(); + + if (c_checkFall(100)) + return; + + if (c_checkSlide()) + return; + + pos.y += cinfo.m.floor; + } + + C_HANDLER( STATE_TURN_LEFT ) + { + c_STATE_TURN_RIGHT(); + } + + C_HANDLER( STATE_DEATH ) + { + cinfo.radius = LARA_RADIUS * 4; + + c_angle(ANGLE_0); + c_default(); + + c_applyOffset(); + pos.y += cinfo.m.floor; + } + + C_HANDLER( STATE_FALL ) + { + flags |= ITEM_FLAG_GRAVITY; + c_jump(); + } + + C_HANDLER( STATE_HANG ) + { + c_hang(0); + + if ((input & IN_UP) && goalState == STATE_HANG) + { + if (abs(cinfo.f.floor) >= 850) + return; + + if (c_checkSpace() || cinfo.staticHit) + return; + + if (input & IN_WALK) { + goalState = STATE_HANDSTAND; + } else { + goalState = STATE_HANG_UP; + } + } + } + + C_HANDLER( STATE_REACH ) + { + flags |= ITEM_FLAG_GRAVITY; + c_angle(ANGLE_0); + c_jump(); + } + + C_HANDLER( STATE_SPLAT ) + { + c_angle(ANGLE_0); + c_default(); + c_applyOffset(); + } + + C_HANDLER( STATE_UW_TREAD ) + { + c_swim(); + } + + C_HANDLER( STATE_LAND ) + { + c_STATE_STOP(); + } + + C_HANDLER( STATE_COMPRESS ) + { + vSpeed = 0; + flags &= ~ITEM_FLAG_GRAVITY; + + cinfo.gapPos = -WALL; + cinfo.gapNeg = WALL; + cinfo.gapCeiling = 0; + + collideRoom(LARA_HEIGHT, 0); + + if (cinfo.m.ceiling > -100) + { + animSet(ANIM_STAND, true); + pos = cinfo.pos; + hSpeed = 0; + vSpeed = 0; + } + } + + C_HANDLER( STATE_BACK ) + { + vSpeed = 0; + flags &= ~ITEM_FLAG_GRAVITY; + + c_angle(ANGLE_180); + + cinfo.gapPos = (waterState == WATER_STATE_WADE) ? -WALL : LARA_STEP_HEIGHT; + cinfo.gapNeg = -LARA_STEP_HEIGHT; + cinfo.gapCeiling = 0; + cinfo.stopOnSlant = true; + + collideRoom(LARA_HEIGHT, 0); + + if (c_checkCeiling()) + return; + + if (c_checkWall()) + { + animSet(ANIM_STAND, true); + } + + if (cinfo.m.floor > 128 && cinfo.m.floor < LARA_STEP_HEIGHT) + { + if (frameIndex < 568) { + animSet(ANIM_BACK_DESCEND_LEFT, false); + } else { + animSet(ANIM_BACK_DESCEND_RIGHT, false); + } + } + + if (c_checkSlide()) + return; + + pos.y += cinfo.m.floor; + } + + C_HANDLER( STATE_UW_SWIM ) + { + c_swim(); + } + + C_HANDLER( STATE_UW_GLIDE ) + { + c_swim(); + } + + C_HANDLER( STATE_HANG_UP ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( STATE_TURN_FAST ) + { + c_STATE_STOP(); + } + + C_HANDLER( STATE_STEP_RIGHT ) + { + c_angle(+ANGLE_90); + c_step(); + } + + C_HANDLER( STATE_STEP_LEFT ) + { + c_angle(-ANGLE_90); + c_step(); + } + + C_HANDLER( STATE_ROLL_END ) + { + c_angle(ANGLE_180); + c_roll(); + } + + C_HANDLER( STATE_SLIDE ) + { + c_angle(ANGLE_0); + c_slide(); + } + + C_HANDLER( STATE_JUMP_BACK ) + { + c_angle(ANGLE_180); + c_jump(); + } + + C_HANDLER( STATE_JUMP_RIGHT ) + { + c_angle(ANGLE_90); + c_jump(); + } + + C_HANDLER( STATE_JUMP_LEFT ) + { + c_angle(-ANGLE_90); + c_jump(); + } + + C_HANDLER( STATE_JUMP_UP ) + { + c_angle(ANGLE_0); + c_jump(); + } + + C_HANDLER( STATE_FALL_BACK ) + { + c_angle(ANGLE_180); + c_jump(); + } + + C_HANDLER( STATE_HANG_LEFT ) + { + c_hang(-ANGLE_90); + } + + C_HANDLER( STATE_HANG_RIGHT ) + { + c_hang(ANGLE_90); + } + + C_HANDLER( STATE_SLIDE_BACK ) + { + c_angle(ANGLE_180); + c_slide(); + } + + C_HANDLER( STATE_SURF_TREAD ) + { + c_angle(ANGLE_0); + c_surf(); + } + + C_HANDLER( STATE_SURF_SWIM ) + { + cinfo.gapNeg = -LARA_STEP_HEIGHT; + + c_angle(ANGLE_0); + c_surf(); + c_checkWaterOut(); + } + + C_HANDLER( STATE_UW_DIVE ) + { + c_swim(); + } + + C_HANDLER( STATE_BLOCK_PUSH ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( STATE_BLOCK_PULL ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( STATE_BLOCK_READY ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( STATE_PICKUP ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( STATE_SWITCH_DOWN ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( STATE_SWITCH_UP ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( STATE_USE_KEY ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( STATE_USE_PUZZLE ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( STATE_DEATH_UW ) + { + health = 0; + oxygen = 0; + + int16 waterLevel = getWaterLevel(); + if (waterLevel != WALL && waterLevel < pos.y - LARA_RADIUS) { + pos.y -= LARA_FLOAT_UP_SPEED; + } + + c_swim(); + } + + C_HANDLER( STATE_ROLL_START ) + { + c_angle(ANGLE_0); + c_roll(); + } + + C_HANDLER( STATE_SPECIAL ) + { + // empty + } + + C_HANDLER( STATE_SURF_BACK ) + { + c_angle(ANGLE_180); + c_surf(); + } + + C_HANDLER( STATE_SURF_LEFT ) + { + c_angle(-ANGLE_90); + c_surf(); + } + + C_HANDLER( STATE_SURF_RIGHT ) + { + c_angle(ANGLE_90); + c_surf(); + } + + C_HANDLER( STATE_USE_MIDAS ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( STATE_DEATH_MIDAS ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( STATE_SWAN_DIVE ) + { + c_angle(ANGLE_0); + c_jump(); + } + + C_HANDLER( STATE_FAST_DIVE ) + { + c_angle(ANGLE_0); + c_jump(); + } + + C_HANDLER( STATE_HANDSTAND ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( STATE_WATER_OUT ) + { + c_angle(ANGLE_0); + c_default(); + } + + C_HANDLER( STATE_CLIMB_START ) {} + C_HANDLER( STATE_CLIMB_UP ) {} + C_HANDLER( STATE_CLIMB_LEFT ) {} + C_HANDLER( STATE_CLIMB_END ) {} + C_HANDLER( STATE_CLIMB_RIGHT ) {} + C_HANDLER( STATE_CLIMB_DOWN ) {} + C_HANDLER( STATE_UNUSED_1 ) {} + C_HANDLER( STATE_UNUSED_2 ) {} + C_HANDLER( STATE_UNUSED_3 ) {} + C_HANDLER( STATE_WADE ) {} + C_HANDLER( STATE_ROLL_UW ) {} + C_HANDLER( STATE_PICKUP_FLARE ) {} + C_HANDLER( STATE_ROLL_AIR ) {} + C_HANDLER( STATE_UNUSED_4 ) {} + C_HANDLER( STATE_ZIPLINE ) {} + + Lara(Room* room) : ItemObj(room) + { + int32 playerIndex = -1; + + for (int32 i = 0; i < X_COUNT(players); i++) + { + if (players[i] == this) { + playerIndex = i; + break; + } + } + + ASSERT(playerIndex != -1); + + extraL = &playersExtra[playerIndex]; + memset(extraL, 0, sizeof(*extraL)); + extraL->hitQuadrant = -1; + + extraL->weapon = extraL->goalWeapon = WEAPON_PISTOLS; // TODO LEVEL10A + setWeaponState(WEAPON_STATE_FREE); + meshSwap(ITEM_LARA, 0xFFFFFFFF); + + bool isHome = gLevelID == LVL_TR1_GYM; + + if (isHome) { + meshSwap(ITEM_LARA_SPEC, JOINT_MASK_UPPER | JOINT_MASK_LOWER); + extraL->ammo[WEAPON_PISTOLS] = 0; + } else { + extraL->ammo[WEAPON_PISTOLS] = -1; + extraL->ammo[WEAPON_MAGNUMS] = -1; + extraL->ammo[WEAPON_UZIS] = -1; + extraL->ammo[WEAPON_SHOTGUN] = -1; + + if (extraL->weapon != WEAPON_MAX) + { + meshSwapPistols(JOINT_MASK_LEG_R1 | JOINT_MASK_LEG_L1, JOINT_MASK_ARM_R3 | JOINT_MASK_ARM_L3); + // TODO check if shotgun on back + meshSwapShotgun(false); + } + + //extraL->weapon = extraL->goalWeapon = WEAPON_SHOTGUN; + } + + animSet(ANIM_STAND, true, 0); + + health = LARA_MAX_HEALTH; + oxygen = LARA_MAX_OXYGEN; + flags |= ITEM_FLAG_SHADOW; + + extraL->camera.init(this); + extraL->healthTimer = 100; + } + +// update control + void updateInput() + { + extraL->lastInput = input; + + input = 0; + + #if defined(__3DO__) + if (keys & IK_A) input |= IN_JUMP; + if (keys & IK_B) input |= IN_ACTION; + if (keys & IK_C) input |= IN_WEAPON; + + if ((keys & (IK_L | IK_R)) == (IK_L | IK_R)) { + input |= IN_UP | IN_DOWN; + } else { + if (keys & IK_L) input |= IN_LOOK; + if (keys & IK_R) input |= IN_WALK; + } + #elif defined(__GBA__) || defined(_WIN32) + int32 ikA, ikB; + + if (gSettings.controls_swap) { + ikA = IK_B; + ikB = IK_A; + } else { + ikA = IK_A; + ikB = IK_B; + } + + if (keys & ikA) + { + if (keys & IK_L) { + if (extraL->weaponState != WEAPON_STATE_BUSY) { + input |= IN_WEAPON; + } else { + input |= IN_ACTION; + } + } else { + input |= IN_ACTION; + } + } + + if (keys & ikB) + { + if (keys & IK_L) { + input |= IN_UP | IN_DOWN; + } else { + input |= IN_JUMP; + } + } + + if (keys & IK_R) + { + if (keys & IK_L) { + input |= IN_LOOK; + } else { + input |= IN_WALK; + } + } + #elif defined(__NDS__) + if (keys & IK_A) input |= IN_UP | IN_DOWN; + if (keys & IK_B) input |= IN_ACTION; + if (keys & IK_X) input |= IN_WEAPON; + if (keys & IK_Y) input |= IN_JUMP; + if (keys & IK_L) input |= IN_LOOK; + if (keys & IK_R) input |= IN_WALK; + #endif + + if (keys & IK_LEFT) input |= IN_LEFT; + if (keys & IK_RIGHT) input |= IN_RIGHT; + if (keys & IK_UP) input |= IN_UP; + if (keys & IK_DOWN) input |= IN_DOWN; + if (keys & IK_SELECT) input |= IN_SELECT; + + if (extraL->camera.mode == CAMERA_MODE_FREE) { + input = 0; + } + + if (keys & IK_START) input |= IN_START; + + if (isKeyHit(IN_START)) + { + if (extraL->camera.mode != CAMERA_MODE_FREE) { + extraL->camera.mode = CAMERA_MODE_FREE; + } else { + extraL->camera.mode = CAMERA_MODE_FOLLOW; + } + } + } + + void updateLook() + { + ExtraInfoLara::Arm &R = extraL->armR; + ExtraInfoLara::Arm &L = extraL->armL; + vec3s &H = extraL->head.angle; + vec3s &T = extraL->torso.angle; + + if (health <= 0) { + H = T = _vec3s(0, 0, 0); + return; + } + + if (R.target || L.target) + { + if (extraL->weapon < WEAPON_SHOTGUN) + { + int32 aX = R.angle.x + L.angle.x; + int32 aY = R.angle.y + L.angle.y; + + if (R.aim && L.aim) { + H.x = T.x = aX >> 2; + H.y = T.y = aY >> 2; + } else { + H.x = T.x = aX >> 1; + H.y = T.y = aY >> 1; + } + } else { + T.x = R.angle.x; + T.y = R.angle.y; + H.x = H.y = 0; + } + return; + } + + if ((input & IN_LOOK) && extraL->camera.mode != CAMERA_MODE_FIXED) + { + extraL->camera.lookAtItem = NULL; + + if (input & IN_UP) { + H.x -= LARA_LOOK_TURN_SPEED; + } + + if (input & IN_DOWN) { + H.x += LARA_LOOK_TURN_SPEED; + } + + if (input & IN_LEFT) { + H.y -= LARA_LOOK_TURN_SPEED; + } + + if (input & IN_RIGHT) { + H.y += LARA_LOOK_TURN_SPEED; + } + + H.x = T.x = X_CLAMP(H.x, LARA_LOOK_ANGLE_MIN, LARA_LOOK_ANGLE_MAX); + H.y = T.y = X_CLAMP(H.y, -LARA_LOOK_ANGLE_Y, LARA_LOOK_ANGLE_Y); + + input &= ~(IN_RIGHT | IN_LEFT | IN_UP | IN_DOWN); + return; + } + + if (extraL->camera.lastItem != NULL) + return; + + H.x = T.x = angleDec(H.x, abs(H.x) >> 3); + H.y = T.y = angleDec(H.y, abs(H.y) >> 3); + } + + void updateWaterState() + { + int32 waterLevel = getWaterLevel(); + int32 waterDist = WALL; + + if (waterLevel != WALL) { + waterDist = pos.y - waterLevel; + } + + // change water state + switch (waterState) + { + case WATER_STATE_ABOVE: + { + if (waterDist == WALL || waterDist < LARA_WADE_MIN_DEPTH) { + break; + } + + int32 waterDepth = getWaterDepth(); + if (waterDepth > LARA_WADE_MAX_DEPTH - 256) + { + if (!ROOM_FLAG_WATER(room->info->flags)) // go dive + break; + + waterState = WATER_STATE_UNDER; + flags &= ~ITEM_FLAG_GRAVITY; + oxygen = LARA_MAX_OXYGEN; + + pos.y += 100; + updateRoom(0); + stopScreaming(); + + if (state == STATE_SWAN_DIVE) { + angle.x = ANGLE(-45); + goalState = STATE_UW_DIVE; + animProcess(); + vSpeed *= 2; + //game->waterDrop(pos, 128.0f, 0.2f); + } else if (state == STATE_FAST_DIVE) { + angle.x = ANGLE(-85); + goalState = STATE_UW_DIVE; + animProcess(); + vSpeed *= 2; + //game->waterDrop(pos, 128.0f, 0.2f); + } else { + angle.x = ANGLE(-45); + animSet(ANIM_WATER_FALL, true); + state = STATE_UW_DIVE; // TODO check necessary + goalState = STATE_UW_SWIM; + vSpeed = vSpeed * 3 / 2; + //game->waterDrop(pos, 256.0f, 0.2f); + } + + fxSplash(); + } else if (waterDist > LARA_WADE_MIN_DEPTH) { + waterState = WATER_STATE_WADE; + if (!(flags & ITEM_FLAG_GRAVITY)) { + goalState = STATE_STOP; + } + } + break; + } + + case WATER_STATE_SURFACE: + { + if (ROOM_FLAG_WATER(room->info->flags)) + break; + + if (waterDist > LARA_WADE_MIN_DEPTH) { + waterState = WATER_STATE_WADE; + animSet(ANIM_STAND_NORMAL, true); + goalState = STATE_WADE; + animProcess(); + } else { + waterState = WATER_STATE_ABOVE; + animSet(ANIM_FALL_FORTH, true); + hSpeed = vSpeed / 4; + flags |= ITEM_FLAG_GRAVITY; + } + + vSpeed = 0; + angle.x = angle.z = 0; + break; + } + + case WATER_STATE_UNDER: + { + if (ROOM_FLAG_WATER(room->info->flags) || extraL->dozy) + break; + + if ((getWaterDepth() != WALL) && abs(waterDist) < 256) { + waterState = WATER_STATE_SURFACE; + pos.y -= (waterDist - 1); + animSet(ANIM_SURF, true); + vSpeed = 0; + extraL->swimTimer = LARA_SWIM_TIMER + 1; // block dive before we press jump button again + updateRoom(-LARA_HEIGHT / 2); + //game->playSound(TR::SND_BREATH, pos, Sound::PAN | Sound::UNIQUE); + } else { + waterState = WATER_STATE_ABOVE; + animSet(ANIM_FALL_FORTH, true); + hSpeed = vSpeed / 4; + vSpeed = 0; + flags |= ITEM_FLAG_GRAVITY; + } + + angle.x = angle.z = 0; + break; + } + + case WATER_STATE_WADE: + { + if (waterDist < LARA_WADE_MIN_DEPTH) + { + waterState = WATER_STATE_ABOVE; + if (state == STATE_WADE) { + goalState = STATE_RUN; + } + } else if (waterDist > LARA_WADE_MAX_DEPTH) { + waterState = WATER_STATE_SURFACE; + pos.y -= (waterDist - 1); + + if (state == STATE_BACK) { + animSet(ANIM_SURF_BACK, true); + } else if (state == STATE_STEP_RIGHT) { + animSet(ANIM_SURF_RIGHT, true); + } else if (state == STATE_STEP_LEFT) { + animSet(ANIM_SURF_LEFT, true); + } else { + animSet(ANIM_SURF_SWIM, true); + } + + extraL->swimTimer = 0; + vSpeed = 0; + flags &= ~ITEM_FLAG_GRAVITY; + angle.x = angle.z = 0; + updateRoom(0); + } + break; + } + } + } + + void updateAbove() + { + cinfo.trigger = NULL; + cinfo.radius = LARA_RADIUS; + cinfo.pos = pos; + cinfo.enemyPush = true; + cinfo.enemyHit = true; + cinfo.stopOnSlant = false; + cinfo.stopOnLava = false; + + updateState(); + + angle.z = angleDec(angle.z, ANGLE(1)); + turnSpeed = angleDec(turnSpeed, ANGLE(2)); + angle.y += turnSpeed; + } + + void updateSurface() + { + cinfo.trigger = NULL; + cinfo.radius = LARA_RADIUS; + cinfo.gapPos = -WALL; + cinfo.gapNeg = -128; + cinfo.gapCeiling = 100; + cinfo.pos = pos; + cinfo.enemyPush = false; + cinfo.enemyHit = false; + cinfo.stopOnSlant = false; + cinfo.stopOnLava = false; + + updateState(); + + angle.z = angleDec(angle.z, ANGLE(2)); + + int32 s, c; + sincos(extraL->moveAngle, s, c); + + pos.x += (s * vSpeed) >> 16; + pos.z += (c * vSpeed) >> 16; + + extraL->camera.targetAngle.x = ANGLE(-22); + } + + void updateUnder() + { + cinfo.trigger = NULL; + cinfo.radius = LARA_RADIUS_WATER; + cinfo.gapPos = -WALL; + cinfo.gapNeg = -LARA_HEIGHT_UW; + cinfo.gapCeiling = LARA_HEIGHT_UW; + cinfo.pos = pos; + cinfo.enemyPush = false; + cinfo.enemyHit = false; + cinfo.stopOnSlant = false; + cinfo.stopOnLava = false; + + updateState(); + + angle.z = angleDec(angle.z, ANGLE(2)); + turnSpeed = angleDec(turnSpeed, ANGLE(2)); + angle.y += turnSpeed; + + angle.x = X_CLAMP(angle.x, ANGLE(-85), ANGLE(85)); + angle.z = X_CLAMP(angle.z, ANGLE(-22), ANGLE(22)); + + int32 sx, cx; + int32 sy, cy; + sincos(angle.x, sx, cx); + sincos(angle.y, sy, cy); + + pos.y -= (sx * vSpeed) >> 16; + pos.x += (cx * ((sy * vSpeed) >> 16)) >> FIXED_SHIFT; + pos.z += (cx * ((cy * vSpeed) >> 16)) >> FIXED_SHIFT; + } + + bool weaponFire(const ExtraInfoLara::Arm* arm) + { + int16 ammo = extraL->ammo[extraL->weapon]; + + if (!ammo) { + soundPlay(SND_EMPTY, &pos); + extraL->goalWeapon = WEAPON_PISTOLS; + return false; + } + + if (ammo > 0) { + ammo--; + } + + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + Location from; + from.pos.x = pos.x; + from.pos.y = pos.y - params.height; + from.pos.z = pos.z; + from.room = room; + + int32 count = (extraL->weapon == WEAPON_SHOTGUN) ? 6 : 1; + + for (int32 i = 0; i < count; i++) + { + int32 aimX = int32(rand_logic() - 0x4000) * params.spread >> 16; + int32 aimY = int32(rand_logic() - 0x4000) * params.spread >> 16; + + aimX += arm->angle.x; + aimY += arm->angle.y; + aimY += angle.y; + + matrixSetView(from.pos, aimX, aimY); + + int32 minDist = INT_MAX; + + if (arm->target && arm->target->health > 0) + { + Sphere* spheres = gSpheres[0]; + int32 spheresCount = arm->target->getSpheres(spheres, false); + + for (int32 i = 0; i < spheresCount; i++) + { + const Sphere &s = spheres[i]; + + if (abs(s.center.x) >= s.radius) + continue; + + if (abs(s.center.y) >= s.radius) + continue; + + if (s.center.z <= s.radius) + continue; + + if (fastLength(s.center.x, s.center.y) > s.radius) + continue; + + int32 dist = s.center.z - s.radius; + + if (dist < minDist) { + minDist = dist; + } + } + } + + vec3i dir = matrixGetDir(matrixGet()); + + Location to = from; + + if (minDist != INT_MAX) + { + dir *= minDist; + to.pos.x += dir.x >> FIXED_SHIFT; + to.pos.y += dir.y >> FIXED_SHIFT; + to.pos.z += dir.z >> FIXED_SHIFT; + + arm->target->hit(params.damage, to.pos, 0); + } else { + to.pos += dir; + + trace(from, to, true); + fxRicochet(to.room, to.pos, true); + } + } + + soundPlay(params.soundId, &pos); + + return true; + } + + void setWeaponState(WeaponState weaponState) + { + if (weaponState == extraL->weaponState) + return; + extraL->weaponState = weaponState; + + ExtraInfoLara::Arm &R = extraL->armR; + ExtraInfoLara::Arm &L = extraL->armL; + + if (weaponState == WEAPON_STATE_DRAW) + { + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + int32 anim = (extraL->weapon == WEAPON_SHOTGUN) ? ANIM_SHOTGUN_DRAW : ANIM_PISTOLS_PICK; + R.animIndex = L.animIndex = level.models[params.animType].animIndex + anim; + R.frameIndex = L.frameIndex = 0; + } + + if (weaponState == WEAPON_STATE_HOLSTER) + { + R.target = L.target = NULL; + } + + if (weaponState == WEAPON_STATE_FREE) + { + R.useBasis = L.useBasis = false; + R.animIndex = L.animIndex = 0; + R.frameIndex = L.frameIndex = 0; + #ifdef __3DO__ + extraL->goalWeapon = extraL->weapon = (extraL->weapon + 1) % WEAPON_MAX; + #endif + } + } + + void weaponAim(ExtraInfoLara::Arm &arm) + { + if (arm.aim) { + arm.angle.x = angleLerp(arm.angle.x, arm.angleAim.x, ANGLE(10)); + arm.angle.y = angleLerp(arm.angle.y, arm.angleAim.y, ANGLE(10)); + } else { + arm.angle.x = angleLerp(arm.angle.x, 0, ANGLE(10)); + arm.angle.y = angleLerp(arm.angle.y, 0, ANGLE(10)); + } + } + + void weaponDrawPistols() + { + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + ExtraInfoLara::Arm* arm = &extraL->armR; + + const Anim* animPtr = level.anims + arm->animIndex; + int32 animLength = animPtr->frameEnd - animPtr->frameBegin; + int32 frame = arm->frameIndex + 1; + int32 anim = arm->animIndex - level.models[params.animType].animIndex; + + if (frame > animLength) + { + anim++; + + if (anim == ANIM_PISTOLS_DRAW) { + meshSwapPistols(JOINT_MASK_ARM_R3 | JOINT_MASK_ARM_L3, JOINT_MASK_LEG_R1 | JOINT_MASK_LEG_L1); + soundPlay(SND_DRAW, &pos); + } else if (anim == ANIM_PISTOLS_FIRE) { + anim = ANIM_PISTOLS_AIM; + setWeaponState(WEAPON_STATE_READY); + } + + frame = 0; + } + + extraL->armR.angle = extraL->armL.angle = _vec3s(0, 0, 0); + extraL->armR.animIndex = extraL->armL.animIndex = anim + level.models[params.animType].animIndex; + extraL->armR.frameIndex = extraL->armL.frameIndex = frame; + } + + void weaponHolsterPistols() + { + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + for (int32 i = 0; i < LARA_ARM_MAX; i++) + { + ExtraInfoLara::Arm* arm = &extraL->armR + i; + + if (!arm->animIndex) + continue; + + int32 frame = arm->frameIndex; + int32 anim = arm->animIndex - level.models[params.animType].animIndex; + + if (frame) + { + if (anim == ANIM_PISTOLS_AIM) { + arm->angle.x -= arm->angle.x / frame; + arm->angle.y -= arm->angle.y / frame; + } + + if (anim == ANIM_PISTOLS_FIRE) { + frame = 0; + } else { + frame--; + } + } else { + if (anim == ANIM_PISTOLS_AIM) { + anim = ANIM_PISTOLS_DRAW; + } else if (anim == ANIM_PISTOLS_PICK) { + arm->animIndex = 0; + continue; + } else if (anim == ANIM_PISTOLS_DRAW) { + anim = ANIM_PISTOLS_PICK; + if (i == LARA_ARM_R) { + meshSwapPistols(JOINT_MASK_LEG_R1, JOINT_MASK_ARM_R3); + } else { + meshSwapPistols(JOINT_MASK_LEG_L1, JOINT_MASK_ARM_L3); + } + soundPlay(SND_HOLSTER, &pos); + } else if (anim == ANIM_PISTOLS_FIRE) { + anim = ANIM_PISTOLS_AIM; + } + + arm->animIndex = anim + level.models[params.animType].animIndex; + frame = level.anims[arm->animIndex].frameEnd - level.anims[arm->animIndex].frameBegin; + } + + arm->frameIndex = frame; + } + + if (!extraL->armR.animIndex && !extraL->armL.animIndex) { + setWeaponState(WEAPON_STATE_FREE); + } + } + + void weaponDrawShotgun() + { + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + ExtraInfoLara::Arm &arm = extraL->armR; + + const Anim* animPtr = level.anims + arm.animIndex; + int32 animLength = animPtr->frameEnd - animPtr->frameBegin; + int32 frame = arm.frameIndex + 1; + int32 anim = arm.animIndex - level.models[params.animType].animIndex; + + ASSERT(anim == ANIM_SHOTGUN_DRAW); + + if (frame == 10) { + meshSwapShotgun(true); + soundPlay(SND_DRAW, &pos); + } + + if (frame == animLength) { + setWeaponState(WEAPON_STATE_READY); + } + + extraL->armR.angle = extraL->armL.angle = _vec3s(0, 0, 0); + extraL->armR.animIndex = extraL->armL.animIndex = anim + level.models[params.animType].animIndex; + extraL->armR.frameIndex = extraL->armL.frameIndex = frame; + } + + void weaponHolsterShotgun() + { + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + ExtraInfoLara::Arm &arm = extraL->armR; + + int32 frame = arm.frameIndex; + int32 anim = arm.animIndex - level.models[params.animType].animIndex; + + if (anim == ANIM_SHOTGUN_AIM) { + if (frame == 0) { + anim = ANIM_SHOTGUN_DRAW; + const Anim* animPtr = level.anims + level.models[params.animType].animIndex + anim; + frame = animPtr->frameEnd - animPtr->frameBegin; + } else { + frame--; + } + } else if (anim == ANIM_SHOTGUN_FIRE) { + frame++; + if (frame > 12) { + anim = ANIM_SHOTGUN_DRAW; + const Anim* animPtr = level.anims + level.models[params.animType].animIndex + anim; + frame = animPtr->frameEnd - animPtr->frameBegin; + } + } else if (anim == ANIM_SHOTGUN_DRAW) { + if (frame == 0) { + setWeaponState(WEAPON_STATE_FREE); + return; + } else { + if (frame == 10) { + meshSwapShotgun(false); + soundPlay(SND_HOLSTER, &pos); + } + frame--; + } + } + + extraL->armR.angle = extraL->armL.angle = _vec3s(0, 0, 0); + extraL->armR.animIndex = extraL->armL.animIndex = anim + level.models[params.animType].animIndex; + extraL->armR.frameIndex = extraL->armL.frameIndex = frame; + } + + void weaponDraw() + { + switch (extraL->weapon) + { + case WEAPON_PISTOLS: + case WEAPON_MAGNUMS: + case WEAPON_UZIS: + weaponDrawPistols(); + break; + case WEAPON_SHOTGUN: + weaponDrawShotgun(); + break; + default: ASSERT(false); + } + } + + void weaponHolster() + { + meshSwap(ITEM_LARA, JOINT_MASK_HEAD); + + switch (extraL->weapon) + { + case WEAPON_PISTOLS: + case WEAPON_MAGNUMS: + case WEAPON_UZIS: + weaponHolsterPistols(); + break; + case WEAPON_SHOTGUN: + weaponHolsterShotgun(); + break; + default: ASSERT(false); + } + } + + void weaponUpdatePistols() + { + ExtraInfoLara::Arm &R = extraL->armR; + ExtraInfoLara::Arm &L = extraL->armL; + + weaponAim(R); + weaponAim(L); + + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + for (int32 i = 0; i < LARA_ARM_MAX; i++) + { + ExtraInfoLara::Arm* arm = &extraL->armR + i; + + const Anim* animPtr = level.anims + arm->animIndex; + int32 animLength = animPtr->frameEnd - animPtr->frameBegin; + int32 frame = arm->frameIndex; + int32 anim = arm->animIndex - level.models[params.animType].animIndex; + + if (((input & IN_ACTION) && !arm->target) || arm->aim) + { + if (anim == ANIM_PISTOLS_AIM) + { + if (frame == animLength) + { + if ((input & IN_ACTION) && weaponFire(arm)) + { + anim = ANIM_PISTOLS_FIRE; + frame = 0; + + arm->flash.timer = params.flashTimer; + arm->flash.angle = int16(rand_draw() << 1); + arm->flash.offset = params.flashOffset; + arm->flash.intensity = params.flashIntensity << 8; + } + } else { + frame++; + } + } else { // ANIM_DUAL_FIRE + frame++; + if (frame == params.reloadTimer) + { + anim = ANIM_PISTOLS_AIM; + const Anim* animPtr = level.anims + anim + level.models[params.animType].animIndex; + frame = animPtr->frameEnd - animPtr->frameBegin; + } + } + } else { + if (anim == ANIM_PISTOLS_FIRE) + { + anim = ANIM_PISTOLS_AIM; + const Anim* animPtr = level.anims + anim + level.models[params.animType].animIndex; + frame = animPtr->frameEnd - animPtr->frameBegin; + } else if (frame) { + frame--; + }; + } + + arm->animIndex = anim + level.models[params.animType].animIndex; + arm->frameIndex = frame; + arm->useBasis = (anim == ANIM_PISTOLS_AIM && frame) || (anim == ANIM_PISTOLS_FIRE); + } + } + + void weaponUpdateShotgun() + { + ExtraInfoLara::Arm &R = extraL->armR; + ExtraInfoLara::Arm &L = extraL->armL; + + weaponAim(R); + + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + ExtraInfoLara::Arm* arm = &extraL->armR; + + const Anim* animPtr = level.anims + arm->animIndex; + int32 animLength = animPtr->frameEnd - animPtr->frameBegin; + int32 frame = arm->frameIndex; + int32 anim = arm->animIndex - level.models[params.animType].animIndex; + + bool aim = ((input & IN_ACTION) && !arm->target) || arm->aim; + + switch (anim) + { + case ANIM_SHOTGUN_FIRE: + { + frame++; + if (frame == 10) { + soundPlay(SND_SHOTGUN_RELOAD, &pos); + } else if (frame == params.reloadTimer) { + anim = ANIM_SHOTGUN_AIM; + animPtr = level.anims + level.models[params.animType].animIndex + anim; + frame = animPtr->frameEnd - animPtr->frameBegin; + } else if ((animLength - frame < 10) && !aim) { + anim = ANIM_SHOTGUN_AIM; + frame = animLength - frame; // how many frames left for fire animation + animPtr = level.anims + level.models[params.animType].animIndex + anim; + frame = animPtr->frameEnd - animPtr->frameBegin - frame; // offset aim frames from the end + } + break; + } + case ANIM_SHOTGUN_DRAW: + { + if (aim) + { + anim = ANIM_SHOTGUN_AIM; + frame = 1; + } + break; + } + case ANIM_SHOTGUN_AIM: + { + if (aim) + { + if (frame == animLength) + { + if ((input & IN_ACTION) && weaponFire(arm)) + { + frame = 1; + anim = ANIM_SHOTGUN_FIRE; + } + } else { + frame++; + } + } else { + if (frame == 0) { + anim = ANIM_SHOTGUN_DRAW; + animPtr = level.anims + level.models[params.animType].animIndex + anim; + animLength = animPtr->frameEnd - animPtr->frameBegin; + frame = animLength; + } else { + frame--; + } + } + break; + } + } + + R.useBasis = L.useBasis = false; + R.animIndex = L.animIndex = anim + level.models[params.animType].animIndex; + R.frameIndex = L.frameIndex = frame; + } + + void weaponUpdateState() + { + bool change = false; + if (waterState == WATER_STATE_ABOVE || waterState == WATER_STATE_WADE) + { + if (extraL->weapon != extraL->goalWeapon) + { + if (extraL->weaponState == WEAPON_STATE_FREE) { + extraL->weapon = extraL->goalWeapon; + change = true; + } else if (extraL->weaponState == WEAPON_STATE_READY) { + change = true; + } + } else if (input & IN_WEAPON) { + change = true; + } + } else if (extraL->weaponState == WEAPON_STATE_READY) { + change = true; + } + + if (!change) + return; + + if (extraL->weaponState == WEAPON_STATE_FREE) + { + if (extraL->ammo[WEAPON_PISTOLS] != 0) + { + setWeaponState(WEAPON_STATE_DRAW); + } + } + + if (extraL->weaponState == WEAPON_STATE_READY) + { + setWeaponState(WEAPON_STATE_HOLSTER); + } + } + + void weaponGetAimPoint(ItemObj* target, Location &point) + { + const AABBs &box = target->getBoundingBox(false); + vec3i p; + p.x = (box.minX + box.maxX) >> 1; + p.y = box.minY + (box.maxY - box.minY) / 3; + p.z = (box.minZ + box.maxZ) >> 1; + int32 s, c; + sincos(target->angle.y, s, c); + X_ROTXY(p.x, p.z, -s, c); + + point.pos = target->pos + p; + point.room = target->room; + } + + void weaponTrackTargets() + { + ExtraInfoLara::Arm &arm = extraL->armR; + + if (arm.target && arm.target->health <= 0) + { + arm.target = NULL; + } + + if (!arm.target) + { + extraL->armR.aim = extraL->armL.aim = false; + return; + } + + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + Location from; + from.pos.x = pos.x; + from.pos.y = pos.y - params.height; + from.pos.z = pos.z; + from.room = room; + + Location to; + weaponGetAimPoint(arm.target, to); + + vec3i dir = to.pos - from.pos; + vec3s angleAim; + + anglesFromVector(dir.x, dir.y, dir.z, angleAim.x, angleAim.y); + + angleAim.x -= angle.x; + angleAim.y -= angle.y; + + if (trace(from, to, false)) + { + if (abs(angleAim.x) <= params.aimX && abs(angleAim.y) <= params.aimY) { + extraL->armR.aim = extraL->armL.aim = true; + } else { + extraL->armR.aim = extraL->armR.aim && (abs(angleAim.x) <= params.armX) && (angleAim.y >= params.armMinY) && (angleAim.y <= params.armMaxY); + extraL->armL.aim = extraL->armL.aim && (abs(angleAim.x) <= params.armX) && (angleAim.y >= -params.armMaxY) && (angleAim.y <= -params.armMinY); + } + } else { + extraL->armR.aim = extraL->armL.aim = false; + } + + extraL->armR.angleAim = extraL->armL.angleAim = angleAim; + } + + void weaponFindTargets() + { + if (!ItemObj::sFirstActive) + return; + + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + int32 range = params.range; + int32 rangeQ = X_SQR(range); + int32 minAimY = params.aimY; + + Location from; + from.pos.x = pos.x; + from.pos.y = pos.y - params.height; + from.pos.z = pos.z; + from.room = room; + + ItemObj* item = ItemObj::sFirstActive; + do + { + if (item->health <= 0) + continue; + + if ((item->flags & ITEM_FLAG_STATUS) != ITEM_FLAG_STATUS_ACTIVE) + continue; + + vec3i d = item->pos - pos; + int32 distQ = X_SQR(d.x) + X_SQR(d.y) + X_SQR(d.z); + + if (distQ > rangeQ) + continue; + + Location to; + weaponGetAimPoint(item, to); + + if (!trace(from, to, false)) + continue; + + vec3i dir = to.pos - from.pos; + vec3s angleAim; + + anglesFromVector(dir.x, dir.y, dir.z, angleAim.x, angleAim.y); + + angleAim.x -= angle.x + extraL->torso.angle.x; + angleAim.y -= angle.y + extraL->torso.angle.y; + + angleAim.x = abs(angleAim.x); + angleAim.y = abs(angleAim.y); + + if (angleAim.x > params.aimX || angleAim.y > params.aimY || angleAim.y > minAimY) + continue; + + minAimY = angleAim.y; + extraL->armR.target = item; + } while ((item = item->nextActive) != NULL); + } + + void weaponUpdateTargets() + { + if (input & IN_ACTION) { + meshSwap(ITEM_LARA_UZIS, JOINT_MASK_HEAD); + } else { + meshSwap(ITEM_LARA, JOINT_MASK_HEAD); + extraL->armR.target = NULL; + } + + if (extraL->armR.target == NULL) { + weaponFindTargets(); + } + + weaponTrackTargets(); + + extraL->armL.target = extraL->armR.target; + } + + void updateWeapon() + { + if (extraL->armR.flash.timer) { + extraL->armR.flash.timer--; + } + + if (extraL->armL.flash.timer) { + extraL->armL.flash.timer--; + } + + if (extraL->weapon == WEAPON_MAX) + return; + + if (health <= 0) + { + extraL->armR.animIndex = extraL->armL.animIndex = 0; + extraL->armR.useBasis = extraL->armL.useBasis = false; + return; + } + + weaponUpdateState(); + + switch (extraL->weaponState) + { + case WEAPON_STATE_DRAW: + { + extraL->camera.toCombat(); + weaponDraw(); + break; + } + + case WEAPON_STATE_HOLSTER: + { + weaponHolster(); + break; + } + + case WEAPON_STATE_READY: + { + extraL->camera.toCombat(); + weaponUpdateTargets(); + + if (extraL->weapon < WEAPON_SHOTGUN) { + weaponUpdatePistols(); + } else { + weaponUpdateShotgun(); + } + } + + default: ; + } + } + + void changeWeapon(Weapon weapon) + { + extraL->goalWeapon = weapon; + if ((extraL->weaponState == WEAPON_STATE_FREE) && (extraL->goalWeapon == extraL->weapon)) + { + extraL->weapon = WEAPON_NONE; + } + } + + bool useItem(InvSlot slot) + { + switch (slot) + { + case SLOT_PISTOLS: + changeWeapon(WEAPON_PISTOLS); + break; + case SLOT_SHOTGUN: + changeWeapon(WEAPON_SHOTGUN); + break; + case SLOT_MAGNUMS: + changeWeapon(WEAPON_MAGNUMS); + break; + case SLOT_UZIS: + changeWeapon(WEAPON_UZIS); + break; + case SLOT_MEDIKIT_BIG: + case SLOT_MEDIKIT_SMALL: + if (health < LARA_MAX_HEALTH) + { + health += (slot == SLOT_MEDIKIT_BIG) ? LARA_MAX_HEALTH : (LARA_MAX_HEALTH >> 1); + if (health > LARA_MAX_HEALTH) { + health = LARA_MAX_HEALTH; + } + inventory.remove(slot, 1); + extraL->healthTimer = 40; + soundPlay(SND_HEALTH, &pos); + } + break; + default: return false; + } + return true; + } + + virtual void hit(int32 damage, const vec3i &point, int32 soundId) + { + if (health <= 0 || damage <= 0) + return; + + osJoyVibrate(0, 0xFF, 0xFF); + extraL->healthTimer = 40; + health = X_MAX(0, health - damage); + } + + virtual void update() + { + vec3i oldPos = pos; + + updateInput(); + + if ((input & (IN_JUMP | IN_WEAPON)) == (IN_JUMP | IN_WEAPON)) + { + restore(); + } + + if (isKeyHit(IN_SELECT) && (gBrightness == 0)) + { + inventory.open(this, (health > 0) ? INV_PAGE_MAIN : INV_PAGE_DEATH); + } + + updateLook(); + + updateWaterState(); + + if (health > 0) + { + if (waterState == WATER_STATE_UNDER) + { + if (oxygen > 0) { + oxygen--; + } else { + hit(5, pos, 0); + } + } else { + oxygen = X_MIN(oxygen + 10, LARA_MAX_OXYGEN); + } + } + + switch (waterState) + { + case WATER_STATE_ABOVE : + case WATER_STATE_WADE : updateAbove(); break; + case WATER_STATE_SURFACE : updateSurface(); break; + case WATER_STATE_UNDER : updateUnder(); break; + } + + animProcess(); + + updateCollision(); + + int32 offset; + if (waterState == WATER_STATE_SURFACE) { + offset = LARA_RADIUS; + } else if (waterState == WATER_STATE_UNDER) { + offset = 0; + } else { + offset = -LARA_HEIGHT / 2; + } + updateRoom(offset); + + const Sector* sector = room->getSector(pos.x, pos.z); + bool badPos = (sector->floor == NO_FLOOR); + + //if (!badPos) { + // int32 h = pos.y - roomFloor; + // badPos = (h > cinfo.gapPos) || (h < cinfo.gapNeg); + //} + + if (badPos) + { + pos = oldPos; + updateRoom(offset); + } + + updateWeapon(); + + checkTrigger(cinfo.trigger, this); + + extraL->camera.update(); + + if (health > 0 && extraL->healthTimer > 0) { + extraL->healthTimer--; + } + } + + void meshSwap(ItemType type, uint32 mask) + { + int32 start = level.models[type].start; + + for (int32 i = 0; i < JOINT_MAX && mask; i++, mask >>= 1) + { + if (mask & 1) { + extraL->meshes[i] = start + i; + } + } + } + + void meshSwapPistols(uint32 weaponMask, uint32 bodyMask) + { + const WeaponParams ¶ms = weaponParams[extraL->weapon]; + + meshSwap(ITEM_LARA, bodyMask); + meshSwap(params.modelType, weaponMask); + } + + void meshSwapShotgun(bool armed) + { + const WeaponParams ¶ms = weaponParams[WEAPON_SHOTGUN]; + + if (armed) { + meshSwap(ITEM_LARA, JOINT_MASK_TORSO); + meshSwap(params.modelType, JOINT_MASK_ARM_R3 | JOINT_MASK_ARM_L3); + } else { + meshSwap(ITEM_LARA, JOINT_MASK_ARM_R3 | JOINT_MASK_ARM_L3); + meshSwap(params.modelType, JOINT_MASK_TORSO); + } + } + + virtual void draw() + { + int32 tmpAnimIndex = animIndex; + int32 tmpFrameIndex = frameIndex; + + if (extraL->hitQuadrant != -1) + { + switch (extraL->hitQuadrant) + { + case 0 : animIndex = ANIM_HIT_FRONT; break; + case 1 : animIndex = ANIM_HIT_LEFT; break; + case 2 : animIndex = ANIM_HIT_BACK; break; + case 3 : animIndex = ANIM_HIT_RIGHT; break; + default : ASSERT(false); + } + frameIndex = level.anims[animIndex].frameBegin + extraL->hitFrame; + } + + drawModel(this); + + animIndex = tmpAnimIndex; + frameIndex = tmpFrameIndex; + } + + struct LaraSave { + int16 vSpeed; + int16 hSpeed; + int16 health; // oxygen already saved as alias of ItemObj::timer + + uint8 weaponState; + uint8 weapon; + uint8 goalWeapon; + uint8 waterState; + + struct Arm { + uint16 animIndex; + uint16 frameIndex; + }; + + Arm armR; + Arm armL; + + uint8 cameraRoom; + uint8 cameraLastIndex; + + int16 cameraViewX; + int16 cameraViewY; + int16 cameraViewZ; + + uint16 meshes[JOINT_MAX]; + }; + + virtual uint8* save(uint8* data) + { + data = ItemObj::save(data); + + LaraSave* sg = (LaraSave*)data; + + sg->vSpeed = vSpeed; + sg->hSpeed = hSpeed; + sg->health = health; + sg->weaponState = extraL->weaponState; + sg->weapon = extraL->weapon; + sg->goalWeapon = extraL->goalWeapon; + sg->waterState = waterState; + + sg->armR.animIndex = extraL->armR.animIndex; + sg->armR.frameIndex = extraL->armR.frameIndex; + sg->armL.animIndex = extraL->armL.animIndex; + sg->armL.frameIndex = extraL->armL.frameIndex; + + const Room* camRoom = extraL->camera.view.room; + sg->cameraRoom = camRoom - rooms; + sg->cameraLastIndex = extraL->camera.lastIndex; + sg->cameraViewX = extraL->camera.view.pos.x - (camRoom->info->x << 8); + sg->cameraViewY = extraL->camera.view.pos.y - (camRoom->info->yTop); + sg->cameraViewZ = extraL->camera.view.pos.z - (camRoom->info->z << 8); + + ASSERT(sizeof(sg->meshes) == sizeof(extraL->meshes)); + memcpy(sg->meshes, extraL->meshes, sizeof(extraL->meshes)); + + return data + sizeof(LaraSave); + } + + virtual uint8* load(uint8* data) + { + data = ItemObj::load(data); + + LaraSave* sg = (LaraSave*)data; + + vSpeed = sg->vSpeed; + hSpeed = sg->hSpeed; + health = sg->health; + extraL->weaponState = sg->weaponState; + extraL->weapon = sg->weapon; + extraL->goalWeapon = sg->goalWeapon; + waterState = sg->waterState; + + extraL->armR.animIndex = sg->armR.animIndex; + extraL->armR.frameIndex = sg->armR.frameIndex; + extraL->armL.animIndex = sg->armL.animIndex; + extraL->armL.frameIndex = sg->armL.frameIndex; + + extraL->camera.init(this); + + Room* camRoom = rooms + sg->cameraRoom; + extraL->camera.view.room = camRoom; + extraL->camera.lastIndex = sg->cameraLastIndex; + extraL->camera.view.pos.x = sg->cameraViewX + (camRoom->info->x << 8); + extraL->camera.view.pos.y = sg->cameraViewY + (camRoom->info->yTop); + extraL->camera.view.pos.z = sg->cameraViewZ + (camRoom->info->z << 8); + + ASSERT(sizeof(sg->meshes) == sizeof(extraL->meshes)); + memcpy(extraL->meshes, sg->meshes, sizeof(extraL->meshes)); + + return data + sizeof(LaraSave); + } +}; + +const Lara::Handler Lara::sHandlers[X_MAX] = { LARA_STATES(DECL_S_HANDLER) }; +const Lara::Handler Lara::cHandlers[X_MAX] = { LARA_STATES(DECL_C_HANDLER) }; + +#undef DECL_ENUM +#undef DECL_S_HANDLER +#undef DECL_C_HANDLER +#undef S_HANDLER +#undef C_HANDLER + +int32 doTutorial(ItemObj* lara, int32 track) +{ + if (!lara) + return track; + + switch (track) + { + case 28 : + if ((gSaveGame.tracks[track] & TRACK_FLAG_ONCE) && lara->state == Lara::STATE_JUMP_UP) { + track = 29; + } + break; + + case 37 : + case 41 : + if (lara->state != Lara::STATE_HANG) { + track = 0; + } + break; + + case 42 : + if ((gSaveGame.tracks[track] & TRACK_FLAG_ONCE) && lara->state == Lara::STATE_HANG) { + track = 43; + } + break; + + case 49 : + if (lara->state != Lara::STATE_SURF_TREAD) { + track = 0; + } + break; + + case 50 : // end of GYM + if (gSaveGame.tracks[track] & TRACK_FLAG_ONCE) { + lara->gymTimer++; + if (lara->gymTimer > 90) + { + nextLevel(LVL_TR1_TITLE); + } + } else { + if (lara->state != Lara::STATE_WATER_OUT) + track = 0; + lara->gymTimer = 0; + } + break; + } + + return track; +} + +Lara* getLara(const vec3i &pos) +{ + return players[0]; // TODO find nearest player +} + +#endif diff --git a/src/fixed/level.h b/src/fixed/level.h new file mode 100644 index 00000000..528af50f --- /dev/null +++ b/src/fixed/level.h @@ -0,0 +1,227 @@ +#ifndef H_LEVEL +#define H_LEVEL + +#include "common.h" +#include "camera.h" + +Level level; + +#ifndef MODEHW +IWRAM_DATA uint8 gLightmap[256 * 32]; // IWRAM 8k +#endif + +EWRAM_DATA ItemObj items[MAX_ITEMS]; + +#ifdef ROM_READ +EWRAM_DATA Texture textures[MAX_TEXTURES]; // animated textures require memory swap +EWRAM_DATA Sprite sprites[MAX_SPRITES]; +EWRAM_DATA FixedCamera cameras[MAX_CAMERAS]; +EWRAM_DATA Box boxes[MAX_BOXES]; +#endif + +EWRAM_DATA Room rooms[MAX_ROOMS]; +EWRAM_DATA Model models[MAX_MODELS]; +EWRAM_DATA const Mesh* meshes[MAX_MESHES]; +EWRAM_DATA StaticMesh staticMeshes[MAX_STATIC_MESHES]; + +EWRAM_DATA ItemObj* ItemObj::sFirstActive; +EWRAM_DATA ItemObj* ItemObj::sFirstFree; + +EWRAM_DATA int32 gBrightness; + +void readLevel_GBA(const uint8* data) +{ + memcpy(&level, data, sizeof(level)); + + { // fix level data offsets + uint32* ptr = (uint32*)&level.palette; + while (ptr <= (uint32*)&level.soundOffsets) + { + *ptr++ += (uint32)data; + } + } + + { // prepare rooms + for (int32 i = 0; i < level.roomsCount; i++) + { + Room* room = rooms + i; + room->info = level.roomsInfo + i; + room->data = room->info->data; + + for (uint32 j = 0; j < sizeof(room->data) / 4; j++) + { + int32* x = (int32*)&room->data + j; + *x += (int32)data; + } + + room->sectors = room->data.sectors; + room->firstItem = NULL; + } + } + +#ifndef MODEHW + // initialize global pointers + gBrightness = -128; + palSet(level.palette, gSettings.video_gamma << 4, gBrightness); + memcpy(gLightmap, level.lightmap, sizeof(gLightmap)); +#endif + + // prepare models // TODO prerocess + memset(models, 0, sizeof(models)); + for (int32 i = 0; i < level.modelsCount; i++) + { + const Model* model = level.models + i; + ASSERT(model->type < MAX_MODELS); + models[model->type] = *model; + } + level.models = models; + + // prepare meshes + for (int32 i = 0; i < level.meshesCount; i++) + { + meshes[i] = (Mesh*)((uint8*)level.meshes + level.meshOffsets[i]); + } + level.meshes = meshes; + + // prepare static meshes // TODO preprocess + memset(staticMeshes, 0, sizeof(staticMeshes)); + for (int32 i = 0; i < level.staticMeshesCount; i++) + { + const StaticMesh* staticMesh = level.staticMeshes + i; + + ASSERT(staticMesh->id < MAX_STATIC_MESHES); + staticMeshes[staticMesh->id] = *staticMesh; + } + level.staticMeshes = staticMeshes; + + // prepare sprites // TODO preprocess + for (int32 i = 0; i < level.spriteSequencesCount; i++) + { + const SpriteSeq* spriteSeq = level.spriteSequences + i; + + if (spriteSeq->type >= TR1_ITEM_MAX) // WTF? + continue; + + Model* m = models + spriteSeq->type; + m->count = int8(spriteSeq->count); + m->start = spriteSeq->start; + } + +#ifdef ROM_READ + // prepare textures (required by anim tex logic) + memcpy(textures, level.textures, level.texturesCount * sizeof(Texture)); + level.textures = textures; + + // prepare sprites (TODO preprocess tile address in packer) + memcpy(sprites, level.sprites, level.spritesCount * sizeof(Sprite)); + level.sprites = sprites; + + // prepare boxes + memcpy(boxes, level.boxes, level.boxesCount * sizeof(Box)); + level.boxes = boxes; + + // prepare fixed cameras + memcpy(cameras, level.cameras, level.camerasCount * sizeof(FixedCamera)); + level.cameras = cameras; +#endif + +#ifdef __3DO__ + for (int32 i = 0; i < level.texturesCount; i++) + { + Texture* tex = level.textures + i; + tex->data += intptr_t(RAM_TEX); + } +#else + // TODO preprocess in packer + for (int32 i = 0; i < level.texturesCount; i++) + { + level.textures[i].tile += (uint32)level.tiles; + } + + for (int32 i = 0; i < level.spritesCount; i++) + { + level.sprites[i].tile += (uint32)level.tiles; + } +#endif +} + +void readLevel(const uint8* data) +{ +//#ifdef ROM_READ + dynSectorsCount = 0; +//#endif + + readLevel_GBA(data); + + gAnimTexFrame = 0; +} + +void animTexturesShift() +{ + const int16* data = level.animTexData; + + int16 texRangesCount = *data++; + + for (int32 i = 0; i < texRangesCount; i++) + { + int16 count = *data++; + + Texture tmp = level.textures[*data]; + while (count > 0) + { + level.textures[data[0]] = level.textures[data[1]]; + data++; + count--; + } + level.textures[*data++] = tmp; + } +} + +#define FADING_RATE_SHIFT 4 + +void updateFading(int32 frames) +{ + if (gBrightness == 0) + return; + + frames <<= FADING_RATE_SHIFT; + + if (gBrightness < 0) + { + gBrightness += frames; + if (gBrightness > 0) { + gBrightness = 0; + } + } + + if (gBrightness > 0) + { + gBrightness -= frames; + if (gBrightness < 0) { + gBrightness = 0; + } + } + + palSet(level.palette, gSettings.video_gamma << 4, gBrightness); +} + +void updateLevel(int32 frames) +{ + updateFading(frames); + + gCausticsFrame += frames; + + gAnimTexFrame += frames; + while (gAnimTexFrame > 5) + { + animTexturesShift(); + gAnimTexFrame -= 5; + } +} + +int32 getAmbientTrack() +{ + return gLevelInfo[gLevelID].track; +} + +#endif diff --git a/src/fixed/nav.h b/src/fixed/nav.h new file mode 100644 index 00000000..136c7cff --- /dev/null +++ b/src/fixed/nav.h @@ -0,0 +1,227 @@ +#ifndef H_NAV +#define H_NAV + +#include "common.h" + +#define NAV_INDEX 0x3FFF +#define NAV_WEIGHT 0x7FFF +#define NAV_BLOCKED 0x8000 + +void Nav::init(uint32 boxIndex) +{ + switch (stepHeight) + { + case 256 : zoneType = ZONE_GROUND_1; break; + case 512 : zoneType = ZONE_GROUND_2; break; + default : zoneType = ZONE_FLY; + } + + weight = 0; + endBox = NO_BOX; + nextBox = NO_BOX; + + headBox = NO_BOX; + tailBox = NO_BOX; + + mask = 0x400; + + for (int32 i = 0; i < level.boxesCount; i++) + { + cells[i].end = NO_BOX; + cells[i].next = NO_BOX; + cells[i].weight = 0; + } + + const uint16* defZones = level.zones[0][zoneType]; + const uint16* altZones = level.zones[1][zoneType]; + + uint16 defZone = defZones[boxIndex]; + uint16 altZone = altZones[boxIndex]; + + cellsCount = 0; + Nav::Cell* cell = cells; + + for (int32 i = 0; i < level.boxesCount; i++) + { + if ((defZone == defZones[i]) || (altZone == altZones[i])) + { + (*cell++).boxIndex = i; + cellsCount++; + } + } + + ASSERT(cellsCount > 0); +} + +vec3i Nav::getWaypoint(uint32 boxIndex, const vec3i &from) +{ + if (nextBox != NO_BOX && nextBox != endBox) + { + endBox = nextBox; + + Nav::Cell &cell = cells[endBox]; + + if (cell.next == NO_BOX && tailBox != endBox) + { + cell.next = headBox; + + if (headBox == NO_BOX) { + tailBox = endBox; + } + + headBox = endBox; + } + + weight++; + cell.weight = weight; + cell.end = NO_BOX; + } + + if (headBox != NO_BOX) + { + const uint16* zones = level.zones[gSaveGame.flipped][zoneType]; + uint16 zone = zones[headBox]; + + for (int32 i = 0; (i < NAV_STEPS) && (headBox != NO_BOX); i++) + { + search(zone, zones); + } + } + + if (boxIndex == endBox) + return pos; + + vec3i wp = from; + + if (boxIndex == NO_BOX) + return wp; + + const Box* box = level.boxes + boxIndex; + + int32 bMinX = (box->minX << 10); + int32 bMaxX = (box->maxX << 10) - 1; + int32 bMinZ = (box->minZ << 10); + int32 bMaxZ = (box->maxZ << 10) - 1; + + int32 minX = bMinX; + int32 maxX = bMaxX; + int32 minZ = bMinZ; + int32 maxZ = bMaxZ; + + while ((boxIndex != NO_BOX) && !(level.boxes[boxIndex].overlap & mask)) + { + box = level.boxes + boxIndex; + + bMinX = (box->minX << 10); + bMaxX = (box->maxX << 10) - 1; + bMinZ = (box->minZ << 10); + bMaxZ = (box->maxZ << 10) - 1; + + if (from.x >= bMinX && from.x <= bMaxX && from.z >= bMinZ && from.z <= bMaxZ) + { + minX = bMinX; + maxX = bMaxX; + minZ = bMinZ; + maxZ = bMaxZ; + } else { + if ((wp.x < bMinX) || (wp.x > bMaxX)) + { + if ((wp.z < minZ) || (wp.z > maxZ)) + break; + wp.x = X_CLAMP(wp.x, bMinX + 512, bMaxX - 512); + minZ = X_MAX(minZ, bMinZ); + maxZ = X_MIN(maxZ, bMaxZ); + } + + if ((wp.z < bMinZ) || (wp.z > bMaxZ)) + { + if ((wp.x < minX) || (wp.x > maxX)) + break; + wp.z = X_CLAMP(wp.z, bMinZ + 512, bMaxZ - 512); + minX = X_MAX(minX, bMinX); + maxX = X_MIN(maxX, bMaxX); + } + } + + if (boxIndex == endBox) + { + wp.x = X_CLAMP(wp.x, bMinX + 512, bMaxX - 512); + wp.z = X_CLAMP(wp.z, bMinZ + 512, bMaxZ - 512); + break; + } + + boxIndex = cells[boxIndex].end; + } + + wp.y = box->floor - ((zoneType == ZONE_FLY) ? 384 : 0); // TODO check for 320 + + return wp; +} + +void Nav::search(uint16 zone, const uint16* zones) +{ + Nav::Cell &curr = cells[headBox]; + const Box &b = level.boxes[headBox]; + + uint16 overlapIndex = b.overlap & NAV_INDEX; + + bool end = false; + + do { + uint16 boxIndex = level.overlaps[overlapIndex++]; + + end = boxIndex & NAV_BLOCKED; + if (end) { + boxIndex &= NAV_INDEX; + } + + if (zone != zones[boxIndex]) + continue; + + int32 diff = level.boxes[boxIndex].floor - b.floor; + if (diff > stepHeight || diff < dropHeight) + continue; + + Nav::Cell &next = cells[boxIndex]; + + uint16 cWeight = curr.weight & NAV_WEIGHT; + uint16 nWeight = next.weight & NAV_WEIGHT; + + if (cWeight < nWeight) + continue; + + if (curr.weight & NAV_BLOCKED) + { + if (cWeight == nWeight) + continue; + + next.weight = curr.weight; + } + else + { + if ((cWeight == nWeight) && !(next.weight & NAV_BLOCKED)) + continue; + + if (level.boxes[boxIndex].overlap & mask) + { + next.weight = curr.weight | NAV_BLOCKED; + } + else + { + next.weight = curr.weight; + next.end = headBox; + } + } + + if (next.next == NO_BOX && boxIndex != tailBox) + { + cells[tailBox].next = boxIndex; + tailBox = boxIndex; + } + } while (!end); + + headBox = curr.next; + curr.next = NO_BOX; +} + +#endif diff --git a/src/fixed/object.h b/src/fixed/object.h new file mode 100644 index 00000000..c178454a --- /dev/null +++ b/src/fixed/object.h @@ -0,0 +1,1080 @@ +#ifndef H_OBJECT +#define H_OBJECT + +#include "item.h" +#include "lara.h" +#include "inventory.h" + +vec3i getBlockOffset(int16 angleY, int32 offset) +{ + if (angleY == ANGLE_0) + return _vec3i(0, 0, -offset); + if (angleY == ANGLE_180) + return _vec3i(0, 0, offset); + if (angleY == ANGLE_90) + return _vec3i(-offset, 0, 0); + return _vec3i(offset, 0, 0); +} + +struct Limit +{ + AABBs box; + vec3s angle; +}; + +// armcpp won't initialize structs + +const int16 LIMIT_SWITCH[] = { + -200, 200, 0, 0, 312, 512, + ANGLE(10), ANGLE(30), ANGLE(10) +}; + +const int16 LIMIT_SWITCH_UW[] = { + -1024, 1024, -1024, 1024, -1024, 1024, + ANGLE(80), ANGLE(80), ANGLE(80) +}; + +const int16 LIMIT_BLOCK[] = { + -300, 300, 0, 0, -692, -512, + ANGLE(10), ANGLE(30), ANGLE(10) +}; + +const int16 LIMIT_PICKUP[] = { + -256, 256, -100, 100, -256, 100, + ANGLE(10), 0, 0 +}; + +const int16 LIMIT_PICKUP_UW[] = { + -512, 512, -512, 512, -512, 512, + ANGLE(45), ANGLE(45), ANGLE(45) +}; + +const int16 LIMIT_HOLE[] = { + -200, 200, 0, 0, 312, 512, + ANGLE(10), ANGLE(30), ANGLE(10) +}; + + +struct Object : ItemObj +{ + Object(Room* room) : ItemObj(room) {} + + virtual void update() + { + animProcess(); + } + + bool isActive() + { + if (((flags & ITEM_FLAG_MASK) != ITEM_FLAG_MASK) || (timer == -1)) + return (flags & ITEM_FLAG_REVERSE); + + if (timer == 0) + return !(flags & ITEM_FLAG_REVERSE); + + timer--; + + if (timer == 0) + timer = -1; + + return !(flags & ITEM_FLAG_REVERSE); + } + + bool checkLimit(Lara* lara, const int16* limitData) + { + Limit* limit = (Limit*)limitData; + + int16 ax = abs(lara->angle.x - angle.x); + int16 ay = abs(lara->angle.y - angle.y); + int16 az = abs(lara->angle.z - angle.z); + + if (ax > limit->angle.x || ay > limit->angle.y || az > limit->angle.z) + return false; + + vec3i d = lara->pos - pos; + + matrixSetIdentity(); + matrixRotateZ(-angle.z); + matrixRotateX(-angle.x); + matrixRotateY(-angle.y); + + const Matrix &m = matrixGet(); + + vec3i p; + p.x = DP33(m.e00, m.e01, m.e02, d.x, d.y, d.z) >> FIXED_SHIFT; + p.y = DP33(m.e10, m.e11, m.e12, d.x, d.y, d.z) >> FIXED_SHIFT; + p.z = DP33(m.e20, m.e21, m.e22, d.x, d.y, d.z) >> FIXED_SHIFT; + + return boxContains(limit->box, p); + } + + void collideDefault(Lara* lara, CollisionInfo* cinfo) + { + if (!updateHitMask(lara, cinfo)) + return; + + if (!cinfo->enemyPush) + return; + + collidePush(lara, cinfo, false); + } +}; + + +struct SpriteEffect : ItemObj +{ + SpriteEffect(Room* room) : ItemObj(room) + { + tick = 0; + timer = 0; + hSpeed = 0; + frameIndex = 0; + activate(); + } + + virtual void update() + { + tick++; + if (tick >= timer) + { + tick = 0; + + if (flags & ITEM_FLAG_ANIMATED) + { + frameIndex++; + if (frameIndex >= -level.models[type].count) + { + remove(); + return; + } + } else { + remove(); + return; + } + } + + if (hSpeed) + { + int32 s, c; + sincos(angle.y, s, c); + pos.x += s * hSpeed >> FIXED_SHIFT; + pos.z += c * hSpeed >> FIXED_SHIFT; + } + } +}; + + +struct Bubble : ItemObj +{ + Bubble(Room* room) : ItemObj(room) + { + soundPlay(SND_BUBBLE, &pos); + frameIndex = rand_draw() % (-level.models[type].count); + vSpeed = -(10 + (rand_draw() % 6)); + angle = _vec3s(0, 0, ANGLE_90); + activate(); + + roomFloor = getWaterLevel(); + } + + virtual void update() + { + pos.y += vSpeed; + if (roomFloor > pos.y) + { + remove(); + return; + } + + angle.x += ANGLE(9); + angle.z += ANGLE(13); + + int32 dx = sin(angle.x); + int32 dz = sin(angle.z); + + pos.x += dx * 11 >> FIXED_SHIFT; + pos.z += dz * 8 >> FIXED_SHIFT; + + Room* nextRoom = room->getRoom(pos.x, pos.y, pos.z); + if (nextRoom != room) + { + room->remove(this); + nextRoom->add(this); + } + } +}; + + +struct ViewTarget : Object +{ + ViewTarget(Room* room) : Object(room) {} + + virtual void draw() {} +}; + + +struct Waterfall : Object +{ + Waterfall(Room* room) : Object(room) {} + + virtual void draw() {} +}; + + +struct LavaEmitter : Object +{ + LavaEmitter(Room* room) : Object(room) {} + + virtual void draw() {} +}; + + +struct Door : Object +{ + enum { + STATE_CLOSE, + STATE_OPEN + }; + + Door(Room* room) : Object(room) + { + flags |= ITEM_FLAG_COLLISION; + action(true); + } + + virtual void update() + { + if (isActive()) { + if (state == STATE_CLOSE) { + goalState = STATE_OPEN; + } else { + action(false); + } + } else { + if (state == STATE_OPEN) { + goalState = STATE_CLOSE; + } else { + action(true); + } + } + + animProcess(); + } + + virtual void collide(Lara* lara, CollisionInfo* cinfo) + { + // TODO door collision + } + + void action(bool close) + { + vec3i nextPos = getBlockOffset(angle.y, 1); + nextPos.x = pos.x + (nextPos.x << 10); + nextPos.z = pos.z + (nextPos.z << 10); + + setDoorState(close, false, room, nextPos.x, nextPos.z); // use the sector behind the door + + // TODO flip rooms + } + + void setDoorState(bool close, bool behind, Room* room, int32 x, int32 z) + { + room->modify(); // make room->sectors dynamic (non ROM) + + Sector* sector = (Sector*)room->getSector(x, z); // now we can modify room sectors + + Room* nextRoom; + + if (close) { + nextRoom = sector->getNextRoom(); + + sector->floorIndex = 0; + sector->boxIndex = NO_BOX; + sector->roomBelow = NO_ROOM; + sector->floor = NO_FLOOR; + sector->roomAbove = NO_ROOM; + sector->ceiling = NO_FLOOR; + } else { + *sector = room->data.sectors[sector - room->sectors]; + + nextRoom = sector->getNextRoom(); + } + + // TODO modify level.boxes + + if (!behind && nextRoom) { + setDoorState(close, true, nextRoom, pos.x, pos.z); // use sector from item pos + } + } +}; + + +struct TrapDoor : Object +{ + enum { + STATE_CLOSE, + STATE_OPEN + }; + + TrapDoor(Room* room) : Object(room) {} + + virtual void update() + { + if (isActive()) { + if (state == STATE_CLOSE) { + goalState = STATE_OPEN; + } + } else { + if (state == STATE_OPEN) { + goalState = STATE_CLOSE; + } + } + + animProcess(); + } +}; + + +struct Crystal : Object +{ + Crystal(Room* room) : Object(room) + { + flags &= ~ITEM_FLAG_STATUS; + flags |= ITEM_FLAG_STATUS_INVISIBLE; // disable crystals for now + } + + virtual void collide(Lara* lara, CollisionInfo* cinfo) + { + // TODO + } +}; + + +struct Switch : Object +{ + enum { + STATE_UP, + STATE_DOWN + }; + + Switch(Room* room) : Object(room) {} + + virtual void update() + { + flags |= ITEM_FLAG_MASK; + if (!isActive()) + { + goalState = STATE_DOWN; + timer = 0; + } + Object::update(); + } + + virtual void collide(Lara* lara, CollisionInfo* cinfo) + { + if (lara->extraL->weaponState != WEAPON_STATE_FREE) + return; + + if (!(lara->input & IN_ACTION)) + return; + + if (lara->state != Lara::STATE_STOP) + return; + + if (flags & ITEM_FLAG_STATUS) + return; + + if (!checkLimit(lara, LIMIT_SWITCH)) + return; + + lara->angle.y = angle.y; + + ASSERT(state == STATE_DOWN || state == STATE_UP); + + bool isDown = (state == STATE_DOWN); + + goalState = isDown ? STATE_UP : STATE_DOWN; + lara->animSkip(isDown ? Lara::STATE_SWITCH_DOWN : Lara::STATE_SWITCH_UP, Lara::STATE_STOP, true); + activate(); + flags &= ~ITEM_FLAG_STATUS; + flags |= ITEM_FLAG_STATUS_ACTIVE; + lara->extraL->weaponState = WEAPON_STATE_BUSY; + } + + bool use(int32 t) + { + if ((flags & ITEM_FLAG_STATUS) == ITEM_FLAG_STATUS_INACTIVE) + { + flags &= ~ITEM_FLAG_STATUS; + + if (t > 0 && state == Switch::STATE_UP) + { + if (t != 1) { + t *= 30; + } + timer = t; + flags |= ITEM_FLAG_STATUS_ACTIVE; + } else { + deactivate(); + } + return true; + } + + return false; + } +}; + + +struct SwitchWater : Switch +{ + SwitchWater(Room* room) : Switch(room) {} + + virtual void collide(Lara* lara, CollisionInfo* cinfo) + { + if (lara->extraL->weaponState != WEAPON_STATE_FREE) + return; + + if (!(lara->input & IN_ACTION)) + return; + + if (lara->state != Lara::STATE_UW_TREAD) + return; + + if (!checkLimit(lara, LIMIT_SWITCH_UW)) + return; + + if (!lara->moveTo(_vec3i(0, 0, 108), this, true)) + return; + + lara->vSpeed = 0; // underwater speed + goalState = (state == STATE_UP) ? STATE_DOWN : STATE_UP; + + lara->animSkip(Lara::STATE_SWITCH_DOWN, Lara::STATE_UW_TREAD, true); + activate(); + flags &= ~ITEM_FLAG_STATUS; + flags |= ITEM_FLAG_STATUS_ACTIVE; + + //TODO TR2+ + //lara->weaponState = WEAPON_STATE_BUSY; + } +}; + + +struct Key : Object +{ + Key(Room* room) : Object(room) {} + + bool use(ItemObj* lara) + { + if (((flags & ITEM_FLAG_STATUS) == ITEM_FLAG_STATUS_ACTIVE) && (lara->extraL->weaponState == WEAPON_STATE_FREE)) // TODO check weapons + { + flags &= ~ITEM_FLAG_STATUS; + flags |= ITEM_FLAG_STATUS_INACTIVE; + return true; + } + return false; + } +}; + + +struct Pickup : Object +{ + Pickup(Room* room) : Object(room) + { + frameIndex = 0; + } + + bool use() + { + if ((flags & ITEM_FLAG_STATUS) == ITEM_FLAG_STATUS_INVISIBLE) + { + flags &= ~ITEM_FLAG_STATUS; + flags |= ITEM_FLAG_STATUS_INACTIVE; + return true; + } + return false; + } + + virtual void collide(Lara* lara, CollisionInfo* cinfo) + { + angle.y = lara->angle.y; + angle.z = 0; + + if (lara->waterState == WATER_STATE_ABOVE || lara->waterState == WATER_STATE_WADE) + { + angle.x = 0; + + if (!checkLimit(lara, LIMIT_PICKUP)) + return; + + if (lara->state == Lara::STATE_PICKUP) + { + if (!lara->animIsEnd(23)) + return; + + inventory.add((ItemType)type); + gSaveGame.pickups++; + room->remove(this); + flags &= ~ITEM_FLAG_STATUS; + flags |= ITEM_FLAG_STATUS_INVISIBLE; + } + else if (lara->state == Lara::STATE_STOP) + { + if (lara->extraL->weaponState != WEAPON_STATE_FREE) + return; + + if (!(lara->input & IN_ACTION)) + return; + + if (!lara->moveTo(_vec3i(0, 0, -100), this, false)) + return; + + lara->animSkip(Lara::STATE_PICKUP, Lara::STATE_STOP); + lara->extraL->weaponState = WEAPON_STATE_BUSY; + } + } + + if (lara->waterState == WATER_STATE_UNDER) + { + angle.x = ANGLE(-25); + + if (!checkLimit(lara, LIMIT_PICKUP_UW)) + return; + + if (lara->state == Lara::STATE_PICKUP) + { + if (!lara->animIsEnd(14)) + return; + + inventory.add((ItemType)type); + gSaveGame.pickups++; + room->remove(this); + flags &= ~ITEM_FLAG_STATUS; + flags |= ITEM_FLAG_STATUS_INVISIBLE; + } + else if (lara->state == Lara::STATE_UW_TREAD) + { + // TODO TR2+ + //if (lara->weaponState != WEAPON_STATE_FREE) + // return; + + if (!(lara->input & IN_ACTION)) + return; + + if (!lara->moveTo(_vec3i(0, -200, -350), this, true)) + return; + + lara->animSkip(Lara::STATE_PICKUP, Lara::STATE_UW_TREAD); + + // TODO TR2+ + //lara->weaponState = WEAPON_STATE_BUSY; // TODO check CMD_EMPTY event + } + } + } +}; + + +bool useSwitch(ItemObj* item, int32 timer) +{ + return ((Switch*)item)->use(timer); +} + +bool useKey(ItemObj* item, ItemObj* lara) +{ + return ((Key*)item)->use(lara); +} + +bool usePickup(ItemObj* item) +{ + return ((Pickup*)item)->use(); +} + +struct Hole : Object // parent class for KeyHole and PuzzleHole +{ + Hole(Room* room) : Object(room) {} + + void apply(int32 offset, Lara* lara, Lara::State stateUse) + { + if (lara->extraL->weaponState != WEAPON_STATE_FREE) + return; + + if (flags & ITEM_FLAG_STATUS) + return; + + if (lara->state != Lara::STATE_STOP || lara->animIndex != Lara::ANIM_STAND_NORMAL) + return; + + if (!(lara->input & IN_ACTION) && (inventory.useSlot == SLOT_MAX)) + return; + + if (!checkLimit(lara, LIMIT_HOLE)) + return; + + if (inventory.useSlot == SLOT_MAX) + { + if (inventory.numKeys > 0) { + inventory.open(lara, INV_PAGE_USE, type); + return; + } + } else { + if (inventory.applyItem(this)) + { + lara->moveTo(_vec3i(0, 0, offset), this, false); + lara->animSkip(stateUse, Lara::STATE_STOP); + lara->extraL->weaponState = WEAPON_STATE_BUSY; + flags &= ~ITEM_FLAG_STATUS; + flags |= ITEM_FLAG_STATUS_ACTIVE; + return; + } + inventory.useSlot = SLOT_MAX; + } + soundPlay(SND_NO, &lara->pos); + } +}; + + +struct KeyHole : Hole +{ + KeyHole(Room* room) : Hole(room) {} + + virtual void collide(Lara* lara, CollisionInfo* cinfo) + { + apply(362, lara, Lara::STATE_USE_KEY); + } +}; + + +struct PuzzleHole : Hole +{ + PuzzleHole(Room* room) : Hole(room) {} + + virtual void collide(Lara* lara, CollisionInfo* cinfo) + { + if (lara->state == Lara::STATE_USE_PUZZLE) + { + if (!checkLimit(lara, LIMIT_HOLE)) + return; + + if (!lara->animIsEnd(28)) + return; + + switch (type) { + case ITEM_PUZZLEHOLE_1 : type = ITEM_PUZZLEHOLE_DONE_1; break; + case ITEM_PUZZLEHOLE_2 : type = ITEM_PUZZLEHOLE_DONE_2; break; + case ITEM_PUZZLEHOLE_3 : type = ITEM_PUZZLEHOLE_DONE_3; break; + case ITEM_PUZZLEHOLE_4 : type = ITEM_PUZZLEHOLE_DONE_4; break; + } + + return; + } + + apply(327, lara, Lara::STATE_USE_PUZZLE); + } +}; + + +struct TrapFloor : Object +{ + enum { + STATE_STATIC, + STATE_SHAKE, + STATE_FALL, + STATE_DOWN + }; + + TrapFloor(Room* room) : Object(room) {} + + virtual void update() + { + switch (state) + { + case STATE_STATIC: + if (getLara(pos)->pos.y != pos.y - 512) + { + flags &= ~ITEM_FLAG_STATUS; + deactivate(); + return; + } + goalState = STATE_SHAKE; + break; + case STATE_SHAKE: + goalState = STATE_FALL; + break; + case STATE_FALL: + if (goalState != STATE_DOWN) + { + flags |= ITEM_FLAG_GRAVITY; + } + break; + } + + animProcess(); + + if ((flags & ITEM_FLAG_STATUS) == ITEM_FLAG_STATUS_INACTIVE) + { + deactivate(); + return; + } + + if (state == STATE_FALL) { + updateRoom(); + } + + if (state == STATE_FALL && pos.y >= roomFloor) + { + pos.y = roomFloor; + vSpeed = 0; + flags &= ~ITEM_FLAG_GRAVITY; + goalState = STATE_DOWN; + } + } + + virtual void draw() + { + int32 oldAnimIndex = animIndex; + if ((state == STATE_STATIC) && level.models[ITEM_TRAP_FLOOR_LOD].type) { + type = ITEM_TRAP_FLOOR_LOD; + animIndex = level.models[type].animIndex; + } + Object::draw(); + type = ITEM_TRAP_FLOOR; + animIndex = oldAnimIndex; + } +}; + + +struct TrapSwingBlade : Object +{ + enum { + STATE_STATIC, + STATE_BEGIN, + STATE_SWING, + STATE_END + }; + + TrapSwingBlade(Room* room) : Object(room) + { + flags |= ITEM_FLAG_SHADOW; + } + + virtual void collide(Lara* lara, CollisionInfo* cinfo) + { + if ((flags & ITEM_FLAG_STATUS) != ITEM_FLAG_STATUS_ACTIVE) + return; + + if (state != STATE_SWING) + return; + + if (!updateHitMask(lara, cinfo)) + return; + + vec3i offsetPos = _vec3i((rand_logic() - 0x4000) >> 8, -256 - (rand_logic() >> 6), (rand_logic() - 0x4000) >> 8); + int32 offsetAngle = (rand_logic() - 0x4000) >> 3; + lara->fxBlood(lara->pos + offsetPos, lara->angle.y + offsetAngle, lara->hSpeed); + lara->hit(100, pos, 0); // TODO TR2 50? + } + + virtual void update() + { + if (isActive()) { + if (state == STATE_STATIC) { + goalState = STATE_SWING; + } + } else { + if (state == STATE_SWING) { + goalState = STATE_STATIC; + } + } + + animProcess(); + } +}; + + +struct Dart : Object +{ + Dart(Room* room) : Object(room) + { + flags |= ITEM_FLAG_SHADOW; + } + + virtual void collide(Lara* lara, CollisionInfo* cinfo) + { + if (!updateHitMask(lara, cinfo)) + return; + + if (hitMask) + { + lara->fxBlood(pos, lara->angle.y, lara->hSpeed); + lara->hit(50, pos, 0); + } + } + + virtual void update() + { + animProcess(); + updateRoom(); + + if (pos.y < roomFloor) + return; + + remove(); + + fxRicochet(room, pos, false); + } +}; + + +struct TrapDartEmitter : Object +{ + enum { + STATE_IDLE, + STATE_FIRE + }; + + TrapDartEmitter(Room* room) : Object(room) {} + + virtual void update() + { + goalState = isActive() ? STATE_FIRE : STATE_IDLE; + + if (state == STATE_IDLE && state == goalState) + { + deactivate(); + return; + } + + if (state == STATE_FIRE && frameIndex == level.anims[animIndex].frameBegin) + { + vec3i p = getBlockOffset(angle.y, 412); + p.y = -512; + p += pos; + + ItemObj* dart = ItemObj::add(ITEM_DART, room, p, angle.y); + + if (dart) + { + soundPlay(SND_DART, &pos); + + dart->intensity = 0; + dart->flags &= ~ITEM_FLAG_STATUS; + dart->flags |= ITEM_FLAG_STATUS_ACTIVE; + dart->activate(); + + fxSmoke(p); + } + } + + animProcess(); + } +}; + + +struct Block : Object +{ + enum { + STATE_NONE, + STATE_READY, + STATE_PUSH, + STATE_PULL + }; + + Block(Room* room) : Object(room) + { + if ((flags & ITEM_FLAG_STATUS) != ITEM_FLAG_STATUS_INVISIBLE) { + updateFloor(-1024); + } + } + + void updateFloor(int32 offset) + { + room->modify(); + + Sector* sector = (Sector*)room->getSector(pos.x, pos.z); + + if (sector->floor == NO_FLOOR) { + sector->floor = sector->ceiling + (offset >> 8); + } else { + sector->floor += (offset >> 8); + if (sector->floor == sector->ceiling) { + sector->floor = NO_FLOOR; + } + } + + // TODO modify level.boxes + } + + bool checkBlocking() + { + const Sector* sector = room->getSector(pos.x, pos.z); + + return (sector->floor == NO_FLOOR) || ((sector->floor << 8) + 1024 == pos.y); + } + + bool checkObstacles(int32 x, int32 z, int32 height) + { + Room* nextRoom = room->getRoom(x, pos.y, z); + const Sector* sector = nextRoom->getSector(x, z); + + int32 floor = pos.y; + int32 ceiling = pos.y - height; + + if ((sector->floor << 8) != floor) + return false; + + nextRoom = nextRoom->getRoom(x, ceiling, z); + sector = nextRoom->getSector(x, z); + + if ((sector->ceiling << 8) > ceiling) + return false; + + return true; + } + + bool checkPush() + { + if (!checkBlocking()) + return false; + + vec3i offset = getBlockOffset(angle.y, -1024); + + return checkObstacles(pos.x + offset.x, pos.z + offset.z, 1024); + } + + bool checkPull(ItemObj* lara) + { + if (!checkBlocking()) + return false; + + vec3i offset = getBlockOffset(angle.y, 1024); + + if (!checkObstacles(pos.x + offset.x, pos.z + offset.z, 1024)) + return false; + + return checkObstacles(lara->pos.x + offset.x, lara->pos.z + offset.z, LARA_HEIGHT); + } + + virtual void collide(Lara* lara, CollisionInfo* cinfo) + { + if (!(lara->input & IN_ACTION)) + return; + + if ((flags & ITEM_FLAG_STATUS) == ITEM_FLAG_STATUS_ACTIVE) + return; + + if (lara->pos.y != pos.y) + return; + + uint16 quadrant = uint16(lara->angle.y + ANGLE_45) >> ANGLE_SHIFT_90; + + if ((lara->animIndex == Lara::ANIM_BLOCK_READY) && (lara->input & (IN_UP | IN_DOWN))) + { + if (!lara->animIsEnd(0)) + return; + + if (!checkLimit(lara, LIMIT_BLOCK)) + return; + + if (lara->input & IN_UP) + { + if (!checkPush()) + return; + + lara->goalState = Lara::STATE_BLOCK_PUSH; + goalState = STATE_PUSH; + } + else + { + if (!checkPull(lara)) + return; + + lara->goalState = Lara::STATE_BLOCK_PULL; + goalState = STATE_PULL; + } + + updateFloor(1024); + + activate(); + flags &= ~ITEM_FLAG_STATUS; + flags |= ITEM_FLAG_STATUS_ACTIVE; + + animProcess(); + lara->animProcess(); + } + + if (lara->state == Lara::STATE_STOP) + { + if (lara->extraL->weaponState != WEAPON_STATE_FREE) + return; + + if (lara->input & (IN_UP | IN_DOWN)) + return; + + angle.y = quadrant * ANGLE_90; + + if (!checkLimit(lara, LIMIT_BLOCK)) + return; + + lara->goalState = Lara::STATE_BLOCK_READY; + lara->angle.y = angle.y; + lara->alignWall(LARA_RADIUS); + lara->animProcess(); + if (lara->state == Lara::STATE_BLOCK_READY) { + lara->setWeaponState(WEAPON_STATE_BUSY); + } + } + } + + virtual void update() + { + if (flags & ITEM_FLAG_ONCE) + { + updateFloor(1024); + remove(); + return; + } + + animProcess(); + + updateRoom(); // it'll get roomFloor and gLastFloorData + + if (pos.y > roomFloor) + { + flags |= ITEM_FLAG_GRAVITY; + } + else if (flags & ITEM_FLAG_GRAVITY) + { + flags &= ~(ITEM_FLAG_GRAVITY | ITEM_FLAG_STATUS); + flags |= ITEM_FLAG_STATUS_INACTIVE; + pos.y = roomFloor; + // TODO EarthQuake + playSound 70 (Thor room) + } + + if ((flags & ITEM_FLAG_STATUS) == ITEM_FLAG_STATUS_INACTIVE) + { + deactivate(); + flags &= ~ITEM_FLAG_STATUS; + + updateFloor(-1024); + + checkTrigger(gLastFloorData, NULL); + } + } + + virtual uint8* load(uint8* data) + { + if ((flags & ITEM_FLAG_STATUS) != ITEM_FLAG_STATUS_INVISIBLE) { + updateFloor(1024); + } + + data = ItemObj::load(data); + + if ((flags & ITEM_FLAG_STATUS) != ITEM_FLAG_STATUS_INVISIBLE) { + updateFloor(-1024); + } + + return data; + } +}; + +#endif diff --git a/src/fixed/room.h b/src/fixed/room.h new file mode 100644 index 00000000..f48b7881 --- /dev/null +++ b/src/fixed/room.h @@ -0,0 +1,1145 @@ +#ifndef H_ROOM +#define H_ROOM + +#include "common.h" + +EWRAM_DATA Room* roomsList[MAX_ROOM_LIST]; + +//#ifdef ROM_READ +EWRAM_DATA int32 dynSectorsCount; +EWRAM_DATA Sector dynSectors[MAX_DYN_SECTORS]; // EWRAM 8k +//#endif + +const Sector* Sector::getSectorBelow(int32 posX, int32 posZ) const +{ + if (roomBelow == NO_ROOM) + return this; + return rooms[roomBelow].getSector(posX, posZ)->getSectorBelow(posX, posZ); +} + +const Sector* Sector::getSectorAbove(int32 posX, int32 posZ) const +{ + if (roomAbove == NO_ROOM) + return this; + return rooms[roomAbove].getSector(posX, posZ)->getSectorAbove(posX, posZ); +} + +int32 Sector::getFloor(int32 x, int32 y, int32 z) const +{ + gLastFloorData = NULL; + + const Sector* lowerSector = getSectorBelow(x, z); + + int32 floor = lowerSector->floor << 8; + + gLastFloorSlant = 0; + + if (lowerSector->floorIndex) + { + const FloorData* fd = level.floors + lowerSector->floorIndex; + uint16 cmd = *fd++; + + if (FD_FLOOR_TYPE(cmd) == FLOOR_TYPE_FLOOR) // found floor + { + gLastFloorSlant = *fd; + int32 sx = FD_SLANT_X(gLastFloorSlant); + int32 sz = FD_SLANT_Z(gLastFloorSlant); + int32 dx = x & 1023; + int32 dz = z & 1023; + floor -= sx * (sx < 0 ? dx : (dx - 1023)) >> 2; + floor -= sz * (sz < 0 ? dz : (dz - 1023)) >> 2; + } + + lowerSector->getTriggerFloorCeiling(x, y, z, &floor, NULL); + } + + return floor; +} + +int32 Sector::getCeiling(int32 x, int32 y, int32 z) const +{ + const Sector* upperSector = getSectorAbove(x, z); + + int32 ceiling = upperSector->ceiling << 8; + + if (upperSector->floorIndex) + { + const FloorData* fd = level.floors + upperSector->floorIndex; + uint16 cmd = *fd++; + + if (FD_FLOOR_TYPE(cmd) == FLOOR_TYPE_FLOOR) // skip floor + { + fd++; + cmd = *fd++; + } + + if (FD_FLOOR_TYPE(cmd) == FLOOR_TYPE_CEILING) // found ceiling + { + int32 sx = FD_SLANT_X(*fd); + int32 sz = FD_SLANT_Z(*fd); + int32 dx = x & 1023; + int32 dz = z & 1023; + ceiling -= sx * (sx < 0 ? (dx - 1023) : dx) >> 2; + ceiling += sz * (sz < 0 ? dz : (dz - 1023)) >> 2; + } + } + + const Sector* lowerSector = getSectorBelow(x, z); + + if (lowerSector->floorIndex) + { + lowerSector->getTriggerFloorCeiling(x, y, z, NULL, &ceiling); + } + + return ceiling; +} + +Room* Sector::getNextRoom() const +{ + if (!floorIndex) + return NULL; + + // always in this order + // - floor + // - ceiling + // - portal + // - other + + const FloorData* fd = level.floors + floorIndex; + uint16 cmd = *fd++; + + if (FD_FLOOR_TYPE(cmd) == FLOOR_TYPE_FLOOR) // skip floor + { + if (FD_END(cmd)) return NULL; + fd++; + cmd = *fd++; + } + + if (FD_FLOOR_TYPE(cmd) == FLOOR_TYPE_CEILING) // skip ceiling + { + if (FD_END(cmd)) return NULL; + fd++; + cmd = *fd++; + } + + if (FD_FLOOR_TYPE(cmd) != FLOOR_TYPE_PORTAL) // no portal + return NULL; + + ASSERT(*fd != NO_ROOM); + + return rooms + *fd; +} + +void Sector::getTriggerFloorCeiling(int32 x, int32 y, int32 z, int32* floor, int32* ceiling) const +{ + if (!floorIndex) + return; + + uint16 cmd; + const FloorData* fd = level.floors + floorIndex; + + do { + cmd = *fd++; + + switch (FD_FLOOR_TYPE(cmd)) + { + case FLOOR_TYPE_PORTAL: + case FLOOR_TYPE_FLOOR: + case FLOOR_TYPE_CEILING: + { + fd++; + break; + } + + case FLOOR_TYPE_TRIGGER: + { + if (floor && !gLastFloorData) { + gLastFloorData = fd - 1; + } + + fd++; + uint16 trigger; + + do { + trigger = *fd++; + + if (FD_ACTION(trigger) == TRIGGER_ACTION_ACTIVATE_OBJECT) + { + items[FD_ARGS(trigger)].getItemFloorCeiling(x, y, z, floor, ceiling); + } + + if (FD_ACTION(trigger) == TRIGGER_ACTION_ACTIVATE_CAMERA) + { + trigger = *fd++; // skip camera index + } + + } while (!FD_END(trigger)); + + break; + } + + case FLOOR_TYPE_LAVA: + if (floor) { + gLastFloorData = fd - 1; + } + break; + } + + } while (!FD_END(cmd)); +} + +#ifndef USE_ASM +const Sector* Room::getSector(int32 x, int32 z) const +{ + // TODO remove clamp? + int32 sx = X_CLAMP((x - (info->x << 8)) >> 10, 0, info->xSectors - 1); + int32 sz = X_CLAMP((z - (info->z << 8)) >> 10, 0, info->zSectors - 1); + + return sectors + sx * info->zSectors + sz; +} +#endif + +const Sector* Room::getWaterSector(int32 x, int32 z) const +{ + const Room* room = this; + const Sector* sector = room->getSector(x, z); + + // go up to the air + if (ROOM_FLAG_WATER(room->info->flags)) + { + while (sector->roomAbove != NO_ROOM) + { + room = rooms + sector->roomAbove; + + if (!ROOM_FLAG_WATER(room->info->flags)) { + return sector; + } + + sector = room->getSector(x, z); + } + return sector; + } + + // go down to the water + while (sector->roomBelow != NO_ROOM) + { + room = rooms + sector->roomBelow; + sector = room->getSector(x, z); + + if (ROOM_FLAG_WATER(room->info->flags)) { + return sector; + } + } + + return NULL; +} + +Room* Room::getRoom(int32 x, int32 y, int32 z) +{ + const Sector* sector = getSector(x, z); + + Room* room = this; + + while (1) + { + Room* nextRoom = sector->getNextRoom(); + if (!nextRoom) + break; + room = nextRoom; + sector = room->getSector(x, z); + }; + + while (sector->roomAbove != NO_ROOM && y < (sector->ceiling << 8)) + { + room = rooms + sector->roomAbove; + sector = room->getSector(x, z); + } + + while (sector->roomBelow != NO_ROOM && y >= (sector->floor << 8)) + { + room = rooms + sector->roomBelow; + sector = room->getSector(x, z); + } + + return room; +} + +bool Room::collideStatic(CollisionInfo &cinfo, const vec3i &p, int32 height) +{ + cinfo.staticHit = false; + cinfo.offset = _vec3i(0, 0, 0); + + AABBi objBox; + objBox.minX = -cinfo.radius; + objBox.maxX = cinfo.radius; + objBox.minZ = -cinfo.radius; + objBox.maxZ = cinfo.radius; + objBox.minY = -height; + objBox.maxY = 0; + + Room** nearRoom = getNearRooms(p, cinfo.radius, height); + + while (*nearRoom) + { + const Room* room = *nearRoom++; + + int32 rx = p.x - (room->info->x << 8); + int32 ry = p.y; + int32 rz = p.z - (room->info->z << 8); + + for (int i = 0; i < room->info->meshesCount; i++) + { + const RoomMesh* mesh = room->data.meshes + i; + + #ifdef NO_STATIC_MESH_PLANTS + if (STATIC_MESH_ID(mesh->zf) < 10) + continue; + #endif + + const StaticMesh* staticMesh = level.staticMeshes + STATIC_MESH_ID(mesh->zf); + + if (staticMesh->flags & STATIC_MESH_FLAG_NO_COLLISION) + continue; + + int32 x = (int32(mesh->xy) >> 16) - rx; + int32 y = (int32(mesh->xy) << 16 >> 16) - ry; + int32 z = (int32(mesh->zf) >> 16) - rz; + + if (abs(x) > MAX_STATIC_MESH_RADIUS || abs(z) > MAX_STATIC_MESH_RADIUS || abs(y) > MAX_STATIC_MESH_RADIUS) + continue; + + AABBi meshBox(staticMesh->cbox); + boxRotateYQ(meshBox, STATIC_MESH_QUADRANT(mesh->zf)); + boxTranslate(meshBox, x, y, z); + + if (!boxIntersect(meshBox, objBox)) + continue; + + cinfo.offset = boxPushOut(meshBox, objBox); + + bool flip = (cinfo.quadrant > 1); + + if (cinfo.quadrant & 1) { + if (abs(cinfo.offset.z) > cinfo.radius) { + cinfo.offset.z = cinfo.pos.z - p.z; + if ((cinfo.offset.x < 0 && cinfo.quadrant == 1) || (cinfo.offset.x > 0 && cinfo.quadrant == 3)) { + cinfo.type = CT_FRONT; + } + } else if (cinfo.offset.z != 0) { + cinfo.offset.x = 0; + cinfo.type = ((cinfo.offset.z > 0) ^ flip) ? CT_RIGHT : CT_LEFT; + } else { + cinfo.offset = _vec3i(0, 0, 0); + } + } else { + if (abs(cinfo.offset.x) > cinfo.radius) { + cinfo.offset.x = cinfo.pos.x - p.x; + if ((cinfo.offset.z < 0 && cinfo.quadrant == 0) || (cinfo.offset.z > 0 && cinfo.quadrant == 2)) { + cinfo.type = CT_FRONT; + } + } else if (cinfo.offset.x != 0) { + cinfo.offset.z = 0; + cinfo.type = ((cinfo.offset.x > 0) ^ flip) ? CT_LEFT : CT_RIGHT; + } else { + cinfo.offset = _vec3i(0, 0, 0); + } + } + + cinfo.staticHit = (cinfo.offset.x != 0 || cinfo.offset.z != 0); + + return true; + } + } + + return false; +} + +bool Room::checkPortal(const Portal* portal) +{ + vec3i d; + d.x = portal->v[0].x - gCameraViewPos.x + (info->x << 8); + d.y = portal->v[0].y - gCameraViewPos.y; + d.z = portal->v[0].z - gCameraViewPos.z + (info->z << 8); + + Matrix &m = matrixGet(); + + vec3i pv[4]; +/* +#ifdef __3DO__ + int32 axis = 0; + if (d.x >= 0) axis |= (2 << 0); + if (d.x < 0) axis |= (1 << 0); + if (d.y >= 0) axis |= (2 << 2); + if (d.y < 0) axis |= (1 << 2); + if (d.z >= 0) axis |= (2 << 4); + if (d.z < 0) axis |= (1 << 4); + + if (!(portal->normalMask & axis)) + return false; +#else*/ + if (DP33(portal->n.x, portal->n.y, portal->n.z, d.x, d.y, d.z) >= 0) + return false; +//#endif + + int32 x0 = clip.x1; + int32 y0 = clip.y1; + int32 x1 = clip.x0; + int32 y1 = clip.y0; + + int32 znear = 0, zfar = 0; + + for (int32 i = 0; i < 4; i++) + { + int32 px = portal->v[i].x; + int32 py = portal->v[i].y; + int32 pz = portal->v[i].z; + + int32 x = DP43(m.e00, m.e01, m.e02, m.e03, px, py, pz); + int32 y = DP43(m.e10, m.e11, m.e12, m.e13, px, py, pz); + int32 z = DP43(m.e20, m.e21, m.e22, m.e23, px, py, pz); + + pv[i].x = x; + pv[i].y = y; + pv[i].z = z; + + if (z <= 0) { + znear++; + continue; + } + + if (z > VIEW_MAX_F) + { + z = VIEW_MAX_F; + zfar++; + } + + x >>= FIXED_SHIFT; + y >>= FIXED_SHIFT; + z >>= FIXED_SHIFT; + + int32 dz = PERSPECTIVE_DZ(z); + + if (dz > 0) { + PERSPECTIVE(x, y, z); + + x += FRAME_WIDTH >> 1; + y += FRAME_HEIGHT >> 1; + } else { + x = (x < 0) ? viewport.x0 : viewport.x1; + y = (y < 0) ? viewport.y0 : viewport.y1; + } + + if (x < x0) x0 = x; + if (x > x1) x1 = x; + if (y < y0) y0 = y; + if (y > y1) y1 = y; + } + + if (znear == 4 || zfar == 4) + return false; + + if (znear) + { + vec3i *a = pv; + vec3i *b = pv + 3; + for (int32 i = 0; i < 4; i++) + { + if ((a->z < 0) ^ (b->z < 0)) + { + if (a->x < 0 && b->x < 0) { + x0 = 0; + } else if (a->x > 0 && b->x > 0) { + x1 = FRAME_WIDTH; + } else { + x0 = 0; + x1 = FRAME_WIDTH; + } + + if (a->y < 0 && b->y < 0) { + y0 = 0; + } else if (a->y > 0 && b->y > 0) { + y1 = FRAME_HEIGHT; + } else { + y0 = 0; + y1 = FRAME_HEIGHT; + } + } + b = a; + a++; + } + } + + if (x0 < clip.x0) x0 = clip.x0; + if (x1 > clip.x1) x1 = clip.x1; + if (y0 < clip.y0) y0 = clip.y0; + if (y1 > clip.y1) y1 = clip.y1; + + if (x0 >= x1 || y0 >= y1) + return false; + + Room* nextRoom = rooms + portal->roomIndex; + + if (x0 < nextRoom->clip.x0) nextRoom->clip.x0 = x0; + if (x1 > nextRoom->clip.x1) nextRoom->clip.x1 = x1; + if (y0 < nextRoom->clip.y0) nextRoom->clip.y0 = y0; + if (y1 > nextRoom->clip.y1) nextRoom->clip.y1 = y1; + + return true; +} + +Room** Room::addVisibleRoom(Room** list) +{ + matrixPush(); + matrixTranslateAbs(info->x << 8, 0, info->z << 8); + + for (int32 i = 0; i < info->portalsCount; i++) + { + const Portal* portal = data.portals + i; + + if (checkPortal(portal)) + { + Room* nextRoom = rooms + portal->roomIndex; + + list = nextRoom->addVisibleRoom(list); + + if (!nextRoom->visible) + { + nextRoom->visible = true; + *list++ = nextRoom; + } + } + } + + matrixPop(); + + return list; +} + +Room** Room::getVisibleRooms() +{ + Room** list = addVisibleRoom(roomsList); + *list++ = this; + *list++ = NULL; + + ASSERT(list - roomsList <= MAX_ROOM_LIST); + + return roomsList; +} + +void Room::reset() +{ + visible = false; + clip = RectMinMax( FRAME_WIDTH, FRAME_HEIGHT, 0, 0 ); +} + +Room** Room::addNearRoom(Room** list, int32 x, int32 y, int32 z) +{ + Room* nearRoom = getRoom(x, y, z); + + int32 count = list - roomsList; + for (int32 i = 0; i < count; i++) + { + if (roomsList[i] == nearRoom) + return list; + } + + *list++ = nearRoom; + return list; +} + +Room** Room::getNearRooms(const vec3i &pos, int32 radius, int32 height) +{ + Room** list = roomsList; + + *list++ = this; + + list = addNearRoom(list, pos.x - radius, pos.y, pos.z - radius); + list = addNearRoom(list, pos.x + radius, pos.y, pos.z - radius); + list = addNearRoom(list, pos.x + radius, pos.y, pos.z + radius); + list = addNearRoom(list, pos.x - radius, pos.y, pos.z + radius); + + list = addNearRoom(list, pos.x - radius, pos.y - height, pos.z - radius); + list = addNearRoom(list, pos.x + radius, pos.y - height, pos.z - radius); + list = addNearRoom(list, pos.x + radius, pos.y - height, pos.z + radius); + list = addNearRoom(list, pos.x - radius, pos.y - height, pos.z + radius); + + *list++ = NULL; + + return roomsList; +} + +Room** Room::getAdjRooms() +{ + Room** list = roomsList; + + *list++ = this; + for (int32 i = 0; i < info->portalsCount; i++) + { + *list++ = rooms + data.portals[i].roomIndex; + } + *list++ = NULL; + + return roomsList; +} + +void Room::modify() +{ +//#ifdef ROM_READ + if (sectors == data.sectors) + { + // convert room->sectors to mutable (non-ROM) data + sectors = dynSectors + dynSectorsCount; + memcpy((Sector*)sectors, data.sectors, info->xSectors * info->zSectors * sizeof(Sector)); + + dynSectorsCount += info->xSectors * info->zSectors; + //printf("dynSectors: %d\n", dynSectorsCount); + ASSERT(dynSectorsCount <= MAX_DYN_SECTORS); + } +//#endif +} + +void Room::add(ItemObj* item) +{ + ASSERT(item && item->nextItem == NULL); + + item->room = this; + item->nextItem = firstItem; + firstItem = item; +} + +void Room::remove(ItemObj* item) +{ + ASSERT(item && item->room == this); + + ItemObj* prev = NULL; + ItemObj* curr = firstItem; + + while (curr) + { + ItemObj* next = curr->nextItem; + + if (curr == item) + { + item->nextItem = NULL; + + if (prev) { + prev->nextItem = next; + } else { + firstItem = next; + } + + break; + } + + prev = curr; + curr = next; + } +} + +#define TRACE_SHIFT 10 // trace precision + +#define TRACE_CHECK(r, x, y, z) \ +{ \ + const Sector* sector = r->getSector(x, z); \ + if (accurate) { \ + if (y > sector->getFloor(x, y, z) || y < sector->getCeiling(x, y, z)) \ + { \ + to.pos = p; \ + to.room = room; \ + return false; \ + } \ + } else { \ + if (y > (sector->floor << 8) || y < (sector->ceiling << 8)) \ + { \ + to.pos = p; \ + to.room = room; \ + return false; \ + } \ + } \ +} + +bool traceX(const Location &from, Location &to, bool accurate) +{ + vec3i d = to.pos - from.pos; + + if (!d.x) + return true; + + d.y = (d.y << TRACE_SHIFT) / d.x; + d.z = (d.z << TRACE_SHIFT) / d.x; + + vec3i p = from.pos; + + Room* room = from.room; + + if (d.x < 0) + { + d.x = 1024; + p.x &= ~1023; + p.y += d.y * (p.x - from.pos.x) >> TRACE_SHIFT; + p.z += d.z * (p.x - from.pos.x) >> TRACE_SHIFT; + + while (p.x > to.pos.x) + { + room = room->getRoom(p.x, p.y, p.z); + TRACE_CHECK(room, p.x, p.y, p.z); + + Room* nextRoom = room->getRoom(p.x - 1, p.y, p.z); + TRACE_CHECK(nextRoom, p.x - 1, p.y, p.z); + + room = nextRoom; + p -= d; + } + } + else + { + d.x = 1024; + p.x |= 1023; + p.y += d.y * (p.x - from.pos.x) >> TRACE_SHIFT; + p.z += d.z * (p.x - from.pos.x) >> TRACE_SHIFT; + + while (p.x < to.pos.x) + { + room = room->getRoom(p.x, p.y, p.z); + TRACE_CHECK(room, p.x, p.y, p.z); + + Room* nextRoom = room->getRoom(p.x + 1, p.y, p.z); + TRACE_CHECK(nextRoom, p.x + 1, p.y, p.z); + + room = nextRoom; + p += d; + } + } + + to.room = room; + + return true; +} + +bool traceZ(const Location &from, Location &to, bool accurate) +{ + vec3i d = to.pos - from.pos; + + if (!d.z) + return true; + + d.x = (d.x << TRACE_SHIFT) / d.z; + d.y = (d.y << TRACE_SHIFT) / d.z; + + vec3i p = from.pos; + + Room* room = from.room; + + if (d.z < 0) + { + d.z = 1024; + p.z &= ~1023; + p.x += d.x * (p.z - from.pos.z) >> TRACE_SHIFT; + p.y += d.y * (p.z - from.pos.z) >> TRACE_SHIFT; + + while (p.z > to.pos.z) + { + room = room->getRoom(p.x, p.y, p.z); + TRACE_CHECK(room, p.x, p.y, p.z); + + Room* nextRoom = room->getRoom(p.x, p.y, p.z - 1); + TRACE_CHECK(nextRoom, p.x, p.y, p.z - 1); + + room = nextRoom; + p -= d; + } + } + else + { + d.z = 1024; + p.z |= 1023; + p.x += d.x * (p.z - from.pos.z) >> TRACE_SHIFT; + p.y += d.y * (p.z - from.pos.z) >> TRACE_SHIFT; + + while (p.z < to.pos.z) + { + room = room->getRoom(p.x, p.y, p.z); + TRACE_CHECK(room, p.x, p.y, p.z); + + Room* nextRoom = room->getRoom(p.x, p.y, p.z + 1); + TRACE_CHECK(nextRoom, p.x, p.y, p.z + 1); + + room = nextRoom; + p += d; + } + } + + to.room = room; + + return true; +} + +#undef TRACE_CHECK + +bool trace(const Location &from, Location &to, bool accurate) +{ + int32 dx = abs(to.pos.x - from.pos.x); + int32 dz = abs(to.pos.z - from.pos.z); + int32 dy; + + bool res; + + if (dz > dx) { + res = traceX(from, to, accurate); + if (!traceZ(from, to, accurate)) + return false; + } else { + res = traceZ(from, to, accurate); + if (!traceX(from, to, accurate)) + return false; + } + + dy = to.pos.y - from.pos.y; + + if (dy) + { + const Sector* sector = to.room->getSector(to.pos.x, to.pos.z); + + int32 h = sector->getFloor(to.pos.x, to.pos.y, to.pos.z); + if (to.pos.y <= h || from.pos.y >= h) + { + h = sector->getCeiling(to.pos.x, to.pos.y, to.pos.z); + if (to.pos.y >= h || from.pos.y <= h) + { + h = WALL; + } + } + + if (h != WALL) + { + to.pos.y = h; + h -= from.pos.y; + to.pos.x = from.pos.x + (to.pos.x - from.pos.x) * h / dy; + to.pos.z = from.pos.z + (to.pos.z - from.pos.z) * h / dy; + return false; + } + } + + return res; +} + +void checkCamera(const FloorData* fd, Camera* camera) +{ + bool lookAt = false; + + while (1) + { + uint16 triggerCmd = *fd++; + + switch (FD_ACTION(triggerCmd)) + { + case TRIGGER_ACTION_ACTIVATE_CAMERA: + { + FD_SET_END(triggerCmd, FD_END(*fd++)); + + if (FD_ARGS(triggerCmd) != camera->lastIndex) + { + camera->lookAtItem = NULL; + return; + } + + camera->index = FD_ARGS(triggerCmd); + + if (camera->timer < 0 || camera->mode == CAMERA_MODE_LOOK || camera->mode == CAMERA_MODE_COMBAT) + { + camera->timer = -1; + camera->lookAtItem = NULL; + return; + } + + camera->mode = CAMERA_MODE_FIXED; + lookAt = true; + break; + } + + case TRIGGER_ACTION_CAMERA_TARGET: + { + if (camera->mode == CAMERA_MODE_LOOK || camera->mode == CAMERA_MODE_COMBAT) + break; + + ASSERT(FD_ARGS(triggerCmd) < level.itemsCount); + camera->lookAtItem = items + FD_ARGS(triggerCmd); + break; + } + + case TRIGGER_ACTION_FLYBY: + { + FD_SET_END(triggerCmd, FD_END(*fd++)); + break; + } + } + + if (FD_END(triggerCmd)) + break; + }; + + if (!lookAt && camera->lookAtItem && camera->lookAtItem != camera->lastItem && (camera->lookAtItem->flags & ITEM_FLAG_ANIMATED)) { + camera->lookAtItem = NULL; + } +} + +void checkTrigger(const FloorData* fd, ItemObj* lara) +{ + if (!fd) + return; + + if (FD_FLOOR_TYPE(*fd) == FLOOR_TYPE_LAVA) + { + // TODO lava + + if (FD_END(*fd)) + return; + + fd++; + } + + uint16 cmd = *fd++; + uint16 data = *fd++; + + ItemObj* switchItem = NULL; + ItemObj* cameraItem = NULL; + + Camera* camera = lara ? &lara->extraL->camera : &playersExtra[0].camera; + + if (camera->mode != CAMERA_MODE_OBJECT) { + checkCamera(fd, camera); + } + + if (!lara && FD_TRIGGER_TYPE(cmd) != TRIGGER_TYPE_OBJECT) + return; + + if (lara) + { + switch (FD_TRIGGER_TYPE(cmd)) + { + case TRIGGER_TYPE_ACTIVATE: + break; + + case TRIGGER_TYPE_PAD: + case TRIGGER_TYPE_ANTIPAD: + { + if (lara->pos.y != lara->roomFloor) + return; + break; + } + + case TRIGGER_TYPE_SWITCH: + { + switchItem = items + FD_ARGS(*fd); + if (!useSwitch(switchItem, FD_TIMER(data))) + return; + fd++; + break; + } + + case TRIGGER_TYPE_KEY: + { + ItemObj* keyItem = items + FD_ARGS(*fd); + if (!useKey(keyItem, lara)) + return; + fd++; + break; + } + + case TRIGGER_TYPE_PICKUP: + { + ItemObj* pickupItem = items + FD_ARGS(*fd); + if (!usePickup(pickupItem)) + return; + fd++; + break; + } + + case TRIGGER_TYPE_OBJECT: + return; + + case TRIGGER_TYPE_COMBAT: + { + if (lara->extraL->weaponState != WEAPON_STATE_READY) + return; + break; + } + + case TRIGGER_TYPE_DUMMY: + return; + } + } + + while (1) + { + uint16 triggerCmd = *fd++; + + switch (FD_ACTION(triggerCmd)) + { + case TRIGGER_ACTION_ACTIVATE_OBJECT: + { + ASSERT(FD_ARGS(triggerCmd) < level.itemsCount); + ItemObj* item = items + FD_ARGS(triggerCmd); + + if (item->flags & ITEM_FLAG_ONCE) + break; + + item->timer = FD_TIMER(data); + if (item->timer != 1) { + item->timer *= 30; + } + + if (FD_TRIGGER_TYPE(cmd) == TRIGGER_TYPE_SWITCH) { + item->flags ^= (FD_MASK(data) << ITEM_FLAGS_MASK_SHIFT); + } else if (FD_TRIGGER_TYPE(cmd) == TRIGGER_TYPE_ANTIPAD) { + item->flags &= ~(FD_MASK(data) << ITEM_FLAGS_MASK_SHIFT); + } else { + item->flags |= (FD_MASK(data) << ITEM_FLAGS_MASK_SHIFT); + } + + if ((item->flags & ITEM_FLAG_MASK) != ITEM_FLAG_MASK) + break; + + if (FD_ONCE(data)) { + item->flags |= ITEM_FLAG_ONCE; + } + + if (item->flags & ITEM_FLAG_ACTIVE) + break; + + item->activate(); + + if (!(item->flags & ITEM_FLAG_STATUS) && (item->flags & ITEM_FLAG_ACTIVE)) { + item->flags |= ITEM_FLAG_STATUS_ACTIVE; + } + + break; + } + + case TRIGGER_ACTION_ACTIVATE_CAMERA: + { + uint16 cam = *fd++; + FD_SET_END(triggerCmd, FD_END(cam)); + + if (level.cameras[FD_ARGS(triggerCmd)].flags & FIXED_CAMERA_FLAG_ONCE) + break; + + camera->index = FD_ARGS(triggerCmd); + + if (camera->mode == CAMERA_MODE_LOOK || camera->mode == CAMERA_MODE_COMBAT) + break; + + if (FD_TRIGGER_TYPE(cmd) == TRIGGER_TYPE_COMBAT) + break; + + if (FD_TRIGGER_TYPE(cmd) == TRIGGER_TYPE_SWITCH && (switchItem->state == 1) && (FD_TIMER(data) != 0)) + break; + + if (FD_TRIGGER_TYPE(cmd) == TRIGGER_TYPE_SWITCH || camera->index != camera->lastIndex) + { + camera->timer = FD_TIMER(cam); + if (camera->timer != 1) { + camera->timer *= 30; + } + + if (FD_ONCE(cam)) { + level.cameras[camera->index].flags |= FIXED_CAMERA_FLAG_ONCE; + } + + camera->speed = (FD_SPEED(cam) << 3) + 1; + camera->mode = lara ? CAMERA_MODE_FIXED : CAMERA_MODE_OBJECT; + } + break; + } + + case TRIGGER_ACTION_FLOW: + // TODO flow + break; + + case TRIGGER_ACTION_FLIP: + // TODO flipmap + break; + + case TRIGGER_ACTION_FLIP_ON: + // TODO flipmap + break; + + case TRIGGER_ACTION_FLIP_OFF: + // TODO flipmap + break; + + case TRIGGER_ACTION_CAMERA_TARGET: + { + cameraItem = items + FD_ARGS(triggerCmd); + break; + } + + case TRIGGER_ACTION_END: + nextLevel(LevelID(gLevelID + 1)); + break; + + case TRIGGER_ACTION_SOUNDTRACK: + { + int32 track = doTutorial(lara, FD_ARGS(triggerCmd)); + + if (track == 0) break; + + uint8 &flags = gSaveGame.tracks[track]; + + if (flags & TRACK_FLAG_ONCE) + break; + + if (FD_TRIGGER_TYPE(cmd) == TRIGGER_TYPE_SWITCH) + flags ^= FD_MASK(data); + else if (FD_TRIGGER_TYPE(cmd) == TRIGGER_TYPE_ANTIPAD) + flags &= ~FD_MASK(data); + else + flags |= FD_MASK(data); + + if ((flags & TRACK_FLAG_MASK) == TRACK_FLAG_MASK) + { + if (FD_ONCE(data)) { + flags |= TRACK_FLAG_ONCE; + } + sndPlayTrack(track); + } else { + sndStopTrack(); + } + break; + } + + case TRIGGER_ACTION_EFFECT: + // TODO effect + break; + + case TRIGGER_ACTION_SECRET: + { + if (gSaveGame.secrets & (1 << FD_ARGS(triggerCmd))) + break; + gSaveGame.secrets |= (1 << FD_ARGS(triggerCmd)); + sndPlayTrack(13); + break; + } + + case TRIGGER_ACTION_CLEAR_BODIES: + break; + + case TRIGGER_ACTION_FLYBY: + FD_SET_END(triggerCmd, FD_END(*fd++)); + break; + + case TRIGGER_ACTION_CUTSCENE: + break; + } + + if (FD_END(triggerCmd)) + break; + }; + + if (cameraItem && (camera->mode == CAMERA_MODE_FIXED || camera->mode == CAMERA_MODE_OBJECT)) + { + camera->lookAtItem = cameraItem; + } +} + +#endif diff --git a/src/format.h b/src/format.h index 8465e14f..d5f9b0d1 100644 --- a/src/format.h +++ b/src/format.h @@ -3226,8 +3226,8 @@ namespace TR { #endif #ifdef _GAPI_TA - ASSERT((version & VER_TR1_PSX) == VER_TR1_PSX); - if ((version & VER_TR1_PSX) != VER_TR1_PSX) { + ASSERT((version & VER_TR1_PC) == VER_TR1_PC); + if ((version & VER_TR1_PC) != VER_TR1_PC) { return; } #endif @@ -3920,8 +3920,62 @@ namespace TR { #define CHUNK(str) ((uint64)((const char*)(str))[0] | ((uint64)((const char*)(str))[1] << 8) | ((uint64)((const char*)(str))[2] << 16) | ((uint64)((const char*)(str))[3] << 24) | \ ((uint64)((const char*)(str))[4] << 32) | ((uint64)((const char*)(str))[5] << 40) | ((uint64)((const char*)(str))[6] << 48) | ((uint64)((const char*)(str))[7] << 56)) + #define SAT_ROOMFILE 0x454C49464D4F4F52ULL /* CHUNK("ROOMFILE") */ + #define SAT_ROOMTINF 0x464E49544D4F4F52ULL /* CHUNK("ROOMTINF") */ + #define SAT_ROOMTQTR 0x525451544D4F4F52ULL /* CHUNK("ROOMTQTR") */ + #define SAT_ROOMTSUB 0x425553544D4F4F52ULL /* CHUNK("ROOMTSUB") */ + #define SAT_ROOMTPAL 0x4C4150544D4F4F52ULL /* CHUNK("ROOMTPAL") */ + #define SAT_ROOMSPAL 0x4C4150534D4F4F52ULL /* CHUNK("ROOMSPAL") */ + #define SAT_ROOMDATA 0x415441444D4F4F52ULL /* CHUNK("ROOMDATA") */ + #define SAT_ROOMNUMB 0x424D554E4D4F4F52ULL /* CHUNK("ROOMNUMB") */ + #define SAT_MESHPOS_ 0x20534F504853454DULL /* CHUNK("MESHPOS ") */ + #define SAT_MESHSIZE 0x455A49534853454DULL /* CHUNK("MESHSIZE") */ + #define SAT_DOORDATA 0x41544144524F4F44ULL /* CHUNK("DOORDATA") */ + #define SAT_FLOORDAT 0x544144524F4F4C46ULL /* CHUNK("FLOORDAT") */ + #define SAT_FLOORSIZ 0x5A4953524F4F4C46ULL /* CHUNK("FLOORSIZ") */ + #define SAT_FLORDATA 0x41544144524F4C46ULL /* CHUNK("FLORDATA") */ + #define SAT_LIGHTAMB 0x424D41544847494CULL /* CHUNK("LIGHTAMB") */ + #define SAT_RM_FLIP_ 0x2050494C465F4D52ULL /* CHUNK("RM_FLIP ") */ + #define SAT_RM_FLAGS 0x5347414C465F4D52ULL /* CHUNK("RM_FLAGS") */ + #define SAT_LIGHTSIZ 0x5A4953544847494CULL /* CHUNK("LIGHTSIZ") */ + #define SAT_CAMERAS_ 0x20534152454D4143ULL /* CHUNK("CAMERAS ") */ + #define SAT_SOUNDFX_ 0x205846444E554F53ULL /* CHUNK("SOUNDFX ") */ + #define SAT_BOXES___ 0x2020205345584F42ULL /* CHUNK("BOXES ") */ + #define SAT_OVERLAPS 0x5350414C5245564FULL /* CHUNK("OVERLAPS") */ + #define SAT_GND_ZONE 0x454E4F5A5F444E47ULL /* CHUNK("GND_ZONE") */ + #define SAT_GND_ZON2 0x324E4F5A5F444E47ULL /* CHUNK("GND_ZON2") */ + #define SAT_FLY_ZONE 0x454E4F5A5F594C46ULL /* CHUNK("FLY_ZONE") */ + #define SAT_ARANGES_ 0x205345474E415241ULL /* CHUNK("ARANGES ") */ + #define SAT_ITEMDATA 0x415441444D455449ULL /* CHUNK("ITEMDATA") */ + #define SAT_ROOMEND_ 0x20444E454D4F4F52ULL /* CHUNK("ROOMEND ") */ + #define SAD_OBJFILE_ 0x20454C49464A424FULL /* CHUNK("OBJFILE ") */ + #define SAD_ANIMS___ 0x202020534D494E41ULL /* CHUNK("ANIMS ") */ + #define SAD_CHANGES_ 0x205345474E414843ULL /* CHUNK("CHANGES ") */ + #define SAD_RANGES_z 0x00205345474E4152ULL /* CHUNK("RANGES \0") */ + #define SAD_COMMANDS 0x53444E414D4D4F43ULL /* CHUNK("COMMANDS") */ + #define SAD_ANIBONES 0x53454E4F42494E41ULL /* CHUNK("ANIBONES") */ + #define SAD_ANIMOBJ_ 0x204A424F4D494E41ULL /* CHUNK("ANIMOBJ ") */ + #define SAD_STATOBJ_ 0x204A424F54415453ULL /* CHUNK("STATOBJ ") */ + #define SAD_FRAMES__ 0x202053454D415246ULL /* CHUNK("FRAMES ") */ + #define SAD_MESHPTRS 0x535254504853454DULL /* CHUNK("MESHPTRS") */ + #define SAD_MESHDATA 0x415441444853454DULL /* CHUNK("MESHDATA") */ + #define SAD_OTEXTINF 0x464E49545845544FULL /* CHUNK("OTEXTINF") */ + #define SAD_OTEXTDAT 0x544144545845544FULL /* CHUNK("OTEXTDAT") */ + #define SAD_ITEXTINF 0x464E495458455449ULL /* CHUNK("ITEXTINF") */ + #define SAD_ITEXTDAT 0x5441445458455449ULL /* CHUNK("ITEXTDAT") */ + #define SAD_OBJEND__ 0x2020444E454A424FULL /* CHUNK("OBJEND ") */ + #define SPR_SPRFILE_ 0x20454C4946525053ULL /* CHUNK("SPRFILE ") */ + #define SPR_SPRITINF 0x464E495449525053ULL /* CHUNK("SPRITINF") */ + #define SPR_SPRITDAT 0x5441445449525053ULL /* CHUNK("SPRITDAT") */ + #define SPR_OBJECTS_ 0x20535443454A424FULL /* CHUNK("OBJECTS ") */ + #define SPR_SPRITEND 0x444E455449525053ULL /* CHUNK("SPRITEND") */ + #define SND_SAMPLUT_ 0x2054554C504D4153ULL /* CHUNK("SAMPLUT ") */ + #define SND_SAMPINFS 0x53464E49504D4153ULL /* CHUNK("SAMPINFS") */ + #define SND_SAMPLE__ 0x2020454C504D4153ULL /* CHUNK("SAMPLE ") */ + #define SND_ENDFILEz 0x00454C4946444E45ULL /* CHUNK("ENDFILE\0") */ + void readSAT(Stream &stream) { - #if !defined(_OS_PSP) && !defined(_OS_3DS) && !defined(_OS_XBOX) + #if !defined(_OS_PSP) Room *room = NULL; while (stream.pos < stream.size) { @@ -3937,18 +3991,18 @@ namespace TR { switch (chunkType) { // SAT - case CHUNK("ROOMFILE") : + case SAT_ROOMFILE : ASSERTV(stream.readBE32() == 0x00000000); ASSERTV(stream.readBE32() == 0x00000020); break; - case CHUNK("ROOMTINF") : + case SAT_ROOMTINF : ASSERTV(stream.readBE32() == 0x00000010); roomTexturesCount = stream.readBE32(); roomTextures = roomTexturesCount ? new TextureInfo[roomTexturesCount] : NULL; for (int i = 0; i < roomTexturesCount; i++) readObjectTex(stream, roomTextures[i], TEX_TYPE_ROOM); break; - case CHUNK("ROOMTQTR") : { + case SAT_ROOMTQTR : { ASSERTV(stream.readBE32() == 0x00000001); roomTexturesDataSize = stream.readBE32(); roomTexturesData = roomTexturesDataSize ? new uint8[roomTexturesDataSize] : NULL; @@ -3959,7 +4013,7 @@ namespace TR { */ break; } - case CHUNK("ROOMTSUB") : + case SAT_ROOMTSUB : ASSERTV(stream.readBE32() == 0x00000001); /* roomTexturesDataSize = stream.readBE32(); @@ -3972,34 +4026,34 @@ namespace TR { stream.raw(tsub, sizeof(uint8) * tsubCount); break; - case CHUNK("ROOMTPAL") : { + case SAT_ROOMTPAL : { ASSERTV(stream.readBE32() == 0x00000003); stream.seek(stream.readBE32() * 3); break; } - case CHUNK("ROOMSPAL") : { + case SAT_ROOMSPAL : { ASSERTV(stream.readLE32() == 0x02000000); stream.seek(stream.readBE32() * 2); break; } - case CHUNK("ROOMDATA") : + case SAT_ROOMDATA : ASSERTV(stream.readBE32() == 0x00000044); roomsCount = stream.readBE32(); rooms = new Room[roomsCount]; memset(rooms, 0, sizeof(Room) * roomsCount); break; - case CHUNK("ROOMNUMB") : + case SAT_ROOMNUMB : ASSERTV(stream.readBE32() == 0x00000000); room = &rooms[stream.readBE32()]; break; - case CHUNK("MESHPOS ") : + case SAT_MESHPOS_ : ASSERT(room); room->info.x = stream.readBE32(); room->info.z = stream.readBE32(); room->info.yBottom = stream.readBE32(); room->info.yTop = stream.readBE32(); break; - case CHUNK("MESHSIZE") : { + case SAT_MESHSIZE : { ASSERT(room); uint32 flag = stream.readBE32(); if (flag == 0x00000014) { @@ -4120,7 +4174,7 @@ namespace TR { data.fCount = fIndex; break; } - case CHUNK("DOORDATA") : { + case SAT_DOORDATA : { int32 roomIndex = stream.readBE32(); ASSERT(roomIndex < roomsCount); Room *room = &rooms[roomIndex]; @@ -4143,12 +4197,12 @@ namespace TR { } break; } - case CHUNK("FLOORDAT") : + case SAT_FLOORDAT : ASSERT(room); room->zSectors = stream.readBE32(); room->xSectors = stream.readBE32(); break; - case CHUNK("FLOORSIZ") : { + case SAT_FLOORSIZ : { ASSERTV(stream.readBE32() == 0x00000008); ASSERT(room && room->sectors == NULL); @@ -4169,7 +4223,7 @@ namespace TR { } break; } - case CHUNK("FLORDATA") : + case SAT_FLORDATA : ASSERTV(stream.readBE32() == 0x00000002); ASSERT(floors == NULL); floorsCount = stream.readBE32(); @@ -4177,25 +4231,25 @@ namespace TR { for (int i = 0; i < floorsCount; i++) floors[i].value = stream.readBE16(); break; - case CHUNK("LIGHTAMB") : + case SAT_LIGHTAMB : ASSERT(room); room->ambient = stream.readBE32(); room->ambient2 = stream.readBE32(); break; - case CHUNK("RM_FLIP ") : { + case SAT_RM_FLIP_ : { ASSERTV(stream.readBE32() == 0x00000002); uint32 value = stream.readBE32(); room->alternateRoom = value == 0xFFFFFFFF ? -1 : value; break; } - case CHUNK("RM_FLAGS") : { + case SAT_RM_FLAGS : { ASSERT(room); ASSERTV(stream.readBE32() == 0x00000002); uint32 value = stream.readBE32(); room->flags.water = (value & 0x01) != 0; break; } - case CHUNK("LIGHTSIZ") : { + case SAT_LIGHTSIZ : { ASSERTV(stream.readBE32() == 0x00000014); ASSERT(room && room->lights == NULL); room->lightsCount = stream.readBE32(); @@ -4217,7 +4271,7 @@ namespace TR { } break; } - case CHUNK("CAMERAS ") : + case SAT_CAMERAS_ : ASSERTV(stream.readBE32() == 0x00000010); ASSERT(cameras == NULL); camerasCount = stream.readBE32(); @@ -4231,7 +4285,7 @@ namespace TR { cam.flags.boxIndex = stream.readBE16(); } break; - case CHUNK("SOUNDFX ") : { + case SAT_SOUNDFX_ : { uint32 flag = stream.readBE32(); if (flag == 0x00000000) { // SND ASSERTV(stream.readBE32() == 0x00000000); @@ -4265,7 +4319,7 @@ namespace TR { } break; } - case CHUNK("BOXES ") : + case SAT_BOXES___ : ASSERTV(stream.readBE32() == 0x00000014); ASSERT(boxes == NULL); boxesCount = stream.readBE32(); @@ -4280,7 +4334,7 @@ namespace TR { b.overlap.value = stream.readBE16(); } break; - case CHUNK("OVERLAPS") : + case SAT_OVERLAPS : ASSERTV(stream.readBE32() == 0x00000002); ASSERT(overlaps == NULL); overlapsCount = stream.readBE32(); @@ -4288,16 +4342,16 @@ namespace TR { for (int i = 0; i < overlapsCount; i++) overlaps[i].value = stream.readBE16(); break; - case CHUNK("GND_ZONE") : - case CHUNK("GND_ZON2") : - case CHUNK("FLY_ZONE") : { + case SAT_GND_ZONE : + case SAT_GND_ZON2 : + case SAT_FLY_ZONE : { ASSERTV(stream.readBE32() == 0x00000002); uint16 **ptr; switch (chunkType) { - case CHUNK("GND_ZONE") : ptr = zones[0].ground1 ? &zones[1].ground1 : &zones[0].ground1; break; - case CHUNK("GND_ZON2") : ptr = zones[0].ground2 ? &zones[1].ground2 : &zones[0].ground2; break; - case CHUNK("FLY_ZONE") : ptr = zones[0].fly ? &zones[1].fly : &zones[0].fly; break; + case SAT_GND_ZONE : ptr = zones[0].ground1 ? &zones[1].ground1 : &zones[0].ground1; break; + case SAT_GND_ZON2 : ptr = zones[0].ground2 ? &zones[1].ground2 : &zones[0].ground2; break; + case SAT_FLY_ZONE : ptr = zones[0].fly ? &zones[1].fly : &zones[0].fly; break; default : ptr = NULL; } @@ -4310,7 +4364,7 @@ namespace TR { (*ptr)[i] = stream.readBE16(); break; } - case CHUNK("ARANGES ") : { + case SAT_ARANGES_ : { ASSERTV(stream.readBE32() == 0x00000008); animTexturesCount = stream.readBE32(); animTextures = new AnimTexture[animTexturesCount]; @@ -4325,7 +4379,7 @@ namespace TR { } break; } - case CHUNK("ITEMDATA") : { + case SAT_ITEMDATA : { ASSERTV(stream.readBE32() == 0x00000014); entitiesBaseCount = stream.readBE32(); entitiesCount = entitiesBaseCount + MAX_RESERVED_ENTITIES; @@ -4344,16 +4398,16 @@ namespace TR { } break; } - case CHUNK("ROOMEND ") : + case SAT_ROOMEND_ : ASSERTV(stream.readBE32() == 0x00000000); ASSERTV(stream.readBE32() == 0x00000000); break; // SAD - case CHUNK("OBJFILE ") : + case SAD_OBJFILE_ : ASSERTV(stream.readBE32() == 0x00000000); ASSERTV(stream.readBE32() == 0x00000020); break; - case CHUNK("ANIMS ") : + case SAD_ANIMS___ : ASSERTV(stream.readBE32() == 0x00000022); ASSERT(anims == NULL); animsCount = stream.readBE32(); @@ -4378,7 +4432,7 @@ namespace TR { anim.animCommand = stream.readBE16(); } break; - case CHUNK("CHANGES ") : + case SAD_CHANGES_ : ASSERTV(stream.readBE32() == 0x00000008); ASSERT(states == NULL); statesCount = stream.readBE32(); @@ -4391,7 +4445,7 @@ namespace TR { ASSERTV(stream.readBE16() == state.rangesOffset); // dummy } break; - case CHUNK("RANGES \0") : + case SAD_RANGES_z : ASSERTV(stream.readBE32() == 0x00000008); ASSERT(ranges == NULL); rangesCount = stream.readBE32(); @@ -4404,7 +4458,7 @@ namespace TR { range.nextFrame = stream.readBE16(); } break; - case CHUNK("COMMANDS") : + case SAD_COMMANDS : ASSERTV(stream.readBE32() == 0x00000002); ASSERT(commands == NULL); commandsCount = stream.readBE32(); @@ -4412,7 +4466,7 @@ namespace TR { for (int i = 0; i < commandsCount; i++) commands[i] = stream.readBE16(); break; - case CHUNK("ANIBONES") : + case SAD_ANIBONES : ASSERTV(stream.readBE32() == 0x00000004); ASSERT(nodesData == NULL); nodesDataSize = stream.readBE32(); @@ -4420,7 +4474,7 @@ namespace TR { for (int i = 0; i < nodesDataSize; i++) nodesData[i] = stream.readBE32(); break; - case CHUNK("ANIMOBJ ") : + case SAD_ANIMOBJ_ : ASSERTV(stream.readBE32() == 0x00000038); ASSERT(models == NULL); modelsCount = stream.readBE32(); @@ -4438,7 +4492,7 @@ namespace TR { ASSERTV(stream.readBE16() == model.animation); } break; - case CHUNK("STATOBJ ") : + case SAD_STATOBJ_ : ASSERTV(stream.readBE32() == 0x00000020); ASSERT(staticMeshes == NULL); staticMeshesCount = stream.readBE32(); @@ -4462,7 +4516,7 @@ namespace TR { mesh.flags = stream.readBE16(); } break; - case CHUNK("FRAMES ") : + case SAD_FRAMES__ : ASSERTV(stream.readBE32() == 0x00000002); ASSERT(frameData == NULL); frameDataSize = stream.readBE32(); @@ -4470,7 +4524,7 @@ namespace TR { for (int i = 0; i < frameDataSize; i++) frameData[i] = stream.readBE16(); break; - case CHUNK("MESHPTRS") : + case SAD_MESHPTRS : ASSERTV(stream.readBE32() == 0x00000004); ASSERT(meshOffsets == NULL); meshOffsetsCount = stream.readBE32(); @@ -4478,14 +4532,14 @@ namespace TR { for (int i = 0; i < meshOffsetsCount; i++) meshOffsets[i] = stream.readBE32(); break; - case CHUNK("MESHDATA") : + case SAD_MESHDATA : ASSERTV(stream.readBE32() == 0x00000002); ASSERT(meshData == NULL); meshDataSize = stream.readBE32(); meshData = meshDataSize ? new uint16[meshDataSize] : NULL; stream.raw(meshData, sizeof(uint16) * meshDataSize); break; - case CHUNK("OTEXTINF") : + case SAD_OTEXTINF : ASSERTV(stream.readBE32() == 0x00000010); ASSERT(objectTextures == NULL); objectTexturesCount = stream.readBE32(); @@ -4495,14 +4549,14 @@ namespace TR { objectTexturesBaseCount = objectTexturesCount; expandObjectTex(objectTextures, objectTexturesCount); break; - case CHUNK("OTEXTDAT") : { + case SAD_OTEXTDAT : { ASSERTV(stream.readBE32() == 0x00000001); objectTexturesDataSize = stream.readBE32(); objectTexturesData = objectTexturesDataSize ? new uint8[objectTexturesDataSize] : NULL; stream.raw(objectTexturesData, objectTexturesDataSize); break; } - case CHUNK("ITEXTINF") : { + case SAD_ITEXTINF : { ASSERTV(stream.readBE32() == 0x00000014); itemTexturesCount = stream.readBE32(); itemTextures = itemTexturesCount ? new TextureInfo[itemTexturesCount * 5] : NULL; @@ -4512,23 +4566,23 @@ namespace TR { expandObjectTex(itemTextures, itemTexturesCount); break; } - case CHUNK("ITEXTDAT") : { + case SAD_ITEXTDAT : { ASSERTV(stream.readBE32() == 0x00000001); itemTexturesDataSize = stream.readBE32(); itemTexturesData = itemTexturesDataSize ? new uint8[itemTexturesDataSize] : NULL; stream.raw(itemTexturesData, itemTexturesDataSize); break; } - case CHUNK("OBJEND ") : + case SAD_OBJEND__ : ASSERTV(stream.readBE32() == 0x00000000); ASSERTV(stream.readBE32() == 0x00000000); break; // SPR - case CHUNK("SPRFILE ") : + case SPR_SPRFILE_ : ASSERTV(stream.readBE32() == 0x00000000); ASSERTV(stream.readBE32() == 0x00000020); break; - case CHUNK("SPRITINF") : { + case SPR_SPRITINF : { ASSERTV(stream.readBE32() == 0x00000010); spriteTexturesCount = stream.readBE32(); spriteTextures = spriteTexturesCount ? new TextureInfo[spriteTexturesCount] : NULL; @@ -4536,14 +4590,14 @@ namespace TR { readSpriteTex(stream, spriteTextures[i]); break; } - case CHUNK("SPRITDAT") : { + case SPR_SPRITDAT : { ASSERTV(stream.readBE32() == 0x00000001); spriteTexturesDataSize = stream.readBE32(); spriteTexturesData = spriteTexturesDataSize ? new uint8[spriteTexturesDataSize] : NULL; stream.raw(spriteTexturesData, spriteTexturesDataSize); break; } - case CHUNK("OBJECTS ") : { + case SPR_OBJECTS_ : { ASSERTV(stream.readBE32() == 0x00000000); spriteSequencesCount = stream.readBE32(); spriteSequences = spriteSequencesCount ? new SpriteSequence[spriteSequencesCount] : NULL; @@ -4557,12 +4611,12 @@ namespace TR { } break; } - case CHUNK("SPRITEND") : + case SPR_SPRITEND : ASSERTV(stream.readBE32() == 0x00000000); ASSERTV(stream.readBE32() == 0x00000000); break; // SND - case CHUNK("SAMPLUT ") : { + case SND_SAMPLUT_ : { ASSERTV(stream.readBE32() == 0x00000002); int count = stream.readBE32(); soundsMap = new int16[count]; @@ -4570,7 +4624,7 @@ namespace TR { soundsMap[i] = stream.readBE16(); break; } - case CHUNK("SAMPINFS") : { + case SND_SAMPINFS : { ASSERTV(stream.readBE32() == 0x00000008); soundsInfoCount = stream.readBE32(); soundsInfo = soundsInfoCount ? new SoundInfo[soundsInfoCount] : NULL; @@ -4585,7 +4639,7 @@ namespace TR { } break; } - case CHUNK("SAMPLE ") : { + case SND_SAMPLE__ : { int32 index = stream.readBE32(); int32 size = stream.readBE32(); ASSERT(index < soundOffsetsCount); @@ -4595,7 +4649,7 @@ namespace TR { stream.seek(size); break; } - case CHUNK("ENDFILE\0") : + case SND_ENDFILEz : ASSERTV(stream.readBE32() == 0x00000000); ASSERTV(stream.readBE32() == 0x00000000); break; @@ -4653,7 +4707,7 @@ namespace TR { void prepare() { if (version == VER_TR1_PC) { - // Amiga -> PC color palette for TR1 PC + // DOS 6-bit -> 8-bit per component ASSERT(palette); Color24 *c = palette; for (int i = 0; i < 256; i++) { @@ -5852,10 +5906,13 @@ namespace TR { break; } case VER_TR3_PSX : { + mesh.tCount = 0; + mesh.rCount = 0; + mesh.fCount = 0; + if (!mesh.vCount) { mesh.vertices = NULL; mesh.faces = NULL; - mesh.tCount = mesh.rCount = mesh.fCount = 0; break; } @@ -6251,7 +6308,7 @@ namespace TR { for (int j = 0; j < animTex.count; j++) animTex.textures[j] = *(ptr++); } - delete animTexBlock; + delete[] animTexBlock; } if (version & (VER_TR4 | VER_TR5)) { diff --git a/src/game.h b/src/game.h index 9659d345..4c4c60dc 100644 --- a/src/game.h +++ b/src/game.h @@ -14,29 +14,43 @@ namespace Game { Level *level; Stream *nextLevel; - ControlKey cheatSeq[MAX_CHEAT_SEQUENCE]; + ControlKey cheatSeq[MAX_PLAYERS][MAX_CHEAT_SEQUENCE]; + + void cheatControl(int32 playerIndex) { + ControlKey key = Input::lastState[playerIndex]; - void cheatControl(ControlKey key) { if (key == cMAX || !level || level->level.isTitle() || level->level.isCutsceneLevel()) return; - const ControlKey CHEAT_ALL_WEAPONS[] = { cLook, cWeapon, cDash, cDuck, cDuck, cDash, cRoll, cLook }; - const ControlKey CHEAT_SKIP_LEVEL[] = { cDuck, cDash, cLook, cRoll, cWeapon, cLook, cDash, cDuck }; - const ControlKey CHEAT_DOZY_MODE[] = { cWalk, cLook, cWalk, cLook, cWalk, cLook, cWalk, cLook }; + const ControlKey CHEAT_ALL_WEAPONS_1[] = { cLook, cWeapon, cDash, cDuck, cDuck, cDash, cRoll, cLook }; + const ControlKey CHEAT_ALL_WEAPONS_2[] = { cWeapon, cLook, cWeapon, cLook, cWeapon, cLook, cWeapon, cLook }; + + const ControlKey CHEAT_SKIP_LEVEL_1[] = { cDuck, cDash, cLook, cRoll, cWeapon, cLook, cDash, cDuck }; + const ControlKey CHEAT_SKIP_LEVEL_2[] = { cJump, cLook, cJump, cLook, cJump, cLook, cJump, cLook }; + + const ControlKey CHEAT_DOZY_MODE[] = { cWalk, cLook, cWalk, cLook, cWalk, cLook, cWalk, cLook }; for (int i = 0; i < MAX_CHEAT_SEQUENCE - 1; i++) - cheatSeq[i] = cheatSeq[i + 1]; - cheatSeq[MAX_CHEAT_SEQUENCE - 1] = key; + cheatSeq[playerIndex][i] = cheatSeq[playerIndex][i + 1]; + cheatSeq[playerIndex][MAX_CHEAT_SEQUENCE - 1] = key; + + #define CHECK_CHEAT(seq) (!memcmp(&cheatSeq[playerIndex][MAX_CHEAT_SEQUENCE - COUNT(seq)], seq, sizeof(seq))) // add all weapons - if (!memcmp(&cheatSeq[MAX_CHEAT_SEQUENCE - COUNT(CHEAT_ALL_WEAPONS)], CHEAT_ALL_WEAPONS, sizeof(CHEAT_ALL_WEAPONS))) { + if (CHECK_CHEAT(CHEAT_ALL_WEAPONS_1) || CHECK_CHEAT(CHEAT_ALL_WEAPONS_2)) + { inventory->addWeapons(); level->playSound(TR::SND_SCREAM); } + // skip level - if (!memcmp(&cheatSeq[MAX_CHEAT_SEQUENCE - COUNT(CHEAT_SKIP_LEVEL)], CHEAT_SKIP_LEVEL, sizeof(CHEAT_SKIP_LEVEL))) + if (CHECK_CHEAT(CHEAT_SKIP_LEVEL_1) || CHECK_CHEAT(CHEAT_SKIP_LEVEL_2)) + { level->loadNextLevel(); + } + // dozy mode - if (!memcmp(&cheatSeq[MAX_CHEAT_SEQUENCE - COUNT(CHEAT_DOZY_MODE)], CHEAT_DOZY_MODE, sizeof(CHEAT_DOZY_MODE))) { - Lara *lara = (Lara*)level->getLara(0); + if (CHECK_CHEAT(CHEAT_DOZY_MODE)) + { + Lara *lara = (Lara*)level->getLara(playerIndex); if (lara) { lara->setDozy(true); } @@ -97,7 +111,7 @@ void loadSettings(Stream *stream, void *userData) { Core::settings.version = SETTINGS_VERSION; Core::setVSync(Core::settings.detail.vsync != 0); - #if defined(_GAPI_SW) || defined(_GAPI_GU) || defined(_GAPI_TA) + #if defined(_GAPI_SW) || defined(_GAPI_GU) Core::settings.detail.filter = Core::Settings::LOW; Core::settings.detail.lighting = Core::Settings::LOW; Core::settings.detail.shadows = Core::Settings::LOW; @@ -196,7 +210,12 @@ namespace Game { Input::update(); Network::update(); - cheatControl(Input::lastState[0]); + for (int32 i = 0; i < MAX_PLAYERS; i++) + { + if (level->players[i]) { + cheatControl(i); + } + } if (!level->level.isTitle()) { if (Input::lastState[0] == cStart) level->addPlayer(0); diff --git a/src/gameflow.h b/src/gameflow.h index f651e012..5eab3e94 100644 --- a/src/gameflow.h +++ b/src/gameflow.h @@ -189,8 +189,8 @@ namespace TR { TRACK_TR1_CAVES = 5, TRACK_TR1_SECRET = 13, TRACK_TR1_CISTERN = 57, - TRACK_TR1_EGYPT = 58, - TRACK_TR1_MINE = 59, + TRACK_TR1_WIND = 58, + TRACK_TR1_PYRAMID = 59, TRACK_TR1_CUT_1 = 23, TRACK_TR1_CUT_2 = 25, TRACK_TR1_CUT_3 = 24, @@ -267,24 +267,24 @@ namespace TR { { "LEVEL3A" , STR_TR1_LEVEL3A , TRACK_TR1_CAVES , 5 }, { "LEVEL3B" , STR_TR1_LEVEL3B , TRACK_TR1_CAVES , 3 }, { "CUT1" , STR_EMPTY , TRACK_TR1_CUT_1 , 0 }, - { "LEVEL4" , STR_TR1_LEVEL4 , TRACK_TR1_CAVES , 4 }, - { "LEVEL5" , STR_TR1_LEVEL5 , TRACK_TR1_CAVES , 3 }, - { "LEVEL6" , STR_TR1_LEVEL6 , TRACK_TR1_CAVES , 3 }, + { "LEVEL4" , STR_TR1_LEVEL4 , TRACK_TR1_WIND , 4 }, + { "LEVEL5" , STR_TR1_LEVEL5 , TRACK_TR1_WIND , 3 }, + { "LEVEL6" , STR_TR1_LEVEL6 , TRACK_TR1_WIND , 3 }, { "LEVEL7A" , STR_TR1_LEVEL7A , TRACK_TR1_CISTERN , 3 }, { "LEVEL7B" , STR_TR1_LEVEL7B , TRACK_TR1_CISTERN , 2 }, { "CUT2" , STR_EMPTY , TRACK_TR1_CUT_2 , 0 }, - { "LEVEL8A" , STR_TR1_LEVEL8A , TRACK_TR1_EGYPT , 3 }, - { "LEVEL8B" , STR_TR1_LEVEL8B , TRACK_TR1_EGYPT , 3 }, - { "LEVEL8C" , STR_TR1_LEVEL8C , TRACK_TR1_EGYPT , 1 }, - { "LEVEL10A" , STR_TR1_LEVEL10A , TRACK_TR1_MINE , 3 }, + { "LEVEL8A" , STR_TR1_LEVEL8A , TRACK_TR1_WIND , 3 }, + { "LEVEL8B" , STR_TR1_LEVEL8B , TRACK_TR1_WIND , 3 }, + { "LEVEL8C" , STR_TR1_LEVEL8C , TRACK_TR1_WIND , 1 }, + { "LEVEL10A" , STR_TR1_LEVEL10A , TRACK_TR1_CISTERN , 3 }, { "CUT3" , STR_EMPTY , TRACK_TR1_CUT_3 , 0 }, - { "LEVEL10B" , STR_TR1_LEVEL10B , TRACK_TR1_MINE , 3 }, + { "LEVEL10B" , STR_TR1_LEVEL10B , TRACK_TR1_PYRAMID , 3 }, { "CUT4" , STR_EMPTY , TRACK_TR1_CUT_4 , 0 }, - { "LEVEL10C" , STR_TR1_LEVEL10C , TRACK_TR1_MINE , 3 }, - { "EGYPT" , STR_TR1_EGYPT , TRACK_TR1_EGYPT , 3 }, - { "CAT" , STR_TR1_CAT , TRACK_TR1_EGYPT , 4 }, - { "END" , STR_TR1_END , TRACK_TR1_EGYPT , 2 }, - { "END2" , STR_TR1_END2 , TRACK_TR1_EGYPT , 1 }, + { "LEVEL10C" , STR_TR1_LEVEL10C , TRACK_TR1_PYRAMID , 3 }, + { "EGYPT" , STR_TR1_EGYPT , TRACK_TR1_WIND , 3 }, + { "CAT" , STR_TR1_CAT , TRACK_TR1_WIND , 4 }, + { "END" , STR_TR1_END , TRACK_TR1_WIND , 2 }, + { "END2" , STR_TR1_END2 , TRACK_TR1_WIND , 1 }, // TR2 { "TITLE" , STR_EMPTY , TRACK_TR2_TITLE , 0 }, { "ASSAULT" , STR_TR2_ASSAULT , NO_TRACK , 0 }, @@ -1087,7 +1087,7 @@ namespace TR { id == LVL_TR2_CUT_4 || id == LVL_TR2_XIAN || id == LVL_TR2_HOUSE) { char buf[64]; strcpy(buf, LEVEL_INFO[id].name); - String::toLower(buf); + StrUtils::toLower(buf); sprintf(dst, "DATA/%s.TR2", buf); } else if (id == LVL_TR2_TITLE) { sprintf(dst, "DATA/%s.tr2", LEVEL_INFO[id].name); @@ -1098,7 +1098,7 @@ namespace TR { } if (Stream::existsContent(dst)) break; strcpy(dst, LEVEL_INFO[id].name); - String::toLower(dst); + StrUtils::toLower(dst); strcat(dst, ".TR2"); break; } @@ -1277,13 +1277,8 @@ namespace TR { bool checkWebDub(Version version, int track) { if (getSubs(version, track) != STR_EMPTY) { int lang = Core::settings.audio.language + STR_LANG_EN; -#ifdef __DC__ - return lang == STR_LANG_EN || lang == STR_LANG_DE || lang == STR_LANG_FR; - } -#else return lang == STR_LANG_EN || lang == STR_LANG_DE || lang == STR_LANG_FR || lang == STR_LANG_RU || lang == STR_LANG_JA; } -#endif return false; } diff --git a/src/gapi/c3d.h b/src/gapi/c3d.h index 809ec1bd..c0daea5d 100644 --- a/src/gapi/c3d.h +++ b/src/gapi/c3d.h @@ -84,6 +84,11 @@ namespace GAPI { #include "compose_room_shbin.h" #include "compose_entity_shbin.h" #include "compose_mirror_shbin.h" + #include "compose_sprite_u_shbin.h" + #include "compose_room_u_shbin.h" + #include "compose_entity_u_shbin.h" + #include "ambient_sprite_shbin.h" + #include "ambient_room_shbin.h" #include "shadow_entity_shbin.h" #include "filter_upscale_shbin.h" #include "gui_shbin.h" @@ -91,15 +96,20 @@ namespace GAPI { } #define SHADERS_LIST(E) \ - E( compose_sprite ) \ - E( compose_flash ) \ - E( compose_room ) \ - E( compose_entity ) \ - E( compose_mirror ) \ - E( shadow_entity ) \ - E( filter_upscale ) \ - E( gui ) \ - E( dummy ) + E( compose_sprite ) \ + E( compose_flash ) \ + E( compose_room ) \ + E( compose_entity ) \ + E( compose_mirror ) \ + E( compose_sprite_u ) \ + E( compose_room_u ) \ + E( compose_entity_u ) \ + E( ambient_sprite ) \ + E( ambient_room ) \ + E( shadow_entity ) \ + E( filter_upscale ) \ + E( gui ) \ + E( dummy ) #define SHADER_DECL(v) DVLB_s* v; #define SHADER_INIT(v) v = DVLB_ParseFile((u32*)v##_shbin, v##_shbin_size); @@ -124,6 +134,14 @@ namespace GAPI { SHADERS_LIST(SHADER_DECL); + struct FogLUT { + vec4 params; + uint32 color; + C3D_FogLut table; + } fogLUT[3]; + + vec4 fogParams; + struct Shader { shaderProgram_s program; C3D_TexEnv env[4]; @@ -141,14 +159,46 @@ namespace GAPI { DVLB_s* src = NULL; + bool underwater = false; + bool grayscale = false; + + for (int i = 0; i < defCount; i++) { + if (def[i] == SD_UNDERWATER) { + underwater = true; + } + if (def[i] == SD_FILTER_GRAYSCALE) { + grayscale = true; + } + } + switch (pass) { case Core::passCompose : + if (underwater) { + switch (type) { + case 0 : src = compose_sprite_u; break; + case 1 : src = compose_flash; break; + case 2 : src = compose_room_u; break; + case 3 : src = compose_entity_u; break; + case 4 : src = compose_mirror; break; + default : src = dummy; + } + } else { + switch (type) { + case 0 : src = compose_sprite; break; + case 1 : src = compose_flash; break; + case 2 : src = compose_room; break; + case 3 : src = compose_entity; break; + case 4 : src = compose_mirror; break; + default : src = dummy; + } + } + break; + case Core::passAmbient : switch (type) { - case 0 : src = compose_sprite; break; - case 1 : src = compose_flash; break; - case 2 : src = compose_room; break; - case 3 : src = compose_entity; break; - case 4 : src = compose_mirror; break; + case 0 : src = ambient_sprite; break; + case 1 : src = ambient_room; break; + case 2 : src = ambient_room; break; + default : src = dummy; } break; case Core::passShadow : src = shadow_entity; break; @@ -159,18 +209,6 @@ namespace GAPI { shaderProgramSetVsh(&program, &src->DVLE[0]); - bool underwater = false; - bool grayscale = false; - - for (int i = 0; i < defCount; i++) { - if (def[i] == SD_UNDERWATER) { - underwater = true; - } - if (def[i] == SD_FILTER_GRAYSCALE) { - grayscale = true; - } - } - for (int ut = 0; ut < uMAX; ut++) { uID[ut] = shaderInstanceGetUniformLocation(program.vertexShader, UniformName[ut]); } @@ -184,21 +222,16 @@ namespace GAPI { C3D_TexEnv *e = env; - GPU_TEVSRC texSrc = GPU_TEXTURE1; - if (src == compose_mirror) { - texSrc = GPU_TEXTURE0; - } - { // texture * vertex color - C3D_TexEnvSrc(e, C3D_Both, texSrc, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR); + C3D_TexEnvSrc(e, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR); C3D_TexEnvFunc(e, C3D_Both, GPU_MODULATE); - if (pass == Core::passCompose) { - C3D_TexEnvScale(e, C3D_RGB, GPU_TEVSCALE_2); + if (pass == Core::passCompose || pass == Core::passAmbient) { + C3D_TexEnvScale(e, C3D_Both, GPU_TEVSCALE_4); } e++; } - if (underwater) { // multiply by underwater color + if (pass == Core::passAmbient && underwater) { // multiply by underwater color only for ambient pass C3D_TexEnvSrc(e, C3D_Both, GPU_PREVIOUS, GPU_CONSTANT, GPU_PRIMARY_COLOR); C3D_TexEnvFunc(e, C3D_Both, GPU_MODULATE); C3D_TexEnvColor(e, 0xFFE5E599); @@ -428,17 +461,28 @@ namespace GAPI { FormatDesc desc = formats[fmt]; if (width < 8 || height < 8) { - LOG("texture too small %dx%d [%d %d]!\n", width, height, fmt, opt); + LOG("\ntexture too small %dx%d [%d %d]!\n\n", width, height, fmt, opt); width = 8; height = 8; data = NULL; } + void* tmpData = NULL; + if (width > 1024 || height > 1024) { - LOG("texture too large %dx%d [%d %d]!\n", width, height, fmt, opt); - width = 8; - height = 8; - data = NULL; + LOG("\ntexture too large %dx%d [%d %d]!\n", width, height, fmt, opt); + + origWidth >>= 1; + origHeight >>= 1; + width >>= 1; + height >>= 1; + + LOG("downsample to %dx%d\n\n", width, height); + + tmpData = linearAlloc(width * height * desc.bpp / 8); + downsampleImage(tmpData, data, width << 1, height << 1); + + data = tmpData; } bool isCube = (opt & OPT_CUBEMAP) != 0; @@ -467,6 +511,11 @@ namespace GAPI { ret = C3D_TexInitWithParams(&tex, &texCube, params); } + if (width != origWidth || height != origHeight) { + uint32 texSize = C3D_TexCalcTotalSize(tex.size, tex.maxLevel); + memset(tex.data, 0, texSize); + } + ASSERT(ret); mmLogVRAM(); @@ -475,6 +524,10 @@ namespace GAPI { update(data); } + if (tmpData) { + linearFree(tmpData); + } + GPU_TEXTURE_FILTER_PARAM filter = (opt & OPT_NEAREST) ? GPU_NEAREST : GPU_LINEAR; C3D_TexSetFilter(&tex, filter, filter); C3D_TexSetFilterMipmap(&tex, filter); @@ -557,11 +610,7 @@ namespace GAPI { void bind(int sampler) { if (opt & OPT_PROXY) return; - if (sampler == sEnvironment) { - sampler = 0; // PICA200 can fetch cubemap only from tex unit 0 - } else if (sampler == sDiffuse) { - sampler = 1; - } else { + if (sampler > 3) { return; } @@ -749,10 +798,8 @@ namespace GAPI { C3D_RenderTarget* checkRenderTarget(Texture *texture, int face, int group, GPU_DEPTHBUF depthFmt) { if (!texture->target[face].frameBuf.colorBuf) { - LOG("create RT for face:%d %dx%d\n", face, texture->width, texture->height); - C3D_FrameBuf &fb = texture->target[face].frameBuf; - fb.colorBuf = (texture->opt & OPT_CUBEMAP) ? texture->texCube.data[face] : texture->tex.data; + fb.colorBuf = (texture->opt & OPT_CUBEMAP) ? texture->tex.cube->data[face] : texture->tex.data; fb.depthBuf = getDepthBuffer(texture->width, texture->height, group, depthFmt); fb.colorFmt = GPU_COLORBUF(formats[texture->fmt].format); fb.depthFmt = depthFmt; @@ -769,6 +816,10 @@ namespace GAPI { void init() { memset(depthBuffers, 0, sizeof(depthBuffers)); + for (int i = 0; i < COUNT(fogLUT); i++) { + fogLUT[i].params = vec4(-2.0f); // initialize with some unique value + } + gfxInitDefault(); vramFree(vramAlloc(0)); // vramInit() @@ -869,6 +920,7 @@ namespace GAPI { } void resetState() { + fogParams = vec4(-1.0f); C3D_SetAttrInfo(&vertexAttribs); } @@ -975,6 +1027,39 @@ namespace GAPI { } } + void setFog(const vec4 ¶ms) { + if (fogParams == params) return; + + fogParams = params; + + int32 index; + + if (fogLUT[0].params == params) { + index = 0; + } else if (fogLUT[1].params == params) { + index = 1; + } else if (fogLUT[2].params == params) { + index = 2; + } else { + index = 2; + fogLUT[0] = fogLUT[1]; + fogLUT[1] = fogLUT[2]; + + // for some reason GPU_NO_FOG breaks depth, blend or texEnv states in some cases, so we use low density fog table (0.0f) as NO_FOG + FogLut_Exp(&fogLUT[index].table, params.w, 1.0f, 32.0f, 45.0f * 1024.0f); + fogLUT[index].params = params; + fogLUT[index].color = 0xFF000000 + | (uint32(clamp(params.x * 255.0f, 0.0f, 255.0f)) << 0) + | (uint32(clamp(params.y * 255.0f, 0.0f, 255.0f)) << 8) + | (uint32(clamp(params.z * 255.0f, 0.0f, 255.0f)) << 16); + } + + + C3D_FogGasMode(GPU_FOG, GPU_PLAIN_DENSITY, false); + C3D_FogColor(fogLUT[index].color); + C3D_FogLutBind(&fogLUT[index].table); + } + void clear(bool color, bool depth) { uint32 mask = 0; if (color) mask |= C3D_CLEAR_COLOR; @@ -982,7 +1067,7 @@ namespace GAPI { if (!mask) return; C3D_FrameSplit(0); - C3D_RenderTargetClear(curTarget, C3D_ClearBits(mask), clearColor, 0); + C3D_FrameBufClear(&curTarget->frameBuf, C3D_ClearBits(mask), clearColor, 0); } void setClearColor(const vec4 &color) { diff --git a/src/gapi/d3d11.h b/src/gapi/d3d11.h index 801a7e16..019cddf6 100644 --- a/src/gapi/d3d11.h +++ b/src/gapi/d3d11.h @@ -6,7 +6,7 @@ #define SAFE_RELEASE(P) if(P){P->Release(); P = NULL;} -#if defined(_DEBUG) || defined(PROFILE) +#if 0 //defined(_DEBUG) || defined(PROFILE) #include struct Marker { @@ -35,9 +35,19 @@ #define PROFILE_TIMING(time) #endif -extern ID3D11Device *device; -extern ID3D11DeviceContext *deviceContext; -extern IDXGISwapChain *swapChain; +#ifdef _OS_WP8 + extern Microsoft::WRL::ComPtr osDevice; + extern Microsoft::WRL::ComPtr osContext; + extern Microsoft::WRL::ComPtr osSwapChain; +#else + extern ID3D11Device *osDevice; + extern ID3D11DeviceContext *osDeviceContext; + #ifdef _OS_XB1 + extern IDXGISwapChain1 *osSwapChain; + #else + extern IDXGISwapChain *osSwapChain; + #endif +#endif namespace GAPI { using namespace Core; @@ -54,7 +64,6 @@ namespace GAPI { ID3D11RenderTargetView *defRTV; ID3D11DepthStencilView *defDSV; - ID3D11InputLayout *inputLayout; ID3D11BlendState *BS[2][bmMAX]; // [colorWrite][blendMode] ONLY two colorWrite modes are supported (A and RGBA) ID3D11RasterizerState *RS[cmMAX]; // [cullMode] @@ -105,6 +114,7 @@ namespace GAPI { struct Shader { ID3D11VertexShader *VS; ID3D11PixelShader *PS; + ID3D11InputLayout *IL; ID3D11Buffer *CB; vec4 cbMem[98 + MAX_CONTACTS]; @@ -134,7 +144,7 @@ namespace GAPI { #define SHADER_U(S,P) (underwater ? SHADER(S##_u,P) : SHADER(S,P)) #define SHADER_AU(S,P) ((underwater && alphatest) ? SHADER(S##_au,P) : (alphatest ? SHADER(S##_a,P) : SHADER_U(S,P))) - const uint8 *vSrc, *fSrc; + const uint8 *vSrc = NULL, *fSrc = NULL; switch (pass) { case passCompose : switch (type) { @@ -161,7 +171,14 @@ namespace GAPI { default : ASSERT(false); } break; - case passSky : SHADER ( gui, v ); SHADER ( gui, f ); break; // TODO + case passSky : + switch (type) { + case 0 : SHADER ( sky, v ); SHADER ( sky, f ); break; + case 1 : SHADER ( sky_clouds, v ); SHADER ( sky_clouds, f ); break; + case 2 : SHADER ( sky_azure, v ); SHADER ( sky_azure, f ); break; + default : ASSERT(false); + } + break; case passWater : switch (type) { case 0 : SHADER ( water_drop, v ); SHADER ( water_drop, f ); break; @@ -179,7 +196,7 @@ namespace GAPI { case 1 : SHADER ( filter_downsample, v ); SHADER ( filter_downsample, f ); break; case 3 : SHADER ( filter_grayscale, v ); SHADER ( filter_grayscale, f ); break; case 4 : SHADER ( filter_blur, v ); SHADER ( filter_blur, f ); break; - case 5 : SHADER ( filter_anaglyph, v ); SHADER ( filter_anaglyph, f ); break; // TODO anaglyph + case 5 : SHADER ( filter_anaglyph, v ); SHADER ( filter_anaglyph, f ); break; default : ASSERT(false); } break; @@ -192,8 +209,8 @@ namespace GAPI { #undef SHADER_AU HRESULT ret; - ret = device->CreateVertexShader ((DWORD*)vSrc, vSize, NULL, &VS); ASSERT(ret == S_OK); - ret = device->CreatePixelShader ((DWORD*)fSrc, fSize, NULL, &PS); ASSERT(ret == S_OK); + ret = osDevice->CreateVertexShader ((DWORD*)vSrc, vSize, NULL, &VS); ASSERT(ret == S_OK); + ret = osDevice->CreatePixelShader ((DWORD*)fSrc, fSize, NULL, &PS); ASSERT(ret == S_OK); const D3D11_INPUT_ELEMENT_DESC vertexDecl[] = { { "POSITION", 0, DXGI_FORMAT_R16G16B16A16_SINT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // aCoord @@ -203,7 +220,7 @@ namespace GAPI { { "COLOR", 1, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0 }, // aLight }; - ret = device->CreateInputLayout(vertexDecl, COUNT(vertexDecl), vSrc, vSize, &inputLayout); + ret = osDevice->CreateInputLayout(vertexDecl, COUNT(vertexDecl), vSrc, vSize, &IL); ASSERT(ret == S_OK); rebind = true; @@ -214,10 +231,12 @@ namespace GAPI { desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; desc.ByteWidth = sizeof(cbMem); - device->CreateBuffer(&desc, NULL, &CB); + osDevice->CreateBuffer(&desc, NULL, &CB); } void deinit() { + SAFE_RELEASE(CB); + SAFE_RELEASE(IL); SAFE_RELEASE(VS); SAFE_RELEASE(PS); } @@ -232,19 +251,19 @@ namespace GAPI { void validate() { if (rebind) { - deviceContext->IASetInputLayout(inputLayout); - deviceContext->VSSetShader(VS, NULL, 0); - deviceContext->PSSetShader(PS, NULL, 0); + osContext->IASetInputLayout(IL); + osContext->VSSetShader(VS, NULL, 0); + osContext->PSSetShader(PS, NULL, 0); rebind = false; } D3D11_MAPPED_SUBRESOURCE mapped; - deviceContext->Map(CB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + osContext->Map(CB, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); memcpy(mapped.pData, cbMem, sizeof(cbMem)); - deviceContext->Unmap(CB, 0); + osContext->Unmap(CB, 0); - deviceContext->VSSetConstantBuffers(0, 1, &CB); - deviceContext->PSSetConstantBuffers(0, 1, &CB); + osContext->VSSetConstantBuffers(0, 1, &CB); + osContext->PSSetConstantBuffers(0, 1, &CB); Core::stats.cb++; } @@ -332,11 +351,11 @@ namespace GAPI { desc.Format = formats[fmt].format; desc.Usage = D3D11_USAGE_DEFAULT; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - device->CreateTexture3D(&desc, data ? initialData : NULL, &tex3D); + osDevice->CreateTexture3D(&desc, data ? initialData : NULL, &tex3D); ASSERT(tex3D); descSRV.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; - device->CreateShaderResourceView(tex3D, &descSRV, &SRV); + osDevice->CreateShaderResourceView(tex3D, &descSRV, &SRV); } else { D3D11_TEXTURE2D_DESC desc; memset(&desc, 0, sizeof(desc)); @@ -368,7 +387,7 @@ namespace GAPI { desc.MiscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS; } - device->CreateTexture2D(&desc, data ? initialData : NULL, &tex2D); + osDevice->CreateTexture2D(&desc, data ? initialData : NULL, &tex2D); ASSERT(tex2D); if (isTarget) { @@ -377,7 +396,7 @@ namespace GAPI { memset(&descDSV, 0, sizeof(descDSV)); descDSV.Format = DXGI_FORMAT_D16_UNORM; descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; - device->CreateDepthStencilView(tex2D, &descDSV, &DSV); + osDevice->CreateDepthStencilView(tex2D, &descDSV, &DSV); ASSERT(DSV); } else { D3D11_RENDER_TARGET_VIEW_DESC descRTV; @@ -388,7 +407,7 @@ namespace GAPI { for (int i = 0; i < 6; i++) { descRTV.Texture2DArray.FirstArraySlice = i; - device->CreateRenderTargetView(tex2D, &descRTV, &RTV[i]); + osDevice->CreateRenderTargetView(tex2D, &descRTV, &RTV[i]); ASSERT(RTV[i]); if (!isCube) break; } @@ -396,7 +415,7 @@ namespace GAPI { } descSRV.ViewDimension = isCube ? D3D11_SRV_DIMENSION_TEXTURECUBE : D3D11_SRV_DIMENSION_TEXTURE2D; - device->CreateShaderResourceView(tex2D, &descSRV, &SRV); + osDevice->CreateShaderResourceView(tex2D, &descSRV, &SRV); } ASSERT(SRV); @@ -414,16 +433,16 @@ namespace GAPI { void generateMipMap() { ASSERT(SRV && tex2D); - deviceContext->GenerateMips(SRV); + osContext->GenerateMips(SRV); } void update(void *data) { ASSERT(tex2D); ASSERT(opt & OPT_DYNAMIC); D3D11_MAPPED_SUBRESOURCE mapped; - deviceContext->Map(tex2D, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + osContext->Map(tex2D, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); memcpy(mapped.pData, data, mapped.RowPitch * height); - deviceContext->Unmap(tex2D, 0); + osContext->Unmap(tex2D, 0); } void bind(int sampler) { @@ -433,10 +452,13 @@ namespace GAPI { if (Core::active.textures[sampler] != this) { Core::active.textures[sampler] = this; + #ifndef _OS_WP8 if (opt & OPT_VERTEX) { - deviceContext->VSSetShaderResources(sampler, 1, &SRV); + osContext->VSSetShaderResources(sampler, 1, &SRV); } - deviceContext->PSSetShaderResources(sampler, 1, &SRV); + #endif + + osContext->PSSetShaderResources(sampler, 1, &SRV); } } @@ -447,9 +469,9 @@ namespace GAPI { ID3D11ShaderResourceView *none = NULL; if (opt & OPT_VERTEX) { - deviceContext->VSSetShaderResources(sampler, 1, &none); + osContext->VSSetShaderResources(sampler, 1, &none); } - deviceContext->PSSetShaderResources(sampler, 1, &none); + osContext->PSSetShaderResources(sampler, 1, &none); } } @@ -484,12 +506,12 @@ namespace GAPI { desc.BindFlags = D3D11_BIND_INDEX_BUFFER; desc.ByteWidth = iCount * sizeof(Index); initData.pSysMem = indices; - device->CreateBuffer(&desc, dynamic ? NULL : &initData, &ID[0]); + osDevice->CreateBuffer(&desc, dynamic ? NULL : &initData, &ID[0]); desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; desc.ByteWidth = vCount * sizeof(Vertex); initData.pSysMem = vertices; - device->CreateBuffer(&desc, dynamic ? NULL : &initData, &ID[1]); + osDevice->CreateBuffer(&desc, dynamic ? NULL : &initData, &ID[1]); } void deinit() { @@ -503,23 +525,23 @@ namespace GAPI { D3D11_MAPPED_SUBRESOURCE mapped; if (indices && iCount) { - deviceContext->Map(ID[0], 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + osContext->Map(ID[0], 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); memcpy(mapped.pData, indices, iCount * sizeof(indices[0])); - deviceContext->Unmap(ID[0], 0); + osContext->Unmap(ID[0], 0); } if (vertices && vCount) { - deviceContext->Map(ID[1], 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + osContext->Map(ID[1], 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); memcpy(mapped.pData, vertices, vCount * sizeof(vertices[0])); - deviceContext->Unmap(ID[1], 0); + osContext->Unmap(ID[1], 0); } } void bind(const MeshRange &range) const { UINT stride = sizeof(Vertex); UINT offset = 0;//range.vStart * stride; - deviceContext->IASetIndexBuffer(ID[0], sizeof(Index) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); - deviceContext->IASetVertexBuffers(0, 1, &ID[1], &stride, &offset); + osContext->IASetIndexBuffer(ID[0], sizeof(Index) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); + osContext->IASetVertexBuffers(0, 1, &ID[1], &stride, &offset); } void initNextRange(MeshRange &range, int &aIndex) const { @@ -566,7 +588,7 @@ namespace GAPI { if (cmp) { desc.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR; - desc.ComparisonFunc = D3D11_COMPARISON_LESS; + desc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL; desc.BorderColor[0] = desc.BorderColor[1] = desc.BorderColor[2] = @@ -582,7 +604,7 @@ namespace GAPI { desc.MaxLOD = D3D11_FLOAT32_MAX; ID3D11SamplerState *sampler; - device->CreateSamplerState(&desc, &sampler); + osDevice->CreateSamplerState(&desc, &sampler); return sampler; } @@ -598,7 +620,7 @@ namespace GAPI { void init() { memset(&rtCache, 0, sizeof(rtCache)); - + /* TODO D3DADAPTER_IDENTIFIER9 adapterInfo; D3D->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &adapterInfo); @@ -624,6 +646,18 @@ namespace GAPI { support.texHalf = true; support.tex3D = true; + #ifdef _OS_WP8 + support.depthTexture = false; + support.shadowSampler = false; + support.colorFloat = true; + support.colorHalf = true; + support.texFloatLinear = true; + support.texFloat = true; + support.texHalfLinear = true; + support.texHalf = true; + support.tex3D = false; + #endif + #ifdef PROFILE support.profMarker = false; support.profTiming = false; @@ -637,7 +671,7 @@ namespace GAPI { #define BLEND_FUNC(B,S,D)\ desc.RenderTarget[0].SrcBlend = S;\ desc.RenderTarget[0].DestBlend = D;\ - device->CreateBlendState(&desc, &BS[i][B]) + osDevice->CreateBlendState(&desc, &BS[i][B]) D3D11_BLEND_DESC desc; memset(&desc, 0, sizeof(desc)); @@ -670,11 +704,11 @@ namespace GAPI { desc.DepthClipEnable = TRUE; desc.FillMode = D3D11_FILL_SOLID; desc.CullMode = D3D11_CULL_NONE; - device->CreateRasterizerState(&desc, &RS[cmNone]); + osDevice->CreateRasterizerState(&desc, &RS[cmNone]); desc.CullMode = D3D11_CULL_BACK; - device->CreateRasterizerState(&desc, &RS[cmBack]); + osDevice->CreateRasterizerState(&desc, &RS[cmBack]); desc.CullMode = D3D11_CULL_FRONT; - device->CreateRasterizerState(&desc, &RS[cmFront]); + osDevice->CreateRasterizerState(&desc, &RS[cmFront]); } // init depth stencil states @@ -689,7 +723,7 @@ namespace GAPI { desc.DepthEnable = i ? TRUE : FALSE; for (int j = 0; j < 2; j++) { desc.DepthWriteMask = j ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; - device->CreateDepthStencilState(&desc, &DS[i][j]); + osDevice->CreateDepthStencilState(&desc, &DS[i][j]); } } } @@ -710,7 +744,7 @@ namespace GAPI { desc.Usage = D3D11_USAGE_STAGING; desc.MiscFlags = 0; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - device->CreateTexture2D(&desc, NULL, &stagingPixel); + osDevice->CreateTexture2D(&desc, NULL, &stagingPixel); } void resetDevice() { @@ -756,20 +790,35 @@ namespace GAPI { mat4 ortho(float l, float r, float b, float t, float znear, float zfar) { mat4 m; m.ortho(getProjRange(), l, r, b, t, znear, zfar); + + #ifdef _OS_WP8 + m.rot90(); + #endif + return m; } mat4 perspective(float fov, float aspect, float znear, float zfar, float eye) { mat4 m; + + #ifdef _OS_WP8 + aspect = 1.0f / aspect; + #endif + m.perspective(getProjRange(), fov, aspect, znear, zfar, eye); + + #ifdef _OS_WP8 + m.rot90(); + #endif + return m; } bool beginFrame() { if (!defRTV) { ID3D11Texture2D *pBackBuffer = NULL; - swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer); - device->CreateRenderTargetView(pBackBuffer, NULL, &defRTV); + osSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer); + osDevice->CreateRenderTargetView(pBackBuffer, NULL, &defRTV); SAFE_RELEASE(pBackBuffer); } @@ -786,19 +835,19 @@ namespace GAPI { desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; ID3D11Texture2D *dsTex; - device->CreateTexture2D(&desc, NULL, &dsTex); + osDevice->CreateTexture2D(&desc, NULL, &dsTex); D3D11_DEPTH_STENCIL_VIEW_DESC descDSV; memset(&descDSV, 0, sizeof(descDSV)); descDSV.Format = desc.Format; descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; descDSV.Texture2D.MipSlice = 0; - device->CreateDepthStencilView(dsTex, &descDSV, &defDSV); + osDevice->CreateDepthStencilView(dsTex, &descDSV, &defDSV); SAFE_RELEASE(dsTex); } - deviceContext->OMSetRenderTargets(1, &defRTV, defDSV); + osContext->OMSetRenderTargets(1, &defRTV, defDSV); return true; } @@ -808,13 +857,16 @@ namespace GAPI { } void resetState() { - deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - deviceContext->RSSetState(RS[cmNone]); + osContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + osContext->RSSetState(RS[cmNone]); depthTest = depthWrite = dirtyDepthState = true; colorWrite = dirtyBlendState = true; - deviceContext->VSSetSamplers(0, COUNT(samplers), samplers); - deviceContext->PSSetSamplers(0, COUNT(samplers), samplers); + #ifndef _OS_WP8 + osContext->VSSetSamplers(0, COUNT(samplers), samplers); + #endif + + osContext->PSSetSamplers(0, COUNT(samplers), samplers); } void cacheRenderTarget(ID3D11RenderTargetView **RTV, ID3D11DepthStencilView **DSV, int width, int height) { @@ -849,8 +901,8 @@ namespace GAPI { desc.SampleDesc.Count = 1; desc.Usage = D3D11_USAGE_DEFAULT; desc.BindFlags = D3D11_BIND_RENDER_TARGET; - device->CreateTexture2D(&desc, NULL, &tex2D); - device->CreateRenderTargetView(tex2D, NULL, &rtCache.items[index].RTV); + osDevice->CreateTexture2D(&desc, NULL, &tex2D); + osDevice->CreateRenderTargetView(tex2D, NULL, &rtCache.items[index].RTV); SAFE_RELEASE(tex2D); } *RTV = rtCache.items[index].RTV; @@ -871,13 +923,13 @@ namespace GAPI { desc.SampleDesc.Count = 1; desc.Usage = D3D11_USAGE_DEFAULT; desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; - device->CreateTexture2D(&desc, NULL, &tex2D); + osDevice->CreateTexture2D(&desc, NULL, &tex2D); D3D11_DEPTH_STENCIL_VIEW_DESC descDSV; memset(&descDSV, 0, sizeof(descDSV)); descDSV.Format = DXGI_FORMAT_D16_UNORM; descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; - device->CreateDepthStencilView(tex2D, &descDSV, &rtCache.items[index].DSV); + osDevice->CreateDepthStencilView(tex2D, &descDSV, &rtCache.items[index].DSV); SAFE_RELEASE(tex2D); } *DSV = rtCache.items[index].DSV; @@ -903,7 +955,7 @@ namespace GAPI { } } - deviceContext->OMSetRenderTargets(1, &RTV, DSV); + osContext->OMSetRenderTargets(1, &RTV, DSV); Core::active.viewport = short4(0, 0, 0, 0); // forcing viewport reset } @@ -923,10 +975,10 @@ namespace GAPI { ID3D11RenderTargetView *RTV; ID3D11Resource *res; - deviceContext->OMGetRenderTargets(1, &RTV, NULL); + osContext->OMGetRenderTargets(1, &RTV, NULL); RTV->GetResource(&res); - deviceContext->CopySubresourceRegion(dst->tex2D, 0, xOffset, yOffset, 0, res, 0, &box); + osContext->CopySubresourceRegion(dst->tex2D, 0, xOffset, yOffset, 0, res, 0, &box); SAFE_RELEASE(RTV); SAFE_RELEASE(res); @@ -939,14 +991,14 @@ namespace GAPI { ID3D11RenderTargetView *RTV = NULL; ID3D11DepthStencilView *DSV = NULL; - deviceContext->OMGetRenderTargets(1, &RTV, &DSV); + osContext->OMGetRenderTargets(1, &RTV, &DSV); if (color && RTV) { - deviceContext->ClearRenderTargetView(RTV, (FLOAT*)&clearColor); + osContext->ClearRenderTargetView(RTV, (FLOAT*)&clearColor); } if (depth && DSV) { - deviceContext->ClearDepthStencilView(DSV, D3D11_CLEAR_DEPTH, 1.0, 0); + osContext->ClearDepthStencilView(DSV, D3D11_CLEAR_DEPTH, 1.0, 0); } SAFE_RELEASE(RTV); @@ -966,7 +1018,7 @@ namespace GAPI { viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; - deviceContext->RSSetViewports(1, &viewport); + osContext->RSSetViewports(1, &viewport); } void setScissor(const short4 &s) { @@ -976,7 +1028,7 @@ namespace GAPI { scissor.right = s.x + s.z; scissor.bottom = active.viewport.w - s.y; - deviceContext->RSSetScissorRects(1, &scissor); + osContext->RSSetScissorRects(1, &scissor); } void setDepthTest(bool enable) { @@ -1002,7 +1054,7 @@ namespace GAPI { case RS_CULL_BACK : cullMode = cmBack; break; case RS_CULL_FRONT : cullMode = cmFront; break; } - deviceContext->RSSetState(RS[cullMode]); + osContext->RSSetState(RS[cullMode]); } void setBlendMode(int rsMask) { @@ -1031,16 +1083,16 @@ namespace GAPI { } if (dirtyDepthState) { - deviceContext->OMSetDepthStencilState(DS[depthTest][depthWrite], 0); + osContext->OMSetDepthStencilState(DS[depthTest][depthWrite], 0); dirtyDepthState = false; } if (dirtyBlendState) { - deviceContext->OMSetBlendState(BS[colorWrite][blendMode], NULL, 0xFFFFFFFF); + osContext->OMSetBlendState(BS[colorWrite][blendMode], NULL, 0xFFFFFFFF); dirtyBlendState = false; } - deviceContext->DrawIndexed(range.iCount, range.iStart, range.vStart); + osContext->DrawIndexed(range.iCount, range.iStart, range.vStart); } vec4 copyPixel(int x, int y) { @@ -1053,13 +1105,13 @@ namespace GAPI { srcBox.back = 1; ASSERT(Core::active.target); - deviceContext->CopySubresourceRegion(stagingPixel, 0, 0, 0, 0, Core::active.target->tex2D, 0, &srcBox); + osContext->CopySubresourceRegion(stagingPixel, 0, 0, 0, 0, Core::active.target->tex2D, 0, &srcBox); D3D11_MAPPED_SUBRESOURCE res; - deviceContext->Map(stagingPixel, 0, D3D11_MAP_READ, 0, &res); + osContext->Map(stagingPixel, 0, D3D11_MAP_READ, 0, &res); ASSERT(res.pData); Color32 c = *((Color32*)res.pData); - deviceContext->Unmap(stagingPixel, 0); + osContext->Unmap(stagingPixel, 0); return vec4(float(c.r), float(c.g), float(c.b), float(c.a)) * (1.0f / 255.0f); } diff --git a/src/gapi/d3d8.h b/src/gapi/d3d8.h index 7f99f91f..6295733e 100644 --- a/src/gapi/d3d8.h +++ b/src/gapi/d3d8.h @@ -53,6 +53,7 @@ namespace GAPI { int cullMode, blendMode; uint32 clearColor; + bool isFrontCW; LPDIRECT3DSURFACE8 defRT, defDS; @@ -222,7 +223,6 @@ namespace GAPI { void setConstant(UniformType uType, const float *value, int vectors) { const Binding &b = bindings[uType]; if (b.usage | USAGE_VS) device->SetVertexShaderConstant (b.reg, value, vectors); -// if (b.usage | USAGE_PS) device->SetPixelShaderConstant (b.reg, value, vectors); } void setParam(UniformType uType, const vec4 &value, int count = 1) { @@ -302,21 +302,6 @@ namespace GAPI { if (texCube) texCube->Release(); } - VOID XBUtil_SwizzleTexture2D( D3DLOCKED_RECT* pLock, const D3DSURFACE_DESC* pDesc ) - { - DWORD dwPixelSize = XGBytesPerPixelFromFormat( pDesc->Format ); - DWORD dwTextureSize = pDesc->Width * pDesc->Height * dwPixelSize; - - BYTE* pSrcBits = new BYTE[ dwTextureSize ]; - memcpy( pSrcBits, pLock->pBits, dwTextureSize ); - - XGSwizzleRect( pSrcBits, 0, NULL, pLock->pBits, - pDesc->Width, pDesc->Height, - NULL, dwPixelSize ); - - delete[] pSrcBits; - } - void updateLevel(int32 level, void *data) { int32 bpp; switch (fmt) { @@ -525,6 +510,7 @@ namespace GAPI { void init() { memset(rtCache, 0, sizeof(rtCache)); + isFrontCW = true; D3DADAPTER_IDENTIFIER8 adapterInfo; D3D->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &adapterInfo); @@ -555,11 +541,13 @@ namespace GAPI { defRT = defDS = NULL; - const float factors[] = { + const float factors[] = { + 1.0f, -19.555555555556f, 60.444444444444f, -56.888888888889f, // uCosCoeff + 0.5f, 0.5f/PI, 0.75f, 1.0f/1024.0f, // uAngles 0.0f, 0.5f, 1.0f, 2.0f, 0.6f, 0.9f, 0.9f, 32767.0f }; - device->SetVertexShaderConstant(94, factors, 2); + device->SetVertexShaderConstant(92, factors, 4); device->SetRenderState(D3DRS_FOGTABLEMODE, D3DFOG_EXP); } @@ -710,8 +698,8 @@ namespace GAPI { } void setVSync(bool enable) { - d3dpp.FullScreen_PresentationInterval = enable ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE; - GAPI::resetDevice(); + // d3dpp.FullScreen_PresentationInterval = enable ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE; + // GAPI::resetDevice(); } void waitVBlank() {} @@ -775,12 +763,17 @@ namespace GAPI { void setCullMode(int rsMask) { cullMode = rsMask; switch (rsMask) { - case RS_CULL_BACK : device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); break; - case RS_CULL_FRONT : device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); break; + case RS_CULL_BACK : device->SetRenderState(D3DRS_CULLMODE, isFrontCW ? D3DCULL_CW : D3DCULL_CCW); break; + case RS_CULL_FRONT : device->SetRenderState(D3DRS_CULLMODE, isFrontCW ? D3DCULL_CCW : D3DCULL_CW); break; default : device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); } } + void setFrontFace(bool cw) { + isFrontCW = cw; + setCullMode(cullMode); + } + void setBlendMode(int rsMask) { blendMode = rsMask; switch (rsMask) { diff --git a/src/gapi/gl.h b/src/gapi/gl.h index 360d3163..a3e5e9cb 100644 --- a/src/gapi/gl.h +++ b/src/gapi/gl.h @@ -10,6 +10,7 @@ #ifdef _OS_WIN #include #include + #include #elif _OS_ANDROID #include @@ -66,61 +67,66 @@ #include #if defined(_GAPI_GLES) // Default in SDL2 is GLES3. If we want GLES2, pass -D_GAPI_GLES2. - #if defined (_GAPI_GLES2) // We want GLES2 on SDL2 - #include - #include - - #define GL_CLAMP_TO_BORDER 0x812D - #define GL_TEXTURE_BORDER_COLOR 0x1004 - - #define GL_TEXTURE_COMPARE_MODE 0x884C - #define GL_TEXTURE_COMPARE_FUNC 0x884D - #define GL_COMPARE_REF_TO_TEXTURE 0x884E - - #undef GL_RG - #undef GL_RG32F - #undef GL_RG16F - #undef GL_RGBA32F - #undef GL_RGBA16F - #undef GL_HALF_FLOAT - - #define GL_RG GL_RGBA - #define GL_RGBA32F GL_RGBA - #define GL_RGBA16F GL_RGBA - #define GL_RG32F GL_RGBA - #define GL_RG16F GL_RGBA - #define GL_HALF_FLOAT GL_HALF_FLOAT_OES - - #define GL_TEXTURE_WRAP_R 0 - #define GL_DEPTH_STENCIL GL_DEPTH_STENCIL_OES - #define GL_UNSIGNED_INT_24_8 GL_UNSIGNED_INT_24_8_OES - //We need this on GLES2, too. - #define GL_TEXTURE_MAX_LEVEL GL_TEXTURE_MAX_LEVEL_APPLE - - #define glTexImage3D(...) 0 - #ifndef GL_TEXTURE_3D // WUUUUUT!? - #define GL_TEXTURE_3D GL_TEXTURE_3D_OES - #endif - - #define GL_PROGRAM_BINARY_LENGTH GL_PROGRAM_BINARY_LENGTH_OES - #else // We want GLES3 on SDL2 - #include - #include - #include - #endif //GAPI_GLES2 - - // These are needed for both GLES2 and GLES3 on SDL2 - #define glGenVertexArrays(...) - #define glDeleteVertexArrays(...) - #define glBindVertexArray(...) - #define glGetProgramBinary(...) - #define glProgramBinary(...) - - #define PFNGLGENVERTEXARRAYSPROC PFNGLGENVERTEXARRAYSOESPROC - #define PFNGLDELETEVERTEXARRAYSPROC PFNGLDELETEVERTEXARRAYSOESPROC - #define PFNGLBINDVERTEXARRAYPROC PFNGLBINDVERTEXARRAYOESPROC - #define PFNGLGETPROGRAMBINARYPROC PFNGLGETPROGRAMBINARYOESPROC - #define PFNGLPROGRAMBINARYPROC PFNGLPROGRAMBINARYOESPROC + #if defined (_GAPI_GLES2) // We want GLES2 on SDL2 + #include + #include + + #define GL_CLAMP_TO_BORDER 0x812D + #define GL_TEXTURE_BORDER_COLOR 0x1004 + + #define GL_TEXTURE_COMPARE_MODE 0x884C + #define GL_TEXTURE_COMPARE_FUNC 0x884D + #define GL_COMPARE_REF_TO_TEXTURE 0x884E + + #define GL_NUM_EXTENSIONS 0x821D + + #undef GL_RG + #undef GL_RG32F + #undef GL_RG16F + #undef GL_RGBA32F + #undef GL_RGBA16F + #undef GL_HALF_FLOAT + + #define GL_RG GL_RGBA + #define GL_RGBA32F GL_RGBA + #define GL_RGBA16F GL_RGBA + #define GL_RG32F GL_RGBA + #define GL_RG16F GL_RGBA + #define GL_HALF_FLOAT GL_HALF_FLOAT_OES + + #define GL_R8 GL_R8_EXT + #define GL_RED GL_RED_EXT + + #define GL_TEXTURE_WRAP_R 0 + #define GL_DEPTH_STENCIL GL_DEPTH_STENCIL_OES + #define GL_UNSIGNED_INT_24_8 GL_UNSIGNED_INT_24_8_OES + //We need this on GLES2, too. + #define GL_TEXTURE_MAX_LEVEL GL_TEXTURE_MAX_LEVEL_APPLE + + #define glTexImage3D(...) 0 + #ifndef GL_TEXTURE_3D // WUUUUUT!? + #define GL_TEXTURE_3D GL_TEXTURE_3D_OES + #endif + + #define GL_PROGRAM_BINARY_LENGTH GL_PROGRAM_BINARY_LENGTH_OES + #else // We want GLES3 on SDL2 + #include + #include + #include + #endif //GAPI_GLES2 + + // These are needed for both GLES2 and GLES3 on SDL2 + #define glGenVertexArrays(...) + #define glDeleteVertexArrays(...) + #define glBindVertexArray(...) + #define glGetProgramBinary(...) + #define glProgramBinary(...) + + #define PFNGLGENVERTEXARRAYSPROC PFNGLGENVERTEXARRAYSOESPROC + #define PFNGLDELETEVERTEXARRAYSPROC PFNGLDELETEVERTEXARRAYSOESPROC + #define PFNGLBINDVERTEXARRAYPROC PFNGLBINDVERTEXARRAYOESPROC + #define PFNGLGETPROGRAMBINARYPROC PFNGLGETPROGRAMBINARYOESPROC + #define PFNGLPROGRAMBINARYPROC PFNGLPROGRAMBINARYOESPROC #else // We want OpenGL on SDL2, not GLES #include @@ -265,32 +271,25 @@ //#define GL_TEXTURE_COMPARE_FUNC GL_TEXTURE_COMPARE_FUNC_EXT //#define GL_COMPARE_REF_TO_TEXTURE GL_COMPARE_REF_TO_TEXTURE_EXT #else - #include #include - #include - #include + #include + #include #include - #include - #define GL_RG 0x8227 - #define GL_RG16F 0x822F - #define GL_RG32F 0x8230 - #define GL_RGBA16F 0x881A - #define GL_RGBA32F 0x8814 - #define GL_HALF_FLOAT 0x140B - - #define GL_RGB565 GL_RGBA - #define GL_TEXTURE_COMPARE_MODE 0x884C - #define GL_TEXTURE_COMPARE_FUNC 0x884D - #define GL_COMPARE_REF_TO_TEXTURE 0x884E + // In OpenGL 2.1, the 16-bit RGB565 sized internal format is unavailable + // (because macOS doesn't provide the GL_ARB_ES2_compatibility extension), + // so use a 32-bit format for renderbuffer storage on macOS. See issue 360. + #undef GL_RGB565 + #define GL_RGB565 GL_RGBA + // In compatibility mode, macOS only provides OpenGL 2.1 (no VAO), but it does + // support the Apple-specific VAO extension which is older and in all relevant + // parts 100% compatible. So use those functions instead. #define glGenVertexArrays glGenVertexArraysAPPLE #define glDeleteVertexArrays glDeleteVertexArraysAPPLE #define glBindVertexArray glBindVertexArrayAPPLE - #define GL_PROGRAM_BINARY_LENGTH 0 - #define glGetProgramBinary(...) 0 - #define glProgramBinary(...) 0 + #define GL_LUMINANCE 0x1909 #endif #elif _OS_WEB #include @@ -343,6 +342,7 @@ #ifdef _OS_WIN PFNGLTEXIMAGE3DPROC glTexImage3D; #endif + PFNGLGETSTRINGIPROC glGetStringi; // Profiling #ifdef PROFILE PFNGLOBJECTLABELPROC glObjectLabel; @@ -379,16 +379,17 @@ PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; PFNGLGETPROGRAMIVPROC glGetProgramiv; // Render to texture - PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers; - PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; - PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers; - PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer; - PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D; - PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer; - PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage; - PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus; - PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers; - PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers; + PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers; + PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; + PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers; + PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer; + PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D; + PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer; + PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage; + PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus; + PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers; + PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers; + PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetFramebufferAttachmentParameteriv; // Mesh PFNGLGENBUFFERSARBPROC glGenBuffers; PFNGLDELETEBUFFERSARBPROC glDeleteBuffers; @@ -493,6 +494,8 @@ namespace GAPI { char GLSL_HEADER_VERT[512]; char GLSL_HEADER_FRAG[512]; + bool GL_VER_3 = false; + // Shader #ifndef FFP const char SHADER_COMPOSE[] = @@ -887,6 +890,11 @@ namespace GAPI { desc.fmt = GL_RGBA; } + if (fmt == FMT_LUMINANCE && Core::support.texRG) { + desc.ifmt = GL_R8; + desc.fmt = GL_RED; + } + #ifdef _OS_WEB // fucking firefox! if (WEBGL_VERSION == 1) { if (fmt == FMT_RG_FLOAT) { @@ -1139,12 +1147,33 @@ namespace GAPI { }; Array rtCache[2]; - bool extSupport(const char *str, const char *ext) { - if (!str) return false; - return strstr(str, ext) != NULL; + + bool extSupport(const char *str) { + #if !defined(_GAPI_GLES2) && !_OS_MAC + if (glGetStringi != NULL) { + GLint count = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &count); + for (int i = 0; i < count; i++) { + const char *ext = (const char*)glGetStringi(GL_EXTENSIONS, i); + if (strstr(ext, str) != NULL) { + return true; + } + } + } else + #endif + { + const char *ext = (const char*)glGetString(GL_EXTENSIONS); + if (!ext) { + return false; + } + return strstr(ext, str) != NULL; + } + + return false; } void init() { + #ifdef _OS_ANDROID //void *libGL = dlopen("libGLESv2.so", RTLD_LAZY); #endif @@ -1170,6 +1199,8 @@ namespace GAPI { GetProcOGL(glTexImage3D); #endif + GetProcOGL(glGetStringi); + #ifdef PROFILE GetProcOGL(glObjectLabel); GetProcOGL(glPushDebugGroup); @@ -1215,6 +1246,7 @@ namespace GAPI { GetProcOGL(glCheckFramebufferStatus); GetProcOGL(glDeleteFramebuffers); GetProcOGL(glDeleteRenderbuffers); + GetProcOGL(glGetFramebufferAttachmentParameteriv); GetProcOGL(glGenBuffers); GetProcOGL(glDeleteBuffers); @@ -1239,21 +1271,6 @@ namespace GAPI { LOG("Renderer : %s\n", (char*)glGetString(GL_RENDERER)); LOG("Version : %s\n", (char*)glGetString(GL_VERSION)); - char *ext = (char*)glGetString(GL_EXTENSIONS); -/* - if (ext != NULL) { - char buf[255]; - int len = strlen(ext); - int start = 0; - for (int i = 0; i < len; i++) - if (ext[i] == ' ' || (i == len - 1)) { - memcpy(buf, &ext[start], i - start); - buf[i - start] = 0; - LOG("%s\n", buf); - start = i + 1; - } - } -*/ #ifndef FFP bool GLES3 = false; #ifdef _OS_WEB @@ -1274,19 +1291,19 @@ namespace GAPI { #endif #endif - bool _GL_EXT_shadow_samplers = extSupport(ext, "GL_EXT_shadow_samplers"); - bool _GL_ARB_shadow = extSupport(ext, "GL_ARB_shadow"); - bool _GL_OES_standard_derivatives = extSupport(ext, "GL_OES_standard_derivatives"); + bool _GL_EXT_shadow_samplers = extSupport("GL_EXT_shadow_samplers"); + bool _GL_ARB_shadow = extSupport("GL_ARB_shadow"); + bool _GL_OES_standard_derivatives = extSupport("GL_OES_standard_derivatives"); - support.shaderBinary = extSupport(ext, "_program_binary"); - support.VAO = GLES3 || extSupport(ext, "_vertex_array_object"); + support.shaderBinary = extSupport("_program_binary"); + support.VAO = GLES3 || extSupport("_vertex_array_object"); support.VBO = glGenBuffers != NULL; - support.depthTexture = GLES3 || extSupport(ext, "_depth_texture"); + support.depthTexture = GLES3 || extSupport("_depth_texture"); support.shadowSampler = _GL_EXT_shadow_samplers || _GL_ARB_shadow; - support.discardFrame = extSupport(ext, "_discard_framebuffer"); - support.texNPOT = GLES3 || extSupport(ext, "_texture_npot") || extSupport(ext, "_texture_non_power_of_two"); - support.texRG = GLES3 || extSupport(ext, "_texture_rg "); // hope that isn't last extension in string ;) - support.texMaxLevel = GLES3 || extSupport(ext, "_texture_max_level"); + support.discardFrame = extSupport("_discard_framebuffer"); + support.texNPOT = GLES3 || extSupport("_texture_npot") || extSupport("_texture_non_power_of_two"); + support.texRG = GLES3 || extSupport("_texture_rg"); + support.texMaxLevel = GLES3 || extSupport("_texture_max_level"); #ifdef _GAPI_GLES2 // TODO support.shaderBinary = false; @@ -1301,15 +1318,15 @@ namespace GAPI { support.derivatives = true; support.tex3D = glTexImage3D != NULL; #endif - support.texBorder = extSupport(ext, "_texture_border_clamp"); - support.maxAniso = extSupport(ext, "_texture_filter_anisotropic"); - support.colorFloat = extSupport(ext, "_color_buffer_float"); - support.colorHalf = extSupport(ext, "_color_buffer_half_float") || extSupport(ext, "GL_ARB_half_float_pixel"); - support.texFloatLinear = support.colorFloat || extSupport(ext, "GL_ARB_texture_float") || extSupport(ext, "_texture_float_linear"); - support.texFloat = support.texFloatLinear || extSupport(ext, "_texture_float"); - support.texHalfLinear = support.colorHalf || extSupport(ext, "GL_ARB_texture_float") || extSupport(ext, "_texture_half_float_linear") || extSupport(ext, "_color_buffer_half_float"); + support.texBorder = extSupport("_texture_border_clamp"); + support.maxAniso = extSupport("_texture_filter_anisotropic"); + support.colorFloat = extSupport("_color_buffer_float"); + support.colorHalf = extSupport("_color_buffer_half_float") || extSupport("GL_ARB_half_float_pixel"); + support.texFloatLinear = support.colorFloat || extSupport("GL_ARB_texture_float") || extSupport("_texture_float_linear"); + support.texFloat = support.texFloatLinear || extSupport("_texture_float"); + support.texHalfLinear = support.colorHalf || extSupport("GL_ARB_texture_float") || extSupport("_texture_half_float_linear") || extSupport("_color_buffer_half_float"); - support.texHalf = support.texHalfLinear || extSupport(ext, "_texture_half_float"); + support.texHalf = support.texHalfLinear || extSupport("_texture_half_float"); #ifdef SDL2_GLES support.shaderBinary = false; // TODO @@ -1318,8 +1335,8 @@ namespace GAPI { #endif #ifdef PROFILE - support.profMarker = extSupport(ext, "_KHR_debug"); - support.profTiming = extSupport(ext, "_timer_query"); + support.profMarker = extSupport("_KHR_debug"); + support.profTiming = extSupport("_timer_query"); #endif if (support.maxAniso) @@ -1366,7 +1383,6 @@ namespace GAPI { if (!GLES3) { strcat(extHeader, "#extension GL_EXT_shadow_samplers : enable\n"); } - strcat(extHeader, "#define USE_GL_EXT_shadow_samplers\n"); } #ifdef _GAPI_GLES @@ -1390,7 +1406,7 @@ namespace GAPI { "#define texture2D texture\n" "#define texture3D texture\n" "#define textureCube texture\n" - "#define shadow2DEXT texture\n" + "#define FETCH_SHADOW2D(a,b) texture(a,b)\n" "out vec4 fragColor;\n"); } else { // vertex @@ -1404,6 +1420,7 @@ namespace GAPI { strcat(GLSL_HEADER_FRAG, "precision lowp int;\n" "precision highp float;\n" "#define FRAGMENT\n" + "#define FETCH_SHADOW2D(a,b) shadow2DEXT(a,b)\n" "#define fragColor gl_FragColor\n"); } @@ -1411,14 +1428,33 @@ namespace GAPI { strcat(GLSL_HEADER_FRAG, "#define sampler2DShadow lowp sampler2DShadow\n"); } #else - // vertex - strcat(GLSL_HEADER_VERT, "#version 110\n" - "#define VERTEX\n"); - // fragment - strcat(GLSL_HEADER_FRAG, "#version 110\n"); - strcat(GLSL_HEADER_FRAG, extHeader); - strcat(GLSL_HEADER_FRAG, "#define FRAGMENT\n" - "#define fragColor gl_FragColor\n"); + if (GL_VER_3) { + strcat(GLSL_HEADER_VERT, "#version 150\n" + "#define VERTEX\n" + "#define varying out\n" + "#define attribute in\n" + "#define texture2D texture\n"); + // fragment + strcat(GLSL_HEADER_FRAG, "#version 150\n"); + strcat(GLSL_HEADER_FRAG, extHeader); + strcat(GLSL_HEADER_FRAG, "#define FRAGMENT\n" + "#define varying in\n" + "#define texture2D texture\n" + "#define texture3D texture\n" + "#define textureCube texture\n" + "#define FETCH_SHADOW2D(a,b) texture(a,b)\n" + "out vec4 fragColor;\n"); + } else { + // vertex + strcat(GLSL_HEADER_VERT, "#version 110\n" + "#define VERTEX\n"); + // fragment + strcat(GLSL_HEADER_FRAG, "#version 110\n"); + strcat(GLSL_HEADER_FRAG, extHeader); + strcat(GLSL_HEADER_FRAG, "#define FRAGMENT\n" + "#define FETCH_SHADOW2D(a,b) shadow2D(a,b).x\n" + "#define fragColor gl_FragColor\n"); + } #endif ASSERT(strlen(GLSL_HEADER_VERT) < COUNT(GLSL_HEADER_VERT)); ASSERT(strlen(GLSL_HEADER_FRAG) < COUNT(GLSL_HEADER_FRAG)); @@ -1563,6 +1599,8 @@ namespace GAPI { if (wglSwapIntervalEXT) wglSwapIntervalEXT(enable ? 1 : 0); #elif _OS_LINUX if (glXSwapIntervalSGI) glXSwapIntervalSGI(enable ? 1 : 0); + #elif defined(__SDL2__) + SDL_GL_SetSwapInterval(enable ? 1 : 0); #elif defined(_OS_RPI) || defined(_OS_CLOVER) || defined(_OS_SWITCH) eglSwapInterval(display, enable ? 1 : 0); #endif @@ -1697,6 +1735,10 @@ namespace GAPI { #endif } + void setFog(const vec4 ¶ms) { + // FFP TODO + } + void DIP(Mesh *mesh, const MeshRange &range) { #ifdef FFP mat4 m = mView * mModel; diff --git a/src/gapi/gxm.h b/src/gapi/gxm.h index f7e1dd03..519c4381 100644 --- a/src/gapi/gxm.h +++ b/src/gapi/gxm.h @@ -472,7 +472,6 @@ namespace GAPI { bool rebind; void init(Pass pass, int type, int *def, int defCount) { - LOG("init shader %d %d ", int(pass), int(type)); memset(pso, 0, sizeof(pso)); outputFmt = SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4; @@ -536,6 +535,7 @@ namespace GAPI { case 1 : vSrc = SHADER ( filter_downsample, v ); fSrc = SHADER ( filter_downsample, f ); break; case 3 : vSrc = SHADER ( filter_grayscale, v ); fSrc = SHADER ( filter_grayscale, f ); break; case 4 : vSrc = SHADER ( filter_blur, v ); fSrc = SHADER ( filter_blur, f ); break; + case 5 : vSrc = SHADER ( filter_blur, v ); fSrc = SHADER ( filter_blur, f ); break; default : ASSERT(false); } break; @@ -545,8 +545,6 @@ namespace GAPI { default : ASSERT(false); LOG("! wrong pass id\n"); return; } - LOG(" %s", vSrc != NULL ? "true" : "false"); - #undef SHADER_A #undef SHADER_U #undef SHADER_AU @@ -602,8 +600,6 @@ namespace GAPI { } colorMask = blendMode = -1; - - LOG("done\n"); } void deinit() { @@ -770,9 +766,9 @@ namespace GAPI { bool mipmaps = (opt & OPT_MIPMAPS) != 0; bool isCube = (opt & OPT_CUBEMAP) != 0; bool isTarget = (opt & OPT_TARGET) != 0; - bool isShadow = fmt == FMT_SHADOW; + bool isDynamic = (opt & OPT_DYNAMIC) != 0; bool isTiled = isTarget; - bool isSwizzled = !isTiled && filter; + bool isSwizzled = !isDynamic && !isTiled && filter; FormatDesc desc = formats[fmt]; @@ -817,7 +813,7 @@ namespace GAPI { size *= 6; } - SceGxmMemoryAttribFlags flags = (isTarget || mipCount > 1) ? SCE_GXM_MEMORY_ATTRIB_RW : SCE_GXM_MEMORY_ATTRIB_READ; + SceGxmMemoryAttribFlags flags = (isTarget || isDynamic || mipCount > 1) ? SCE_GXM_MEMORY_ATTRIB_RW : SCE_GXM_MEMORY_ATTRIB_READ; this->data = (uint8*)Context::allocGPU(SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, size, flags, &uid); if (data && this->data) { @@ -861,7 +857,7 @@ namespace GAPI { if (opt & OPT_REPEAT) { addrMode = SCE_GXM_TEXTURE_ADDR_REPEAT; } else { - addrMode = (isShadow && support.texBorder) ? SCE_GXM_TEXTURE_ADDR_CLAMP_FULL_BORDER : SCE_GXM_TEXTURE_ADDR_CLAMP; + addrMode = SCE_GXM_TEXTURE_ADDR_CLAMP; } sceGxmTextureSetUAddrMode(&ID, addrMode); @@ -1127,7 +1123,6 @@ namespace GAPI { support.colorHalf = true; support.texHalfLinear = true; support.texHalf = true; - support.clipDist = true; Core::width = DISPLAY_WIDTH; Core::height = DISPLAY_HEIGHT; @@ -1198,7 +1193,7 @@ namespace GAPI { } inline mat4::ProjRange getProjRange() { - return mat4::PROJ_ZERO_POS; + return mat4::PROJ_NEG_POS; } mat4 ortho(float l, float r, float b, float t, float znear, float zfar) { @@ -1280,7 +1275,7 @@ namespace GAPI { sceGxmBeginScene(Context::gxmContext, flags, target->renderTarget, NULL, NULL, NULL, colorSurface, &target->depthSurface); } - active.viewport = Viewport(0, 0, 0, 0); // forcing viewport reset + active.viewport = short4(0, 0, 0, 0); // forcing viewport reset } void discardTarget(bool color, bool depth) {} @@ -1300,12 +1295,16 @@ namespace GAPI { Context::checkPendings(); } - void setViewport(const Viewport &vp) { + void setViewport(const short4 &v) { int vh = active.target ? active.target->height : Core::height; - int sw = vp.width / 2; - int sh = vp.height / 2; - sceGxmSetViewport(Context::gxmContext, float(vp.x + sw), float(sw), float(vh - vp.y - sh), float(-sh), 0.0f, 1.0f); - sceGxmSetRegionClip(Context::gxmContext, SCE_GXM_REGION_CLIP_OUTSIDE, vp.x, vh - vp.y - vp.height, vp.x + vp.width, vp.y + vp.height); + int sw = v.z / 2; + int sh = v.w / 2; + sceGxmSetViewport(Context::gxmContext, float(v.x + sw), float(sw), float(vh - v.y - sh), float(-sh), 0.0f, 1.0f); + } + + void setScissor(const short4 &s) { + //int vh = active.target ? active.target->height : Core::height; + //sceGxmSetRegionClip(Context::gxmContext, SCE_GXM_REGION_CLIP_OUTSIDE, 0, 0, 256, 256); } void setDepthTest(bool enable) { @@ -1365,6 +1364,7 @@ namespace GAPI { } void clear(bool color, bool depth) { + // TODO save and restore states int oColorMask = colorMask; int oBlendMode = blendMode; bool oDepthTest = depthTest; diff --git a/src/gapi/sw.h b/src/gapi/sw.h index 4800313d..7086699e 100644 --- a/src/gapi/sw.h +++ b/src/gapi/sw.h @@ -7,12 +7,14 @@ #define PROFILE_LABEL(id, name, label) #define PROFILE_TIMING(time) -#ifdef _OS_LINUX +//#define DITHER_FILTER + +#if defined(_OS_LINUX) || defined(_OS_TNS) #define COLOR_16 #endif #ifdef COLOR_16 - #ifdef _OS_LINUX + #if defined(_OS_LINUX) || defined(_OS_TNS) #define COLOR_FMT_565 #define CONV_COLOR(r,g,b) (((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3)) #else @@ -247,7 +249,7 @@ namespace GAPI { void resize() { delete[] swDepth; - swDepth = new DepthSW[Core::width * Core::height]; + //swDepth = new DepthSW[Core::width * Core::height]; } inline mat4::ProjRange getProjRange() { @@ -290,7 +292,7 @@ namespace GAPI { } if (depth) { - memset(swDepth, 0xFF, Core::width * Core::height * sizeof(DepthSW)); + //memset(swDepth, 0xFF, Core::width * Core::height * sizeof(DepthSW)); } } @@ -335,6 +337,8 @@ namespace GAPI { } } + void setFog(const vec4 ¶ms) {} + bool checkBackface(const VertexSW *a, const VertexSW *b, const VertexSW *c) { return ((b->x - a->x) >> 16) * (c->y - a->y) - ((c->x - a->x) >> 16) * (b->y - a->y) <= 0; @@ -394,18 +398,25 @@ namespace GAPI { int32 i = y * Core::width; + #ifdef DITHER_FILTER const int *dithY = uvDither + ((y & 1) * 4); + #endif for (int x = i + x1; x < i + x2; x++) { S.z += dS.z; DepthSW z = DepthSW(uint32(S.z) >> 16); - if (swDepth[x] >= z) { + {//if (swDepth[x] >= z) { + #ifdef DITHER_FILTER const int *dithX = dithY + (x & 1); uint32 u = uint32(S.u + dithX[0]) >> 16; uint32 v = uint32(S.v + dithX[2]) >> 16; + #else + uint32 u = uint32(S.u) >> 16; + uint32 v = uint32(S.v) >> 16; + #endif uint8 index = curTile->index[(v << 8) + u]; @@ -413,7 +424,7 @@ namespace GAPI { index = swLightmap[((S.l >> (16 + 3)) << 8) + index]; swColor[x] = swPalette[index]; - swDepth[x] = z; + //swDepth[x] = z; } } @@ -474,9 +485,9 @@ namespace GAPI { b */ VertexSW _n; - VertexSW *t = swVertices + indices[0]; - VertexSW *m = swVertices + indices[1]; - VertexSW *b = swVertices + indices[2]; + VertexSW *t = swVertices.items + indices[0]; + VertexSW *m = swVertices.items + indices[1]; + VertexSW *b = swVertices.items + indices[2]; VertexSW *n = &_n; if (checkBackface(t, m, b)) @@ -521,10 +532,10 @@ namespace GAPI { */ VertexSW _n; VertexSW _p; - VertexSW *t = swVertices + indices[0]; - VertexSW *m = swVertices + indices[1]; - VertexSW *b = swVertices + indices[2]; - VertexSW *o = swVertices + indices[3]; + VertexSW *t = swVertices.items + indices[0]; + VertexSW *m = swVertices.items + indices[1]; + VertexSW *b = swVertices.items + indices[2]; + VertexSW *o = swVertices.items + indices[3]; VertexSW *n = &_n; VertexSW *p = &_p; @@ -578,8 +589,8 @@ namespace GAPI { } void applyLighting(VertexSW &result, const Vertex &vertex, float depth) { - vec3 coord = vec3(vertex.coord); - vec3 normal = vec3(vertex.normal).normal(); + vec3 coord = vec3(float(vertex.coord.x), float(vertex.coord.y), float(vertex.coord.z)); + vec3 normal = vec3(float(vertex.normal.x), float(vertex.normal.y), float(vertex.normal.z)).normal(); float lighting = 0.0f; for (int i = 0; i < lightsCount; i++) { LightSW &light = lightsRel[i]; diff --git a/src/gapi/ta.h b/src/gapi/ta.h index 9ae92625..e96efa56 100644 --- a/src/gapi/ta.h +++ b/src/gapi/ta.h @@ -544,22 +544,22 @@ namespace GAPI { void clear(bool color, bool depth) {} void setClearColor(const vec4 &color) { - pvr_set_bg_color(color.x, color.y, color.z); + pvr_set_bg_color(color.x, color.y, color.z); } void setViewport(const short4 &v) { float w = v.z * 0.5f; - float h = v.w * 0.5f; + float h = v.w * 0.5f; float near = 0.0f; float far = 1.0f; //mat4 matrix; - clear_matrix(); - save_matrix(&m_Matrix[0].m); + clear_matrix(); + save_matrix(&m_Matrix[0].m); //matrix.identity(); - m_Matrix[0].e00 = w; + m_Matrix[0].e00 = w; m_Matrix[0].e11 = -h; m_Matrix[0].e22 = (far - near); //(far - near) * 0.5f; m_Matrix[0].e23 = near; //(far + near) * 0.5f; diff --git a/src/gltf.h b/src/gltf.h index 6430cca5..b5e7c8e4 100644 --- a/src/gltf.h +++ b/src/gltf.h @@ -26,9 +26,9 @@ struct GLTF { JSON *textures; JSON *materials; JSON *nodes; + JSON *scenes; JSON *skins; JSON *animations; - JSON *scenes; char *binaryData; int binarySize; @@ -46,9 +46,9 @@ struct GLTF { accessors = root->add(JSON::ARRAY, "accessors"); meshes = root->add(JSON::ARRAY, "meshes"); nodes = root->add(JSON::ARRAY, "nodes"); - skins = root->add(JSON::ARRAY, "skins"); - animations = root->add(JSON::ARRAY, "animations"); scenes = root->add(JSON::ARRAY, "scenes"); + skins = NULL;//root->add(JSON::ARRAY, "skins"); + animations = NULL;//root->add(JSON::ARRAY, "animations"); asset->add("generator", "OpenLara"); asset->add("version", "2.0"); @@ -120,13 +120,17 @@ struct GLTF { return header->length; } - JSON* addAccessor(int bufferView, int byteOffset, int count, AccessorType type, int format, bool normalized = false, const vec4 &vMin = vec4(0.0f), const vec4 &vMax = vec4(0.0f)) { + JSON* addAccessor(int bufferView, int byteStride, int byteOffset, int count, AccessorType type, int format, bool normalized = false, const vec4 &vMin = vec4(0.0f), const vec4 &vMax = vec4(0.0f)) { static const char *AccessorTypeName[ACCESSOR_TYPE_MAX] = { ACCESSOR_TYPES(DECL_STR) }; JSON *item = accessors->add(JSON::OBJECT); item->add("bufferView", bufferView); + if (byteStride) { + //item->add("byteStride", byteStride); + } + if (byteOffset) { item->add("byteOffset", byteOffset); } @@ -280,6 +284,10 @@ struct GLTF { } JSON *addSkin(const char *name, int inverseBindMatrices, int skeleton, int *joints, int jointsCount) { + if (!skins) { + skins = root->add(JSON::ARRAY, "skins"); + } + JSON *item = skins->add(JSON::OBJECT); if (name) { @@ -301,6 +309,9 @@ struct GLTF { } JSON* addAnimation(const char *name, JSON **samplers, JSON **channels) { + if (!animations) { + animations = root->add(JSON::ARRAY, "animations"); + } JSON *item = animations->add(JSON::OBJECT); if (name) item->add("name", name); diff --git a/src/input.h b/src/input.h index 4f953026..fb019ce4 100644 --- a/src/input.h +++ b/src/input.h @@ -208,6 +208,26 @@ namespace Input { state[playerIndex][key] = down; } + + int32 getTouchWidth() + { + #ifdef _OS_WP8 + return Core::height; + #else + return Core::width; + #endif + } + + int32 getTouchHeight() + { + #ifdef _OS_WP8 + return Core::width; + #else + return Core::height; + #endif + } + + void update() { bool newState[MAX_PLAYERS][cMAX]; @@ -231,16 +251,16 @@ namespace Input { touchTimerVis = max(0.0f, touchTimerVis - Core::deltaTime); // update buttons - float offset = Core::height * 0.25f; + float offset = getTouchHeight() * 0.25f; float radius = offset; - vec2 center = vec2(Core::width - offset * 0.7f, Core::height - offset * 0.7f); + vec2 center = vec2(getTouchWidth() - offset * 0.7f, getTouchHeight() - offset * 0.7f); - btnRadius = Core::height * (25.0f / 1080.0f); + btnRadius = getTouchHeight() * (25.0f / 1080.0f); btnPos[bWeapon] = center; btnPos[bJump] = center + vec2(cosf(-PI * 0.5f), sinf(-PI * 0.5f)) * radius; btnPos[bAction] = center + vec2(cosf(-PI * 3.0f / 4.0f), sinf(-PI * 3.0f / 4.0f)) * radius; btnPos[bWalk] = center + vec2(cosf(-PI), sinf(-PI)) * radius; - btnPos[bInventory] = vec2(Core::width - btnRadius * 8.0f, btnRadius * 4.0f); + btnPos[bInventory] = vec2(getTouchWidth() - btnRadius * 8.0f, btnRadius * 4.0f); // touch update Joystick &joy = Input::joy[Core::settings.controls[0].joyIndex]; @@ -248,8 +268,10 @@ namespace Input { if (checkTouchZone(zMove)) joy.L = vec2(0.0f); - if (checkTouchZone(zLook)) + if (checkTouchZone(zLook)) { joy.L = vec2(0.0f); + joy.R = vec2(0.0f); + } if (checkTouchZone(zButton)) btn = bMove; // no active buttons == bNone @@ -257,7 +279,7 @@ namespace Input { if (doubleTap) doubleTap = false; - float zoneSize = Core::width / 3.0f; + float zoneSize = getTouchWidth() / 3.0f; for (int i = 0; i < COUNT(touch); i++) { InputKey key = InputKey(i + ikTouchA); diff --git a/src/inventory.h b/src/inventory.h index 2d993827..cbf030e7 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -82,8 +82,10 @@ struct OptionItem { int maxWidth = UI::getTextSize(STR[color + value]).x; maxWidth = maxWidth / 2 + 8; x += w * 0.5f; - if (checkValue(value - 1)) UI::specOut(vec2(x - maxWidth - 16.0f, y), 108); - if (checkValue(value + 1)) UI::specOut(vec2(x + maxWidth, y), 109); + if (maxValue != 0xFF) { + if (checkValue(value - 1)) UI::specOut(vec2(x - maxWidth - 16.0f, y), 108); + if (checkValue(value + 1)) UI::specOut(vec2(x + maxWidth, y), 109); + } } return y + LINE_HEIGHT; } @@ -123,23 +125,29 @@ struct OptionItem { } }; -#define SETTINGS(x) OFFSETOF(Core::Settings, x) +#define SETTINGS(x) int32(OFFSETOF(Core::Settings, x)) + +#ifdef INV_SINGLE_PLAYER + #define INV_CTRL_START_OPTION 1 +#else + #define INV_CTRL_START_OPTION 2 +#endif static const OptionItem optDetail[] = { OptionItem( OptionItem::TYPE_TITLE, STR_SELECT_DETAIL ), OptionItem( ), +#ifdef INV_QUALITY OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_FILTER, SETTINGS( detail.filter ), STR_QUALITY_LOW, 0, 2 ), OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_LIGHTING, SETTINGS( detail.lighting ), STR_QUALITY_LOW, 0, 2 ), OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_SHADOWS, SETTINGS( detail.shadows ), STR_QUALITY_LOW, 0, 2 ), OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_WATER, SETTINGS( detail.water ), STR_QUALITY_LOW, 0, 2 ), +#endif OptionItem( OptionItem::TYPE_PARAM, STR_OPT_SIMPLE_ITEMS, SETTINGS( detail.simple ), STR_OFF, 0, 1 ), -#if !defined(_OS_3DS) && !defined(_OS_GCW0) +#ifdef INV_QUALITY OptionItem( OptionItem::TYPE_PARAM, STR_OPT_RESOLUTION, SETTINGS( detail.scale ), STR_SCALE_100, 0, 3 ), -#endif -#if defined(_OS_WIN) || defined(_OS_LINUX) || defined(_OS_PSP) || defined(_OS_RPI) || defined(_OS_PSV) OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_VSYNC, SETTINGS( detail.vsync ), STR_OFF, 0, 1 ), #endif -#if !defined(_OS_PSP) && !defined(_OS_PSV) && !defined(_OS_3DS) && !defined(_OS_GCW0) +#ifdef INV_STEREO OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_STEREO, SETTINGS( detail.stereo ), STR_NO_STEREO, 0, #if defined(_OS_WIN) || defined(_OS_ANDROID) 4 /* with VR option */ @@ -160,32 +168,10 @@ static const OptionItem optSound[] = { OptionItem( OptionItem::TYPE_PARAM, STR_REVERBERATION, SETTINGS( audio.reverb ), STR_OFF, 0, 1 ), OptionItem( OptionItem::TYPE_PARAM, STR_OPT_SUBTITLES, SETTINGS( audio.subtitles ), STR_OFF, 0, 1 ), #ifndef FFP - OptionItem( OptionItem::TYPE_PARAM, STR_OPT_LANGUAGE, SETTINGS( audio.language ), STR_LANG_EN, 0, STR_LANG_CN - STR_LANG_EN ), + OptionItem( OptionItem::TYPE_PARAM, STR_OPT_LANGUAGE, SETTINGS( audio.language ), STR_LANG_EN, 0, STR_LANG_SV - STR_LANG_EN ), #endif }; -#if defined(_OS_PSP) || defined(_OS_PSV) || defined(_OS_3DS) || defined(_OS_GCW0) || defined(_OS_CLOVER) || defined(_OS_PSC) || defined(_OS_XBOX) || defined(_OS_DC) - #define INV_GAMEPAD_ONLY -#endif - -#if defined(_OS_PSP) || defined(_OS_PSV) || defined(_OS_3DS) || defined(_OS_GCW0) || defined(_OS_DC) - #define INV_SINGLE_PLAYER -#endif - -#if defined(_OS_PSP) || defined(_OS_PSV) || defined(_OS_3DS) || defined(_OS_CLOVER) - #define INV_GAMEPAD_NO_TRIGGER -#endif - -#ifdef INV_SINGLE_PLAYER - #define INV_CTRL_START_OPTION 1 -#else - #define INV_CTRL_START_OPTION 2 -#endif - -#if defined(_OS_WIN) || defined(_OS_LINUX) || defined(_OS_RPI) || defined(_OS_GCW0) || defined(_OS_XBOX) - #define INV_VIBRATION -#endif - static const OptionItem optControls[] = { OptionItem( OptionItem::TYPE_TITLE, STR_SET_CONTROLS ), OptionItem( ), @@ -199,7 +185,7 @@ static const OptionItem optControls[] = { OptionItem( OptionItem::TYPE_PARAM, STR_OPT_CONTROLS_RETARGET , SETTINGS( controls[0].retarget ), STR_OFF, 0, 1 ), OptionItem( OptionItem::TYPE_PARAM, STR_OPT_CONTROLS_MULTIAIM , SETTINGS( controls[0].multiaim ), STR_OFF, 0, 1 ), #ifdef INV_GAMEPAD_ONLY - OptionItem( OptionItem::TYPE_PARAM, STR_EMPTY , SETTINGS( playerIndex ), STR_OPT_CONTROLS_GAMEPAD, 0, 0 ), + OptionItem( OptionItem::TYPE_PARAM, STR_EMPTY , SETTINGS( ctrlIndex ), STR_OPT_CONTROLS_KEYBOARD, 0, 0xFF ), #else OptionItem( OptionItem::TYPE_PARAM, STR_EMPTY , SETTINGS( ctrlIndex ), STR_OPT_CONTROLS_KEYBOARD, 0, 1 ), #endif @@ -499,14 +485,14 @@ struct Inventory { case cUp : nextSlot(slot, -1); break; case cDown : nextSlot(slot, +1); break; case cLeft : - if (opt->type == OptionItem::TYPE_PARAM && opt->checkValue(value - 1)) { + if (opt->type == OptionItem::TYPE_PARAM && (opt->maxValue != 0xFF) && opt->checkValue(value - 1)) { value--; timer = 0.2f; return opt; } break; case cRight : - if (opt->type == OptionItem::TYPE_PARAM && opt->checkValue(value + 1)) { + if (opt->type == OptionItem::TYPE_PARAM && (opt->maxValue != 0xFF) && opt->checkValue(value + 1)) { value++; timer = 0.2f; return opt; @@ -1420,6 +1406,9 @@ struct Inventory { #endif #ifdef FFP return; // TODO + #endif + #ifdef _GAPI_TA + return; #endif game->setShader(Core::passFilter, Shader::FILTER_BLUR, false, false); float s = 1.0f / INV_BG_SIZE; @@ -1442,6 +1431,7 @@ struct Inventory { #ifdef FFP return; // TODO #endif + float s = 1.0f / INV_BG_SIZE; game->setShader(Core::passFilter, Shader::FILTER_GRAYSCALE, false, false); Core::setTarget(texOut, NULL, RT_STORE_COLOR); @@ -1452,9 +1442,9 @@ struct Inventory { } void prepareBackground() { - #ifdef FFP - return; - #endif + #ifdef FFP + return; + #endif if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) return; @@ -1463,9 +1453,9 @@ struct Inventory { return; #endif - #ifdef _OS_3DS - GAPI::rotate90 = false; - #endif + #ifdef _OS_3DS + GAPI::rotate90 = false; + #endif game->renderGame(false, true); @@ -1486,9 +1476,9 @@ struct Inventory { swap(background[view], background[2]); } - #ifdef _OS_3DS - GAPI::rotate90 = true; - #endif + #ifdef _OS_3DS + GAPI::rotate90 = true; + #endif Core::setDepthTest(true); } @@ -1749,6 +1739,11 @@ struct Inventory { } float aspectDst = float(Core::width) / float(Core::height) * Core::aspectFix; + + #ifdef _OS_WP8 + aspectDst = 1.0f / aspectDst; + #endif + float aspectImg = aspectSrc / aspectDst; #ifdef FFP @@ -1867,7 +1862,6 @@ struct Inventory { m.identity(); Core::setViewProj(m, m); Core::mModel.identity(); - #else if (Core::settings.detail.stereo == Core::Settings::STEREO_VR || !background[0]) { backTex = Core::blackTex; // black background @@ -2090,7 +2084,7 @@ struct Inventory { const char *bSelect = STR[STR_KEY_FIRST + ikEnter]; const char *bBack = STR[STR_KEY_FIRST + Core::settings.controls[playerIndex].keys[cInventory].key]; - #if defined(_OS_SWITCH) || defined(_OS_3DS) || defined(_OS_GCW0) || defined(_OS_XBOX) || defined(_OS_DC) + #if defined(_OS_SWITCH) || defined(_OS_3DS) || defined(_OS_GCW0) || defined(_OS_XBOX) || defined(_OS_XB1) || defined(_OS_DC) bSelect = "A"; bBack = "B"; #endif diff --git a/src/json.h b/src/json.h index a433e3f2..e569cfcc 100644 --- a/src/json.h +++ b/src/json.h @@ -23,7 +23,7 @@ struct JSON { Type type; JSON(Type type, const char *name = NULL) : nodes(NULL), prev(NULL), next(NULL), type(type) { - this->name = String::copy(name); + this->name = StrUtils::copy(name); } ~JSON() { @@ -66,7 +66,7 @@ struct JSON { void add(const char *name, const char *value) { - add(STRING, name)->sValue = String::copy(value); + add(STRING, name)->sValue = StrUtils::copy(value); } void add(const char *name, int value) { diff --git a/src/lang.h b/src/lang.h index d1b9e685..ab6ef6ff 100644 --- a/src/lang.h +++ b/src/lang.h @@ -25,13 +25,6 @@ enum StringID { , STR_QUALITY_LOW , STR_QUALITY_MEDIUM , STR_QUALITY_HIGH -#ifdef __DC__ - , STR_LANG_EN - , STR_LANG_FR - , STR_LANG_DE - , STR_LANG_ES - , STR_LANG_IT -#else , STR_LANG_EN , STR_LANG_FR , STR_LANG_DE @@ -45,7 +38,8 @@ enum StringID { , STR_LANG_FI , STR_LANG_CZ , STR_LANG_CN -#endif + , STR_LANG_HU + , STR_LANG_SV , STR_APPLY , STR_GAMEPAD_1 , STR_GAMEPAD_2 @@ -266,14 +260,6 @@ enum StringID { #define STR_RUSSIAN "Ãóññêè{è" #endif -#ifdef __DC__ -#define STR_LANGUAGES \ - "English" \ - , "Fran|cais" \ - , "Deutsch" \ - , "Espa+nol" \ - , "Italiano" -#else #define STR_LANGUAGES \ "English" \ , "Fran|cais" \ @@ -287,10 +273,11 @@ enum StringID { , "\x11\x01\x22\x01\x0F\x01\x0F\x01\x0E\x01\x06\x01\x04\x01\x0C\x01\x0B\xFF\xFF" \ , "Suomi" \ , "{Cesky" \ - , "\x11\x02\x8A\x02\x6C\x01\x54\x03\x02\xFF\xFF" -#endif + , "\x11\x02\x8A\x02\x6C\x01\x54\x03\x02\xFF\xFF" \ + , "Magyar" \ + , "Svenska" -#define LANG_PREFIXES "_EN", "_FR", "_DE", "_ES", "_IT", "_PL", "_PT", "_RU", "_JA", "_GR", "_FI", "_CZ", "_CN" +#define LANG_PREFIXES "_EN", "_FR", "_DE", "_ES", "_IT", "_PL", "_PT", "_RU", "_JA", "_GR", "_FI", "_CZ", "_CN", "_HU", "_SV" #define STR_KEYS \ "NONE", "LEFT", "RIGHT", "UP", "DOWN", "SPACE", "TAB", "ENTER", "ESCAPE", "SHIFT", "CTRL", "ALT" \ @@ -329,19 +316,6 @@ const char *helpText = #include "lang/de.h" #include "lang/es.h" #include "lang/it.h" -#ifdef __DC__ - -inline char remapCyrillic(char c) { - return c; -} - -#define JA_GLYPH_COUNT 0 -#define GR_GLYPH_COUNT 0 -#define GR_GLYPH_BASE 0 -const uint8 GR_GLYPH_WIDTH[] = { 0 }; -#define CN_GLYPH_COUNT 0 - -#else #include "lang/pl.h" #include "lang/pt.h" #include "lang/ru.h" @@ -350,29 +324,11 @@ const uint8 GR_GLYPH_WIDTH[] = { 0 }; #include "lang/fi.h" #include "lang/cz.h" #include "lang/cn.h" -#endif +#include "lang/hu.h" +#include "lang/sv.h" char **STR = NULL; -#ifdef __DC__ -void ensureLanguage(int lang) { - ASSERT(COUNT(STR_EN) == STR_MAX); - ASSERT(COUNT(STR_FR) == STR_MAX); - ASSERT(COUNT(STR_DE) == STR_MAX); - ASSERT(COUNT(STR_ES) == STR_MAX); - ASSERT(COUNT(STR_IT) == STR_MAX); - - lang += STR_LANG_EN; - - switch (lang) { - case STR_LANG_FR : STR = (char**)STR_FR; break; - case STR_LANG_DE : STR = (char**)STR_DE; break; - case STR_LANG_ES : STR = (char**)STR_ES; break; - case STR_LANG_IT : STR = (char**)STR_IT; break; - default : STR = (char**)STR_EN; break; - } -} -#else void ensureLanguage(int lang) { ASSERT(COUNT(STR_EN) == STR_MAX); ASSERT(COUNT(STR_FR) == STR_MAX); @@ -387,6 +343,8 @@ void ensureLanguage(int lang) { ASSERT(COUNT(STR_FI) == STR_MAX); ASSERT(COUNT(STR_CZ) == STR_MAX); ASSERT(COUNT(STR_CN) == STR_MAX); + ASSERT(COUNT(STR_HU) == STR_MAX); + ASSERT(COUNT(STR_SV) == STR_MAX); lang += STR_LANG_EN; @@ -403,9 +361,10 @@ void ensureLanguage(int lang) { case STR_LANG_FI : STR = (char**)STR_FI; break; case STR_LANG_CZ : STR = (char**)STR_CZ; break; case STR_LANG_CN : STR = (char**)STR_CN; break; + case STR_LANG_HU : STR = (char**)STR_HU; break; + case STR_LANG_SV : STR = (char**)STR_SV; break; default : STR = (char**)STR_EN; break; } } -#endif #endif diff --git a/src/lang/cz.h b/src/lang/cz.h index 5c6063bd..a7a31bc9 100644 --- a/src/lang/cz.h +++ b/src/lang/cz.h @@ -1,7 +1,7 @@ #ifndef H_LANG_CZ #define H_LANG_CZ -// Thanks: bax-cz +// Thanks: bax-cz, Hansy995 const char *STR_CZ[] = { "" // help @@ -10,7 +10,7 @@ const char *STR_CZ[] = { "" , helpText , "%s@@@" "ZABITO %d@@" - "SEBR)ANO P{REDM{ET*U %d@@" + "SEBR)ANO P{REDM{ET^U %d@@" "NALEZENO SKR)Y{S)I %d z %d@@" "STR)AVENO {CASU %s" , "Ukl)ad)an)i hry..." @@ -53,7 +53,7 @@ const char *STR_CZ[] = { "" , "Kompas" , "Statistiky" , "La{rin Domov" - , ")Urove{n Detail*u" + , ")Urove{n Detail^u" , "Zvuk" , "Ovl)ad)an)i" , "Gama Korekce" @@ -65,7 +65,7 @@ const char *STR_CZ[] = { "" , "Ukon{cit Hru" , "Vybrat )Urove{n" // detail options - , "Nastaven)i Detail*u" + , "Nastaven)i Detail^u" , "Filtrov)an)i" , "Osv{etlen)i" , "St)iny" @@ -88,7 +88,7 @@ const char *STR_CZ[] = { "" , "Auto-zm{ena c)ile" , "Multi-m)i{ren)i" // controls - , "Vlevo", "Vpravo", "Vp{red", "Vzad", "Skok", "Ch*uze", "Akce", "Tasit zbra{n", "Rozhl)ednout", "D{rep", "Sprint", "Kotoul", "Invent)a{r", "Start" + , "Vlevo", "Vpravo", "Vp{red", "Vzad", "Skok", "Ch^uze", "Akce", "Tasit zbra{n", "Rozhl)ednout", "D{rep", "Sprint", "Kotoul", "Invent)a{r", "Start" , STR_KEYS // inventory items , "Nezn)am)y" @@ -113,8 +113,8 @@ const char *STR_CZ[] = { "" , "Saf)irov)y Kl)i{c" , "Neptunov)y Kl)i{c" , "Atlasov)y Kl)i{c" - , "Damokl*uv Kl)i{c" - , "Thor*uv Kl)i{c" + , "Damokl^uv Kl)i{c" + , "Thor^uv Kl)i{c" , "Zdoben)y Kl)i{c" // puzzles , "H)adanka" @@ -144,117 +144,117 @@ const char *STR_CZ[] = { "" "[78000]Peru. Rozlehl)a poho{r)i.@Strm)e st{eny hol)eho ledu. Skaln)i )utesy. Zu{riv)y v)itr." "[87500]A pak je tu ta jedna drobnost:@prastar)y artefakt s tajemn)ymi schopnostmi" "[92500]hluboko poh{rben)y v dosud nenalezen)e hrobce Qualopeca." - "[96000]To je zase m*uj z)ajem." + "[96000]To je zase m^uj z)ajem." "[98000]Mohla byste let{et u{z z)itra.@M)ate na z)it{rek n{ejak)e pl)any?" /* LIFT */ , - "[49000]Relocated now to St. Francis' Folly, new temptations torment me." - "[53500]Rumour amongst my fellow brothers is that entombed@beneath our monastery lies the body of Tihocan," - "[60000]one of the three legendary rulers of@the lost continent, Atlantis," - "[64500]and that within lies his piece@of the Atlantean Scion." - "[68000]The pendant divided and shared between the three rulers@" - "[72500]which curbs tremendous powers.@Powers beyond the creator himself." - "[79000]My toes sweat at such possibilities@lying so close to my mortal self." - "[85500]Each night, I bid myself rid of these@fantasies, but it is indeed a test." + "[49000]Ted', kdy{z jsem se p{rem)istil k Monumentu sv. Franti{ska, za{calo m{e tr)iznit nov)e poku{sen)i." + "[53500]Mezi m)ymi bratry se {r)ik)a, {ze je pod t)imto na{s)im kl)a{sterem poh{rbeno t{elo Tihocana," + "[60000]jednoho ze t{r)i legend)arn)ich vl)adc^u ztracen)eho kontinentu Atlantidy." + "[64500]A tak)e, {ze je poh{rben se svou {c)ast)i Atlantsk)eho Scionu." + "[68000]Pendantem, kter)y byl rozd{elen a sd)ilen mezi t{remi zm)in{en)ymi vl)adci," + "[72500]a kter)y dr{z)i ohromnou moc. Moc v{et{s)i, ne{z m)a s)am stvo{ritel." + "[79000]Moje prsty se pot)i p{ri pomy{slen)i na mo{znosti, kter)e le{z)i tak bl)izko m)emu smrteln)emu j)a." + "[85500]Ka{zdou noc se sna{z)im zbavit t{echto fantazi)i, ale je to vskutku zkou{ska." "[92000]" "[93500]Pierre. Tss. Ty bordel)a{ri." /* CANYON */ , - "[13500]You just pulled the tough end of a wishbone." - "[16500]Howdy." - "[17500]Afternoon." - "[20000]Left Larson sucking wind then, eh?" - "[22500]If that is the phrase." - "[24000]Well, your little vacation riot's over now." - "[27000]Time to give back what you've hijacked off me." - "[30000]Let's try the lunch-box." + "[13500]Pr)av{e ti do{slo {st{est)i." + "[16500]Zdrav)i{cko." + "[17500]Dobr)e odpoledne." + "[20000]Nechala jste Larsona v z)av{esu, co?" + "[22500]Jestli se to tak d)a {r)ict." + "[24000]Va{se mal)a pr)azdninov)a vzpoura pr)av{e skon{cila." + "[27000]Je {cas vr)atit to, co jste mi ukradla." + "[30000]Pod)iv)ame se do ob{edov)e krabi{cky." "[32000]" - "[42500]Well? Kill her!" - "[45000]Hey!" + "[42500]No tak? Zabte ji!" + "[45000]Hej!" "[48000]" - "[50500]You morons!" + "[50500]Vy blbci!" "[53000]" - "[62500]Let's go." + "[62500]Jedeme." "[65000]" - "[136000]What the heck was that?" - "[138000]What?" - "[138500]That-a-way." - "[140500]Probably just a fish." - "[142500]That's some fish, kid." - "[145000]Man, you have got to learn to chill.@I'm going back inside. You coming?" + "[136000]Co to sakra bylo?" + "[138000]Co?" + "[138500]T)amhle." + "[140500]Nejsp)i{s jenom ryba." + "[142500]To by byla po{r)adn)a ryba, chlap{ce." + "[145000]Chlape, mus)i{s se hodit do klidu. Jdu zp)atky dovnit{r. Jde{s?" "[152000]" - "[158000]Steady..." - "[160000]Here she goes." - "[161500]You ready yet?" + "[158000]Hezky pomalu..." + "[160000]U{z jde." + "[161500]U{z jste p{ripraveni?" /* PRISON */ , - "[00001]You can't do this!" - "[01500]We condemn you, Natla of Atlantis, for your crimes." - "[06000]For the flagrant misuse of your powers@and for robbing us of our..." - "[11500]You can't! I..." - "[12500]Breaking the free bond of consent that our@people are ruled and secured under," - "[18500]and invading Tihocan and myself with our army." - "[23500]Our warriors emptied from our pyramid" - "[27000]so that you could use the pyramid - its powers@of creation - for your own mindless destruction." - "[33500]Mindless!? Look at you!" - "[35500]Neither of you have one squirt of@inventive juice in your heads." - "[40500]Wasters!" - "[41500]Let's just do it." - "[44000]Tihocan!" - "[45000]You used the sacramental place as a source@of individual pleasure," - "[49500]as some freak factory." - "[51000]They're survivalists. A new generation." - "[54000]A slaughter heap now." - "[56000]And you. We're going to lock you in limbo." - "[60000]Make your veins, heart, feet," - "[64000]and that diseased brain stick solid with frozen blood." - "[70000]Greet your eternal unrest, Natla." - "[73000]You won't rest either, or your@damned continent of Atlantis!" + "[00001]Tohle nem^u{zete!" + "[01500]Odsuzujme t{e za tv)e zlo{ciny, Natlo z Atlantidy." + "[06000]Za o{cividn)e zneu{zit)i tv)e moci a za okr)ad)an)i na{s)i..." + "[11500]Nem^u{zete! J)a..." + "[12500]Za poru{sen)i svobodn)e )Umluvy, kter)a na{sim lidem vl)adne," + "[18500]a za napaden)i Tihocana a m{e na{s)i vlastn)i amr)adou." + "[23500]Na{si v)ale{cn)ici se odebrali z na{s)i pyramidy," + "[27000]abys potom mohla zneu{z)it jej)i moci tvo{ren)i - pro sv)e vlastn)i, bezduch)e ni{cen)i." + "[33500]Bezduch)e!? Pod)ivejte se na sebe!" + "[35500]Ani jeden z v)as nem)ate v hlav{e ani kapku vynal)ezavosti." + "[40500]Mrha{ci!" + "[41500]Prost{e to ud{elejme." + "[44000]Tihocane!" + "[45000]Vyu{zila jsi posv)atn)e m)isto pro sv)e vlastn)i pot{e{sen)i -" + "[49500]jako n{ejakou tov)arnu na monstra." + "[51000]Jsou to p{re{ziv{s)i. Nov)a generace." + "[54000]Ted' je to vra{zd)ic)i tlupa." + "[56000]A tebe. Tebe zamkneme v zapomn{en)i." + "[60000]Zmrzlou krv)i zatvrd)ime tv)e {z)ily, srdce, chodidla" + "[64000]a ten tv^uj chorobn)y mozek." + "[70000]P{riv)itej sv^uj v{e{cn)y neklid, Natlo." + "[73000]V)am se tak)e nedostane klidu! Ani va{semu zatracen)emu Atlantsk)emu kontinentu!" /* 22 */ , - "[04000]Back again?" - "[05500]And you - for a grand re-opening, I assume." - "[09500]Evolution's in a rut - natural selection at an all time low..." - "[13500]shipping out fresh meat will incite territorial rages again" - "[17500] - will strengthen and advance us..." - "[20500]Even create new breeds." - "[22500]Kind of evolution on steroids, then." - "[24500]A kick in the pants...@those runts Qualopec and Tihocan had no idea" - "[29500] - the cataclysm of Atlantis struck a race of langouring wimps..." - "[33500]plummeted them to the very basics of survival again..." - "[37000]It shouldn't happen like that." - "[39000]Or like this." - "[40000]Hatching commences in 15 seconds." - "[43000]Too late for abortions now!" - "[45000]Not without the heart of the operation!" - "[47000]Noooo!" - "[50000]TEN" - "[54000]FIVE..." + "[04000]Zase zp{et?" + "[05500]A vy tak)e. H)ad)am, {ze na slavnostn)i znovuotev{ren)i." + "[09500]Evoluce je v {r)iji - P{r)irodn)i v)yb{er je na tom h^u{r, ne{z kdy d{r)iv..." + "[13500]V)yvoz {cerstv)eho masa znovu vyvol)a teritori)aln)i agresi" + "[17500] - posiln)i a posune n)as..." + "[20500]Dokonce stvo{r)i nov)e rasy." + "[22500]Tak trochu evoluce na steroidech." + "[24500]Nakopnut)i do spr)avn)eho sm{eru... ten nicotn)y Qualopec s Tihocanem nem{eli tu{sen)i" + "[29500] - ta pohroma, kterou je Atlantida, ude{rila rasu stagnuj)ic)ich slaboch^u..." + "[33500]Srazila je zp{et k samotn)ym z)aklad^um p{re{zit)i..." + "[37000]Tak by se to nem{elo st)at." + "[39000]To ani takhle." + "[40000]L)ihnut)i za{cne za 15 sekund." + "[43000]U{z je moc pozd{e na potrat!" + "[45000]Bez srdce t)eto operace to nep^ujde!" + "[47000]Neee!" + "[50000]DESET" + "[54000]P{eT..." "[55500]4...3...2..." - "[60000]ONE..." + "[60000]JEDNA..." /* 23 */ , "[00001]Tak{ze, te{d m)a{s mou plnou pozornost" "[02500]- A{ckoli si nejsem a{z tak jist)a jestli m)am tvou." - "[05000]Hello?" - "[06000]I'll heel an' hide ye to a barn door yit." - "[09000]Of course." - "[10000]Ye and that drivelin' piece of the Scion." - "[13000]Ye want to keep it so bad, I'll harness it right up y..." - "[17000]Wait... we're talking about the artifact here?" - "[20000]Damn straight we are ... right up y ..." - "[22000]Hold on - I'm sorry" - "[24000]- this piece, you say - where's the rest?" - "[26500]Ms. Natla put Pierre Dupont on that trail." - "[29500]And where is that?" - "[30500]Hah. Ye ain't fast enough fer him." - "[34000]So you think all this talking is just holding me up?" - "[37000]I don't know where his little jackrabbit-frog-legs are runnin' him to." - "[42000]You'll have to ask Ms. Natla." + "[05000]No tak?" + "[06000]Dostanu t{e a st)ahnu t{e z k^u{ze." + "[09000]Samoz{rejm{e." + "[10000]Tebe a tu pitomou {c)ast Scionu." + "[13000]Jestli si ji chce{s tak moc nechat, narvu ti ji rovnou do..." + "[17000]Po{ckat... bav)ime se tady o tom artefaktu?" + "[20000]To si pi{s ... rovnou do ..." + "[22000]Vydr{z - Promi{n" + "[24000]{R)ikal jsi {c)ast. Kde je ten zbytek?" + "[26500]Pan)i Natla za touhle stopou poslala Pierra Duponta." + "[29500]A kde je ta stopa?" + "[30500]Hah. Nejsi na n{ej dost rychl)a." + "[34000]Tak{ze mysl)i{s, {ze v{sechno tohle mluven)i m{e jen zdr{zuje?" + "[37000]Nev)im kam ho ty jeho zaj)ico-{zab)i no{zi{cky vedou." + "[42000]Bude{s se muset poptat pan)i Natly." "[46000]" - "[51000]Thank you. I will." + "[51000]D)iky. Zept)am se." /* 24 */ , "" /* 25 */ , "[03500]Zde odpo{c)iv)a Tihocan" - "[05000]...jeden ze dvou spravedliv)ych vl)adc*u Atlantidy..." + "[05000]...jeden ze dvou spravedliv)ych vl)adc^u Atlantidy..." "[10000]Ten, kter)y i po proklet)i kontinentu..." "[13000]...se pokusil udr{zet pr)avo v t{echto pust)ych ciz)ich zem)ich..." - "[19000]Zem{rel bez d)it{ete a jeho v{ed{en)i z*ustalo bez n)asledovn)ika..." + "[19000]Zem{rel bez d)it{ete a jeho v{ed{en)i z^ustalo bez n)asledovn)ika..." "[25500]Bu{d k n)am milostiv, Tihocane." /* 26 */ , "V)itejte u m{e doma!@Vezmu V)as na prohl)idku." /* 27 */ , "Pomoc)i sm{erov)ych kl)aves p{rejd{ete do hudebn)i m)istnosti." @@ -262,31 +262,31 @@ const char *STR_CZ[] = { "" /* 29 */ , "Nyn)i ji stiskn{ete je{st{e jednou v kombinaci s n{ekterou ze sm{erov)ych kl)aves@a j)a po{zadovan)ym sm{erem sko{c)im." /* 30 */ , "Ach, ta hlavn)i hala.@Omlouv)am se za ty bedny, m)am tu p)ar v{eci k uskladn{en)i@a st{ehov)aci je je{st{e nevyzvedli." /* 31 */ , "P{rib{ehn{ete k bedn{e a za sou{casn)eho dr{zen)i kl)avesy vp{red,@stiskn{ete kl)avesu akce a j)a se na n)i vy{svihnu." - /* 32 */ , "Tohle kdysi b)yval tane{cn)i s)al ne{z jsem si z n{ej ud{elala t{elocvi{cnu.@Co na n)i {r)ik)ate? No, poj{dme zkusit p)ar cvik*u." - /* 33 */ , "Ve skute{cnosti nemus)im v{sude jen b{ehat.@Kdy{z chci b)yt opatrn)a, tak chod)im krokem.@Podr{zte kl)avesu pro ch*uzi a dojd{ete k b)il)e {c)a{re." - /* 34 */ , "P{ri dr{zen)i kl)avesy ch*uze nespadnu dol*u ani pokud se o to pokus)ite.@Schv)aln{e to zkuste." + /* 32 */ , "Tohle kdysi b)yval tane{cn)i s)al ne{z jsem si z n{ej ud{elala t{elocvi{cnu.@Co na n)i {r)ik)ate? No, poj{dme zkusit p)ar cvik^u." + /* 33 */ , "Ve skute{cnosti nemus)im v{sude jen b{ehat.@Kdy{z chci b)yt opatrn)a, tak chod)im krokem.@Podr{zte kl)avesu pro ch^uzi a dojd{ete k b)il)e {c)a{re." + /* 34 */ , "P{ri dr{zen)i kl)avesy ch^uze nespadnu dol^u ani pokud se o to pokus)ite.@Schv)aln{e to zkuste." /* 35 */ , "Jestli{ze se budete cht)it rozhl)ednout, stiskn{ete a podr{zte kl)avesu pro rozhl)ednut)i.@Pot)e stiskn{ete kl)avesu sm{eru, kter)ym se chcete rozhl)ednout." - /* 36 */ , "Pokud je pro m{e skok p{r)ili{s vzd)alen)y, m*u{zu se zachytit okraje@a vyhnout se tak p)adu. B{e{zte a{z k okraji s b)ilou {c)arou@jak to jen p*ujde. Pot)e stiskn{ete kl)avesu pro skok, n)asledovanou@kl)avesou vp{red a zat)imco budu je{st{e ve vzduchu, stiskn{ete a podr{zte kl)avesu akce." + /* 36 */ , "Pokud je pro m{e skok p{r)ili{s vzd)alen)y, m^u{zu se zachytit okraje@a vyhnout se tak p)adu. B{e{zte a{z k okraji s b)ilou {c)arou@jak to jen p^ujde. Pot)e stiskn{ete kl)avesu pro skok, n)asledovanou@kl)avesou vp{red a zat)imco budu je{st{e ve vzduchu, stiskn{ete a podr{zte kl)avesu akce." /* 37 */ , "Podr{zte kl)avesu vp{red a j)a na p{rek)a{zku vy{splh)am." /* 38 */ , "V p{r)ipad{e skoku s rozb{ehem pro m{e takov)a vzd)alenost nep{redstavuje probl)em." - /* 39 */ , "Pomoc)i kl)avesy ch*uze b{e{zte a{z k okraji s b)ilou {c)arou dokud se sama nezastav)im.@Pot)e uvoln{ete kl)avesu ch*uze a stiskn{ete kl)avesu vzad, abych m{ela prostor pro rozb{eh.@Stiskn{ete kl)avesu vp{red a hned na to stiskn{ete a podr{zte kl)avesu pro skok.@Sko{c)im sama a{z v posledn)i chv)ili." + /* 39 */ , "Pomoc)i kl)avesy ch^uze b{e{zte a{z k okraji s b)ilou {c)arou dokud se sama nezastav)im.@Pot)e uvoln{ete kl)avesu ch^uze a stiskn{ete kl)avesu vzad, abych m{ela prostor pro rozb{eh.@Stiskn{ete kl)avesu vp{red a hned na to stiskn{ete a podr{zte kl)avesu pro skok.@Sko{c)im sama a{z v posledn)i chv)ili." /* 40 */ , "Dob{re. Tenhle je opravdu dost daleko.@Tak{ze stejn{e jako p{redt)im. Skok s rozb{ehem n)asledovan)y podr{zen)im kl)avesy akce,@abych se zachytila okraje." /* 41 */ , "P{ekn{e." /* 42 */ , "Zkuste tu vysko{cit nahoru.@Stiskn{ete kl)avesu vp{red a podr{zte kl)avesu akce." /* 43 */ , "Nahoru nevylezu, proto{ze mezera je p{r)ili{s mal)a.@Av{sak podr{zen)im sm{erov)e kl)avesy vpravo,@odru{ckuji a{z do m)ista kde je prostor, pot)e stiskn{ete kl)avesu vp{red." - /* 44 */ , "V)yborn{e!@Pokud se nechci zranit p{ri seskoku z velk)e v)y{sky,@m*u{zu se opatrn{e spustit dol*u." + /* 44 */ , "V)yborn{e!@Pokud se nechci zranit p{ri seskoku z velk)e v)y{sky,@m^u{zu se opatrn{e spustit dol^u." /* 45 */ , "Klepn{ete na kl)avesu vzad a sesko{c)im pozp)atku.@Hned na to, podr{zte kl)avesu akce@a j)a se zachyt)im okraje." /* 46 */ , "Pot)e ji pus{tte." /* 47 */ , "Poj{dme si zaplavat." /* 48 */ , "Pro pohyb pod vodou pou{zijte kl)avesu pro skok v kombinaci se sm{erov)ymi kl)avesami." /* 49 */ , "Ach! Vzduch!@Pro man)evrov)an)i na hladin{e prost{e pou{zijte kl)avesy vp{red a vlevo a vpravo.@Pro dal{s)i potopen)i se, stiskn{ete kl)avesu pro skok.@Nebo doplavte ke kraji a stiskn{ete kl)avesu akce a j)a z vody vylezu." /* 50 */ , "Dob{re. Te{d bych rad{eji m{ela svl)eknout to prom)a{cen)e oble{cen)i." - /* 51 */ , "Say cheese!" - /* 52 */ , "Ain't nothin' personal." - /* 53 */ , "I still git a pain in my brain from ye.@An' it's tellin' me funny ideas now.@Like to shoot you to hell!" - /* 54 */ , "You can't bump off me and my brood so easy, Lara." - /* 55 */ , "A leetle late for ze prize giving - non?@Still, it is ze taking-part wheech counts." - /* 56 */ , "You firin' at me?@You firin' at me, huh?@Ain't nobody else here, you must be firin' at me!" + /* 51 */ , "{Rekni s)yr!" + /* 52 */ , "Nen)i to nic osobn)iho." + /* 53 */ , "Po{r)ad mi kv^uli tob{e vyst{reluje bolest do mozku. A d)av)a mi to srandovn)i n)apady. T{reba abych t{e rozt{r)ilel na kousky!" + /* 54 */ , "M{e a m)ych potomk^u se tak snadno nezbav)ite, Laro." + /* 55 */ , "Tro{sku pozd{e na ud{elen)i cen - non? Ale )U{cast se i tak po{c)it)a." + /* 56 */ , "To st{r)il)i{s na m{e? To st{r)il)i{s na m{e, co? Nikdo jinej tady nen)i, ur{cit{e st{r)il)i{s na m{e!" // TR1 levels , "La{rin Domov" , "Jeskyn{e" @@ -309,47 +309,47 @@ const char *STR_CZ[] = { "" , "Atlantsk)a Pevnost" , "Hn)izdo" // TR2 levels - , "Lara's Home" - , "The Great Wall" - , "Venice" - , "Bartoli's Hideout" - , "Opera House" - , "Offshore Rig" - , "Diving Area" - , "40 Fathoms" - , "Wreck of the Maria Doria" - , "Living Quarters" - , "The Deck" - , "Tibetan Foothills" - , "Barkhang Monastery" - , "Catacombs of the Talion" - , "Ice Palace" - , "Temple of Xian" - , "Floating Islands" - , "The Dragon's Lair" - , "Home Sweet Home" + , "La{rin Domov" + , "Velk)a Zed'" + , "Ben)atky" + , "Bartoliho )Ukryt" + , "D^um Opery" + , "Ropn)a plo{sina" + , "Pot)ape{csk)a oblast" + , "40 S)ah^u" + , "Vrak Maria Dorie" + , "Obytn)e m)istnosti" + , "Paluba" + , "Tibetsk)e P{redh^u{r)i" + , "Kl)a{ster Barkhang" + , "Katakomby Talionu" + , "Ledov)y Pal)ac" + , "Chr)am Xian" + , "Vzn)a{sej)ic)i Ostrovy" + , "Dra{c)i Doup{e" + , "Domove, Sladk)y Domove" // TR3 levels - , "Lara's House" - , "Jungle" - , "Temple Ruins" - , "The River Ganges" - , "Caves Of Kaliya" - , "Coastal Village" - , "Crash Site" - , "Madubu Gorge" - , "Temple Of Puna" - , "Thames Wharf" + , "La{rin Domov" + , "D{zungle" + , "Ruiny Chr)amu" + , "{Reka Ganga" + , "Jeskyn{e Kaliya" + , "Pob{re{zn)i Vesnice" + , "M)isto Hav)arie" + , "Sout{eska Madubu" + , "Chr)am Puny" + , "P{rist)avi{st{e Tem{ze" , "Aldwych" - , "Lud's Gate" - , "City" - , "Nevada Desert" - , "High Security Compound" - , "Area 51" - , "Antarctica" - , "RX-Tech Mines" - , "Lost City Of Tinnos" - , "Meteorite Cavern" - , "All Hallows" + , "Ludova Br)ana" + , "M{esto" + , "Nevadsk)a Pou{st'" + , "Oblast S Vysok)ym Zabezpe{cen)im" + , "Oblast 51" + , "Antarktida" + , "Doly RX-Tech" + , "Ztracen)e M{esto Tinnos" + , "Meteoritov)a Jeskyn{e" + , "V{sechny Relikvie" }; #endif diff --git a/src/lang/en.h b/src/lang/en.h index 6811fb2f..ca0746aa 100644 --- a/src/lang/en.h +++ b/src/lang/en.h @@ -257,7 +257,7 @@ const char *STR_EN[] = { "" "[19000]He died without child and his knowledge has no heritage..." "[25500]Look over us kindly, Tihocan." /* 26 */ , "Welcome to my home!@I'll take you on a guided tour." - /* 27 */ , "Use the directional buttons to go into the music room." + /* 27 */ , "Use the directional keys to go into the music room." /* 28 */ , "OK. Let's do some tumbling.@Press the jump button." /* 29 */ , "Now press it again and quickly press one of@the directions and I'll jump that way." /* 30 */ , "Ah, the main hall.@Sorry about the crates, I'm having some things put@ into storage and the delivery people haven't been yet." diff --git a/src/lang/fr.h b/src/lang/fr.h index 21b5ce17..a14d4d9c 100644 --- a/src/lang/fr.h +++ b/src/lang/fr.h @@ -1,7 +1,7 @@ #ifndef H_LANG_FR #define H_LANG_FR -// Thanks: Zellphie +// Thanks: Zellphie, Laripette const char *STR_FR[] = { "" // help @@ -142,7 +142,7 @@ const char *STR_FR[] = { "" "[73500]Je suis d)esol)ee, je fais |ca pour le sport." "[76000]Alors, vous allez adorer le P)erou." "[78000]D'immenses montagnes $a franchir,@des falaises de glace. Les cr(etes, les vents glac)es." - "[87500]Et il y a ce petit bibelot:@Et il y a ce petit bibelot, un objet mill)enaire au pouvoir myst)erieux," + "[87500]Et il y a ce petit bibelot, un objet mill)enaire au pouvoir myst)erieux," "[92500]enfoui dans le tombeau perdu de Qualopec." "[96000]Et il m'int)eresse..." "[98000]Partez donc d$es demain. Vous (etes libre demain?" @@ -210,18 +210,18 @@ const char *STR_FR[] = { "" /* 22 */ , "[04000]Vous revoil$a?" "[05500]Comme vous, pour la grand r)e-ouverture je pr)esume." - "[09000]L')evolution s'enlise, la s)el)ection naturelle est plus lente que jamais" - "[13500]cette chair bien fra(iche r)eveillera les haines ancestrales et cruelles, " - "[17500]nous donnant force et conviction" + "[09000]L')evolution s'enlise la s)el)ection naturelle est plus lente que jamais," + "[13500]et cette chair bien fra(iche r)eveillera les haines ancestrales et cruelles," + "[17500]nous donnant force et conviction," "[20500]et m(eme de nouvelles races." "[22500]Une )evolution gr(ace aux st)ero~ides, en fait." "[24500]Un bon coup de fouet. Ces idiots de Qualopec et Tihocan sont des ignares." "[29500]Le cataclysme de l'Atlantide a engendr)e une race de mauviettes," "[33500]les ramenant au stade primitif de la simple survie." "[37000]|ca n'aurait jamais d(u arriver." - "[39000]Moi, j'arrive." + "[39000]Moi j'arrive." "[40000]L')eclosion commence dans 15 secondes." - "[43000]Trop tard pour un avortemen!" + "[43000]Trop tard pour un avortement!" "[45000]Pas sans le coeur de l'op)eration!" "[47000]Noooon!" "[49500]10..." @@ -260,7 +260,7 @@ const char *STR_FR[] = { "" /* 27 */ , "Utilisez les touches fl)ech)ees pour@aller dans le salon de musique." /* 28 */ , "OK. On va bouger un peu.@Appuyez sur la touche de saut." /* 29 */ , "Recommencez en appuyant sur une touche fl)ech)ee@et je sauterai dans cette direction." - /* 30 */ , "Oh! Voici le grand hall!@Excusez le d)esordre, mais je veux tout envoyer au garde-meuble@mais les d)em)enageurs ne sont toujours pas arriv)es." + /* 30 */ , "Oh! Voici le grand hall!@Excusez le d)esordre, mais je veux tout envoyer au garde-meuble@et les d)em)enageurs ne sont toujours pas arriv)es." /* 31 */ , "Courez vers une caisse et appuyez@en m(eme temps sur la touche fl)ech)ee haut et la touche d'action@je sauterai dessus." /* 32 */ , "C')etait autrefois la salle de bal,@mais je l'ai transform)ee en salle de gym personnelle.@C'est chouette, non?@Allez, un peu d'exercice." /* 33 */ , "Je ne cours pas sans arr(et dans ce jeu en fait.@Quand je dois (etre prudente, je peux aussi marcher.@Appuyez sur la touche de marche et avancez@jusqu'$a la ligne blanche." @@ -309,47 +309,47 @@ const char *STR_FR[] = { "" , "La Forteresse Atlantique" , "La Ruche" // TR2 levels - , "Lara's Home" - , "The Great Wall" - , "Venice" - , "Bartoli's Hideout" - , "Opera House" - , "Offshore Rig" - , "Diving Area" - , "40 Fathoms" - , "Wreck of the Maria Doria" - , "Living Quarters" - , "The Deck" - , "Tibetan Foothills" - , "Barkhang Monastery" - , "Catacombs of the Talion" - , "Ice Palace" - , "Temple of Xian" - , "Floating Islands" - , "The Dragon's Lair" + , "La Demeure de Lara" + , "La Grande Muraille" + , "Venise" + , "La Cache de Bartoli" + , "L'Op)era" + , "La plate-forme p)etroli(ere" + , "L'aire de plongeon" + , "Par 40 brasses de fond" + , "L')epave du Maria Doria" + , "Les Quartiers d')equipage" + , "Le pont" + , "Les collines tib)etaines" + , "Monast(ere de Barkhang" + , "Les Catacombes du Talion" + , "Le Palais des Glaces" + , "Le Temple de Xian" + , "Les ÃŽles du Ciel" + , "L'Antre du Dragon" , "Home Sweet Home" // TR3 levels - , "Lara's House" - , "Jungle" - , "Temple Ruins" - , "The River Ganges" - , "Caves Of Kaliya" - , "Coastal Village" - , "Crash Site" - , "Madubu Gorge" - , "Temple Of Puna" - , "Thames Wharf" + , "La Demeure de Lara" + , "La Jungle" + , "Les Ruines du Temple" + , "Le Gange" + , "Les Grottes de Kaliya" + , "Le Village Côtier" + , "Le Lieu du Crash" + , "La Gorge de Madubu" + , "Le Temple de Puna" + , "Les Quais de la Tamise" , "Aldwych" - , "Lud's Gate" - , "City" - , "Nevada Desert" - , "High Security Compound" - , "Area 51" - , "Antarctica" - , "RX-Tech Mines" - , "Lost City Of Tinnos" - , "Meteorite Cavern" - , "All Hallows" + , "Le Portail du Lude" + , "La Ville" + , "Le Désert du Nevada" + , "Quartier de Haute Sécurit)e" + , "La Zone 51" + , "L'Antarctique" + , "Les Mines de RX-Tech" + , "La Cité Perdu de Tinos" + , "La Caverne du M)etéore" + , "L')Eglise Hallows" }; #endif diff --git a/src/lang/glyph_ru.h b/src/lang/glyph_ru.h index 5b40c24e..8015f558 100644 --- a/src/lang/glyph_ru.h +++ b/src/lang/glyph_ru.h @@ -1,207 +1,136 @@ #ifndef __GLYPH_RU__ #define __GLYPH_RU__ -static unsigned int size_GLYPH_RU = 3169; +static unsigned int size_GLYPH_RU = 2035; static unsigned char GLYPH_RU[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x30, 0x08, 0x03, 0x00, 0x00, 0x00, 0xc9, 0xa1, 0x54, - 0x4d, 0x00, 0x00, 0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, - 0x65, 0x00, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x61, - 0x64, 0x79, 0x71, 0xc9, 0x65, 0x3c, 0x00, 0x00, 0x03, 0x66, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, - 0x4c, 0x3a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65, - 0x67, 0x69, 0x6e, 0x3d, 0x22, 0xef, 0xbb, 0xbf, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x57, 0x35, - 0x4d, 0x30, 0x4d, 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, - 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x22, 0x3f, 0x3e, 0x20, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, - 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, - 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, - 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x58, 0x4d, 0x50, - 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x33, 0x2d, 0x63, 0x30, 0x31, 0x31, 0x20, 0x36, - 0x36, 0x2e, 0x31, 0x34, 0x35, 0x36, 0x36, 0x31, 0x2c, 0x20, 0x32, 0x30, 0x31, 0x32, 0x2f, 0x30, - 0x32, 0x2f, 0x30, 0x36, 0x2d, 0x31, 0x34, 0x3a, 0x35, 0x36, 0x3a, 0x32, 0x37, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x3e, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, - 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, - 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, - 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, - 0x74, 0x61, 0x78, 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x61, - 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, - 0x70, 0x4d, 0x4d, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, - 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, - 0x2f, 0x6d, 0x6d, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x73, 0x74, 0x52, 0x65, - 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, - 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x73, - 0x54, 0x79, 0x70, 0x65, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x66, - 0x23, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, - 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, - 0x4d, 0x3a, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, - 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x42, 0x42, - 0x34, 0x35, 0x44, 0x30, 0x32, 0x41, 0x33, 0x38, 0x33, 0x36, 0x45, 0x39, 0x31, 0x31, 0x41, 0x46, - 0x41, 0x46, 0x45, 0x31, 0x39, 0x34, 0x30, 0x32, 0x31, 0x32, 0x32, 0x37, 0x37, 0x46, 0x22, 0x20, - 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, - 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x37, 0x35, 0x38, 0x44, 0x39, 0x41, - 0x36, 0x31, 0x34, 0x32, 0x45, 0x39, 0x31, 0x31, 0x45, 0x39, 0x41, 0x45, 0x35, 0x43, 0x46, 0x35, - 0x46, 0x42, 0x33, 0x44, 0x37, 0x46, 0x44, 0x41, 0x35, 0x41, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, - 0x4d, 0x3a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, - 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x37, 0x35, 0x38, 0x44, 0x39, 0x41, 0x36, 0x30, 0x34, 0x32, - 0x45, 0x39, 0x31, 0x31, 0x45, 0x39, 0x41, 0x45, 0x35, 0x43, 0x46, 0x35, 0x46, 0x42, 0x33, 0x44, - 0x37, 0x46, 0x44, 0x41, 0x35, 0x41, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x50, - 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20, 0x43, 0x53, 0x36, 0x20, 0x28, 0x57, 0x69, - 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x29, 0x22, 0x3e, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, - 0x44, 0x65, 0x72, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x20, 0x73, 0x74, 0x52, 0x65, - 0x66, 0x3a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, - 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x36, 0x45, 0x42, 0x33, 0x33, 0x37, 0x36, 0x37, 0x45, 0x39, - 0x34, 0x32, 0x45, 0x39, 0x31, 0x31, 0x42, 0x46, 0x32, 0x46, 0x41, 0x35, 0x36, 0x41, 0x42, 0x35, - 0x44, 0x38, 0x43, 0x34, 0x31, 0x38, 0x22, 0x20, 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x64, 0x6f, - 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, - 0x64, 0x3a, 0x42, 0x42, 0x34, 0x35, 0x44, 0x30, 0x32, 0x41, 0x33, 0x38, 0x33, 0x36, 0x45, 0x39, - 0x31, 0x31, 0x41, 0x46, 0x41, 0x46, 0x45, 0x31, 0x39, 0x34, 0x30, 0x32, 0x31, 0x32, 0x32, 0x37, - 0x37, 0x46, 0x22, 0x2f, 0x3e, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, - 0x44, 0x46, 0x3e, 0x20, 0x3c, 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, - 0x20, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x22, - 0x72, 0x22, 0x3f, 0x3e, 0xb9, 0xc4, 0x8c, 0x25, 0x00, 0x00, 0x00, 0x3f, 0x50, 0x4c, 0x54, 0x45, - 0xce, 0x94, 0x5a, 0xff, 0xef, 0x8c, 0xa5, 0x6b, 0x39, 0xff, 0xff, 0x9c, 0xa5, 0x7b, 0x4a, 0xdc, - 0xa0, 0x64, 0x70, 0x5c, 0x2c, 0x30, 0x0c, 0x04, 0xe8, 0xc0, 0x70, 0xff, 0xd6, 0x7b, 0x58, 0x44, - 0x00, 0xc8, 0x90, 0x58, 0xb0, 0x80, 0x50, 0x73, 0x5a, 0x29, 0x8c, 0x5a, 0x29, 0xb5, 0x84, 0x52, - 0xef, 0xc6, 0x73, 0xde, 0xa5, 0x63, 0x31, 0x08, 0x00, 0x5a, 0x42, 0x00, 0xff, 0xff, 0xff, 0xed, - 0xab, 0xcf, 0x9c, 0x00, 0x00, 0x00, 0x15, 0x74, 0x52, 0x4e, 0x53, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x2b, 0xd9, 0x7d, 0xea, 0x00, 0x00, 0x08, 0x25, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xec, 0x9a, - 0x89, 0x72, 0xdb, 0x38, 0x0c, 0x86, 0x29, 0xfa, 0x88, 0x93, 0x5d, 0x9e, 0xd0, 0xfb, 0x3f, 0xeb, - 0x02, 0x20, 0x09, 0x02, 0x94, 0xdd, 0xc6, 0x1d, 0xef, 0x4c, 0x32, 0x8d, 0x26, 0x76, 0x4b, 0xf1, - 0x02, 0x3e, 0x41, 0x14, 0x7f, 0x58, 0x6e, 0xff, 0xcb, 0x0f, 0xf7, 0x03, 0xe0, 0x8b, 0x1f, 0x91, - 0x0e, 0xe0, 0xaf, 0xcf, 0xb7, 0x1f, 0xc7, 0x67, 0x00, 0x04, 0x39, 0xda, 0x89, 0xb0, 0x9e, 0x90, - 0x53, 0xb3, 0xb0, 0xf7, 0x4f, 0x3b, 0x05, 0xc6, 0xb8, 0x31, 0xb3, 0x32, 0xc0, 0x98, 0xb3, 0x9a, - 0xa7, 0x5b, 0xdd, 0x69, 0xbe, 0xc7, 0x6d, 0xcb, 0x09, 0xf8, 0xcb, 0xcc, 0x1f, 0x1e, 0x38, 0x4c, - 0xed, 0x61, 0xcb, 0x1b, 0x7e, 0x4a, 0x1c, 0xc6, 0x1f, 0x00, 0x89, 0x99, 0x08, 0xa0, 0x6c, 0xd8, - 0x05, 0x8f, 0x3e, 0x21, 0x97, 0xf5, 0x89, 0x3d, 0xd0, 0x89, 0xad, 0x0c, 0x00, 0xf4, 0xbf, 0x88, - 0x95, 0x61, 0xcb, 0x7c, 0x2a, 0x66, 0x6c, 0x5d, 0x84, 0x80, 0xa7, 0xc6, 0x9e, 0x4e, 0xe7, 0x14, - 0x87, 0x0b, 0x34, 0x5e, 0xf2, 0xb3, 0x9e, 0x86, 0x9f, 0xb5, 0xa0, 0xff, 0xdd, 0x63, 0xa1, 0x7a, - 0x9c, 0x70, 0x00, 0x22, 0xd7, 0xe9, 0xab, 0x03, 0x38, 0x9d, 0x60, 0x87, 0xd3, 0x49, 0x00, 0x2c, - 0x0e, 0xef, 0x90, 0x3d, 0x40, 0x06, 0xd8, 0x3c, 0x77, 0x08, 0x38, 0x94, 0x01, 0xd0, 0xec, 0x8b, - 0xc3, 0x02, 0x87, 0x2d, 0x70, 0x6c, 0x80, 0x32, 0xfc, 0xf5, 0xad, 0x2c, 0x27, 0xc2, 0x46, 0xe5, - 0x9c, 0xc6, 0x28, 0x29, 0x03, 0x03, 0x88, 0x8d, 0x10, 0xfa, 0x5f, 0xa0, 0x68, 0x02, 0xed, 0x5a, - 0xc5, 0xa2, 0x3c, 0xa4, 0xfe, 0x76, 0x7c, 0x29, 0x43, 0xe9, 0x00, 0xfa, 0x10, 0xbe, 0xa0, 0x43, - 0xd9, 0x01, 0x42, 0x6d, 0xc4, 0x04, 0x40, 0x6f, 0x4f, 0xed, 0xb0, 0x56, 0xe6, 0x5b, 0x1c, 0x96, - 0x72, 0xee, 0xe5, 0x20, 0xd7, 0xce, 0xd8, 0xe7, 0xbb, 0x05, 0x0d, 0x00, 0x07, 0xf2, 0x40, 0xd4, - 0xe7, 0x1a, 0x27, 0x00, 0x01, 0xec, 0x0a, 0x00, 0x20, 0x01, 0x74, 0x7e, 0x34, 0x8b, 0x85, 0x0c, - 0x57, 0x00, 0x5a, 0x05, 0xfa, 0xd3, 0x4d, 0xe6, 0x32, 0x88, 0x03, 0xc7, 0x32, 0x13, 0x88, 0xe8, - 0xf3, 0x2e, 0xe3, 0x65, 0xd7, 0x1c, 0x15, 0x83, 0x57, 0x00, 0xe5, 0x08, 0x20, 0x0b, 0x80, 0xd4, - 0xca, 0xbd, 0x07, 0xda, 0x6f, 0x00, 0xf4, 0xa1, 0xc6, 0x88, 0x0c, 0xc0, 0xac, 0x17, 0xca, 0x36, - 0x03, 0x60, 0xce, 0x98, 0x32, 0x02, 0xb8, 0x8c, 0xf1, 0x01, 0x76, 0x65, 0x5f, 0xef, 0x8f, 0xfe, - 0x0f, 0x00, 0xf0, 0x1b, 0x00, 0xe8, 0x21, 0x8f, 0xe0, 0xf7, 0xfb, 0x00, 0x5a, 0x04, 0x94, 0x09, - 0xc0, 0x11, 0x80, 0x62, 0xe6, 0x33, 0x0e, 0xdf, 0x01, 0xa0, 0x97, 0x81, 0x03, 0x00, 0xc8, 0xea, - 0x8e, 0xfc, 0x0c, 0x00, 0x22, 0x90, 0x2f, 0xba, 0x51, 0x54, 0x11, 0xc9, 0x4d, 0x29, 0x9e, 0x1f, - 0x01, 0x58, 0xcb, 0xe8, 0x1c, 0x99, 0xdb, 0x03, 0x80, 0x00, 0x44, 0x02, 0x10, 0x1f, 0x01, 0x70, - 0x04, 0xc0, 0x3d, 0x05, 0x80, 0x56, 0x1d, 0xdd, 0x9e, 0x57, 0x41, 0x05, 0x80, 0xef, 0x49, 0x59, - 0x03, 0xc4, 0xf2, 0x87, 0x00, 0xc8, 0xe1, 0x8b, 0x6e, 0x83, 0x11, 0x69, 0x22, 0x80, 0x8c, 0x7f, - 0x0c, 0x80, 0x2a, 0xcc, 0x24, 0x3e, 0x3b, 0x37, 0x02, 0x80, 0x00, 0x60, 0x80, 0x39, 0x0a, 0xa1, - 0x47, 0x00, 0xbc, 0xf7, 0xcf, 0x00, 0x28, 0xec, 0x5f, 0x9e, 0xf3, 0xe3, 0x0c, 0xb8, 0x40, 0x4d, - 0x00, 0xa5, 0xdd, 0xb3, 0xe3, 0x4e, 0xf9, 0x3d, 0x00, 0xf2, 0xff, 0x72, 0x89, 0x0f, 0x08, 0x60, - 0xd3, 0x76, 0x9b, 0x2a, 0x03, 0xf4, 0xa0, 0x47, 0x00, 0x18, 0x02, 0x12, 0x00, 0x4c, 0xa0, 0x64, - 0x87, 0xee, 0xc3, 0x7e, 0x0f, 0x00, 0x11, 0xa0, 0xe3, 0x29, 0x00, 0xd8, 0x49, 0x01, 0xc0, 0x08, - 0xc0, 0x1b, 0xb4, 0x3c, 0x03, 0x00, 0x4c, 0xc8, 0xa6, 0x2d, 0x5e, 0x70, 0x0d, 0xd0, 0xcf, 0x71, - 0x13, 0xe2, 0x1c, 0xbc, 0x4f, 0x00, 0xa0, 0x15, 0xd0, 0x9b, 0x27, 0x35, 0xf2, 0x80, 0xb9, 0x48, - 0x5a, 0x00, 0xb4, 0xea, 0x80, 0x3b, 0xcf, 0x22, 0x5d, 0x61, 0x15, 0x71, 0x71, 0x29, 0x43, 0xe1, - 0x7b, 0xc6, 0x99, 0xf9, 0x89, 0xf2, 0x04, 0xe0, 0x17, 0x00, 0xde, 0x84, 0x77, 0xee, 0xc7, 0xb0, - 0x20, 0xe2, 0x1a, 0x88, 0x00, 0x06, 0x81, 0xb6, 0x1e, 0x99, 0x10, 0xe7, 0xf0, 0x54, 0x06, 0x78, - 0xd0, 0x83, 0x1e, 0x80, 0xc0, 0x7c, 0x04, 0x4c, 0x20, 0x3a, 0x3e, 0xe8, 0x9e, 0x2d, 0x09, 0x54, - 0x13, 0x0d, 0x60, 0x75, 0xd8, 0x97, 0x94, 0xf0, 0xf2, 0xa6, 0xd1, 0x83, 0x9f, 0x18, 0x06, 0x80, - 0x3f, 0x00, 0xc0, 0x09, 0xb0, 0x7d, 0x7d, 0x00, 0xa0, 0x54, 0x3a, 0xc4, 0xe2, 0xd4, 0xf7, 0x00, - 0x71, 0x4b, 0x54, 0xac, 0x89, 0x42, 0x0e, 0x6f, 0x81, 0xaa, 0x26, 0x50, 0x11, 0x80, 0xfd, 0x7f, - 0x0d, 0xa0, 0x3a, 0x58, 0xfc, 0x5f, 0x01, 0xb4, 0x43, 0x87, 0x0c, 0x9c, 0x27, 0x80, 0xe9, 0x70, - 0xbb, 0x20, 0xa9, 0x14, 0xd3, 0x83, 0x6e, 0x7f, 0x1d, 0x32, 0xec, 0xaf, 0x73, 0x23, 0xa6, 0x1c, - 0x8c, 0xd6, 0xc3, 0x7f, 0x1a, 0x40, 0xcf, 0xd5, 0xb7, 0x09, 0xc3, 0x80, 0x40, 0xeb, 0x1f, 0x01, - 0xc0, 0x81, 0x43, 0x27, 0x90, 0x67, 0xf7, 0xd1, 0x1f, 0x86, 0x01, 0x3d, 0x00, 0xe6, 0x53, 0x31, - 0xd1, 0x7f, 0xd3, 0x74, 0xa8, 0xea, 0x1b, 0x7a, 0x30, 0x57, 0x26, 0x30, 0xfe, 0x8a, 0x3d, 0xe2, - 0x5d, 0x00, 0xab, 0xc3, 0x75, 0x1c, 0xbd, 0x07, 0x9c, 0x79, 0xc9, 0x38, 0x57, 0xb5, 0x06, 0x3a, - 0xf2, 0xbf, 0xb7, 0x77, 0xad, 0x31, 0x7e, 0xf4, 0x7c, 0xca, 0xa0, 0xda, 0xaa, 0xea, 0x68, 0x11, - 0xe8, 0x3f, 0xfd, 0x13, 0x94, 0x85, 0xbb, 0xed, 0xcf, 0x9b, 0xc9, 0x6e, 0x90, 0x19, 0xb4, 0x4e, - 0x0b, 0x67, 0x0f, 0xab, 0x4f, 0xec, 0x80, 0xc6, 0x8c, 0xe1, 0x85, 0xb5, 0x17, 0xc6, 0x90, 0x06, - 0x23, 0xf0, 0x56, 0x6e, 0xad, 0xaa, 0xd5, 0x5a, 0xf0, 0x23, 0x87, 0x7f, 0x00, 0xbc, 0x4c, 0xb6, - 0x3f, 0x21, 0xf0, 0xbf, 0x12, 0x80, 0x45, 0x2e, 0x2b, 0x45, 0xaf, 0x4a, 0xd6, 0xbf, 0x35, 0xdf, - 0x40, 0x4f, 0x91, 0xa9, 0x9e, 0x7f, 0xe7, 0x3f, 0x89, 0xdd, 0xe8, 0xbf, 0x10, 0x80, 0x29, 0xad, - 0xc7, 0x33, 0x88, 0xe4, 0xba, 0xac, 0xb9, 0xa4, 0x9f, 0x23, 0x6b, 0x5e, 0xe3, 0x82, 0x92, 0x0f, - 0xdc, 0x01, 0xe0, 0x73, 0x09, 0x9b, 0x9d, 0x37, 0x69, 0x5b, 0x8e, 0x5f, 0x07, 0x40, 0x2e, 0x8b, - 0x5a, 0xdc, 0xf0, 0xc1, 0xaf, 0x04, 0x3e, 0x6d, 0x45, 0xf1, 0x9c, 0x20, 0x89, 0x1b, 0x79, 0x0b, - 0xda, 0xff, 0xf4, 0x59, 0xef, 0xdb, 0xbe, 0x07, 0x1f, 0x5d, 0x5f, 0x87, 0xc0, 0x1d, 0x00, 0xe4, - 0xa9, 0x12, 0xdc, 0x0c, 0xa0, 0xcc, 0x13, 0x91, 0x13, 0x18, 0x73, 0xeb, 0x17, 0xb7, 0x04, 0xcf, - 0xce, 0x8a, 0x04, 0xbe, 0x14, 0x00, 0x23, 0x97, 0x63, 0xd4, 0x09, 0x1a, 0x06, 0xc0, 0x3b, 0x9b, - 0x11, 0x02, 0x31, 0x1b, 0x00, 0xa4, 0xdf, 0x41, 0xdd, 0x00, 0x4b, 0x3e, 0x71, 0x5f, 0x13, 0x8c, - 0x6d, 0x2f, 0x0f, 0x45, 0x12, 0x4e, 0x58, 0x75, 0xbd, 0x5e, 0xf9, 0xa3, 0x7a, 0x98, 0xfe, 0xb8, - 0xcc, 0xe8, 0xdd, 0x29, 0x9d, 0x9a, 0x33, 0xf6, 0xb6, 0x92, 0xb3, 0x44, 0xad, 0xd8, 0xbe, 0xfa, - 0x6c, 0x6d, 0x7a, 0x69, 0xdf, 0x52, 0x8a, 0x33, 0xab, 0x88, 0x00, 0xf2, 0xe9, 0x74, 0xca, 0xb0, - 0x2e, 0x83, 0x02, 0x00, 0x3a, 0x80, 0x22, 0x00, 0x32, 0xa5, 0x41, 0x35, 0x80, 0xa2, 0xc4, 0x20, - 0xae, 0x29, 0x66, 0x3c, 0x1c, 0x9f, 0x26, 0x08, 0x4a, 0x5c, 0x91, 0xd2, 0x1d, 0x09, 0xb3, 0xfd, - 0x6d, 0xbb, 0x5d, 0xaf, 0xf8, 0xb7, 0xdd, 0xde, 0x46, 0x3d, 0x1e, 0xef, 0xb3, 0xff, 0x09, 0x23, - 0xce, 0x9f, 0xd4, 0xe6, 0x34, 0x6c, 0x5b, 0x79, 0x7f, 0x1f, 0x33, 0x84, 0x96, 0xf2, 0x0b, 0x23, - 0xef, 0xc3, 0x39, 0x43, 0xfe, 0x52, 0xc3, 0x95, 0x22, 0xed, 0xb1, 0x1c, 0xf8, 0xaf, 0x77, 0x70, - 0xc1, 0x39, 0x23, 0x97, 0xbb, 0x8f, 0x59, 0x01, 0x18, 0x7b, 0x5b, 0x50, 0xb5, 0x79, 0xaa, 0xcd, - 0x42, 0x6b, 0x86, 0xec, 0x6c, 0x03, 0xe5, 0x02, 0xd4, 0x1d, 0x14, 0x4a, 0x39, 0x87, 0x7a, 0x0e, - 0xd3, 0x7f, 0xdb, 0x7e, 0x7f, 0xbb, 0xdc, 0x10, 0xc0, 0xed, 0x22, 0xfe, 0x67, 0xdc, 0xa3, 0x4d, - 0xbd, 0x4f, 0xa6, 0x19, 0xff, 0xd1, 0x57, 0x80, 0x2a, 0x13, 0x10, 0x60, 0xbc, 0xa0, 0xdb, 0x7b, - 0x0e, 0xd2, 0x9e, 0x26, 0x55, 0xd2, 0x83, 0xa5, 0x8c, 0x00, 0x40, 0xcb, 0xf9, 0xe8, 0x1d, 0x18, - 0x80, 0x96, 0xcb, 0xec, 0x22, 0x67, 0x48, 0x0e, 0x11, 0x30, 0x05, 0x2a, 0x56, 0x2a, 0x00, 0xb4, - 0xd7, 0x9d, 0x00, 0x70, 0xbc, 0x6a, 0x01, 0xf0, 0x5e, 0x58, 0x01, 0xe0, 0xf6, 0x45, 0xce, 0x20, - 0x81, 0x9b, 0xf8, 0x4f, 0xbb, 0x5c, 0xbc, 0x49, 0x94, 0x78, 0xcb, 0x25, 0x1b, 0xff, 0x71, 0x01, - 0x01, 0xea, 0x3f, 0x53, 0x64, 0x29, 0xe3, 0xf5, 0x9f, 0x00, 0x8a, 0xf7, 0x0b, 0x80, 0xdd, 0xb6, - 0x07, 0x20, 0x81, 0x2f, 0x00, 0x16, 0xb9, 0xdc, 0xd7, 0x80, 0xe9, 0x2f, 0x8e, 0xb0, 0x00, 0x20, - 0x38, 0x02, 0xa0, 0x5a, 0xf5, 0xbf, 0xd3, 0x78, 0x5e, 0xe5, 0x2c, 0xb1, 0x5c, 0x17, 0xa5, 0x03, - 0xc3, 0xac, 0x76, 0x7c, 0xdc, 0x2e, 0xb7, 0xcb, 0xe5, 0x43, 0xa7, 0x57, 0x74, 0xbe, 0xa3, 0x38, - 0x9f, 0xb3, 0xde, 0x37, 0x80, 0x5b, 0x00, 0x00, 0x09, 0xd2, 0xf7, 0xbe, 0x96, 0xd3, 0xfd, 0x78, - 0x3a, 0x69, 0x00, 0xee, 0x00, 0x60, 0x27, 0x81, 0xdc, 0x3b, 0x38, 0x76, 0x4c, 0x03, 0x28, 0xed, - 0x29, 0xa0, 0x46, 0x88, 0xed, 0x3e, 0x9f, 0x27, 0xb2, 0x4e, 0x11, 0x41, 0x6b, 0x3f, 0xd5, 0x74, - 0xc1, 0x08, 0x98, 0x11, 0x85, 0x11, 0x71, 0x07, 0x40, 0x9c, 0xd7, 0xf4, 0xe3, 0xf6, 0x0f, 0xde, - 0x02, 0x42, 0x80, 0x16, 0x27, 0xbc, 0x05, 0x94, 0x7c, 0x77, 0xb4, 0x64, 0x0a, 0x01, 0x5c, 0xce, - 0x08, 0x80, 0xc8, 0x5b, 0x74, 0x08, 0x85, 0x1f, 0xd4, 0x11, 0xb1, 0xa8, 0xfe, 0x28, 0xe2, 0x95, - 0xb5, 0x1c, 0x71, 0xa6, 0x3d, 0x01, 0x18, 0x1d, 0x10, 0x1f, 0xad, 0x01, 0x6e, 0xca, 0xf5, 0xc4, - 0x09, 0x3d, 0x9b, 0xc3, 0xc3, 0x73, 0xca, 0x61, 0xf2, 0x7f, 0x02, 0xc0, 0x78, 0x8f, 0x74, 0x4e, - 0xe4, 0xb4, 0x1d, 0xef, 0x00, 0x80, 0x08, 0xa0, 0xff, 0xe3, 0xe4, 0xc7, 0xf6, 0xcf, 0xf5, 0x7a, - 0xc1, 0xbf, 0xed, 0xa3, 0xf7, 0xa7, 0xf8, 0x4c, 0x2a, 0x5d, 0x80, 0xfd, 0xeb, 0x24, 0x10, 0x1c, - 0x64, 0x0d, 0xa0, 0x92, 0x43, 0x2a, 0x24, 0x50, 0x29, 0x63, 0x7b, 0xa7, 0xcc, 0xcf, 0x06, 0x40, - 0x1d, 0x00, 0x7a, 0x07, 0x57, 0x7d, 0x42, 0x79, 0xac, 0x9e, 0x32, 0xa8, 0xef, 0xcb, 0xa2, 0xef, - 0x81, 0xb2, 0x1e, 0x72, 0x85, 0xfb, 0x2f, 0x13, 0x8a, 0x00, 0x2d, 0xeb, 0x22, 0x4f, 0x97, 0xf1, - 0x70, 0x3d, 0xb3, 0xca, 0xb6, 0x52, 0x90, 0xc2, 0xbc, 0x01, 0xfe, 0xc5, 0x47, 0xe0, 0x85, 0x3e, - 0x1f, 0xd2, 0x41, 0x67, 0x3f, 0x6a, 0x6a, 0x21, 0x3c, 0x30, 0x02, 0xa7, 0x0f, 0xaa, 0x24, 0x04, - 0x6a, 0x62, 0xcb, 0xea, 0x40, 0xc6, 0xa9, 0x02, 0x48, 0xd3, 0x7e, 0xbe, 0x00, 0x4b, 0x7b, 0x5a, - 0x45, 0x7a, 0x07, 0x77, 0x54, 0xd2, 0x77, 0xf4, 0xf3, 0xbe, 0xe4, 0x0b, 0xf6, 0x55, 0xd0, 0x1f, - 0xf4, 0xf6, 0x03, 0x21, 0x7f, 0x68, 0xbe, 0xbf, 0xbd, 0xbd, 0xc9, 0xe7, 0x5e, 0xfd, 0xcc, 0x47, - 0xe8, 0x72, 0x5d, 0xca, 0xf3, 0x84, 0x98, 0xb7, 0xff, 0xa2, 0xbd, 0x6a, 0xf2, 0x23, 0x87, 0x7f, - 0x00, 0x7c, 0xff, 0x83, 0x9e, 0xdc, 0xe1, 0x4f, 0x93, 0x0c, 0xdf, 0x1f, 0x40, 0x88, 0x27, 0x87, - 0xa2, 0xde, 0xb9, 0xbf, 0x16, 0x00, 0xee, 0x7d, 0x1d, 0x25, 0x24, 0xfe, 0x1c, 0xc0, 0x31, 0x29, - 0xf4, 0x9d, 0x0e, 0xd2, 0x79, 0xf0, 0xa7, 0xee, 0x33, 0x80, 0x40, 0xef, 0x5b, 0x98, 0xf7, 0x61, - 0x9e, 0x41, 0xa2, 0xde, 0x98, 0xf9, 0xa6, 0x8b, 0x20, 0x6c, 0xb8, 0xb5, 0x98, 0xfa, 0xb5, 0xff, - 0x98, 0x9b, 0x87, 0x3b, 0xab, 0xbe, 0x5e, 0x1d, 0x0e, 0xef, 0xa9, 0xa2, 0x3e, 0xab, 0xe3, 0xa7, - 0xb5, 0xf5, 0x9d, 0x9c, 0x5e, 0x94, 0xb3, 0x47, 0xb6, 0xf7, 0xf4, 0xbf, 0x3e, 0x77, 0x78, 0x61, - 0x09, 0x5e, 0x0a, 0x80, 0xd4, 0x99, 0xf3, 0x35, 0x4f, 0xbd, 0x9c, 0xe6, 0x46, 0xf9, 0x8e, 0xbe, - 0x5e, 0x1d, 0xc6, 0xf6, 0x35, 0x14, 0x05, 0xe0, 0x64, 0x93, 0x8c, 0x3d, 0xe7, 0x28, 0x23, 0x1c, - 0x72, 0x90, 0x7e, 0xd5, 0xff, 0x34, 0x5f, 0xc6, 0x73, 0x5d, 0x42, 0x73, 0xfd, 0xbc, 0x40, 0xc1, - 0x4a, 0xc3, 0x17, 0x00, 0x40, 0xf9, 0xed, 0x3d, 0x6e, 0x67, 0xab, 0x89, 0x00, 0xa7, 0xf5, 0xb8, - 0xd1, 0xd7, 0x8b, 0xc3, 0x07, 0x00, 0xc0, 0x29, 0x26, 0x2d, 0x7e, 0x38, 0x60, 0x44, 0xac, 0xd8, - 0xea, 0x83, 0xfe, 0xa7, 0x86, 0xc1, 0x51, 0xda, 0x31, 0xf3, 0xee, 0x15, 0xeb, 0x83, 0x16, 0x47, - 0x21, 0xff, 0x1f, 0x00, 0x92, 0x06, 0xe0, 0x16, 0x00, 0x8b, 0xbe, 0x7e, 0x08, 0x60, 0xbe, 0xc1, - 0xa0, 0x33, 0x42, 0xbd, 0x28, 0x23, 0x2c, 0xd5, 0x07, 0xfd, 0x4f, 0x0d, 0xdb, 0x5b, 0x20, 0xcd, - 0x06, 0xba, 0x1b, 0xad, 0x3a, 0x4c, 0x2f, 0x06, 0xe0, 0x1d, 0xe9, 0x0d, 0xbc, 0x0b, 0xd4, 0x04, - 0x75, 0x13, 0x09, 0x47, 0xe2, 0xde, 0xe8, 0xeb, 0xc5, 0xe1, 0x51, 0x56, 0x19, 0x11, 0x8a, 0x27, - 0x25, 0x87, 0x5b, 0x71, 0x8c, 0xb0, 0x54, 0x0f, 0xfd, 0xef, 0xf4, 0x0b, 0x1c, 0x85, 0x73, 0x14, - 0xb9, 0xa9, 0x28, 0x70, 0x29, 0x98, 0x5f, 0xb7, 0xcf, 0xaf, 0x05, 0x80, 0xf2, 0x8d, 0x7e, 0x3d, - 0xf5, 0x55, 0x11, 0x26, 0xb1, 0x5b, 0xe7, 0xef, 0xf7, 0x60, 0xf4, 0xf5, 0xe2, 0x30, 0xbf, 0x90, - 0xc1, 0x3f, 0xcf, 0x57, 0x79, 0x1f, 0x00, 0xaf, 0x98, 0x7a, 0x21, 0xa3, 0x15, 0x25, 0x02, 0x6c, - 0x35, 0x3f, 0xc1, 0xec, 0x15, 0xa6, 0xc1, 0x5a, 0x04, 0xd4, 0x61, 0x51, 0x9a, 0x9a, 0x1a, 0x01, - 0xd4, 0x17, 0x6f, 0x85, 0x2b, 0xa7, 0xcd, 0x44, 0x7d, 0x15, 0x94, 0xa3, 0x94, 0x37, 0x33, 0x02, - 0xd1, 0x99, 0x77, 0x9c, 0xf4, 0xfb, 0x08, 0x35, 0xf7, 0xdf, 0xa6, 0x47, 0x0f, 0x48, 0x78, 0x89, - 0x75, 0x7e, 0x81, 0x8b, 0x32, 0xc2, 0x52, 0xdd, 0x81, 0x27, 0xf5, 0x06, 0x0c, 0x1a, 0x43, 0x49, - 0x90, 0xe1, 0x29, 0x27, 0x19, 0x9d, 0x98, 0x53, 0x5f, 0x0f, 0xc0, 0xe4, 0xa7, 0xc6, 0xcf, 0xf1, - 0x60, 0x00, 0x28, 0x7d, 0x3d, 0x1d, 0xae, 0x5a, 0xfe, 0xd2, 0xef, 0xf1, 0x75, 0x26, 0x04, 0xfc, - 0x04, 0xda, 0x8a, 0x32, 0xc2, 0x52, 0x7d, 0xd0, 0xff, 0xdc, 0x90, 0xaf, 0xc2, 0xf0, 0x9f, 0x12, - 0x1e, 0x2a, 0x41, 0xe2, 0x5f, 0x4b, 0xc0, 0xed, 0xe6, 0xd7, 0x76, 0xcd, 0x61, 0x91, 0xf0, 0x56, - 0xef, 0xdf, 0x95, 0xf9, 0x77, 0xd2, 0x09, 0xa3, 0x58, 0x1f, 0x54, 0x3f, 0xd0, 0xff, 0x55, 0xbf, - 0x8f, 0x40, 0x41, 0x6a, 0x13, 0x0e, 0x2f, 0x04, 0xf0, 0x9f, 0x00, 0x03, 0x00, 0x78, 0x70, 0x66, - 0x8d, 0x8a, 0xcc, 0xf4, 0x08, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, - 0x82, + 0x4d, 0x00, 0x00, 0x00, 0x3f, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0x5a, 0x42, 0x00, 0xef, + 0xc6, 0x73, 0xde, 0xa5, 0x63, 0xa5, 0x6b, 0x39, 0xb5, 0x84, 0x52, 0xce, 0x94, 0x5a, 0x73, 0x5a, + 0x29, 0x31, 0x08, 0x00, 0x8c, 0x5a, 0x29, 0xff, 0xef, 0x8c, 0xa5, 0x7b, 0x4a, 0xdc, 0xa0, 0x64, + 0xff, 0xff, 0x9c, 0x70, 0x5c, 0x2c, 0xff, 0xd6, 0x7b, 0x30, 0x0c, 0x04, 0xe8, 0xc0, 0x70, 0x58, + 0x44, 0x00, 0xc8, 0x90, 0x58, 0xb0, 0x80, 0x50, 0x53, 0xa3, 0xe1, 0x5f, 0x00, 0x00, 0x00, 0x01, + 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66, 0x00, 0x00, 0x07, 0x62, 0x49, 0x44, 0x41, + 0x54, 0x78, 0x5e, 0xed, 0x9a, 0xeb, 0x72, 0x22, 0xbb, 0x0e, 0x85, 0x59, 0x92, 0xaf, 0x0d, 0xe4, + 0x32, 0xfb, 0xbc, 0xff, 0xb3, 0x1e, 0xc9, 0xa0, 0x5e, 0x36, 0x81, 0x9d, 0xa4, 0x8a, 0x1f, 0xd9, + 0x53, 0x51, 0xc5, 0xf4, 0x2c, 0x59, 0x72, 0x5b, 0xdf, 0x18, 0x06, 0x75, 0xe6, 0xf0, 0xb3, 0xed, + 0xd7, 0x7e, 0xad, 0xbb, 0x61, 0xbc, 0x7c, 0x35, 0x9e, 0xf6, 0x95, 0x84, 0xba, 0x1b, 0x35, 0x1d, + 0xb3, 0x8b, 0xe2, 0x30, 0x46, 0xb8, 0xb0, 0x6c, 0x2e, 0xee, 0x3c, 0x6d, 0x80, 0xdb, 0xa1, 0xa0, + 0x66, 0xd4, 0x1c, 0x4e, 0xb5, 0x6d, 0x92, 0x31, 0x5e, 0x96, 0xfb, 0xd7, 0x07, 0x05, 0x7b, 0x3c, + 0x36, 0xd9, 0x6c, 0xa8, 0x6b, 0x56, 0xb2, 0xec, 0x8f, 0x80, 0xaa, 0x6e, 0x96, 0x62, 0xe6, 0x2a, + 0x34, 0x1d, 0xee, 0xd9, 0xdc, 0x34, 0x00, 0xf8, 0x9f, 0xba, 0x74, 0x73, 0xcb, 0x70, 0x75, 0xb1, + 0x68, 0xdd, 0x09, 0x24, 0x0f, 0x4e, 0xee, 0x96, 0xdc, 0xa3, 0x04, 0x5f, 0x2f, 0x27, 0xce, 0x8b, + 0xcc, 0xb3, 0xe0, 0xd5, 0x1d, 0xea, 0xf3, 0x76, 0xc3, 0x00, 0xe4, 0xa5, 0xfb, 0xcb, 0x15, 0xc0, + 0xf9, 0x8c, 0x03, 0xce, 0xe7, 0x00, 0xb0, 0x16, 0x6c, 0x06, 0x49, 0x80, 0x00, 0x5b, 0xc2, 0x88, + 0xb7, 0xa5, 0x16, 0x00, 0x97, 0xfd, 0x75, 0xee, 0xa0, 0xfa, 0xda, 0x80, 0x46, 0xbd, 0x69, 0x68, + 0x3a, 0xea, 0xe6, 0x5a, 0x72, 0x60, 0xcc, 0x02, 0x07, 0xe0, 0xe3, 0x5a, 0xbf, 0x42, 0x07, 0x01, + 0xe6, 0xbb, 0x5f, 0x59, 0xe1, 0xc8, 0x5f, 0xd6, 0xa7, 0x86, 0x5e, 0x01, 0x5c, 0x21, 0x26, 0xdd, + 0x04, 0x52, 0x20, 0xa2, 0x17, 0x62, 0x3b, 0x80, 0x6b, 0xbc, 0xc7, 0x41, 0x64, 0xc8, 0x0f, 0x05, + 0x53, 0xfb, 0x85, 0x7f, 0x63, 0x61, 0xdc, 0x9f, 0x5d, 0xa2, 0xc0, 0x8b, 0x03, 0x26, 0x79, 0xc3, + 0xc9, 0x81, 0xcd, 0xf4, 0x04, 0x00, 0x59, 0x60, 0x00, 0x22, 0xac, 0xab, 0xd7, 0x1e, 0x00, 0x22, + 0xdf, 0xeb, 0xc9, 0xa0, 0xc6, 0x70, 0xde, 0xd7, 0x83, 0x40, 0x97, 0x62, 0x8e, 0x58, 0x4f, 0xca, + 0x28, 0x94, 0x40, 0x6f, 0x01, 0x28, 0x01, 0xdc, 0x16, 0x0c, 0xc9, 0x43, 0x47, 0x06, 0xb6, 0xd8, + 0x3a, 0xeb, 0x8b, 0x4b, 0x00, 0x58, 0x3e, 0x2f, 0xb8, 0xb7, 0x15, 0x00, 0x91, 0x67, 0x31, 0x00, + 0xa7, 0x58, 0x1f, 0x08, 0x68, 0xcc, 0xf7, 0xfa, 0x03, 0x00, 0x3e, 0x01, 0x60, 0x15, 0x0e, 0x00, + 0xc9, 0xc5, 0x0a, 0x80, 0xf1, 0x5d, 0x09, 0xa0, 0x38, 0x00, 0x02, 0x67, 0xc1, 0x8f, 0x01, 0x98, + 0x3d, 0x06, 0x00, 0xe1, 0x3b, 0xf2, 0x13, 0x00, 0x24, 0x20, 0xa3, 0x7e, 0x42, 0x15, 0x2c, 0x00, + 0xbb, 0x28, 0x1e, 0x01, 0xa0, 0x8e, 0x6c, 0x15, 0x78, 0x59, 0xa1, 0xb4, 0x3b, 0x80, 0xfe, 0x08, + 0x40, 0x71, 0x00, 0xe5, 0x5b, 0x00, 0xfc, 0x53, 0x67, 0x8e, 0xef, 0xbd, 0x07, 0x80, 0x48, 0x80, + 0x68, 0x10, 0x80, 0x3e, 0x06, 0xc0, 0x82, 0x4f, 0x94, 0x9e, 0xb2, 0x9e, 0x00, 0xaf, 0xff, 0x31, + 0x00, 0xcd, 0x58, 0x6f, 0x92, 0xa4, 0x14, 0x49, 0x3b, 0x0e, 0x95, 0x2e, 0xa5, 0x8b, 0x3e, 0x04, + 0x90, 0x52, 0xfa, 0x0e, 0x00, 0x1d, 0xf5, 0x09, 0xef, 0xaf, 0x2a, 0x9a, 0x03, 0x40, 0x14, 0x6c, + 0xba, 0x7e, 0x0e, 0x80, 0xf5, 0x9f, 0x4e, 0xfd, 0x96, 0x00, 0x01, 0x88, 0xb2, 0xc4, 0xb5, 0xe0, + 0x55, 0xf3, 0x08, 0x48, 0x01, 0x95, 0xaa, 0x14, 0x55, 0xb8, 0x67, 0x05, 0x10, 0x04, 0xdc, 0xbe, + 0x05, 0xc0, 0x92, 0x26, 0x00, 0xb9, 0x77, 0xd5, 0xac, 0xdf, 0x01, 0x80, 0xe5, 0xc8, 0xe6, 0xad, + 0x9f, 0xfa, 0x29, 0xcf, 0xff, 0x8e, 0x63, 0x06, 0xe0, 0xf5, 0x7f, 0x09, 0x00, 0x89, 0x96, 0x34, + 0xa9, 0x6e, 0x1a, 0x40, 0x48, 0x25, 0x80, 0x61, 0x30, 0x2b, 0x47, 0x4a, 0xcd, 0xc0, 0x74, 0xe2, + 0xfa, 0x8d, 0x86, 0x16, 0x38, 0xb5, 0xe5, 0xfe, 0x4e, 0x99, 0x00, 0x52, 0x00, 0xa0, 0xa6, 0x41, + 0xae, 0x16, 0xf1, 0x3d, 0x4b, 0x37, 0x00, 0x41, 0x00, 0xaa, 0x20, 0x80, 0x70, 0x2c, 0x00, 0x12, + 0xb8, 0xe8, 0x1d, 0x20, 0x70, 0x00, 0x0b, 0xf3, 0xd0, 0x01, 0xa0, 0xf7, 0x51, 0x15, 0x03, 0x08, + 0xe0, 0x63, 0xc1, 0x49, 0x73, 0xce, 0x36, 0x22, 0x03, 0x2a, 0x37, 0x00, 0xd2, 0x07, 0x00, 0xc6, + 0xdc, 0xe2, 0xdb, 0x03, 0x00, 0xda, 0xdc, 0xf6, 0xf8, 0x7c, 0xfd, 0x0e, 0xd0, 0xb7, 0xec, 0xb2, + 0x65, 0x71, 0xe2, 0x62, 0xe9, 0xcc, 0x07, 0x01, 0x40, 0x3e, 0x01, 0xd0, 0x0a, 0x58, 0xef, 0x5d, + 0x00, 0xc3, 0x96, 0x23, 0x83, 0x23, 0x01, 0xb0, 0xe0, 0xee, 0x12, 0x59, 0xc3, 0x62, 0x03, 0x12, + 0x47, 0x86, 0xf5, 0x96, 0xd2, 0x79, 0x42, 0x86, 0xb1, 0x7e, 0x5f, 0x20, 0x82, 0x79, 0xb8, 0x77, + 0xc0, 0x55, 0x4e, 0x18, 0x00, 0x20, 0x52, 0xaf, 0x04, 0x24, 0xd2, 0x99, 0x8f, 0x40, 0xec, 0x3c, + 0x42, 0xc5, 0x7c, 0x46, 0x66, 0x41, 0x8d, 0x6f, 0x68, 0x32, 0x9f, 0xb6, 0xd0, 0x86, 0x21, 0x6b, + 0xbf, 0x0b, 0x60, 0x2d, 0x38, 0xc2, 0x99, 0x81, 0x63, 0x71, 0x3b, 0x36, 0x1e, 0x50, 0x35, 0xcd, + 0x23, 0x70, 0x09, 0xb6, 0x61, 0x82, 0xda, 0x05, 0x35, 0x2f, 0x06, 0xc0, 0xf5, 0x75, 0xd4, 0x88, + 0x70, 0xbd, 0xe6, 0x63, 0xd8, 0x45, 0x87, 0x93, 0xf3, 0xd4, 0x66, 0x4c, 0xa6, 0xe3, 0xae, 0x8f, + 0x0a, 0x54, 0xb1, 0xfd, 0x0f, 0x39, 0x00, 0x3a, 0x81, 0x2c, 0xfb, 0xe3, 0x0e, 0x9e, 0x68, 0xbf, + 0xf6, 0xdb, 0xb6, 0x7f, 0x3d, 0xb2, 0xff, 0x24, 0x00, 0x6c, 0x97, 0xd7, 0x8e, 0x79, 0x51, 0xac, + 0x8f, 0x01, 0x74, 0x25, 0x13, 0x22, 0x9f, 0x02, 0x60, 0xfb, 0x2a, 0x3d, 0xfd, 0x20, 0x00, 0x6c, + 0xad, 0xcd, 0xa2, 0xbf, 0x17, 0xed, 0xdc, 0xf0, 0xd6, 0xcd, 0xa7, 0x98, 0x4a, 0x88, 0xf6, 0x81, + 0x09, 0x00, 0xbe, 0x06, 0x00, 0x02, 0x58, 0x42, 0xff, 0x39, 0x00, 0x44, 0xeb, 0x4d, 0xfd, 0xda, + 0x9a, 0x8a, 0xce, 0xdd, 0x9b, 0xf9, 0x76, 0x24, 0x7d, 0x13, 0x98, 0xcd, 0xf5, 0x67, 0xe0, 0xcb, + 0xc0, 0x3d, 0x14, 0x99, 0x04, 0x7e, 0x20, 0x00, 0xaf, 0x14, 0x2a, 0x0b, 0x00, 0xa5, 0xa3, 0x6f, + 0x60, 0xcf, 0x38, 0x74, 0xc6, 0x37, 0xa1, 0x3b, 0x81, 0x1f, 0x05, 0xc0, 0x6c, 0xfd, 0x34, 0x1b, + 0x1c, 0xd8, 0x8c, 0xa8, 0xe2, 0x80, 0x01, 0x66, 0xe8, 0x05, 0x40, 0x57, 0x85, 0xdb, 0xdd, 0x07, + 0x88, 0xd4, 0x13, 0x64, 0xb8, 0x69, 0x1c, 0x28, 0x9f, 0x7a, 0x79, 0x79, 0xf1, 0xc1, 0x8c, 0x25, + 0xdf, 0x45, 0x4a, 0x70, 0x41, 0x17, 0x80, 0x5d, 0xf1, 0xe2, 0x32, 0xa5, 0x34, 0x5e, 0x82, 0x75, + 0xad, 0x4b, 0xbc, 0x09, 0x8f, 0x65, 0xcd, 0x55, 0xe4, 0x7c, 0x3e, 0x0b, 0x86, 0x20, 0x04, 0x02, + 0xc0, 0x15, 0x80, 0xee, 0x00, 0x04, 0x3e, 0x4f, 0x00, 0x50, 0x35, 0xc5, 0xcf, 0x94, 0x65, 0xbd, + 0x2a, 0xe3, 0x06, 0x04, 0x80, 0x4d, 0x3c, 0x25, 0xde, 0x41, 0x6f, 0xdb, 0xfb, 0xcb, 0x8b, 0xfd, + 0x6c, 0xef, 0x6f, 0x31, 0x6f, 0xf6, 0xca, 0xfc, 0xf3, 0x86, 0x43, 0x3a, 0x2b, 0x88, 0x64, 0xdb, + 0xf4, 0xf5, 0x55, 0xb0, 0xab, 0x3a, 0x2e, 0xb9, 0xc6, 0xfd, 0x71, 0x79, 0x99, 0x96, 0x53, 0xf5, + 0xf8, 0xd0, 0x75, 0xfc, 0xec, 0x09, 0xa5, 0xb0, 0x5d, 0x66, 0xbf, 0x2b, 0x13, 0x00, 0xbd, 0x02, + 0x00, 0x67, 0xcd, 0xa6, 0xaf, 0xd2, 0xad, 0xb1, 0xb5, 0xa8, 0xa2, 0xc0, 0xf4, 0x0e, 0xaa, 0xaa, + 0xc7, 0xda, 0x8e, 0x95, 0xf5, 0x33, 0xfe, 0x42, 0xe0, 0xf4, 0x6e, 0x00, 0xde, 0x4f, 0x7b, 0xfd, + 0xd2, 0x5a, 0x63, 0xbf, 0x0f, 0x11, 0x44, 0xfd, 0x01, 0x00, 0x68, 0x2a, 0x20, 0xe0, 0x6a, 0xbe, + 0x57, 0xa9, 0x11, 0x6f, 0x00, 0x94, 0xf9, 0x9a, 0x6d, 0x3d, 0xc6, 0x43, 0x24, 0x0f, 0x7b, 0x15, + 0x02, 0x38, 0xa0, 0xac, 0x00, 0x00, 0xdc, 0x39, 0x01, 0x60, 0xff, 0xae, 0x98, 0x00, 0xf8, 0x77, + 0x5d, 0x02, 0x28, 0x05, 0x6d, 0x05, 0xe0, 0xf3, 0x8d, 0x00, 0x86, 0x76, 0x2f, 0x09, 0xbc, 0xef, + 0xf5, 0x1f, 0x60, 0xa1, 0x98, 0x9b, 0x37, 0x51, 0xf1, 0xfa, 0x69, 0xc8, 0xb0, 0x7c, 0x6e, 0xaf, + 0x64, 0xa9, 0x5b, 0x26, 0x00, 0x4d, 0xe9, 0x06, 0xc0, 0x61, 0x8d, 0x07, 0x32, 0x80, 0x1d, 0x00, + 0xdb, 0xe5, 0xf9, 0x33, 0x00, 0xcc, 0xd0, 0x5b, 0x00, 0x0e, 0x67, 0x07, 0xd0, 0xd8, 0xfd, 0xef, + 0xeb, 0x25, 0xd6, 0xef, 0xda, 0x00, 0xd0, 0x10, 0xf1, 0xbb, 0xf7, 0xcf, 0xfb, 0xe9, 0xfd, 0x74, + 0xfa, 0xc3, 0x10, 0xa8, 0xd7, 0x4f, 0x91, 0x44, 0xd2, 0x0c, 0xa0, 0xdc, 0x00, 0x40, 0x96, 0x8c, + 0x57, 0xad, 0x71, 0x22, 0xe5, 0x7c, 0x9e, 0x01, 0x94, 0x0f, 0x00, 0x0e, 0x82, 0xc3, 0x9c, 0xb0, + 0x02, 0x50, 0xed, 0xc3, 0x4b, 0x84, 0xdd, 0x7c, 0x8b, 0xc3, 0xf3, 0x1d, 0x40, 0xcc, 0x07, 0xe7, + 0x20, 0xd7, 0xa6, 0x13, 0x55, 0xcb, 0x3d, 0x00, 0x5d, 0xc1, 0xfa, 0xff, 0xb1, 0xb7, 0x80, 0x11, + 0xe0, 0x87, 0x54, 0x6b, 0x24, 0x00, 0x2d, 0x80, 0xea, 0x4e, 0xa0, 0x62, 0x00, 0x28, 0x47, 0x02, + 0x68, 0xc8, 0x68, 0x71, 0x62, 0x71, 0x2c, 0xad, 0x05, 0x1f, 0x9e, 0xd0, 0x25, 0xde, 0x01, 0x30, + 0x21, 0x17, 0x00, 0x85, 0xed, 0x7a, 0x16, 0x05, 0x74, 0x7d, 0x86, 0x97, 0x25, 0x1c, 0x51, 0x3f, + 0x01, 0x34, 0x68, 0x77, 0x5f, 0x00, 0xe0, 0x7a, 0xf7, 0x01, 0x38, 0x81, 0xae, 0x68, 0x51, 0xff, + 0xf6, 0xcf, 0xcb, 0xcb, 0xc9, 0x7e, 0xb6, 0x0b, 0x01, 0xe4, 0x0c, 0x20, 0xf3, 0x71, 0x81, 0xe7, + 0x37, 0x12, 0xa8, 0x05, 0x32, 0x03, 0x68, 0x5e, 0x10, 0x8f, 0x84, 0x77, 0xca, 0x16, 0x5f, 0xa6, + 0xed, 0x4b, 0x00, 0x60, 0xbc, 0x4c, 0x09, 0x2d, 0xe5, 0x52, 0x58, 0xff, 0xe8, 0xef, 0xf5, 0xa6, + 0xbf, 0x87, 0xf9, 0xc2, 0x01, 0x75, 0x74, 0x10, 0x21, 0x01, 0x88, 0xb2, 0x20, 0xae, 0x17, 0x05, + 0x68, 0x4c, 0xed, 0xc0, 0x88, 0xcb, 0x00, 0xfc, 0xef, 0xe5, 0x60, 0x00, 0x6c, 0xfc, 0xd9, 0x13, + 0x54, 0x33, 0xf3, 0xf3, 0xe5, 0x08, 0x07, 0x46, 0x8c, 0xc7, 0x07, 0xed, 0xb8, 0x03, 0xc8, 0xb9, + 0x5d, 0x2e, 0xe0, 0xa3, 0x02, 0x64, 0xee, 0xdf, 0x01, 0xde, 0xc6, 0x67, 0x30, 0x61, 0x6d, 0x97, + 0xe9, 0x59, 0x94, 0xfb, 0x56, 0xed, 0x86, 0xc5, 0x45, 0xb1, 0xac, 0x17, 0x82, 0xb6, 0xce, 0xbf, + 0xbd, 0xbd, 0xc5, 0xb8, 0x3b, 0xcf, 0xe7, 0x11, 0xd4, 0xe1, 0xa5, 0xa6, 0xc3, 0x35, 0xd5, 0x83, + 0x78, 0x86, 0xfc, 0xe5, 0xf6, 0x6b, 0x6c, 0x79, 0xff, 0xf6, 0x67, 0x11, 0xd5, 0x46, 0x58, 0x05, + 0xc6, 0xa0, 0x43, 0x36, 0xc1, 0x5f, 0x0b, 0xa0, 0xf6, 0x73, 0x29, 0xd5, 0xc6, 0xf4, 0x55, 0x19, + 0x3e, 0xa6, 0x90, 0xed, 0xa0, 0xed, 0xef, 0x05, 0x20, 0x82, 0x62, 0x03, 0x04, 0x00, 0x1f, 0x4b, + 0x88, 0x3e, 0x02, 0xc0, 0x86, 0xeb, 0x3f, 0x6b, 0x00, 0xc6, 0x20, 0x91, 0x13, 0xc6, 0x98, 0x0c, + 0xed, 0x5f, 0xeb, 0x87, 0x35, 0x6b, 0x75, 0xa6, 0xf1, 0x25, 0x24, 0x8c, 0x8f, 0xf1, 0x63, 0x88, + 0x8c, 0xf1, 0x65, 0xc3, 0x56, 0x4a, 0x39, 0xcb, 0xcd, 0x6f, 0xaf, 0x25, 0x00, 0xb0, 0xbf, 0xbe, + 0x5f, 0x70, 0x7d, 0xcd, 0xad, 0x6e, 0x68, 0x92, 0xaf, 0x9a, 0xf0, 0x76, 0x1d, 0x17, 0x6a, 0x1a, + 0x33, 0xa8, 0x56, 0x5f, 0x0d, 0x11, 0x0a, 0xcf, 0xe5, 0xb5, 0x69, 0x2b, 0xa9, 0x89, 0x20, 0x74, + 0xe6, 0x17, 0xe5, 0xb5, 0xbf, 0xbe, 0x5b, 0x30, 0x36, 0xd3, 0x4a, 0x5d, 0xd9, 0xfc, 0x87, 0xae, + 0xb1, 0x0c, 0xf5, 0x64, 0x89, 0xfd, 0x3f, 0xef, 0x27, 0xe6, 0x3b, 0x56, 0xce, 0x9f, 0x85, 0xfb, + 0xd1, 0x27, 0x03, 0x90, 0xd6, 0x52, 0x6a, 0x76, 0x59, 0x4e, 0x00, 0x9b, 0x11, 0xf6, 0xd7, 0x73, + 0xc1, 0x8f, 0x01, 0x40, 0x74, 0x3e, 0x03, 0x57, 0x59, 0x03, 0x29, 0xa7, 0xef, 0xf7, 0xff, 0x1e, + 0x58, 0x8b, 0x00, 0x90, 0x23, 0x2e, 0xf3, 0x35, 0x9a, 0xa3, 0x98, 0x7f, 0x3e, 0x80, 0x3c, 0x03, + 0x28, 0x04, 0xc0, 0xfe, 0xfa, 0x0b, 0x00, 0xf8, 0x3f, 0x18, 0xf6, 0x27, 0x42, 0x94, 0x81, 0x90, + 0xd3, 0x0f, 0xfa, 0x7f, 0x0f, 0x44, 0xe1, 0x1e, 0x20, 0x2b, 0x00, 0x68, 0x7e, 0x32, 0x80, 0x54, + 0xbc, 0xdf, 0x28, 0xa9, 0x4d, 0x37, 0x68, 0x5b, 0x69, 0xfb, 0x3c, 0xfb, 0xeb, 0x0f, 0x05, 0x53, + 0xcf, 0x4f, 0x44, 0x52, 0x1b, 0xc7, 0x66, 0x95, 0x01, 0x80, 0xd3, 0x4b, 0xff, 0x4f, 0xe0, 0xea, + 0x36, 0x00, 0x68, 0xbb, 0x10, 0xcb, 0x15, 0xf3, 0xfc, 0x11, 0x87, 0xa7, 0x5a, 0x4b, 0xc5, 0x2c, + 0x35, 0x12, 0x06, 0x20, 0xb9, 0x21, 0x1c, 0x00, 0xfb, 0xeb, 0x8f, 0x05, 0x43, 0x32, 0xa0, 0x80, + 0x46, 0x06, 0x34, 0xb5, 0x56, 0x64, 0x08, 0xca, 0x00, 0xc0, 0x69, 0x2e, 0x7f, 0xd3, 0xff, 0x2b, + 0x70, 0x01, 0x50, 0x5a, 0xec, 0x28, 0x97, 0xd2, 0x08, 0xe0, 0xd9, 0x4d, 0x4c, 0xf3, 0xcd, 0xb7, + 0x5d, 0x69, 0x51, 0x15, 0xf1, 0xa6, 0x91, 0xc6, 0xfe, 0x9a, 0x05, 0xf3, 0x89, 0xd0, 0xb0, 0x1c, + 0x19, 0xc8, 0xa9, 0xc5, 0xf3, 0x00, 0xca, 0x58, 0x81, 0xd3, 0x0b, 0xf0, 0xcc, 0xf5, 0x4b, 0x36, + 0xde, 0x05, 0x88, 0x4a, 0xab, 0x28, 0xdc, 0x19, 0x5b, 0x79, 0x3e, 0x00, 0x3e, 0xce, 0x61, 0x3b, + 0x0b, 0x2c, 0x00, 0x40, 0x1e, 0x2c, 0xb8, 0x31, 0xde, 0x0d, 0xe1, 0x48, 0x79, 0x3f, 0x50, 0x94, + 0xc8, 0x77, 0xa7, 0xd9, 0xff, 0x2f, 0xb7, 0x32, 0x02, 0x1a, 0xf5, 0xfb, 0x41, 0x20, 0x20, 0xcb, + 0x7f, 0x3e, 0x01, 0xdc, 0x69, 0x8d, 0x57, 0x57, 0xa3, 0x6c, 0xb4, 0xfb, 0x19, 0x9c, 0x9b, 0x65, + 0xbb, 0x37, 0xfd, 0xb8, 0xff, 0xa7, 0xaf, 0x0e, 0x09, 0x46, 0x3c, 0xb9, 0x91, 0xff, 0x3f, 0x00, + 0x75, 0x50, 0xd5, 0x0c, 0x7e, 0x2a, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, + 0x42, 0x60, 0x82, }; #endif diff --git a/src/lang/glyph_ru.png b/src/lang/glyph_ru.png index 0c819065..2af50a85 100644 Binary files a/src/lang/glyph_ru.png and b/src/lang/glyph_ru.png differ diff --git a/src/lang/hu.h b/src/lang/hu.h new file mode 100644 index 00000000..26402321 --- /dev/null +++ b/src/lang/hu.h @@ -0,0 +1,355 @@ +#ifndef H_LANG_HU +#define H_LANG_HU + +// Thanks: Varga Viktor + +const char *STR_HU[] = { "" +// help + , "Bet~olt)es..." + , "Nyomj H-t a S)ug)ohoz" + , helpText + , "%s@@@" + "~OL)ESEK %d@@" + "FELVETT %d@@" + "TITKOK %d/%d@@" + "SZ~UKS)EGES ID\"O %s" + , "J)at)ek ment)ese..." + , "Ment)es k)esz!" + , "MENT)ESI HIBA!" + , "IGEN" + , "NEM" + , "Ki" + , "Be" + , "Ki" + , "Side-By-Side" + , "Anaglyph" + , "Osztott k)eperny\"o" + , "VR" + , "Alacsony" + , "K~ozepes" + , "Magas" + , STR_LANGUAGES + , "Alkalmaz" + , "Gamepad 1" + , "Gamepad 2" + , "Gamepad 3" + , "Gamepad 4" + , "Nincs k)esz" + , "1. j)at)ekos" + , "2. j)at)ekos" + , "Nyomj meg egy gombot" + , "%s - Kiv)alaszt" + , "%s - Vissza" +// inventory pages + , "OPCI)OK" + , "T)ARGYLISTA" + , "T)ARGYAK" +// save game page + , "J)at)ek ment)ese?" + , "Aktu)alis poz)ici)o" +// inventory option + , "J)at)ek" + , "T)erk)ep" + , "Ir)anyt\"u" + , "Statisztika" + , "Lara otthona" + , "R)eszletess)egi szintek" + , "Hang" + , "Ir)any)it)as" + , "Gamma" +// passport menu + , "J)at)ek bet~olt)ese" + , ")Uj j)at)ek" + , "Szint )ujraind)it)asa" + , "Kil)ep)es a kezd\"ok)eperny\"oh~oz" + , "Kil)ep)es a j)at)ekb)ol" + , "Szint kiv)alaszt)asa" +// detail options + , "R)eszletek kiv)alaszt)asa" + , "Sz\"ur)es" + , "Vil)ag)it)as" + , ")Arny)ekok" + , "V)iz" + , "VSync" + , "Sztere)o" + , "Egyszer\"u t)argyak" + , "Felbont)as" + , STR_SCALE +// sound options + , "Hanger\"o be)all)it)asa" + , "Visszaver\"od)es" + , "Feliratok" + , "Nyelv" +// controls options + , "Ir)any)it)as be)all)it)asa" + , "Billenty\"uzet" + , "Gamepad" + , "Vibr)aci)o" + , ")Uj c)elpont" + , "Multi-c)elz)as" + // controls + , "Bal", "Jobb", "Fut)as", "Vissza", "Ugr)as", "S)eta", "Akci)o", "Fegyver el\"ov)etel", "N)ez", "Gugol", "L~ok", "Gurul", "T)argylista", "Kezd)es" + , STR_KEYS +// inventory items + , "Ismeretlen" + , "Robban)oanyag" + , "Pisztolyok" + , "Shotgun" + , "Magnumok" + , "Uzik" + , "Pisztoly t~olt)enyek" + , "Shotgun t~olt)enyek" + , "Magnum t~olt)enyek" + , "Uzi t~olt)enyek" + , "Kis Medi pakk" + , "Nagy Medi pakk" + , ")Olomr)ud" + , "Scion" +// keys + , "Kulcs" + , "Ez~ust kulcs" + , "Rozsd)as kulcs" + , "Arany kulcs" + , "Zaf)ir kulcs" + , "Neptunusz kulcs" + , "Atlasz kulcs" + , "Damokl)esz kulcs" + , "Thor kulcs" + , "D)iszes kulcs" +// puzzles + , "Rejtv)eny" + , "Arany szobor" + , "Arany r)ud" + , "Fogasker)ek" + , "Biztos)it)ek" + , "Ankh" + , "H)orusz szeme" + , "Anubisz pecs)etje" + , "Szkarabeusz" + , "Piramis kulcs" +// TR1 subtitles + /* CAFE */ , + "[43500]Mit kell tennie az embernek, ahhoz,@hogy megkapja azt a bizonyos figyelmet?" + "[47500]Neh)ez pontosan megmondani,@de )ugy t\"unik j)ol vagy." + "[50000]Nos, nagyszer\"u. B)ar az az igazs)ag,@nem )en akarlak." + "[54500]Nem?" + "[55000]Nem. Miss Jacqueline Natla szeretne,@a Natla Technologies-t)ol." + "[59000]Tudod, minden f)enyes@)es sz)ep alkot)oja?" + "[64500]Z)arja le, Larson." + "[66000]H~olgyem." + "[68000]Ezt figyeld, Lara." + "[70500]Hogyan )erinti ez a p)enzt)arc)ad?" + "[73500]Bocs. )En csak a sport)ert j)atszom." + "[76000]Akkor tetszeni fog egy nagy park." + "[78000]Peru. Hatalmas hegyl)ancok, amit fel kell fedezni.@J)egfalak. )Eles szikl)ak. Vad szelek." + "[87500])Es itt van ez a kis csecsebecse:@\"osi id\"ok misztikus er\"ovel rendelkez\"o m\"ut)argya" + "[92500]eltemetve Qualopec fel nem lelt s)irj)aban." + "[96000]Ez az ami )erdekel." + "[98000]Holnap elmehetsz.@Elfoglalt vagy holnap?" + /* LIFT */ , + "[49000])Athelyezve Szt. Ferenc kolostor)aba, )uj k)is)ert)esek k)inoznak engem." + "[53500]A testv)erek k~ozt az a h)ir j)arja, hogy a kolostor alatt@ker~ult s)irba Tihocan," + "[60000]egyike az elveszett kontinens, Atlantisz,@ legend)as uralkod)oinak," + "[64500])es vele egy~utt fekszik az \"o darabja@az Atlantiszi Scion-nak." + "[68000]A med)alt felosztott)ak a h)arom uralkod)o k~oz~ott@" + "[72500]ami hatalmas er\"oket f)ekez meg.@Er\"ot, ami meghaladja az alkot)o erej)et is." + "[79000]Izzad a l)abam ezekt\"ol a lehet\"os)egekt\"ol,@amik olyan k~ozel )allnak a haland)o )enemhez." + "[85500]Minden este t)ul teszem magam@ezeken a fant)azi)akon, de ez val)oj)aban egy teszt." + "[92000]" + "[93500]Pierre. Nee. Te szem)et." + /* CANYON */ , + "[13500])Epp most h)uztad a szerencsecsont nagyobb v)eg)et." + "[16500]Mizu." + "[17500]D)elut)an." + "[20000]Ott hagytuk Larson-t szelet aratni, mi?" + "[22500]Ha ez a kifejez)es." + "[24000]Nos, a kis nyaral)asi zavarg)asnak most m)ar v)ege." + "[27000]Itt az ideje visszaadni azt, amit ellopt)al t\"olem." + "[30000]Pr)ob)aljuk meg az )eteldobozt." + "[32000]" + "[42500]Nos? ~Old meg!" + "[45000]H)e!" + "[48000]" + "[50500]Debilek!" + "[53000]" + "[62500]Gyer~unk." + "[65000]" + "[136000]Mi a fene volt ez?" + "[138000]Mi?" + "[138500]Egyszer\"u." + "[140500]Val)osz)in\"uleg csak egy hal." + "[142500]Ez egy hal, k~oly~ok." + "[145000]Ember, meg kell tanulnod laz)itani.@Visszamegyek. J~ossz?" + "[152000]" + "[158000])Alland)o..." + "[160000]Itt van." + "[161500]K)eszen )allsz m)ar?" + /* PRISON */ , + "[00001]Nem tehetik ezt!" + "[01500]El)it)elj~uk ~Ont, atlantiszi Natla a b\"unei)ert." + "[06000]Hatalmaddal er\"oteljesen vissza)el)esei miatt,@ )es mert kiraboltad a mi..." + "[11500]Nem tehetik! )En..." + "[12500]Megsz~untetve a beleegyez)es szabad k~otel)ek)et@amely alatt n)ep~unket ir)any)itj)ak," + "[18500])es megt)amadva Tihocan-t )es engem a sereg~unkkel." + "[23500]A harcosaink t)avoztak a piramisunkb)ol" + "[27000])igy fel tudod haszn)alni a piramist - annak erej)et@a teremt)esre - az esztelen rombol)asodhoz." + "[33500]Esztelen!? N)ezz magadra!" + "[35500]Egyik\"ot~oknek sincs egy lelem)enyes@gondolat se a fej)eben." + "[40500]Pazarl)ok!" + "[41500]Csak csin)alj)atok." + "[44000]Tihocan!" + "[45000]Egy szent helyet haszn)alt)al@saj)at ~or~om~odre," + "[49500]mint valami korcs gy)arat." + "[51000]\"Ok a t)ul)el\"ok. Egy )uj gener)aci)o." + "[54000]Egy lem)esz)arolt halom most." + "[56000])Es te. A pokol torn)ac)ara z)arunk." + "[60000]Az ereid, a sz)ived, a l)abad," + "[64000])es a beteg agyad szil)ardd)a teszi majd a fagyott v)er." + "[70000]~Udv~oz~old az ~or~ok nyugtalans)agod, Natla." + "[73000]Te sem fogsz nyugodni, ak)ar az @)atkozott kontinensed Atlantisz!" + /* 22 */ , + "[04000])Ujra itt?" + "[05500])Es te - a nagy )ujra megnyit)ora, gondolom." + "[09500]Az evol)uci)o bajban van - a term)eszetes szelekci)o m)ara alig )erv)enyes~ul..." + "[13500]Az )uj h)us megjelen)ese fel fogja ism)et gerjeszteni a ter~uleti vit)akat" + "[17500] - meger\"os)it )es el\"oseg)it minket..." + "[20500]M)eg )uj fajokat is l)etre hozhat." + "[22500]Az evol)uci)o szteroidokon, akkor." + "[24500]Egy seggber)ug)as...@a nyomorult Qualopec-enk )es Tihocan-nak nem voltak ~otletei" + "[29500] - Atlantisz kataklizm)aja az )ert)ektelen gyeng)ek verseny)et s)ujtotta..." + "[33500]visszazuhan)asra k)enyszer)itve \"oket )ujra a t)ul)el)es alapjaihoz..." + "[37000]Nem volna szabad )ugy t~ort)ennie." + "[39000]Vagy )igy." + "[40000]A kikel)es 15 m)asodperc m)ulva kezd\"odik." + "[43000]Most m)ar k)es\"o le)all)itani!" + "[45000]A m\"uvelet sz)ive n)elk~ul nem!" + "[47000]Neee!" + "[50000]T)IZ" + "[54000]~OT..." + "[55500]4...3...2..." + "[60000]EGY..." + /* 23 */ , + "[00001]Nos, most m)ar a teljes figyelmem rajtad" + "[02500]- M)egsem vagyok biztos abban, hogy )en is megkaptam a tied." + "[05000]Hell)o?" + "[06000]Elkaplak )es lel\"olek, mint egy kuty)at." + "[09000]Term)eszetesen." + "[10000]Te )es az az idi)ota Scion darabod." + "[13000]Annyira szeretn)ed megtartani, hogy kihaszn)alom ezt..." + "[17000]V)arjunk... itt most a m\"ut)argyr)ol besz)el~unk?" + "[20000]Egyenesben vagyunk ... eg)eszen ..." + "[22000]Tarts ki - Sajn)alom" + "[24000]- ez csak egy r)esz, mondtad - hol van a t~obbi?" + "[26500]Ms. Natla Pierre Dupont-ot )all)itotta arra a nyomra." + "[29500])Es hol van az?" + "[30500]H)a)a)a. Nem vagy el)eg gyors, hogy utol)erd." + "[34000]Sz)oval azt gondolod, ez a besz)elget)es csak arra van, hogy feltartson?" + "[37000]Nem tudom, hogy a kis ny)ul-b)eka l)abai hov)a vezetik \"ot." + "[42000]Meg kell k)erdezned Ms. Natla-t)ol." + "[46000]" + "[51000]K~osz~on~om. Megteszem." + /* 24 */ , "" + /* 25 */ , + "[03500]Itt nyugszik Tihocan" + "[05000]...Atlantisz k)et ur)anak egyike..." + "[10000]Ki a kontinens )atka ut)an is..." + "[13000]...megpr)ob)alt uralkodni ezeken a kop)ar m)as-f~oldeken..." + "[19000]Ki Gyermek n)elk~ul halt meg )es a tud)as)anak nem volt ~or~ok~ose..." + "[25500]Tekints r)ank j)oindulattal, Tihocan." + /* 26 */ , "K~osz~ontelek az otthonomban!@Vezetett t)ur)ara foglak vinni." + /* 27 */ , "Haszn)ald az ir)any gombokat, hogy a zeneszob)aba menj." + /* 28 */ , "OK. Ugr)aljunk egy kicsit.@Nyomd meg az ugr)as gombot." + /* 29 */ , "Most nyomd meg )ujra )es gyorsan nyomj hozz)a@egy ir)anyt )es abba az ir)anyba fogok ugrani." + /* 30 */ , ")A)a, a nagyterem.@Bocs a l)ad)ak)ert, de n)eh)any dolgot@rakt)arakba vitetek )es sz)all)it)ok m)eg nem j~ottek." + /* 31 */ , "Fuss oda egy l)ad)ahoz, )es am)ig nyomva tartod az el\"ore gombot,@nyomj akci)o gombot )es )en felugrok r)a." + /* 32 */ , "Ez kor)abban b)alterem volt, de )atalak)itottam@ a saj)at torna termemnek.@Mi a v)elem)enyed?@Nos csin)aljunk n)eh)any gyakorlatot." + /* 33 */ , "Nem mindenhova rohanok.@Ha )ovatos akarok lenni, s)et)alok@Tartsd nyomva a s)eta gombot, )es s)et)alj a feh)er vonalhoz." + /* 34 */ , "A s)eta gomb nyomva tart)as)aval nem fogok leesni, m)eg akkor sem ha megpr)ob)alod.@Rajta, pr)ob)ald ki." + /* 35 */ , "Ha szeretn)el k~orbe n)ezni, tartsd nyomva a n)ez gombot.@Ut)ana nyomd le az ir)anyt amerre n)ezni szeretn)el." + /* 36 */ , "Ha egy ugr)as t)ul nagy nekem, el tudom kapni a peremet )es megmenteni magam@egy cs)unya zuhan)ast)ol. S)et)alj a sz)el)ehez a feh)er vonallal, addig, am)ig @m)ar nem megyek tov)abb. Ut)ana nyomj ugr)ast, ut)ana meg r~ogt~on@ el\"ore gombot, )es am)ig a leveg\"oben vagyok, nyomd le )es tartsd nyomva az akci)o gombot." + /* 37 */ , "Nyomj el\"or)et, )es felm)aszom." + /* 38 */ , "Ha fut)o ugr)ast csin)alok, akkor tudok ekkor)at ugrani, nem probl)ema." + /* 39 */ , "S)et)alj a sz)el)ehez a feh)er vonallal, addig, am)ig m)ar nem megyek tov)abb.@Azt)an engedd el a s)et)at, nyomj h)atr)at, hogy neki tudjak futni.@Nyomj el\"or)et, )es r~ogt~on nyomd le )es tartsd nyomva az ugr)as gombot.@Nem fogok elugrani az utols)o pillanatig." + /* 40 */ , "Helyes. Ez egy igaz)an nagy@ Csin)alj egy fut)o ugr)ast, mint kor)abban, de am)ig a leveg\"oben@vagyok, nyomd le )es tarts nyomva az akci)o gombot, hogy elkapjam a peremet." + /* 41 */ , "Sz)ep." + /* 42 */ , "Pr)ob)alj meg felm)aszni itt.@Nyomj el\"or)et, )es tartsd nyomva az akci)o gombot." + /* 43 */ , "Nem tudok felm)aszni, mert a r)es t)ul kicsi.@Nyomj jobbot, )es oldalra m)aszom,@am)ig el)eg hely lesz, akkor nyomj el\"or)et." + /* 44 */ , "Remek!@Ha nagy a m)elys)eg, )es nem szeretn)ek@s)er~ulni a leugr)assal, le tudom magam ereszteni )ovatosan." + /* 45 */ , "Nyomj h)atr)at )es h)atrafel)e ugrok.@Azonnal nyomd meg )es tartsd nyomva az akci)o gombot,@)es elkapom a peremet )utban lefel)e." + /* 46 */ , "Akkor engedd el." + /* 47 */ , "Menj~unk )uszni egyet." + /* 48 */ , "Az ugr)as gomb )es az ir)anyok@navig)alnak engem a v)iz alatt." + /* 49 */ , ")O! Leveg\"o!@Csak haszn)ald az el\"or)et, balr)at, vagy jobbr)at@a felsz)in k~ozeli man\"overez)eshez.@Nyomj ugr)ast egy )ujabb )usz)ashoz lemer~ul)eshez.@Vagy menj a sz)el)ehez, )es nyomj akci)o gombot a kim)asz)ashoz." + /* 50 */ , "Helyes. A legjobb lesz, ha leveszem ezeket a nedves ruh)akat." + /* 51 */ , "Mond: Cs)i)iz!" + /* 52 */ , "Semmi szem)elyes." + /* 53 */ , "M)eg mindig f)aj a fejem t\"oled.@)Es m)ok)as ~otleteket ad nekem.@Hogy l\"ojelek a pokolra, p)eld)aul!" + /* 54 */ , "Nem tudsz meg~olni engem vagy a fi)ok)aimat olyan k~onnyed)en, Lara." + /* 55 */ , "Egy kicsit elk)esett a d)ij)atad)as, non?@M)eg mindig a r)eszv)etel ami sz)am)it." + /* 56 */ , "R)am l\"osz?@R)am l\"osz, mi?@Nincs senki m)as itt, csak r)am l\"ohetsz!" +// TR1 levels + , "Lara otthona" + , "Barlangok" + , "Vilcabamba v)arosa" + , "Elveszett v~olgy" + , "Qualopec s)irja" + , "Szt. Ferenc kolostor" + , "Colosseum" + , "Mid)asz palota" + , "A Ciszterna" + , "Tihocan s)irja" + , "Khamoon v)arosa" + , "Khamoon obeliszkje" + , "Scion szent)elye" + , "Natla b)any)ai" + , "Atlantisz" + , "A nagy piramis" + , "Visszat)er)es Egyiptomba" + , "Macska templom" + , "Atlantiszi t)amaszpont" + , "A kapt)ar" +// TR2 levels + , "Lara otthona" + , "A nagy fal" + , "Velence" + , "Bartoli rejtekhelye" + , "Operah)az" + , "Tengeri f)ur)otorony" + , "B)uv)arter~ulet" + , "40 ~ol" + , "A Maria Doria roncsa" + , "Lak)okabinok" + , "A fed)elzet" + , "Tibeti hegyl)abak" + , "Barkhang kolostor" + , "Talion katakomb)ai" + , "J)egpalota" + , "Xian temploma" + , ")Usz)o szigetek" + , "A S)ark)anyod)u" + , "Otthon )edes otthon" +// TR3 levels + , "Lara otthona" + , "Dzsungel" + , "Templom romok" + , "A Gangesz foly)o" + , "Kaliya barlangjai" + , "Tengerparti falu" + , "A baleset helysz)ine" + , "Madubu szurdok" + , "Puna temploma" + , "Temze rakpart" + , "Aldwych" + , "Lud kapuja" + , "V)aros" + , "Nevada sivatag" + , "Magas biztons)ag)u )ep~ulet" + , "51-es k~orzet" + , "Antarktisz" + , "RX-Tech b)any)ak" + , "Tinnos elveszett v)arosa" + , "Meteorit barlang" + , "Mindenszentek" +}; + +#endif diff --git a/src/lang/it.h b/src/lang/it.h index 1c540ed0..bfdb78e4 100644 --- a/src/lang/it.h +++ b/src/lang/it.h @@ -5,7 +5,7 @@ const char *STR_IT[] = { "" // help - , "Caricamento..." + , "Caricamento in corso" , "Premi H per la lista dei comandi" , helpText , "%s@@@" @@ -13,17 +13,17 @@ const char *STR_IT[] = { "" "OGGETTI RACCOLTI %d@@" "SEGRETI %d di %d@@" "TEMPO IMPIEGATO %s" - , "Salvataggio in corso..." - , "Salvataggio completato!" - , "ERRORE DURANTE IL SALVATAGGIO!" + , "Salvataggio in corso" + , "Salvataggio completato" + , "ERRORE DURANTE IL SALVATAGGIO" , "S$I" , "NO" - , "Off" - , "On" - , "Off" + , "Spento" + , "Acceso" + , "Spento" , "Doppio schermo" , "Anaglifo" - , "Visione compressa" + , "Compressione" , "VR" , "Basso" , "Medio" @@ -65,30 +65,30 @@ const char *STR_IT[] = { "" , "Esci dal gioco" , "Selezione del livello" // detail options - , "Imposta i Dettagli" + , "Livello di Dettaglio" , "Textures" , "Illuminazione" , "Ombre" , "Acqua" , "VSync" , "Stereo" - , "Oggetti Semplificati" + , "Oggetti semplificati" , "Risoluzione" , STR_SCALE // sound options - , "Imposta il Volume" + , "Impostazioni audio" , "Riverbero" , "Sottotitoli" , "Lingua" // controls options - , "Controlli Personalizzati" + , "Personalizzazione comandi" , "Tastiera" , "Gamepad" , "Vibrazione" , "Retargeting" , "Mira multipla" // controls - , "Sinistra", "Destra", "Correre", "Indietro", "Saltare", "Camminare", "Azione", "Estrarre l'Arma", "Guardare", "Accovacciarsi", "Scattare", "Capriola", "Inventario", "Start" + , "Sinistra", "Destra", "Avanti", "Indietro", "Salto", "Camminata", "Azione", "Estrarre l'arma", "Osservare", "Accovacciarsi", "Scatto", "Capriola", "Inventario", "Start" , STR_KEYS // inventory items , "Sconosciuto" @@ -97,8 +97,8 @@ const char *STR_IT[] = { "" , "Fucile" , "Magnum" , "Uzi" - , "Munizioni Pistola" - , "Munizioni Fucile" + , "Munizioni pistola" + , "Munizioni fucile" , "Munizioni Magnum" , "Munizioni Uzi" , "Medikit piccolo" @@ -120,7 +120,7 @@ const char *STR_IT[] = { "" , "Puzzle" , "Idolo d'oro" , "Barra d'oro" - , "Ruota Dentata" + , "Ruota dentata" , "Fusibile" , "Ankh" , "Occhio di Horus" @@ -129,57 +129,57 @@ const char *STR_IT[] = { "" , "Chiave della piramide" // TR1 subtitles /* CAFE */ , - "[43500]Cosa deve fare un uomo per ottenere@da te questo tipo di attenzioni?" - "[47500]Difficile dirlo con precisione,@ma sembra che tu sia partito bene." - "[50000]Bene, magnifico. Ma la verit$a@$e che non sono io che ti sto cercando." + "[43500]Che cosa deve fare un uomo@per ricevere da te questo genere di attenzioni?" + "[47500]Difficile dirlo con certezza,@ma forse sei sulla buona strada." + "[50000]Ottimo allora. Anche se a dire il vero@non sono io che ti sto cercando." "[54500]No?" - "[55000]No. Sono qui per Miss Jacqueline Natla,@della Natla Technologies." - "[59000]Hai presente, creatrice di@cose brillanti e spettacolari?" - "[64500]Sta zitto, Larson." - "[66000]Mmm." + "[55000]No. Sono qui per conto di Miss Jacqueline Natla,@della Natla Technologies." + "[59000]Hai presente quella che crea@cose scintillanti e belle?" + "[64500]Sta' zitto, Larson." + "[66000]Mmm.." "[68000]Rifatti gli occhi, Lara." "[70500]Non senti gi$a il tintinnio del tuo portamonete?" - "[73500]Mi dispiace. Lavoro solo per sport." + "[73500]Mi spiace; Lavoro solo per divertirmi." "[76000]Allora non potrai non amare questo immenso parco giochi." - "[78000]Peru'. Vaste catene montuose da attraversare.@Muri di ghiaccio trasparente. Dirupi rocciosi. Venti selvaggi." - "[87500]E ci sarebbe questo gingillo:@un antichissimo artefatto dai poteri mistici" + "[78000]Peru'. Sterminate catene montuose da valicare.@Muri di ghiaccio trasparente. Dirupi rocciosi. Venti sferzanti." + "[87500]E poi ci sarebbe questo gingillo:@un antichissimo artefatto dai poteri mistici" "[92500]sepolto nella tomba perduta di Qualopec." - "[96000]E' ci$o che mi interessa." - "[98000]Potresti partire gi$a domani.@O hai gi$a altri impegni per domani?" + "[96000]E' tutto ci$o che mi interessa." + "[98000]Potresti partire gi$a domani.@O avevi in previsione altri programmi?" /* LIFT */ , - "[49000]Sono stato riassegnato alle rovine di St. Francis,@nuove tentazioni mi tormentano." + "[49000]Sono stato assegnato alle rovine di St. Francis,@nuove tentazioni mi tormentano." "[53500]Tra i fratelli gira voce che sepolto@sotto il nostro monastero riposi il corpo di Tihocan," "[60000]uno dei tre leggendari sovrani@del continente perduto, Atlantide," - "[64500]e che con lui si trovi il suo frammento@dello Scion di Atlantide." - "[68000]L'amuleto diviso e detenuto tra i tre sovrani" - "[72500]che controlla poteri inimmaginabili.@Poteri che superano quelli dello stesso Creatore." - "[79000]Mi tremano le gambe al solo pensiero@che giaccia cos$i vicino al mio corpo mortale." - "[85500]Ogni notte cerco di rifuggire certe fantasie,@ma $e una sfida ardua." + "[64500]e che con lui si trovi il suo frammento@dello Scion atlantideo." + "[68000]L'amuleto diviso e spartito tra i tre sovrani" + "[72500]capace di controllare poteri inimmaginabili.@Poteri che superano quelli dello stesso Iddio." + "[79000]Mi tremano le gambe al solo pensiero@che giaccia talmente vicino al mio corpo mortale!" + "[85500]Ogni notte provo a rifuggire certe fantasie,@ma $e una sfida estremamente ardua." "[92000]" - "[93500]Pierre. Tsss. Che idiota." + "[93500]Pierre. Tsss. Che idiota!" /* CANYON */ , - "[13500]Sei appena giunta al capolinea." + "[13500]Sei appena giunta al capolinea!" "[16500]Salve." "[17500]Buon pomeriggio." "[20000]E cos$i hai lasciato Larson a bocca asciutta?" "[22500]Se cos$i si pu$o dire." - "[24000]Bene, le tue scorribande sono giunte al termine." + "[24000]Bene, le tue scorribande finiscono qui." "[27000]E' tempo di restituirmi ci$o che mi hai sottratto." "[30000]Diamo un'occhiata nel suo sacchetto per il pranzo." "[32000]" - "[42500]Ottimo! Uccidila!" + "[42500]Ottimo. Sbarazzatene!" "[45000]Hey!" "[48000]" - "[50500]Cretini!" + "[50500]Idioti!" "[53000]" "[62500]Andiamo." "[65000]" - "[136000]Che cavolo era quello?" + "[136000]Che diamine era quello?" "[138000]Cosa?" - "[138500]Quella cosa." - "[140500]Probabilmente era solo un pesce." - "[142500]Deve essere stato un pesce bello grosso, genio." - "[145000]Ragazzo, devi imparare a calmarti.@Torno dentro. Vieni?" + "[138500]Quella cosa.." + "[140500]Sar$a stato solo un pesce." + "[142500]Allora deve essere stato un pesce bello grosso, genio!" + "[145000]Ragazzo, vedi di darti una calmata!@Torno dentro. Vieni con me?" "[152000]" "[158000]Piano..." "[160000]Eccola qui." @@ -187,127 +187,127 @@ const char *STR_IT[] = { "" /* PRISON */ , "[00001]Non puoi farlo!" "[01500]Noi ti condanniamo, Natla di Atlantide, per i tuoi crimini." - "[06000]Per l'abuso dei tuoi poteri@e per averci derubato del nostro..." + "[06000]Per l'uso spregiudicato dei tuoi poteri@e per averci derubato del nostro..." "[11500]Non ne avete il diritto! Io..." "[12500]Infrangendo le leggi che governano@e tutelano il nostro popolo" - "[18500]e sfidando Tihocan e me con il tuo esercito." - "[23500]I nostri guerrieri hanno lasciato la nostra piramide" - "[27000]cosicch)e tu potessi usare - il suo potere creativo -@per la tua dissennata brama di distruzione." + "[18500]e invadendo Tihocan e me con il nostro esercito." + "[23500]I nostri guerrieri hanno lasciato la piramide" + "[27000]cosicch)e tu potessi usare il suo potere creativo@per perseguire la tua dissennata brama di distruzione." "[33500]Dissennata!? Guardatevi!" - "[35500]Nessuno di voi possiede un briciolo@di inventiva nel suo cervello." + "[35500]Nessuno di voi possiede un briciolo di ambizione!" "[40500]Inetti!" "[41500]Facciamolo e basta." "[44000]Tihocan!" - "[45000]Hai usato il luogo sacro@come una fonte di piacere personale," + "[45000]Hai usato il nostro luogo sacro@come fonte di piacere personale," "[49500]come fosse una fabbrica di mostri." - "[51000]Sono dei sopravvissuti. Una nuova generazione." + "[51000]Sono dei sopravvissuti; una nuova generazione!" "[54000]Solo un mucchio di carcasse dilaniate ora." - "[56000]E tu. Verrai imprigionata nel limbo." + "[56000]E tu, tu verrai imprigionata nel limbo." "[60000]Render$a le tue vene, cuore, gambe," - "[64000]e quella tua mente disturbata un tutt'uno col sangue congelato." - "[70000]Goditi la tua eterna disperazione, Natla." + "[64000]e quella tua mente schizzata un tutt'uno con il sangue gelido." + "[70000]Saluta la tua eterna disperazione, Natla!" "[73000]Neppure tu riposerai bene, o tantomeno il tuo@dannato continente di Atlantide!" /* 22 */ , "[04000]Di nuovo qui?" - "[05500]Anche tu - per la grande riapertura, suppongo." - "[09500]L'evoluzione fluisce - la selezione naturale scorre pi$u lenta che mai..." - "[13500]un rifornimento di carne fresca risveglier$a il nostro orgoglio identitario" + "[05500]Anche tu - per assistere alla grande riapertura, suppongo." + "[09500]L'evoluzione fluisce ma la selezione naturale scorre pi$u lenta che mai..." + "[13500]Un rifornimento di carne fresca risveglier$a il nostro orgoglio identitario" "[17500] - ci rinforzer$a e ci avvantagger$a..." - "[20500]Dar$a persino origine a nuove razze" + "[20500]..dar$a persino origine a nuove razze." "[22500]Una specie di evoluzione sotto steroidi, quindi." - "[24500]Un calcio nel sedere...@quei poveretti di Qualopec e Tihocan non immaginano neppure" + "[24500]Una spintarella...quei buoni a nulla@di Qualopec e Tihocan non avevano la bench)e minima idea" "[29500] - il cataclisma che colp$i Atlantide ha spazzato via una razza di rammolliti..." "[33500]facendoli ripiombare alle basi della sopravvivenza..." - "[37000]Non doveva andare in quel modo." + "[37000]..ma non doveva andare in quel modo!" "[39000]O in questo." "[40000]La schiusura avr$a inizio tra 15 secondi." - "[43000]Troppo tardi per tirarsi indietro adesso!" + "[43000]Troppo tardi ormai per tirarsi indietro!" "[45000]Non senza la mente dell'operazione!" "[47000]Noooo!" - "[50000]DIECI" - "[54000]CINQUE..." + "[50000]10" + "[54000]5..." "[55500]4...3...2..." - "[60000]UNO..." + "[60000]1..." /* 23 */ , "[00001]Bene, adesso hai la mia attenzione" "[02500]- Tuttavia, non sono cos$i sicura di avere la tua." "[05000]Hey?" - "[06000]Ti accopper$o e ti sbatter$o dietro la porta di una stalla." + "[06000]Ti accopper$o e ti sbatter$o dentro una stalla." "[09000]Come no." "[10000]Tu e quel dannato pezzo di Scion." - "[13000]Se lo vuoi a tutti i costi, provveder$o immediatamente a..." + "[13000]Se vuoi tenerlo ad ogni costo, provveder$o immediatamente a..." "[17000]Aspetta... stiamo parlando di questo artefatto?" - "[20000]Siamo sulla buona strada ... te lo inf..." - "[22000]Fermo - Scusa un secondo" - "[24000]- hai detto questo pezzo, - dove sono gli altri?" - "[26500]Miss Natla ha messo Pierre Dupont sulle loro tracce." + "[20000]Siamo sulla buona strada ... te lo far$o ingoi..." + "[22000]Alt - Scusa un secondo" + "[24000]- hai detto questo pezzo - dove sono gli altri?" + "[26500]Miss Natla ha gi$a messo Pierre Dupont sulle loro tracce." "[29500]E lui dove si trova adesso?" "[30500]Hah. Non sei abbastanza veloce per raggiungerlo." - "[34000]Credi di trattenermi con tutto questo blaterare?" - "[37000]Non so dove lo stiano conducendo le sue gambette da leprotto." + "[34000]Hai forse intenzione di trattenermi blaterando?" + "[37000]Non so dove lo stiano conducendo ora le sue gambette da leprotto." "[42000]Dovresti chiederlo a Miss Natla." "[46000]" "[51000]Grazie. Provveder$o." /* 24 */ , "" /* 25 */ , "[03500]Qui giace Tihocan" - "[05000]...uno dei due indiscussi sovrani di Atlantide..." - "[10000]Che anche dopo la rovina del continente..." - "[13000]...tent$o di stabilire l'ordine in queste lande desolate..." + "[05000]...uno dei due onorati sovrani di Atlantide..." + "[10000]..che anche dopo la rovina del continente..." + "[13000]...tent$o di ristabilire l'ordine in queste lande desolate..." "[19000]Egli mor$i senza un erede e la sua conoscenza non fu tramandata..." "[25500]Veglia su di noi, Tihocan." - /* 26 */ , "Benvenuto nella mia casa." - /* 27 */ , "Usa i tasti di controllo per andare nella stanza della musica." - /* 28 */ , "Ok. Facciamo qualche acrobazia.@Premi il tasto Salto." - /* 29 */ , "Ora fallo di nuovo premendo in una@delle direzioni: salter$o da quella parte." - /* 30 */ , "Ecco l'atrio principale.@Scusa per quelle casse, ho imballato alcune cose@e il fattorino non $e ancora passato." - /* 31 */ , "Corri vicino a una cassa e, continuando a premere in Avanti,@premi Azione e io ci salter$o sopra." - /* 32 */ , "Una volta questa stanza era una sala da ballo,@ma io l'ho trasformata nella mia palestra personale.@Cosa ne pensi?@Ok, facciamo qualche esercizio." - /* 33 */ , "In effetti io non corro sempre.@Quando occorre essere cauta, cammino.@Tieni premuto il tasto Cammina e raggiungi la linea bianca." - /* 34 */ , "Con il tasto Cammina premuto, non cadr$o@nemmeno se mi spingi a farlo.@Avanti, prova pure." - /* 35 */ , "Se vuoi guardarti intorno, premi e tieni premuto il tasto Guarda.@Quindi premi nella direzione in cui vuoi guardare." - /* 36 */ , "Se un salto $e troppo lungo, posso aggrapparmi al@bordo per evitare una brutta caduta.@Cammina verso il bordo con la linea bianca@finch)e non posso pi$u andare avanti.@Quindi premi Salto, immediatamente seguito da Avanti,@e mentre sono in aria premi e tieni premuto il tasto Azione." - /* 37 */ , "Premi Avanti e mi arrampicher$o." - /* 38 */ , "Se faccio un salto con rincorsa, posso arrivare@a quella distanza, senza alcun problema." - /* 39 */ , "Cammina verso il bordo con la linea bianca finch)e non mi fermo.@Quindi rilascia il tasto Cammina e premi una@volta Avanti per farmi prendere la rincorsa.@Premi Avanti e subito dopo premi e tieni premuto il tasto Salto.@Non spiccher$o il salto fino all'ultimo secondo." - /* 40 */ , "Perfetto. Questo $e davvero grande.@Allora, fai un salto con rincorsa esattamente come prima,@ma questa volta, mentre sono in aria premi e tieni premuto@il tasto Azione per farmi aggrappare al bordo." - /* 41 */ , "Bene." - /* 42 */ , "Prova a saltare qui su.@Premi avanti e tieni premuto il tasto Azione." - /* 43 */ , "Non posso tirarmi su perch)e la fessura $e troppo stretta.@Per$o puoi premere verso destra: mi sposter$o@lateralmente finch)e c'$e spazio, poi premi avanti." - /* 44 */ , "Ottimo!@Se c'$e un grosso dislivello e non voglio farmi@male cadendo, posso calarmi gi$u lentamente." - /* 45 */ , "Premi una volta indietro e far$o un salto in quella direzione.@Poi, immediatamente, premi e tieni premuto il@tasto Azione: mi aggrapper$o al bordo per scendere." + /* 26 */ , "Benvenuto nella mia casa! Lascia che ti porti a dare un'occhiata in giro." + /* 27 */ , "Usa i tasti direzionali per spostarti nella sala della musica." + /* 28 */ , "Ok, facciamo qualche acrobazia!@Premi il tasto Salto." + /* 29 */ , "Ora fallo di nuovo premendo un tasto direzionale;@io salter$o da quella parte." + /* 30 */ , "Questo $e l'atrio principale!@Vorrai perdonare il disordine ma ho imballato alcune cose@e come immaginerai il fattorino non si $e ancora fatto vivo." + /* 31 */ , "Posizionami di fronte ad una cassa e continuando a premere Avanti@premi il tasto Azione: mi ci arrampicher$o sopra." + /* 32 */ , "Una volta questa era la sala da ballo@ma io l'ho trasformata nella mia palestra personale; Che te ne pare?@Bene, facciamo qualche esercizio!" + /* 33 */ , "Ovviamente io non corro sempre;@quando serve una certa prudenza cammino.@Tieni premuto il tasto Camminata e raggiungi la linea bianca." + /* 34 */ , "Con il tasto Camminata premuto non cadr$o@nemmeno se mi spingi a farlo.@Avanti, prova pure!" + /* 35 */ , "Se vuoi guardarti intorno premi e tieni premuto il tasto Osserva;@infine con i tasti direzionali muovi la visuale verso il punto che vuoi." + /* 36 */ , "Se un salto $e troppo lungo posso aggrapparmi al bordo@evitando una rovinosa caduta.@Cammina verso il bordo con la linea bianca finch)e non posso pi$u andare avanti;@quindi premi Salto, immediatamente seguito da Avanti@e mentre sono in aria premi e tieni premuto il tasto Azione." + /* 37 */ , "Adesso premi il tasto Avanti e mi arrampicher$o." + /* 38 */ , "Facendo un salto con rincorsa arrivare a quella distanza@sar$a sicuramente uno scherzo!" + /* 39 */ , "Cammina verso il bordo con la linea bianca finch)e non mi fermo.@Quindi rilascia il tasto Cammina e premi una volta Indietro@in modo da farmi prendere la rincorsa.@Premi Avanti e subito dopo premi e tieni premuto il tasto Salto:@non salter$o fino all'ultimo secondo." + /* 40 */ , "Perfetto; questo $e davvero un gran bel salto!@Allora: fai un salto con rincorsa esattamente come prima@ma questa volta mentre sono in aria premi e tieni premuto Azione@per farmi aggrappare al bordo." + /* 41 */ , "Bene!" + /* 42 */ , "Ora prova a sollevarti qui!@Premi Avanti e tieni premuto il tasto Azione." + /* 43 */ , "Come vedi non posso tirarmi su perch)e la fessura $e troppo stretta,@per$o puoi spingere verso destra: mi sposter$o@lateralmente finch)e c'$e spazio. Infine premi Avanti." + /* 44 */ , "Ottimo!@Se c'$e un grosso dislivello e non voglio farmi male cadendo@posso calarmi gi$u lentamente." + /* 45 */ , "Premi una volta Indietro e far$o uno scatto in quella direzione.@Poi, immediatamente, premi e tieni premuto il@tasto Azione: mi aggrapper$o al bordo per scendere." /* 46 */ , "Ora rilascia." - /* 47 */ , "Andiamo a farci una nuotata." - /* 48 */ , "Usa il tasto Salto ed i tasti di controllo@per muovermi sott'acqua." - /* 49 */ , "Ah! Aria!@Usa avanti, sinistra e destra per muoverti quando sei a galla.@Premi Salto per immergerti e fare un'altra nuotata.@Oppure raggiungi il bordo e premi Azione per uscire." - /* 50 */ , "Perfetto. Ora sar$a meglio che mi tolga@questi vestiti bagnati." + /* 47 */ , "Adesso andiamo a farci una bella nuotata!" + /* 48 */ , "Usa il tasto Salto insieme ai tasti direzionali@per farmi muovere quando sono sott'acqua." + /* 49 */ , "Ah! Aria!@Premi i tasti direzionali per muovermi quando sono a galla.@Premi Salto per farmi immergere di nuovo,@altrimenti raggiungi il bordo e premi Avanti e Azione per farmi uscire." + /* 50 */ , "Perfetto! Ora per$o sar$a meglio che mi tolga@questi vestiti bagnati.." /* 51 */ , "Sorridi!" /* 52 */ , "Niente di personale." - /* 53 */ , "Maledetto, mi fai ancora venire il mal di testa.@E mi fai venire in mente delle brutte idee@ad esempio spararti!" - /* 54 */ , "Non puoi liberarti di me e della mia@stirpe cos$i facilmente, Lara." - /* 55 */ , "Un po' in ritardo per la premiazione, no?@Ma l'importante $e partecipare, giusto?" + /* 53 */ , "Essere maledetto, mi fai sempre venire il mal di testa.@E mi provochi anche brutti pensieri,@ad esempio quello di spararti!" + /* 54 */ , "Non puoi liberarti di me e della mia stirpe@cos$i facilmente, Lara!" + /* 55 */ , "Un po' in ritardo per la premiazione, no?@Ma l'importante in fin dei conti $e partecipare, sbaglio?" /* 56 */ , "Mi stai sparando?@Ehi, stai sparando a me?@Eh s$i, non c'$e nessun altro, stai proprio sparando a me!" // TR1 levels , "Casa di Lara" , "Caverne" , "Citt$a di Vilcabamba" - , "Valle perduta" + , "La Valle Perduta" , "Tomba di Qualopec" , "Rovine di St. Francis" , "Colosseo" , "Palazzo di Mida" - , "La Cisterna" + , "Cisterna" , "Tomba di Tihocan" , "Citt$a di Khamoon" , "Obelisco di Khamoon" , "Santuario dello Scion" , "Miniere di Natla" , "Atlantide" - , "La grande piramide" + , "La Grande Piramide" , "Ritorno in Egitto" , "Tempio del Gatto" , "Fortezza atlantidea" - , "L'Alveare" + , "L'alveare" // TR2 levels , "Casa di Lara" , "La Grande Muraglia" @@ -323,10 +323,10 @@ const char *STR_IT[] = { "" , "Pendici tibetante" , "Monastero di Barkhang" , "Catacombe di Talion" - , "Palazzo del ghiaccio" + , "Palazzo di ghiaccio" , "Tempio dello Xian" , "Isole galleggianti" - , "La tana del Drago" + , "La tana del drago" , "Casa dolce casa" // TR3 levels , "Casa di Lara" @@ -347,9 +347,9 @@ const char *STR_IT[] = { "" , "Area 51" , "Antartico" , "Miniere RX-Tech" - , "CittГ  perduta di Tinnos" + , "Citt$a perduta di Tinnos" , "Caverna del meteorite" - , "All Hallows" + , "Hallows" }; #endif diff --git a/src/lang/sv.h b/src/lang/sv.h new file mode 100644 index 00000000..22fce1d6 --- /dev/null +++ b/src/lang/sv.h @@ -0,0 +1,355 @@ +#ifndef H_LANG_SV +#define H_LANG_SV + +// Thanks: Carl Lindmark, Rickard Andreasson + +const char *STR_SV[] = { "" +// help + , "Laddar..." + , "Tryck H f~or hj~alp" + , helpText + , "%s@@@" + "D~ODADE %d@@" + "PLOCKAT %d@@" + "HEMLIGHETER %d av %d@@" + "TID TAGEN %s" + , "Sparar spel..." + , "Sparar klart!" + , "SPARA MISSLYCKATS!" + , "JA" + , "NEJ" + , "Av" + , "P^a" + , "Av" + , "Sida-vid-Sida" + , "Anaglyf" + , "Delad sk~arm" + , "VR" + , "L^ag" + , "Mellan" + , "H~og" + , STR_LANGUAGES + , "Till~ampa" + , "Handkontroll 1" + , "Handkontroll 2" + , "Handkontroll 3" + , "Handkontroll 4" + , "Ej Redo" + , "Spelare 1" + , "Spelare 2" + , "Tryck Valfri Knapp" + , "%s - V~alj" + , "%s - Tillbaka" +// inventory pages + , "ALTERNATIV" + , "LAGER" + , "OBJEKT" +// save game page + , "Spara Spel?" + , "Nuvarande Position" +// inventory option + , "Spel" + , "Karta" + , "Kompass" + , "Statistik" + , "Lara's Hem" + , "Detaljniv^aer" + , "Ljud" + , "Kontroller" + , "Gamma" +// passport menu + , "~Oppna Spel" + , "Nytt Spel" + , "Starta om Niv^a" + , "Avsluta till Titel" + , "Avsluta Spel" + , "V~alj Niv^a" +// detail options + , "V~alj detalj" + , "Filtrering" + , "Belysning" + , "Skuggor" + , "Vatten" + , "VSync" + , "Stereo" + , "Enkla Objekt" + , "Uppl~osning" + , STR_SCALE +// sound options + , "St~all in Volym" + , "Eko" + , "Undertexter" + , "Spr^ak" +// controls options + , "St~all in Kontroller" + , "Tangentbord" + , "Handkontroll" + , "Vibration" + , "Omniriktning" + , "Multi-riktning" + // controls + , "V~anster", "H~oger", "Spring", "Backa", "Hoppa", "G^a", "Action", "Dra Vapen", "Titta", "Ducka", "Rusa", "Rulla", "Lager", "Start" + , STR_KEYS +// inventory items + , "Ok~and" + , "Explosivt" + , "Pistoler" + , "Hagelgev~ar" + , "Magnum" + , "Uzis" + , "Pistol-Ammunition" + , "Hagelgev~ar-Ammunition" + , "Magnum-Ammunition" + , "Uzi-Ammunition" + , "Litet First-Aid" + , "Stort First-Aid" + , "Blytacka" + , "Scion" +// keys + , "Nyckel" + , "Silvernyckel" + , "Rostig Nyckel" + , "Guld Nyckel" + , "Safir Nyckel" + , "Neptunes Nyckel" + , "Atlas Nyckel" + , "Damokles Nyckel" + , "Thors Nyckel" + , "Dekorerad Nyckel" +// puzzles + , "Pussel" + , "Guld-Idol" + , "Guld-Tacka" + , "Kugghjul" + , "S~akring" + , "Ankh" + , "Horus ~Oga" + , "Anubis Sigill" + , "Scarab" + , "Pyramid-Nyckeln" +// TR1 subtitles + /* CAFE */ , + "[43500]Vad m^aste en man g~ora f~or att@f^a din uppm~arskamhet?" + "[47500]Det ~ar sv^art att s~aga,@men du verkar klara det bra." + "[50000]Bra. Men det ~ar inte jag som beh~over dig." + "[54500]Inte?" + "[55000]Nej, men fr~oken Jacqueline Natla,@fr^an Natla Technologies." + "[59000]Du vet, skaparen av@allt ljust och vackert?" + "[64500]Tyst, Larson." + "[66000]Fr~oken." + "[68000]Titta, Lara." + "[70500]Vill du tj~ana stora pengar?" + "[73500]Detta ~ar bara en sport f~or mig, tyv~arr." + "[76000]D^a kanske du kommer att gilla det h~ar." + "[78000]Peru. Enorma bergskedjor, branta isv~aggar,@bergskrevor, kalla vindar" + "[87500]och sedan finns denna saken:@en gammal artefakt med mystiska krafter," + "[92500]begravd i Qualopecs bortgl~omda grav." + "[96000]Det vill jag ha." + "[98000]Du kan ^aka i morgon.@Har du n^agra planer f~or morgondagen?" + /* LIFT */ , + "[49000]Jag befinner mig i S:t Francis folly,@nya frestelser pl^agar mig." + "[53500]Det g^ar rykten att Tihocans kropp@~ar begravd under v^art kloster," + "[60000]en av de tre legendariska h~arskarna@av kontinenten Atlantis," + "[64500]och att med honom vilar hans del av The scion." + "[68000]ett smycke som delades mellan de tre h~arskarna@ och som har enorma krafter," + "[72500]krafter bortom Skaparen@." + "[79000]Jag blir yr av tankarna p^a vad som@ligger s^a n~ara mitt d~odliga jag.." + "[85500]Varje kv~all sl^ar jag bort de hemska tankarna)en,@men det ~ar verkligen ett test." + "[92000]" + "[93500]Pierre. Din nerskr~apare." + /* CANYON */ , + "[13500]H~ar har vi ~antligen det lilla odjuret." + "[16500]Howdy." + "[17500]God eftermiddag." + "[20000]Bl^aste du ut ljuset f~or Larson?" + "[22500]Om du vill uttrycka det s^a." + "[24000]N^av~al, din lilla semesterresa ~ar ~over." + "[27000]Dags att ge tillbaka det du stal fr^an mig." + "[30000]L^at oss ta en titt i lunchl^adan." + "[32000]" + "[42500]N^a? D~oda henne!" + "[45000]Hej!" + "[48000]" + "[50500]Era idioter!" + "[53000]" + "[62500]L^at oss g^a." + "[65000]" + "[136000]Vad i helvete var det?" + "[138000]Vad?" + "[138500]D~ar borta." + "[140500]F~ormodligen bara en fisk." + "[142500]Men m^aste ha varit ganska stor." + "[145000]Mannen, du m^aste l~ara dig a det lugnt@Jag g^ar in igen, kommer du?" + "[152000]" + "[158000]H^all dig lugn..." + "[160000]H~ar kommer det." + "[161500]~Ar du redo?" + /* PRISON */ , + "[00001]Ni kan inte g~ora detta!" + "[01500]Vi d~ommer dig, Natla fr^an Atlantis, f~or dina brott," + "[06000]f~or grovt missbruk av dina befogenheter@och f~or att stj~ala v^ara befogenheter." + "[11500]Du kan inte! Jag..." + "[12500]Du har f~orst~ort den trippelallians som har@lett och skyddat v^art folk..." + "[18500]och utmanade Tihocan och mig med v^ar egen arm)e..." + "[23500]lockade v^ara krigare bort fr^an pyramiden..." + "[27000]s^a att du kan missbruka pyramidens kreativa@kraft f~or din sjuka destruktivitet." + "[33500]Sjuk!? Titta p^a dig!" + "[35500]Ingen av er har en gnista av kreativitet i huvudet." + "[40500]Od^agor!" + "[41500]L^at oss bara g~ora det nu." + "[44000]Tihocan!" + "[45000]Du har vandaliserat ditt heliga offer av ren girighet," + "[49500]som en freak show." + "[51000]En ny generation, f~odd f~or att ~overleva." + "[54000]Nu ~ar de k~ottf~ars." + "[56000]Och du, Natla, vi kommer att sm~ada..." + "[60000]dina vener och f~otter, ditt hj~arta..." + "[64000]och din sjuka hj~arna fryses ned till is med ditt blod," + "[70000]Se din eviga rastl~oshet i ~ogonen, Natla.." + "[73000]Du kommer aldrig finna frid,@f~orbannad m^a din kontinent Atlantis!" + /* 22 */ , + "[04000]Tillbaka igen?" + "[05500]Och du - F~or den ceremoniella ^ater~oppningen, antar jag." + "[09500]Evolutionen ~ar i en ^aterv~andsgr~and - n~astan inget naturligt urval..." + "[13500]spridningen av nya varelser" + "[17500] - kommer att stimulera nya regionala krafter..." + "[20500]~Aven skapa nya arter." + "[22500]som en evolution p^a steroider." + "[24500]Ja, med en spark i rumpan...@De d~ar Qualopec och Tihocan hade ingen aning." + "[29500] - Atlantis fall har drabbat ett lopp av tr~otta svagheter..." + "[33500]och kastade dem tillbaka till ursprunget f~or ~overlevnad...." + "[37000]Detta ~ar vad v~arlden beh~over." + "[39000]Inte s^a." + "[40000]Det b~orjar om 15 sekunder." + "[43000]F~or sent f~or att avbryta!" + "[45000]Inte utan hj~artat av operationen!" + "[47000]Neeeeej!" + "[50000]10" + "[54000]5..." + "[55500]4...3...2..." + "[60000]1..." + /* 23 */ , + "[00001]N^a, du har min fulla uppm~arksamhet nu," + "[02500]men jag har nog inte din" + "[05000]Hall^a!" + "[06000]Jag ska jaga dig." + "[09000]Naturligtvis." + "[10000]Du och den d~ar dumma delen av Scion." + "[13000]Om du s^a g~arna vill beh^alla den, ska jag trycka den i din..." + "[17000]V~anta... pratar vi om artifakten?" + "[20000]Det kan du ta dig p^a ... r~att upp i din ..." + "[22000]V~anta lite - Jag ~ar ledsen" + "[24000]-Denna delen sa du - vart ~ar resten?" + "[26500]Miss Natla satte Pierre Dupont p^a att hitta den." + "[29500]Vart d^a?" + "[30500]Ha! Du ~ar inte lika snabb som honom." + "[34000]S^a du menar att detta samtalet h~ar bara hindrar mig?" + "[37000]Jag har ingen aning var de d~ar@skurkbenen leder honom." + "[42000]Du f^ar fr^aga fr~oken Natla." + "[46000]" + "[51000]Det ska jag, tackar." + /* 24 */ , "" + /* 25 */ , + "[03500]H~ar ligger Tihocan" + "[05000]...en av de tv^a r~attf~ardiga ledarna i Atlantis..." + "[10000]som ~aven efter att kontinentens f~orbannelse..." + "[13000]...f~ors~okte styra i detta karga, fr~ammande land..." + "[19000]Han dog utan barn och hans kunskaper levde inte vidare..." + "[25500]V~anligen vaka ~over oss, Tihocan." + /* 26 */ , "V~alkommen till mitt hem!@Vi tar en liten rundtur." + /* 27 */ , "Anv~and pilknapparna f~or att g^a till musikrummet." + /* 28 */ , "OK, vi ska r~ora oss lite nu!@Tryck p^a hoppknappen." + /* 29 */ , "Tryck nu p^a den igen och tryck sedan snabbt p^a en av@pilknapparna, och jag hoppar i den riktningen." + /* 30 */ , "Ah, den stora salen. Urs~akta l^adorna.@Jag ska l~agga saker i f~orr^ad och flyttfirman har inte kommit." + /* 31 */ , "Spring fram till en l^ada och medan du h^aller inne pilknappen fram^at,@tryck p^a actionknappen, d^a kl~attrar jag upp." + /* 32 */ , "Detta var en g^ang balsalen, men det ~ar mitt egna gym nu@Vad tycker du? @ Vi g~or n^agra ~ovningar." + /* 33 */ , "Jag springer inte ~overallt. Om jag vill vara f~orsiktig,@ g^ar jag. H^all ner g^a-knappen@och gÃ¥ till den vita linjen." + /* 34 */ , "Med g^a-knappen nedtryckt kan jag inte ramla av kanten@~aven om du f~ors~oker. Prova!." + /* 35 */ , "Om du vill se dig omkring h^aller du ner titta-knappen@och trycker p^a pilknapparna f~or att titta." + /* 36 */ , "Om ett hopp ~ar f~or l^angt f~or mig kan jag@ta tag i kanten och undvika ett otrevligt fall. G^a fram till kanten med den@vita linjen. Tryck sedan p^a hoppknappen@och direkt d~arefter framm^at. Medan jag fortfarande ~ar i luften, tryck och h^all inne actionknappen s^a jag tar tag i kanten." + /* 37 */ , "Tryck p^a pil upp f~or att kl~attra upp" + /* 38 */ , "Om jag springer och hoppar kan jag klara stora hopp utan bekymmer." + /* 39 */ , "G^a till kanten med den vita linjen tills jag stannar. Sl~app sedan@g^a knappen och tryck ner bak^at kort. Jag tar ett litet hopp bak^at. Tryck in pilknappen fram^at och omedelbart d~arefter hopp-knappen@ Jag hoppar i sista stund." + /* 40 */ , "Detta ~ar ett riktigt stort hopp! Starta hoppet precis som f~orut,@ n~ar jag ~ar i luften trycker du p^a actionknappen@och h^aller den nedtryck, s^a kan jag ta tag i kanten." + /* 41 */ , "Bra." + /* 42 */ , "F~ors~ok att kl~attra upp.@Tryck pil upp^at och actionknappen." + /* 43 */ , "Jag kan inte kl~attra upp h~ar eftersom utrymmet ~ar f~or litet.@Men om du trycker p^a knappen f~or h~oger, flyttar jag mig@i sidled tills att det finns tillr~ackligt med utrymme att komma upp." + /* 44 */ , "" + /* 45 */ , "Tryck bak^at s^a hoppar jag bak^at.@Tryck omedelbart p^a actionknappen och h^all ned den s^a att jag@tar tag i kanten n~ar jag faller ner." + /* 46 */ , "Sl~app nu." + /* 47 */ , "L^at oss ta en simtur." + /* 48 */ , "Hoppknappen och v~anster eller h~ogerknappen- samt anv~ands f~or@att styra mig under vatten." + /* 49 */ , "Ah! Luft!@Tryck bara p^a pilknapparna fram^at, v~anster och h~oger,@f~or att r~ora dig runt ytan. Tryck p^a hoppknappen@om du vill att jag ska dyka igen. Eller g^a till kanten och@tryck p^a actionknappen f~or att hoppa ur vattnet." + /* 50 */ , "jag m^aste byta kl~aderna." + /* 51 */ , "Va god le!" + /* 52 */ , "Det ~ar inget personligt." + /* 53 */ , "Du ger mig fortfarande huvudv~ark.@En liten f^agel viskade att jag ska skicka dig till helvetet!" + /* 54 */ , "Jag och mina v~anner f~orsvinner inte s^a l~att, Lara." + /* 55 */ , "Inte lite sent f~or prisutdelningen?@Men den som tar priset har vunnit" + /* 56 */ , "Du skjuter p^a mig?@Du skjuter p^a mig?@Det finns ingen annan h~ar, s^a du m^aste skjuta p^a mig!" +// TR1 levels + , "Lara's Hem" + , "Grottan" + , "Vilcabambas Stad" + , "F~orsvunna Dalen" + , "Qualopecs Gravkammare" + , "S:t Francis Kloster" + , "Kolosseum" + , "Midas Palats" + , "Cisternen" + , "Tihocans Gravkammare" + , "Khamoons Stad" + , "Khamoons Obelisk" + , "Scions Fristad" + , "Natla's Gruva" + , "Atlantis" + , "Den Stora Pyramiden" + , "^Aterv~and till Egypten" + , "Kattens tempel" + , "Atlantiska F~astningen" + , "Kupan" +// TR2 levels + , "Lara's Hem" + , "Den Kinesiska Muren" + , "Venedig" + , "Bartolis G~omst~alle" + , "Operahus" + , "Kustriggen" + , "Dykomr^ade" + , "40 Famnar" + , "Maria Dorias Vrakplats" + , "Hytterna" + , "D~acket" + , "Tibetanska H~ogl~andet" + , "Barkhang-Klostret" + , "Talion-Katakomberna" + , "Ispalatset" + , "Xian-templet" + , "Den Flytande ~On" + , "Drakens Lya" + , "Hem Ljuva Hem" +// TR3 levels + , "Lara's Hem" + , "Djungel" + , "Tempelruinerna" + , "Floden Ganges" + , "Kaliya-Grottorna" + , "Kustby" + , "Kraschplats" + , "Madubu Gorge" + , "Punatemplet" + , "Thames Wharf" + , "Aldwych" + , "Lud's Port" + , "Stad" + , "Nevada~oknen" + , "H~og s~akerhetsanstalten" + , "Area 51" + , "Antarktis" + , "RX-Techs Gruvor" + , "Tinnos F~orlorade Stad" + , "Meteoritgrottan" + , "All Hallows" +}; + +#endif diff --git a/src/lara.h b/src/lara.h index d54f9a68..bc893194 100644 --- a/src/lara.h +++ b/src/lara.h @@ -1301,7 +1301,7 @@ struct Lara : Character { updateTargets(); Controller *lookTarget = canLookAt() ? target : NULL; - if (camera->mode == Camera::MODE_LOOK) { + if (canLookAt() && (camera->mode == Camera::MODE_LOOK || (lookTarget == NULL && (camera->viewAngle.x != 0.0f || camera->viewAngle.y != 0.0f)))) { vec3 p = pos + vec3(camera->targetAngle.x, camera->targetAngle.y) * 8192.0f; Character::lookAtPos(&p); } else @@ -2304,7 +2304,7 @@ struct Lara : Character { if (stand == STAND_UNDERWATER || stand == STAND_ONWATER) return stand; if (stand == STAND_AIR) { - if (velocity.y > 0.0f && pos.y - waterLevel > 300.0) { + if (velocity.y > 0.0f && pos.y - waterLevel > 300.0f) { stopScreaming(); return STAND_UNDERWATER; } @@ -2326,7 +2326,7 @@ struct Lara : Character { return STAND_AIR; if (stand == STAND_AIR) { - if (velocity.y > 0.0f && pos.y - waterLevel > 300.0) { + if (velocity.y > 0.0f && pos.y - waterLevel > 300.0f) { waterSplash(); pos.y = waterLevel + waterDepth; game->playSound(TR::SND_WATER_SPLASH, pos, Sound::PAN); @@ -3116,8 +3116,6 @@ struct Lara : Character { vec2 L = joy.L; - if (L.length() < INPUT_JOY_DZ_STICK) L = vec2(0.0f); // dead zone - if (!((state == STATE_STOP || state == STATE_SURF_TREAD || state == STATE_HANG) && fabsf(L.x) < 0.5f && fabsf(L.y) < 0.5f)) { bool moving = state == STATE_RUN || state == STATE_WALK || state == STATE_BACK || state == STATE_FAST_BACK || state == STATE_SURF_SWIM || state == STATE_SURF_BACK || state == STATE_FORWARD_JUMP; @@ -3128,13 +3126,13 @@ struct Lara : Character { L.y = 0.0f; } - if (L.x != 0.0f) { + if (fabsf(L.x) > INPUT_JOY_DZ_STICK) { input |= (L.x < 0.0f) ? LEFT : RIGHT; if (moving || stand == STAND_UNDERWATER || stand == STAND_ONWATER) rotFactor.y = min(fabsf(L.x) / 0.9f, 1.0f); } - if (L.y != 0.0f) { + if (fabsf(L.y) > INPUT_JOY_DZ_STICK) { input |= (L.y < 0.0f) ? FORTH : BACK; if (stand == STAND_UNDERWATER) rotFactor.x = min(fabsf(L.y) / 0.9f, 1.0f); @@ -3210,6 +3208,10 @@ struct Lara : Character { vec4 p = game->projectPoint(vec4(item->pos, 1.0f)); + #ifdef _OS_WP8 + swap(p.x, p.y); + #endif + if (p.w != 0.0f) { p.x = ( p.x / p.w * 0.5f + 0.5f) * UI::width; p.y = (-p.y / p.w * 0.5f + 0.5f) * UI::height; @@ -3625,6 +3627,11 @@ struct Lara : Character { return false; } + #ifdef _OS_3DS // for some reason move() math works incorrect on 3DS + #pragma GCC push_options + #pragma GCC optimize ("O0") + #endif + void move() { vec3 vel = (velocity + flowVelocity) * Core::deltaTime * 30.0f + collisionOffset; vec3 opos(pos), offset(0.0f); @@ -3775,6 +3782,10 @@ struct Lara : Character { if (dozy) stand = STAND_UNDERWATER; } + #ifdef _OS_3DS + #pragma GCC pop_options + #endif + virtual void applyFlow(TR::Camera &sink) { if (stand != STAND_UNDERWATER && stand != STAND_ONWATER) return; @@ -3865,10 +3876,14 @@ struct Lara : Character { game->setRoomParams(getRoomIndex(), Shader::MIRROR, 0.3f, 0.3f, 0.3f, 1.0f, false); Core::updateLights(); */ - environment->bind(sEnvironment); + GAPI::Texture *dtex = Core::active.textures[sDiffuse]; + + environment->bind(sDiffuse); visibleMask ^= 0xFFFFFFFF; Controller::render(frustum, mesh, type, caustics); visibleMask ^= 0xFFFFFFFF; + + if (dtex) dtex->bind(sDiffuse); } } }; diff --git a/src/level.h b/src/level.h index 89dfcd69..8e85e5b5 100644 --- a/src/level.h +++ b/src/level.h @@ -66,6 +66,7 @@ struct Level : IGame { bool needRenderInventory; bool showStats; bool skyIsVisible; + bool paused; TR::LevelID nextLevel; @@ -345,6 +346,11 @@ struct Level : IGame { controller->next = NULL; controller->flags.state = TR::Entity::asNone; if (i >= level.entitiesBaseCount) { + + if (e.type == TR::Entity::ENEMY_SKATEBOARD) { + continue; + } + delete controller; e.controller = NULL; } @@ -396,7 +402,7 @@ struct Level : IGame { Stream::cacheWrite("settings", (char*)&settings, sizeof(settings)); if (rebuildShaders) { - #if !defined(_GAPI_D3D9) && !defined(_GAPI_D3D11) && !defined(_GAPI_GXM) + #if !defined(_GAPI_D3D8) && !defined(_GAPI_D3D9) && !defined(_GAPI_D3D11) && !defined(_GAPI_GXM) delete shaderCache; shaderCache = new ShaderCache(); #endif @@ -531,7 +537,7 @@ struct Level : IGame { alphaTest = true; } - #ifdef FFP + #if defined(FFP) || defined(_GAPI_TA) switch (type) { case Shader::SPRITE : case Shader::ROOM : @@ -595,11 +601,10 @@ struct Level : IGame { } virtual void setupBinding() { - atlasRooms->bind(sDiffuse); Core::whiteTex->bind(sNormal); Core::whiteTex->bind(sMask); Core::whiteTex->bind(sReflect); - Core::whiteCube->bind(sEnvironment); + atlasRooms->bind(sDiffuse); if (Core::pass != Core::passShadow) { Texture *shadowMap = shadow[player ? player->camera->cameraIndex : 0]; @@ -610,18 +615,25 @@ struct Level : IGame { } virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0, Core::Pass pass = Core::passAmbient) { - #ifdef FFP - return; - #endif + #ifdef FFP + return; + #endif - #ifdef _GAPI_SW - return; - #endif + #ifdef _GAPI_SW + return; + #endif - #ifdef _OS_3DS - return; // TODO render to cubemap face - GAPI::rotate90 = false; - #endif + #ifdef _GAPI_TA + return; + #endif + + #ifdef _GAPI_C3D + GAPI::rotate90 = false; + #endif + + #ifdef _GAPI_D3D8 + GAPI::setFrontFace(false); + #endif PROFILE_MARKER("ENVIRONMENT"); setupBinding(); @@ -644,9 +656,13 @@ struct Level : IGame { renderView(rIndex, false, false); } - #ifdef _OS_3DS - GAPI::rotate90 = true; - #endif + #ifdef _GAPI_D3D8 + GAPI::setFrontFace(true); + #endif + + #ifdef _GAPI_C3D + GAPI::rotate90 = true; + #endif Core::pass = tmpPass; Core::eye = tmpEye; @@ -662,6 +678,7 @@ struct Level : IGame { Core::setBasis(joints, level.models[modelIndex].mCount); Core::active.shader->setParam(uMaterial, Core::active.material); Core::active.shader->setParam(uAmbient, ambient[0], 6); + Core::setFog(FOG_NONE); Core::updateLights(); mesh->renderModel(modelIndex, underwater); // transparent @@ -670,6 +687,7 @@ struct Level : IGame { Core::setBasis(joints, level.models[modelIndex].mCount); Core::active.shader->setParam(uMaterial, Core::active.material); Core::active.shader->setParam(uAmbient, ambient[0], 6); + Core::setFog(FOG_NONE); Core::updateLights(); mesh->renderModel(modelIndex, underwater); // additive @@ -680,6 +698,7 @@ struct Level : IGame { Core::setBasis(joints, level.models[modelIndex].mCount); Core::active.shader->setParam(uMaterial, Core::active.material); Core::active.shader->setParam(uAmbient, ambient[0], 6); + Core::setFog(FOG_NONE); Core::updateLights(); mesh->renderModel(modelIndex, underwater); Core::setDepthWrite(true); @@ -922,6 +941,8 @@ struct Level : IGame { //============================== Level(Stream &stream) : level(stream), waitTrack(false), isEnded(false), cutsceneWaitTimer(0.0f), animTexTimer(0.0f), statsTimeDelta(0.0f) { + paused = false; + level.simpleItems = Core::settings.detail.simple == 1; level.initModelIndices(); @@ -1001,6 +1022,27 @@ struct Level : IGame { } */ + #if DUMP_SAMPLES + for (int i = 0; i < 256; i++) { + int16 a = level.soundsMap[i]; + if (a == -1) continue; + ASSERT(a < level.soundsInfoCount); + TR::SoundInfo &b = level.soundsInfo[a]; + for (int j = 0; j < b.flags.count; j++) { + //ASSERT((b.index + j) < level.soundOffsetsCount); + if ((b.index + j) < level.soundOffsetsCount) { + Debug::Level::dumpSample(&level, b.index + j, i, j); + } + } + } + loadNextLevel(); + #endif + + #if DUMP_PALETTE + Debug::Level::dumpPalette(&level, level.id); + loadNextLevel(); + #endif + saveResult = SAVE_RESULT_SUCCESS; if (loadSlot != -1 && saveSlots[loadSlot].getLevelID() == level.id) { parseSaveSlot(saveSlots[loadSlot]); @@ -1028,7 +1070,7 @@ struct Level : IGame { delete zoneCache; delete atlasRooms; - #ifndef FFP + #ifndef SPLIT_BY_TILE delete atlasObjects; delete atlasSprites; delete atlasGlyphs; @@ -1067,6 +1109,14 @@ struct Level : IGame { void addPlayer(int index) { if (level.isCutsceneLevel()) return; + Controller *c = Controller::first; + while (c) { + Controller *next = c->next; + if (c->getEntity().type == TR::Entity::FLAME && ((Flame*)c)->owner == players[index]) + removeEntity(c); + c = next; + } + if (!players[index]) { players[index] = (Lara*)addEntity(TR::Entity::LARA, 0, vec3(0.0f), 0.0f); players[index]->camera->cameraIndex = index; @@ -1080,14 +1130,6 @@ struct Level : IGame { Lara *lead = players[index ^ 1]; if (!lead) return; - Controller *c = Controller::first; - while (c) { - Controller *next = c->next; - if (c->getEntity().type == TR::Entity::FLAME && ((Flame*)c)->owner == players[index]) - removeEntity(c); - c = next; - } - players[index]->reset(lead->getRoomIndex(), lead->pos, lead->angle.y, lead->stand); } @@ -1100,6 +1142,7 @@ struct Level : IGame { } } } + removeEntity(players[index]); players[index] = NULL; } @@ -1649,7 +1692,7 @@ struct Level : IGame { tileData = new AtlasTile(); atlasRooms = rAtlas->pack(OPT_MIPMAPS | OPT_VRAM_3DS); - atlasObjects = oAtlas->pack(OPT_MIPMAPS | OPT_VRAM_3DS); + atlasObjects = oAtlas->pack(OPT_MIPMAPS); atlasSprites = sAtlas->pack(OPT_MIPMAPS); atlasGlyphs = gAtlas->pack(0); @@ -1796,12 +1839,18 @@ struct Level : IGame { TR::Entity &e = level.entities[i]; if (e.type == TR::Entity::CRYSTAL) { Crystal *c = (Crystal*)e.controller; - renderEnvironment(c->getRoomIndex(), c->pos - vec3(0, 512, 0), &c->environment); - #ifdef USE_CUBEMAP_MIPS - c->environment->generateMipMap(); - #endif + if (c->environment) { // already initialized and baked + continue; + } + c->bake(); + + #ifdef _GAPI_C3D + // C3D has a limit of GX commands for buffers clearing (GX_MemoryFill), so we limit render to one cubemap per frame + return; + #endif } } + needRedrawReflections = false; } void setMainLight(Controller *controller) { @@ -1810,7 +1859,7 @@ struct Level : IGame { } void renderSky() { - #ifndef _GAPI_GL + #if !defined(_GAPI_GL) && !defined(_GAPI_D3D11) return; #endif ASSERT(mesh->transparent == 0); @@ -1821,7 +1870,7 @@ struct Level : IGame { if (level.version & TR::VER_TR1) { if (Core::settings.detail.lighting < Core::Settings::HIGH || !Core::support.tex3D || !TR::getSkyParams(level.id, skyParams)) return; - type = Shader::SKY_CLOUDS_AZURE; + type = Shader::SKY_AZURE; } else { // TR2, TR3 if (level.extra.sky == -1) return; @@ -1859,7 +1908,7 @@ struct Level : IGame { time = (time - int(time)) * SKY_TIME_PERIOD; } - Core::active.shader->setParam(uParam, vec4(skyParams.wind * time, 1.0)); + Core::active.shader->setParam(uParam, vec4(skyParams.wind * time * 2.0f, 1.0)); Core::active.shader->setParam(uLightProj, *(mat4*)&skyParams); Core::active.shader->setParam(uPosScale, skyParams.cloudDownColor, 2); @@ -2222,25 +2271,35 @@ struct Level : IGame { } } - params->time += Core::deltaTime; - animTexTimer += Core::deltaTime; + if (camera->spectator && Input::lastState[0] == cStart) { + paused = !paused; + } - float timeStep = ANIM_TEX_TIMESTEP; - if (level.version & TR::VER_TR1) - timeStep *= 0.5f; + if (!paused) { + params->time += Core::deltaTime; + animTexTimer += Core::deltaTime; - if (animTexTimer > timeStep) { - level.shiftAnimTex(); - animTexTimer -= timeStep; - } + float timeStep = ANIM_TEX_TIMESTEP; + if (level.version & TR::VER_TR1) + timeStep *= 0.5f; - updateEffect(); + if (animTexTimer > timeStep) { + level.shiftAnimTex(); + animTexTimer -= timeStep; + } - Controller *c = Controller::first; - while (c) { - Controller *next = c->next; - c->update(); - c = next; + updateEffect(); + + Controller *c = Controller::first; + while (c) { + Controller *next = c->next; + c->update(); + c = next; + } + } else { + if (camera->spectator) { + camera->update(); + } } if (waterCache) @@ -2610,7 +2669,18 @@ struct Level : IGame { Texture *screen = NULL; if (water) { screen = (waterCache && waterCache->visible) ? waterCache->getScreenTex() : NULL; - Core::setTarget(screen, NULL, RT_CLEAR_COLOR | RT_CLEAR_DEPTH | RT_STORE_COLOR | (screen ? RT_STORE_DEPTH : 0)); // render to screen texture (FUCK YOU iOS!) or back buffer + + int clearFlags = RT_STORE_COLOR; + + if (screen) { + clearFlags |= RT_CLEAR_COLOR | RT_CLEAR_DEPTH | RT_STORE_DEPTH; + } + + #ifndef EARLY_CLEAR + clearFlags |= RT_CLEAR_COLOR | RT_CLEAR_DEPTH; + #endif + + Core::setTarget(screen, NULL, clearFlags); // render to screen texture or back buffer Core::validateRenderState(); setupBinding(); } @@ -2651,7 +2721,7 @@ struct Level : IGame { Core::Pass pass = Core::pass; if (water && waterCache && waterCache->visible && screen) { - Core::setTarget(NULL, NULL, RT_STORE_COLOR); + Core::setTarget(NULL, NULL, RT_CLEAR_DEPTH | RT_STORE_COLOR); Core::validateRenderState(); waterCache->blitTexture(screen); } @@ -2678,6 +2748,11 @@ struct Level : IGame { Core::mViewInv = mat4(pos, pos + dir, up); Core::mView = Core::mViewInv.inverseOrtho(); Core::mProj = GAPI::perspective(90, 1.0f, 32.0f, 45.0f * 1024.0f, 0.0f); + + #ifdef _GAPI_D3D8 + Core::mProj.scale(vec3(1.0f, -1.0f, 1.0f)); + #endif + Core::mViewProj = Core::mProj * Core::mView; Core::viewPos = Core::mViewInv.offset().xyz(); @@ -2702,7 +2777,7 @@ struct Level : IGame { if (GAPI::getProjRange() == mat4::PROJ_ZERO_POS) bias = mat4( 0.5f, 0.0f, 0.0f, 0.0f, - 0.0f,-0.5f, 0.0f, 0.0f, + 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.0f, 1.0f ); @@ -2715,6 +2790,10 @@ struct Level : IGame { ); } + #if defined(_GAPI_D3D8) || defined(_GAPI_D3D9) || defined(_GAPI_D3D11) || defined(_GAPI_GXM) + bias.e11 = -bias.e11; // vertical flip for UVs + #endif + m = bias * m; Core::mLightProj = m; @@ -2970,17 +3049,17 @@ struct Level : IGame { Core::setDepthTest(false); Core::validateRenderState(); // Debug::Level::rooms(level, lara->pos, lara->getEntity().room); - // Debug::Level::lights(level, player->getRoomIndex(), player); + // Debug::Level::lights(level, player->getRoomIndex(), player); // Debug::Level::sectors(this, players[0]->getRoomIndex(), (int)players[0]->pos.y); // Core::setDepthTest(false); // Debug::Level::portals(level); // Core::setDepthTest(true); // Debug::Level::meshes(level); // Debug::Level::entities(level); - // Debug::Level::zones(level, lara); + // Debug::Level::zones(this, players[0]); // Debug::Level::blocks(level); - // Debug::Level::path(level, (Enemy*)level.entities[105].controller); - // Debug::Level::debugOverlaps(level, lara->box); + // Debug::Level::path(level, (Enemy*)level.entities[21].controller); + // Debug::Level::debugOverlaps(level, players[0]->box); // Debug::Level::debugBoxes(level, lara->dbgBoxes, lara->dbgBoxesCount); Core::setDepthTest(true); Core::setBlendMode(bmNone); @@ -3157,7 +3236,6 @@ struct Level : IGame { if (needRedrawReflections) { initReflections(); - needRedrawReflections = false; } if (ambientCache) { @@ -3203,6 +3281,19 @@ struct Level : IGame { #endif } } + + if (Core::defaultTarget) { + Core::viewportDef = short4(0, 0, Core::defaultTarget->origWidth, Core::defaultTarget->origHeight); + } else { + Core::viewportDef = short4(0, 0, Core::width, Core::height); + } + + #ifdef EARLY_CLEAR + if (view == 0 && eye <= 0) { + Core::setTarget(NULL, NULL, RT_CLEAR_COLOR | RT_CLEAR_DEPTH | RT_STORE_COLOR | RT_STORE_DEPTH); + Core::validateRenderState(); + } + #endif } void renderEye(int eye, bool showUI, bool invBG) { @@ -3354,7 +3445,7 @@ struct Level : IGame { Core::resetLights(); - if (!level.isCutsceneLevel()) { + if (!level.isCutsceneLevel() && !camera->spectator) { // render health & oxygen bars vec2 size = vec2(180, 10); @@ -3391,7 +3482,9 @@ struct Level : IGame { UI::renderHelp(); } - UI::renderSubs(); + if (!camera->spectator) { + UI::renderSubs(); + } UI::end(); diff --git a/src/libs/gl/wglext.h b/src/libs/gl/wglext.h new file mode 100644 index 00000000..1f447dcb --- /dev/null +++ b/src/libs/gl/wglext.h @@ -0,0 +1,840 @@ +#ifndef __wglext_h_ +#define __wglext_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Copyright (c) 2013-2015 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ +/* +** This header is generated from the Khronos OpenGL / OpenGL ES XML +** API Registry. The current version of the Registry, generator scripts +** used to make the header, and the header can be found at +** http://www.opengl.org/registry/ +** +** Khronos $Revision: 31597 $ on $Date: 2015-06-25 16:32:35 -0400 (Thu, 25 Jun 2015) $ +*/ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#define WIN32_LEAN_AND_MEAN 1 +#include +#endif + +#define WGL_WGLEXT_VERSION 20150623 + +/* Generated C header for: + * API: wgl + * Versions considered: .* + * Versions emitted: _nomatch_^ + * Default extensions included: wgl + * Additional extensions included: _nomatch_^ + * Extensions removed: _nomatch_^ + */ + +#ifndef WGL_ARB_buffer_region +#define WGL_ARB_buffer_region 1 +#define WGL_FRONT_COLOR_BUFFER_BIT_ARB 0x00000001 +#define WGL_BACK_COLOR_BUFFER_BIT_ARB 0x00000002 +#define WGL_DEPTH_BUFFER_BIT_ARB 0x00000004 +#define WGL_STENCIL_BUFFER_BIT_ARB 0x00000008 +typedef HANDLE (WINAPI * PFNWGLCREATEBUFFERREGIONARBPROC) (HDC hDC, int iLayerPlane, UINT uType); +typedef VOID (WINAPI * PFNWGLDELETEBUFFERREGIONARBPROC) (HANDLE hRegion); +typedef BOOL (WINAPI * PFNWGLSAVEBUFFERREGIONARBPROC) (HANDLE hRegion, int x, int y, int width, int height); +typedef BOOL (WINAPI * PFNWGLRESTOREBUFFERREGIONARBPROC) (HANDLE hRegion, int x, int y, int width, int height, int xSrc, int ySrc); +#ifdef WGL_WGLEXT_PROTOTYPES +HANDLE WINAPI wglCreateBufferRegionARB (HDC hDC, int iLayerPlane, UINT uType); +VOID WINAPI wglDeleteBufferRegionARB (HANDLE hRegion); +BOOL WINAPI wglSaveBufferRegionARB (HANDLE hRegion, int x, int y, int width, int height); +BOOL WINAPI wglRestoreBufferRegionARB (HANDLE hRegion, int x, int y, int width, int height, int xSrc, int ySrc); +#endif +#endif /* WGL_ARB_buffer_region */ + +#ifndef WGL_ARB_context_flush_control +#define WGL_ARB_context_flush_control 1 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#endif /* WGL_ARB_context_flush_control */ + +#ifndef WGL_ARB_create_context +#define WGL_ARB_create_context 1 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define ERROR_INVALID_VERSION_ARB 0x2095 +typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC hDC, HGLRC hShareContext, const int *attribList); +#ifdef WGL_WGLEXT_PROTOTYPES +HGLRC WINAPI wglCreateContextAttribsARB (HDC hDC, HGLRC hShareContext, const int *attribList); +#endif +#endif /* WGL_ARB_create_context */ + +#ifndef WGL_ARB_create_context_profile +#define WGL_ARB_create_context_profile 1 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define ERROR_INVALID_PROFILE_ARB 0x2096 +#endif /* WGL_ARB_create_context_profile */ + +#ifndef WGL_ARB_create_context_robustness +#define WGL_ARB_create_context_robustness 1 +#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261 +#endif /* WGL_ARB_create_context_robustness */ + +#ifndef WGL_ARB_extensions_string +#define WGL_ARB_extensions_string 1 +typedef const char *(WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC) (HDC hdc); +#ifdef WGL_WGLEXT_PROTOTYPES +const char *WINAPI wglGetExtensionsStringARB (HDC hdc); +#endif +#endif /* WGL_ARB_extensions_string */ + +#ifndef WGL_ARB_framebuffer_sRGB +#define WGL_ARB_framebuffer_sRGB 1 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9 +#endif /* WGL_ARB_framebuffer_sRGB */ + +#ifndef WGL_ARB_make_current_read +#define WGL_ARB_make_current_read 1 +#define ERROR_INVALID_PIXEL_TYPE_ARB 0x2043 +#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 +typedef BOOL (WINAPI * PFNWGLMAKECONTEXTCURRENTARBPROC) (HDC hDrawDC, HDC hReadDC, HGLRC hglrc); +typedef HDC (WINAPI * PFNWGLGETCURRENTREADDCARBPROC) (void); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglMakeContextCurrentARB (HDC hDrawDC, HDC hReadDC, HGLRC hglrc); +HDC WINAPI wglGetCurrentReadDCARB (void); +#endif +#endif /* WGL_ARB_make_current_read */ + +#ifndef WGL_ARB_multisample +#define WGL_ARB_multisample 1 +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 +#endif /* WGL_ARB_multisample */ + +#ifndef WGL_ARB_pbuffer +#define WGL_ARB_pbuffer 1 +DECLARE_HANDLE(HPBUFFERARB); +#define WGL_DRAW_TO_PBUFFER_ARB 0x202D +#define WGL_MAX_PBUFFER_PIXELS_ARB 0x202E +#define WGL_MAX_PBUFFER_WIDTH_ARB 0x202F +#define WGL_MAX_PBUFFER_HEIGHT_ARB 0x2030 +#define WGL_PBUFFER_LARGEST_ARB 0x2033 +#define WGL_PBUFFER_WIDTH_ARB 0x2034 +#define WGL_PBUFFER_HEIGHT_ARB 0x2035 +#define WGL_PBUFFER_LOST_ARB 0x2036 +typedef HPBUFFERARB (WINAPI * PFNWGLCREATEPBUFFERARBPROC) (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList); +typedef HDC (WINAPI * PFNWGLGETPBUFFERDCARBPROC) (HPBUFFERARB hPbuffer); +typedef int (WINAPI * PFNWGLRELEASEPBUFFERDCARBPROC) (HPBUFFERARB hPbuffer, HDC hDC); +typedef BOOL (WINAPI * PFNWGLDESTROYPBUFFERARBPROC) (HPBUFFERARB hPbuffer); +typedef BOOL (WINAPI * PFNWGLQUERYPBUFFERARBPROC) (HPBUFFERARB hPbuffer, int iAttribute, int *piValue); +#ifdef WGL_WGLEXT_PROTOTYPES +HPBUFFERARB WINAPI wglCreatePbufferARB (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList); +HDC WINAPI wglGetPbufferDCARB (HPBUFFERARB hPbuffer); +int WINAPI wglReleasePbufferDCARB (HPBUFFERARB hPbuffer, HDC hDC); +BOOL WINAPI wglDestroyPbufferARB (HPBUFFERARB hPbuffer); +BOOL WINAPI wglQueryPbufferARB (HPBUFFERARB hPbuffer, int iAttribute, int *piValue); +#endif +#endif /* WGL_ARB_pbuffer */ + +#ifndef WGL_ARB_pixel_format +#define WGL_ARB_pixel_format 1 +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_DRAW_TO_BITMAP_ARB 0x2002 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NEED_PALETTE_ARB 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006 +#define WGL_SWAP_METHOD_ARB 0x2007 +#define WGL_NUMBER_OVERLAYS_ARB 0x2008 +#define WGL_NUMBER_UNDERLAYS_ARB 0x2009 +#define WGL_TRANSPARENT_ARB 0x200A +#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037 +#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038 +#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039 +#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A +#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B +#define WGL_SHARE_DEPTH_ARB 0x200C +#define WGL_SHARE_STENCIL_ARB 0x200D +#define WGL_SHARE_ACCUM_ARB 0x200E +#define WGL_SUPPORT_GDI_ARB 0x200F +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_STEREO_ARB 0x2012 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_RED_SHIFT_ARB 0x2016 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_GREEN_SHIFT_ARB 0x2018 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_BLUE_SHIFT_ARB 0x201A +#define WGL_ALPHA_BITS_ARB 0x201B +#define WGL_ALPHA_SHIFT_ARB 0x201C +#define WGL_ACCUM_BITS_ARB 0x201D +#define WGL_ACCUM_RED_BITS_ARB 0x201E +#define WGL_ACCUM_GREEN_BITS_ARB 0x201F +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_GENERIC_ACCELERATION_ARB 0x2026 +#define WGL_FULL_ACCELERATION_ARB 0x2027 +#define WGL_SWAP_EXCHANGE_ARB 0x2028 +#define WGL_SWAP_COPY_ARB 0x2029 +#define WGL_SWAP_UNDEFINED_ARB 0x202A +#define WGL_TYPE_RGBA_ARB 0x202B +#define WGL_TYPE_COLORINDEX_ARB 0x202C +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues); +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBFVARBPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, FLOAT *pfValues); +typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATARBPROC) (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetPixelFormatAttribivARB (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues); +BOOL WINAPI wglGetPixelFormatAttribfvARB (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, FLOAT *pfValues); +BOOL WINAPI wglChoosePixelFormatARB (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +#endif +#endif /* WGL_ARB_pixel_format */ + +#ifndef WGL_ARB_pixel_format_float +#define WGL_ARB_pixel_format_float 1 +#define WGL_TYPE_RGBA_FLOAT_ARB 0x21A0 +#endif /* WGL_ARB_pixel_format_float */ + +#ifndef WGL_ARB_render_texture +#define WGL_ARB_render_texture 1 +#define WGL_BIND_TO_TEXTURE_RGB_ARB 0x2070 +#define WGL_BIND_TO_TEXTURE_RGBA_ARB 0x2071 +#define WGL_TEXTURE_FORMAT_ARB 0x2072 +#define WGL_TEXTURE_TARGET_ARB 0x2073 +#define WGL_MIPMAP_TEXTURE_ARB 0x2074 +#define WGL_TEXTURE_RGB_ARB 0x2075 +#define WGL_TEXTURE_RGBA_ARB 0x2076 +#define WGL_NO_TEXTURE_ARB 0x2077 +#define WGL_TEXTURE_CUBE_MAP_ARB 0x2078 +#define WGL_TEXTURE_1D_ARB 0x2079 +#define WGL_TEXTURE_2D_ARB 0x207A +#define WGL_MIPMAP_LEVEL_ARB 0x207B +#define WGL_CUBE_MAP_FACE_ARB 0x207C +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x207D +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x207E +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x207F +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x2080 +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x2081 +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x2082 +#define WGL_FRONT_LEFT_ARB 0x2083 +#define WGL_FRONT_RIGHT_ARB 0x2084 +#define WGL_BACK_LEFT_ARB 0x2085 +#define WGL_BACK_RIGHT_ARB 0x2086 +#define WGL_AUX0_ARB 0x2087 +#define WGL_AUX1_ARB 0x2088 +#define WGL_AUX2_ARB 0x2089 +#define WGL_AUX3_ARB 0x208A +#define WGL_AUX4_ARB 0x208B +#define WGL_AUX5_ARB 0x208C +#define WGL_AUX6_ARB 0x208D +#define WGL_AUX7_ARB 0x208E +#define WGL_AUX8_ARB 0x208F +#define WGL_AUX9_ARB 0x2090 +typedef BOOL (WINAPI * PFNWGLBINDTEXIMAGEARBPROC) (HPBUFFERARB hPbuffer, int iBuffer); +typedef BOOL (WINAPI * PFNWGLRELEASETEXIMAGEARBPROC) (HPBUFFERARB hPbuffer, int iBuffer); +typedef BOOL (WINAPI * PFNWGLSETPBUFFERATTRIBARBPROC) (HPBUFFERARB hPbuffer, const int *piAttribList); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglBindTexImageARB (HPBUFFERARB hPbuffer, int iBuffer); +BOOL WINAPI wglReleaseTexImageARB (HPBUFFERARB hPbuffer, int iBuffer); +BOOL WINAPI wglSetPbufferAttribARB (HPBUFFERARB hPbuffer, const int *piAttribList); +#endif +#endif /* WGL_ARB_render_texture */ + +#ifndef WGL_ARB_robustness_application_isolation +#define WGL_ARB_robustness_application_isolation 1 +#define WGL_CONTEXT_RESET_ISOLATION_BIT_ARB 0x00000008 +#endif /* WGL_ARB_robustness_application_isolation */ + +#ifndef WGL_ARB_robustness_share_group_isolation +#define WGL_ARB_robustness_share_group_isolation 1 +#endif /* WGL_ARB_robustness_share_group_isolation */ + +#ifndef WGL_3DFX_multisample +#define WGL_3DFX_multisample 1 +#define WGL_SAMPLE_BUFFERS_3DFX 0x2060 +#define WGL_SAMPLES_3DFX 0x2061 +#endif /* WGL_3DFX_multisample */ + +#ifndef WGL_3DL_stereo_control +#define WGL_3DL_stereo_control 1 +#define WGL_STEREO_EMITTER_ENABLE_3DL 0x2055 +#define WGL_STEREO_EMITTER_DISABLE_3DL 0x2056 +#define WGL_STEREO_POLARITY_NORMAL_3DL 0x2057 +#define WGL_STEREO_POLARITY_INVERT_3DL 0x2058 +typedef BOOL (WINAPI * PFNWGLSETSTEREOEMITTERSTATE3DLPROC) (HDC hDC, UINT uState); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglSetStereoEmitterState3DL (HDC hDC, UINT uState); +#endif +#endif /* WGL_3DL_stereo_control */ + +#ifndef WGL_AMD_gpu_association +#define WGL_AMD_gpu_association 1 +#define WGL_GPU_VENDOR_AMD 0x1F00 +#define WGL_GPU_RENDERER_STRING_AMD 0x1F01 +#define WGL_GPU_OPENGL_VERSION_STRING_AMD 0x1F02 +#define WGL_GPU_FASTEST_TARGET_GPUS_AMD 0x21A2 +#define WGL_GPU_RAM_AMD 0x21A3 +#define WGL_GPU_CLOCK_AMD 0x21A4 +#define WGL_GPU_NUM_PIPES_AMD 0x21A5 +#define WGL_GPU_NUM_SIMD_AMD 0x21A6 +#define WGL_GPU_NUM_RB_AMD 0x21A7 +#define WGL_GPU_NUM_SPI_AMD 0x21A8 +typedef UINT (WINAPI * PFNWGLGETGPUIDSAMDPROC) (UINT maxCount, UINT *ids); +typedef INT (WINAPI * PFNWGLGETGPUINFOAMDPROC) (UINT id, int property, GLenum dataType, UINT size, void *data); +typedef UINT (WINAPI * PFNWGLGETCONTEXTGPUIDAMDPROC) (HGLRC hglrc); +typedef HGLRC (WINAPI * PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC) (UINT id); +typedef HGLRC (WINAPI * PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC) (UINT id, HGLRC hShareContext, const int *attribList); +typedef BOOL (WINAPI * PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC) (HGLRC hglrc); +typedef BOOL (WINAPI * PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC) (HGLRC hglrc); +typedef HGLRC (WINAPI * PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC) (void); +typedef VOID (WINAPI * PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC) (HGLRC dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#ifdef WGL_WGLEXT_PROTOTYPES +UINT WINAPI wglGetGPUIDsAMD (UINT maxCount, UINT *ids); +INT WINAPI wglGetGPUInfoAMD (UINT id, int property, GLenum dataType, UINT size, void *data); +UINT WINAPI wglGetContextGPUIDAMD (HGLRC hglrc); +HGLRC WINAPI wglCreateAssociatedContextAMD (UINT id); +HGLRC WINAPI wglCreateAssociatedContextAttribsAMD (UINT id, HGLRC hShareContext, const int *attribList); +BOOL WINAPI wglDeleteAssociatedContextAMD (HGLRC hglrc); +BOOL WINAPI wglMakeAssociatedContextCurrentAMD (HGLRC hglrc); +HGLRC WINAPI wglGetCurrentAssociatedContextAMD (void); +VOID WINAPI wglBlitContextFramebufferAMD (HGLRC dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif +#endif /* WGL_AMD_gpu_association */ + +#ifndef WGL_ATI_pixel_format_float +#define WGL_ATI_pixel_format_float 1 +#define WGL_TYPE_RGBA_FLOAT_ATI 0x21A0 +#endif /* WGL_ATI_pixel_format_float */ + +#ifndef WGL_EXT_create_context_es2_profile +#define WGL_EXT_create_context_es2_profile 1 +#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 +#endif /* WGL_EXT_create_context_es2_profile */ + +#ifndef WGL_EXT_create_context_es_profile +#define WGL_EXT_create_context_es_profile 1 +#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004 +#endif /* WGL_EXT_create_context_es_profile */ + +#ifndef WGL_EXT_depth_float +#define WGL_EXT_depth_float 1 +#define WGL_DEPTH_FLOAT_EXT 0x2040 +#endif /* WGL_EXT_depth_float */ + +#ifndef WGL_EXT_display_color_table +#define WGL_EXT_display_color_table 1 +typedef GLboolean (WINAPI * PFNWGLCREATEDISPLAYCOLORTABLEEXTPROC) (GLushort id); +typedef GLboolean (WINAPI * PFNWGLLOADDISPLAYCOLORTABLEEXTPROC) (const GLushort *table, GLuint length); +typedef GLboolean (WINAPI * PFNWGLBINDDISPLAYCOLORTABLEEXTPROC) (GLushort id); +typedef VOID (WINAPI * PFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC) (GLushort id); +#ifdef WGL_WGLEXT_PROTOTYPES +GLboolean WINAPI wglCreateDisplayColorTableEXT (GLushort id); +GLboolean WINAPI wglLoadDisplayColorTableEXT (const GLushort *table, GLuint length); +GLboolean WINAPI wglBindDisplayColorTableEXT (GLushort id); +VOID WINAPI wglDestroyDisplayColorTableEXT (GLushort id); +#endif +#endif /* WGL_EXT_display_color_table */ + +#ifndef WGL_EXT_extensions_string +#define WGL_EXT_extensions_string 1 +typedef const char *(WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC) (void); +#ifdef WGL_WGLEXT_PROTOTYPES +const char *WINAPI wglGetExtensionsStringEXT (void); +#endif +#endif /* WGL_EXT_extensions_string */ + +#ifndef WGL_EXT_framebuffer_sRGB +#define WGL_EXT_framebuffer_sRGB 1 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x20A9 +#endif /* WGL_EXT_framebuffer_sRGB */ + +#ifndef WGL_EXT_make_current_read +#define WGL_EXT_make_current_read 1 +#define ERROR_INVALID_PIXEL_TYPE_EXT 0x2043 +typedef BOOL (WINAPI * PFNWGLMAKECONTEXTCURRENTEXTPROC) (HDC hDrawDC, HDC hReadDC, HGLRC hglrc); +typedef HDC (WINAPI * PFNWGLGETCURRENTREADDCEXTPROC) (void); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglMakeContextCurrentEXT (HDC hDrawDC, HDC hReadDC, HGLRC hglrc); +HDC WINAPI wglGetCurrentReadDCEXT (void); +#endif +#endif /* WGL_EXT_make_current_read */ + +#ifndef WGL_EXT_multisample +#define WGL_EXT_multisample 1 +#define WGL_SAMPLE_BUFFERS_EXT 0x2041 +#define WGL_SAMPLES_EXT 0x2042 +#endif /* WGL_EXT_multisample */ + +#ifndef WGL_EXT_pbuffer +#define WGL_EXT_pbuffer 1 +DECLARE_HANDLE(HPBUFFEREXT); +#define WGL_DRAW_TO_PBUFFER_EXT 0x202D +#define WGL_MAX_PBUFFER_PIXELS_EXT 0x202E +#define WGL_MAX_PBUFFER_WIDTH_EXT 0x202F +#define WGL_MAX_PBUFFER_HEIGHT_EXT 0x2030 +#define WGL_OPTIMAL_PBUFFER_WIDTH_EXT 0x2031 +#define WGL_OPTIMAL_PBUFFER_HEIGHT_EXT 0x2032 +#define WGL_PBUFFER_LARGEST_EXT 0x2033 +#define WGL_PBUFFER_WIDTH_EXT 0x2034 +#define WGL_PBUFFER_HEIGHT_EXT 0x2035 +typedef HPBUFFEREXT (WINAPI * PFNWGLCREATEPBUFFEREXTPROC) (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList); +typedef HDC (WINAPI * PFNWGLGETPBUFFERDCEXTPROC) (HPBUFFEREXT hPbuffer); +typedef int (WINAPI * PFNWGLRELEASEPBUFFERDCEXTPROC) (HPBUFFEREXT hPbuffer, HDC hDC); +typedef BOOL (WINAPI * PFNWGLDESTROYPBUFFEREXTPROC) (HPBUFFEREXT hPbuffer); +typedef BOOL (WINAPI * PFNWGLQUERYPBUFFEREXTPROC) (HPBUFFEREXT hPbuffer, int iAttribute, int *piValue); +#ifdef WGL_WGLEXT_PROTOTYPES +HPBUFFEREXT WINAPI wglCreatePbufferEXT (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList); +HDC WINAPI wglGetPbufferDCEXT (HPBUFFEREXT hPbuffer); +int WINAPI wglReleasePbufferDCEXT (HPBUFFEREXT hPbuffer, HDC hDC); +BOOL WINAPI wglDestroyPbufferEXT (HPBUFFEREXT hPbuffer); +BOOL WINAPI wglQueryPbufferEXT (HPBUFFEREXT hPbuffer, int iAttribute, int *piValue); +#endif +#endif /* WGL_EXT_pbuffer */ + +#ifndef WGL_EXT_pixel_format +#define WGL_EXT_pixel_format 1 +#define WGL_NUMBER_PIXEL_FORMATS_EXT 0x2000 +#define WGL_DRAW_TO_WINDOW_EXT 0x2001 +#define WGL_DRAW_TO_BITMAP_EXT 0x2002 +#define WGL_ACCELERATION_EXT 0x2003 +#define WGL_NEED_PALETTE_EXT 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_EXT 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_EXT 0x2006 +#define WGL_SWAP_METHOD_EXT 0x2007 +#define WGL_NUMBER_OVERLAYS_EXT 0x2008 +#define WGL_NUMBER_UNDERLAYS_EXT 0x2009 +#define WGL_TRANSPARENT_EXT 0x200A +#define WGL_TRANSPARENT_VALUE_EXT 0x200B +#define WGL_SHARE_DEPTH_EXT 0x200C +#define WGL_SHARE_STENCIL_EXT 0x200D +#define WGL_SHARE_ACCUM_EXT 0x200E +#define WGL_SUPPORT_GDI_EXT 0x200F +#define WGL_SUPPORT_OPENGL_EXT 0x2010 +#define WGL_DOUBLE_BUFFER_EXT 0x2011 +#define WGL_STEREO_EXT 0x2012 +#define WGL_PIXEL_TYPE_EXT 0x2013 +#define WGL_COLOR_BITS_EXT 0x2014 +#define WGL_RED_BITS_EXT 0x2015 +#define WGL_RED_SHIFT_EXT 0x2016 +#define WGL_GREEN_BITS_EXT 0x2017 +#define WGL_GREEN_SHIFT_EXT 0x2018 +#define WGL_BLUE_BITS_EXT 0x2019 +#define WGL_BLUE_SHIFT_EXT 0x201A +#define WGL_ALPHA_BITS_EXT 0x201B +#define WGL_ALPHA_SHIFT_EXT 0x201C +#define WGL_ACCUM_BITS_EXT 0x201D +#define WGL_ACCUM_RED_BITS_EXT 0x201E +#define WGL_ACCUM_GREEN_BITS_EXT 0x201F +#define WGL_ACCUM_BLUE_BITS_EXT 0x2020 +#define WGL_ACCUM_ALPHA_BITS_EXT 0x2021 +#define WGL_DEPTH_BITS_EXT 0x2022 +#define WGL_STENCIL_BITS_EXT 0x2023 +#define WGL_AUX_BUFFERS_EXT 0x2024 +#define WGL_NO_ACCELERATION_EXT 0x2025 +#define WGL_GENERIC_ACCELERATION_EXT 0x2026 +#define WGL_FULL_ACCELERATION_EXT 0x2027 +#define WGL_SWAP_EXCHANGE_EXT 0x2028 +#define WGL_SWAP_COPY_EXT 0x2029 +#define WGL_SWAP_UNDEFINED_EXT 0x202A +#define WGL_TYPE_RGBA_EXT 0x202B +#define WGL_TYPE_COLORINDEX_EXT 0x202C +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVEXTPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, int *piValues); +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBFVEXTPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, FLOAT *pfValues); +typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATEXTPROC) (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetPixelFormatAttribivEXT (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, int *piValues); +BOOL WINAPI wglGetPixelFormatAttribfvEXT (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, FLOAT *pfValues); +BOOL WINAPI wglChoosePixelFormatEXT (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +#endif +#endif /* WGL_EXT_pixel_format */ + +#ifndef WGL_EXT_pixel_format_packed_float +#define WGL_EXT_pixel_format_packed_float 1 +#define WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT 0x20A8 +#endif /* WGL_EXT_pixel_format_packed_float */ + +#ifndef WGL_EXT_swap_control +#define WGL_EXT_swap_control 1 +typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC) (int interval); +typedef int (WINAPI * PFNWGLGETSWAPINTERVALEXTPROC) (void); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglSwapIntervalEXT (int interval); +int WINAPI wglGetSwapIntervalEXT (void); +#endif +#endif /* WGL_EXT_swap_control */ + +#ifndef WGL_EXT_swap_control_tear +#define WGL_EXT_swap_control_tear 1 +#endif /* WGL_EXT_swap_control_tear */ + +#ifndef WGL_I3D_digital_video_control +#define WGL_I3D_digital_video_control 1 +#define WGL_DIGITAL_VIDEO_CURSOR_ALPHA_FRAMEBUFFER_I3D 0x2050 +#define WGL_DIGITAL_VIDEO_CURSOR_ALPHA_VALUE_I3D 0x2051 +#define WGL_DIGITAL_VIDEO_CURSOR_INCLUDED_I3D 0x2052 +#define WGL_DIGITAL_VIDEO_GAMMA_CORRECTED_I3D 0x2053 +typedef BOOL (WINAPI * PFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC) (HDC hDC, int iAttribute, int *piValue); +typedef BOOL (WINAPI * PFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC) (HDC hDC, int iAttribute, const int *piValue); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetDigitalVideoParametersI3D (HDC hDC, int iAttribute, int *piValue); +BOOL WINAPI wglSetDigitalVideoParametersI3D (HDC hDC, int iAttribute, const int *piValue); +#endif +#endif /* WGL_I3D_digital_video_control */ + +#ifndef WGL_I3D_gamma +#define WGL_I3D_gamma 1 +#define WGL_GAMMA_TABLE_SIZE_I3D 0x204E +#define WGL_GAMMA_EXCLUDE_DESKTOP_I3D 0x204F +typedef BOOL (WINAPI * PFNWGLGETGAMMATABLEPARAMETERSI3DPROC) (HDC hDC, int iAttribute, int *piValue); +typedef BOOL (WINAPI * PFNWGLSETGAMMATABLEPARAMETERSI3DPROC) (HDC hDC, int iAttribute, const int *piValue); +typedef BOOL (WINAPI * PFNWGLGETGAMMATABLEI3DPROC) (HDC hDC, int iEntries, USHORT *puRed, USHORT *puGreen, USHORT *puBlue); +typedef BOOL (WINAPI * PFNWGLSETGAMMATABLEI3DPROC) (HDC hDC, int iEntries, const USHORT *puRed, const USHORT *puGreen, const USHORT *puBlue); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetGammaTableParametersI3D (HDC hDC, int iAttribute, int *piValue); +BOOL WINAPI wglSetGammaTableParametersI3D (HDC hDC, int iAttribute, const int *piValue); +BOOL WINAPI wglGetGammaTableI3D (HDC hDC, int iEntries, USHORT *puRed, USHORT *puGreen, USHORT *puBlue); +BOOL WINAPI wglSetGammaTableI3D (HDC hDC, int iEntries, const USHORT *puRed, const USHORT *puGreen, const USHORT *puBlue); +#endif +#endif /* WGL_I3D_gamma */ + +#ifndef WGL_I3D_genlock +#define WGL_I3D_genlock 1 +#define WGL_GENLOCK_SOURCE_MULTIVIEW_I3D 0x2044 +#define WGL_GENLOCK_SOURCE_EXTERNAL_SYNC_I3D 0x2045 +#define WGL_GENLOCK_SOURCE_EXTERNAL_FIELD_I3D 0x2046 +#define WGL_GENLOCK_SOURCE_EXTERNAL_TTL_I3D 0x2047 +#define WGL_GENLOCK_SOURCE_DIGITAL_SYNC_I3D 0x2048 +#define WGL_GENLOCK_SOURCE_DIGITAL_FIELD_I3D 0x2049 +#define WGL_GENLOCK_SOURCE_EDGE_FALLING_I3D 0x204A +#define WGL_GENLOCK_SOURCE_EDGE_RISING_I3D 0x204B +#define WGL_GENLOCK_SOURCE_EDGE_BOTH_I3D 0x204C +typedef BOOL (WINAPI * PFNWGLENABLEGENLOCKI3DPROC) (HDC hDC); +typedef BOOL (WINAPI * PFNWGLDISABLEGENLOCKI3DPROC) (HDC hDC); +typedef BOOL (WINAPI * PFNWGLISENABLEDGENLOCKI3DPROC) (HDC hDC, BOOL *pFlag); +typedef BOOL (WINAPI * PFNWGLGENLOCKSOURCEI3DPROC) (HDC hDC, UINT uSource); +typedef BOOL (WINAPI * PFNWGLGETGENLOCKSOURCEI3DPROC) (HDC hDC, UINT *uSource); +typedef BOOL (WINAPI * PFNWGLGENLOCKSOURCEEDGEI3DPROC) (HDC hDC, UINT uEdge); +typedef BOOL (WINAPI * PFNWGLGETGENLOCKSOURCEEDGEI3DPROC) (HDC hDC, UINT *uEdge); +typedef BOOL (WINAPI * PFNWGLGENLOCKSAMPLERATEI3DPROC) (HDC hDC, UINT uRate); +typedef BOOL (WINAPI * PFNWGLGETGENLOCKSAMPLERATEI3DPROC) (HDC hDC, UINT *uRate); +typedef BOOL (WINAPI * PFNWGLGENLOCKSOURCEDELAYI3DPROC) (HDC hDC, UINT uDelay); +typedef BOOL (WINAPI * PFNWGLGETGENLOCKSOURCEDELAYI3DPROC) (HDC hDC, UINT *uDelay); +typedef BOOL (WINAPI * PFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC) (HDC hDC, UINT *uMaxLineDelay, UINT *uMaxPixelDelay); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglEnableGenlockI3D (HDC hDC); +BOOL WINAPI wglDisableGenlockI3D (HDC hDC); +BOOL WINAPI wglIsEnabledGenlockI3D (HDC hDC, BOOL *pFlag); +BOOL WINAPI wglGenlockSourceI3D (HDC hDC, UINT uSource); +BOOL WINAPI wglGetGenlockSourceI3D (HDC hDC, UINT *uSource); +BOOL WINAPI wglGenlockSourceEdgeI3D (HDC hDC, UINT uEdge); +BOOL WINAPI wglGetGenlockSourceEdgeI3D (HDC hDC, UINT *uEdge); +BOOL WINAPI wglGenlockSampleRateI3D (HDC hDC, UINT uRate); +BOOL WINAPI wglGetGenlockSampleRateI3D (HDC hDC, UINT *uRate); +BOOL WINAPI wglGenlockSourceDelayI3D (HDC hDC, UINT uDelay); +BOOL WINAPI wglGetGenlockSourceDelayI3D (HDC hDC, UINT *uDelay); +BOOL WINAPI wglQueryGenlockMaxSourceDelayI3D (HDC hDC, UINT *uMaxLineDelay, UINT *uMaxPixelDelay); +#endif +#endif /* WGL_I3D_genlock */ + +#ifndef WGL_I3D_image_buffer +#define WGL_I3D_image_buffer 1 +#define WGL_IMAGE_BUFFER_MIN_ACCESS_I3D 0x00000001 +#define WGL_IMAGE_BUFFER_LOCK_I3D 0x00000002 +typedef LPVOID (WINAPI * PFNWGLCREATEIMAGEBUFFERI3DPROC) (HDC hDC, DWORD dwSize, UINT uFlags); +typedef BOOL (WINAPI * PFNWGLDESTROYIMAGEBUFFERI3DPROC) (HDC hDC, LPVOID pAddress); +typedef BOOL (WINAPI * PFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC) (HDC hDC, const HANDLE *pEvent, const LPVOID *pAddress, const DWORD *pSize, UINT count); +typedef BOOL (WINAPI * PFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC) (HDC hDC, const LPVOID *pAddress, UINT count); +#ifdef WGL_WGLEXT_PROTOTYPES +LPVOID WINAPI wglCreateImageBufferI3D (HDC hDC, DWORD dwSize, UINT uFlags); +BOOL WINAPI wglDestroyImageBufferI3D (HDC hDC, LPVOID pAddress); +BOOL WINAPI wglAssociateImageBufferEventsI3D (HDC hDC, const HANDLE *pEvent, const LPVOID *pAddress, const DWORD *pSize, UINT count); +BOOL WINAPI wglReleaseImageBufferEventsI3D (HDC hDC, const LPVOID *pAddress, UINT count); +#endif +#endif /* WGL_I3D_image_buffer */ + +#ifndef WGL_I3D_swap_frame_lock +#define WGL_I3D_swap_frame_lock 1 +typedef BOOL (WINAPI * PFNWGLENABLEFRAMELOCKI3DPROC) (void); +typedef BOOL (WINAPI * PFNWGLDISABLEFRAMELOCKI3DPROC) (void); +typedef BOOL (WINAPI * PFNWGLISENABLEDFRAMELOCKI3DPROC) (BOOL *pFlag); +typedef BOOL (WINAPI * PFNWGLQUERYFRAMELOCKMASTERI3DPROC) (BOOL *pFlag); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglEnableFrameLockI3D (void); +BOOL WINAPI wglDisableFrameLockI3D (void); +BOOL WINAPI wglIsEnabledFrameLockI3D (BOOL *pFlag); +BOOL WINAPI wglQueryFrameLockMasterI3D (BOOL *pFlag); +#endif +#endif /* WGL_I3D_swap_frame_lock */ + +#ifndef WGL_I3D_swap_frame_usage +#define WGL_I3D_swap_frame_usage 1 +typedef BOOL (WINAPI * PFNWGLGETFRAMEUSAGEI3DPROC) (float *pUsage); +typedef BOOL (WINAPI * PFNWGLBEGINFRAMETRACKINGI3DPROC) (void); +typedef BOOL (WINAPI * PFNWGLENDFRAMETRACKINGI3DPROC) (void); +typedef BOOL (WINAPI * PFNWGLQUERYFRAMETRACKINGI3DPROC) (DWORD *pFrameCount, DWORD *pMissedFrames, float *pLastMissedUsage); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetFrameUsageI3D (float *pUsage); +BOOL WINAPI wglBeginFrameTrackingI3D (void); +BOOL WINAPI wglEndFrameTrackingI3D (void); +BOOL WINAPI wglQueryFrameTrackingI3D (DWORD *pFrameCount, DWORD *pMissedFrames, float *pLastMissedUsage); +#endif +#endif /* WGL_I3D_swap_frame_usage */ + +#ifndef WGL_NV_DX_interop +#define WGL_NV_DX_interop 1 +#define WGL_ACCESS_READ_ONLY_NV 0x00000000 +#define WGL_ACCESS_READ_WRITE_NV 0x00000001 +#define WGL_ACCESS_WRITE_DISCARD_NV 0x00000002 +typedef BOOL (WINAPI * PFNWGLDXSETRESOURCESHAREHANDLENVPROC) (void *dxObject, HANDLE shareHandle); +typedef HANDLE (WINAPI * PFNWGLDXOPENDEVICENVPROC) (void *dxDevice); +typedef BOOL (WINAPI * PFNWGLDXCLOSEDEVICENVPROC) (HANDLE hDevice); +typedef HANDLE (WINAPI * PFNWGLDXREGISTEROBJECTNVPROC) (HANDLE hDevice, void *dxObject, GLuint name, GLenum type, GLenum access); +typedef BOOL (WINAPI * PFNWGLDXUNREGISTEROBJECTNVPROC) (HANDLE hDevice, HANDLE hObject); +typedef BOOL (WINAPI * PFNWGLDXOBJECTACCESSNVPROC) (HANDLE hObject, GLenum access); +typedef BOOL (WINAPI * PFNWGLDXLOCKOBJECTSNVPROC) (HANDLE hDevice, GLint count, HANDLE *hObjects); +typedef BOOL (WINAPI * PFNWGLDXUNLOCKOBJECTSNVPROC) (HANDLE hDevice, GLint count, HANDLE *hObjects); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglDXSetResourceShareHandleNV (void *dxObject, HANDLE shareHandle); +HANDLE WINAPI wglDXOpenDeviceNV (void *dxDevice); +BOOL WINAPI wglDXCloseDeviceNV (HANDLE hDevice); +HANDLE WINAPI wglDXRegisterObjectNV (HANDLE hDevice, void *dxObject, GLuint name, GLenum type, GLenum access); +BOOL WINAPI wglDXUnregisterObjectNV (HANDLE hDevice, HANDLE hObject); +BOOL WINAPI wglDXObjectAccessNV (HANDLE hObject, GLenum access); +BOOL WINAPI wglDXLockObjectsNV (HANDLE hDevice, GLint count, HANDLE *hObjects); +BOOL WINAPI wglDXUnlockObjectsNV (HANDLE hDevice, GLint count, HANDLE *hObjects); +#endif +#endif /* WGL_NV_DX_interop */ + +#ifndef WGL_NV_DX_interop2 +#define WGL_NV_DX_interop2 1 +#endif /* WGL_NV_DX_interop2 */ + +#ifndef WGL_NV_copy_image +#define WGL_NV_copy_image 1 +typedef BOOL (WINAPI * PFNWGLCOPYIMAGESUBDATANVPROC) (HGLRC hSrcRC, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, HGLRC hDstRC, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglCopyImageSubDataNV (HGLRC hSrcRC, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, HGLRC hDstRC, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#endif +#endif /* WGL_NV_copy_image */ + +#ifndef WGL_NV_delay_before_swap +#define WGL_NV_delay_before_swap 1 +typedef BOOL (WINAPI * PFNWGLDELAYBEFORESWAPNVPROC) (HDC hDC, GLfloat seconds); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglDelayBeforeSwapNV (HDC hDC, GLfloat seconds); +#endif +#endif /* WGL_NV_delay_before_swap */ + +#ifndef WGL_NV_float_buffer +#define WGL_NV_float_buffer 1 +#define WGL_FLOAT_COMPONENTS_NV 0x20B0 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_R_NV 0x20B1 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RG_NV 0x20B2 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGB_NV 0x20B3 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGBA_NV 0x20B4 +#define WGL_TEXTURE_FLOAT_R_NV 0x20B5 +#define WGL_TEXTURE_FLOAT_RG_NV 0x20B6 +#define WGL_TEXTURE_FLOAT_RGB_NV 0x20B7 +#define WGL_TEXTURE_FLOAT_RGBA_NV 0x20B8 +#endif /* WGL_NV_float_buffer */ + +#ifndef WGL_NV_gpu_affinity +#define WGL_NV_gpu_affinity 1 +DECLARE_HANDLE(HGPUNV); +struct _GPU_DEVICE { + DWORD cb; + CHAR DeviceName[32]; + CHAR DeviceString[128]; + DWORD Flags; + RECT rcVirtualScreen; +}; +typedef struct _GPU_DEVICE *PGPU_DEVICE; +#define ERROR_INCOMPATIBLE_AFFINITY_MASKS_NV 0x20D0 +#define ERROR_MISSING_AFFINITY_MASK_NV 0x20D1 +typedef BOOL (WINAPI * PFNWGLENUMGPUSNVPROC) (UINT iGpuIndex, HGPUNV *phGpu); +typedef BOOL (WINAPI * PFNWGLENUMGPUDEVICESNVPROC) (HGPUNV hGpu, UINT iDeviceIndex, PGPU_DEVICE lpGpuDevice); +typedef HDC (WINAPI * PFNWGLCREATEAFFINITYDCNVPROC) (const HGPUNV *phGpuList); +typedef BOOL (WINAPI * PFNWGLENUMGPUSFROMAFFINITYDCNVPROC) (HDC hAffinityDC, UINT iGpuIndex, HGPUNV *hGpu); +typedef BOOL (WINAPI * PFNWGLDELETEDCNVPROC) (HDC hdc); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglEnumGpusNV (UINT iGpuIndex, HGPUNV *phGpu); +BOOL WINAPI wglEnumGpuDevicesNV (HGPUNV hGpu, UINT iDeviceIndex, PGPU_DEVICE lpGpuDevice); +HDC WINAPI wglCreateAffinityDCNV (const HGPUNV *phGpuList); +BOOL WINAPI wglEnumGpusFromAffinityDCNV (HDC hAffinityDC, UINT iGpuIndex, HGPUNV *hGpu); +BOOL WINAPI wglDeleteDCNV (HDC hdc); +#endif +#endif /* WGL_NV_gpu_affinity */ + +#ifndef WGL_NV_multisample_coverage +#define WGL_NV_multisample_coverage 1 +#define WGL_COVERAGE_SAMPLES_NV 0x2042 +#define WGL_COLOR_SAMPLES_NV 0x20B9 +#endif /* WGL_NV_multisample_coverage */ + +#ifndef WGL_NV_present_video +#define WGL_NV_present_video 1 +DECLARE_HANDLE(HVIDEOOUTPUTDEVICENV); +#define WGL_NUM_VIDEO_SLOTS_NV 0x20F0 +typedef int (WINAPI * PFNWGLENUMERATEVIDEODEVICESNVPROC) (HDC hDC, HVIDEOOUTPUTDEVICENV *phDeviceList); +typedef BOOL (WINAPI * PFNWGLBINDVIDEODEVICENVPROC) (HDC hDC, unsigned int uVideoSlot, HVIDEOOUTPUTDEVICENV hVideoDevice, const int *piAttribList); +typedef BOOL (WINAPI * PFNWGLQUERYCURRENTCONTEXTNVPROC) (int iAttribute, int *piValue); +#ifdef WGL_WGLEXT_PROTOTYPES +int WINAPI wglEnumerateVideoDevicesNV (HDC hDC, HVIDEOOUTPUTDEVICENV *phDeviceList); +BOOL WINAPI wglBindVideoDeviceNV (HDC hDC, unsigned int uVideoSlot, HVIDEOOUTPUTDEVICENV hVideoDevice, const int *piAttribList); +BOOL WINAPI wglQueryCurrentContextNV (int iAttribute, int *piValue); +#endif +#endif /* WGL_NV_present_video */ + +#ifndef WGL_NV_render_depth_texture +#define WGL_NV_render_depth_texture 1 +#define WGL_BIND_TO_TEXTURE_DEPTH_NV 0x20A3 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_DEPTH_NV 0x20A4 +#define WGL_DEPTH_TEXTURE_FORMAT_NV 0x20A5 +#define WGL_TEXTURE_DEPTH_COMPONENT_NV 0x20A6 +#define WGL_DEPTH_COMPONENT_NV 0x20A7 +#endif /* WGL_NV_render_depth_texture */ + +#ifndef WGL_NV_render_texture_rectangle +#define WGL_NV_render_texture_rectangle 1 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGB_NV 0x20A0 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGBA_NV 0x20A1 +#define WGL_TEXTURE_RECTANGLE_NV 0x20A2 +#endif /* WGL_NV_render_texture_rectangle */ + +#ifndef WGL_NV_swap_group +#define WGL_NV_swap_group 1 +typedef BOOL (WINAPI * PFNWGLJOINSWAPGROUPNVPROC) (HDC hDC, GLuint group); +typedef BOOL (WINAPI * PFNWGLBINDSWAPBARRIERNVPROC) (GLuint group, GLuint barrier); +typedef BOOL (WINAPI * PFNWGLQUERYSWAPGROUPNVPROC) (HDC hDC, GLuint *group, GLuint *barrier); +typedef BOOL (WINAPI * PFNWGLQUERYMAXSWAPGROUPSNVPROC) (HDC hDC, GLuint *maxGroups, GLuint *maxBarriers); +typedef BOOL (WINAPI * PFNWGLQUERYFRAMECOUNTNVPROC) (HDC hDC, GLuint *count); +typedef BOOL (WINAPI * PFNWGLRESETFRAMECOUNTNVPROC) (HDC hDC); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglJoinSwapGroupNV (HDC hDC, GLuint group); +BOOL WINAPI wglBindSwapBarrierNV (GLuint group, GLuint barrier); +BOOL WINAPI wglQuerySwapGroupNV (HDC hDC, GLuint *group, GLuint *barrier); +BOOL WINAPI wglQueryMaxSwapGroupsNV (HDC hDC, GLuint *maxGroups, GLuint *maxBarriers); +BOOL WINAPI wglQueryFrameCountNV (HDC hDC, GLuint *count); +BOOL WINAPI wglResetFrameCountNV (HDC hDC); +#endif +#endif /* WGL_NV_swap_group */ + +#ifndef WGL_NV_vertex_array_range +#define WGL_NV_vertex_array_range 1 +typedef void *(WINAPI * PFNWGLALLOCATEMEMORYNVPROC) (GLsizei size, GLfloat readfreq, GLfloat writefreq, GLfloat priority); +typedef void (WINAPI * PFNWGLFREEMEMORYNVPROC) (void *pointer); +#ifdef WGL_WGLEXT_PROTOTYPES +void *WINAPI wglAllocateMemoryNV (GLsizei size, GLfloat readfreq, GLfloat writefreq, GLfloat priority); +void WINAPI wglFreeMemoryNV (void *pointer); +#endif +#endif /* WGL_NV_vertex_array_range */ + +#ifndef WGL_NV_video_capture +#define WGL_NV_video_capture 1 +DECLARE_HANDLE(HVIDEOINPUTDEVICENV); +#define WGL_UNIQUE_ID_NV 0x20CE +#define WGL_NUM_VIDEO_CAPTURE_SLOTS_NV 0x20CF +typedef BOOL (WINAPI * PFNWGLBINDVIDEOCAPTUREDEVICENVPROC) (UINT uVideoSlot, HVIDEOINPUTDEVICENV hDevice); +typedef UINT (WINAPI * PFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC) (HDC hDc, HVIDEOINPUTDEVICENV *phDeviceList); +typedef BOOL (WINAPI * PFNWGLLOCKVIDEOCAPTUREDEVICENVPROC) (HDC hDc, HVIDEOINPUTDEVICENV hDevice); +typedef BOOL (WINAPI * PFNWGLQUERYVIDEOCAPTUREDEVICENVPROC) (HDC hDc, HVIDEOINPUTDEVICENV hDevice, int iAttribute, int *piValue); +typedef BOOL (WINAPI * PFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC) (HDC hDc, HVIDEOINPUTDEVICENV hDevice); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglBindVideoCaptureDeviceNV (UINT uVideoSlot, HVIDEOINPUTDEVICENV hDevice); +UINT WINAPI wglEnumerateVideoCaptureDevicesNV (HDC hDc, HVIDEOINPUTDEVICENV *phDeviceList); +BOOL WINAPI wglLockVideoCaptureDeviceNV (HDC hDc, HVIDEOINPUTDEVICENV hDevice); +BOOL WINAPI wglQueryVideoCaptureDeviceNV (HDC hDc, HVIDEOINPUTDEVICENV hDevice, int iAttribute, int *piValue); +BOOL WINAPI wglReleaseVideoCaptureDeviceNV (HDC hDc, HVIDEOINPUTDEVICENV hDevice); +#endif +#endif /* WGL_NV_video_capture */ + +#ifndef WGL_NV_video_output +#define WGL_NV_video_output 1 +DECLARE_HANDLE(HPVIDEODEV); +#define WGL_BIND_TO_VIDEO_RGB_NV 0x20C0 +#define WGL_BIND_TO_VIDEO_RGBA_NV 0x20C1 +#define WGL_BIND_TO_VIDEO_RGB_AND_DEPTH_NV 0x20C2 +#define WGL_VIDEO_OUT_COLOR_NV 0x20C3 +#define WGL_VIDEO_OUT_ALPHA_NV 0x20C4 +#define WGL_VIDEO_OUT_DEPTH_NV 0x20C5 +#define WGL_VIDEO_OUT_COLOR_AND_ALPHA_NV 0x20C6 +#define WGL_VIDEO_OUT_COLOR_AND_DEPTH_NV 0x20C7 +#define WGL_VIDEO_OUT_FRAME 0x20C8 +#define WGL_VIDEO_OUT_FIELD_1 0x20C9 +#define WGL_VIDEO_OUT_FIELD_2 0x20CA +#define WGL_VIDEO_OUT_STACKED_FIELDS_1_2 0x20CB +#define WGL_VIDEO_OUT_STACKED_FIELDS_2_1 0x20CC +typedef BOOL (WINAPI * PFNWGLGETVIDEODEVICENVPROC) (HDC hDC, int numDevices, HPVIDEODEV *hVideoDevice); +typedef BOOL (WINAPI * PFNWGLRELEASEVIDEODEVICENVPROC) (HPVIDEODEV hVideoDevice); +typedef BOOL (WINAPI * PFNWGLBINDVIDEOIMAGENVPROC) (HPVIDEODEV hVideoDevice, HPBUFFERARB hPbuffer, int iVideoBuffer); +typedef BOOL (WINAPI * PFNWGLRELEASEVIDEOIMAGENVPROC) (HPBUFFERARB hPbuffer, int iVideoBuffer); +typedef BOOL (WINAPI * PFNWGLSENDPBUFFERTOVIDEONVPROC) (HPBUFFERARB hPbuffer, int iBufferType, unsigned long *pulCounterPbuffer, BOOL bBlock); +typedef BOOL (WINAPI * PFNWGLGETVIDEOINFONVPROC) (HPVIDEODEV hpVideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetVideoDeviceNV (HDC hDC, int numDevices, HPVIDEODEV *hVideoDevice); +BOOL WINAPI wglReleaseVideoDeviceNV (HPVIDEODEV hVideoDevice); +BOOL WINAPI wglBindVideoImageNV (HPVIDEODEV hVideoDevice, HPBUFFERARB hPbuffer, int iVideoBuffer); +BOOL WINAPI wglReleaseVideoImageNV (HPBUFFERARB hPbuffer, int iVideoBuffer); +BOOL WINAPI wglSendPbufferToVideoNV (HPBUFFERARB hPbuffer, int iBufferType, unsigned long *pulCounterPbuffer, BOOL bBlock); +BOOL WINAPI wglGetVideoInfoNV (HPVIDEODEV hpVideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo); +#endif +#endif /* WGL_NV_video_output */ + +#ifndef WGL_OML_sync_control +#define WGL_OML_sync_control 1 +typedef BOOL (WINAPI * PFNWGLGETSYNCVALUESOMLPROC) (HDC hdc, INT64 *ust, INT64 *msc, INT64 *sbc); +typedef BOOL (WINAPI * PFNWGLGETMSCRATEOMLPROC) (HDC hdc, INT32 *numerator, INT32 *denominator); +typedef INT64 (WINAPI * PFNWGLSWAPBUFFERSMSCOMLPROC) (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder); +typedef INT64 (WINAPI * PFNWGLSWAPLAYERBUFFERSMSCOMLPROC) (HDC hdc, int fuPlanes, INT64 target_msc, INT64 divisor, INT64 remainder); +typedef BOOL (WINAPI * PFNWGLWAITFORMSCOMLPROC) (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder, INT64 *ust, INT64 *msc, INT64 *sbc); +typedef BOOL (WINAPI * PFNWGLWAITFORSBCOMLPROC) (HDC hdc, INT64 target_sbc, INT64 *ust, INT64 *msc, INT64 *sbc); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetSyncValuesOML (HDC hdc, INT64 *ust, INT64 *msc, INT64 *sbc); +BOOL WINAPI wglGetMscRateOML (HDC hdc, INT32 *numerator, INT32 *denominator); +INT64 WINAPI wglSwapBuffersMscOML (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder); +INT64 WINAPI wglSwapLayerBuffersMscOML (HDC hdc, int fuPlanes, INT64 target_msc, INT64 divisor, INT64 remainder); +BOOL WINAPI wglWaitForMscOML (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder, INT64 *ust, INT64 *msc, INT64 *sbc); +BOOL WINAPI wglWaitForSbcOML (HDC hdc, INT64 target_sbc, INT64 *ust, INT64 *msc, INT64 *sbc); +#endif +#endif /* WGL_OML_sync_control */ + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/libs/tinf/tinflate.c b/src/libs/tinf/tinflate.c index b2bec565..8de5f73c 100644 --- a/src/libs/tinf/tinflate.c +++ b/src/libs/tinf/tinflate.c @@ -303,7 +303,7 @@ static int tinf_inflate_block_data(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt) /* check for end of block */ if (sym == 256) { - *d->destLen += d->dest - start; + *d->destLen += (unsigned int)(d->dest - start); return TINF_OK; } diff --git a/src/mesh.h b/src/mesh.h index cf6e17e6..519a7cea 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -348,8 +348,8 @@ struct MeshBuilder { Geometry &geom = range.geometry[transp]; - // rooms geometry - buildRoom(geom, range.dynamic[transp], blendMask, room, level, indices, vertices, iCount, vCount, vStartRoom); + // room geometry + buildRoom(geom, range.dynamic + transp, blendMask, room, level, indices, vertices, iCount, vCount, vStartRoom); // static meshes for (int j = 0; j < room.meshesCount; j++) { @@ -1004,11 +1004,13 @@ struct MeshBuilder { return 1 << texAttribute; } - void buildRoom(Geometry &geom, Dynamic &dyn, int blendMask, const TR::Room &room, TR::Level *level, Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart) { + void buildRoom(Geometry &geom, Dynamic *dyn, int blendMask, const TR::Room &room, TR::Level *level, Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart) { const TR::Room::Data &d = room.data; - dyn.count = 0; - dyn.faces = NULL; + if (dyn) { + dyn->count = 0; + dyn->faces = NULL; + } for (int j = 0; j < d.fCount; j++) { TR::Face &f = d.faces[j]; @@ -1023,9 +1025,9 @@ struct MeshBuilder { if (!(blendMask & getBlendMask(t.attribute))) continue; - if (t.animated) { - ASSERT(dyn.count < 0xFFFF); - dyn.count++; + if (dyn && t.animated) { + ASSERT(dyn->count < 0xFFFF); + dyn->count++; continue; } @@ -1036,9 +1038,9 @@ struct MeshBuilder { } // if room has non-static polygons, fill the list of dynamic faces - if (dyn.count) { - dyn.faces = new uint16[dyn.count]; - dyn.count = 0; + if (dyn && dyn->count) { + dyn->faces = new uint16[dyn->count]; + dyn->count = 0; for (int j = 0; j < d.fCount; j++) { TR::Face &f = d.faces[j]; TR::TextureInfo &t = level->objectTextures[f.flags.texture]; @@ -1049,7 +1051,7 @@ struct MeshBuilder { continue; if (t.animated) - dyn.faces[dyn.count++] = j; + dyn->faces[dyn->count++] = j; } } } @@ -1562,7 +1564,7 @@ struct MeshBuilder { part += models[modelIndex].parts[transparent][i]; continue; } - #ifdef FFP + #if defined(FFP) || defined(_GAPI_TA) Core::mModel.identity(); Core::mModel.setRot(basis.rot); Core::mModel.setPos(basis.pos); diff --git a/src/objects.h b/src/objects.h index 1af31c6b..ce631f1c 100644 --- a/src/objects.h +++ b/src/objects.h @@ -153,9 +153,9 @@ struct Flame : Sprite { Controller *owner; int32 jointIndex; - float sleep; + float sleepTime; - Flame(IGame *game, int entity) : Sprite(game, entity, false, Sprite::FRAME_ANIMATED), owner(NULL), jointIndex(0), sleep(0.0f) { + Flame(IGame *game, int entity) : Sprite(game, entity, false, Sprite::FRAME_ANIMATED), owner(NULL), jointIndex(0), sleepTime(0.0f) { time = randf() * 3.0f; activate(); } @@ -186,15 +186,15 @@ struct Flame : Sprite { lara->hit(FLAME_BURN_DAMAGE * Core::deltaTime, this); } else if (lara->health > 0.0f) { - if (sleep > 0.0f) - sleep = max(0.0f, sleep - Core::deltaTime); + if (sleepTime > 0.0f) + sleepTime = max(0.0f, sleepTime - Core::deltaTime); - if (sleep == 0.0f && !lara->burn && lara->collide(Sphere(pos, 600.0f))) { + if (sleepTime == 0.0f && !lara->burn && lara->collide(Sphere(pos, 600.0f))) { lara->hit(FLAME_HEAT_DAMAGE * Core::deltaTime, this); if (lara->collide(Sphere(pos, 300.0f))) { Flame::add(game, lara, 0); - sleep = 3.0f; // stay inactive for 3 seconds + sleepTime = 3.0f; // stay inactive for 3 seconds } } } @@ -865,12 +865,7 @@ struct Drawbridge : Controller { struct Crystal : Controller { Texture *environment; - Crystal(IGame *game, int entity) : Controller(game, entity) { - uint32 opt = OPT_CUBEMAP | OPT_TARGET; - #ifdef USE_CUBEMAP_MIPS - opt |= OPT_MIPMAPS; - #endif - environment = new Texture(CRYSTAL_CUBEMAP_SIZE, CRYSTAL_CUBEMAP_SIZE, 1, FMT_RGB16, opt); + Crystal(IGame *game, int entity) : Controller(game, entity), environment(NULL) { activate(); } @@ -895,9 +890,30 @@ struct Crystal : Controller { } virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { - Core::setMaterial(0.5, 0.5, 3.0, 1.0f); - environment->bind(sEnvironment); + Core::setMaterial(0.5, 0.5, 3.0, 0.0f); // 0.0f - use vertex normal as texcoord + GAPI::Texture *dtex = Core::active.textures[sDiffuse]; + if (environment) { + environment->bind(sDiffuse); + } else { + Core::whiteCube->bind(sDiffuse); + } Controller::render(frustum, mesh, type, caustics); + if (dtex) dtex->bind(sDiffuse); + } + + void bake() { + ASSERT(!environment); + + uint32 opt = OPT_CUBEMAP | OPT_TARGET; + #ifdef USE_CUBEMAP_MIPS + opt |= OPT_MIPMAPS; + #endif + + environment = new Texture(CRYSTAL_CUBEMAP_SIZE, CRYSTAL_CUBEMAP_SIZE, 1, FMT_RGB16, opt); + game->renderEnvironment(getRoomIndex(), pos - vec3(0, 512, 0), &environment); + if (opt & OPT_MIPMAPS) { + environment->generateMipMap(); + } } }; @@ -1822,4 +1838,4 @@ struct Bullet : Controller { } }; -#endif \ No newline at end of file +#endif diff --git a/src/platform/3do/CD/AppStartup b/src/platform/3do/CD/AppStartup new file mode 100644 index 00000000..883e48b2 --- /dev/null +++ b/src/platform/3do/CD/AppStartup @@ -0,0 +1,3 @@ +#minmem +# show CPU, DRAM, VRAM and DSP usage +#$c/sysload diff --git a/src/platform/3do/CD/System/Audio/aiff/sinewave.aiff b/src/platform/3do/CD/System/Audio/aiff/sinewave.aiff new file mode 100644 index 00000000..9c0c3142 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/aiff/sinewave.aiff differ diff --git a/src/platform/3do/CD/System/Audio/dsp/add.dsp b/src/platform/3do/CD/System/Audio/dsp/add.dsp new file mode 100644 index 00000000..0f9d9931 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/add.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/adpcmduck22s.dsp b/src/platform/3do/CD/System/Audio/dsp/adpcmduck22s.dsp new file mode 100644 index 00000000..a0bc0963 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/adpcmduck22s.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/adpcmhalfmono.dsp b/src/platform/3do/CD/System/Audio/dsp/adpcmhalfmono.dsp new file mode 100644 index 00000000..d9ce5cfb Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/adpcmhalfmono.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/adpcmmono.dsp b/src/platform/3do/CD/System/Audio/dsp/adpcmmono.dsp new file mode 100644 index 00000000..8f5ca55d Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/adpcmmono.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/adpcmvarmono.dsp b/src/platform/3do/CD/System/Audio/dsp/adpcmvarmono.dsp new file mode 100644 index 00000000..d51221d6 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/adpcmvarmono.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/benchmark.dsp b/src/platform/3do/CD/System/Audio/dsp/benchmark.dsp new file mode 100644 index 00000000..0dee342c Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/benchmark.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/dcsqxdhalfmono.dsp b/src/platform/3do/CD/System/Audio/dsp/dcsqxdhalfmono.dsp new file mode 100644 index 00000000..7321d32f Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/dcsqxdhalfmono.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/dcsqxdhalfstereo.dsp b/src/platform/3do/CD/System/Audio/dsp/dcsqxdhalfstereo.dsp new file mode 100644 index 00000000..4996231d Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/dcsqxdhalfstereo.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/dcsqxdmono.dsp b/src/platform/3do/CD/System/Audio/dsp/dcsqxdmono.dsp new file mode 100644 index 00000000..ea3eb8c3 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/dcsqxdmono.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/dcsqxdstereo.dsp b/src/platform/3do/CD/System/Audio/dsp/dcsqxdstereo.dsp new file mode 100644 index 00000000..35395e18 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/dcsqxdstereo.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/dcsqxdvarmono.dsp b/src/platform/3do/CD/System/Audio/dsp/dcsqxdvarmono.dsp new file mode 100644 index 00000000..7397e108 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/dcsqxdvarmono.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/decodeadpcm.dsp b/src/platform/3do/CD/System/Audio/dsp/decodeadpcm.dsp new file mode 100644 index 00000000..0f77f571 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/decodeadpcm.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/deemphcd.dsp b/src/platform/3do/CD/System/Audio/dsp/deemphcd.dsp new file mode 100644 index 00000000..a179e33e Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/deemphcd.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/delay1tap.dsp b/src/platform/3do/CD/System/Audio/dsp/delay1tap.dsp new file mode 100644 index 00000000..8f743204 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/delay1tap.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/delaymono.dsp b/src/platform/3do/CD/System/Audio/dsp/delaymono.dsp new file mode 100644 index 00000000..f42be17a Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/delaymono.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/delaystereo.dsp b/src/platform/3do/CD/System/Audio/dsp/delaystereo.dsp new file mode 100644 index 00000000..e72cdc19 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/delaystereo.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/directin.dsp b/src/platform/3do/CD/System/Audio/dsp/directin.dsp new file mode 100644 index 00000000..8d3ff761 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/directin.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/directout.dsp b/src/platform/3do/CD/System/Audio/dsp/directout.dsp new file mode 100644 index 00000000..62a1d758 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/directout.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/envelope.dsp b/src/platform/3do/CD/System/Audio/dsp/envelope.dsp new file mode 100644 index 00000000..4e47ed39 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/envelope.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/envfollower.dsp b/src/platform/3do/CD/System/Audio/dsp/envfollower.dsp new file mode 100644 index 00000000..a47cf3fa Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/envfollower.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/ezflix225.dsp b/src/platform/3do/CD/System/Audio/dsp/ezflix225.dsp new file mode 100644 index 00000000..ae5bd81b Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/ezflix225.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/filterednoise.dsp b/src/platform/3do/CD/System/Audio/dsp/filterednoise.dsp new file mode 100644 index 00000000..0c3c2b53 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/filterednoise.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/fixedmono8.dsp b/src/platform/3do/CD/System/Audio/dsp/fixedmono8.dsp new file mode 100644 index 00000000..d826d995 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/fixedmono8.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/fixedmonosample.dsp b/src/platform/3do/CD/System/Audio/dsp/fixedmonosample.dsp new file mode 100644 index 00000000..afd80250 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/fixedmonosample.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/fixedstereo16swap.dsp b/src/platform/3do/CD/System/Audio/dsp/fixedstereo16swap.dsp new file mode 100644 index 00000000..a4348d02 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/fixedstereo16swap.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/fixedstereo8.dsp b/src/platform/3do/CD/System/Audio/dsp/fixedstereo8.dsp new file mode 100644 index 00000000..e095d934 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/fixedstereo8.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/fixedstereosample.dsp b/src/platform/3do/CD/System/Audio/dsp/fixedstereosample.dsp new file mode 100644 index 00000000..ff1404c5 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/fixedstereosample.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/halfmono8.dsp b/src/platform/3do/CD/System/Audio/dsp/halfmono8.dsp new file mode 100644 index 00000000..06de9088 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/halfmono8.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/halfmonosample.dsp b/src/platform/3do/CD/System/Audio/dsp/halfmonosample.dsp new file mode 100644 index 00000000..6cc58e82 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/halfmonosample.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/halfstereo8.dsp b/src/platform/3do/CD/System/Audio/dsp/halfstereo8.dsp new file mode 100644 index 00000000..1e1b4672 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/halfstereo8.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/halfstereosample.dsp b/src/platform/3do/CD/System/Audio/dsp/halfstereosample.dsp new file mode 100644 index 00000000..93f69ae0 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/halfstereosample.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/head.dsp b/src/platform/3do/CD/System/Audio/dsp/head.dsp new file mode 100644 index 00000000..a2665d08 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/head.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/impulse.dsp b/src/platform/3do/CD/System/Audio/dsp/impulse.dsp new file mode 100644 index 00000000..aeb6362b Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/impulse.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/maximum.dsp b/src/platform/3do/CD/System/Audio/dsp/maximum.dsp new file mode 100644 index 00000000..61b2ae32 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/maximum.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/minimum.dsp b/src/platform/3do/CD/System/Audio/dsp/minimum.dsp new file mode 100644 index 00000000..78563a54 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/minimum.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/mixer12x2.dsp b/src/platform/3do/CD/System/Audio/dsp/mixer12x2.dsp new file mode 100644 index 00000000..23d8b6f8 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/mixer12x2.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/mixer2x2.dsp b/src/platform/3do/CD/System/Audio/dsp/mixer2x2.dsp new file mode 100644 index 00000000..40831915 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/mixer2x2.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/mixer4x2.dsp b/src/platform/3do/CD/System/Audio/dsp/mixer4x2.dsp new file mode 100644 index 00000000..2e561690 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/mixer4x2.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/mixer8x2.dsp b/src/platform/3do/CD/System/Audio/dsp/mixer8x2.dsp new file mode 100644 index 00000000..2de70167 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/mixer8x2.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/mixer8x2amp.dsp b/src/platform/3do/CD/System/Audio/dsp/mixer8x2amp.dsp new file mode 100644 index 00000000..eeb3f71f Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/mixer8x2amp.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/monitor.dsp b/src/platform/3do/CD/System/Audio/dsp/monitor.dsp new file mode 100644 index 00000000..60802e5f Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/monitor.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/multiply.dsp b/src/platform/3do/CD/System/Audio/dsp/multiply.dsp new file mode 100644 index 00000000..38ddcd69 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/multiply.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/noise.dsp b/src/platform/3do/CD/System/Audio/dsp/noise.dsp new file mode 100644 index 00000000..1414a933 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/noise.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/oscupdownfp.dsp b/src/platform/3do/CD/System/Audio/dsp/oscupdownfp.dsp new file mode 100644 index 00000000..1429eaf1 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/oscupdownfp.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/probe.dsp b/src/platform/3do/CD/System/Audio/dsp/probe.dsp new file mode 100644 index 00000000..67ff100b Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/probe.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/pulse.dsp b/src/platform/3do/CD/System/Audio/dsp/pulse.dsp new file mode 100644 index 00000000..cabf3983 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/pulse.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/pulse_lfo.dsp b/src/platform/3do/CD/System/Audio/dsp/pulse_lfo.dsp new file mode 100644 index 00000000..9a950cd9 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/pulse_lfo.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/pulser.dsp b/src/platform/3do/CD/System/Audio/dsp/pulser.dsp new file mode 100644 index 00000000..1b427677 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/pulser.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/randomhold.dsp b/src/platform/3do/CD/System/Audio/dsp/randomhold.dsp new file mode 100644 index 00000000..8d8c3f14 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/randomhold.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/rednoise.dsp b/src/platform/3do/CD/System/Audio/dsp/rednoise.dsp new file mode 100644 index 00000000..daeced19 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/rednoise.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/romhead.dsp b/src/platform/3do/CD/System/Audio/dsp/romhead.dsp new file mode 100644 index 00000000..8b711413 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/romhead.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/romtail.dsp b/src/platform/3do/CD/System/Audio/dsp/romtail.dsp new file mode 100644 index 00000000..91c7026d Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/romtail.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/sampler.dsp b/src/platform/3do/CD/System/Audio/dsp/sampler.dsp new file mode 100644 index 00000000..1a23090f Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/sampler.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/sampler3d.dsp b/src/platform/3do/CD/System/Audio/dsp/sampler3d.dsp new file mode 100644 index 00000000..3e3e6d81 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/sampler3d.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/samplerenv.dsp b/src/platform/3do/CD/System/Audio/dsp/samplerenv.dsp new file mode 100644 index 00000000..4a718488 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/samplerenv.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/samplermod.dsp b/src/platform/3do/CD/System/Audio/dsp/samplermod.dsp new file mode 100644 index 00000000..6833a4b7 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/samplermod.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/sawenv.dsp b/src/platform/3do/CD/System/Audio/dsp/sawenv.dsp new file mode 100644 index 00000000..49d06288 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/sawenv.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/sawenvsvfenv.dsp b/src/platform/3do/CD/System/Audio/dsp/sawenvsvfenv.dsp new file mode 100644 index 00000000..c7362185 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/sawenvsvfenv.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/sawfilterednoise.dsp b/src/platform/3do/CD/System/Audio/dsp/sawfilterednoise.dsp new file mode 100644 index 00000000..2f3ece32 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/sawfilterednoise.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/sawfilteredsaw.dsp b/src/platform/3do/CD/System/Audio/dsp/sawfilteredsaw.dsp new file mode 100644 index 00000000..9f59903c Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/sawfilteredsaw.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/sawtooth.dsp b/src/platform/3do/CD/System/Audio/dsp/sawtooth.dsp new file mode 100644 index 00000000..855d0206 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/sawtooth.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/splitexec.dsp b/src/platform/3do/CD/System/Audio/dsp/splitexec.dsp new file mode 100644 index 00000000..cb8ade67 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/splitexec.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/square.dsp b/src/platform/3do/CD/System/Audio/dsp/square.dsp new file mode 100644 index 00000000..b1c30c9d Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/square.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/square_lfo.dsp b/src/platform/3do/CD/System/Audio/dsp/square_lfo.dsp new file mode 100644 index 00000000..3f14cef5 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/square_lfo.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/submixer2x2.dsp b/src/platform/3do/CD/System/Audio/dsp/submixer2x2.dsp new file mode 100644 index 00000000..69ce6d43 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/submixer2x2.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/submixer4x2.dsp b/src/platform/3do/CD/System/Audio/dsp/submixer4x2.dsp new file mode 100644 index 00000000..1f52d5d6 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/submixer4x2.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/submixer8x2.dsp b/src/platform/3do/CD/System/Audio/dsp/submixer8x2.dsp new file mode 100644 index 00000000..7f56dac6 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/submixer8x2.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/subtract.dsp b/src/platform/3do/CD/System/Audio/dsp/subtract.dsp new file mode 100644 index 00000000..d82d5170 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/subtract.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/svfilter.dsp b/src/platform/3do/CD/System/Audio/dsp/svfilter.dsp new file mode 100644 index 00000000..87a2fbaa Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/svfilter.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/tail.dsp b/src/platform/3do/CD/System/Audio/dsp/tail.dsp new file mode 100644 index 00000000..e20f2c95 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/tail.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/tapoutput.dsp b/src/platform/3do/CD/System/Audio/dsp/tapoutput.dsp new file mode 100644 index 00000000..e23b4b42 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/tapoutput.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/timesplus.dsp b/src/platform/3do/CD/System/Audio/dsp/timesplus.dsp new file mode 100644 index 00000000..d9a16c40 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/timesplus.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/triangle.dsp b/src/platform/3do/CD/System/Audio/dsp/triangle.dsp new file mode 100644 index 00000000..a747848f Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/triangle.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/triangle_lfo.dsp b/src/platform/3do/CD/System/Audio/dsp/triangle_lfo.dsp new file mode 100644 index 00000000..6245ff72 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/triangle_lfo.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/varmono16.dsp b/src/platform/3do/CD/System/Audio/dsp/varmono16.dsp new file mode 100644 index 00000000..52acda78 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/varmono16.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/varmono8.dsp b/src/platform/3do/CD/System/Audio/dsp/varmono8.dsp new file mode 100644 index 00000000..ee572d65 Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/varmono8.dsp differ diff --git a/src/platform/3do/CD/System/Audio/dsp/varmono8_s.dsp b/src/platform/3do/CD/System/Audio/dsp/varmono8_s.dsp new file mode 100644 index 00000000..7e33f58f Binary files /dev/null and b/src/platform/3do/CD/System/Audio/dsp/varmono8_s.dsp differ diff --git a/src/platform/3do/CD/System/Daemons/junk b/src/platform/3do/CD/System/Daemons/junk new file mode 100644 index 00000000..67c32976 --- /dev/null +++ b/src/platform/3do/CD/System/Daemons/junk @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/platform/3do/CD/System/Devices/FMV/CL45016Bit.UCode b/src/platform/3do/CD/System/Devices/FMV/CL45016Bit.UCode new file mode 100644 index 00000000..c28c0e92 Binary files /dev/null and b/src/platform/3do/CD/System/Devices/FMV/CL45016Bit.UCode differ diff --git a/src/platform/3do/CD/System/Devices/FMV/CL45024Bit.UCode b/src/platform/3do/CD/System/Devices/FMV/CL45024Bit.UCode new file mode 100644 index 00000000..00607067 Binary files /dev/null and b/src/platform/3do/CD/System/Devices/FMV/CL45024Bit.UCode differ diff --git a/src/platform/3do/CD/System/Devices/FMV/CL450Boot.UCode b/src/platform/3do/CD/System/Devices/FMV/CL450Boot.UCode new file mode 100644 index 00000000..d437289d Binary files /dev/null and b/src/platform/3do/CD/System/Devices/FMV/CL450Boot.UCode differ diff --git a/src/platform/3do/CD/System/Devices/FMVVIDEODEVICE.PRIVDEVICE b/src/platform/3do/CD/System/Devices/FMVVIDEODEVICE.PRIVDEVICE new file mode 100644 index 00000000..44758444 Binary files /dev/null and b/src/platform/3do/CD/System/Devices/FMVVIDEODEVICE.PRIVDEVICE differ diff --git a/src/platform/3do/CD/System/Devices/ns.privdevice b/src/platform/3do/CD/System/Devices/ns.privdevice new file mode 100644 index 00000000..192d3839 Binary files /dev/null and b/src/platform/3do/CD/System/Devices/ns.privdevice differ diff --git a/src/platform/3do/CD/System/Drivers/cport1.rom b/src/platform/3do/CD/System/Drivers/cport1.rom new file mode 100644 index 00000000..d06af3f2 Binary files /dev/null and b/src/platform/3do/CD/System/Drivers/cport1.rom differ diff --git a/src/platform/3do/CD/System/Drivers/cport41.rom b/src/platform/3do/CD/System/Drivers/cport41.rom new file mode 100644 index 00000000..44a5ce9a Binary files /dev/null and b/src/platform/3do/CD/System/Drivers/cport41.rom differ diff --git a/src/platform/3do/CD/System/Drivers/cport49.rom b/src/platform/3do/CD/System/Drivers/cport49.rom new file mode 100644 index 00000000..e09ca1b8 Binary files /dev/null and b/src/platform/3do/CD/System/Drivers/cport49.rom differ diff --git a/src/platform/3do/CD/System/Drivers/cport4d.rom b/src/platform/3do/CD/System/Drivers/cport4d.rom new file mode 100644 index 00000000..b1d49270 Binary files /dev/null and b/src/platform/3do/CD/System/Drivers/cport4d.rom differ diff --git a/src/platform/3do/CD/System/Drivers/languages/da.language b/src/platform/3do/CD/System/Drivers/languages/da.language new file mode 100644 index 00000000..b5e1012d Binary files /dev/null and b/src/platform/3do/CD/System/Drivers/languages/da.language differ diff --git a/src/platform/3do/CD/System/Drivers/languages/de.language b/src/platform/3do/CD/System/Drivers/languages/de.language new file mode 100644 index 00000000..da1c3109 Binary files /dev/null and b/src/platform/3do/CD/System/Drivers/languages/de.language differ diff --git a/src/platform/3do/CD/System/Drivers/languages/es.language b/src/platform/3do/CD/System/Drivers/languages/es.language new file mode 100644 index 00000000..a5b78fb0 Binary files /dev/null and b/src/platform/3do/CD/System/Drivers/languages/es.language differ diff --git a/src/platform/3do/CD/System/Drivers/languages/fr.language b/src/platform/3do/CD/System/Drivers/languages/fr.language new file mode 100644 index 00000000..8fd332c2 Binary files /dev/null and b/src/platform/3do/CD/System/Drivers/languages/fr.language differ diff --git a/src/platform/3do/CD/System/Drivers/languages/it.language b/src/platform/3do/CD/System/Drivers/languages/it.language new file mode 100644 index 00000000..df36d39f Binary files /dev/null and b/src/platform/3do/CD/System/Drivers/languages/it.language differ diff --git a/src/platform/3do/CD/System/Drivers/languages/ja.language b/src/platform/3do/CD/System/Drivers/languages/ja.language new file mode 100644 index 00000000..fb86b932 Binary files /dev/null and b/src/platform/3do/CD/System/Drivers/languages/ja.language differ diff --git a/src/platform/3do/CD/System/Drivers/languages/nl.language b/src/platform/3do/CD/System/Drivers/languages/nl.language new file mode 100644 index 00000000..4aae529c Binary files /dev/null and b/src/platform/3do/CD/System/Drivers/languages/nl.language differ diff --git a/src/platform/3do/CD/System/Drivers/languages/pt.language b/src/platform/3do/CD/System/Drivers/languages/pt.language new file mode 100644 index 00000000..aa3eb5ea Binary files /dev/null and b/src/platform/3do/CD/System/Drivers/languages/pt.language differ diff --git a/src/platform/3do/CD/System/Drivers/tuners/access.tuner b/src/platform/3do/CD/System/Drivers/tuners/access.tuner new file mode 100644 index 00000000..d6743c4b Binary files /dev/null and b/src/platform/3do/CD/System/Drivers/tuners/access.tuner differ diff --git a/src/platform/3do/CD/System/Folios/audio.privfolio b/src/platform/3do/CD/System/Folios/audio.privfolio new file mode 100644 index 00000000..142f397c Binary files /dev/null and b/src/platform/3do/CD/System/Folios/audio.privfolio differ diff --git a/src/platform/3do/CD/System/Folios/compression.folio b/src/platform/3do/CD/System/Folios/compression.folio new file mode 100644 index 00000000..cb3e0662 Binary files /dev/null and b/src/platform/3do/CD/System/Folios/compression.folio differ diff --git a/src/platform/3do/CD/System/Folios/debugger.privfolio b/src/platform/3do/CD/System/Folios/debugger.privfolio new file mode 100644 index 00000000..5850df16 Binary files /dev/null and b/src/platform/3do/CD/System/Folios/debugger.privfolio differ diff --git a/src/platform/3do/CD/System/Folios/graphics.privfolio b/src/platform/3do/CD/System/Folios/graphics.privfolio new file mode 100644 index 00000000..4db0f5d9 Binary files /dev/null and b/src/platform/3do/CD/System/Folios/graphics.privfolio differ diff --git a/src/platform/3do/CD/System/Folios/international.privfolio b/src/platform/3do/CD/System/Folios/international.privfolio new file mode 100644 index 00000000..195792f2 Binary files /dev/null and b/src/platform/3do/CD/System/Folios/international.privfolio differ diff --git a/src/platform/3do/CD/System/Folios/international/CountryDatabase b/src/platform/3do/CD/System/Folios/international/CountryDatabase new file mode 100644 index 00000000..bfe9d6b1 Binary files /dev/null and b/src/platform/3do/CD/System/Folios/international/CountryDatabase differ diff --git a/src/platform/3do/CD/System/Folios/jstring.folio b/src/platform/3do/CD/System/Folios/jstring.folio new file mode 100644 index 00000000..6c4a5a37 Binary files /dev/null and b/src/platform/3do/CD/System/Folios/jstring.folio differ diff --git a/src/platform/3do/CD/System/Folios/operamath.privfolio b/src/platform/3do/CD/System/Folios/operamath.privfolio new file mode 100644 index 00000000..0a8cacca Binary files /dev/null and b/src/platform/3do/CD/System/Folios/operamath.privfolio differ diff --git a/src/platform/3do/CD/System/Graphics/Fonts/Kanji16.4 b/src/platform/3do/CD/System/Graphics/Fonts/Kanji16.4 new file mode 100644 index 00000000..b91a5314 Binary files /dev/null and b/src/platform/3do/CD/System/Graphics/Fonts/Kanji16.4 differ diff --git a/src/platform/3do/CD/System/Kernel/boot_code b/src/platform/3do/CD/System/Kernel/boot_code new file mode 100644 index 00000000..1983f9f1 Binary files /dev/null and b/src/platform/3do/CD/System/Kernel/boot_code differ diff --git a/src/platform/3do/CD/System/Kernel/misc_code b/src/platform/3do/CD/System/Kernel/misc_code new file mode 100644 index 00000000..ca791f8c Binary files /dev/null and b/src/platform/3do/CD/System/Kernel/misc_code differ diff --git a/src/platform/3do/CD/System/Kernel/os_code b/src/platform/3do/CD/System/Kernel/os_code new file mode 100644 index 00000000..d81ff658 Binary files /dev/null and b/src/platform/3do/CD/System/Kernel/os_code differ diff --git a/src/platform/3do/CD/System/Programs/copy b/src/platform/3do/CD/System/Programs/copy new file mode 100644 index 00000000..3c2a6c9f Binary files /dev/null and b/src/platform/3do/CD/System/Programs/copy differ diff --git a/src/platform/3do/CD/System/Programs/delete b/src/platform/3do/CD/System/Programs/delete new file mode 100644 index 00000000..a31efd75 Binary files /dev/null and b/src/platform/3do/CD/System/Programs/delete differ diff --git a/src/platform/3do/CD/System/Programs/dismount b/src/platform/3do/CD/System/Programs/dismount new file mode 100644 index 00000000..bcdc45d1 Binary files /dev/null and b/src/platform/3do/CD/System/Programs/dismount differ diff --git a/src/platform/3do/CD/System/Programs/eeprom b/src/platform/3do/CD/System/Programs/eeprom new file mode 100644 index 00000000..a4e4bd79 Binary files /dev/null and b/src/platform/3do/CD/System/Programs/eeprom differ diff --git a/src/platform/3do/CD/System/Programs/format b/src/platform/3do/CD/System/Programs/format new file mode 100644 index 00000000..c2a1478e Binary files /dev/null and b/src/platform/3do/CD/System/Programs/format differ diff --git a/src/platform/3do/CD/System/Programs/fscheck b/src/platform/3do/CD/System/Programs/fscheck new file mode 100644 index 00000000..4724e7ce Binary files /dev/null and b/src/platform/3do/CD/System/Programs/fscheck differ diff --git a/src/platform/3do/CD/System/Programs/gdbug b/src/platform/3do/CD/System/Programs/gdbug new file mode 100644 index 00000000..88aee4a3 Binary files /dev/null and b/src/platform/3do/CD/System/Programs/gdbug differ diff --git a/src/platform/3do/CD/System/Programs/lmadm b/src/platform/3do/CD/System/Programs/lmadm new file mode 100644 index 00000000..29f058da Binary files /dev/null and b/src/platform/3do/CD/System/Programs/lmadm differ diff --git a/src/platform/3do/CD/System/Programs/lmdump b/src/platform/3do/CD/System/Programs/lmdump new file mode 100644 index 00000000..b1598dd0 Binary files /dev/null and b/src/platform/3do/CD/System/Programs/lmdump differ diff --git a/src/platform/3do/CD/System/Programs/ls b/src/platform/3do/CD/System/Programs/ls new file mode 100644 index 00000000..12ab0f9e Binary files /dev/null and b/src/platform/3do/CD/System/Programs/ls differ diff --git a/src/platform/3do/CD/System/Programs/mount b/src/platform/3do/CD/System/Programs/mount new file mode 100644 index 00000000..99997381 Binary files /dev/null and b/src/platform/3do/CD/System/Programs/mount differ diff --git a/src/platform/3do/CD/System/Programs/sysload b/src/platform/3do/CD/System/Programs/sysload new file mode 100644 index 00000000..3e705bf2 Binary files /dev/null and b/src/platform/3do/CD/System/Programs/sysload differ diff --git a/src/platform/3do/CD/System/Programs/type b/src/platform/3do/CD/System/Programs/type new file mode 100644 index 00000000..fd8ce90e Binary files /dev/null and b/src/platform/3do/CD/System/Programs/type differ diff --git a/src/platform/3do/CD/System/Programs/walker b/src/platform/3do/CD/System/Programs/walker new file mode 100644 index 00000000..00aa3695 Binary files /dev/null and b/src/platform/3do/CD/System/Programs/walker differ diff --git a/src/platform/3do/CD/System/Programs/what b/src/platform/3do/CD/System/Programs/what new file mode 100644 index 00000000..dce9765b Binary files /dev/null and b/src/platform/3do/CD/System/Programs/what differ diff --git a/src/platform/3do/CD/System/Scripts/startopera b/src/platform/3do/CD/System/Scripts/startopera new file mode 100644 index 00000000..39dd645b --- /dev/null +++ b/src/platform/3do/CD/System/Scripts/startopera @@ -0,0 +1 @@ +# Copyright (C) 1995, an unpublished work by The 3DO Company. All rights reserved. # This material contains confidential information that is the property of The 3DO Company. # Any unauthorized duplication, disclosure or use is prohibited. # $Id: startopera,v 1.27 1994/08/06 00:20:23 peabody Exp $ killkprintf alias aiff $audio/aiff alias dsp $audio/dsp alias languages $drivers/Languages alias tuners $drivers/Tuners alias programs $boot/System/Programs alias fonts {/rom2/System/Graphics/Fonts|$boot/System/Graphics/Fonts} alias c $programs alias s $scripts alias app $boot fg $c/fscheck bg $tasks/eventbroker@ #minmem $boot/AppStartup% \ No newline at end of file diff --git a/src/platform/3do/CD/System/Tasks/eventbroker b/src/platform/3do/CD/System/Tasks/eventbroker new file mode 100644 index 00000000..f07391fc Binary files /dev/null and b/src/platform/3do/CD/System/Tasks/eventbroker differ diff --git a/src/platform/3do/CD/System/Tasks/shell b/src/platform/3do/CD/System/Tasks/shell new file mode 100644 index 00000000..aa860e7e Binary files /dev/null and b/src/platform/3do/CD/System/Tasks/shell differ diff --git a/src/platform/3do/CD/rom_tags b/src/platform/3do/CD/rom_tags new file mode 100644 index 00000000..527822e4 Binary files /dev/null and b/src/platform/3do/CD/rom_tags differ diff --git a/src/platform/3do/CD/signatures b/src/platform/3do/CD/signatures new file mode 100644 index 00000000..a8e3b6da --- /dev/null +++ b/src/platform/3do/CD/signatures @@ -0,0 +1 @@ +UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU \ No newline at end of file diff --git a/src/platform/3do/Makefile b/src/platform/3do/Makefile new file mode 100644 index 00000000..88838747 --- /dev/null +++ b/src/platform/3do/Makefile @@ -0,0 +1,84 @@ +SDK = C:/Projects/3do-devkit +FILESYSTEM = CD +EXENAME = $(FILESYSTEM)/LaunchMe +ISONAME = OpenLara.iso +STACKSIZE = 4096 +BANNER = banner.bmp + +CC = armcc +CXX = armcpp +AS = armasm +LD = armlink +RM = rm +MODBIN = modbin +MAKEBANNER = MakeBanner +3DOISO = 3doiso +3DOENCRYPT = 3DOEncrypt + +OPT = -O2 +CFLAGS = $(OPT) -bi -za1 -zas1 -wn -ff -fa -d __3DO__=1 -cpu ARM6 +CXXFLAGS = $(CFLAGS) +ASFLAGS = -BI -i $(SDK)/include/3do +INCPATH = -I $(SDK)/include/3do -I $(SDK)/include/ttl -I ../../fixed +LIBPATH = $(SDK)/lib/3do +LDFLAGS = -aif -reloc -ro-base 0 -libpath $(LIBPATH) -nodebug -remove -info Sizes +STARTUP = $(LIBPATH)/cstartup.o + +LIBS = \ + $(LIBPATH)/clib.lib \ + $(LIBPATH)/cpluslib.lib \ + $(LIBPATH)/swi.lib \ + $(LIBPATH)/lib3do.lib \ + $(LIBPATH)/operamath.lib \ + $(LIBPATH)/audio.lib \ + $(LIBPATH)/music.lib \ + $(LIBPATH)/filesystem.lib \ + $(LIBPATH)/graphics.lib \ + $(LIBPATH)/input.lib \ + +SRC_S = $(wildcard *.s) +SRC_C = $(wildcard *.c) +SRC_CXX = $(wildcard *.cpp) + +OBJ += $(SRC_S:%.s=build/%.s.o) +OBJ += $(SRC_C:%.c=build/%.c.o) +OBJ += $(SRC_CXX:%.cpp=build/%.cpp.o) +OBJ += build/common.cpp.o + +all: clean launchme modbin banner iso run + +launchme: builddir $(OBJ) + $(LD) -dupok -o $(EXENAME) $(LDFLAGS) $(STARTUP) $(LIBS) $(OBJ) + +modbin: + $(MODBIN) --stack=$(STACKSIZE) $(EXENAME) $(EXENAME) + +banner: + $(MAKEBANNER) $(BANNER) $(FILESYSTEM)/BannerScreen + +iso: + $(3DOISO) -in $(FILESYSTEM) -out $(ISONAME) + $(3DOENCRYPT) genromtags $(ISONAME) + +run: + /c/RetroArch/retroarch.exe -L C:\RetroArch\cores\opera_libretro.dll C:\Projects\OpenLara\src\platform\3do\OpenLara.iso + +builddir: + mkdir -p build/ + +build/%.s.o: %.s + $(AS) $(INCPATH) $(ASFLAGS) $< -o $@ + +build/%.c.o: %.c + $(CC) $(INCPATH) $(CFLAGS) -c $< -o $@ + +build/%.cpp.o: %.cpp + $(CXX) $(INCPATH) $(CXXFLAGS) -c $< -o $@ + +build/common.cpp.o: ../../fixed/common.cpp + $(CXX) $(INCPATH) $(CXXFLAGS) -c ../../fixed/common.cpp -o build/common.cpp.o + +clean: + $(RM) -vf $(OBJ) $(EXENAME) $(EXENAME).sym $(ISONAME) + +.PHONY: clean modbin banner iso diff --git a/src/platform/3do/banner.bmp b/src/platform/3do/banner.bmp new file mode 100644 index 00000000..fbebb34b Binary files /dev/null and b/src/platform/3do/banner.bmp differ diff --git a/src/platform/3do/boxIsVisible.s b/src/platform/3do/boxIsVisible.s new file mode 100644 index 00000000..cfd9eaf7 --- /dev/null +++ b/src/platform/3do/boxIsVisible.s @@ -0,0 +1,205 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT boxIsVisible_asm + +mx RN r0 +my RN r1 +mz RN r2 +m RN r3 +vx RN r4 +vy RN r5 +vz RN r6 +x RN r7 +y RN r8 +z RN r9 +rMinX RN r10 +rMinY RN r11 +rMaxX RN r12 +rMaxY RN lr + +boxArg RN mx +divLUT RN mz + +bz RN divLUT +offset RN m +xx RN rMinX +yy RN rMinY +zz RN rMaxX +min RN rMaxY +max RN rMaxY +vMinXY RN x +vMaxXY RN y +vp RN x + +minX RN x +minY RN y +minZ RN z +maxX RN mx +maxY RN my +maxZ RN mz + +MAX_X EQU (0 * 3 * 4) +MIN_X EQU (1 * 3 * 4) +MAX_Y EQU (2 * 3 * 4) +MIN_Y EQU (3 * 3 * 4) +MAX_Z EQU (4 * 3 * 4) +MIN_Z EQU (5 * 3 * 4) +SIZE EQU (6 * 3 * 4) + + MACRO +$index project $dx, $dy, $dz + add offset, sp, $dz + ldmia offset, {x, y, z} + + add offset, sp, $dy + ldmia offset, {vx, vy, vz} + add x, x, vx + add y, y, vy + add z, z, vz + + add offset, sp, $dx + ldmia offset, {vx, vy, vz} + add z, z, vz + + ; check z clipping + sub offset, z, #VIEW_MIN_F + cmp offset, #(VIEW_MAX_F - VIEW_MIN_F) + bhi $index.skip + + add x, x, vx + add y, y, vy + + mov z, z, lsr #(FIXED_SHIFT + PROJ_SHIFT) ; z is positive + ldr z, [divLUT, z, lsl #2] + mul x, z, x + mul y, z, y + + cmp x, rMinX + movlt rMinX, x + cmp y, rMinY + movlt rMinY, y + cmp x, rMaxX + movgt rMaxX, x + cmp y, rMaxY + movgt rMaxY, y +$index.skip + MEND + +boxIsVisible_asm + ldr m, =gMatrixPtr + ldr m, [m] + ldr bz, [m, #(11 * 4)] + add bz, bz, #VIEW_OFF_F + cmp bz, #(VIEW_OFF_F + VIEW_MAX_F) + movhi r0, #0 + movhi pc, lr + + stmfd sp!, {r4-r11, lr} + + ldmia boxArg, {xx, yy, zz} + + add m, m, #(12 * 4) + + ; pre-transform min/max Z + ldmdb m!, {mx, my, mz, vx, vy, vz} + mov min, zz, asr #16 + mla minX, min, mx, vx + mla minY, min, my, vy + mla minZ, min, mz, vz + mov minX, minX, asr #FIXED_SHIFT + mov minY, minY, asr #FIXED_SHIFT + + mov max, zz, lsl #16 + mov max, max, asr #16 + mla maxX, max, mx, vx + mla maxY, max, my, vy + mla maxZ, max, mz, vz + mov maxX, maxX, asr #FIXED_SHIFT + mov maxY, maxY, asr #FIXED_SHIFT + stmdb sp!, {maxX, maxY, maxZ, minX, minY, minZ} + + ; pre-transform min/max Y + ldmdb m!, {mx, my, mz} + + mov min, yy, asr #16 + mul minX, mx, min + mul minY, my, min + mul minZ, mz, min + mov minX, minX, asr #FIXED_SHIFT + mov minY, minY, asr #FIXED_SHIFT + + mov max, yy, lsl #16 + mov max, max, asr #16 + mul maxX, max, mx + mul maxY, max, my + mul maxZ, max, mz + mov maxX, maxX, asr #FIXED_SHIFT + mov maxY, maxY, asr #FIXED_SHIFT + stmdb sp!, {maxX, maxY, maxZ, minX, minY, minZ} + + ; pre-transform min/max X + ldmdb m!, {mx, my, mz} + + mov min, xx, asr #16 + mul minX, mx, min + mul minY, my, min + mul minZ, mz, min + mov minX, minX, asr #FIXED_SHIFT + mov minY, minY, asr #FIXED_SHIFT + + mov max, xx, lsl #16 + mov max, max, asr #16 + mul maxX, max, mx + mul maxY, max, my + mul maxZ, max, mz + mov maxX, maxX, asr #FIXED_SHIFT + mov maxY, maxY, asr #FIXED_SHIFT + stmdb sp!, {maxX, maxY, maxZ, minX, minY, minZ} + + ldr divLUT, =divTable + mov rMinX, #MAX_INT32 + mov rMinY, #MAX_INT32 + mov rMaxX, #MIN_INT32 + mov rMaxY, #MIN_INT32 + +_0 project #MIN_X, #MIN_Y, #MIN_Z +_1 project #MAX_X, #MIN_Y, #MIN_Z +_2 project #MIN_X, #MAX_Y, #MIN_Z +_3 project #MAX_X, #MAX_Y, #MIN_Z +_4 project #MIN_X, #MIN_Y, #MAX_Z +_5 project #MAX_X, #MIN_Y, #MAX_Z +_6 project #MIN_X, #MAX_Y, #MAX_Z +_7 project #MAX_X, #MAX_Y, #MAX_Z + + mov r0, #0 + + mov rMinX, rMinX, asr #(16 - PROJ_SHIFT) + mov rMaxX, rMaxX, asr #(16 - PROJ_SHIFT) + + cmp rMinX, rMaxX + beq _done + + ; rect Y must remain shifted up by 16 + mov rMinY, rMinY, lsl #PROJ_SHIFT + mov rMaxY, rMaxY, lsl #PROJ_SHIFT + + ; check xy clipping + ldr vp, =viewportRel + ldmia vp, {vMinXY, vMaxXY} + + cmp rMaxX, vMinXY, asr #16 + blt _done + cmp rMaxY, vMinXY, lsl #16 + blt _done + cmp rMinX, vMaxXY, asr #16 + bgt _done + cmp rMinY, vMaxXY, lsl #16 + bgt _done + + mov r0, #1 +_done add sp, sp, #SIZE + ldmfd sp!, {r4-r11, pc} + END diff --git a/src/platform/3do/boxRotateYQ.s b/src/platform/3do/boxRotateYQ.s new file mode 100644 index 00000000..1d83b65a --- /dev/null +++ b/src/platform/3do/boxRotateYQ.s @@ -0,0 +1,58 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT boxRotateYQ_asm + +vx RN r0 +q RN r1 +vz RN r2 + +min RN q +max RN r3 + +minX RN r12 +maxX RN lr +minZ RN minX +maxZ RN maxX + +boxRotateYQ_asm + cmp q, #2 + moveq pc, lr + + stmfd sp!, {lr} + + add vz, vx, #(4 * 4) + + cmp q, #1 + beq q_1 + cmp q, #3 + beq q_3 + +q_0 ldmia vx, {minX, maxX} + rsb min, maxX, #0 + rsb max, minX, #0 + stmia vx, {min, max} + ldmia vz, {minZ, maxZ} + rsb min, maxZ, #0 + rsb max, minZ, #0 + stmia vz, {min, max} + ldmfd sp!, {pc} + +q_1 ldmia vz, {minZ, maxZ} + ldmia vx, {min, max} + stmia vz, {min, max} + rsb min, maxZ, #0 + rsb max, minZ, #0 + stmia vx, {min, max} + ldmfd sp!, {pc} + +q_3 ldmia vx, {minX, maxX} + ldmia vz, {min, max} + stmia vx, {min, max} + rsb min, maxX, #0 + rsb max, minX, #0 + stmia vz, {min, max} + ldmfd sp!, {pc} + END diff --git a/src/platform/3do/boxTranslate.s b/src/platform/3do/boxTranslate.s new file mode 100644 index 00000000..9c7cf0ae --- /dev/null +++ b/src/platform/3do/boxTranslate.s @@ -0,0 +1,32 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT boxTranslate_asm + +aabb RN r0 +x RN r1 +y RN r2 +z RN r3 +minX RN r4 +maxX RN r5 +minY RN r6 +maxY RN r7 +minZ RN r12 +maxZ RN lr + +boxTranslate_asm + stmfd sp!, {r4-r7, lr} + + ldmia aabb, {minX, maxX, minY, maxY, minZ, maxZ} + add minX, minX, x + add maxX, maxX, x + add minY, minY, y + add maxY, maxY, y + add minZ, minZ, z + add maxZ, maxZ, z + stmia aabb, {minX, maxX, minY, maxY, minZ, maxZ} + + ldmfd sp!, {r4-r7, pc} + END diff --git a/src/platform/3do/common_asm.inc b/src/platform/3do/common_asm.inc new file mode 100644 index 00000000..42b56141 --- /dev/null +++ b/src/platform/3do/common_asm.inc @@ -0,0 +1,132 @@ + IMPORT gMatrixPtr + IMPORT viewportRel + IMPORT gVertices + IMPORT gFacesBase + IMPORT gOT + IMPORT gPalette + IMPORT gShadowQuads + IMPORT gCameraViewPos + IMPORT shadeTable + IMPORT divTable + IMPORT gSinCosTable + IMPORT level + +CCB_NOBLK EQU 0x00000010 +CCB_BGND EQU 0x00000020 +CCB_ACE EQU 0x00004000 +CCB_ACCW EQU 0x00020000 +CCB_ACW EQU 0x00040000 +CCB_ALSC EQU 0x00080000 +CCB_ACSC EQU 0x00100000 +CCB_YOXY EQU 0x00200000 +CCB_CCBPRE EQU 0x00400000 +CCB_LDPLUT EQU 0x00800000 +CCB_LDPPMP EQU 0x01000000 +CCB_LDPRS EQU 0x02000000 +CCB_LDSIZE EQU 0x04000000 +CCB_PPABS EQU 0x08000000 +CCB_SPABS EQU 0x10000000 +CCB_NPABS EQU 0x20000000 +SIZE_OF_CCB EQU 60 + +FRAME_WIDTH EQU 320 +FRAME_HEIGHT EQU 240 + +FIXED_SHIFT EQU 14 +F16_SHIFT EQU (16 - FIXED_SHIFT) +PROJ_SHIFT EQU 4 +LVL_TEX_OFFSET EQU (23 * 4) + +CLIP_SHIFT EQU 8 +CLIP_MASK EQU ((1 << CLIP_SHIFT) - 1) +CLIP_LEFT EQU (1 << 0) +CLIP_RIGHT EQU (1 << 1) +CLIP_TOP EQU (1 << 2) +CLIP_BOTTOM EQU (1 << 3) +CLIP_FAR EQU (1 << 4) +CLIP_NEAR EQU (1 << 5) + +FACE_MIP_SHIFT EQU 11 +MIP_DIST EQU (1024 * 5) +VIEW_DIST EQU (1024 * 10) ; max = DIV_TABLE_END << PROJ_SHIFT +FOG_SHIFT EQU 1 +FOG_MAX EQU VIEW_DIST +FOG_MIN EQU (FOG_MAX - (8192 >> FOG_SHIFT)) +VIEW_MIN_F EQU (64 << FIXED_SHIFT) +VIEW_MAX_F EQU (VIEW_DIST << FIXED_SHIFT) +VIEW_OFF_F EQU (1024 << FIXED_SHIFT) +OT_SHIFT EQU 4 +OT_SIZE EQU ((VIEW_MAX_F >> (FIXED_SHIFT + OT_SHIFT)) + 1) + +DIV_TABLE_END EQU (1025 - 1) +VIEW_MIN EQU (256 << CLIP_SHIFT) +VIEW_MAX EQU (VIEW_DIST << CLIP_SHIFT) + +MIN_INT32 EQU 0x80000000 +MAX_INT32 EQU 0x7FFFFFFF + +MulManyVec3Mat33_F16 EQU (0x50000 + 2) + + +; max depth = max(z0, z1, z2, z3) >> (CLIP_SHIFT + OT_SHIFT) + MACRO + MAX_Z4 $depth, $z0, $z1, $z2, $z3 + mov $depth, $z0, asr #(CLIP_SHIFT + OT_SHIFT) + cmp $depth, $z1, asr #(CLIP_SHIFT + OT_SHIFT) + movlt $depth, $z1, asr #(CLIP_SHIFT + OT_SHIFT) + cmp $depth, $z2, asr #(CLIP_SHIFT + OT_SHIFT) + movlt $depth, $z2, asr #(CLIP_SHIFT + OT_SHIFT) + cmp $depth, $z3, asr #(CLIP_SHIFT + OT_SHIFT) + movlt $depth, $z3, asr #(CLIP_SHIFT + OT_SHIFT) + MEND + + +; max depth = max(z0, z1, z2) >> (CLIP_SHIFT + OT_SHIFT) + MACRO + MAX_Z3 $depth, $z0, $z1, $z2, $z3 + mov $depth, $z0, asr #(CLIP_SHIFT + OT_SHIFT) + cmp $depth, $z1, asr #(CLIP_SHIFT + OT_SHIFT) + movlt $depth, $z1, asr #(CLIP_SHIFT + OT_SHIFT) + cmp $depth, $z2, asr #(CLIP_SHIFT + OT_SHIFT) + movlt $depth, $z2, asr #(CLIP_SHIFT + OT_SHIFT) + MEND + + +; average depth = (z0 + z1 + z2 + z2) / 4 >> (CLIP_SHIFT + OT_SHIFT) + MACRO + AVG_Z4 $depth, $z0, $z1, $z2, $z3 + add $depth, $z0, $z1 + add $depth, $depth, $z2 + add $depth, $depth, $z3 + mov $depth, $depth, asr #(2 + CLIP_SHIFT + OT_SHIFT) + MEND + + +; average depth = (z0 + z1 + z2 + z2) / 4 >> (CLIP_SHIFT + OT_SHIFT) + MACRO + AVG_Z3 $depth, $z0, $z1, $z2 + add $depth, $z0, $z1 + add $depth, $depth, $z2, lsl #1 + mov $depth, $depth, asr #(2 + CLIP_SHIFT + OT_SHIFT) + MEND + + +; back facing check + MACRO + CCW $cross, $dx0, $dy0, $dx1, $dy1, $skip + mul $cross, $dy0, $dx1 + rsb $cross, cross, #0 + mlas $cross, $dx0, $dy1, $cross + ble skip + MEND + + +; back/front facing check depending on sign + MACRO + CCW_SIGN $cross, $sign, $dx0, $dy0, $dx1, $dy1, $skip + mul $cross, $dy0, $dx1 + rsb $cross, cross, #0 + mla $cross, $dx0, $dy1, $cross + teq $cross, $sign + bmi skip + MEND diff --git a/src/platform/3do/faceAddMeshQuads.s b/src/platform/3do/faceAddMeshQuads.s new file mode 100644 index 00000000..eed7d228 --- /dev/null +++ b/src/platform/3do/faceAddMeshQuads.s @@ -0,0 +1,234 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT faceAddMeshQuads_asm + +polysArg RN r0 +countArg RN r1 +shadeArg RN r2 + +flags RN polysArg + +vx0 RN shadeArg +vy0 RN r3 + +vx1 RN r4 +vy1 RN r5 + +vx3 RN r6 +vy3 RN r7 + +vx2 RN r8 +vy2 RN r9 + +pixc RN r10 +tex RN r11 + +face RN r12 +depth RN lr + +mask RN depth + +fPolys RN countArg +fLast RN tex +fVertices RN face + +spPolys RN vx0 +spLast RN vx1 +spVertices RN vy3 +spOT RN vx2 +spFaceBase RN vy2 +spTextures RN tex +spPalette RN face + +faceBase RN vy2 +cross RN vy2 + +indices RN vy0 + +vz0 RN vy0 +vz1 RN vy1 +vz2 RN vy2 +vz3 RN vy3 + +vp0 RN vx0 +vp1 RN vx1 +vp2 RN vx2 +vp3 RN vx3 + +xpos RN vx0 +ypos RN vy0 +hdx0 RN vx1 +hdy0 RN vy1 +hdx1 RN vx2 +hdy1 RN vy2 +vdx0 RN vx3 +vdy0 RN vy3 +hddx RN hdx1 +hddy RN hdy1 + +nextPtr RN vy2 +dataPtr RN polysArg +plutPtr RN countArg + +tmp RN countArg +ot RN countArg +otTail RN depth +nextFace RN depth + +plutOffset RN vy2 +texIndex RN vy2 + +ws RN tex +hs RN depth +shift RN depth + +SP_POLYS EQU 0 +SP_LAST EQU 4 +SP_VERTICES EQU 8 +SP_OT EQU 12 +SP_FACEBASE EQU 16 +SP_TEXTURES EQU 20 +SP_PALETTE EQU 24 +SP_SIZE EQU 28 + +faceAddMeshQuads_asm + stmfd sp!, {r4-r11, lr} + sub sp, sp, #SP_SIZE + + mov pixc, shadeArg + + add spLast, polysArg, countArg, lsl #3 + ldr spVertices, =gVertices + ldr spOT, =gOT + ldr spFaceBase, =gFacesBase + ldr spTextures, =level + ldr spTextures, [spTextures, #LVL_TEX_OFFSET] + ldr spPalette, =gPalette + ldr spPalette, [spPalette] + + stmia sp, {polysArg, spLast, spVertices, spOT, spFaceBase, spTextures, spPalette} + +loop ldmia sp, {fPolys, fLast, fVertices} +skip cmp fPolys, fLast + bge done + + ldmia fPolys!, {flags, indices} + + ; get vertex pointers + mov mask, #0xFF + and vp0, mask, indices + and vp1, mask, indices, lsr #8 + and vp2, mask, indices, lsr #16 + and vp3, mask, indices, lsr #24 + + add vp0, vp0, vp0, lsl #1 + add vp1, vp1, vp1, lsl #1 + add vp2, vp2, vp2, lsl #1 + add vp3, vp3, vp3, lsl #1 + + add vp0, fVertices, vp0, lsl #2 + add vp1, fVertices, vp1, lsl #2 + add vp2, fVertices, vp2, lsl #2 + add vp3, fVertices, vp3, lsl #2 + + ; read z value with clip mask + ldr vz0, [vp0, #8] + ldr vz1, [vp1, #8] + ldr vz2, [vp2, #8] + ldr vz3, [vp3, #8] + + ; check clipping + and mask, vz1, vz0 + and mask, vz2, mask + and mask, vz3, mask + tst mask, #CLIP_MASK + bne skip + + AVG_Z4 depth, vz0, vz1, vz2, vz3 + + ; (vx1 - vx0) * (vy3 - vy0) <= (vy1 - vy0) * (vx3 - vx0) + ldmia vp0, {vx0, vy0} + ldmia vp1, {vx1, vy1} + ldmia vp3, {vx3, vy3} + sub hdx0, vx1, vx0 + sub hdy0, vy1, vy0 + sub vdx0, vx3, vx0 + sub vdy0, vy3, vy0 + + CCW_SIGN cross, flags, hdx0, hdy0, vdx0, vdy0, skip + + ; poly is visible, store fPolys on the stack to reuse the reg + str fPolys, [sp, #SP_POLYS] + + add tmp, sp, #SP_OT + ldmia tmp, {ot, faceBase, tex} + + ; faceAdd + add ot, ot, depth, lsl #3 ; mul by size of OT element + + ldr face, [faceBase] + add nextFace, face, #SIZE_OF_CCB + str nextFace, [faceBase] + + ; get texture ptr + mov texIndex, flags, lsl #(32 - FACE_MIP_SHIFT) + add tex, tex, texIndex, lsr #(32 - FACE_MIP_SHIFT - 3) ; sizeof(Texture) = 2^3 + + ; add face to Ordering Table + ldmia ot, {nextPtr, otTail} + cmp nextPtr, #0 + moveq otTail, face + stmia ot, {face, otTail} + + ; ccb flags + ands flags, flags, #(1 << 30) + movne flags, #(CCB_BGND) + orr flags, flags, #(CCB_NOBLK) + orr flags, flags, #(CCB_ACE + CCB_ACCW + CCB_ACW + CCB_ALSC + CCB_ACSC + CCB_YOXY) + orr flags, flags, #(CCB_LDPLUT + CCB_LDPPMP + CCB_LDPRS + CCB_LDSIZE + CCB_PPABS + CCB_SPABS + CCB_NPABS) + + ; ccbMap4 + stmia face!, {flags, nextPtr} + ldmia tex, {dataPtr, shift} + + ; plutPtr = plutOffset + (tex->shift >> 16) + ldr plutOffset, [sp, #SP_PALETTE] + add plutPtr, plutOffset, shift, lsr #16 + + ldmia vp2, {vx2, vy2} + sub vx2, vx2, vx0 + sub vy2, vy2, vy0 + sub hdx1, vx2, vx3 + sub hdy1, vy2, vy3 + + and ws, shift, #0xFF + mov hs, shift, lsr #8 + and hs, hs, #0xFF + + mov hdx0, hdx0, lsl ws + mov hdy0, hdy0, lsl ws + + mov vdx0, vdx0, lsl hs + mov vdy0, vdy0, lsl hs + + rsb hs, hs, #16 + rsb hddx, hdx0, hdx1, lsl ws + rsb hddy, hdy0, hdy1, lsl ws + mov hddx, hddx, asr hs + mov hddy, hddy, asr hs + + add xpos, vx0, #(FRAME_WIDTH >> 1) + add ypos, vy0, #(FRAME_HEIGHT >> 1) + mov xpos, vx0, lsl #16 + mov ypos, vy0, lsl #16 + + stmia face, {dataPtr, plutPtr, xpos, ypos, hdx0, hdy0, vdx0, vdy0, hddx, hddy, pixc} + + b loop + +done add sp, sp, #SP_SIZE + ldmfd sp!, {r4-r11, pc} + END diff --git a/src/platform/3do/faceAddMeshQuadsFlat.s b/src/platform/3do/faceAddMeshQuadsFlat.s new file mode 100644 index 00000000..f554e615 --- /dev/null +++ b/src/platform/3do/faceAddMeshQuadsFlat.s @@ -0,0 +1,216 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT faceAddMeshQuadsFlat_asm + +polysArg RN r0 +countArg RN r1 +shadeArg RN r2 + +flags RN polysArg + +vx0 RN shadeArg +vy0 RN r3 + +vx1 RN r4 +vy1 RN r5 + +vx3 RN r6 +vy3 RN r7 + +vx2 RN r8 +vy2 RN r9 + +pixc RN r10 +color RN r11 + +face RN r12 +depth RN lr + +mask RN depth + +fPolys RN countArg +fLast RN color +fVertices RN face + +spPolys RN vx0 +spLast RN vx1 +spVertices RN vy3 +spFlags RN vx2 +spOT RN vy2 +spFaceBase RN color +spPalette RN face + +faceBase RN vy2 +cross RN vy2 + +indices RN vy0 + +vz0 RN vy0 +vz1 RN vy1 +vz2 RN vy2 +vz3 RN vy3 + +vp0 RN vx0 +vp1 RN vx1 +vp2 RN vx2 +vp3 RN vx3 + +xpos RN vx0 +ypos RN vy0 +hdx0 RN vx1 +hdy0 RN vy1 +hdx1 RN vx2 +hdy1 RN vy2 +vdx0 RN vx3 +vdy0 RN vy3 +hddx RN hdx1 +hddy RN hdy1 + +nextPtr RN vy2 +dataPtr RN color +plutPtr RN countArg + +tmp RN countArg +ot RN countArg +otTail RN depth +nextFace RN depth + +plutOffset RN color +colorIndex RN face + +SP_POLYS EQU 0 +SP_LAST EQU 4 +SP_VERTICES EQU 8 +SP_FLAGS EQU 12 +SP_OT EQU 16 +SP_FACEBASE EQU 20 +SP_PALETTE EQU 24 +SP_SIZE EQU 28 + +faceAddMeshQuadsFlat_asm + stmfd sp!, {r4-r11, lr} + sub sp, sp, #SP_SIZE + + mov pixc, shadeArg + + add spLast, polysArg, countArg, lsl #3 + ldr spVertices, =gVertices + mov spFlags, #(CCB_NOBLK + CCB_BGND) + orr spFlags, spFlags, #(CCB_ACE + CCB_ACCW + CCB_ACW + CCB_ALSC + CCB_ACSC + CCB_YOXY) + orr spFlags, spFlags, #(CCB_CCBPRE + CCB_LDPPMP + CCB_LDPRS + CCB_LDSIZE + CCB_PPABS + CCB_SPABS + CCB_NPABS) + ldr spOT, =gOT + ldr spFaceBase, =gFacesBase + ldr spPalette, =gPalette + ldr spPalette, [spPalette] + + stmia sp, {polysArg, spLast, spVertices, spFlags, spOT, spFaceBase, spPalette} + +loop ldmia sp, {fPolys, fLast, fVertices} +skip cmp fPolys, fLast + bge done + + ldmia fPolys!, {flags, indices} + + ; get vertex pointers + mov mask, #0xFF + and vp0, mask, indices + and vp1, mask, indices, lsr #8 + and vp2, mask, indices, lsr #16 + and vp3, mask, indices, lsr #24 + + add vp0, vp0, vp0, lsl #1 + add vp1, vp1, vp1, lsl #1 + add vp2, vp2, vp2, lsl #1 + add vp3, vp3, vp3, lsl #1 + + add vp0, fVertices, vp0, lsl #2 + add vp1, fVertices, vp1, lsl #2 + add vp2, fVertices, vp2, lsl #2 + add vp3, fVertices, vp3, lsl #2 + + ; read z value with clip mask + ldr vz0, [vp0, #8] + ldr vz1, [vp1, #8] + ldr vz2, [vp2, #8] + ldr vz3, [vp3, #8] + + ; check clipping + and mask, vz1, vz0 + and mask, vz2, mask + and mask, vz3, mask + tst mask, #CLIP_MASK + bne skip + + AVG_Z4 depth, vz0, vz1, vz2, vz3 + + ; (vx1 - vx0) * (vy3 - vy0) <= (vy1 - vy0) * (vx3 - vx0) + ldmia vp0, {vx0, vy0} + ldmia vp1, {vx1, vy1} + ldmia vp3, {vx3, vy3} + sub hdx0, vx1, vx0 + sub hdy0, vy1, vy0 + sub vdx0, vx3, vx0 + sub vdy0, vy3, vy0 + + CCW cross, hdx0, hdy0, vdx0, vdy0, skip + + ; poly is visible, store fPolys on the stack to reuse the reg + str fPolys, [sp, #SP_POLYS] + + ; get color index from flags + and colorIndex, flags, #0xFF + + add tmp, sp, #SP_FLAGS + ldmia tmp, {flags, ot, faceBase, plutOffset} + + ; get color ptr + add dataPtr, plutOffset, colorIndex, lsl #1 + + ; faceAdd + add ot, ot, depth, lsl #3 ; mul by size of OT element + + ldr face, [faceBase] + add nextFace, face, #SIZE_OF_CCB + str nextFace, [faceBase] + + ; add face to Ordering Table + ldmia ot, {nextPtr, otTail} + cmp nextPtr, #0 + moveq otTail, face + stmia ot, {face, otTail} + + ; ccbMap4 (colored) + stmia face, {flags, nextPtr, dataPtr} + + ldmia vp2, {vx2, vy2} + sub vx2, vx2, vx0 + sub vy2, vy2, vy0 + sub hdx1, vx2, vx3 + sub hdy1, vy2, vy3 + + mov hdx0, hdx0, lsl #20 + mov hdy0, hdy0, lsl #20 + + mov vdx0, vdx0, lsl #16 + mov vdy0, vdy0, lsl #16 + + rsb hddx, hdx0, hdx1, lsl #20 + rsb hddy, hdy0, hdy1, lsl #20 + + add xpos, vx0, #(FRAME_WIDTH >> 1) + add ypos, vy0, #(FRAME_HEIGHT >> 1) + mov xpos, vx0, lsl #16 + mov ypos, vy0, lsl #16 + + add face, face, #16 ; skip flags, nextPtr, dataPtr, plutPtr + + stmia face, {xpos, ypos, hdx0, hdy0, vdx0, vdy0, hddx, hddy, pixc} + + b loop + +done add sp, sp, #SP_SIZE + ldmfd sp!, {r4-r11, pc} + END diff --git a/src/platform/3do/faceAddMeshTriangles.s b/src/platform/3do/faceAddMeshTriangles.s new file mode 100644 index 00000000..42ba3ee1 --- /dev/null +++ b/src/platform/3do/faceAddMeshTriangles.s @@ -0,0 +1,218 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT faceAddMeshTriangles_asm + +polysArg RN r0 +countArg RN r1 +shadeArg RN r2 + +flags RN polysArg + +vx0 RN shadeArg +vy0 RN r3 + +vx1 RN r4 +vy1 RN r5 + +vx2 RN r6 +vy2 RN r7 + +vx3 RN r8 +vy3 RN r9 + +pixc RN r10 +tex RN r11 + +face RN r12 +depth RN lr + +mask RN depth + +fPolys RN countArg +fLast RN tex +fVertices RN face + +spPolys RN vx0 +spLast RN vx1 +spVertices RN vy2 +spOT RN vx3 +spPalette RN vy3 +spFaceBase RN tex +spTextures RN face + +faceBase RN vy3 +cross RN vy3 + +indices RN vy0 + +vz0 RN vy0 +vz1 RN vy1 +vz2 RN vy2 + +vp0 RN vx0 +vp1 RN vx1 +vp2 RN vx2 + +xpos RN vx0 +ypos RN vy0 +hdx0 RN vx1 +hdy0 RN vy1 +vdx0 RN vx2 +vdy0 RN vy2 +hddx RN vx3 +hddy RN vy3 + +nextPtr RN vy3 +dataPtr RN polysArg +plutPtr RN countArg + +tmp RN countArg +ot RN countArg +otTail RN depth +nextFace RN depth + +plutOffset RN vx3 +texIndex RN vy3 + +ws RN tex +hs RN depth +shift RN depth + +SP_POLYS EQU 0 +SP_LAST EQU 4 +SP_VERTICES EQU 8 +SP_OT EQU 12 +SP_PALETTE EQU 16 +SP_FACEBASE EQU 20 +SP_TEXTURES EQU 24 +SP_SIZE EQU 28 + +faceAddMeshTriangles_asm + stmfd sp!, {r4-r11, lr} + sub sp, sp, #SP_SIZE + + mov pixc, shadeArg + + add spLast, polysArg, countArg, lsl #3 + ldr spVertices, =gVertices + ldr spOT, =gOT + ldr spPalette, =gPalette + ldr spPalette, [spPalette] + ldr spFaceBase, =gFacesBase + ldr spTextures, =level + ldr spTextures, [spTextures, #LVL_TEX_OFFSET] + + stmia sp, {polysArg, spLast, spVertices, spOT, spPalette, spFaceBase, spTextures} + +loop ldmia sp, {fPolys, fLast, fVertices} +skip cmp fPolys, fLast + bge done + + ldmia fPolys!, {flags, indices} + + ; get vertex pointers + mov mask, #0xFF + and vp0, mask, indices + and vp1, mask, indices, lsr #8 + and vp2, mask, indices, lsr #16 + + add vp0, vp0, vp0, lsl #1 + add vp1, vp1, vp1, lsl #1 + add vp2, vp2, vp2, lsl #1 + + add vp0, fVertices, vp0, lsl #2 + add vp1, fVertices, vp1, lsl #2 + add vp2, fVertices, vp2, lsl #2 + + ; read z value with clip mask + ldr vz0, [vp0, #8] + ldr vz1, [vp1, #8] + ldr vz2, [vp2, #8] + + ; check clipping + and mask, vz1, vz0 + and mask, vz2, mask + tst mask, #CLIP_MASK + bne skip + + AVG_Z3 depth, vz0, vz1, vz2 + + ; (vx1 - vx0) * (vy2 - vy0) - (vy1 - vy0) * (vx2 - vx0) <= 0 + ldmia vp0, {vx0, vy0} + ldmia vp1, {vx1, vy1} + ldmia vp2, {vx2, vy2} + sub hdx0, vx1, vx0 + sub hdy0, vy1, vy0 + sub vdx0, vx2, vx0 + sub vdy0, vy2, vy0 + + CCW cross, hdx0, hdy0, vdx0, vdy0, skip + + ; poly is visible, store fPolys on the stack to reuse the reg + str fPolys, [sp, #SP_POLYS] + + add tmp, sp, #SP_OT + ldmia tmp, {ot, plutOffset, faceBase, tex} + + ; faceAdd + add ot, ot, depth, lsl #3 ; mul by size of OT element + + ldr face, [faceBase] + add nextFace, face, #SIZE_OF_CCB + str nextFace, [faceBase] + + ; get texture ptr + mov texIndex, flags, lsl #(32 - FACE_MIP_SHIFT) + add tex, tex, texIndex, lsr #(32 - FACE_MIP_SHIFT - 3) ; sizeof(Texture) = 2^3 + + ; add face to Ordering Table + ldmia ot, {nextPtr, otTail} + cmp nextPtr, #0 + moveq otTail, face + stmia ot, {face, otTail} + + ; ccb flags + ands flags, flags, #(1 << 30) + movne flags, #(CCB_BGND) + orr flags, flags, #(CCB_NOBLK) + orr flags, flags, #(CCB_ACE + CCB_ACCW + CCB_ACW + CCB_ALSC + CCB_ACSC + CCB_YOXY) + orr flags, flags, #(CCB_LDPLUT + CCB_LDPPMP + CCB_LDPRS + CCB_LDSIZE + CCB_PPABS + CCB_SPABS + CCB_NPABS) + + ; ccbMap3 + stmia face!, {flags, nextPtr} + ldmia tex, {dataPtr, shift} + + ; plutPtr = plutOffset + (tex->shift >> 16) + add plutPtr, plutOffset, shift, lsr #16 + + and ws, shift, #0xFF + mov hs, shift, lsr #8 + and hs, hs, #0xFF + + mov hdx0, hdx0, lsl ws + mov hdy0, hdy0, lsl ws + + mov vdx0, vdx0, lsl hs + mov vdy0, vdy0, lsl hs + + rsb hs, hs, #16 + rsb hddx, hdx0, #0 + rsb hddy, hdy0, #0 + mov hddx, hddx, asr hs + mov hddy, hddy, asr hs + + add xpos, vx0, #(FRAME_WIDTH >> 1) + add ypos, vy0, #(FRAME_HEIGHT >> 1) + mov xpos, vx0, lsl #16 + mov ypos, vy0, lsl #16 + + stmia face, {dataPtr, plutPtr, xpos, ypos, hdx0, hdy0, vdx0, vdy0, hddx, hddy, pixc} + + b loop + +done add sp, sp, #SP_SIZE + ldmfd sp!, {r4-r11, pc} + END diff --git a/src/platform/3do/faceAddMeshTrianglesFlat.s b/src/platform/3do/faceAddMeshTrianglesFlat.s new file mode 100644 index 00000000..c8c8eadf --- /dev/null +++ b/src/platform/3do/faceAddMeshTrianglesFlat.s @@ -0,0 +1,201 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT faceAddMeshTrianglesFlat_asm + +polysArg RN r0 +countArg RN r1 +shadeArg RN r2 + +flags RN polysArg + +vx0 RN shadeArg +vy0 RN r3 + +vx1 RN r4 +vy1 RN r5 + +vx2 RN r6 +vy2 RN r7 + +vx3 RN r8 +vy3 RN r9 + +pixc RN r10 +color RN r11 + +face RN r12 +depth RN lr + +mask RN depth + +fPolys RN countArg +fLast RN color +fVertices RN face + +spPolys RN vx0 +spLast RN vx1 +spVertices RN vy2 +spFlags RN vx3 +spOT RN vy3 +spFaceBase RN color +spPalette RN face + +faceBase RN vy3 +cross RN vy3 + +indices RN vy0 + +vz0 RN vy0 +vz1 RN vy1 +vz2 RN vy3 + +vp0 RN vx0 +vp1 RN vx1 +vp2 RN vx3 + +xpos RN vx0 +ypos RN vy0 +hdx0 RN vx1 +hdy0 RN vy1 +vdx0 RN vx2 +vdy0 RN vy2 +hddx RN vx3 +hddy RN vy3 + +nextPtr RN vy3 +dataPtr RN color +plutPtr RN countArg + +tmp RN countArg +ot RN countArg +otTail RN depth +nextFace RN depth + +plutOffset RN color +colorIndex RN face + +SP_POLYS EQU 0 +SP_LAST EQU 4 +SP_VERTICES EQU 8 +SP_FLAGS EQU 12 +SP_OT EQU 16 +SP_FACEBASE EQU 20 +SP_PALETTE EQU 24 +SP_SIZE EQU 28 + +faceAddMeshTrianglesFlat_asm + stmfd sp!, {r4-r11, lr} + sub sp, sp, #SP_SIZE + + mov pixc, shadeArg + + add spLast, polysArg, countArg, lsl #3 + ldr spVertices, =gVertices + mov spFlags, #(CCB_NOBLK + CCB_BGND) + orr spFlags, spFlags, #(CCB_ACE + CCB_ACCW + CCB_ACW + CCB_ALSC + CCB_ACSC + CCB_YOXY) + orr spFlags, spFlags, #(CCB_CCBPRE + CCB_LDPPMP + CCB_LDPRS + CCB_LDSIZE + CCB_PPABS + CCB_SPABS + CCB_NPABS) + ldr spOT, =gOT + ldr spFaceBase, =gFacesBase + ldr spPalette, =gPalette + ldr spPalette, [spPalette] + + stmia sp, {polysArg, spLast, spVertices, spFlags, spOT, spFaceBase, spPalette} + +loop ldmia sp, {fPolys, fLast, fVertices} +skip cmp fPolys, fLast + bge done + + ldmia fPolys!, {flags, indices} + + ; get vertex pointers + mov mask, #0xFF + and vp0, mask, indices + and vp1, mask, indices, lsr #8 + and vp2, mask, indices, lsr #16 + + add vp0, vp0, vp0, lsl #1 + add vp1, vp1, vp1, lsl #1 + add vp2, vp2, vp2, lsl #1 + + add vp0, fVertices, vp0, lsl #2 + add vp1, fVertices, vp1, lsl #2 + add vp2, fVertices, vp2, lsl #2 + + ; read z value with clip mask + ldr vz0, [vp0, #8] + ldr vz1, [vp1, #8] + ldr vz2, [vp2, #8] + + ; check clipping + and mask, vz1, vz0 + and mask, vz2, mask + tst mask, #CLIP_MASK + bne skip + + AVG_Z3 depth, vz0, vz1, vz2 + + ; (vx1 - vx0) * (vy2 - vy0) - (vy1 - vy0) * (vx2 - vx0) <= 0 + ldmia vp0, {vx0, vy0} + ldmia vp1, {vx1, vy1} + ldmia vp2, {vx2, vy2} + sub hdx0, vx1, vx0 + sub hdy0, vy1, vy0 + sub vdx0, vx2, vx0 + sub vdy0, vy2, vy0 + + CCW cross, hdx0, hdy0, vdx0, vdy0, skip + + ; poly is visible, store fPolys on the stack to reuse the reg + str fPolys, [sp, #SP_POLYS] + + ; get color index from flags + and colorIndex, flags, #0xFF + + add tmp, sp, #SP_FLAGS + ldmia tmp, {flags, ot, faceBase, plutOffset} + + ; get color ptr + add dataPtr, plutOffset, colorIndex, lsl #1 + + ; faceAdd + add ot, ot, depth, lsl #3 ; mul by size of OT element + + ldr face, [faceBase] + add nextFace, face, #SIZE_OF_CCB + str nextFace, [faceBase] + + ; add face to Ordering Table + ldmia ot, {nextPtr, otTail} + cmp nextPtr, #0 + moveq otTail, face + stmia ot, {face, otTail} + + ; ccbMap3 (colored) + stmia face, {flags, nextPtr, dataPtr} + + mov hdx0, hdx0, lsl #20 + mov hdy0, hdy0, lsl #20 + + mov vdx0, vdx0, lsl #16 + mov vdy0, vdy0, lsl #16 + + rsb hddx, hdx0, #0 + rsb hddy, hdy0, #0 + + add xpos, vx0, #(FRAME_WIDTH >> 1) + add ypos, vy0, #(FRAME_HEIGHT >> 1) + mov xpos, vx0, lsl #16 + mov ypos, vy0, lsl #16 + + add face, face, #16 ; skip flags, nextPtr, dataPtr, plutPtr + + stmia face, {xpos, ypos, hdx0, hdy0, vdx0, vdy0, hddx, hddy, pixc} + + b loop + +done add sp, sp, #SP_SIZE + ldmfd sp!, {r4-r11, pc} + END diff --git a/src/platform/3do/faceAddRoomQuads.s b/src/platform/3do/faceAddRoomQuads.s new file mode 100644 index 00000000..df760f0c --- /dev/null +++ b/src/platform/3do/faceAddRoomQuads.s @@ -0,0 +1,249 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT faceAddRoomQuads_asm + +polysArg RN r0 +countArg RN r1 + +flags RN polysArg + +vx0 RN r2 +vy0 RN r3 + +vx1 RN r4 +vy1 RN r5 + +vx3 RN r6 +vy3 RN r7 + +vx2 RN r8 +vy2 RN r9 + +pixc RN r10 +tex RN r11 + +mask RN r12 +depth RN lr + +fPolys RN countArg +fLast RN pixc +fVertices RN tex + +spPolys RN vx0 +spLast RN vx1 +spVertices RN vy3 +spOT RN vx2 +spShadeLUT RN vy2 +spTextures RN pixc +spFaceBase RN tex +spPalette RN mask + +face RN mask +faceBase RN mask +cross RN mask + +i0 RN vy0 +i1 RN vy1 + +vz0 RN vy0 +vz1 RN vy1 +vz2 RN vy2 +vz3 RN vy3 + +vp0 RN vx0 +vp1 RN vx1 +vp2 RN vx2 +vp3 RN vx3 + +xpos RN vx0 +ypos RN vy0 +hdx0 RN vx1 +hdy0 RN vy1 +hdx1 RN vx2 +hdy1 RN vy2 +vdx0 RN vx3 +vdy0 RN vy3 +hddx RN hdx1 +hddy RN hdy1 + +nextPtr RN vy2 +dataPtr RN polysArg +plutPtr RN countArg + +tmp RN countArg +ot RN countArg +otTail RN depth + +shadeLUT RN pixc +fog RN pixc + +intensity RN vy2 +plutOffset RN vy2 +texIndex RN vy2 + +ws RN tex +hs RN depth +shift RN depth + +SP_POLYS EQU 0 +SP_LAST EQU 4 +SP_VERTICES EQU 8 +SP_OT EQU 12 +SP_SHADELUT EQU 16 +SP_TEXTURES EQU 20 +SP_FACEBASE EQU 24 +SP_PALETTE EQU 28 +SP_SIZE EQU 32 + +faceAddRoomQuads_asm + stmfd sp!, {r4-r11, lr} + sub sp, sp, #SP_SIZE + + add countArg, countArg, countArg, lsl #1 + add spLast, polysArg, countArg, lsl #2 + ldr spVertices, =gVertices + ldr spOT, =gOT + ldr spShadeLUT, =shadeTable + ldr spTextures, =level + ldr spTextures, [spTextures, #LVL_TEX_OFFSET] + ldr spFaceBase, =gFacesBase + ldr spPalette, =gPalette + ldr spPalette, [spPalette] + + stmia sp, {polysArg, spLast, spVertices, spOT, spShadeLUT, spTextures, spFaceBase, spPalette} + +loop ldmia sp, {fPolys, fLast, fVertices} +skip cmp fPolys, fLast + bge done + + ldmia fPolys!, {flags, i0, i1} + + ; get vertex pointers (indices are pre-multiplied by 12) + add vp0, fVertices, i0, lsr #16 + mov i0, i0, lsl #16 + add vp1, fVertices, i0, lsr #16 + + add vp2, fVertices, i1, lsr #16 + mov i1, i1, lsl #16 + add vp3, fVertices, i1, lsr #16 + + ; read z value with clip mask + ldr vz0, [vp0, #8] + ldr vz1, [vp1, #8] + ldr vz2, [vp2, #8] + ldr vz3, [vp3, #8] + + ; check clipping + and mask, vz1, vz0 + and mask, vz2, mask + and mask, vz3, mask + tst mask, #CLIP_MASK + bne skip + + MAX_Z4 depth, vz0, vz1, vz2, vz3 + + ; (vx1 - vx0) * (vy3 - vy0) <= (vy1 - vy0) * (vx3 - vx0) + ldmia vp0, {vx0, vy0} + ldmia vp1, {vx1, vy1} + ldmia vp3, {vx3, vy3} + sub hdx0, vx1, vx0 + sub hdy0, vy1, vy0 + sub vdx0, vx3, vx0 + sub vdy0, vy3, vy0 + + CCW cross, hdx0, hdy0, vdx0, vdy0, skip + + ; poly is visible, store fPolys on the stack to reuse the reg + str fPolys, [sp, #SP_POLYS] + + ; fog = max(0, (depth - (FOG_MIN >> OT_SHIFT)) >> 1) + sub fog, depth, #(FOG_MIN >> OT_SHIFT) + movs fog, fog, asr #1 + movmi fog, #0 + + ; intensity = min(255, fog + ((flags >> (FACE_MIP_SHIFT + FACE_MIP_SHIFT)) & 0xFF)) >> 3 + mov intensity, flags, lsl #(32 - 8 - FACE_MIP_SHIFT - FACE_MIP_SHIFT) + add intensity, fog, intensity, lsr #(32 - 8) + cmp intensity, #255 + movcs intensity, #255 + mov intensity, intensity, lsr #3 + + add tmp, sp, #SP_OT + ldmia tmp, {ot, shadeLUT, tex, faceBase} + + ; pixc = shadeTable[intensity] + ldr pixc, [shadeLUT, intensity, lsl #2] + + ; get texture ptr (base or mip) + mov texIndex, flags + cmp depth, #(MIP_DIST >> OT_SHIFT) + movgt texIndex, texIndex, lsr #FACE_MIP_SHIFT + mov texIndex, texIndex, lsl #(32 - FACE_MIP_SHIFT) + add tex, tex, texIndex, lsr #(32 - FACE_MIP_SHIFT - 3) ; sizeof(Texture) = 2^3 + + ; faceAdd + add ot, ot, depth, lsl #3 ; mul by size of OT element + + mov depth, faceBase ; use depth reg due face vs faceBase reg collision + + ldr face, [depth] + add nextPtr, face, #SIZE_OF_CCB + str nextPtr, [depth] + + ldmia ot, {nextPtr, otTail} + cmp nextPtr, #0 + moveq otTail, face + stmia ot, {face, otTail} + + ; ccb flags + ands flags, flags, #(1 << 30) + movne flags, #(CCB_BGND) + orr flags, flags, #(CCB_NOBLK) + orr flags, flags, #(CCB_ACE + CCB_ACCW + CCB_ACW + CCB_ALSC + CCB_ACSC + CCB_YOXY) + orr flags, flags, #(CCB_LDPLUT + CCB_LDPPMP + CCB_LDPRS + CCB_LDSIZE + CCB_PPABS + CCB_SPABS + CCB_NPABS) + + ; ccbMap4 + stmia face!, {flags, nextPtr} + ldmia tex, {dataPtr, shift} + + ; plutPtr = plutOffset + (tex->shift >> 16) + ldr plutOffset, [sp, #SP_PALETTE] + add plutPtr, plutOffset, shift, lsr #16 + + ldmia vp2, {vx2, vy2} + sub vx2, vx2, vx0 + sub vy2, vy2, vy0 + sub hdx1, vx2, vx3 + sub hdy1, vy2, vy3 + + and ws, shift, #0xFF + mov hs, shift, lsr #8 + and hs, hs, #0xFF + + mov hdx0, hdx0, lsl ws + mov hdy0, hdy0, lsl ws + + mov vdx0, vdx0, lsl hs + mov vdy0, vdy0, lsl hs + + rsb hs, hs, #16 + rsb hddx, hdx0, hdx1, lsl ws + rsb hddy, hdy0, hdy1, lsl ws + mov hddx, hddx, asr hs + mov hddy, hddy, asr hs + + mov xpos, vx0, lsl #16 + mov ypos, vy0, lsl #16 + add xpos, xpos, #(FRAME_WIDTH << 15) + add ypos, ypos, #(FRAME_HEIGHT << 15) + + stmia face, {dataPtr, plutPtr, xpos, ypos, hdx0, hdy0, vdx0, vdy0, hddx, hddy, pixc} + + b loop + +done add sp, sp, #SP_SIZE + ldmfd sp!, {r4-r11, pc} + END diff --git a/src/platform/3do/faceAddRoomTriangles.s b/src/platform/3do/faceAddRoomTriangles.s new file mode 100644 index 00000000..d5956e4b --- /dev/null +++ b/src/platform/3do/faceAddRoomTriangles.s @@ -0,0 +1,234 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT faceAddRoomTriangles_asm + +polysArg RN r0 +countArg RN r1 + +flags RN polysArg + +vx0 RN r2 +vy0 RN r3 + +vx1 RN r4 +vy1 RN r5 + +vx2 RN r6 +vy2 RN r7 + +vx3 RN r8 +vy3 RN r9 + +pixc RN r10 +tex RN r11 + +mask RN r12 +depth RN lr + +fPolys RN countArg +fLast RN pixc +fVertices RN tex + +spPolys RN vx0 +spLast RN vx1 +spVertices RN vy2 +spOT RN vx3 +spPalette RN vy3 +spShadeLUT RN pixc +spTextures RN tex +spFaceBase RN mask + +face RN mask +faceBase RN mask +cross RN mask + +i0 RN vy0 +i1 RN vy1 + +vz0 RN vy0 +vz1 RN vy1 +vz2 RN vy2 + +vp0 RN vx0 +vp1 RN vx1 +vp2 RN vx2 + +xpos RN vx0 +ypos RN vy0 +hdx0 RN vx1 +hdy0 RN vy1 +vdx0 RN vx2 +vdy0 RN vy2 +hddx RN vx3 +hddy RN vy3 + +nextPtr RN vy3 +dataPtr RN polysArg +plutPtr RN countArg + +tmp RN countArg +ot RN countArg +otTail RN depth + +shadeLUT RN pixc +fog RN pixc + +intensity RN vy3 +plutOffset RN vx3 +texIndex RN vy3 + +ws RN tex +hs RN depth +shift RN depth + +SP_POLYS EQU 0 +SP_LAST EQU 4 +SP_VERTICES EQU 8 +SP_OT EQU 12 +SP_PALETTE EQU 16 +SP_SHADELUT EQU 20 +SP_TEXTURES EQU 24 +SP_FACEBASE EQU 28 +SP_SIZE EQU 32 + +faceAddRoomTriangles_asm + stmfd sp!, {r4-r11, lr} + sub sp, sp, #SP_SIZE + + add countArg, countArg, countArg, lsl #1 + add spLast, polysArg, countArg, lsl #2 + ldr spVertices, =gVertices + ldr spOT, =gOT + ldr spPalette, =gPalette + ldr spPalette, [spPalette] + ldr spShadeLUT, =shadeTable + ldr spTextures, =level + ldr spTextures, [spTextures, #LVL_TEX_OFFSET] + ldr spFaceBase, =gFacesBase + + stmia sp, {polysArg, spLast, spVertices, spOT, spPalette, spShadeLUT, spTextures, spFaceBase} + +loop ldmia sp, {fPolys, fLast, fVertices} +skip cmp fPolys, fLast + bge done + + ldmia fPolys!, {flags, i0, i1} + + ; get vertex pointers (indices are pre-multiplied by 12) + add vp0, fVertices, i0, lsr #16 + mov i0, i0, lsl #16 + add vp1, fVertices, i0, lsr #16 + + add vp2, fVertices, i1, lsr #16 + + ; read z value with clip mask + ldr vz0, [vp0, #8] + ldr vz1, [vp1, #8] + ldr vz2, [vp2, #8] + + ; check clipping + and mask, vz1, vz0 + and mask, vz2, mask + tst mask, #CLIP_MASK + bne skip + + MAX_Z3 depth, vz0, vz1, vz2 + + ; (vx1 - vx0) * (vy2 - vy0) <= (vy1 - vy0) * (vx2 - vx0) + ldmia vp0, {vx0, vy0} + ldmia vp1, {vx1, vy1} + ldmia vp2, {vx2, vy2} + sub hdx0, vx1, vx0 + sub hdy0, vy1, vy0 + sub vdx0, vx2, vx0 + sub vdy0, vy2, vy0 + + CCW cross, hdx0, hdy0, vdx0, vdy0, skip + + ; poly is visible, store fPolys on the stack to reuse the reg + str fPolys, [sp, #SP_POLYS] + + ; fog = max(0, (depth - (FOG_MIN >> OT_SHIFT)) >> 1) + sub fog, depth, #(FOG_MIN >> OT_SHIFT) + movs fog, fog, asr #1 + movmi fog, #0 + + ; intensity = min(255, fog + ((flags >> (FACE_MIP_SHIFT + FACE_MIP_SHIFT)) & 0xFF)) >> 3 + mov intensity, flags, lsl #(32 - 8 - FACE_MIP_SHIFT - FACE_MIP_SHIFT) + add intensity, fog, intensity, lsr #(32 - 8) + cmp intensity, #255 + movcs intensity, #255 + mov intensity, intensity, lsr #3 + + add tmp, sp, #SP_OT + ldmia tmp, {ot, plutOffset, shadeLUT, tex, faceBase} + + ; pixc = shadeTable[intensity] + ldr pixc, [shadeLUT, intensity, lsl #2] + + ; get texture ptr (base or mip) + mov texIndex, flags + cmp depth, #(MIP_DIST >> OT_SHIFT) + movgt texIndex, texIndex, lsr #FACE_MIP_SHIFT + mov texIndex, texIndex, lsl #(32 - FACE_MIP_SHIFT) + add tex, tex, texIndex, lsr #(32 - FACE_MIP_SHIFT - 3) ; sizeof(Texture) = 2^3 + + ; faceAdd + add ot, ot, depth, lsl #3 ; mul by size of OT element + + mov depth, faceBase ; use depth reg due face vs faceBase reg collision + + ldr face, [depth] + add nextPtr, face, #SIZE_OF_CCB + str nextPtr, [depth] + + ldmia ot, {nextPtr, otTail} + cmp nextPtr, #0 + moveq otTail, face + stmia ot, {face, otTail} + + ; ccb flags + ands flags, flags, #(1 << 30) + movne flags, #(CCB_BGND) + orr flags, flags, #(CCB_NOBLK) + orr flags, flags, #(CCB_ACE + CCB_ACCW + CCB_ACW + CCB_ALSC + CCB_ACSC + CCB_YOXY) + orr flags, flags, #(CCB_LDPLUT + CCB_LDPPMP + CCB_LDPRS + CCB_LDSIZE + CCB_PPABS + CCB_SPABS + CCB_NPABS) + + ; ccbMap4 + stmia face!, {flags, nextPtr} + ldmia tex, {dataPtr, shift} + + ; plutPtr = plutOffset + (tex->shift >> 16) + add plutPtr, plutOffset, shift, lsr #16 + + and ws, shift, #0xFF + mov hs, shift, lsr #8 + and hs, hs, #0xFF + + mov hdx0, hdx0, lsl ws + mov hdy0, hdy0, lsl ws + + mov vdx0, vdx0, lsl hs + mov vdy0, vdy0, lsl hs + + rsb hs, hs, #16 + rsb hddx, hdx0, #0 + rsb hddy, hdy0, #0 + mov hddx, hddx, asr hs + mov hddy, hddy, asr hs + + add xpos, vx0, #(FRAME_WIDTH >> 1) + add ypos, vy0, #(FRAME_HEIGHT >> 1) + mov xpos, vx0, lsl #16 + mov ypos, vy0, lsl #16 + + stmia face, {dataPtr, plutPtr, xpos, ypos, hdx0, hdy0, vdx0, vdy0, hddx, hddy, pixc} + + b loop + +done add sp, sp, #SP_SIZE + ldmfd sp!, {r4-r11, pc} + END diff --git a/src/platform/3do/main.cpp b/src/platform/3do/main.cpp new file mode 100644 index 00000000..f49e84aa --- /dev/null +++ b/src/platform/3do/main.cpp @@ -0,0 +1,367 @@ +const void* TRACKS_IMA; +const void* TITLE_SCR; // TODO + +void* RAM_LVL; +void* RAM_TEX; +void* RAM_CEL; +void* RAM_SND; + +#include "game.h" + +Item irqVBL; +Item irqVRAM; +Item irqTimer; + +ScreenContext screen; +int32 screenPage; +Item screenItem; +int32 fps; +int32 g_timer; + +const int chars[10][20] = { + { + 0,1,1,0, + 1,0,0,1, + 1,0,0,1, + 1,0,0,1, + 0,1,1,0 + }, { + 0,0,1,0, + 0,1,1,0, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0 + }, { + 0,1,1,0, + 1,0,0,1, + 0,0,1,1, + 0,1,0,0, + 1,1,1,1 + }, { + 1,1,1,0, + 0,0,0,1, + 0,0,1,0, + 1,0,0,1, + 0,1,1,0 + }, { + 0,0,0,1, + 0,0,1,1, + 0,1,0,1, + 1,1,1,1, + 0,0,0,1 + }, { + 1,1,1,1, + 1,0,0,0, + 1,1,1,0, + 0,0,0,1, + 1,1,1,0 + }, { + 0,1,1,0, + 1,0,0,0, + 1,1,1,0, + 1,0,0,1, + 0,1,1,0 + }, { + 1,1,1,1, + 0,0,0,1, + 0,0,1,0, + 0,1,0,0, + 0,1,0,0 + }, { + 0,1,1,0, + 1,0,0,1, + 0,1,1,0, + 1,0,0,1, + 0,1,1,0 + }, { + 0,1,1,0, + 1,0,0,1, + 0,1,1,1, + 0,0,0,1, + 0,1,1,0 + } +}; + +void drawChar(int32 x, int32 y, int32 c) +{ + uint16* ptr = (uint16*)screen.sc_Bitmaps[screenPage]->bm_Buffer; + + ptr += y / 2 * 2 * FRAME_WIDTH; + ptr += x * 2; + ptr += y & 1; + + for (int32 i = 0; i < 5; i++) + { + for (int32 j = 0; j < 4; j++) + { + if (chars[c][i * 4 + j]) + { + ptr[j * 2] = 0xFFFF; + } + } + + if ((y + i) & 1) { + ptr += FRAME_WIDTH * 2 - 1; + } else { + ptr += 1; + } + } +} + +void drawInt(int32 x, int32 y, int32 number) +{ + while (number) + { + drawChar(x, y, number % 10); + number /= 10; + x -= 6; + } +} + +void osSetPalette(const uint16* palette) +{ + // +} + +int32 osGetSystemTimeMS() +{ + return GetMSecTime(irqTimer); +} + +bool osSaveSettings() +{ + return false; +} + +bool osLoadSettings() +{ + return false; +} + +bool osCheckSave() +{ + return false; +} + +bool osSaveGame() +{ + return false; +} + +bool osLoadGame() +{ + return false; +} + +void osJoyVibrate(int32 index, int32 L, int32 R) +{ + // +} + +IOInfo clearInfo = { + FLASHWRITE_CMD, IO_QUICK, 0, 0, -1, 0, 0, + { NULL, 0 }, + { NULL, FRAME_WIDTH * FRAME_HEIGHT * 2 } +}; + +X_INLINE void clearFast(int32 page) +{ + clearInfo.ioi_Recv.iob_Buffer = screen.sc_Bitmaps[page]->bm_Buffer; + SendIO(irqVRAM, &clearInfo); +} + +uint32 joyMask; + +void updateInput() +{ + ControlPadEventData event; + event.cped_ButtonBits = 0; + if (GetControlPad(1, 0, &event)) { + joyMask = event.cped_ButtonBits; + } + + keys = 0; + + if (joyMask & ControlUp) keys |= IK_UP; + if (joyMask & ControlRight) keys |= IK_RIGHT; + if (joyMask & ControlDown) keys |= IK_DOWN; + if (joyMask & ControlLeft) keys |= IK_LEFT; + if (joyMask & ControlA) keys |= IK_A; + if (joyMask & ControlB) keys |= IK_B; + if (joyMask & ControlC) keys |= IK_C; + if (joyMask & ControlLeftShift) keys |= IK_L; + if (joyMask & ControlRightShift) keys |= IK_R; + if (joyMask & ControlStart) keys |= IK_START; + if (joyMask & ControlX) keys |= IK_SELECT; +} + +#include +void* readFile(char* fileName, void* buffer, int32 bufferSize) +{ + printf("readFile %s\n", fileName); + + Item f = OpenDiskFile(fileName); + + if (f < 0) { + printf("failed to open file %s\n", fileName); + return NULL; + } + + IOInfo params; + DeviceStatus ds; + + Item req = CreateIOReq(NULL, 0, f, 0); + memset(¶ms, 0, sizeof(IOInfo)); + memset(&ds, 0, sizeof(DeviceStatus)); + params.ioi_Command = CMD_STATUS; + params.ioi_Recv.iob_Buffer = &ds; + params.ioi_Recv.iob_Len = sizeof(DeviceStatus); + + void* ptr = buffer; + + if (DoIO(req, ¶ms) >= 0) + { + int32 size = ds.ds_DeviceBlockCount * ds.ds_DeviceBlockSize; + + if (bufferSize < size) + { + printf("not enough buffer size for %s. %d bytes required!\n", fileName, size); + ptr = NULL; + } + + if (ptr != NULL) + { + memset(¶ms, 0, sizeof(IOInfo)); + params.ioi_Command = CMD_READ; + params.ioi_Recv.iob_Len = size; + params.ioi_Recv.iob_Buffer = ptr; + + if (DoIO(req, ¶ms) < 0) + { + printf("can't get file content!\n"); + ptr = NULL; + } + } + } + + DeleteIOReq(req); + CloseDiskFile(f); + + return ptr; +} + +void* osLoadLevel(const char* name) +{ + char buf[32]; + + sprintf(buf, "data/%s.D", name); + readFile(buf, RAM_LVL, MAX_RAM_LVL); + + sprintf(buf, "data/%s.V", name); + readFile(buf, RAM_TEX, MAX_RAM_TEX); + + return RAM_LVL; +} + +uint32 frame; +uint32 lastFrame; + +int main(int argc, char *argv[]) +{ + printf("OpenLara 3DO\n"); + MemInfo memInfoVRAM; + AvailMem(&memInfoVRAM, MEMTYPE_DRAM); + printf("DRAM: %d\n", memInfoVRAM.minfo_SysFree); + AvailMem(&memInfoVRAM, MEMTYPE_VRAM); + printf("VRAM: %d\n", memInfoVRAM.minfo_SysFree); + + uint32 lastSec = 0; + uint32 frameIndex = 0; + + OpenMathFolio(); + OpenGraphicsFolio(); + OpenAudioFolio(); + InitEventUtility(1, 0, LC_Observer); + CreateBasicDisplay(&screen, DI_TYPE_DEFAULT, 2); + + for (int32 i = 0; i < 2; i++) + { + //SetCEControl(screen.sc_BitmapItems[i], 0xFFFFFFFF, ASCALL); // -5 ms but perf spikes and total blackout :( + //DisableHAVG(screen.sc_BitmapItems[i]); + //DisableVAVG(screen.sc_BitmapItems[i]); + } + + irqVBL = GetVBLIOReq(); + irqVRAM = GetVRAMIOReq(); + irqTimer = GetTimerIOReq(); + + uint8* memVRAM = (uint8*)AllocMem(MAX_RAM_TEX, MEMTYPE_VRAM); + uint8* memDRAM = (uint8*)AllocMem(MAX_RAM_LVL + MAX_RAM_CEL + MAX_RAM_SND, MEMTYPE_DRAM); + + if (!memVRAM) printf("VRAM failed!\n"); + if (!memDRAM) printf("DRAM failed!\n"); + + RAM_TEX = memVRAM; + RAM_LVL = memDRAM; + RAM_CEL = memDRAM + MAX_RAM_LVL; + RAM_SND = memDRAM + MAX_RAM_LVL + MAX_RAM_CEL; + + sndInit(); + + gameInit(gLevelInfo[gLevelID].name); + + AvailMem(&memInfoVRAM, MEMTYPE_DRAM); + printf("DRAM: %d\n", memInfoVRAM.minfo_SysFree); + AvailMem(&memInfoVRAM, MEMTYPE_VRAM); + printf("VRAM: %d\n", memInfoVRAM.minfo_SysFree); + + GetVBLTime(irqVBL, NULL, &lastFrame); + lastFrame /= 2; + lastFrame--; + + while (1) + { + WaitVBL(irqVBL, 1); + clearFast(screenPage); + + updateInput(); + + //GetVBLTime(irqVBL, NULL, &frame); // slower + QueryGraphics(QUERYGRAF_TAG_FIELDCOUNT, &frame); + + if (screen.sc_DisplayType != DI_TYPE_NTSC) + { + frame = frame * 6 / 5; // PAL fix + } + + if (frame/60 != lastSec) { + lastSec = frame/60; + fps = frameIndex; + frameIndex = 0; + } + + frame /= 2; // 30 Hz + +int32 updateTime = osGetSystemTimeMS(); + gameUpdate(frame - lastFrame); +updateTime = osGetSystemTimeMS() - updateTime; + + lastFrame = frame; + + screenItem = screen.sc_ScreenItems[screenPage]; + +int32 renderTime = osGetSystemTimeMS(); + gameRender(); +renderTime = osGetSystemTimeMS() - renderTime; + + drawInt(FRAME_WIDTH - 8, 4 + 16, updateTime); + drawInt(FRAME_WIDTH - 8, 4 + 24, renderTime); + + DisplayScreen(screen.sc_Screens[screenPage], NULL); + screenPage ^= 1; + + frameIndex++; + } + + return 0; +} diff --git a/src/platform/3do/matrixLerp.s b/src/platform/3do/matrixLerp.s new file mode 100644 index 00000000..26fd094a --- /dev/null +++ b/src/platform/3do/matrixLerp.s @@ -0,0 +1,120 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT matrixLerp_asm + +n RN r0 +pmul RN r1 +pdiv RN r2 +m0 RN r3 +m1 RN r4 +m2 RN r5 +n0 RN r6 +n1 RN r7 +n2 RN r12 +m RN lr +tmp RN m0 +divLUT RN m0 + + MACRO + load + ldmia m, {m0, m1, m2} + ldmia n!, {n0, n1, n2} + MEND + + MACRO + store + stmia m!, {m0, m1, m2} + MEND + + MACRO ; a = (a + b) / 2 + _1_2 + load + add m0, m0, n0 + add m1, m1, n1 + add m2, m2, n2 + mov m0, m0, asr #1 + mov m1, m1, asr #1 + mov m2, m2, asr #1 + store + MEND + + MACRO ; a = a + (b - a) / 4 + _1_4 + load + sub n0, n0, m0 + sub n1, n1, m1 + sub n2, n2, m2 + add m0, m0, n0, asr #2 + add m1, m1, n1, asr #2 + add m2, m2, n2, asr #2 + store + MEND + + MACRO ; a = b - (b - a) / 4 + _3_4 + load + sub m0, n0, m0 + sub m1, n1, m1 + sub m2, n2, m2 + sub m0, n0, m0, asr #2 + sub m1, n1, m1, asr #2 + sub m2, n2, m2, asr #2 + store + MEND + + MACRO ; a = a + (b - a) * mul / div + _X_Y + load + sub n0, n0, m0 + sub n1, n1, m1 + sub n2, n2, m2 + mul n0, pmul, n0 + mul n1, pmul, n1 + mul n2, pmul, n2 + add m0, m0, n0, asr #8 + add m1, m1, n1, asr #8 + add m2, m2, n2, asr #8 + store + MEND + + MACRO + lerp $func + $func ; e00, e10, e20 + $func ; e01, e11, e21 + $func ; e02, e12, e22 + MEND + +matrixLerp_asm + stmfd sp!, {r4-r7, lr} + ldr m, =gMatrixPtr + ldr m, [m] +check_2 + cmp pdiv, #2 + beq m1_d2 +check_4 + cmp pdiv, #4 + bne mX_dY + cmp pmul, #1 + beq m1_d4 + cmp pmul, #2 + beq m1_d2 ; 2/4 = 1/2 +m3_d4 + lerp _3_4 + b done +m1_d4 + lerp _1_4 + b done +m1_d2 + lerp _1_2 + b done +mX_dY + ldr divLUT, =divTable + ldr tmp, [divLUT, pdiv, lsl #2] + mul tmp, pmul, tmp + mov pmul, tmp, asr #8 + lerp _X_Y +done ldmfd sp!, {r4-r7, pc} + END diff --git a/src/platform/3do/matrixPush.s b/src/platform/3do/matrixPush.s new file mode 100644 index 00000000..286b727e --- /dev/null +++ b/src/platform/3do/matrixPush.s @@ -0,0 +1,33 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT matrixPush_asm + +e0 RN r0 +e1 RN r1 +e2 RN r2 +e3 RN r3 +m RN e0 +src RN r12 +dst RN lr + +matrixPush_asm + stmfd sp!, {lr} + ldr m, =gMatrixPtr + ldr src, [m] + add dst, src, #(12 * 4) + str dst, [m] + + ldmia src!, {e0, e1, e2, e3} + stmia dst!, {e0, e1, e2, e3} + + ldmia src!, {e0, e1, e2, e3} + stmia dst!, {e0, e1, e2, e3} + + ldmia src!, {e0, e1, e2, e3} + stmia dst!, {e0, e1, e2, e3} + + ldmfd sp!, {pc} + END diff --git a/src/platform/3do/matrixRotate.s b/src/platform/3do/matrixRotate.s new file mode 100644 index 00000000..b8251f84 --- /dev/null +++ b/src/platform/3do/matrixRotate.s @@ -0,0 +1,245 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + IMPORT phd_sin_asm + + EXPORT matrixRotateX_asm + EXPORT matrixRotateY_asm + EXPORT matrixRotateZ_asm + EXPORT matrixRotateYQ_asm + EXPORT matrixRotateYXZ_asm + + MACRO + sincos $angle, $sin, $cos + ldr $sin, =gSinCosTable + ldr $sin, [$sin, $angle, lsl #2] + mov $cos, $sin, lsl #16 + mov $cos, $cos, asr #16 + mov $sin, $sin, asr #16 + MEND + + MACRO + rotxy $x, $y, $sin, $cos, $t + mul $t, $y, $cos + mla $t, $x, $sin, $t + mul $x, $cos, $x + rsb $y, $y, #0 + mla $x, $y, $sin, $x + mov $y, $t, asr #FIXED_SHIFT + mov $x, $x, asr #FIXED_SHIFT + MEND + +angle RN r0 +ex0 RN r1 +ex1 RN r2 +ex2 RN r3 +ey0 RN r4 +ey1 RN r5 +ey2 RN r6 +s RN r7 +c RN r12 +v RN lr +m RN angle + +matrixRotateX_asm + stmfd sp!, {r4-r7, lr} + + mov angle, angle, lsl #16 + mov angle, angle, lsr #20 + + sincos angle, s, c + + ldr m, =gMatrixPtr + ldr m, [m] + add m, m, #(3 * 4) + + ldmia m, {ex0, ex1, ex2, ey0, ey1, ey2} + + rotxy ey0, ex0, s, c, v + rotxy ey1, ex1, s, c, v + rotxy ey2, ex2, s, c, v + + stmia m, {ex0, ex1, ex2, ey0, ey1, ey2} + + ldmfd sp!, {r4-r7, pc} + + +matrixRotateY_asm + stmfd sp!, {r4-r7, lr} + + mov angle, angle, lsl #16 + mov angle, angle, lsr #20 + + sincos angle, s, c + + ldr m, =gMatrixPtr + ldr m, [m] + + ldmia m!, {ex0, ex1, ex2} + add m, m, #(3 * 4) + ldmia m!, {ey0, ey1, ey2} + + rotxy ex0, ey0, s, c, v + rotxy ex1, ey1, s, c, v + rotxy ex2, ey2, s, c, v + + stmdb m!, {ey0, ey1, ey2} + sub m, m, #(3 * 4) + stmdb m!, {ex0, ex1, ex2} + + ldmfd sp!, {r4-r7, pc} + + +matrixRotateZ_asm + stmfd sp!, {r4-r7, lr} + + mov angle, angle, lsl #16 + mov angle, angle, lsr #20 + + sincos angle, s, c + + ldr m, =gMatrixPtr + ldr m, [m] + + ldmia m, {ex0, ex1, ex2, ey0, ey1, ey2} + + rotxy ey0, ex0, s, c, v + rotxy ey1, ex1, s, c, v + rotxy ey2, ex2, s, c, v + + stmia m, {ex0, ex1, ex2, ey0, ey1, ey2} + + ldmfd sp!, {r4-r7, pc} + + +angleX RN r0 +angleY RN r1 +angleZ RN r2 +e00 RN r3 +e10 RN r4 +e20 RN r5 +e01 RN r6 +e11 RN r7 +e21 RN r8 +e02 RN r9 +e12 RN r10 +e22 RN r11 +tmp RN r12 +sinX RN lr +sinY RN sinX +sinZ RN sinX +cosX RN angleX +cosY RN angleY +cosZ RN angleZ +mask RN tmp +mm RN tmp + +matrixRotateYXZ_asm + mov mask, #0xFF + orr mask, mask, #0xF00 ; mask = 0xFFF + + and angleX, mask, angleX, lsr #4 + and angleY, mask, angleY, lsr #4 + and angleZ, mask, angleZ, lsr #4 + + orr mask, angleX, angleY + orrs mask, mask, angleZ + moveq pc, lr + + stmfd sp!, {r4-r11, lr} + + ldr mm, =gMatrixPtr + ldr mm, [mm] + ldmia mm, {e00, e10, e20, e01, e11, e21, e02, e12, e22} + +_rotY cmp angleY, #0 + beq _rotX + + sincos angleY, sinY, cosY + + rotxy e00, e02, sinY, cosY, tmp + rotxy e10, e12, sinY, cosY, tmp + rotxy e20, e22, sinY, cosY, tmp + +_rotX cmp angleX, #0 + beq _rotZ + + sincos angleX, sinX, cosX + + rotxy e02, e01, sinX, cosX, tmp + rotxy e12, e11, sinX, cosX, tmp + rotxy e22, e21, sinX, cosX, tmp + +_rotZ cmp angleZ, #0 + beq _done + + sincos angleZ, sinZ, cosZ + + rotxy e01, e00, sinZ, cosZ, tmp + rotxy e11, e10, sinZ, cosZ, tmp + rotxy e21, e20, sinZ, cosZ, tmp + +_done ldr mm, =gMatrixPtr + ldr mm, [mm] + stmia mm, {e00, e10, e20, e01, e11, e21, e02, e12, e22} + ldmfd sp!, {r4-r11, pc} + +q RN r0 +mx RN r1 +my RN r2 + +mx0 RN r3 +mx1 RN r4 +mx2 RN r5 + +my0 RN q +my1 RN r12 +my2 RN lr + +matrixRotateYQ_asm + cmp q, #2 + moveq pc, lr + + stmfd sp!, {r4-r5, lr} + + ldr mx, =gMatrixPtr + ldr mx, [mx] + add my, mx, #(6 * 4) + + ldmia mx, {mx0, mx1, mx2} + + cmp q, #1 + beq q_1 + cmp q, #3 + beq q_3 + +q_0 ldmia my, {my0, my1, my2} + rsb mx0, mx0, #0 + rsb mx1, mx1, #0 + rsb mx2, mx2, #0 + rsb my0, my0, #0 + rsb my1, my1, #0 + rsb my2, my2, #0 + stmia mx, {mx0, mx1, mx2} + stmia my, {my0, my1, my2} + ldmfd sp!, {r4-r5, pc} + +q_1 ldmia my, {my0, my1, my2} + stmia mx, {my0, my1, my2} + rsb mx0, mx0, #0 + rsb mx1, mx1, #0 + rsb mx2, mx2, #0 + stmia my, {mx0, mx1, mx2} + ldmfd sp!, {r4-r5, pc} + +q_3 ldmia my, {my0, my1, my2} + stmia my, {mx0, mx1, mx2} + rsb my0, my0, #0 + rsb my1, my1, #0 + rsb my2, my2, #0 + stmia mx, {my0, my1, my2} + ldmfd sp!, {r4-r5, pc} + + END diff --git a/src/platform/3do/matrixSetBasis.s b/src/platform/3do/matrixSetBasis.s new file mode 100644 index 00000000..759190a9 --- /dev/null +++ b/src/platform/3do/matrixSetBasis.s @@ -0,0 +1,32 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT matrixSetBasis_asm + +dst RN r0 +src RN r1 + +e0 RN r2 +e1 RN r3 +e2 RN r12 + +matrixSetBasis_asm + ; column-major + ; e0 e1 e2 + ; e0 e1 e2 + ; e0 e1 e2 + ; x y z + + ldmia src!, {e0, e1, e2} + stmia dst!, {e0, e1, e2} + + ldmia src!, {e0, e1, e2} + stmia dst!, {e0, e1, e2} + + ldmia src!, {e0, e1, e2} + stmia dst!, {e0, e1, e2} + + mov pc, lr + END diff --git a/src/platform/3do/matrixSetIdentity.s b/src/platform/3do/matrixSetIdentity.s new file mode 100644 index 00000000..58e789d6 --- /dev/null +++ b/src/platform/3do/matrixSetIdentity.s @@ -0,0 +1,33 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT matrixSetIdentity_asm + +e0 RN r0 +e1 RN r1 +e2 RN r2 +e3 RN r3 +m RN r12 + +matrixSetIdentity_asm + ldr m, =gMatrixPtr + ldr m, [m] + mov e0, #0x4000 + mov e1, #0 + mov e2, #0 + mov e3, #0 + + ; column-major + ; e0 e1 e2 + ; e3 e0 e1 + ; e2 e3 e0 + ; e1 e2 e3 + + stmia m!, {e0, e1, e2, e3} + stmia m!, {e0, e1, e2, e3} + stmia m!, {e0, e1, e2, e3} + + mov pc, lr + END diff --git a/src/platform/3do/matrixTranslate.s b/src/platform/3do/matrixTranslate.s new file mode 100644 index 00000000..da25ed3c --- /dev/null +++ b/src/platform/3do/matrixTranslate.s @@ -0,0 +1,108 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT matrixTranslateRel_asm + EXPORT matrixTranslateAbs_asm + EXPORT matrixTranslateSet_asm + +x RN r0 +y RN r1 +z RN r2 +m RN r3 +e0 RN r4 +e1 RN r5 +e2 RN r6 +dx RN r7 +dy RN r12 +dz RN lr + +matrixTranslateRel_asm + stmfd sp!, {r4-r7, lr} + + ldr m, =gMatrixPtr + ldr m, [m] + add m, m, #(12 * 4) + + ldmdb m!, {dx, dy, dz} + + ldmdb m!, {e0, e1, e2} + mla dx, e0, z, dx + mla dy, e1, z, dy + mla dz, e2, z, dz + + ldmdb m!, {e0, e1, e2} + mla dx, e0, y, dx + mla dy, e1, y, dy + mla dz, e2, y, dz + + ldmdb m!, {e0, e1, e2} + mla dx, e0, x, dx + mla dy, e1, x, dy + mla dz, e2, x, dz + + add m, m, #(9 * 4) + stmia m!, {dx, dy, dz} + + ldmfd sp!, {r4-r7, pc} + + +matrixTranslateAbs_asm + stmfd sp!, {r4-r7, lr} + + ldr m, =gCameraViewPos + ldmia m, {e0, e1, e2} + sub x, x, e0 + sub y, y, e1 + sub z, z, e2 + + ldr m, =gMatrixPtr + ldr m, [m] + + ldmia m!, {e0, e1, e2} + mul dx, e0, x + mul dy, e1, x + mul dz, e2, x + + ldmia m!, {e0, e1, e2} + mla dx, e0, y, dx + mla dy, e1, y, dy + mla dz, e2, y, dz + + ldmia m!, {e0, e1, e2} + mla dx, e0, z, dx + mla dy, e1, z, dy + mla dz, e2, z, dz + + stmia m!, {dx, dy, dz} + + ldmfd sp!, {r4-r7, pc} + + +matrixTranslateSet_asm + stmfd sp!, {r4-r7, lr} + + ldr m, =gMatrixPtr + ldr m, [m] + + ldmia m!, {e0, e1, e2} + mul dx, e0, x + mul dy, e1, x + mul dz, e2, x + + ldmia m!, {e0, e1, e2} + mla dx, e0, y, dx + mla dy, e1, y, dy + mla dz, e2, y, dz + + ldmia m!, {e0, e1, e2} + mla dx, e0, z, dx + mla dy, e1, z, dy + mla dz, e2, z, dz + + stmia m, {dx, dy, dz} + + ldmfd sp!, {r4-r7, pc} + + END diff --git a/src/platform/3do/projectVertices.s b/src/platform/3do/projectVertices.s new file mode 100644 index 00000000..48f59573 --- /dev/null +++ b/src/platform/3do/projectVertices.s @@ -0,0 +1,93 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT projectVertices_asm + +vCount RN r0 +mx RN vCount +my RN r1 +mz RN r2 +x RN r3 +y RN r4 +z RN r5 +dz RN r6 +minXY RN r7 +maxXY RN r8 +minZ RN r9 +maxZ RN r12 +m RN dz +vp RN dz +last RN r10 +vertex RN r11 +divLUT RN lr + +projectVertices_asm + stmfd sp!, {r4-r11, lr} + + ldr m, =gMatrixPtr + ldr m, [m] + ldr vertex, =gVertices + mov r3, vCount + add vCount, vCount, vCount, lsl #1 + add last, vertex, vCount, lsl #2 ; last = gVertices + vCount * 12 + + mov r2, m + mov r1, vertex + mov r0, vertex + swi MulManyVec3Mat33_F16 + + add m, m, #36 ; skip 3x3 matrix part + ldmia m, {mx, my, mz} ; get view space offset from matrix + ldr divLUT, =divTable + ldr vp, =viewportRel + ldmia vp, {minXY, maxXY} + + mov minZ, #VIEW_MIN + mov maxZ, #VIEW_MAX + + mov my, my, asr #FIXED_SHIFT + mov my, my, lsl #PROJ_SHIFT + + mov mz, mz, asr #FIXED_SHIFT + mov mz, mz, lsl #CLIP_SHIFT + +loop ldmia vertex, {x, y, z} ; read transformed vertex + + add x, x, mx, asr #FIXED_SHIFT + add y, my, y, lsl #PROJ_SHIFT ; extra shift for min/max cmp with hi half-word + add z, mz, z, lsl #CLIP_SHIFT ; add some bits for the clipping flags + + ; check z clipping + cmp z, minZ + orrle z, minZ, #CLIP_NEAR + cmp z, maxZ + orrge z, maxZ, #CLIP_FAR + + ; projection + mov dz, z, lsr #(PROJ_SHIFT + CLIP_SHIFT) ; z is positive + ldr dz, [divLUT, dz, lsl #2] + mul x, dz, x + mul y, dz, y + mov x, x, asr #(16 - PROJ_SHIFT) + ; keep y shifted by 16 for min/max cmp + + ; check xy clipping + cmp x, minXY, asr #16 + orrle z, z, #CLIP_LEFT + cmp y, minXY, lsl #16 + orrle z, z, #CLIP_TOP + cmp x, maxXY, asr #16 + orrge z, z, #CLIP_RIGHT + cmp y, maxXY, lsl #16 + orrge z, z, #CLIP_BOTTOM + + mov y, y, asr #16 + + stmia vertex!, {x, y, z} ; store projected vertex + cmp vertex, last + blt loop + + ldmfd sp!, {r4-r11, pc} + END diff --git a/src/platform/3do/render_cel.cpp b/src/platform/3do/render_cel.cpp new file mode 100644 index 00000000..bc63d518 --- /dev/null +++ b/src/platform/3do/render_cel.cpp @@ -0,0 +1,1192 @@ +#include "common.h" + +//#define DEBUG_CLIPPING +//#define CHECK_LIMITS + +struct Vertex +{ + int32 x, y, z; // for rooms z = (depth << CLIP_SHIFT) | ClipFlags +}; + +uint16* gPalette; // offset to the default or underwater PLUTs + +extern Level level; + +int32 gVerticesCount; +int32 gFacesCount; + +Vertex gVertices[MAX_VERTICES]; + +Face* gFaces; // MAX_FACES +Face* gFacesBase; + +struct ListOT { + Face* head; + Face* tail; +}; + +ListOT gOT[OT_SIZE]; + +struct ViewportRel { + int32 minXY; + int32 maxXY; +}; + +ViewportRel viewportRel; + +#define SHADOW_OPACITY 3 // 50% +#define MIP_DIST (1024 * 5) + +extern Item screenItem; + +#define FACE_CCW (1 << 31) +#define FACE_MIP_SHIFT 11 +#define FACE_TEXTURE 0x07FF + + +#define F16_SHIFT 2 // special f14 to f16 shift for the math co-processor + +#define DUP16(value) value | (value << 16) + +enum ShadeValue +{ + SHADE_SHADOW = PPMPC_1S_CFBD | PPMPC_SF_8 | PPMPC_2S_PDC | (SHADOW_OPACITY << PPMPC_MF_SHIFT), // transparent + SHADE_1 = DUP16( PPMPC_MF_1 | PPMPC_SF_8 | PPMPC_2D_2 ), // 1/16 + SHADE_2 = DUP16( PPMPC_MF_2 | PPMPC_SF_8 | PPMPC_2D_2 ), // 2/16 + SHADE_3 = DUP16( PPMPC_MF_3 | PPMPC_SF_8 | PPMPC_2D_2 ), // 3/16 + SHADE_4 = DUP16( PPMPC_MF_4 | PPMPC_SF_8 | PPMPC_2D_2 ), // 4/16 + SHADE_5 = DUP16( PPMPC_MF_5 | PPMPC_SF_8 | PPMPC_2D_2 ), // 5/16 + SHADE_6 = DUP16( PPMPC_MF_6 | PPMPC_SF_8 | PPMPC_2D_2 ), // 6/16 + SHADE_7 = DUP16( PPMPC_MF_7 | PPMPC_SF_8 | PPMPC_2D_2 ), // 7/16 + SHADE_8 = DUP16( PPMPC_MF_8 | PPMPC_SF_8 | PPMPC_2D_2 ), // 8/16 + SHADE_9 = DUP16( PPMPC_MF_1 | PPMPC_SF_8 | PPMPC_2D_2 | PPMPC_2S_PDC ), // 9/16 + SHADE_10 = DUP16( PPMPC_MF_2 | PPMPC_SF_8 | PPMPC_2D_2 | PPMPC_2S_PDC ), // 10/16 + SHADE_11 = DUP16( PPMPC_MF_3 | PPMPC_SF_8 | PPMPC_2D_2 | PPMPC_2S_PDC ), // 11/16 + SHADE_12 = DUP16( PPMPC_MF_4 | PPMPC_SF_8 | PPMPC_2D_2 | PPMPC_2S_PDC ), // 12/16 + SHADE_13 = DUP16( PPMPC_MF_5 | PPMPC_SF_8 | PPMPC_2D_2 | PPMPC_2S_PDC ), // 13/16 + SHADE_14 = DUP16( PPMPC_MF_6 | PPMPC_SF_8 | PPMPC_2D_2 | PPMPC_2S_PDC ), // 14/16 + SHADE_15 = DUP16( PPMPC_MF_7 | PPMPC_SF_8 | PPMPC_2D_2 | PPMPC_2S_PDC ), // 15/16 + SHADE_16 = DUP16( PPMPC_MF_8 | PPMPC_SF_8 ), // 1 + SHADE_17 = DUP16( PPMPC_MF_1 | PPMPC_SF_16 | PPMPC_2S_PDC ), // 1 + 1/16 + SHADE_18 = DUP16( PPMPC_MF_2 | PPMPC_SF_16 | PPMPC_2S_PDC ), // 1 + 2/16 + SHADE_19 = DUP16( PPMPC_MF_3 | PPMPC_SF_16 | PPMPC_2S_PDC ), // 1 + 3/16 + SHADE_20 = DUP16( PPMPC_MF_4 | PPMPC_SF_16 | PPMPC_2S_PDC ), // 1 + 4/16 + SHADE_21 = DUP16( PPMPC_MF_5 | PPMPC_SF_16 | PPMPC_2S_PDC ), // 1 + 5/16 + SHADE_22 = DUP16( PPMPC_MF_6 | PPMPC_SF_16 | PPMPC_2S_PDC ), // 1 + 6/16 + SHADE_23 = DUP16( PPMPC_MF_7 | PPMPC_SF_16 | PPMPC_2S_PDC ), // 1 + 7/16 + SHADE_24 = DUP16( PPMPC_MF_8 | PPMPC_SF_16 | PPMPC_2S_PDC ) // 1 + 8/16 +}; + +extern "C" const uint32 shadeTable[32] = { + SHADE_24, + SHADE_24, + SHADE_23, + SHADE_23, + SHADE_22, + SHADE_22, + SHADE_21, + SHADE_21, + SHADE_20, + SHADE_20, + SHADE_19, + SHADE_19, + SHADE_18, + SHADE_18, + SHADE_17, + SHADE_17, + SHADE_16, + SHADE_15, + SHADE_14, + SHADE_13, + SHADE_12, + SHADE_11, + SHADE_10, + SHADE_9, + SHADE_8, + SHADE_7, + SHADE_6, + SHADE_5, + SHADE_4, + SHADE_3, + SHADE_2, + SHADE_1 +}; + +extern "C" const MeshQuad gShadowQuads[] = { + 0, (0 | (1 << 8) | (2 << 16) | (7 << 24)), + 0, (7 | (2 << 8) | (3 << 16) | (6 << 24)), + 0, (6 | (3 << 8) | (4 << 16) | (5 << 24)), +}; + +void renderInit() +{ + gPalette = (uint16*)RAM_TEX; + gFaces = (Face*)RAM_CEL; + gFacesBase = gFaces; + + Face* face = gFaces; + for (int32 i = 0; i < MAX_FACES; i++, face++) + { + // set preamble for CCB_CCBPRE flag + face->ccb_PRE0 = PRE0_BGND | PRE0_LINEAR | PRE0_BPP_16; + face->ccb_PRE1 = PRE1_TLLSB_PDC0; + } +} + +void setViewport(const RectMinMax &vp) +{ + viewport = vp; + + int32 minX = vp.x0 - (FRAME_WIDTH >> 1); + int32 minY = vp.y0 - (FRAME_HEIGHT >> 1); + int32 maxX = vp.x1 - (FRAME_WIDTH >> 1); + int32 maxY = vp.y1 - (FRAME_HEIGHT >> 1); + + viewportRel.minXY = (minX << 16) | (minY & 0xFFFF); + viewportRel.maxXY = (maxX << 16) | (maxY & 0xFFFF); +} + +void setPaletteIndex(int32 index) +{ + uint32 paletteOffset = *(uint32*)RAM_TEX; + gPalette = (uint16*)(intptr_t(RAM_TEX) + paletteOffset + index * level.tilesCount * sizeof(uint16) * 16); +} + +X_INLINE int32 cross(const Vertex *a, const Vertex *b, const Vertex *c) +{ + return (b->x - a->x) * (c->y - a->y) - + (c->x - a->x) * (b->y - a->y); +} + +enum ClipFlags { + CLIP_SHIFT = 8, + CLIP_MASK = (1 << CLIP_SHIFT) - 1, + CLIP_LEFT = 1 << 0, + CLIP_RIGHT = 1 << 1, + CLIP_TOP = 1 << 2, + CLIP_BOTTOM = 1 << 3, + CLIP_FAR = 1 << 4, + CLIP_NEAR = 1 << 5 +}; + +X_INLINE Face* faceAdd(int32 depth) +{ + Face* face = gFacesBase++; + + if (gOT[depth].head) { + face->ccb_NextPtr = gOT[depth].head; + } else { + gOT[depth].tail = face; + } + + gOT[depth].head = face; + + return face; +} + +X_INLINE void ccbSetTexture(uint32 flags, Face* face, const Texture* texture) +{ + face->ccb_Flags = + CCB_NPABS | + CCB_SPABS | + CCB_PPABS | + CCB_LDSIZE | + CCB_LDPRS | + CCB_LDPPMP | + CCB_LDPLUT | + CCB_YOXY | + CCB_ACSC | CCB_ALSC | + CCB_ACW | CCB_ACCW | + CCB_ACE | + CCB_NOBLK | + (flags >> (8 + FACE_MIP_SHIFT + FACE_MIP_SHIFT) << 5); // set CCB_BGND (0x20 == 1 << 5) + + face->ccb_SourcePtr = (CelData*)texture->data; + face->ccb_PLUTPtr = (uint8*)gPalette + (texture->shift >> 16); +} + +X_INLINE void ccbSetColor(uint32 flags, Face* face) +{ + face->ccb_Flags = + CCB_NPABS | + CCB_SPABS | + CCB_PPABS | + CCB_LDSIZE | + CCB_LDPRS | + CCB_LDPPMP | + CCB_CCBPRE | // use the preamble words set in renderInit + CCB_YOXY | + CCB_ACSC | CCB_ALSC | + CCB_ACW | CCB_ACCW | + CCB_ACE | + CCB_NOBLK | + CCB_BGND; + + face->ccb_SourcePtr = (CelData*)&gPalette[flags & 0xFF]; +} + +#ifdef USE_ASM + #define unpackRoom unpackRoom_asm // -53% + #define unpackMesh unpackMesh_asm // -48% + #define projectVertices projectVertices_asm // -18% + #define faceAddRoomQuads faceAddRoomQuads_asm // -46% + #define faceAddRoomTriangles faceAddRoomTriangles_asm // -30% + #define faceAddMeshQuads faceAddMeshQuads_asm // -36% + #define faceAddMeshTriangles faceAddMeshTriangles_asm // -38% + #define faceAddMeshQuadsFlat faceAddMeshQuadsFlat_asm // -28% + #define faceAddMeshTrianglesFlat faceAddMeshTrianglesFlat_asm // -35% + + extern "C" { + void unpackRoom_asm(const RoomVertex* vertices, int32 vCount); + void unpackMesh_asm(const MeshVertex* vertices, int32 vCount); + void projectVertices_asm(int32 vCount); + void faceAddRoomQuads_asm(const RoomQuad* polys, int32 count); + void faceAddRoomTriangles_asm(const RoomTriangle* polys, int32 count); + void faceAddMeshQuads_asm(const MeshQuad* polys, int32 count, uint32 shade); + void faceAddMeshTriangles_asm(const MeshTriangle* polys, int32 count, uint32 shade); + void faceAddMeshQuadsFlat_asm(const MeshQuad* polys, int32 count, uint32 shade); + void faceAddMeshTrianglesFlat_asm(const MeshTriangle* polys, int32 count, uint32 shade); + } +#else + #define unpackRoom unpackRoom_c + #define unpackMesh unpackMesh_c + #define projectVertices projectVertices_c + #define faceAddRoomQuads faceAddRoomQuads_c + #define faceAddRoomTriangles faceAddRoomTriangles_c + #define faceAddMeshQuads faceAddMeshQuads_c + #define faceAddMeshTriangles faceAddMeshTriangles_c + #define faceAddMeshQuadsFlat faceAddMeshQuadsFlat_c + #define faceAddMeshTrianglesFlat faceAddMeshTrianglesFlat_c + +void unpackRoom_c(const RoomVertex* vertices, int32 vCount) +{ + Vertex* res = gVertices; + + uint32 *v32 = (uint32*)vertices; + + for (int32 i = 0; i < vCount; i += 4) + { + uint32 n0 = *v32++; + uint32 n1 = *v32++; + + res->x = (n0 << 12) & 0x1F000; + res->y = (n0 << 5) & 0xFC00; + res->z = (n0 << 1) & 0x1F000; + res++; + + res->x = (n0 >> 4) & 0x1F000; + res->y = (n0 >> 11) & 0xFC00; + res->z = (n0 >> 15) & 0x1F000; + res++; + + res->x = (n1 << 12) & 0x1F000; + res->y = (n1 << 5) & 0xFC00; + res->z = (n1 << 1) & 0x1F000; + res++; + + res->x = (n1 >> 4) & 0x1F000; + res->y = (n1 >> 11) & 0xFC00; + res->z = (n1 >> 15) & 0x1F000; + res++; + } +} + +void unpackMesh_c(const MeshVertex* vertices, int32 vCount) +{ + uint32 *v32 = (uint32*)vertices; + + Vertex* res = gVertices; + for (int32 i = 0; i < vCount; i += 2) + { + uint32 n0 = *v32++; + uint32 n1 = *v32++; + uint32 n2 = *v32++; + + res->x = int16(n0 >> 16); + res->y = int16(n0); + res->z = int16(n1 >> 16); + res++; + + res->x = int16(n1); + res->y = int16(n2 >> 16); + res->z = int16(n2); + res++; + } +} + +void projectVertices_c(int32 vCount) +{ + Vertex* v = gVertices; + Vertex* last = gVertices + vCount; + + Matrix &m = matrixGet(); + int32 mx = m.e03 >> FIXED_SHIFT; + int32 my = m.e13 >> FIXED_SHIFT; + int32 mz = m.e23 >> FIXED_SHIFT; + + MulManyVec3Mat33_F16((vec3f16*)v, (vec3f16*)v, *(mat33f16*)&m, vCount); + + do + { + int32 x = v->x + mx; + int32 y = v->y + my; + int32 z = v->z + mz; + + int32 clip = 0; + + if (z <= (VIEW_MIN_F >> FIXED_SHIFT)) { + z = (VIEW_MIN_F >> FIXED_SHIFT); + clip = CLIP_NEAR; + } else if (z >= (VIEW_MAX_F >> FIXED_SHIFT)) { + z = (VIEW_MAX_F >> FIXED_SHIFT); + clip = CLIP_FAR; + } + + PERSPECTIVE(x, y, z); + + // Y is shifted left by 16 + y <<= 16; + + int32 vMinXY = viewportRel.minXY; + int32 vMaxXY = viewportRel.maxXY; + + if (x < (vMinXY >> 16)) clip |= CLIP_LEFT; + if (y < (vMinXY << 16)) clip |= CLIP_TOP; + if (x > (vMaxXY >> 16)) clip |= CLIP_RIGHT; + if (y > (vMaxXY << 16)) clip |= CLIP_BOTTOM; + + v->x = x; + v->y = y >> 16; + v->z = (z << CLIP_SHIFT) | clip; + v++; + } while (v < last); +} + +X_INLINE void ccbMap4(Face* f, const Vertex* v0, const Vertex* v1, const Vertex* v2, const Vertex* v3, uint32 shift) +{ + int32 x1 = v1->x; + int32 y1 = v1->y; + int32 x3 = v3->x; + int32 y3 = v3->y; + int32 x2 = v2->x; + int32 y2 = v2->y; + int32 x0 = v0->x; + int32 y0 = v0->y; + + uint32 ws = shift & 0xFF; + uint32 hs = (shift >> 8) & 0xFF; + + int32 hdx0 = (x1 - x0) << ws; + int32 hdy0 = (y1 - y0) << ws; + int32 hdx1 = (x2 - x3) << ws; + int32 hdy1 = (y2 - y3) << ws; + int32 vdx0 = (x3 - x0) << hs; + int32 vdy0 = (y3 - y0) << hs; + + hs = 16 - hs; + int32 hddx = (hdx1 - hdx0) >> hs; + int32 hddy = (hdy1 - hdy0) >> hs; + + f->ccb_XPos = (x0 + (FRAME_WIDTH >> 1)) << 16; + f->ccb_YPos = (y0 + (FRAME_HEIGHT >> 1)) << 16; + f->ccb_HDX = hdx0; + f->ccb_HDY = hdy0; + f->ccb_VDX = vdx0; + f->ccb_VDY = vdy0; + f->ccb_HDDX = hddx; + f->ccb_HDDY = hddy; + +#ifdef DEBUG_CLIPPING + f->ccb_PIXC = SHADE_SHADOW; +#endif +} + +X_INLINE void ccbMap3(Face* f, const Vertex* v0, const Vertex* v1, const Vertex* v2, uint32 shift) +{ + int32 x0 = v0->x; + int32 y0 = v0->y; + int32 x1 = v1->x; + int32 y1 = v1->y; + int32 x2 = v2->x; + int32 y2 = v2->y; + + uint32 ws = shift & 0xFF; + uint32 hs = (shift >> 8) & 0xFF; + + int32 hdx0 = (x1 - x0) << ws; + int32 hdy0 = (y1 - y0) << ws; + int32 vdx0 = (x2 - x0) << hs; + int32 vdy0 = (y2 - y0) << hs; + + f->ccb_XPos = (x0 + (FRAME_WIDTH >> 1)) << 16; + f->ccb_YPos = (y0 + (FRAME_HEIGHT >> 1)) << 16; + f->ccb_HDX = hdx0; + f->ccb_HDY = hdy0; + f->ccb_VDX = vdx0; + f->ccb_VDY = vdy0; + + hs = 16 - hs; + f->ccb_HDDX = -hdx0 >> hs; + f->ccb_HDDY = -hdy0 >> hs; + +#ifdef DEBUG_CLIPPING + f->ccb_PIXC = SHADE_SHADOW; +#endif +} + +#define DEPTH_Q_AVG(z0,z1,z2,z3) ((z0 + z1 + z2 + z3) >> (2 + CLIP_SHIFT + OT_SHIFT)) +#define DEPTH_T_AVG(z0,z1,z2) ((z0 + z1 + z2 + z2) >> (2 + CLIP_SHIFT + OT_SHIFT)) +#define DEPTH_Q_MAX(z0,z1,z2,z3) (X_MAX(z0, X_MAX(z1, X_MAX(z2, z3))) >> (CLIP_SHIFT + OT_SHIFT)) +#define DEPTH_T_MAX(z0,z1,z2) (X_MAX(z0, X_MAX(z1, z2)) >> (CLIP_SHIFT + OT_SHIFT)) + +void faceAddRoomQuads_c(const RoomQuad* polys, int32 count) +{ + for (int32 i = 0; i < count; i++, polys++) + { + uint32 flags = polys->flags; + uint32* indices = (uint32*)polys->indices; + + uint32 i01 = indices[0]; + uint32 i23 = indices[1]; + + uint32 i0 = (i01 >> 16); + uint32 i1 = (i01 & 0xFFFF); + uint32 i2 = (i23 >> 16); + uint32 i3 = (i23 & 0xFFFF); + + const Vertex* v0 = (Vertex*)((uint8*)gVertices + i0); + const Vertex* v1 = (Vertex*)((uint8*)gVertices + i1); + const Vertex* v2 = (Vertex*)((uint8*)gVertices + i2); + const Vertex* v3 = (Vertex*)((uint8*)gVertices + i3); + + uint32 c0 = v0->z; + uint32 c1 = v1->z; + uint32 c2 = v2->z; + uint32 c3 = v3->z; + + if ((c0 & c1 & c2 & c3) & CLIP_MASK) + continue; + + int32 depth = DEPTH_Q_MAX(c0, c1, c2, c3); + + if (cross(v0, v1, v3) <= 0) + continue; + + Face* f = faceAdd(depth); + + int32 fog = X_MAX(0, (depth - (FOG_MIN >> OT_SHIFT)) >> 1); + uint32 intensity = X_MIN(255, fog + ((flags >> (FACE_MIP_SHIFT + FACE_MIP_SHIFT)) & 0xFF)); + + f->ccb_PIXC = shadeTable[intensity >> 3]; + + uint32 texIndex = flags; + if (depth > (MIP_DIST >> OT_SHIFT)) { + texIndex >>= FACE_MIP_SHIFT; + } + const Texture* texture = level.textures + (texIndex & FACE_TEXTURE); + ccbSetTexture(flags, f, texture); + + ccbMap4(f, v0, v1, v2, v3, texture->shift); + } +} + +void faceAddRoomTriangles_c(const RoomTriangle* polys, int32 count) +{ + for (int32 i = 0; i < count; i++, polys++) + { + uint32 flags = polys->flags; + uint32* indices = (uint32*)polys->indices; + + uint32 i01 = indices[0]; + uint32 i23 = indices[1]; + + uint32 i0 = (i01 >> 16); + uint32 i1 = (i01 & 0xFFFF); + uint32 i2 = (i23 >> 16); + + const Vertex* v0 = (Vertex*)((uint8*)gVertices + i0); + const Vertex* v1 = (Vertex*)((uint8*)gVertices + i1); + const Vertex* v2 = (Vertex*)((uint8*)gVertices + i2); + + uint32 c0 = v0->z; + uint32 c1 = v1->z; + uint32 c2 = v2->z; + + if ((c0 & c1 & c2) & CLIP_MASK) + continue; + + int32 depth = DEPTH_T_MAX(c0, c1, c2); + + if (cross(v0, v1, v2) <= 0) + continue; + + Face* f = faceAdd(depth); + + uint32 intensity = (flags >> (FACE_MIP_SHIFT + FACE_MIP_SHIFT)) & 0xFF; + if (depth > (FOG_MIN >> OT_SHIFT)) { + intensity += (depth - (FOG_MIN >> OT_SHIFT)) >> 1; + intensity = X_MIN(intensity, 255); + } + + f->ccb_PIXC = shadeTable[intensity >> 3]; + + uint32 texIndex = flags; + if (depth > (MIP_DIST >> OT_SHIFT)) { + texIndex >>= FACE_MIP_SHIFT; + } + const Texture* texture = level.textures + (texIndex & FACE_TEXTURE); + ccbSetTexture(flags, f, texture); + + ccbMap3(f, v0, v1, v2, texture->shift); + } +} + +void faceAddMeshQuads_c(const MeshQuad* polys, int32 count, uint32 shade) +{ + for (int32 i = 0; i < count; i++, polys++) + { + uint32 indices = polys->indices; + uint32 flags = polys->flags; + + uint32 i0 = indices & 0xFF; indices >>= 8; + uint32 i1 = indices & 0xFF; indices >>= 8; + uint32 i2 = indices & 0xFF; indices >>= 8; + uint32 i3 = indices; + + const Vertex* v0 = gVertices + i0; + const Vertex* v1 = gVertices + i1; + const Vertex* v2 = gVertices + i2; + const Vertex* v3 = gVertices + i3; + + uint32 c0 = v0->z; + uint32 c1 = v1->z; + uint32 c2 = v2->z; + uint32 c3 = v3->z; + + if ((c0 & c1 & c2 & c3) & CLIP_MASK) + continue; + + int32 depth = DEPTH_Q_AVG(c0, c1, c2, c3); + + if ((cross(v0, v1, v3) ^ flags) & FACE_CCW) + continue; + + Face* f = faceAdd(depth); + f->ccb_PIXC = shade; + + const Texture* texture = level.textures + (flags & FACE_TEXTURE); + ccbSetTexture(flags, f, texture); + + ccbMap4(f, v0, v1, v2, v3, texture->shift); + } +} + +void faceAddMeshTriangles_c(const MeshTriangle* polys, int32 count, uint32 shade) +{ + for (int32 i = 0; i < count; i++, polys++) + { + uint32 indices = polys->indices; + uint32 flags = polys->flags; + + uint32 i0 = indices & 0xFF; indices >>= 8; + uint32 i1 = indices & 0xFF; indices >>= 8; + uint32 i2 = indices; + + const Vertex* v0 = gVertices + i0; + const Vertex* v1 = gVertices + i1; + const Vertex* v2 = gVertices + i2; + + uint32 c0 = v0->z; + uint32 c1 = v1->z; + uint32 c2 = v2->z; + + if ((c0 & c1 & c2) & CLIP_MASK) + continue; + + int32 depth = DEPTH_T_AVG(v0->z, v1->z, v2->z); + + if (cross(v0, v1, v2) <= 0) + continue; + + Face* f = faceAdd(depth); + f->ccb_PIXC = shade; + + const Texture* texture = level.textures + (flags & FACE_TEXTURE); + ccbSetTexture(flags, f, texture); + + ccbMap3(f, v0, v1, v2, texture->shift); + } +} + +void faceAddMeshQuadsFlat_c(const MeshQuad* polys, int32 count, uint32 shade) +{ + for (int32 i = 0; i < count; i++, polys++) + { + uint32 indices = polys->indices; + uint32 flags = polys->flags; + + uint32 i0 = indices & 0xFF; indices >>= 8; + uint32 i1 = indices & 0xFF; indices >>= 8; + uint32 i2 = indices & 0xFF; indices >>= 8; + uint32 i3 = indices; + + const Vertex* v0 = gVertices + i0; + const Vertex* v1 = gVertices + i1; + const Vertex* v2 = gVertices + i2; + const Vertex* v3 = gVertices + i3; + + uint32 c0 = v0->z; + uint32 c1 = v1->z; + uint32 c2 = v2->z; + uint32 c3 = v3->z; + + if ((c0 & c1 & c2 & c3) & CLIP_MASK) + continue; + + int32 depth = DEPTH_Q_AVG(v0->z, v1->z, v2->z, v3->z); + + if (cross(v0, v1, v3) <= 0) + continue; + + Face* f = faceAdd(depth); + f->ccb_PIXC = shade; + + ccbSetColor(flags, f); + + ccbMap4(f, v0, v1, v2, v3, 20 | (16 << 8)); + } +} + +void faceAddMeshTrianglesFlat_c(const MeshTriangle* polys, int32 count, uint32 shade) +{ + for (int32 i = 0; i < count; i++, polys++) + { + uint32 indices = polys->indices; + uint32 flags = polys->flags; + + uint32 i0 = indices & 0xFF; indices >>= 8; + uint32 i1 = indices & 0xFF; indices >>= 8; + uint32 i2 = indices; + + const Vertex* v0 = gVertices + i0; + const Vertex* v1 = gVertices + i1; + const Vertex* v2 = gVertices + i2; + + uint32 c0 = v0->z; + uint32 c1 = v1->z; + uint32 c2 = v2->z; + + if ((c0 & c1 & c2) & CLIP_MASK) + continue; + + int32 depth = DEPTH_T_AVG(v0->z, v1->z, v2->z); + + if (cross(v0, v1, v2) <= 0) + continue; + + Face* f = faceAdd(depth); + f->ccb_PIXC = shade; + + ccbSetColor(flags, f); + + ccbMap3(f, v0, v1, v2, 20 | (16 << 8)); + } +} + +int32 boxIsVisible_c(const AABBs* box) +{ + Matrix &m = matrixGet(); + + if ((m.e23 < VIEW_MIN_F) || (m.e23 >= VIEW_MAX_F)) { + return 0; + } + + const int32* ptr = (int32*)box; + + enum { + MAX_X, + MIN_X, + MAX_Y, + MIN_Y, + MAX_Z, + MIN_Z, + MIN_MAX_SIZE + }; + + int32 mm[MIN_MAX_SIZE][3]; + int32 min, max, minX, minY, minZ, maxX, maxY, maxZ; + + #define PROJECT(dx,dy,dz){\ + int32 x, y, z;\ + x = mm[dx][0] + mm[dy][0] + mm[dz][0];\ + y = mm[dx][1] + mm[dy][1] + mm[dz][1];\ + z = mm[dx][2] + mm[dy][2] + mm[dz][2];\ + if (z >= VIEW_MIN_F && z <= VIEW_MAX_F) {\ + z = gDivTable[z >> (FIXED_SHIFT + PROJ_SHIFT)];\ + x = x * z;\ + y = y * z;\ + if (x < rMinX) rMinX = x;\ + if (y < rMinY) rMinY = y;\ + if (x > rMaxX) rMaxX = x;\ + if (y > rMaxY) rMaxY = y;\ + }\ + } + + int32 xx = ptr[0]; + int32 yy = ptr[1]; + int32 zz = ptr[2]; + + // pre-transform min/max Z + min = zz >> 16; + minX = m.e02 * min + m.e03; + minY = m.e12 * min + m.e13; + minZ = m.e22 * min + m.e23; + max = zz << 16 >> 16; + maxX = m.e02 * max + m.e03; + maxY = m.e12 * max + m.e13; + maxZ = m.e22 * max + m.e23; + mm[MAX_Z][0] = maxX >> FIXED_SHIFT; + mm[MAX_Z][1] = maxY >> FIXED_SHIFT; + mm[MAX_Z][2] = maxZ; + mm[MIN_Z][0] = minX >> FIXED_SHIFT; + mm[MIN_Z][1] = minY >> FIXED_SHIFT; + mm[MIN_Z][2] = minZ; + + // pre-transform min/max Y + min = yy >> 16; + minX = m.e01 * min; + minY = m.e11 * min; + minZ = m.e21 * min; + max = yy << 16 >> 16; + maxX = m.e01 * max; + maxY = m.e11 * max; + maxZ = m.e21 * max; + mm[MAX_Y][0] = maxX >> FIXED_SHIFT; + mm[MAX_Y][1] = maxY >> FIXED_SHIFT; + mm[MAX_Y][2] = maxZ; + mm[MIN_Y][0] = minX >> FIXED_SHIFT; + mm[MIN_Y][1] = minY >> FIXED_SHIFT; + mm[MIN_Y][2] = minZ; + + // pre-transform min/max X + min = xx >> 16; + minX = m.e00 * min; + minY = m.e10 * min; + minZ = m.e20 * min; + max = xx << 16 >> 16; + maxX = m.e00 * max; + maxY = m.e10 * max; + maxZ = m.e20 * max; + mm[MAX_X][0] = maxX >> FIXED_SHIFT; + mm[MAX_X][1] = maxY >> FIXED_SHIFT; + mm[MAX_X][2] = maxZ; + mm[MIN_X][0] = minX >> FIXED_SHIFT; + mm[MIN_X][1] = minY >> FIXED_SHIFT; + mm[MIN_X][2] = minZ; + + int32 rMinX = INT_MAX; + int32 rMinY = INT_MAX; + int32 rMaxX = INT_MIN; + int32 rMaxY = INT_MIN; + + PROJECT(MIN_X, MIN_Y, MIN_Z); + PROJECT(MAX_X, MIN_Y, MIN_Z); + PROJECT(MIN_X, MAX_Y, MIN_Z); + PROJECT(MAX_X, MAX_Y, MIN_Z); + PROJECT(MIN_X, MIN_Y, MAX_Z); + PROJECT(MAX_X, MIN_Y, MAX_Z); + PROJECT(MIN_X, MAX_Y, MAX_Z); + PROJECT(MAX_X, MAX_Y, MAX_Z); + + rMinX >>= (16 - PROJ_SHIFT); + rMaxX >>= (16 - PROJ_SHIFT); + + if (rMinX > rMaxX) return 0; + + // rect Y is shifted left by 16 + rMinY <<= PROJ_SHIFT; + rMaxY <<= PROJ_SHIFT; + + int32 vMinXY = viewportRel.minXY; + int32 vMaxXY = viewportRel.maxXY; + + if (rMaxX < (vMinXY >> 16) || + rMaxY < (vMinXY << 16) || + rMinX > (vMaxXY >> 16) || + rMinY > (vMaxXY << 16)) return 0; + + return 1; +} + +int32 sphereIsVisible_c(int32 x, int32 y, int32 z, int32 r) +{ + Matrix &m = matrixGet(); + + //if (abs(x) < r && abs(y) < r && abs(z) < r) + // return 1; + + int32 vx = DP33(m.e00, m.e01, m.e02, x, y, z); + int32 vy = DP33(m.e10, m.e11, m.e12, x, y, z); + int32 vz = DP33(m.e20, m.e21, m.e22, x, y, z); + + if (vz < 0 || vz > VIEW_MAX_F) + return 0; + + x = vx >> FIXED_SHIFT; + y = vy >> FIXED_SHIFT; + z = vz >> FIXED_SHIFT; + + z = PERSPECTIVE_DZ(z); + int32 d = FixedInvU(z); + x = (x * d) >> (16 - PROJ_SHIFT); + y = (y * d) << PROJ_SHIFT; + r = (r * d); + + int32 rMinX = x - (r >> (16 - PROJ_SHIFT)); + int32 rMaxX = x + (r >> (16 - PROJ_SHIFT)); + int32 rMinY = y - (r << PROJ_SHIFT); + int32 rMaxY = y + (r << PROJ_SHIFT); + + int32 vMinXY = viewportRel.minXY; + int32 vMaxXY = viewportRel.maxXY; + + if (rMaxX < (vMinXY >> 16) || + rMaxY < (vMinXY << 16) || + rMinX > (vMaxXY >> 16) || + rMinY > (vMaxXY << 16)) return 0; + + return 1; +} +#endif + +void transformRoom(const Room* room, int32 vCount) +{ + unpackRoom(room->data.vertices, vCount); + projectVertices(vCount); + +#ifdef CHECK_LIMITS + gVerticesCount += vCount; +#endif +} + +void transformMesh(const MeshVertex* vertices, int32 vCount) +{ + if (vCount <= 0) + return; + + unpackMesh(vertices, vCount); + projectVertices(vCount); + +#ifdef CHECK_LIMITS + gVerticesCount += vCount; +#endif +} + +void renderShadow(int32 x, int32 z, int32 sx, int32 sz) +{ +#ifdef CHECK_LIMITS + if (gFacesCount + 3 > MAX_FACES) + return; + gFacesCount += 3; +#endif + x <<= F16_SHIFT; + z <<= F16_SHIFT; + sx <<= F16_SHIFT; + sz <<= F16_SHIFT; + + int32 xns1 = x - sx; + int32 xps1 = x + sx; + int32 xns2 = xns1 - sx; + int32 xps2 = xps1 + sx; + + int32 zns1 = z - sz; + int32 zps1 = z + sz; + int32 zns2 = zns1 - sz; + int32 zps2 = zps1 + sz; + + gVertices[0].x = xns1; gVertices[0].y = 0; gVertices[0].z = zps2; + gVertices[1].x = xps1; gVertices[1].y = 0; gVertices[1].z = zps2; + gVertices[2].x = xps2; gVertices[2].y = 0; gVertices[2].z = zps1; + gVertices[3].x = xps2; gVertices[3].y = 0; gVertices[3].z = zns1; + gVertices[4].x = xps1; gVertices[4].y = 0; gVertices[4].z = zns2; + gVertices[5].x = xns1; gVertices[5].y = 0; gVertices[5].z = zns2; + gVertices[6].x = xns2; gVertices[6].y = 0; gVertices[6].z = zns1; + gVertices[7].x = xns2; gVertices[7].y = 0; gVertices[7].z = zps1; + + projectVertices(8); + faceAddMeshQuadsFlat(gShadowQuads, 3, SHADE_SHADOW); +} + +void renderSprite(int32 vx, int32 vy, int32 vz, int32 vg, int32 index) +{ +#ifdef CHECK_LIMITS + if (gFacesCount >= MAX_FACES) + return; + gFacesCount++; +#endif + + const Matrix &m = matrixGet(); + + vx -= gCameraViewPos.x; + vy -= gCameraViewPos.y; + vz -= gCameraViewPos.z; + + int32 z = DP33(m.e20, m.e21, m.e22, vx, vy, vz); + + if (z < VIEW_MIN_F || z >= VIEW_MAX_F) + { + return; + } + + int32 x = DP33(m.e00, m.e01, m.e02, vx, vy, vz); + int32 y = DP33(m.e10, m.e11, m.e12, vx, vy, vz); + + x >>= FIXED_SHIFT; + y >>= FIXED_SHIFT; + z >>= FIXED_SHIFT; + + PERSPECTIVE(x, y, z); + + const Sprite* sprite = level.sprites + index; + + int32 d = (1 << 20) / z; + int32 x0 = sprite->l * d >> 12; + int32 x1 = sprite->r * d >> 12; + if (x0 == x1) return; + + int32 y0 = sprite->t * d >> 12; + int32 y1 = sprite->b * d >> 12; + if (y0 == y1) return; + + x0 += x; + x1 += x; + y0 += y; + y1 += y; + + int32 vMinXY = viewportRel.minXY; + int32 vMaxXY = viewportRel.maxXY; + + if (x0 > (vMaxXY >> 16) || + x1 < (vMinXY >> 16) || + y0 > (vMaxXY << 16 >> 16) || + y1 < (vMinXY << 16 >> 16)) return; + + int32 depth = X_MAX(0, z - 128) >> OT_SHIFT; // depth hack + + Face* f = faceAdd(depth); + + if (depth > (FOG_MIN >> OT_SHIFT)) { + vg += (depth - (FOG_MIN >> OT_SHIFT)) << 4; + vg = X_MIN(vg, 8191); + } + + vg >>= 8; + f->ccb_PIXC = shadeTable[vg]; + + Texture* texture = level.textures + sprite->texture; + ccbSetTexture(0, f, texture); + + uint32 shift = texture->shift; + uint32 ws = shift & 0xFF; + uint32 hs = shift >> 8; + + f->ccb_HDX = (x1 - x0) << ws; + f->ccb_HDY = 0; + f->ccb_VDX = 0; + f->ccb_VDY = (y1 - y0) << hs; + + f->ccb_XPos = (x0 + (FRAME_WIDTH >> 1)) << 16; + f->ccb_YPos = (y0 + (FRAME_HEIGHT >> 1)) << 16; + + f->ccb_HDDX = 0; + f->ccb_HDDY = 0; +} + +void renderGlyph(int32 vx, int32 vy, int32 index) +{ +#ifdef CHECK_LIMITS + if (gFacesCount >= MAX_FACES) + return; + gFacesCount++; +#endif + + const Sprite* sprite = level.sprites + index; + + Face* f = faceAdd(0); + f->ccb_PIXC = SHADE_16; + + Texture* texture = level.textures + sprite->texture; + ccbSetTexture(0, f, texture); + + uint32 shift = texture->shift; + uint32 ws = shift & 0xFF; + uint32 hs = shift >> 8; + + f->ccb_HDX = (sprite->r - sprite->l) << ws; + f->ccb_HDY = 0; + f->ccb_VDX = 0; + f->ccb_VDY = (sprite->b - sprite->t) << hs; + + f->ccb_XPos = (vx + sprite->l) << 16; + f->ccb_YPos = (vy + sprite->t) << 16; + + f->ccb_HDDX = 0; + f->ccb_HDDY = 0; +} + +void faceAddRoom(const Room* room) +{ + if (room->info->quadsCount) { + faceAddRoomQuads(room->data.quads, room->info->quadsCount); + } + + if (room->info->trianglesCount) { + faceAddRoomTriangles(room->data.triangles, room->info->trianglesCount); + } + +#ifdef CHECK_LIMITS + gFacesCount = gFacesBase - gFaces; +#endif +} + +void faceAddMesh(const MeshQuad* rFaces, const MeshQuad* crFaces, const MeshTriangle* tFaces, const MeshTriangle* ctFaces, int32 rCount, int32 crCount, int32 tCount, int32 ctCount, int32 intensity) +{ + uint32 shade = shadeTable[X_CLAMP((gLightAmbient + intensity) >> 8, 0, 31)]; + + if (rCount) { + faceAddMeshQuads(rFaces, rCount, shade); + } + + if (tCount) { + faceAddMeshTriangles(tFaces, tCount, shade); + } + + if (crCount) { + faceAddMeshQuadsFlat(crFaces, crCount, shade); + } + + if (ctCount) { + faceAddMeshTrianglesFlat(ctFaces, ctCount, shade); + } + +#ifdef CHECK_LIMITS + gFacesCount = gFacesBase - gFaces; +#endif +} + +void flush() +{ + Face* facesHead = NULL; + Face* facesTail = NULL; + + if (gFaces != gFacesBase) + { + PROFILE(CNT_FLUSH); + + for (int32 i = OT_SIZE - 1; i >= 0; i--) + { + if (!gOT[i].head) continue; + + Face *face = gOT[i].head; + gOT[i].head = NULL; + + if (face) + { + if (facesTail) + { + facesTail->ccb_NextPtr = face; + } else if (!facesHead) { + facesHead = face; + } + + facesTail = gOT[i].tail; + } + } + + if (facesHead) + { + LAST_CEL(facesTail); + DrawScreenCels(screenItem, (CCB*)facesHead); + UNLAST_CEL(facesTail); + } + } + +#ifdef PROFILING + #if !defined(PROFILE_FRAMETIME) && !defined(PROFILE_SOUNDTIME) + gCounters[CNT_VERT] += gVerticesCount; + gCounters[CNT_POLY] += gFacesCount; + #endif +#endif + + gVerticesCount = 0; + gFacesCount = 0; + gFacesBase = gFaces; +} + +void clear() +{ + // we use fast clear via SPORT on vblank +} + +void renderRoom(const Room* room) +{ + int32 vCount = room->info->verticesCount; + if (vCount <= 0) + return; + + transformRoom(room, vCount); + faceAddRoom(room); +} + +void renderMesh(const Mesh* mesh) +{ + int32 vCount = mesh->vCount; + if (vCount <= 0) + return; + + const uint8* ptr = (uint8*)mesh + sizeof(Mesh); + + const MeshVertex* vertices = (MeshVertex*)ptr; + ptr += vCount * sizeof(vertices[0]); + + if (vCount & 1) { // data alignment + ptr += sizeof(MeshVertex); + } + + MeshQuad* rFaces = (MeshQuad*)ptr; + ptr += mesh->rCount * sizeof(MeshQuad); + + MeshTriangle* tFaces = (MeshTriangle*)ptr; + ptr += mesh->tCount * sizeof(MeshTriangle); + + MeshQuad* crFaces = (MeshQuad*)ptr; + ptr += mesh->crCount * sizeof(MeshQuad); + + MeshTriangle* ctFaces = (MeshTriangle*)ptr; + ptr += mesh->ctCount * sizeof(MeshTriangle); + + transformMesh(vertices, vCount); + faceAddMesh(rFaces, crFaces, tFaces, ctFaces, mesh->rCount, mesh->crCount, mesh->tCount, mesh->ctCount, mesh->intensity); +} + +void renderBorder(int32 x, int32 y, int32 width, int32 height, int32 shade, int32 color1, int32 color2, int32 z) +{ + // TODO +} + +#define BAR_HEIGHT 5 + +void renderBar(int32 x, int32 y, int32 width, int32 value, BarType type) +{ + // TODO +} + +void renderBackground(const void* background) +{ + // TODO +} + +void* copyBackground() +{ + return NULL; // TODO +} diff --git a/src/platform/3do/sound.cpp b/src/platform/3do/sound.cpp new file mode 100644 index 00000000..e851ee6a --- /dev/null +++ b/src/platform/3do/sound.cpp @@ -0,0 +1,331 @@ +#include "common.h" +#include + +Item sndMixer; + +struct Channel +{ + Item gainL; + Item gainR; + Item sampler; + Item attach; + Item frequency; + Item amplitude; + + int32 index; + bool playing; + + void setPitch(uint32 value) + { + if (frequency >= 0) { + TweakKnob(frequency, value); + } + } + + void setVolume(uint32 value) + { + if (amplitude >= 0) { + TweakKnob(amplitude, value); + } + } +}; + +struct SampleData +{ + Item data; + int32 size; +}; + +Channel channels[SND_CHANNELS]; // [sample, sample, sample, music] +SampleData samples[MAX_SAMPLES]; + +#define MUSIC_CHANNEL (SND_CHANNELS - 1) + +Item musicThread; +SPPlayer* musicPlayer; +SPSound* music; +uint32 musicSignal; +int32 musicTrack; + +void musicProc() +{ + OpenAudioFolio(); + + musicSignal = AllocSignal(0); + + void* buffers[SND_BUFFERS]; + for (int32 i = 0; i < SND_BUFFERS; i++) + { + buffers[i] = (uint8*)RAM_SND + i * SND_BUFFER_SIZE; + } + + spCreatePlayer(&musicPlayer, channels[MUSIC_CHANNEL].sampler, SND_BUFFERS, SND_BUFFER_SIZE, buffers); + + int32 signalMask = spGetPlayerSignalMask(musicPlayer); + + while (1) + { + WaitSignal(musicSignal); + + channels[MUSIC_CHANNEL].playing = true; + + int32 track = musicTrack; + { + char path[32]; + sprintf(path, "audio/%d.aifc", track); + + spAddSoundFile(&music, musicPlayer, path); + } + + spStartReading(music, SP_MARKER_NAME_BEGIN); + + spStartPlayingVA(musicPlayer, + AF_TAG_AMPLITUDE, 0x7FFF, + TAG_END); + + while (spGetPlayerStatus(musicPlayer) & SP_STATUS_F_BUFFER_ACTIVE) + { + int32 signal = WaitSignal(signalMask); + if (spService(musicPlayer, signal) < 0) + break; + + if (track != musicTrack) { + spStop(musicPlayer); + } + } + + spRemoveSound(music); + channels[MUSIC_CHANNEL].playing = false; + } +} + + +void sndInit() +{ + #if SND_CHANNELS != 4 + #error change the sound mixer or channels count + #endif + + sndMixer = LoadInstrument("mixer4x2.dsp", 0, 100); + + LoadInstrument("decodeadpcm.dsp", 0, 100); + + char* LGainName = "LeftGain0"; + char* RGainName = "RightGain0"; + char* InputName = "Input0"; + + for (int32 i = 0; i < SND_CHANNELS; i++) + { + Channel* ch = channels + i; + + LGainName[8] = '0' + i; + RGainName[9] = '0' + i; + InputName[5] = '0' + i; + + ch->gainL = GrabKnob(sndMixer, LGainName); + ch->gainR = GrabKnob(sndMixer, RGainName); + + if (i == MUSIC_CHANNEL) { + ch->sampler = LoadInstrument("dcsqxdhalfmono.dsp", 0, 100); + ConnectInstruments(ch->sampler, "Output", sndMixer, InputName); + } else { + ch->sampler = LoadInstrument("adpcmvarmono.dsp", 0, 100); + ConnectInstruments(ch->sampler, "Output", sndMixer, InputName); + } + + ch->frequency = GrabKnob(ch->sampler, "Frequency"); + ch->amplitude = GrabKnob(ch->sampler, "Amplitude"); + ch->setVolume(0x7FFF); + ch->setPitch(0x2000); + + TweakKnob(ch->gainL, 255 << 6); + TweakKnob(ch->gainR, 255 << 6); + + ch->index = -1; + ch->playing = false; + } + + StartInstrument(sndMixer, NULL); + + musicThread = CreateThread("music", 180, musicProc, 2048); +} + +void sndInitSamples() +{ + for (int32 i = 0; i < level.soundOffsetsCount; i++) + { + uint8* data = (uint8*)level.soundData + level.soundOffsets[i]; + int32 frames = *(uint32*)data; + + samples[i].size = frames >> 1; + samples[i].data = CreateSampleVA( + AF_TAG_FRAMES, frames, + AF_TAG_ADDRESS, (uint8*)data + 4, + AF_TAG_CHANNELS, 1, + AF_TAG_WIDTH, 2, + AF_TAG_COMPRESSIONTYPE, ID_ADP4, + AF_TAG_COMPRESSIONRATIO, 4, + TAG_END); + } +} + +void sndFreeSamples() +{ + if (!level.soundOffsetsCount) + return; + + for (int32 i = 0; i < SND_CHANNELS - 1; i++) + { + Channel* ch = channels + i; + + if (ch->index < 0) + continue; + + StopInstrument(ch->sampler, NULL); + DetachSample(ch->attach); + + ch->index = -1; + ch->playing = false; + } + + for (int32 i = 0; i < level.soundOffsetsCount; i++) + { + UnloadSample(samples[i].data); + } +} + +void* sndPlaySample(int32 index, int32 volume, int32 pitch, int32 mode) +{ + volume = volume * 0x7FFF >> SND_VOL_SHIFT; + pitch = pitch * 0x2000 >> SND_PITCH_SHIFT; + +// update playing status + int32 maxPos = -2; + int32 maxPosIndex = -1; + + for (int32 i = 0; i < SND_CHANNELS; i++) + { + Channel* ch = channels + i; + if (!ch->playing) + continue; + + int32 pos = WhereAttachment(ch->attach); + if (pos == -1 || pos >= samples[ch->index].size) + { + ch->playing = false; + } + + if (maxPos < pos) { + maxPos = pos; + maxPosIndex = i; + } + } + +// get existing channel + if (mode == UNIQUE || mode == REPLAY) + { + for (int32 i = 0; i < SND_CHANNELS - 1; i++) + { + Channel* ch = channels + i; + + if (ch->index != index) + continue; + + ch->setVolume(volume); + ch->setPitch(pitch); + + if (!ch->playing || mode == REPLAY) + { + ch->playing = true; + StartInstrument(ch->sampler, NULL); + } + + return (void*)ch->sampler; + } + } + +// get free channel + for (int32 i = 0; i < SND_CHANNELS - 1; i++) + { + Channel* ch = channels + i; + + if (ch->playing) + continue; + + if (ch->index >= 0) + { + StopInstrument(ch->sampler, NULL); + DetachSample(ch->attach); + } + + ch->setVolume(volume); + ch->setPitch(pitch); + ch->attach = AttachSample(ch->sampler, samples[index].data, NULL); + ch->index = index; + ch->playing = true; + + StartInstrument(ch->sampler, NULL); + + return (void*)ch->sampler; + } + +// stop a longest playing sample + if (maxPosIndex != -1) + { + sndStopSample(maxPosIndex); + + Channel* ch = channels + maxPosIndex; + + ch->setVolume(volume); + ch->setPitch(pitch); + ch->attach = AttachSample(ch->sampler, samples[index].data, NULL); + ch->index = index; + ch->playing = true; + + StartInstrument(ch->sampler, NULL); + + return (void*)ch->sampler; + } + + return NULL; +} + +void sndPlayTrack(int32 track) +{ + musicTrack = track; + + if (track >= 0) { + SendSignal(musicThread, musicSignal); + } +} + +void sndStopTrack() +{ + sndPlayTrack(-1); +} + +bool sndTrackIsPlaying() +{ + return channels[MUSIC_CHANNEL].playing; +} + +void sndStopSample(int32 index) +{ + Channel* ch = channels + index; + if (!ch->playing) + return; + + StopInstrument(ch->sampler, NULL); + DetachSample(ch->attach); + ch->index = -1; + ch->playing = false; +} + +void sndStop() +{ + //sndStopTrack(); // TODO wait for signal? + for (int32 i = 0; i < SND_CHANNELS - 1; i++) + { + sndStopSample(i); + } +} \ No newline at end of file diff --git a/src/platform/3do/sphereIsVisible.s b/src/platform/3do/sphereIsVisible.s new file mode 100644 index 00000000..9245e7ef --- /dev/null +++ b/src/platform/3do/sphereIsVisible.s @@ -0,0 +1,87 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT sphereIsVisible_asm + +x RN r0 +y RN r1 +z RN r2 +r RN r3 +vx RN r4 +vy RN r5 +vz RN r6 +mx RN r7 +my RN r8 +mz RN r12 + +m RN lr +divLUT RN m +vp RN m +vMinXY RN z +vMaxXY RN r + +rMinX RN vx +rMaxX RN x +rMinY RN vy +rMaxY RN y + +sphereIsVisible_asm + stmfd sp!, {r4-r8, lr} + + ldr m, =gMatrixPtr + ldr m, [m] + + ldmia m!, {mx, my, mz} + mul vx, mx, x + mul vy, my, x + mul vz, mz, x + ldmia m!, {mx, my, mz} + mla vx, mx, y, vx + mla vy, my, y, vy + mla vz, mz, y, vz + ldmia m!, {mx, my, mz} + mla vx, mx, z, vx + mla vy, my, z, vy + mla vz, mz, z, vz + + cmp vz, #VIEW_MAX_F + bhi _fail + + mov x, vx, asr #FIXED_SHIFT + mov y, vy, asr #FIXED_SHIFT + mov z, vz, lsr #(FIXED_SHIFT + PROJ_SHIFT) + + ldr divLUT, =divTable + ldr z, [divLUT, z, lsl #2] + mul x, z, x + mul y, z, y + mul r, z, r + + mov x, x, asr #(16 - PROJ_SHIFT) + mov y, y, lsl #(PROJ_SHIFT) + + sub rMinX, x, r, lsr #(16 - PROJ_SHIFT) + add rMaxX, x, r, lsr #(16 - PROJ_SHIFT) + sub rMinY, y, r, lsl #PROJ_SHIFT + add rMaxY, y, r, lsl #PROJ_SHIFT + + ldr vp, =viewportRel + ldmia vp, {vMinXY, vMaxXY} + + cmp rMaxX, vMinXY, asr #16 + blt _fail + cmp rMaxY, vMinXY, lsl #16 + blt _fail + cmp rMinX, vMaxXY, asr #16 + bgt _fail + cmp rMinY, vMaxXY, lsl #16 + bgt _fail + + mov r0, #1 + ldmfd sp!, {r4-r8, pc} + +_fail mov r0, #0 + ldmfd sp!, {r4-r8, pc} + END diff --git a/src/platform/3do/unpackMesh.s b/src/platform/3do/unpackMesh.s new file mode 100644 index 00000000..da5ee59b --- /dev/null +++ b/src/platform/3do/unpackMesh.s @@ -0,0 +1,48 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT unpackMesh_asm + +unpackMesh_asm + +data RN r0 +vCount RN r1 +vx0 RN vCount +vy0 RN r2 +vz0 RN r3 +vx1 RN r4 +vy1 RN r5 +vz1 RN r6 +n0 RN vy0 +n1 RN vx1 +n2 RN vz1 +vertex RN r12 +last RN lr + + stmfd sp!, {r4-r6, lr} + ldr vertex, =gVertices + add vCount, vCount, vCount, lsl #1 + add last, data, vCount, lsl #1 ; last = data + vCount * 6 + +loop ldmia data!, {n0, n1, n2} ; load two encoded vertices + cmp data, last + + mov vx0, n0, asr #16 ; x + mov n0, n0, lsl #16 + mov vy0, n0, asr #16 ; y + + mov vz0, n1, asr #16 ; z + mov n1, n1, lsl #16 + mov vx1, n1, asr #16 ; x + + mov vy1, n2, asr #16 ; y + mov n2, n2, lsl #16 + mov vz1, n2, asr #16 ; z + + stmia vertex!, {vx0, vy0, vz0, vx1, vy1, vz1} + blt loop + + ldmfd sp!, {r4-r6, pc} + END diff --git a/src/platform/3do/unpackRoom.s b/src/platform/3do/unpackRoom.s new file mode 100644 index 00000000..3d474758 --- /dev/null +++ b/src/platform/3do/unpackRoom.s @@ -0,0 +1,72 @@ + AREA |C$$code|, CODE, READONLY +|x$codeseg| + + INCLUDE common_asm.inc + + EXPORT unpackRoom_asm + +unpackRoom_asm + +data RN r0 +vCount RN r1 +vx0 RN vCount +vy0 RN r2 +vz0 RN r3 +vx1 RN r4 +vy1 RN r5 +vz1 RN r6 +vx2 RN vx0 +vy2 RN vy0 +vz2 RN vz0 +vx3 RN vx1 +vy3 RN vy1 +vz3 RN vz1 +n0 RN vz1 +n1 RN r7 +maskH RN r8 +maskV RN r9 +vertex RN r12 +last RN lr + + stmfd sp!, {r4-r9, lr} + ldr vertex, =gVertices + add last, data, vCount, lsl #1 ; last = data + vCount * 2 + mov maskH, #0x1F000 + mov maskV, #0x0FC00 + +loop ldmia data!, {n0, n1} ; load four encoded vertices + cmp data, last + + ; n0 = z1:5, y1:6, x1:5, z0:5, y0:6, x0:5 + ; n1 = z3:5, y3:6, x3:5, z2:5, y2:6, x2:5 + + ; 1st vertex + and vx0, maskH, n0, lsl #12 ; decode x0 + and vy0, maskV, n0, lsl #5 ; decode y0 + and vz0, maskH, n0, lsl #1 ; decode z0 + + ; 2nd vertex + and vx1, maskH, n0, lsr #4 ; decode x1 + and vy1, maskV, n0, lsr #11 ; decode y1 + and vz1, maskH, n0, lsr #15 ; decode z1 + + ; store + stmia vertex!, {vx0, vy0, vz0, vx1, vy1, vz1} + + ; 3rd vertex + and vx2, maskH, n1, lsl #12 ; decode x2 + and vy2, maskV, n1, lsl #5 ; decode y2 + and vz2, maskH, n1, lsl #1 ; decode z2 + + ; 4th vertex + and vx3, maskH, n1, lsr #4 ; decode x3 + and vy3, maskV, n1, lsr #11 ; decode y3 + and vz3, maskH, n1, lsr #15 ; decode z3 + + ; store + stmia vertex!, {vx2, vy2, vz2, vx3, vy3, vz3} + + blt loop + + ldmfd sp!, {r4-r9, pc} + END diff --git a/src/platform/3ds/Makefile b/src/platform/3ds/Makefile index 33fa3352..7eb7a4a3 100644 --- a/src/platform/3ds/Makefile +++ b/src/platform/3ds/Makefile @@ -33,7 +33,7 @@ include $(DEVKITARM)/3ds_rules #--------------------------------------------------------------------------------- TARGET := OpenLara BUILD := build -SOURCES := . ../../libs/stb_vorbis ../../libs/tinf +SOURCES := . ../../libs/stb_vorbis ../../libs/tinf ../../shaders/pica DATA := data INCLUDES := ../.. GRAPHICS := gfx diff --git a/src/platform/3ds/deploy.bat b/src/platform/3ds/deploy.bat deleted file mode 100644 index a64a83ee..00000000 --- a/src/platform/3ds/deploy.bat +++ /dev/null @@ -1 +0,0 @@ -C:\devkitPro\tools\bin\3dslink.exe OpenLara.3dsx -a 192.168.1.68 \ No newline at end of file diff --git a/src/platform/3ds/deploy.sh b/src/platform/3ds/deploy.sh new file mode 100644 index 00000000..d90c6628 --- /dev/null +++ b/src/platform/3ds/deploy.sh @@ -0,0 +1,2 @@ +make +/C/devkitPro/tools/bin/3dslink.exe OpenLara.3dsx -a 192.168.1.68 diff --git a/src/platform/3ds/dummy.v.pica b/src/platform/3ds/dummy.v.pica deleted file mode 100644 index 6a14c979..00000000 --- a/src/platform/3ds/dummy.v.pica +++ /dev/null @@ -1,14 +0,0 @@ -; constants -.constf const0(0.0, 0.0, 0.0, 0.0) - -; out -.out vPosition position -.out vTexCoord texcoord0 -.out vColor color - -.proc main - mov vPosition, const0.xxxx - mov vTexCoord, const0.xxxx - mov vColor, const0.xxxx - end -.end diff --git a/src/platform/3ds/main.cpp b/src/platform/3ds/main.cpp index 8ac8b51c..911e5ae7 100644 --- a/src/platform/3ds/main.cpp +++ b/src/platform/3ds/main.cpp @@ -33,7 +33,7 @@ int osGetTimeMS() { } // backlight -bool bottomScreenOn = true; +bool bottomScreenOn = false; void setBottomScreen(bool enable) { gspLcdInit(); @@ -46,10 +46,10 @@ aptHookCookie(cookie); void checkAptHook(APT_HookType hook, void *param) { if (!bottomScreenOn) { switch(hook) { - case APTHOOK_ONSUSPEND : setBottomScreen(1); + case APTHOOK_ONSUSPEND : setBottomScreen(true); break; case APTHOOK_ONRESTORE : - case APTHOOK_ONWAKEUP : setBottomScreen(0); + case APTHOOK_ONWAKEUP : setBottomScreen(false); break; default: break; @@ -97,13 +97,13 @@ void inputUpdate() { if (down & KEY_TOUCH) { bottomScreenOn = !bottomScreenOn; - bottomScreenOn ? setBottomScreen(1) : setBottomScreen(0); + bottomScreenOn ? setBottomScreen(true) : setBottomScreen(false); } } void inputFree() { if (!bottomScreenOn) - setBottomScreen(1); + setBottomScreen(true); hidExit(); } @@ -117,7 +117,6 @@ int sndBufIndex; bool sndReady; void sndFill(void *arg) { - LOG("thread start\n"); memset(sndWaveBuf, 0, sizeof(sndWaveBuf)); sndWaveBuf[0].data_vaddr = sndBuffer + 0; sndWaveBuf[0].nsamples = SND_FRAMES; @@ -176,7 +175,32 @@ void sndFree() { linearFree((uint32*)sndBuffer); } +int checkLanguage() +{ + uint8 id; + CFGU_GetSystemLanguage(&id); + + int str = STR_LANG_EN; + switch (id) + { + case CFG_LANGUAGE_EN : str = STR_LANG_EN; break; + case CFG_LANGUAGE_FR : str = STR_LANG_FR; break; + case CFG_LANGUAGE_DE : str = STR_LANG_DE; break; + case CFG_LANGUAGE_ES : str = STR_LANG_ES; break; + case CFG_LANGUAGE_IT : str = STR_LANG_IT; break; + case CFG_LANGUAGE_PT : str = STR_LANG_PT; break; + case CFG_LANGUAGE_RU : str = STR_LANG_RU; break; + case CFG_LANGUAGE_JP : str = STR_LANG_JA; break; + case CFG_LANGUAGE_ZH : str = STR_LANG_CN; break; + } + return str - STR_LANG_EN; +} + int main() { + cfguInit(); + + setBottomScreen(false); + { bool isNew3DS; APT_CheckNew3DS(&isNew3DS); @@ -189,9 +213,9 @@ int main() { strcpy(saveDir, "sdmc:/3ds/OpenLara/"); strcpy(contentDir, "sdmc:/3ds/OpenLara/"); - if(chdir(contentDir) != 0) { - return 0; - } + Stream::init(); + + Core::defLang = checkLanguage(); sndInit(); inputInit(); @@ -200,16 +224,31 @@ int main() { Game::init(); - while (aptMainLoop() && !Core::isQuit) { - inputUpdate(); + if (Core::isQuit) { + bottomScreenOn = true; + setBottomScreen(true); + consoleClear(); + LOG("\n\nCopy the original game content to:\n\n %s\n\nPress A to exit", contentDir); + while (aptMainLoop()) { + hidScanInput(); + u64 mask = hidKeysDown() | hidKeysHeld(); + if (mask & KEY_A) { + break; + } - if (Input::joy[0].down[jkStart]) - Core::quit(); + gfxFlushBuffers(); + gfxSwapBuffers(); + gspWaitForVBlank(); + } + } else { + while (aptMainLoop() && !Core::isQuit) { + inputUpdate(); - Game::update(); - Game::render(); + Game::update(); + Game::render(); - GAPI::present(); + GAPI::present(); + } } inputFree(); diff --git a/src/platform/android/app/build.gradle b/src/platform/android/app/build.gradle index cfe0ce67..c55e5f63 100644 --- a/src/platform/android/app/build.gradle +++ b/src/platform/android/app/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 26 - buildToolsVersion '26.0.2' + compileSdkVersion 30 + buildToolsVersion '30.0.0' defaultConfig { applicationId "com.xproger.openlara" minSdkVersion 19 - targetSdkVersion 18 + targetSdkVersion 30 versionCode 1 versionName "0.1" ndk { diff --git a/src/platform/android/app/src/main/AndroidManifest.xml b/src/platform/android/app/src/main/AndroidManifest.xml index 3bf325d0..bea39b1a 100644 --- a/src/platform/android/app/src/main/AndroidManifest.xml +++ b/src/platform/android/app/src/main/AndroidManifest.xml @@ -3,7 +3,7 @@ android:versionCode="1" android:versionName="0.1" > - + @@ -13,13 +13,15 @@ --> + + android:isGame="true" + android:requestLegacyExternalStorage="true"> #include +#include #include #include "game.h" @@ -8,10 +9,21 @@ SDL_Surface *screen = NULL; #define SCREEN_WIDTH (SCREEN_HEIGHT/3)*4 #define SCREEN_HEIGHT 240 +#ifdef __MIYOO__ +#define BTN_A SDLK_LALT +#define BTN_B SDLK_LCTRL +#define BTN_X SDLK_LSHIFT +#define BTN_Y SDLK_SPACE +#define BTN_L1 SDLK_BACKSPACE +#define BTN_R1 SDLK_TAB +#define BTN_L2 SDLK_RSHIFT +#define BTN_R2 SDLK_RALT +#else #define BTN_B SDLK_SPACE #define BTN_A SDLK_LCTRL #define BTN_TA SDLK_LALT #define BTN_TB SDLK_LSHIFT +#endif #define BTN_START SDLK_RETURN #define BTN_SELECT SDLK_ESCAPE #define BTN_R SDLK_RCTRL @@ -29,6 +41,99 @@ int osGetTimeMS() { return int((t.tv_sec - startTime) * 1000 + t.tv_usec / 1000); } +// sound +snd_pcm_uframes_t SND_FRAMES = 512; +snd_pcm_t *sndOut; +Sound::Frame *sndData; +pthread_t sndThread; + +void* sndFill(void *arg) { + while (sndOut) { + Sound::fill(sndData, SND_FRAMES); + + int count = SND_FRAMES; + while (count > 0) { + int frames = snd_pcm_writei(sndOut, &sndData[SND_FRAMES - count], count); + if (frames < 0) { + frames = snd_pcm_recover(sndOut, frames, 0); + if (frames == -EAGAIN) { + LOG("snd_pcm_writei try again\n"); + sleep(1); + continue; + } + if (frames < 0) { + LOG("snd_pcm_writei failed: %s\n", snd_strerror(frames)); + sndOut = NULL; + return NULL; + } + } + count -= frames; + } + + snd_pcm_prepare(sndOut); + } + return NULL; +} + +bool sndInit() { + unsigned int freq = 44100; + + int err; + + for (int i = 0; i < 20; i++) { // 20 * 0.1 = 2 secs + sndOut = NULL; + if ((err = snd_pcm_open(&sndOut, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) { + LOG("sound: try to snd_pcm_open #%d...\n", i); + usleep(100000); // wait for 100 ms + continue; + } + break; + } + + // I've bad news for you + if (!sndOut) { + LOG("! sound: snd_pcm_open %s\n", snd_strerror(err)); + return false; + } + + snd_pcm_hw_params_t *params; + + snd_pcm_hw_params_alloca(¶ms); + snd_pcm_hw_params_any(sndOut, params); + snd_pcm_hw_params_set_access(sndOut, params, SND_PCM_ACCESS_RW_INTERLEAVED); + + snd_pcm_hw_params_set_channels(sndOut, params, 2); + snd_pcm_hw_params_set_format(sndOut, params, SND_PCM_FORMAT_S16_LE); + snd_pcm_hw_params_set_rate_near(sndOut, params, &freq, NULL); + + snd_pcm_hw_params_set_periods(sndOut, params, 4, 0); + snd_pcm_hw_params_set_period_size_near(sndOut, params, &SND_FRAMES, NULL); + snd_pcm_hw_params_get_period_size(params, &SND_FRAMES, 0); + + snd_pcm_hw_params(sndOut, params); + snd_pcm_prepare(sndOut); + + sndData = new Sound::Frame[SND_FRAMES]; + memset(sndData, 0, SND_FRAMES * sizeof(Sound::Frame)); + if ((err = snd_pcm_writei(sndOut, sndData, SND_FRAMES)) < 0) { + LOG("! sound: write %s\n", snd_strerror(err)); + sndOut = NULL; + } + + snd_pcm_start(sndOut); + pthread_create(&sndThread, NULL, sndFill, NULL); + + return true; +} + +void sndFree() { + pthread_cancel(sndThread); + snd_pcm_drop(sndOut); + snd_pcm_drain(sndOut); + snd_pcm_close(sndOut); + delete[] sndData; +} + // input bool osJoyReady(int index) { return index == 0; @@ -40,10 +145,21 @@ void osJoyVibrate(int index, float L, float R) { JoyKey getJoyKey(int key) { switch (key) { +#ifdef __MIYOO__ + case BTN_A : return jkA; + case BTN_B : return jkB; + case BTN_X : return jkX; + case BTN_Y : return jkY; + case BTN_L1 : return jkLB; + case BTN_R1 : return jkRB; + case BTN_L2 : return jkLT; + case BTN_R2 : return jkRT; +#else case BTN_B : return jkX; case BTN_A : return jkA; case BTN_TA : return jkRB; case BTN_TB : return jkY; +#endif case BTN_START : return jkStart; case BTN_SELECT : return jkSelect; case BTN_UP : return jkUp; @@ -68,11 +184,13 @@ int main() { Core::defLang = 0; - Game::init("/mnt/games/OpenLara/LEVEL2.PSX"); + Game::init((const char *)NULL); GAPI::resize(); GAPI::swColor = (uint16*)screen->pixels; + sndInit(); + bool isQuit = false; while (!isQuit) { @@ -98,5 +216,7 @@ int main() { Game::deinit(); + sndFree(); + return 0; } diff --git a/src/platform/dc/Makefile b/src/platform/dc/Makefile index 09239910..c42136e6 100644 --- a/src/platform/dc/Makefile +++ b/src/platform/dc/Makefile @@ -1,5 +1,5 @@ ronindir = $(HOME)/repo/libronin -SH_ELF_BIN = /opt/toolchains/dc/bin +SH_ELF_BIN = /opt/toolchains/dc/sh-elf/bin DEBUG = 1 @@ -27,7 +27,8 @@ endif OBJS = main.o -OBJS += ../../libs/stb_vorbis/stb_vorbis.o #../../libs/tinf/tinflate.o +#OBJS += ../../libs/stb_vorbis/stb_vorbis.o +OBJS += ../../libs/tinf/tinflate.o OBJS += dc_hardware.o libpspvram/valloc.o vm_file.o vmu.o OBJS += primitive/polygon.o primitive/modifier.o primitive/prim_buffer.o @@ -44,31 +45,31 @@ partial: make all run: $(TARGET) - dc-tool -x $< -i $(HOME)/Documents/dcdev/TR1-PSX.ISO + dc-tool -x $< -i $(HOME)/Documents/dcdev/TR1-PC.ISO dump: $(TARGET) $(SH_ELF_BIN)/sh-elf-objdump -D $< > dump distclean: - $(RM) *.ISO *.CDI *.BIN *.elf *.bin *.iso ip.txt cd/1ST_READ.BIN + $(RM) *.ISO *.CDI *.BIN *.elf *.iso ip.txt cd/1ST_READ.BIN dist: 1ST_READ.BIN IP.BIN -1ST_READ.BIN: demo.bin +1ST_READ.BIN: demo.BIN scramble $< $@ ip.txt: ip.txt.in sed -e 's/[@]DATE[@]/$(shell date '+%Y%m%d')/' < $< > $@ -demo.bin: $(TARGET) +demo.BIN: $(TARGET) $(SH_ELF_BIN)/sh-elf-objcopy -S -R .stack -O binary $< $@ IP.BIN: ip.txt makeip $< $@ dcdist: dist - cp 1ST_READ.BIN $(HOME)/Documents/dcdev/dcdist/ - mkisofs -C 0,11702 -G IP.BIN -l -o TMP.ISO $(HOME)/Documents/dcdev/dcdist + cp 1ST_READ.BIN $(HOME)/Documents/dcdev/TR1-PC/ + mkisofs -C 0,11702 -G IP.BIN -l -o TMP.ISO $(HOME)/Documents/dcdev/TR1-PC cdi4dc TMP.ISO TEST.CDI cdi: dist diff --git a/src/platform/dc/dc_hardware.c b/src/platform/dc/dc_hardware.c index 0c913ebb..b851eb74 100644 --- a/src/platform/dc/dc_hardware.c +++ b/src/platform/dc/dc_hardware.c @@ -315,10 +315,11 @@ void dc_init_hardware() } -void *get_romfont_address() __asm__(".get_romfont_address"); +extern void *get_romfont_address(); __asm__("\ \n\ -.get_romfont_address: \n\ +.globl _romfont_address \n\ +_get_romfont_address: \n\ mov.l 1f,r0 \n\ mov.l @r0,r0 \n\ jmp @r0 \n\ diff --git a/src/platform/dc/main.cpp b/src/platform/dc/main.cpp index e5fcf9e3..0f10c6a7 100644 --- a/src/platform/dc/main.cpp +++ b/src/platform/dc/main.cpp @@ -318,8 +318,6 @@ void joyUpdate() { tick += t+USEC_TO_TIMER(17000); } - gettimeofday(&tm, NULL); - int mask = getimask(); setimask(15); @@ -337,7 +335,7 @@ void joyUpdate() { int joyy = pad->cond.controller.joyy; if(Buttons == 0x0606) - Core::quit(); + Core::quit(); Input::setJoyDown(JoyCnt, jkUp, Buttons & DC_PAD::JOY_DPAD_UP); Input::setJoyDown(JoyCnt, jkDown, Buttons & DC_PAD::JOY_DPAD_DOWN); @@ -351,17 +349,17 @@ void joyUpdate() { Input::setJoyDown(JoyCnt, jkLB, (Buttons & DC_PAD::JOY_LTRIGGER)); Input::setJoyDown(JoyCnt, jkRB, (Buttons & DC_PAD::JOY_RTRIGGER)); if ((Buttons & DC_PAD::JOY_LTRIGGER) != 0) - Input::setJoyDown(JoyCnt, jkStart, (Buttons & DC_PAD::JOY_BTN_START)); + Input::setJoyDown(JoyCnt, jkStart, (Buttons & DC_PAD::JOY_BTN_START)); else - Input::setJoyDown(JoyCnt, jkSelect, (Buttons & DC_PAD::JOY_BTN_START)); + Input::setJoyDown(JoyCnt, jkSelect, (Buttons & DC_PAD::JOY_BTN_START)); if ((Buttons & DC_PAD::JOY_BTN_C) != 0) - Input::setJoyDown(JoyCnt, jkLB, (Buttons & DC_PAD::JOY_BTN_C)); + Input::setJoyDown(JoyCnt, jkLB, (Buttons & DC_PAD::JOY_BTN_C)); if ((Buttons & DC_PAD::JOY_BTN_Z) != 0) - Input::setJoyDown(JoyCnt, jkRB, (Buttons & DC_PAD::JOY_BTN_Z)); + Input::setJoyDown(JoyCnt, jkRB, (Buttons & DC_PAD::JOY_BTN_Z)); vec2 stick = vec2(float(joyx), float(joyy)) / 128.0f - 1.0f; if (fabsf(joyx) < 0.2f && fabsf(joyy) < 0.2f) - stick = vec2(0.0f); + stick = vec2(0.0f); Input::setJoyPos(JoyCnt, jkL, stick); @@ -422,11 +420,11 @@ int main() joyUpdate(); if (Game::update()) { - ta_begin_frame(); - primitive_buffer_begin(); - Game::render(); - primitive_buffer_flush(); - ta_end_dlist(); + ta_begin_frame(); + primitive_buffer_begin(); + Game::render(); + primitive_buffer_flush(); + ta_end_dlist(); } } diff --git a/src/platform/dos/DOS4GW.exe b/src/platform/dos/DOS4GW.exe new file mode 100644 index 00000000..79f04ac2 Binary files /dev/null and b/src/platform/dos/DOS4GW.exe differ diff --git a/src/platform/dos/deploy.bat b/src/platform/dos/deploy.bat new file mode 100644 index 00000000..c7f3f32a --- /dev/null +++ b/src/platform/dos/deploy.bat @@ -0,0 +1,4 @@ +rm *.obj *.err +copy ..\gba\render.iwram.cpp render.cpp /Y +wcl386.exe *.cpp ..\..\fixed\*.cpp -fe=OpenLara.exe -i="C:\WATCOM/h" -i="..\..\fixed" -wcd726 -w4 -e25 -zq -ox -d2 -6r -bt=dos -fo=.obj -zmf -xd -l=pmodew +C:\Dosbox\dosbox -conf dosbox.conf OpenLara.exe \ No newline at end of file diff --git a/src/platform/dos/dosbox.conf b/src/platform/dos/dosbox.conf new file mode 100644 index 00000000..2f6596d0 --- /dev/null +++ b/src/platform/dos/dosbox.conf @@ -0,0 +1,10 @@ +[autoexec] +@echo off +mount c: C:\Projects\OpenLara\src\platform\dos +c: + +[cpu] +cycles=fixed 26800 + +[serial] +serial1=directserial realport:COM3 \ No newline at end of file diff --git a/src/platform/dos/main.cpp b/src/platform/dos/main.cpp new file mode 100644 index 00000000..e182127b --- /dev/null +++ b/src/platform/dos/main.cpp @@ -0,0 +1,267 @@ +#include "game.h" + +EWRAM_DATA int32 fps; +EWRAM_DATA int32 frameIndex = 0; +EWRAM_DATA int32 fpsCounter = 0; +EWRAM_DATA uint32 curSoundBuffer = 0; + +const void* TRACKS_IMA; +const void* TITLE_SCR; +const void* levelData; + +#define KB_ESC 1 +#define KB_A 30 +#define KB_S 31 +#define KB_Z 44 +#define KB_X 45 +#define KB_UP 72 +#define KB_LEFT 75 +#define KB_RIGHT 77 +#define KB_DOWN 80 +#define KB_ENTER 20 +#define KB_TAB 15 + +#define DOS_ISR __interrupt __far + +#define PIT_TIMER 0x08 +#define PIT_KEYBOARD 0x09 + + +void (DOS_ISR *old_timerISR)(); +void (DOS_ISR *old_keyISR)(); + +bool keyState[128]; + +void setVideoMode(); +#pragma aux setVideoMode = \ + "mov ax,13h" \ + "int 10h"; + +void setTextMode(); +#pragma aux setTextMode = \ + "mov ax,03h" \ + "int 10h"; + +void osSetPalette(const uint16* palette) +{ + outp(0x03C8, 0); + for (int32 i = 0; i < 256; i++) + { + uint16 c = *palette++; + outp(0x03C9, (c & 0x1F) << 1); + outp(0x03C9, ((c >> 5) & 0x1F) << 1); + outp(0x03C9, ((c >> 10) & 0x1F) << 1); + } +} + +void DOS_ISR timerISR() +{ + frameIndex++; + + outp(0x20, 0x20); +} + +void videoAcquire() +{ + setVideoMode(); + + old_timerISR = _dos_getvect(PIT_TIMER); + _dos_setvect(PIT_TIMER, timerISR); + + uint32 divisor = 1193182 / 60; + outp(0x43, 0x36); + outp(0x40, divisor & 0xFF); + outp(0x40, divisor >> 8); +} + +void videoRelease() +{ + _dos_setvect(PIT_TIMER, old_timerISR); + setTextMode(); +} + +void waitVBlank() +{ + while ((inp(0x03DA) & 0x08)); + while (!(inp(0x03DA) & 0x08)); +} + +void blit() +{ + memcpy((uint8*)0xA0000, fb, VRAM_WIDTH * FRAME_HEIGHT * 2); +} + +void DOS_ISR keyISR() +{ + uint32 scancode = inp(0x60); + + if (scancode != 0xE0) { + keyState[scancode & 0x7F] = ((scancode & 0x80) == 0); + } + + outp(0x20, 0x20); +} + +void inputAcquire() +{ + old_keyISR = _dos_getvect(PIT_KEYBOARD); + _dos_setvect(PIT_KEYBOARD, keyISR); +} + +void inputRelease() +{ + _dos_setvect(PIT_KEYBOARD, old_keyISR); +} + +void inputUpdate() +{ + keys = 0; + if (keyState[KB_UP]) keys |= IK_UP; + if (keyState[KB_RIGHT]) keys |= IK_RIGHT; + if (keyState[KB_DOWN]) keys |= IK_DOWN; + if (keyState[KB_LEFT]) keys |= IK_LEFT; + if (keyState[KB_X]) keys |= IK_A; + if (keyState[KB_Z]) keys |= IK_B; + if (keyState[KB_A]) keys |= IK_L; + if (keyState[KB_S]) keys |= IK_R; + if (keyState[KB_ENTER]) keys |= IK_START; + if (keyState[KB_TAB]) keys |= IK_SELECT; +} + +int32 osGetSystemTimeMS() +{ + return 0; +} + +bool osSaveSettings() +{ + return false; +} + +bool osLoadSettings() +{ + return false; +} + +bool osCheckSave() +{ + return false; +} + +bool osSaveGame() +{ + return false; +} + +bool osLoadGame() +{ + return false; +} + +void osJoyVibrate(int32 index, int32 L, int32 R) {} + +void* osLoadLevel(const char* name) +{ + sndStop(); + +// level1 + char buf[32]; + + delete[] levelData; + + sprintf(buf, "data/%s.PKD", name); + + FILE *f = fopen(buf, "rb"); + + if (!f) + return NULL; + + { + fseek(f, 0, SEEK_END); + int32 size = ftell(f); + fseek(f, 0, SEEK_SET); + uint8* data = new uint8[size]; + fread(data, 1, size, f); + fclose(f); + + levelData = data; + } + +// tracks + if (!TRACKS_IMA) + { + FILE *f = fopen("data/TRACKS.IMA", "rb"); + if (!f) + return NULL; + + fseek(f, 0, SEEK_END); + int32 size = ftell(f); + fseek(f, 0, SEEK_SET); + uint8* data = new uint8[size]; + fread(data, 1, size, f); + fclose(f); + + TRACKS_IMA = data; + } + + if (!TITLE_SCR) + { + FILE *f = fopen("data/TITLE.SCR", "rb"); + if (!f) + return NULL; + + fseek(f, 0, SEEK_END); + int32 size = ftell(f); + fseek(f, 0, SEEK_SET); + uint8* data = new uint8[size]; + fread(data, 1, size, f); + fclose(f); + + TITLE_SCR = data; + } + + return (void*)levelData; +} + +int main(void) +{ + videoAcquire(); + inputAcquire(); + + gameInit(gLevelInfo[gLevelID].name); + + int32 lastFrameIndex = -1; + + //int extraFrame = 0; + + while (1) + { + inputUpdate(); + + if (keyState[KB_ESC]) + break; + + int32 frame = frameIndex / 2; + gameUpdate(frame - lastFrameIndex); + lastFrameIndex = frame; + + gameRender(); + + fpsCounter++; + if (frameIndex >= 60) { + frameIndex -= 60; + lastFrameIndex -= 30; + + fps = fpsCounter; + + fpsCounter = 0; + } + + blit(); + } + + inputRelease(); + videoRelease(); + + return 0; +} diff --git a/src/platform/dos/rasterizer.h b/src/platform/dos/rasterizer.h new file mode 100644 index 00000000..1bd9f62a --- /dev/null +++ b/src/platform/dos/rasterizer.h @@ -0,0 +1,815 @@ +#ifndef H_RASTERIZER_MODE13 +#define H_RASTERIZER_MODE13 + +#include "common.h" + +extern uint8 gLightmap[256 * 32]; +extern const uint8* gTile; + +#define rasterizeS rasterizeS_c +#define rasterizeF rasterizeF_c +#define rasterizeG rasterizeG_c +#define rasterizeFT rasterizeFT_c +#define rasterizeGT rasterizeGT_c +#define rasterizeFTA rasterizeFTA_c +#define rasterizeGTA rasterizeGTA_c +#define rasterizeSprite rasterizeSprite_c +#define rasterizeLineH rasterizeLineH_c +#define rasterizeLineV rasterizeLineV_c +#define rasterizeFillS rasterizeFillS_c + +void rasterizeS_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + const uint8* ft_lightmap = &gLightmap[0x1A00]; + + int32 Lh = 0; + int32 Rh = 0; + int32 Ldx = 0; + int32 Rdx = 0; + int32 Rx; + int32 Lx; + + while (1) + { + while (!Lh) + { + const VertexLink* N = L->prev; + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + + if (Lh > 1) + { + uint32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + } + + Lx <<= 16; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R->next; + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + + if (Rh > 1) { + uint32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + } + + Rx <<= 16; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + volatile uint16* ptr = pixel + (x1 >> 1); + + if (x1 & 1) + { + uint16 p = ptr[0]; + + uint16 index = ft_lightmap[p >> 8]; + + ptr[0] = (p & 0x00FF) | (index << 8); + ptr++; + width--; + } + + if (width & 1) + { + uint16 p = ptr[width >> 1]; + + uint16 index = ft_lightmap[p & 0xFF]; + + ptr[width >> 1] = (p & 0xFF00) | index; + width--; + } + + while (width) + { + uint16 p = *ptr; + + uint16 index = ft_lightmap[p & 0xFF]; + index |= ft_lightmap[p >> 8] << 8; + + *ptr++ = index; + width -= 2; + } + + #undef SHADE + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + } + } +} + +void rasterizeF_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + uint16 color = gLightmap[(L->v.g << 8) | L->t.t]; + color |= (color << 8); + + int32 Lh = 0; + int32 Rh = 0; + int32 Ldx = 0; + int32 Rdx = 0; + int32 Rx; + int32 Lx; + + while (1) + { + while (!Lh) + { + const VertexLink* N = L->prev; + + ASSERT(L->v.y >= 0); + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + + if (Lh > 1) + { + uint32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + } + + Lx <<= 16; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R->next; + + ASSERT(R->v.y >= 0); + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + + if (Rh > 1) { + uint32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + } + + Rx <<= 16; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + volatile uint8* ptr = (uint8*)pixel + x1; + + if (intptr_t(ptr) & 1) + { + *ptr++ = uint8(color); + width--; + } + + if (width & 1) + { + ptr[width - 1] = uint8(color); + } + + if (width & 2) + { + *(uint16*)ptr = color; + ptr += 2; + } + + width >>= 2; + while (width--) + { + *(uint16*)ptr = color; + ptr += 2; + *(uint16*)ptr = color; + ptr += 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + } + } +} + +void rasterizeG_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + int32 Lh = 0, Rh = 0; + int32 Lx, Rx, Ldx = 0, Rdx = 0; + int32 Lg, Rg, Ldg = 0, Rdg = 0; + + const uint8* ft_lightmap = gLightmap + L->t.t; + + while (1) + { + while (!Lh) + { + const VertexLink* N = L->prev; + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + Lg = L->v.g; + + if (Lh > 1) + { + int32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + Ldg = tmp * (N->v.g - Lg); + } + + Lx <<= 16; + Lg <<= 16; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R->next; + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + Rg = R->v.g; + + if (Rh > 1) + { + int32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + Rdg = tmp * (N->v.g - Rg); + } + + Rx <<= 16; + Rg <<= 16; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + int32 tmp = FixedInvU(width); + + int32 dgdx = tmp * ((Rg - Lg) >> 5) >> 10; + + int32 g = Lg; + + volatile uint8* ptr = (uint8*)pixel + x1; + + if (intptr_t(ptr) & 1) + { + *ptr++ = ft_lightmap[g >> 16 << 8]; + g += dgdx >> 1; + width--; + } + + if (width & 1) + { + ptr[width - 1] = ft_lightmap[Rg >> 16 << 8]; + } + + if (width & 2) + { + uint8 p = ft_lightmap[g >> 16 << 8]; + g += dgdx; + *(uint16*)ptr = p | (p << 8); + ptr += 2; + } + + width >>= 2; + while (width--) + { + uint8 p; + + p = ft_lightmap[g >> 16 << 8]; + *(uint16*)ptr = p | (p << 8); + g += dgdx; + ptr += 2; + + p = ft_lightmap[g >> 16 << 8]; + *(uint16*)ptr = p | (p << 8); + g += dgdx; + ptr += 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + Lg += Ldg; + Rg += Rdg; + } + } +} + +void rasterizeFT_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + const uint8* ft_lightmap = &gLightmap[L->v.g << 8]; + + int32 Lh = 0, Rh = 0; + int32 Lx, Rx, Ldx = 0, Rdx = 0; + uint32 Lt, Rt, Ldt, Rdt; + Ldt = 0; + Rdt = 0; + + while (1) + { + while (!Lh) + { + const VertexLink* N = L->prev; + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + Lt = L->t.t; + + if (Lh > 1) + { + int32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + + uint32 duv = N->t.t - Lt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Ldt = (du & 0xFFFF0000) | (dv >> 16); + } + + Lx <<= 16; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R->next; + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + Rt = R->t.t; + + if (Rh > 1) + { + int32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + + uint32 duv = N->t.t - Rt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Rdt = (du & 0xFFFF0000) | (dv >> 16); + } + + Rx <<= 16; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + uint32 tmp = FixedInvU(width); + + uint32 duv = Rt - Lt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + uint32 dtdx = (du & 0xFFFF0000) | (dv >> 16); + + uint32 t = Lt; + + volatile uint8* ptr = (uint8*)pixel + x1; + + if (intptr_t(ptr) & 1) + { + *ptr++ = ft_lightmap[gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + width--; + } + + if (width & 1) + { + uint32 tmp = Rt - dtdx; + ptr[width - 1] = ft_lightmap[gTile[(tmp & 0xFF00) | (tmp >> 24)]]; + } + + width >>= 1; + while (width--) + { + uint8 indexA = ft_lightmap[gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + uint8 indexB = ft_lightmap[gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + + #ifdef CPU_BIG_ENDIAN + *(uint16*)ptr = indexB | (indexA << 8); + #else + *(uint16*)ptr = indexA | (indexB << 8); + #endif + + ptr += 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + Lt += Ldt; + Rt += Rdt; + } + } +} + +void rasterizeGT_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ +#ifdef ALIGNED_LIGHTMAP + ASSERT((intptr_t(gLightmap) & 0xFFFF) == 0); // lightmap should be 64k aligned +#endif + + int32 Lh = 0, Rh = 0; + int32 Lx, Rx, Lg, Rg, Ldx = 0, Rdx = 0, Ldg = 0, Rdg = 0; + uint32 Lt, Rt, Ldt, Rdt; + Ldt = 0; + Rdt = 0; + + // 8-bit fractional part precision for Gouraud component + // has some artifacts but allow to save one reg for inner loop + // for aligned by 64k address of lightmap array + + while (1) + { + while (!Lh) + { + const VertexLink* N = L->prev; + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + Lg = L->v.g; + Lt = L->t.t; + + if (Lh > 1) + { + int32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + Ldg = tmp * (N->v.g - Lg) >> 8; + + uint32 duv = N->t.t - Lt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Ldt = (du & 0xFFFF0000) | (dv >> 16); + } + + Lx <<= 16; + Lg <<= 8; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R->next; + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + Rg = R->v.g; + Rt = R->t.t; + + if (Rh > 1) + { + int32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + Rdg = tmp * (N->v.g - Rg) >> 8; + + uint32 duv = N->t.t - Rt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Rdt = (du & 0xFFFF0000) | (dv >> 16); + } + + Rx <<= 16; + Rg <<= 8; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + int32 tmp = FixedInvU(width); + + int32 dgdx = tmp * (Rg - Lg) >> 15; + + uint32 duv = Rt - Lt; + uint32 u = tmp * int16(duv >> 16); + uint32 v = tmp * int16(duv); + uint32 dtdx = (u & 0xFFFF0000) | (v >> 16); + + int32 g = Lg; + uint32 t = Lt; + + volatile uint8* ptr = (uint8*)pixel + x1; + + if (intptr_t(ptr) & 1) + { + *ptr++ = gLightmap[(g >> 8 << 8) | gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + g += dgdx >> 1; + width--; + } + + if (width & 1) + { + uint32 tmp = Rt - dtdx; + ptr[width - 1] = gLightmap[(Rg >> 8 << 8) | gTile[(tmp & 0xFF00) | (tmp >> 24)]]; + } + + #ifdef ALIGNED_LIGHTMAP + g += intptr_t(gLightmap); + #endif + + width >>= 1; + + while (width--) + { + #ifdef ALIGNED_LIGHTMAP + const uint8* LMAP = (uint8*)(g >> 8 << 8); + + uint8 indexA = LMAP[gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + uint8 indexB = LMAP[gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + g += dgdx; + #else + uint8 indexA = gLightmap[(g >> 8 << 8) | gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + uint8 indexB = gLightmap[(g >> 8 << 8) | gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + g += dgdx; + #endif + + #ifdef CPU_BIG_ENDIAN + *(uint16*)ptr = indexB | (indexA << 8); + #else + *(uint16*)ptr = indexA | (indexB << 8); + #endif + + ptr += 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + Lg += Ldg; + Rg += Rdg; + Lt += Ldt; + Rt += Rdt; + } + } +} + +void rasterizeFTA_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + const uint8* ft_lightmap = &gLightmap[L->v.g << 8]; + + int32 Lh = 0, Rh = 0; + int32 Lx, Rx, Ldx = 0, Rdx = 0; + uint32 Lt, Rt, Ldt, Rdt; + Ldt = 0; + Rdt = 0; + + while (1) + { + while (!Lh) + { + const VertexLink* N = L->prev; + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + Lt = L->t.t; + + if (Lh > 1) + { + int32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + + uint32 duv = N->t.t - Lt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Ldt = (du & 0xFFFF0000) | (dv >> 16); + } + + Lx <<= 16; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R->next; + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + Rt = R->t.t; + + if (Rh > 1) + { + int32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + + uint32 duv = N->t.t - Rt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Rdt = (du & 0xFFFF0000) | (dv >> 16); + } + + Rx <<= 16; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + uint32 tmp = FixedInvU(width); + + uint32 duv = Rt - Lt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + uint32 dtdx = (du & 0xFFFF0000) | (dv >> 16); + + uint32 t = Lt; + + volatile uint8* ptr = (uint8*)pixel + x1; + + if (intptr_t(ptr) & 1) + { + uint8 p = gTile[(t & 0xFF00) | (t >> 24)]; + if (p) { + *ptr = ft_lightmap[p]; + } + ptr++; + t += dtdx; + width--; + } + + if (width & 1) + { + uint32 tmp = Rt - dtdx; + uint8 p = gTile[(tmp & 0xFF00) | (tmp >> 24)]; + if (p) { + ptr[width - 1] = ft_lightmap[p]; + } + } + + width >>= 1; + while (width--) + { + uint8 indexA = gTile[(t & 0xFF00) | (t >> 24)]; + t += dtdx; + uint8 indexB = gTile[(t & 0xFF00) | (t >> 24)]; + t += dtdx; + + + if (indexA && indexB) + { + indexA = ft_lightmap[indexA]; + indexB = ft_lightmap[indexB]; + + #ifdef CPU_BIG_ENDIAN + *(uint16*)ptr = indexB | (indexA << 8); + #else + *(uint16*)ptr = indexA | (indexB << 8); + #endif + + }/* else if (indexA) { + *(uint16*)ptr = (*(uint16*)ptr & 0xFF00) | ft_lightmap[indexA]; + } else if (indexB) { + *(uint16*)ptr = (*(uint16*)ptr & 0x00FF) | (ft_lightmap[indexB] << 8); + }*/ + + ptr += 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + Lt += Ldt; + Rt += Rdt; + } + } +} + +void rasterizeGTA_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + rasterizeFTA(pixel, L, R); +} + +void rasterizeSprite_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + // TODO +} + +void rasterizeLineH_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + // TODO +} + +void rasterizeLineV_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + // TODO +} + +void rasterizeFillS_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + // TODO +} + +#endif diff --git a/src/platform/dos/sound.cpp b/src/platform/dos/sound.cpp new file mode 100644 index 00000000..2512df5c --- /dev/null +++ b/src/platform/dos/sound.cpp @@ -0,0 +1,51 @@ +#include "common.h" + +void sndInit() +{ + // TODO +} + +void sndInitSamples() +{ + // TODO +} + +void sndFreeSamples() +{ + // TODO +} + +void* sndPlaySample(int32 index, int32 volume, int32 pitch, int32 mode) +{ + return NULL; // TODO +} + +void sndPlayTrack(int32 track) +{ + // TODO +} + +void sndStopTrack() +{ + // TODO +} + +bool sndTrackIsPlaying() +{ + return false; // TODO +} + +void sndStopSample(int32 index) +{ + // TODO +} + +void sndStop() +{ + // TODO +} + +void sndFill(uint8* buffer, int32 count) +{ + // TODO +} diff --git a/src/platform/gba/IMGS/O/P/OPLA.bmp b/src/platform/gba/IMGS/O/P/OPLA.bmp new file mode 100644 index 00000000..732c4a20 Binary files /dev/null and b/src/platform/gba/IMGS/O/P/OPLA.bmp differ diff --git a/src/platform/gba/Makefile b/src/platform/gba/Makefile index 2c9c2d01..e8d4d583 100644 --- a/src/platform/gba/Makefile +++ b/src/platform/gba/Makefile @@ -22,20 +22,22 @@ include $(DEVKITARM)/gba_rules #--------------------------------------------------------------------------------- TARGET := OpenLara BUILD := build -SOURCES := . -INCLUDES := include +SOURCES := ../../fixed . asm +INCLUDES := include . ../../fixed DATA := data MUSIC := +LIBTONC := $(DEVKITPRO)/libtonc #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- ARCH := -mthumb -mthumb-interwork -CFLAGS := -g -Wall -O3\ +CFLAGS := -g -Wall -O3 -D__GBA__\ -mcpu=arm7tdmi -mtune=arm7tdmi\ -fomit-frame-pointer\ -ffast-math\ + -Wno-class-memaccess\ $(ARCH) CFLAGS += $(INCLUDE) @@ -48,14 +50,14 @@ LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map) #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project #--------------------------------------------------------------------------------- -LIBS := -lmm -lgba +LIBS := -lmm -ltonc #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing # include and lib #--------------------------------------------------------------------------------- -LIBDIRS := $(LIBGBA) +LIBDIRS := $(LIBGBA) $(LIBTONC) #--------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional @@ -118,6 +120,7 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) $(BUILD): @[ -d $@ ] || mkdir -p $@ @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + gbafix $(TARGET).gba -tOpenLara -cOPLA #--------------------------------------------------------------------------------- clean: @@ -150,15 +153,21 @@ soundbank.bin soundbank.h : $(AUDIOFILES) #--------------------------------------------------------------------------------- @mmutil $^ -osoundbank.bin -hsoundbank.h -#--------------------------------------------------------------------------------- -# This rule links in binary data with the .bin extension -#--------------------------------------------------------------------------------- -%.PHD.o %_PHD.h : %.PHD -#--------------------------------------------------------------------------------- +%.PKD.o %_PKD.h : %.PKD + @echo $(notdir $<) + @$(bin2o) + +%.IMA.o %_IMA.h : %.IMA @echo $(notdir $<) @$(bin2o) +%.SCR.o %_SCR.h : %.SCR + @echo $(notdir $<) + @$(bin2o) +%.WAD.o %_WAD.h : %.WAD + @echo $(notdir $<) + @$(bin2o) -include $(DEPSDIR)/*.d diff --git a/src/platform/gba/OpenLara.vcxproj b/src/platform/gba/OpenLara.vcxproj index 24ee8649..26297ce4 100644 --- a/src/platform/gba/OpenLara.vcxproj +++ b/src/platform/gba/OpenLara.vcxproj @@ -19,45 +19,58 @@ - + + + - - - + + + + + + + + + + + + + + 15.0 {990C6F40-6226-4011-B52C-FF042EBB7F15} Win32Proj OpenLara - 10.0.17763.0 + 10.0 Application true - v141 + v142 NotSet Application false - v141 + v142 true NotSet Application true - v141 + v142 Unicode Application false - v141 + v142 true Unicode @@ -81,15 +94,19 @@ true + ..\..\fixed;$(IncludePath) true + ..\..\fixed;$(IncludePath) false + ..\..\fixed;$(IncludePath) false + ..\..\fixed;$(IncludePath) @@ -104,6 +121,7 @@ Console true + winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) @@ -132,12 +150,14 @@ true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true + MultiThreaded Console true true true + winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) @@ -151,6 +171,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true + MultiThreaded Console diff --git a/src/platform/gba/asm/boxIsVisible.s b/src/platform/gba/asm/boxIsVisible.s new file mode 100644 index 00000000..d78aa8a4 --- /dev/null +++ b/src/platform/gba/asm/boxIsVisible.s @@ -0,0 +1,209 @@ +#include "common_asm.inc" + +mx .req r0 +my .req r1 +mz .req r2 +m .req r3 +vx .req r4 +vy .req r5 +vz .req r6 +x .req r7 +y .req r8 +z .req r9 +rMinX .req r10 +rMinY .req r11 +rMaxX .req r12 +rMaxY .req lr + +boxArg .req mx +tmp .req mz + +bz .req mz +offset .req m +dz .req offset +xx .req rMinX +yy .req rMinY +zz .req rMaxX +min .req rMaxY +max .req rMaxY +vMinXY .req x +vMaxXY .req y +vp .req x + +minX .req x +minY .req y +minZ .req z +maxX .req mx +maxY .req my +maxZ .req mz + +MAX_X = (0 * 3 * 4) +MIN_X = (1 * 3 * 4) +MAX_Y = (2 * 3 * 4) +MIN_Y = (3 * 3 * 4) +MAX_Z = (4 * 3 * 4) +MIN_Z = (5 * 3 * 4) +SIZE = (6 * 3 * 4) + +.macro project dx, dy, dz + add offset, sp, \dz + ldmia offset, {x, y, z} + + add offset, sp, \dy + ldmia offset, {vx, vy, vz} + add x, x, vx + add y, y, vy + add z, z, vz + + add offset, sp, \dx + ldmia offset, {vx, vy, vz} + add z, z, vz + + // check z clipping + sub offset, z, #VIEW_MIN_F + cmp offset, #(VIEW_MAX_F - VIEW_MIN_F) + bhi 1f + + add x, x, vx + add y, y, vy + + mov dz, z, lsr #(FIXED_SHIFT + 6) + add dz, dz, z, lsr #(FIXED_SHIFT + 4) + divLUT tmp, dz + mul x, tmp, x + mul y, tmp, y + + cmp x, rMinX + movlt rMinX, x + cmp y, rMinY + movlt rMinY, y + cmp x, rMaxX + movgt rMaxX, x + cmp y, rMaxY + movgt rMaxY, y +1: +.endm + +.global boxIsVisible_asm +boxIsVisible_asm: + ldr m, =gMatrixPtr + ldr m, [m] + ldr bz, [m, #(11 * 4)] + add bz, bz, #VIEW_OFF_F + cmp bz, #(VIEW_OFF_F + VIEW_MAX_F) + movhi r0, #0 + bxhi lr + + stmfd sp!, {r4-r11, lr} + + ldmia boxArg, {xx, yy, zz} + + // pre-transform min/max Z + ldr mx, [m, #8] + ldr vx, [m, #12] + ldr my, [m, #24] + ldr vy, [m, #28] + ldr mz, [m, #40] + ldr vz, [m, #44] + + mov min, zz, asr #16 + mla minX, min, mx, vx + mla minY, min, my, vy + mla minZ, min, mz, vz + mov minX, minX, asr #FIXED_SHIFT + mov minY, minY, asr #FIXED_SHIFT + + mov max, zz, lsl #16 + mov max, max, asr #16 + mla maxX, max, mx, vx + mla maxY, max, my, vy + mla maxZ, max, mz, vz + mov maxX, maxX, asr #FIXED_SHIFT + mov maxY, maxY, asr #FIXED_SHIFT + stmdb sp!, {maxX, maxY, maxZ, minX, minY, minZ} + + // pre-transform min/max Y + ldr mx, [m, #4] + ldr my, [m, #20] + ldr mz, [m, #36] + + mov min, yy, asr #16 + mul minX, mx, min + mul minY, my, min + mul minZ, mz, min + mov minX, minX, asr #FIXED_SHIFT + mov minY, minY, asr #FIXED_SHIFT + + mov max, yy, lsl #16 + mov max, max, asr #16 + mul maxX, max, mx + mul maxY, max, my + mul maxZ, max, mz + mov maxX, maxX, asr #FIXED_SHIFT + mov maxY, maxY, asr #FIXED_SHIFT + stmdb sp!, {maxX, maxY, maxZ, minX, minY, minZ} + + // pre-transform min/max X + ldr mx, [m, #0] + ldr my, [m, #16] + ldr mz, [m, #32] + + mov min, xx, asr #16 + mul minX, mx, min + mul minY, my, min + mul minZ, mz, min + mov minX, minX, asr #FIXED_SHIFT + mov minY, minY, asr #FIXED_SHIFT + + mov max, xx, lsl #16 + mov max, max, asr #16 + mul maxX, max, mx + mul maxY, max, my + mul maxZ, max, mz + mov maxX, maxX, asr #FIXED_SHIFT + mov maxY, maxY, asr #FIXED_SHIFT + stmdb sp!, {maxX, maxY, maxZ, minX, minY, minZ} + + mov rMinX, #MAX_INT32 + mov rMinY, #MAX_INT32 + mov rMaxX, #MIN_INT32 + mov rMaxY, #MIN_INT32 + + project #MIN_X, #MIN_Y, #MIN_Z + project #MAX_X, #MIN_Y, #MIN_Z + project #MIN_X, #MAX_Y, #MIN_Z + project #MAX_X, #MAX_Y, #MIN_Z + project #MIN_X, #MIN_Y, #MAX_Z + project #MAX_X, #MIN_Y, #MAX_Z + project #MIN_X, #MAX_Y, #MAX_Z + project #MAX_X, #MAX_Y, #MAX_Z + mov r0, #0 + + mov rMinX, rMinX, asr #(16 - PROJ_SHIFT) + mov rMaxX, rMaxX, asr #(16 - PROJ_SHIFT) + + cmp rMinX, rMaxX + beq .done + + // rect Y must remain shifted up by 16 + mov rMinY, rMinY, lsl #PROJ_SHIFT + mov rMaxY, rMaxY, lsl #PROJ_SHIFT + + // check xy clipping + ldr vp, =viewportRel + ldmia vp, {vMinXY, vMaxXY} + + cmp rMaxX, vMinXY, asr #16 + blt .done + cmp rMaxY, vMinXY, lsl #16 + blt .done + cmp rMinX, vMaxXY, asr #16 + bgt .done + cmp rMinY, vMaxXY, lsl #16 + bgt .done + + mov r0, #1 +.done: + add sp, sp, #SIZE + ldmfd sp!, {r4-r11, lr} + bx lr \ No newline at end of file diff --git a/src/platform/gba/asm/boxRotateYQ.s b/src/platform/gba/asm/boxRotateYQ.s new file mode 100644 index 00000000..ad01f14c --- /dev/null +++ b/src/platform/gba/asm/boxRotateYQ.s @@ -0,0 +1,57 @@ +#include "common_asm.inc" + +v .req r0 +q .req r1 + +min .req q +max .req r2 + +minX .req r3 +maxX .req r12 +minZ .req minX +maxZ .req maxX + +.global boxRotateYQ_asm +boxRotateYQ_asm: + cmp q, #2 + bxeq lr + + cmp q, #1 + beq .q_1 + cmp q, #3 + beq .q_3 + +.q_0: + ldmia v, {minX, maxX} + rsb min, maxX, #0 + rsb max, minX, #0 + stmia v, {min, max} + add v, #16 + ldmia v, {minZ, maxZ} + rsb min, maxZ, #0 + rsb max, minZ, #0 + stmia v, {min, max} + bx lr + +.q_1: + ldmia v, {min, max} + add v, #16 + ldmia v, {minZ, maxZ} + stmia v, {min, max} + rsb min, maxZ, #0 + rsb max, minZ, #0 + sub v, #16 + stmia v, {min, max} + bx lr + +.q_3: + add v, #16 + ldmia v, {min, max} + sub v, #16 + ldmia v, {minX, maxX} + stmia v, {min, max} + rsb min, maxX, #0 + rsb max, minX, #0 + add v, #16 + stmia v, {min, max} + bx lr diff --git a/src/platform/gba/asm/boxTranslate.s b/src/platform/gba/asm/boxTranslate.s new file mode 100644 index 00000000..b68c256b --- /dev/null +++ b/src/platform/gba/asm/boxTranslate.s @@ -0,0 +1,28 @@ +#include "common_asm.inc" + +aabb .req r0 +x .req r1 +y .req r2 +z .req r3 +minX .req r4 +maxX .req r5 +minY .req r6 +maxY .req r7 +minZ .req r12 +maxZ .req lr + +.global boxTranslate_asm +boxTranslate_asm: + stmfd sp!, {r4-r7, lr} + + ldmia aabb, {minX, maxX, minY, maxY, minZ, maxZ} + add minX, minX, x + add maxX, maxX, x + add minY, minY, y + add maxY, maxY, y + add minZ, minZ, z + add maxZ, maxZ, z + stmia aabb, {minX, maxX, minY, maxY, minZ, maxZ} + + ldmfd sp!, {r4-r7, lr} + bx lr diff --git a/src/platform/gba/asm/common_asm.inc b/src/platform/gba/asm/common_asm.inc new file mode 100644 index 00000000..4da4c9d1 --- /dev/null +++ b/src/platform/gba/asm/common_asm.inc @@ -0,0 +1,141 @@ +.section .iwram +.arm + +#define FRAME_WIDTH 240 +#define FRAME_HEIGHT 160 + +.equ VERTEX_X, 0 +.equ VERTEX_Y, 2 +.equ VERTEX_Z, 4 +.equ VERTEX_G, 6 +.equ VERTEX_CLIP, 7 +.equ VERTEX_T, 8 +.equ VERTEX_PREV, 12 +.equ VERTEX_NEXT, 13 + +.equ VERTEX_SIZEOF_SHIFT, 4 +.equ VERTEX_SIZEOF, (1 << VERTEX_SIZEOF_SHIFT) + +.equ LEVEL_TILES, 40 +.equ LEVEL_TEXTURES, 92 +.equ LEVEL_SPRITES, 96 + +.equ SPRITE_SIZE, 16 + +.equ TEXTURE_TILE, 2 +.equ TEXTURE_UV01, 4 +.equ TEXTURE_UV23, 8 + +.equ EWRAM_START, 0x2000000 +.equ IWRAM_START, 0x3000000 +.equ VRAM_START, 0x6000000 +.equ VRAM_PAGE, 0x000A000 + +.equ DIVLUT_ADDR, EWRAM_START +.equ LMAP_ADDR, IWRAM_START + +.equ CLIP_LEFT, ((1 << 0) << 8) +.equ CLIP_RIGHT, ((1 << 1) << 8) +.equ CLIP_TOP, ((1 << 2) << 8) +.equ CLIP_BOTTOM, ((1 << 3) << 8) +.equ CLIP_FAR, ((1 << 4) << 8) +.equ CLIP_NEAR, ((1 << 5) << 8) +.equ CLIP_MASK_VP, (CLIP_LEFT + CLIP_RIGHT + CLIP_TOP + CLIP_BOTTOM) +.equ CLIP_MASK, (CLIP_MASK_VP + CLIP_FAR + CLIP_NEAR) + +.equ FACE_TEXTURE_BITS, 14 +.equ FACE_TEXTURE, ((1 << FACE_TEXTURE_BITS) - 1) +.equ FACE_GOURAUD, (2 << FACE_TYPE_SHIFT) +.equ FACE_CLIPPED, (1 << 18) +.equ FACE_TRIANGLE, (1 << 19) + +.equ FACE_FLAGS, 0 +.equ FACE_NEXT, 4 +.equ FACE_INDICES, 8 + +.equ FACE_TYPE_SHIFT, 14 +.equ FACE_TYPE_MASK, (15 << FACE_TYPE_SHIFT) + +.equ FACE_TYPE_SHADOW, (0 << FACE_TYPE_SHIFT) +.equ FACE_TYPE_F, (1 << FACE_TYPE_SHIFT) +.equ FACE_TYPE_FT, (2 << FACE_TYPE_SHIFT) +.equ FACE_TYPE_FTA, (3 << FACE_TYPE_SHIFT) +.equ FACE_TYPE_GT, (4 << FACE_TYPE_SHIFT) +.equ FACE_TYPE_GTA, (5 << FACE_TYPE_SHIFT) +.equ FACE_TYPE_SPRITE, (6 << FACE_TYPE_SHIFT) +.equ FACE_TYPE_FILL_S, (7 << FACE_TYPE_SHIFT) +.equ FACE_TYPE_LINE_H, (8 << FACE_TYPE_SHIFT) +.equ FACE_TYPE_LINE_V, (9 << FACE_TYPE_SHIFT) + +.equ FIXED_SHIFT, 14 +.equ PROJ_SHIFT, 4 +.equ OT_SHIFT, 4 + +.equ VIEW_DIST, (1024 * 10) // max = DIV_TABLE_END << PROJ_SHIFT +.equ FOG_SHIFT, 1 +.equ FOG_MAX, VIEW_DIST +.equ FOG_MIN, (FOG_MAX - (8192 >> FOG_SHIFT)) +.equ VIEW_MIN, (64) +.equ VIEW_MAX, (VIEW_DIST) +.equ VIEW_OFF, 4096 + +.equ OT_SIZE, 641 + +.equ VIEW_MIN_F, (VIEW_MIN << FIXED_SHIFT) +.equ VIEW_MAX_F, (VIEW_MAX << FIXED_SHIFT) +.equ VIEW_OFF_F, (VIEW_OFF << FIXED_SHIFT) + +.equ MAX_CAUSTICS, 32 +.equ MAX_RAND_TABLE, 32 +.equ MAX_ANIM_TEX, 128 + +.equ MIN_INT32, 0x80000000 +.equ MAX_INT32, 0x7FFFFFFF + +.macro divLUT res, x + add \res, \x, #DIVLUT_ADDR + ldrh \res, [\res, \x] +.endm + +// vx0 - vg0 +// vy0 - vg1 +// vx1 - vg2 +// vy1 - vg3 +// vx2 - vg2 +// vy2 - vg2 +.macro CCW skip + ldrsh vx0, [vp0, #VERTEX_X] + ldrsh vy0, [vp0, #VERTEX_Y] + ldrsh vx2, [vp2, #VERTEX_X] + ldrsh vy1, [vp1, #VERTEX_Y] + rsb vx2, vx2, vx0 // reverse order for mla + sub vy1, vy1, vy0 + mul vy1, vx2, vy1 + ldrsh vx1, [vp1, #VERTEX_X] + sub vx0, vx1, vx0 + ldrsh vy2, [vp2, #VERTEX_Y] + sub vy0, vy2, vy0 + mlas vy1, vx0, vy0, vy1 + ble \skip +.endm + +.macro scaleUV uv, u, v, f + asr \u, \uv, #16 + mul \u, \f // u = f * int16(uv >> 16) + lsl \v, \uv, #16 + asr \v, #16 + mul \v, \f // v = f * int16(uv) + lsr \u, #16 + lsl \u, #16 + orr \uv, \u, \v, lsr #16 // uv = (u & 0xFFFF0000) | (v >> 16) +.endm + +.macro tex index, uv + and \index, \uv, #0xFF00 + orr \index, \uv, lsr #24 // index = v * 256 + u + ldrb \index, [TILE, \index] +.endm + +.macro lit index + ldrb \index, [LMAP, \index] +.endm diff --git a/src/platform/gba/asm/faceAddMeshQuads.s b/src/platform/gba/asm/faceAddMeshQuads.s new file mode 100644 index 00000000..87923b99 --- /dev/null +++ b/src/platform/gba/asm/faceAddMeshQuads.s @@ -0,0 +1,119 @@ +#include "common_asm.inc" + +polys .req r0 +count .req r1 +vp .req r2 +vg0 .req r3 +vg1 .req r4 +vg2 .req r5 +vg3 .req r6 +flags .req r7 +vp0 .req r8 +vp1 .req r9 +vp2 .req r10 +vp3 .req r11 +ot .req r12 +face .req lr + +vx0 .req vg0 +vy0 .req vg1 +vx1 .req vg2 +vy1 .req vg3 +vx2 .req vg2 +vy2 .req vg2 + +vz0 .req vg0 +vz1 .req vg1 +vz2 .req vg2 +vz3 .req vg3 +depth .req vg0 + +tmp .req flags +vertices .req vg2 +next .req vp0 + +SP_SIZE = 4 + +.global faceAddMeshQuads_asm +faceAddMeshQuads_asm: + stmfd sp!, {r4-r11, lr} + + ldr vp, =gVerticesBase + ldr vp, [vp] + + ldr vertices, =gVertices + lsr vertices, #3 + stmfd sp!, {vertices} + + ldr face, =gFacesBase + ldr face, [face] + + ldr ot, =gOT + + add polys, #2 // skip flags + +.loop: + ldrb vp0, [polys], #1 + ldrb vp1, [polys], #1 + ldrb vp2, [polys], #1 + ldrb vp3, [polys], #3 // + flags + + add vp0, vp, vp0, lsl #3 + add vp1, vp, vp1, lsl #3 + add vp2, vp, vp2, lsl #3 + add vp3, vp, vp3, lsl #3 + + CCW .skip + + // fetch clip flags + ldrb vg0, [vp0, #VERTEX_CLIP] + ldrb vg1, [vp1, #VERTEX_CLIP] + ldrb vg2, [vp2, #VERTEX_CLIP] + ldrb vg3, [vp3, #VERTEX_CLIP] + + // check clipping + and tmp, vg0, vg1 + and tmp, vg2 + ands tmp, vg3 + bne .skip + + // mark if should be clipped by viewport + orr tmp, vg0, vg1 + orr tmp, vg2 + orr tmp, vg3 + tst tmp, #(CLIP_MASK_VP >> 8) + ldrh flags, [polys, #-8] + orrne flags, #FACE_CLIPPED + + // vz0 = AVG_Z4 (depth) + ldrh vz0, [vp0, #VERTEX_Z] + ldrh vz1, [vp1, #VERTEX_Z] + ldrh vz2, [vp2, #VERTEX_Z] + ldrh vz3, [vp3, #VERTEX_Z] + add depth, vz0, vz1 + add depth, vz2 + add depth, vz3 + lsr depth, #(2 + OT_SHIFT) + + // faceAdd + ldr vertices, [sp] + rsb vp0, vertices, vp0, lsr #3 + rsb vp1, vertices, vp1, lsr #3 + rsb vp2, vertices, vp2, lsr #3 + rsb vp3, vertices, vp3, lsr #3 + + orr vp1, vp0, vp1, lsl #16 + orr vp3, vp2, vp3, lsl #16 + + ldr next, [ot, depth, lsl #2] + str face, [ot, depth, lsl #2] + stmia face!, {flags, next, vp1, vp3} +.skip: + subs count, #1 + bne .loop + + ldr tmp, =gFacesBase + str face, [tmp] + + add sp, #SP_SIZE + ldmfd sp!, {r4-r11, pc} diff --git a/src/platform/gba/asm/faceAddMeshTriangles.s b/src/platform/gba/asm/faceAddMeshTriangles.s new file mode 100644 index 00000000..b04b5412 --- /dev/null +++ b/src/platform/gba/asm/faceAddMeshTriangles.s @@ -0,0 +1,104 @@ +#include "common_asm.inc" + +polys .req r0 +count .req r1 +vp .req r2 +vg0 .req r3 +vg1 .req r4 +vg2 .req r5 +vg3 .req r6 +flags .req r7 +vp0 .req r8 +vp1 .req r9 +vp2 .req r10 +vertices .req r11 +ot .req r12 +face .req lr + +vx0 .req vg0 +vy0 .req vg1 +vx1 .req vg2 +vy1 .req vg3 +vx2 .req vg2 +vy2 .req vg2 + +vz0 .req vg0 +vz1 .req vg1 +vz2 .req vg2 +depth .req vg0 + +tmp .req flags +next .req vp0 + +.global faceAddMeshTriangles_asm +faceAddMeshTriangles_asm: + stmfd sp!, {r4-r11, lr} + + ldr vp, =gVerticesBase + ldr vp, [vp] + + ldr face, =gFacesBase + ldr face, [face] + + ldr ot, =gOT + ldr vertices, =gVertices + lsr vertices, #3 + + add polys, #2 // skip flags + +.loop: + ldrb vp0, [polys], #1 + ldrb vp1, [polys], #1 + ldrb vp2, [polys], #4 // + padding + flags + + add vp0, vp, vp0, lsl #3 + add vp1, vp, vp1, lsl #3 + add vp2, vp, vp2, lsl #3 + + CCW .skip + + // fetch clip flags + ldrb vg0, [vp0, #VERTEX_CLIP] + ldrb vg1, [vp1, #VERTEX_CLIP] + ldrb vg2, [vp2, #VERTEX_CLIP] + + // check clipping + and tmp, vg0, vg1 + ands tmp, vg2 + bne .skip + + // mark if should be clipped by viewport + orr tmp, vg0, vg1 + orr tmp, vg2 + tst tmp, #(CLIP_MASK_VP >> 8) + ldrh flags, [polys, #-8] + orrne flags, #FACE_CLIPPED + + // vz0 = AVG_Z3 (depth) + ldrh vz0, [vp0, #VERTEX_Z] + ldrh vz1, [vp1, #VERTEX_Z] + ldrh vz2, [vp2, #VERTEX_Z] + add depth, vz0, vz1 + add depth, vz2, lsl #1 + lsr depth, #(2 + OT_SHIFT) + + // faceAdd + rsb vp0, vertices, vp0, lsr #3 + rsb vp1, vertices, vp1, lsr #3 + rsb vp2, vertices, vp2, lsr #3 + + orr vp1, vp0, vp1, lsl #16 + + orr flags, #FACE_TRIANGLE + + ldr next, [ot, depth, lsl #2] + str face, [ot, depth, lsl #2] + stmia face!, {flags, next, vp1, vp2} +.skip: + subs count, #1 + bne .loop + + ldr tmp, =gFacesBase + str face, [tmp] + + ldmfd sp!, {r4-r11, pc} diff --git a/src/platform/gba/asm/faceAddRoomQuads.s b/src/platform/gba/asm/faceAddRoomQuads.s new file mode 100644 index 00000000..5966999e --- /dev/null +++ b/src/platform/gba/asm/faceAddRoomQuads.s @@ -0,0 +1,130 @@ +#include "common_asm.inc" + +polys .req r0 +count .req r1 +vp .req r2 +vg0 .req r3 +vg1 .req r4 +vg2 .req r5 +vg3 .req r6 +flags .req r7 +vp0 .req r8 +vp1 .req r9 +vp2 .req r10 +vp3 .req r11 +ot .req r12 +face .req lr + +vx0 .req vg0 +vy0 .req vg1 +vx1 .req vg2 +vy1 .req vg3 +vx2 .req vg2 +vy2 .req vg2 + +vz0 .req vg0 +vz1 .req vg1 +vz2 .req vg2 +vz3 .req vg3 +depth .req vg0 + +tmp .req flags +vertices .req vg2 +next .req vp0 + +SP_SIZE = 4 + +.global faceAddRoomQuads_asm +faceAddRoomQuads_asm: + stmfd sp!, {r4-r11, lr} + + ldr vp, =gVerticesBase + ldr vp, [vp] + + ldr vertices, =gVertices + lsr vertices, #3 + stmfd sp!, {vertices} + + ldr face, =gFacesBase + ldr face, [face] + + ldr ot, =gOT + + add polys, #2 // skip flags + +.loop: + ldrh vp0, [polys], #2 + ldrh vp1, [polys], #2 + ldrh vp2, [polys], #2 + ldrh vp3, [polys], #4 // + flags + + add vp0, vp, vp0, lsl #3 + add vp1, vp, vp1, lsl #3 + add vp2, vp, vp2, lsl #3 + add vp3, vp, vp3, lsl #3 + + // fetch ((clip << 8) | g) + ldrh vg0, [vp0, #VERTEX_G] + ldrh vg1, [vp1, #VERTEX_G] + ldrh vg2, [vp2, #VERTEX_G] + ldrh vg3, [vp3, #VERTEX_G] + + // check clipping + and tmp, vg0, vg1 + and tmp, vg2 + and tmp, vg3 + tst tmp, #CLIP_MASK + bne .skip + + // mark if should be clipped by viewport + orr tmp, vg0, vg1 + orr tmp, vg2 + orr tmp, vg3 + tst tmp, #CLIP_MASK_VP + ldrh flags, [polys, #-12] + orrne flags, #FACE_CLIPPED + + // shift and compare VERTEX_G for gouraud rasterization + lsl vg0, #24 + cmp vg0, vg1, lsl #24 + cmpeq vg0, vg2, lsl #24 + cmpeq vg0, vg3, lsl #24 + addne flags, #FACE_GOURAUD + + CCW .skip + + // vz0 = MAX_Z4 (depth) + ldrh vz0, [vp0, #VERTEX_Z] + ldrh vz1, [vp1, #VERTEX_Z] + ldrh vz2, [vp2, #VERTEX_Z] + ldrh vz3, [vp3, #VERTEX_Z] + cmp vz0, vz1 + movlt vz0, vz1 + cmp vz0, vz2 + movlt vz0, vz2 + cmp vz0, vz3 + movlt vz0, vz3 + mov depth, vz0, lsr #OT_SHIFT + + // faceAdd + ldr vertices, [sp] + rsb vp0, vertices, vp0, lsr #3 + rsb vp1, vertices, vp1, lsr #3 + rsb vp2, vertices, vp2, lsr #3 + rsb vp3, vertices, vp3, lsr #3 + + orr vp1, vp0, vp1, lsl #16 + orr vp3, vp2, vp3, lsl #16 + + ldr next, [ot, depth, lsl #2] + str face, [ot, depth, lsl #2] + stmia face!, {flags, next, vp1, vp3} +.skip: + subs count, #1 + bne .loop + + ldr tmp, =gFacesBase + str face, [tmp] + + add sp, #SP_SIZE + ldmfd sp!, {r4-r11, pc} diff --git a/src/platform/gba/asm/faceAddRoomTriangles.s b/src/platform/gba/asm/faceAddRoomTriangles.s new file mode 100644 index 00000000..3f62b7d7 --- /dev/null +++ b/src/platform/gba/asm/faceAddRoomTriangles.s @@ -0,0 +1,113 @@ +#include "common_asm.inc" + +polys .req r0 +count .req r1 +vp .req r2 +vg0 .req r3 +vg1 .req r4 +vg2 .req r5 +vg3 .req r6 +flags .req r7 +vp0 .req r8 +vp1 .req r9 +vp2 .req r10 +vertices .req r11 +ot .req r12 +face .req lr + +vx0 .req vg0 +vy0 .req vg1 +vx1 .req vg2 +vy1 .req vg3 +vx2 .req vg2 +vy2 .req vg2 + +vz0 .req vg0 +vz1 .req vg1 +vz2 .req vg2 +depth .req vg0 + +tmp .req flags +next .req vp0 + +.global faceAddRoomTriangles_asm +faceAddRoomTriangles_asm: + stmfd sp!, {r4-r11, lr} + + ldr vp, =gVerticesBase + ldr vp, [vp] + + ldr face, =gFacesBase + ldr face, [face] + + ldr ot, =gOT + ldr vertices, =gVertices + lsr vertices, #3 + + add polys, #2 // skip flags + +.loop: + ldrh vp0, [polys], #2 + ldrh vp1, [polys], #2 + ldrh vp2, [polys], #4 // + flags + + add vp0, vp, vp0, lsl #3 + add vp1, vp, vp1, lsl #3 + add vp2, vp, vp2, lsl #3 + + // fetch ((clip << 8) | g) + ldrh vg0, [vp0, #VERTEX_G] + ldrh vg1, [vp1, #VERTEX_G] + ldrh vg2, [vp2, #VERTEX_G] + + // check clipping + and tmp, vg0, vg1 + and tmp, vg2 + tst tmp, #CLIP_MASK + bne .skip + + // mark if should be clipped by viewport + orr tmp, vg0, vg1 + orr tmp, vg2 + tst tmp, #CLIP_MASK_VP + ldrh flags, [polys, #-10] + orrne flags, #FACE_CLIPPED + + // shift and compare VERTEX_G for gouraud rasterization + lsl vg0, #24 + cmp vg0, vg1, lsl #24 + cmpeq vg0, vg2, lsl #24 + addne flags, #FACE_GOURAUD + + CCW .skip + + // vz0 = MAX_Z3 (depth) + ldrh vz0, [vp0, #VERTEX_Z] + ldrh vz1, [vp1, #VERTEX_Z] + ldrh vz2, [vp2, #VERTEX_Z] + cmp vz0, vz1 + movlt vz0, vz1 + cmp vz0, vz2 + movlt vz0, vz2 + mov depth, vz0, lsr #OT_SHIFT + + // faceAdd + rsb vp0, vertices, vp0, lsr #3 + rsb vp1, vertices, vp1, lsr #3 + rsb vp2, vertices, vp2, lsr #3 + + orr vp1, vp0, vp1, lsl #16 + + orr flags, #FACE_TRIANGLE + + ldr next, [ot, depth, lsl #2] + str face, [ot, depth, lsl #2] + stmia face!, {flags, next, vp1, vp2} +.skip: + subs count, #1 + bne .loop + + ldr tmp, =gFacesBase + str face, [tmp] + + ldmfd sp!, {r4-r11, pc} diff --git a/src/platform/gba/asm/flush.s b/src/platform/gba/asm/flush.s new file mode 100644 index 00000000..91e46a4f --- /dev/null +++ b/src/platform/gba/asm/flush.s @@ -0,0 +1,192 @@ +#include "common_asm.inc" + +flags .req r0 // flags is always in r0 for rasterize & draw* calls +vXY .req r1 +vZG .req r2 +tmp .req r3 + +OT .req r4 +list .req r5 +face .req r6 +VERTICES .req r7 +TEXTURES .req r8 +SPRITES .req r9 +TILE .req r10 +MASK .req r11 + +index01 .req r12 +index23 .req lr + +faces .req vXY +uv01 .req index01 +uv23 .req index23 +uwvh .req index01 +verticesBase .req vZG +facesBase .req vZG +vertex .req vZG +texture .req tmp +texAnim .req vXY +texIndex .req tmp +texTile .req tmp +sprite .req tmp +sprIndex .req tmp +sprTile .req tmp +type .req tmp +zero .req tmp +uv .req tmp +vXY0 .req vXY +vZG0 .req vZG +vXY1 .req index01 +vZG1 .req index23 + +SP_SIZE = (16 * VERTEX_SIZEOF) + +.extern rasterize_c, drawTriangle, drawQuad, drawPoly + +.global flush_asm +flush_asm: + stmfd sp!, {r4-r11, lr} + + ldr verticesBase, =gVerticesBase + ldr VERTICES, =gVertices + str VERTICES, [verticesBase] + + ldr tmp, =gFacesBase + ldr faces, =gFaces + ldr facesBase, [tmp] + + cmp facesBase, faces + ldmeqfd sp!, {r4-r11, lr} + bxeq lr + + str faces, [tmp] + + ldr tmp, =level + ldr TILE, =gTile + ldr TEXTURES, [tmp, #LEVEL_TEXTURES] + ldr SPRITES, [tmp, #LEVEL_SPRITES] + ldr OT, =gOT + add list, OT, #((OT_SIZE - 1) << 2) + + mov MASK, #0xFF00 + orr MASK, MASK, MASK, lsl #16 + + sub sp, #SP_SIZE +.loop_ot: + ldr face, [list], #-4 // read the first face from the list and decrement + cmp face, #0 + beq .next_ot // list is empty, go next + + mov zero, #0 + str zero, [list, #4] // reset the list pointer in OT + +.loop_list: + ldmia face, {flags, face, index01, index23} // read face params and next face + + and type, flags, #FACE_TYPE_MASK + +.draw_primitive: // shadows, triangles, quads and clipped polys + cmp type, #FACE_TYPE_GTA + bgt .draw_sprite + + .set_vertices: + // 1st vertex + mov vertex, index01, lsl #16 + add vertex, VERTICES, vertex, lsr #(16 - 3) + ldmia vertex, {vXY, vZG} + stmia sp, {vXY, vZG} + + // 2nd vertex + add vertex, VERTICES, index01, lsr #(16 - 3) // assumption: vertex index will never exceed 8191 + ldmia vertex, {vXY, vZG} + str vXY, [sp, #(VERTEX_X + VERTEX_SIZEOF * 1)] + str vZG, [sp, #(VERTEX_Z + VERTEX_SIZEOF * 1)] + + // 3rd vertex + mov vertex, index23, lsl #16 + add vertex, VERTICES, vertex, lsr #(16 - 3) + ldmia vertex, {vXY, vZG} + str vXY, [sp, #(VERTEX_X + VERTEX_SIZEOF * 2)] + str vZG, [sp, #(VERTEX_Z + VERTEX_SIZEOF * 2)] + + // 4th vertex (quads only) + tst flags, #FACE_TRIANGLE + addeq vertex, VERTICES, index23, lsr #(16 - 3) + ldmeqia vertex, {vXY, vZG} + streq vXY, [sp, #(VERTEX_X + VERTEX_SIZEOF * 3)] + streq vZG, [sp, #(VERTEX_Z + VERTEX_SIZEOF * 3)] + + // skip texturing for FACE_TYPE_SHADOW and FACE_TYPE_F + cmp type, #FACE_TYPE_F + ble .draw + + .set_texture: + mov texIndex, flags, lsl #(32 - FACE_TEXTURE_BITS) + //cmp texIndex, #(MAX_ANIM_TEX << (32 - FACE_TEXTURE_BITS)) // TODO split to animated and static textures arrays + add texIndex, texIndex, texIndex, lsl #1 + add texture, TEXTURES, texIndex, lsr #(32 - FACE_TEXTURE_BITS - 2) + //addge texture, TEXTURES, texIndex, lsr #(32 - FACE_TEXTURE_BITS - 2) + //ldrlt texAnim, =gAnimTextures + //addlt texture, texAnim, texIndex, lsr #(32 - FACE_TEXTURE_BITS - 2) + + ldmia texture, {texTile, uv01, uv23} + str texTile, [TILE] + + and uv, MASK, uv01 + str uv, [sp, #(VERTEX_T + VERTEX_SIZEOF * 0)] + and uv, MASK, uv01, lsl #8 + str uv, [sp, #(VERTEX_T + VERTEX_SIZEOF * 1)] + and uv, MASK, uv23 + str uv, [sp, #(VERTEX_T + VERTEX_SIZEOF * 2)] + and uv, MASK, uv23, lsl #8 + str uv, [sp, #(VERTEX_T + VERTEX_SIZEOF * 3)] + + .draw: + // r0 = flags + mov r1, sp + adr lr, .next_face + + tst flags, #FACE_CLIPPED + bne drawPoly + tst flags, #FACE_TRIANGLE + bne drawTriangle + beq drawQuad + +.draw_sprite: // sprites and gui elements + mov vertex, index01, lsl #16 + add vertex, VERTICES, vertex, lsr #(16 - 3) + ldmia vertex, {vXY0, vZG0, vXY1, vZG1} + stmia sp, {vXY0, vZG0} + str vXY1, [sp, #(VERTEX_X + VERTEX_SIZEOF * 1)] + str vZG1, [sp, #(VERTEX_Z + VERTEX_SIZEOF * 1)] + + // r0 = flags + mov r1, sp + adr lr, .next_face + + // gui + cmp type, #FACE_TYPE_SPRITE + bne rasterize_asm + + // sprite + and sprIndex, flags, #0xFF + add sprite, SPRITES, sprIndex, lsl #4 + ldmia sprite, {sprTile, uwvh} + str sprTile, [TILE] + and uv, uwvh, MASK + str uv, [sp, #(VERTEX_T + VERTEX_SIZEOF * 0)] + bic uv, uwvh, MASK + str uv, [sp, #(VERTEX_T + VERTEX_SIZEOF * 1)] + b rasterize_asm + +.next_face: + cmp face, #0 + bne .loop_list + +.next_ot: + cmp list, OT + bge .loop_ot + + add sp, #SP_SIZE + ldmfd sp!, {r4-r11, lr} + bx lr diff --git a/src/platform/gba/asm/getSector.s b/src/platform/gba/asm/getSector.s new file mode 100644 index 00000000..49d61472 --- /dev/null +++ b/src/platform/gba/asm/getSector.s @@ -0,0 +1,44 @@ +#include "common_asm.inc" + +this .req r0 +x .req r1 +z .req r2 +info .req r3 +roomX .req r12 +roomZ .req roomX +sx .req x +sz .req z +sectors .req this +sectorsX .req roomX +sectorsZ .req roomZ +offset .req sectorsZ + +// const Sector* Room::getSector(int32 x, int32 z) const +.global _ZNK4Room9getSectorEii +_ZNK4Room9getSectorEii: + ldr info, [this, #4] + + // sx = X_CLAMP((x - (info->x << 8)) >> 10, 0, info->xSectors - 1); + ldrsh roomX, [info] + subs sx, x, roomX, lsl #8 + movlt sx, #0 + mov sx, sx, lsr #10 + ldrb sectorsX, [info, #20] + cmp sx, sectorsX + subge sx, sectorsX, #1 + + // sz = X_CLAMP((z - (info->z << 8)) >> 10, 0, info->zSectors - 1); + ldrsh roomZ, [info, #2] + subs sz, z, roomZ, lsl #8 + movlt sz, #0 + mov sz, sz, lsr #10 + ldrb sectorsZ, [info, #21] + cmp sz, sectorsZ + subge sz, sectorsZ, #1 + + // return sectors + sx * info->zSectors + sz; + ldr sectors, [this, #8] + mla offset, sx, sectorsZ, sz + add sectors, offset, lsl #3 // sizeof(Sector) == (1 << 3) + + bx lr diff --git a/src/platform/gba/asm/matrixLerp.s b/src/platform/gba/asm/matrixLerp.s new file mode 100644 index 00000000..f309fd2a --- /dev/null +++ b/src/platform/gba/asm/matrixLerp.s @@ -0,0 +1,115 @@ +#include "common_asm.inc" + +n .req r0 +pmul .req r1 +pdiv .req r2 +m0 .req r3 +m1 .req r4 +m2 .req r5 +n0 .req r6 +n1 .req r7 +n2 .req r12 +m .req lr +tmp .req m0 + +.macro load + ldmia m, {m0, m1, m2} + ldmia n, {n0, n1, n2} +.endm + +.macro store + stmia m, {m0, m1, m2} +.endm + +.macro next + add m, m, #16 + add n, n, #16 +.endm + +.macro _1_2 // a = (a + b) / 2 + load + add m0, m0, n0 + add m1, m1, n1 + add m2, m2, n2 + mov m0, m0, asr #1 + mov m1, m1, asr #1 + mov m2, m2, asr #1 + store +.endm + +.macro _1_4 // a = a + (b - a) / 4 + load + sub n0, n0, m0 + sub n1, n1, m1 + sub n2, n2, m2 + add m0, m0, n0, asr #2 + add m1, m1, n1, asr #2 + add m2, m2, n2, asr #2 + store +.endm + +.macro _3_4 // a = b - (b - a) / 4 + load + sub m0, n0, m0 + sub m1, n1, m1 + sub m2, n2, m2 + sub m0, n0, m0, asr #2 + sub m1, n1, m1, asr #2 + sub m2, n2, m2, asr #2 + store +.endm + +.macro _X_Y // a = a + (b - a) * mul / div + load + sub n0, n0, m0 + sub n1, n1, m1 + sub n2, n2, m2 + mul n0, pmul, n0 + mul n1, pmul, n1 + mul n2, pmul, n2 + add m0, m0, n0, asr #8 + add m1, m1, n1, asr #8 + add m2, m2, n2, asr #8 + store +.endm + +.macro lerp func + \func // e00, e01, e02 + next + \func // e10, e11, e12 + next + \func // e20, e21, e22 +.endm + +.global matrixLerp_asm +matrixLerp_asm: + stmfd sp!, {r4-r7, lr} + ldr m, =gMatrixPtr + ldr m, [m] +.check_2: + cmp pdiv, #2 + beq .m1_d2 +.check_4: + cmp pdiv, #4 + bne .mX_dY + cmp pmul, #1 + beq .m1_d4 + cmp pmul, #2 + beq .m1_d2 // 2/4 = 1/2 +.m3_d4: + lerp _3_4 + b .done +.m1_d4: + lerp _1_4 + b .done +.m1_d2: + lerp _1_2 + b .done +.mX_dY: + divLUT tmp, pdiv + mul tmp, pmul, tmp + mov pmul, tmp, asr #8 + lerp _X_Y +.done: + ldmfd sp!, {r4-r7, lr} + bx lr diff --git a/src/platform/gba/asm/matrixPush.s b/src/platform/gba/asm/matrixPush.s new file mode 100644 index 00000000..ae091cfd --- /dev/null +++ b/src/platform/gba/asm/matrixPush.s @@ -0,0 +1,29 @@ +#include "common_asm.inc" + +e0 .req r0 +e1 .req r1 +e2 .req r2 +e3 .req r3 +m .req e0 +src .req r12 +dst .req lr + +.global matrixPush_asm +matrixPush_asm: + stmfd sp!, {lr} + ldr m, =gMatrixPtr + ldr src, [m] + add dst, src, #(12 * 4) + str dst, [m] + + ldmia src!, {e0, e1, e2, e3} + stmia dst!, {e0, e1, e2, e3} + + ldmia src!, {e0, e1, e2, e3} + stmia dst!, {e0, e1, e2, e3} + + ldmia src!, {e0, e1, e2, e3} + stmia dst!, {e0, e1, e2, e3} + + ldmfd sp!, {lr} + bx lr diff --git a/src/platform/gba/asm/matrixRotate.s b/src/platform/gba/asm/matrixRotate.s new file mode 100644 index 00000000..f5d70798 --- /dev/null +++ b/src/platform/gba/asm/matrixRotate.s @@ -0,0 +1,288 @@ +#include "common_asm.inc" + +.macro sincos angle, sin, cos + ldr \sin, =gSinCosTable + ldr \sin, [\sin, \angle, lsl #2] + mov \cos, \sin, lsl #16 + mov \cos, \cos, asr #16 + mov \sin, \sin, asr #16 +.endm + +.macro rotxy x, y, sin, cos, t + mul \t, \y, \cos + mla \t, \x, \sin, \t + mul \x, \cos, \x + rsb \y, \y, #0 + mla \x, \y, \sin, \x + mov \y, \t, asr #FIXED_SHIFT + mov \x, \x, asr #FIXED_SHIFT +.endm + +angle .req r0 +e0 .req r1 +e1 .req r2 +s .req r3 +c .req r12 +v .req lr +m .req angle + +.global matrixRotateX_asm +matrixRotateX_asm: + stmfd sp!, {lr} + + mov angle, angle, lsl #16 + mov angle, angle, lsr #20 + + sincos angle, s, c + + ldr m, =gMatrixPtr + ldr m, [m] + + add m, m, #4 // skip first column + ldmia m, {e0, e1} + rotxy e1, e0, s, c, v + stmia m, {e0, e1} + + add m, #(4 * 4) + ldmia m, {e0, e1} + rotxy e1, e0, s, c, v + stmia m, {e0, e1} + + add m, #(4 * 4) + ldmia m, {e0, e1} + rotxy e1, e0, s, c, v + stmia m, {e0, e1} + + ldmfd sp!, {lr} + bx lr + +.global matrixRotateY_asm +matrixRotateY_asm: + stmfd sp!, {lr} + + mov angle, angle, lsl #16 + mov angle, angle, lsr #20 + + sincos angle, s, c + + ldr m, =gMatrixPtr + ldr m, [m] + + ldr e0, [m, #0] + ldr e1, [m, #8] + rotxy e0, e1, s, c, v + str e0, [m], #8 + str e1, [m], #8 + + ldr e0, [m, #0] + ldr e1, [m, #8] + rotxy e0, e1, s, c, v + str e0, [m], #8 + str e1, [m], #8 + + ldr e0, [m, #0] + ldr e1, [m, #8] + rotxy e0, e1, s, c, v + str e0, [m], #8 + str e1, [m], #8 + + ldmfd sp!, {lr} + bx lr + +.global matrixRotateZ_asm +matrixRotateZ_asm: + stmfd sp!, {lr} + + mov angle, angle, lsl #16 + mov angle, angle, lsr #20 + + sincos angle, s, c + + ldr m, =gMatrixPtr + ldr m, [m] + + ldmia m, {e0, e1} + rotxy e1, e0, s, c, v + stmia m, {e0, e1} + + add m, #(4 * 4) + ldmia m, {e0, e1} + rotxy e1, e0, s, c, v + stmia m, {e0, e1} + + add m, #(4 * 4) + ldmia m, {e0, e1} + rotxy e1, e0, s, c, v + stmia m, {e0, e1} + + ldmfd sp!, {lr} + bx lr + +angleX .req r0 +angleY .req r1 +angleZ .req r2 +e00 .req r3 +e01 .req r4 +e02 .req r5 +e10 .req r6 +e11 .req r7 +e12 .req r8 +e20 .req r9 +e21 .req r10 +e22 .req r11 +tmp .req r12 +sinX .req lr +sinY .req sinX +sinZ .req sinX +cosX .req angleX +cosY .req angleY +cosZ .req angleZ +mask .req tmp +mm .req tmp + +.global matrixRotateYXZ_asm +matrixRotateYXZ_asm: + mov mask, #0xFF + orr mask, mask, #0xF00 ; mask = 0xFFF + + and angleX, mask, angleX, lsr #4 + and angleY, mask, angleY, lsr #4 + and angleZ, mask, angleZ, lsr #4 + + orr mask, angleX, angleY + orrs mask, mask, angleZ + bxeq lr + + stmfd sp!, {r4-r11, lr} + + ldr mm, =gMatrixPtr + ldr mm, [mm] + ldmia mm, {e00, e01, e02} + add mm, #(4 * 4) + ldmia mm, {e10, e11, e12} + add mm, #(4 * 4) + ldmia mm, {e20, e21, e22} + +.rotY: + cmp angleY, #0 + beq .rotX + + sincos angleY, sinY, cosY + + rotxy e00, e02, sinY, cosY, tmp + rotxy e10, e12, sinY, cosY, tmp + rotxy e20, e22, sinY, cosY, tmp + +.rotX: + cmp angleX, #0 + beq .rotZ + + sincos angleX, sinX, cosX + + rotxy e02, e01, sinX, cosX, tmp + rotxy e12, e11, sinX, cosX, tmp + rotxy e22, e21, sinX, cosX, tmp + +.rotZ: + cmp angleZ, #0 + beq .done + + sincos angleZ, sinZ, cosZ + + rotxy e01, e00, sinZ, cosZ, tmp + rotxy e11, e10, sinZ, cosZ, tmp + rotxy e21, e20, sinZ, cosZ, tmp + +.done: + ldr mm, =gMatrixPtr + ldr mm, [mm] + + stmia mm, {e00, e01, e02} + add mm, #(4 * 4) + stmia mm, {e10, e11, e12} + add mm, #(4 * 4) + stmia mm, {e20, e21, e22} + + ldmfd sp!, {r4-r11, lr} + bx lr + +q .req r0 +n .req r1 +mx .req r3 +my .req q + +.global matrixRotateYQ_asm +matrixRotateYQ_asm: + cmp q, #2 + bxeq lr + + ldr n, =gMatrixPtr + ldr n, [n] + + cmp q, #0 + beq .q_0 + cmp q, #1 + beq .q_1 + +.q_3: + ldr mx, [n, #0] + ldr my, [n, #8] + rsb my, my, #0 + str my, [n, #0] + str mx, [n, #8] + + ldr mx, [n, #16] + ldr my, [n, #24] + rsb my, my, #0 + str my, [n, #16] + str mx, [n, #24] + + ldr mx, [n, #32] + ldr my, [n, #40] + rsb my, my, #0 + str my, [n, #32] + str mx, [n, #40] + bx lr + +.q_0: + ldr mx, [n, #0] + ldr my, [n, #8] + rsb mx, mx, #0 + rsb my, my, #0 + str mx, [n, #0] + str my, [n, #8] + + ldr mx, [n, #16] + ldr my, [n, #24] + rsb mx, mx, #0 + rsb my, my, #0 + str mx, [n, #16] + str my, [n, #24] + + ldr mx, [n, #32] + ldr my, [n, #40] + rsb mx, mx, #0 + rsb my, my, #0 + str mx, [n, #32] + str my, [n, #40] + bx lr + +.q_1: + ldr mx, [n, #0] + ldr my, [n, #8] + rsb mx, mx, #0 + str my, [n, #0] + str mx, [n, #8] + + ldr mx, [n, #16] + ldr my, [n, #24] + rsb mx, mx, #0 + str my, [n, #16] + str mx, [n, #24] + + ldr mx, [n, #32] + ldr my, [n, #40] + rsb mx, mx, #0 + str my, [n, #32] + str mx, [n, #40] + bx lr diff --git a/src/platform/gba/asm/matrixSetBasis.s b/src/platform/gba/asm/matrixSetBasis.s new file mode 100644 index 00000000..4d71c68f --- /dev/null +++ b/src/platform/gba/asm/matrixSetBasis.s @@ -0,0 +1,32 @@ +#include "common_asm.inc" + +dst .req r0 +src .req r1 + +e0 .req r2 +e1 .req r3 +e2 .req r12 + +.global matrixSetBasis_asm +matrixSetBasis_asm: + // row-major + // e0 e1 e2 x + // e0 e1 e2 y + // e0 e1 e2 z + + ldmia src, {e0, e1, e2} + stmia dst, {e0, e1, e2} + add src, #(4 * 4) + add dst, #(4 * 4) + + ldmia src, {e0, e1, e2} + stmia dst, {e0, e1, e2} + add src, #(4 * 4) + add dst, #(4 * 4) + + ldmia src, {e0, e1, e2} + stmia dst, {e0, e1, e2} + add src, #(4 * 4) + add dst, #(4 * 4) + + bx lr diff --git a/src/platform/gba/asm/matrixSetIdentity.s b/src/platform/gba/asm/matrixSetIdentity.s new file mode 100644 index 00000000..e6c2268a --- /dev/null +++ b/src/platform/gba/asm/matrixSetIdentity.s @@ -0,0 +1,31 @@ +#include "common_asm.inc" + +e0 .req r0 +e1 .req r1 +e2 .req r2 +e3 .req r3 +e4 .req r12 +m .req lr + +.global matrixSetIdentity_asm +matrixSetIdentity_asm: + stmfd sp!, {lr} + ldr m, =gMatrixPtr + ldr m, [m] + mov e0, #0x4000 + mov e1, #0 + mov e2, #0 + mov e3, #0 + mov e4, #0 + + // row-major + // e0 e1 e2 e3 + // e4 e0 e1 e2 + // e3 e4 e0 e1 + + stmia m!, {e0, e1, e2, e3, e4} + stmia m!, {e0, e1, e2, e3, e4} + stmia m!, {e0, e1} + + ldmfd sp!, {lr} + bx lr diff --git a/src/platform/gba/asm/matrixTranslate.s b/src/platform/gba/asm/matrixTranslate.s new file mode 100644 index 00000000..ffd5888a --- /dev/null +++ b/src/platform/gba/asm/matrixTranslate.s @@ -0,0 +1,109 @@ +#include "common_asm.inc" + +x .req r0 +y .req r1 +z .req r2 +e0 .req r3 +e1 .req r4 +e2 .req r5 +v .req r12 +m .req lr + +.global matrixTranslateRel_asm +matrixTranslateRel_asm: + stmfd sp!, {r4-r5, lr} + + ldr m, =gMatrixPtr + ldr m, [m] + + // x + ldmia m!, {e0, e1, e2, v} + mla v, e0, x, v + mla v, e1, y, v + mla v, e2, z, v + stmdb m, {v} + + // y + ldmia m!, {e0, e1, e2, v} + mla v, e0, x, v + mla v, e1, y, v + mla v, e2, z, v + stmdb m, {v} + + // z + ldmia m!, {e0, e1, e2, v} + mla v, e0, x, v + mla v, e1, y, v + mla v, e2, z, v + stmdb m, {v} + + ldmfd sp!, {r4-r5, lr} + bx lr + +.global matrixTranslateAbs_asm +matrixTranslateAbs_asm: + stmfd sp!, {r4-r5, lr} + + ldr v, =gCameraViewPos + ldmia v, {e0, e1, e2} + sub x, x, e0 + sub y, y, e1 + sub z, z, e2 + + ldr m, =gMatrixPtr + ldr m, [m] + + // x + ldmia m!, {e0, e1, e2} + mul v, e0, x + mla v, e1, y, v + mla v, e2, z, v + stmia m!, {v} + + // y + ldmia m!, {e0, e1, e2} + mul v, e0, x + mla v, e1, y, v + mla v, e2, z, v + stmia m!, {v} + + // z + ldmia m!, {e0, e1, e2} + mul v, e0, x + mla v, e1, y, v + mla v, e2, z, v + stmia m!, {v} + + ldmfd sp!, {r4-r5, lr} + bx lr + +.global matrixTranslateSet_asm +matrixTranslateSet_asm: + stmfd sp!, {r4-r5, lr} + + ldr m, =gMatrixPtr + ldr m, [m] + + // x + ldmia m!, {e0, e1, e2} + mul v, e0, x + mla v, e1, y, v + mla v, e2, z, v + stmia m!, {v} + + // y + ldmia m!, {e0, e1, e2} + mul v, e0, x + mla v, e1, y, v + mla v, e2, z, v + stmia m!, {v} + + // z + ldmia m!, {e0, e1, e2} + mul v, e0, x + mla v, e1, y, v + mla v, e2, z, v + stmia m!, {v} + + ldmfd sp!, {r4-r5, lr} + bx lr diff --git a/src/platform/gba/asm/rasterize.s b/src/platform/gba/asm/rasterize.s new file mode 100644 index 00000000..db543e1f --- /dev/null +++ b/src/platform/gba/asm/rasterize.s @@ -0,0 +1,49 @@ +#include "common_asm.inc" + +flags .req r0 +L .req r1 +R .req r2 +y .req r3 +type .req r12 +pixel .req flags + +.extern rasterizeS_asm +.extern rasterizeF_asm +.extern rasterizeFT_asm +.extern rasterizeFTA_asm +.extern rasterizeGT_asm +.extern rasterizeGTA_asm +.extern rasterizeSprite_c +.extern rasterizeFillS_asm +.extern rasterizeLineH_asm +.extern rasterizeLineV_asm +.extern rasterize_dummy + +.global rasterize_asm +rasterize_asm: + and type, flags, #FACE_TYPE_MASK + + cmp type, #FACE_TYPE_F + andeq R, flags, #0xFF // R = face color for FACE_TYPE_F + movne R, L // R = L otherwise + + ldr pixel, =fb + ldr pixel, [pixel] + ldrsh y, [L, #VERTEX_Y] + + // pixel += y * 240 -> (y * 16 - y) * 16 + rsb y, y, y, lsl #4 + add pixel, pixel, y, lsl #4 + + add pc, type, lsr #(FACE_TYPE_SHIFT - 2) + nop + b rasterizeS_asm + b rasterizeF_asm + b rasterizeFT_asm + b rasterizeFTA_asm + b rasterizeGT_asm + b rasterizeGTA_asm + b rasterizeSprite_c + b rasterizeFillS_asm + b rasterizeLineH_asm + b rasterizeLineV_asm diff --git a/src/platform/gba/asm/rasterizeF.s b/src/platform/gba/asm/rasterizeF.s new file mode 100644 index 00000000..28cb217b --- /dev/null +++ b/src/platform/gba/asm/rasterizeF.s @@ -0,0 +1,136 @@ +#include "common_asm.inc" + +pixel .req r0 +L .req r1 +color .req r2 +index .req r3 +Lh .req r4 +Rh .req r5 +Lx .req r6 +Rx .req r7 +Ldx .req r8 +Rdx .req r9 +N .req r10 +tmp .req r11 +pair .req r12 +width .req lr + +R .req color +h .req N +Rxy .req tmp +Ry2 .req Rh +Lxy .req tmp +Ly2 .req Lh +LMAP .req Lx +ptr .req tmp + +.global rasterizeF_asm +rasterizeF_asm: + stmfd sp!, {r4-r11, lr} + + add LMAP, color, #LMAP_ADDR + ldrb tmp, [L, #VERTEX_G] + ldrb index, [LMAP, tmp, lsl #8] // index = lightmap[color + L->v.g * 256] + + mov R, L + + mov Lh, #0 // Lh = 0 + mov Rh, #0 // Rh = 0 + +.loop: + cmp Lh, #0 + bne .calc_left_end // if (Lh != 0) end with left + + .calc_left_start: + ldr Lxy, [L, #VERTEX_X] // Lxy = (L->v.y << 16) | (L->v.x) + ldrsb N, [L, #VERTEX_PREV] // N = L + L->prev + add L, L, N, lsl #VERTEX_SIZEOF_SHIFT + ldrsh Ly2, [L, #VERTEX_Y] // Ly2 = N->v.y + + subs Lh, Ly2, Lxy, asr #16 // Lh = N->v.y - L->v.y + blt .exit // if (Lh < 0) return + beq .calc_left_start + + lsl Lx, Lxy, #16 // Lx = L->v.x << 16 + cmp Lh, #1 // if (Lh == 1) skip Ldx calc + beq .calc_left_end + + divLUT tmp, Lh // tmp = FixedInvU(Lh) + + ldrsh Ldx, [L, #VERTEX_X] + sub Ldx, Lx, asr #16 + mul Ldx, tmp // Ldx = tmp * (N->v.x - L->v.x) + .calc_left_end: + + cmp Rh, #0 + bne .calc_right_end // if (Rh != 0) end with right + + .calc_right_start: + ldr Rxy, [R, #VERTEX_X] // Rxy = (R->v.y << 16) | (R->v.x) + ldrsb N, [R, #VERTEX_NEXT] // N = R + R->next + add R, R, N, lsl #VERTEX_SIZEOF_SHIFT + ldrsh Ry2, [R, #VERTEX_Y] // Ry2 = N->v.y + + subs Rh, Ry2, Rxy, asr #16 // Rh = N->v.y - R->v.y + blt .exit // if (Rh < 0) return + beq .calc_right_start + + lsl Rx, Rxy, #16 // Rx = R->v.x << 16 + cmp Rh, #1 // if (Rh == 1) skip Rdx calc + beq .calc_right_end + + divLUT tmp, Rh // tmp = FixedInvU(Rh) + + ldrsh Rdx, [R, #VERTEX_X] + sub Rdx, Rx, asr #16 + mul Rdx, tmp // Rdx = tmp * (N->v.x - Rx) + .calc_right_end: + + cmp Rh, Lh // if (Rh < Lh) + movlt h, Rh // h = Rh + movge h, Lh // else h = Lh + sub Lh, h // Lh -= h + sub Rh, h // Rh -= h + +.scanline_start: + asr tmp, Lx, #16 // x1 = (Lx >> 16) + rsbs width, tmp, Rx, asr #16 // width = (Rx >> 16) - x1 + ble .scanline_end // if (width <= 0) go next scanline + + add ptr, pixel, tmp // ptr = pixel + x1 + + // 2 bytes alignment (VRAM write requirement) +.align_left: + tst ptr, #1 // if (ptr & 1) + beq .align_right + ldrb pair, [ptr, #-1]! // *ptr++ = (*ptr & 0x00FF) | (index << 8) + orr pair, index, lsl #8 + strh pair, [ptr], #2 + subs width, #1 // width-- + beq .scanline_end // if (width == 0) + +.align_right: + tst width, #1 + beq .scanline_block_2px + ldrb pair, [ptr, width] + subs width, #1 // width-- + orr pair, index, pair, lsl #8 + strh pair, [ptr, width] + beq .scanline_end // if (width == 0) + +.scanline_block_2px: + strb index, [ptr], #2 // VRAM one as two bytes write hack + subs width, #2 + bne .scanline_block_2px + +.scanline_end: + add Lx, Ldx // Lx += Ldx + add Rx, Rdx // Rx += Rdx + add pixel, #FRAME_WIDTH // pixel += FRAME_WIDTH (240) + + subs h, #1 + bne .scanline_start + b .loop + +.exit: + ldmfd sp!, {r4-r11, pc} \ No newline at end of file diff --git a/src/platform/gba/asm/rasterizeFT.s b/src/platform/gba/asm/rasterizeFT.s new file mode 100644 index 00000000..8cca5aee --- /dev/null +++ b/src/platform/gba/asm/rasterizeFT.s @@ -0,0 +1,257 @@ +#include "common_asm.inc" + +pixel .req r0 +L .req r1 +R .req r2 +LMAP .req r3 + +TILE .req r4 +tmp .req r5 +N .req r6 +Lh .req r7 +Rh .req r8 + +Lx .req r9 +Rx .req r10 +Lt .req r11 +Rt .req r12 +h .req lr + +ptr .req tmp + +Ldx .req h +Rdx .req h + +Ldt .req h +Rdt .req h + +indexA .req Lh +indexB .req Rh +Rxy .req tmp +Ry2 .req Rh +Lxy .req tmp +Ly2 .req Lh + +inv .req Lh +width .req N +t .req L +dtdx .req R + +duv .req R +du .req L +dv .req R + +Ldu .req N +Ldv .req h + +Rdu .req N +Rdv .req h + +Rti .req indexB + +sLdx .req tmp +sLdt .req N +sRdx .req Lh +sRdt .req Rh + +SP_LDX = 0 +SP_LDT = 4 +SP_RDX = 8 +SP_RDT = 12 +SP_L = 16 +SP_R = 20 +SP_LH = 24 +SP_RH = 28 +SP_SIZE = 32 + +.macro PUT_PIXELS + tex indexA, t + lit indexA + + add t, dtdx, lsl #1 + //orr indexA, indexA, lsl #8 + strb indexA, [ptr], #2 // writing a byte to GBA VRAM will write a half word for free +.endm + +.global rasterizeFT_asm +rasterizeFT_asm: + stmfd sp!, {r4-r11, lr} + sub sp, #SP_SIZE // reserve stack space for [Ldx, Ldt, Rdx, Rdt] + + mov LMAP, #LMAP_ADDR + ldrb tmp, [L, #VERTEX_G] + add LMAP, tmp, lsl #8 // tmp = (L->v.g << 8) + + ldr TILE, =gTile + ldr TILE, [TILE] + + mov Lh, #0 // Lh = 0 + mov Rh, #0 // Rh = 0 + +.loop: + + cmp Lh, #0 + bgt .calc_left_end // if (Lh != 0) end with left + + .calc_left_start: + ldrsb N, [L, #VERTEX_PREV] // N = L + L->prev + add N, L, N, lsl #VERTEX_SIZEOF_SHIFT + ldr Lxy, [L, #VERTEX_X] // Lxy = (L->v.y << 16) | (L->v.x) + ldrsh Ly2, [N, #VERTEX_Y] // Ly2 = N->v.y + + subs Lh, Ly2, Lxy, asr #16 // Lh = N->v.y - L->v.y + blt .exit // if (Lh < 0) return + ldrne Lt, [L, #VERTEX_T] // Lt = L->t + mov L, N // L = N + beq .calc_left_start + + lsl Lx, Lxy, #16 // Lx = L->v.x << 16 + cmp Lh, #1 // if (Lh <= 1) skip Ldx calc + beq .calc_left_end + + divLUT tmp, Lh // tmp = FixedInvU(Lh) + + ldrsh Ldx, [L, #VERTEX_X] + sub Ldx, Lx, asr #16 + mul Ldx, tmp // Ldx = tmp * (N->v.x - Lx) + str Ldx, [sp, #SP_LDX] // store Ldx to stack + + ldr Ldt, [L, #VERTEX_T] + sub Ldt, Lt // Ldt = N->v.t - Lt + scaleUV Ldt, Ldu, Ldv, tmp + str Ldt, [sp, #SP_LDT] // store Ldt to stack + .calc_left_end: + + cmp Rh, #0 + bgt .calc_right_end // if (Rh != 0) end with right + + .calc_right_start: + ldrsb N, [R, #VERTEX_NEXT] // N = R + R->next + add N, R, N, lsl #VERTEX_SIZEOF_SHIFT + ldr Rxy, [R, #VERTEX_X] // Rxy = (R->v.y << 16) | (R->v.x) + ldrsh Ry2, [N, #VERTEX_Y] // Ry2 = N->v.y + + subs Rh, Ry2, Rxy, asr #16 // Rh = Ry2 - Rxy + blt .exit // if (Rh < 0) return + ldrne Rt, [R, #VERTEX_T] // Rt = R->t + mov R, N // R = N + beq .calc_right_start + + lsl Rx, Rxy, #16 // Rx = R->v.x << 16 + cmp Rh, #1 // if (Rh <= 1) skip Rdx calc + beq .calc_right_end + + divLUT tmp, Rh // tmp = FixedInvU(Rh) + + ldrsh Rdx, [R, #VERTEX_X] + sub Rdx, Rx, asr #16 + mul Rdx, tmp // Rdx = tmp * (N->v.x - Rx) + str Rdx, [sp, #SP_RDX] // store Rdx to stack + + ldr Rdt, [R, #VERTEX_T] + sub Rdt, Rt // Rdt = N->v.t - Rt + scaleUV Rdt, Rdu, Rdv, tmp + str Rdt, [sp, #SP_RDT] // store Rdt to stack + .calc_right_end: + + cmp Rh, Lh // if (Rh < Lh) + movlt h, Rh // h = Rh + movge h, Lh // else h = Lh + sub Lh, h // Lh -= h + sub Rh, h // Rh -= h + + add tmp, sp, #SP_L + stmia tmp, {L, R, Lh, Rh} + +.scanline_start: + asr tmp, Lx, #16 // x1 = (Lx >> 16) + rsbs width, tmp, Rx, asr #16 // width = (Rx >> 16) - x1 + ble .scanline_end // if (width <= 0) go next scanline + + add ptr, pixel, tmp // ptr = pixel + x1 + + divLUT inv, width // inv = FixedInvU(width) + + sub dtdx, Rt, Lt // duv = Rt - Lt + scaleUV dtdx, du, dv, inv + + mov t, Lt // t = Lt + + // 2 bytes alignment (VRAM write requirement) +.align_left: + tst ptr, #1 // if (ptr & 1) + beq .align_right + + tex indexA, t + lit indexA + + ldrb indexB, [ptr, #-1]! // read pal index from VRAM (byte) + orr indexB, indexA, lsl #8 + strh indexB, [ptr], #2 + add t, dtdx + + subs width, #1 // width-- + beq .scanline_end // if (width == 0) + +.align_right: + tst width, #1 + beq .align_block_4px + + sub Rti, Rt, dtdx + tex indexA, Rti + lit indexA + + ldrb indexB, [ptr, width] + subs width, #1 // width-- + orr indexB, indexA, indexB, lsl #8 + strh indexB, [ptr, width] + + beq .scanline_end // if (width == 0) + +.align_block_4px: + tst width, #2 + beq .align_block_8px + + PUT_PIXELS + + subs width, #2 + beq .scanline_end + +.align_block_8px: + tst width, #4 + beq .scanline_block_8px + + PUT_PIXELS + PUT_PIXELS + + subs width, #4 + beq .scanline_end + +.scanline_block_8px: + PUT_PIXELS + PUT_PIXELS + PUT_PIXELS + PUT_PIXELS + + subs width, #8 + bne .scanline_block_8px + +.scanline_end: + ldmia sp, {sLdx, sLdt, sRdx, sRdt} + add Lx, sLdx + add Lt, sLdt + add Rx, sRdx + add Rt, sRdt + + add pixel, #FRAME_WIDTH // pixel += FRAME_WIDTH (240) + + subs h, #1 + bne .scanline_start + + add tmp, sp, #SP_L + ldmia tmp, {L, R, Lh, Rh} + b .loop + +.exit: + add sp, #SP_SIZE // revert reserved space for [Ldx, Ldt, Rdx, Rdt] + ldmfd sp!, {r4-r11, pc} \ No newline at end of file diff --git a/src/platform/gba/asm/rasterizeFTA.s b/src/platform/gba/asm/rasterizeFTA.s new file mode 100644 index 00000000..20fe35b4 --- /dev/null +++ b/src/platform/gba/asm/rasterizeFTA.s @@ -0,0 +1,261 @@ +#include "common_asm.inc" + +pixel .req r0 +L .req r1 +R .req r2 +LMAP .req r3 + +TILE .req r4 +tmp .req r5 +N .req r6 +Lh .req r7 +Rh .req r8 + +Lx .req r9 +Rx .req r10 +Lt .req r11 +Rt .req r12 +h .req lr + +ptr .req tmp + +Ldx .req h +Rdx .req h + +Ldt .req h +Rdt .req h + +indexA .req Lh +indexB .req Rh +Rxy .req tmp +Ry2 .req Rh +Lxy .req tmp +Ly2 .req Lh + +inv .req Lh +width .req N +t .req L +dtdx .req R + +duv .req R +du .req L +dv .req R + +Ldu .req N +Ldv .req h + +Rdu .req N +Rdv .req h + +Rti .req indexB + +sLdx .req tmp +sLdt .req N +sRdx .req Lh +sRdt .req Rh + +SP_LDX = 0 +SP_LDT = 4 +SP_RDX = 8 +SP_RDT = 12 +SP_L = 16 +SP_R = 20 +SP_LH = 24 +SP_RH = 28 +SP_SIZE = 32 + +.macro PUT_PIXELS + tex indexA, t + add t, dtdx, lsl #1 + cmp indexA, #0 + ldrneb indexA, [LMAP, indexA] + strneb indexA, [ptr] + add ptr, #2 +.endm + +.global rasterizeFTA_asm +rasterizeFTA_asm: + stmfd sp!, {r4-r11, lr} + sub sp, #SP_SIZE // reserve stack space for [Ldx, Ldt, Rdx, Rdt] + + mov LMAP, #LMAP_ADDR + ldrb tmp, [L, #VERTEX_G] + add LMAP, tmp, lsl #8 // tmp = (L->v.g << 8) + + ldr TILE, =gTile + ldr TILE, [TILE] + + mov Lh, #0 // Lh = 0 + mov Rh, #0 // Rh = 0 + +.loop: + + cmp Lh, #0 + bne .calc_left_end // if (Lh != 0) end with left + + .calc_left_start: + ldrsb N, [L, #VERTEX_PREV] // N = L + L->prev + add N, L, N, lsl #VERTEX_SIZEOF_SHIFT + ldr Lxy, [L, #VERTEX_X] // Lxy = (L->v.y << 16) | (L->v.x) + ldrsh Ly2, [N, #VERTEX_Y] // Ly2 = N->v.y + + subs Lh, Ly2, Lxy, asr #16 // Lh = N->v.y - L->v.y + blt .exit // if (Lh < 0) return + ldrne Lt, [L, #VERTEX_T] // Lt = L->t + mov L, N // L = N + beq .calc_left_start + + lsl Lx, Lxy, #16 // Lx = L->v.x << 16 + cmp Lh, #1 // if (Lh <= 1) skip Ldx calc + beq .calc_left_end + + divLUT tmp, Lh // tmp = FixedInvU(Lh) + + ldrsh Ldx, [L, #VERTEX_X] + sub Ldx, Lx, asr #16 + mul Ldx, tmp // Ldx = tmp * (N->v.x - Lx) + str Ldx, [sp, #SP_LDX] // store Ldx to stack + + ldr Ldt, [L, #VERTEX_T] + sub Ldt, Lt // Ldt = N->v.t - Lt + scaleUV Ldt, Ldu, Ldv, tmp + str Ldt, [sp, #SP_LDT] // store Ldt to stack + .calc_left_end: + + cmp Rh, #0 + bne .calc_right_end // if (Rh != 0) end with right + + .calc_right_start: + ldrsb N, [R, #VERTEX_NEXT] // N = R + R->next + add N, R, N, lsl #VERTEX_SIZEOF_SHIFT + ldr Rxy, [R, #VERTEX_X] // Rxy = (R->v.y << 16) | (R->v.x) + ldrsh Ry2, [N, #VERTEX_Y] // Ry2 = N->v.y + + subs Rh, Ry2, Rxy, asr #16 // Rh = N->v.y - R->v.y + blt .exit // if (Rh < 0) return + ldrne Rt, [R, #VERTEX_T] // Rt = R->t + mov R, N // R = N + beq .calc_right_start + + lsl Rx, Rxy, #16 // Rx = R->v.x << 16 + cmp Rh, #1 // if (Rh <= 1) skip Rdx calc + beq .calc_right_end + + divLUT tmp, Rh // tmp = FixedInvU(Rh) + + ldrsh Rdx, [R, #VERTEX_X] + sub Rdx, Rx, asr #16 + mul Rdx, tmp // Rdx = tmp * (N->v.x - Rx) + str Rdx, [sp, #SP_RDX] // store Rdx to stack + + ldr Rdt, [R, #VERTEX_T] + sub Rdt, Rt // Rdt = N->v.t - Rt + scaleUV Rdt, Rdu, Rdv, tmp + str Rdt, [sp, #SP_RDT] // store Rdt to stack + .calc_right_end: + + cmp Rh, Lh // if (Rh < Lh) + movlt h, Rh // h = Rh + movge h, Lh // else h = Lh + sub Lh, h // Lh -= h + sub Rh, h // Rh -= h + + add tmp, sp, #SP_L + stmia tmp, {L, R, Lh, Rh} + +.scanline_start: + asr tmp, Lx, #16 // x1 = (Lx >> 16) + rsbs width, tmp, Rx, asr #16 // width = (Rx >> 16) - x1 + ble .scanline_end // if (width <= 0) go next scanline + + add ptr, pixel, tmp // ptr = pixel + x1 + + divLUT inv, width // inv = FixedInvU(width) + + sub dtdx, Rt, Lt // duv = Rt - Lt + scaleUV dtdx, du, dv, inv + + mov t, Lt // t = Lt + + // 2 bytes alignment (VRAM write requirement) +.align_left: + tst ptr, #1 // if (ptr & 1) + beq .align_right + + tex indexA, t + + cmp indexA, #0 + ldrneb indexB, [ptr, #-1]! // read pal index from VRAM (byte) + ldrneb indexA, [LMAP, indexA] + orrne indexB, indexA, lsl #8 + strneh indexB, [ptr], #2 + addeq ptr, #1 + add t, dtdx + + subs width, #1 // width-- + beq .scanline_end // if (width == 0) + +.align_right: + tst width, #1 + beq .align_block_4px + + sub Rti, Rt, dtdx + tex indexA, Rti + + cmp indexA, #0 + ldrneb indexA, [LMAP, indexA] + ldrneb indexB, [ptr, width] + orrne indexB, indexA, indexB, lsl #8 + addne indexA, ptr, width + strneh indexB, [indexA, #-1] + + subs width, #1 // width-- + beq .scanline_end // if (width == 0) + +.align_block_4px: + tst width, #2 + beq .align_block_8px + + PUT_PIXELS + + subs width, #2 + beq .scanline_end + +.align_block_8px: + tst width, #4 + beq .scanline_block_8px + + PUT_PIXELS + PUT_PIXELS + + subs width, #4 + beq .scanline_end + +.scanline_block_8px: + PUT_PIXELS + PUT_PIXELS + PUT_PIXELS + PUT_PIXELS + + subs width, #8 + bne .scanline_block_8px + +.scanline_end: + ldmia sp, {sLdx, sLdt, sRdx, sRdt} + add Lx, sLdx + add Lt, sLdt + add Rx, sRdx + add Rt, sRdt + + add pixel, #FRAME_WIDTH // pixel += FRAME_WIDTH (240) + + subs h, #1 + bne .scanline_start + + add tmp, sp, #SP_L + ldmia tmp, {L, R, Lh, Rh} + b .loop + +.exit: + add sp, #SP_SIZE // revert reserved space for [Ldx, Ldt, Rdx, Rdt] + ldmfd sp!, {r4-r11, pc} \ No newline at end of file diff --git a/src/platform/gba/asm/rasterizeFillS.s b/src/platform/gba/asm/rasterizeFillS.s new file mode 100644 index 00000000..cb787cef --- /dev/null +++ b/src/platform/gba/asm/rasterizeFillS.s @@ -0,0 +1,71 @@ +#include "common_asm.inc" + +pixel .req r0 +L .req r1 +R .req r2 +p .req r4 +w .req r5 +indexA .req r6 +indexB .req r12 +shade .req lr +width .req L +height .req R +LMAP .req shade + +.global rasterizeFillS_asm +rasterizeFillS_asm: + stmfd sp!, {r4-r6, lr} + + add R, #VERTEX_SIZEOF + ldrsh p, [L, #VERTEX_X] + add pixel, p + ldrb shade, [L, #VERTEX_G] + ldrsh width, [R, #VERTEX_X] + ldrsh height, [R, #VERTEX_Y] + lsl shade, #8 + add LMAP, shade, #LMAP_ADDR + +.loop: + mov p, pixel + mov w, width + + // 2 bytes alignment (VRAM write requirement) +.align_left: + tst p, #1 + beq .align_right + ldrh indexA, [p, #-1]! + ldrb indexB, [LMAP, indexA, lsr #8] + and indexA, indexA, #0xFF + orr indexA, indexB, lsl #8 + strh indexA, [p], #2 + subs w, #1 + beq .scanline_end + +.align_right: + tst w, #1 + beq .scanline_block_2px + subs w, #1 + ldrh indexA, [p, w] + and indexB, indexA, #0xFF + ldrb indexB, [LMAP, indexB] + and indexA, indexA, #0xFF00 + orr indexA, indexA, indexB + strh indexA, [p, w] + beq .scanline_end + +.scanline_block_2px: + ldrh indexA, [p] + and indexB, indexA, #0xFF + ldrb indexA, [LMAP, indexA, lsr #8] + ldrb indexB, [LMAP, indexB] + orr indexA, indexB, indexA, lsl #8 + strh indexA, [p], #2 + subs w, #2 + bne .scanline_block_2px + +.scanline_end: + add pixel, #FRAME_WIDTH + subs height, #1 + bne .loop + + ldmfd sp!, {r4-r6, pc} diff --git a/src/platform/gba/asm/rasterizeGT.s b/src/platform/gba/asm/rasterizeGT.s new file mode 100644 index 00000000..0e4c9d7d --- /dev/null +++ b/src/platform/gba/asm/rasterizeGT.s @@ -0,0 +1,315 @@ +#include "common_asm.inc" + +pixel .req r0 +L .req r1 +R .req r2 + +Lh .req r3 +Rh .req r4 + +Lx .req r5 +Rx .req r6 + +Lg .req r7 +Rg .req r8 + +Lt .req r9 +Rt .req r10 + +tmp .req r11 +N .req r12 + +TILE .req lr + +h .req N + +LMAP .req tmp + +Ldx .req h +Rdx .req h + +Ldg .req h +Rdg .req h + +Ldt .req h +Rdt .req h + +indexA .req Lh +indexB .req tmp + +Rxy .req tmp +Ry2 .req Rh +Lxy .req tmp +Ly2 .req Lh + +inv .req Lh + +ptr .req Lx +width .req Rh + +g .req Lg +dgdx .req L + +t .req Lt +dtdx .req R +du .req L +dv .req R + +Ldu .req TILE +Ldv .req N + +Rdu .req TILE +Rdv .req N + +Rti .req tmp +Rgi .req tmp + +sLdx .req L +sLdg .req R +sLdt .req Lh +sRdx .req Rh +sRdg .req tmp +sRdt .req tmp // not enough regs for one ldmia + +SP_LDX = 0 +SP_LDG = 4 +SP_LDT = 8 +SP_RDX = 12 +SP_RDG = 16 +SP_RDT = 20 +SP_L = 24 +SP_R = 28 +SP_LH = 32 +SP_RH = 36 +SP_SIZE = 40 +SP_TILE = SP_SIZE + +.macro PUT_PIXELS + bic LMAP, g, #255 + add g, dgdx + + tex indexA, t + lit indexA + + add t, dtdx, lsl #1 + //orr indexA, indexA, lsl #8 + strb indexA, [ptr], #2 // writing a byte to GBA VRAM will write a half word for free +.endm + +.global rasterizeGT_asm +rasterizeGT_asm: + ldr r3, =gTile + ldr r3, [r3] + + stmfd sp!, {r3-r11, lr} + sub sp, #SP_SIZE // reserve stack space for [Ldx, Ldg, Ldt, Rdx, Rdg, Rdt] + + mov Lh, #0 // Lh = 0 + mov Rh, #0 // Rh = 0 + +.loop: + + cmp Lh, #0 + bne .calc_left_end // if (Lh != 0) end with left + + .calc_left_start: + ldrsb N, [L, #VERTEX_PREV] // N = L + L->prev + add N, L, N, lsl #VERTEX_SIZEOF_SHIFT + ldr Lxy, [L, #VERTEX_X] // Lxy = (L->v.y << 16) | (L->v.x) + ldrsh Ly2, [N, #VERTEX_Y] // Ly2 = N->v.y + + subs Lh, Ly2, Lxy, asr #16 // Lh = N->v.y - L->v.y + blt .exit // if (Lh < 0) return + ldrneb Lg, [L, #VERTEX_G] // Lg = L->v.g + ldrne Lt, [L, #VERTEX_T] // Lt = L->t + mov L, N // L = N + beq .calc_left_start + + lsl Lx, Lxy, #16 // Lx = L->v.x << 16 + lsl Lg, #8 // Lg <<= 8 + cmp Lh, #1 // if (Lh <= 1) skip Ldx calc + beq .calc_left_end + + divLUT tmp, Lh // tmp = FixedInvU(Lh) + + ldrsh Ldx, [L, #VERTEX_X] + sub Ldx, Lx, asr #16 + mul Ldx, tmp // Ldx = tmp * (N->v.x - Lx) + str Ldx, [sp, #SP_LDX] // store Ldx to stack + + ldrb Ldg, [L, #VERTEX_G] + sub Ldg, Lg, lsr #8 + mul Ldg, tmp // Ldg = tmp * (N->v.g - Lg) + asr Ldg, #8 // 8-bit for fractional part + str Ldg, [sp, #SP_LDG] // store Ldg to stack + + ldr Ldt, [L, #VERTEX_T] + sub Ldt, Lt // Ldt = N->v.t - Lt + scaleUV Ldt, Ldu, Ldv, tmp + str Ldt, [sp, #SP_LDT] // store Ldt to stack + .calc_left_end: + + cmp Rh, #0 + bne .calc_right_end // if (Rh != 0) end with right + + .calc_right_start: + ldrsb N, [R, #VERTEX_NEXT] // N = R + R->next + add N, R, N, lsl #VERTEX_SIZEOF_SHIFT + ldr Rxy, [R, #VERTEX_X] // Rxy = (R->v.y << 16) | (R->v.x) + ldrsh Ry2, [N, #VERTEX_Y] // Ry2 = N->v.y + + subs Rh, Ry2, Rxy, asr #16 // Rh = N->v.y - R->v.y + blt .exit // if (Rh < 0) return + ldrneb Rg, [R, #VERTEX_G] // Rg = R->v.g + ldrne Rt, [R, #VERTEX_T] // Rt = R->t + mov R, N // R = N + beq .calc_right_start + + lsl Rx, Rxy, #16 // Rx = R->v.x << 16 + lsl Rg, #8 // Rg <<= 8 + cmp Rh, #1 // if (Rh <= 1) skip Rdx calc + beq .calc_right_end + + divLUT tmp, Rh // tmp = FixedInvU(Rh) + + ldrsh Rdx, [R, #VERTEX_X] + sub Rdx, Rx, asr #16 + mul Rdx, tmp // Rdx = tmp * (N->v.x - Rx) + str Rdx, [sp, #SP_RDX] // store Rdx to stack + + ldrb Rdg, [R, #VERTEX_G] + sub Rdg, Rg, lsr #8 + mul Rdg, tmp // Rdg = tmp * (N->v.g - Rg) + asr Rdg, #8 // 8-bit for fractional part + str Rdg, [sp, #SP_RDG] // store Ldg to stack + + ldr Rdt, [R, #VERTEX_T] + sub Rdt, Rt // Rdt = N->v.t - Rt + scaleUV Rdt, Rdu, Rdv, tmp + str Rdt, [sp, #SP_RDT] // store Rdt to stack + .calc_right_end: + + orr Lg, #LMAP_ADDR + orr Rg, #LMAP_ADDR + + cmp Rh, Lh // if (Rh < Lh) + movlt h, Rh // h = Rh + movge h, Lh // else h = Lh + sub Lh, h // Lh -= h + sub Rh, h // Rh -= h + + ldr TILE, [sp, #SP_TILE] + + add tmp, sp, #SP_L + stmia tmp, {L, R, Lh, Rh} + +.scanline_start: + stmfd sp!, {Lx, Lg, Lt} + + asr Lx, Lx, #16 // x1 = (Lx >> 16) + rsbs width, Lx, Rx, asr #16 // width = (Rx >> 16) - x1 + ble .scanline_end // if (width <= 0) go next scanline + + add ptr, pixel, Lx // ptr = pixel + x1 + + divLUT inv, width // inv = FixedInvU(width) + + sub dtdx, Rt, Lt // dtdx = Rt - Lt + scaleUV dtdx, du, dv, inv + // t == Lt (alias) + + sub dgdx, Rg, Lg // dgdx = Rg - Lg + mul dgdx, inv // dgdx *= FixedInvU(width) + asr dgdx, #15 // dgdx >>= 15 + // g == Lg (alias) + + // 2 bytes alignment (VRAM write requirement) +.align_left: + tst ptr, #1 // if (ptr & 1) + beq .align_right + + bic LMAP, g, #255 + add g, dgdx, asr #1 + tex indexA, t + lit indexA + + ldrb indexB, [ptr, #-1]! // read pal index from VRAM (byte) + orr indexB, indexA, lsl #8 + strh indexB, [ptr], #2 + add t, dtdx + + subs width, #1 // width-- + beq .scanline_end // if (width == 0) + +.align_right: + tst width, #1 + beq .align_block_4px + + sub Rti, Rt, dtdx + tex indexA, Rti + + sub Rgi, Rg, dgdx, asr #1 + bic LMAP, Rgi, #255 + lit indexA + + ldrb indexB, [ptr, width] + subs width, #1 // width-- + orr indexB, indexA, indexB, lsl #8 + strh indexB, [ptr, width] + + beq .scanline_end // if (width == 0) + +.align_block_4px: + tst width, #2 + beq .align_block_8px + + PUT_PIXELS + + subs width, #2 + beq .scanline_end + +.align_block_8px: + tst width, #4 + beq .scanline_block_8px + + PUT_PIXELS + PUT_PIXELS + + subs width, #4 + beq .scanline_end + +.scanline_block_8px: + PUT_PIXELS + PUT_PIXELS + PUT_PIXELS + PUT_PIXELS + + subs width, #8 + bne .scanline_block_8px + +.scanline_end: + ldmfd sp!, {Lx, Lg, Lt} + + ldmia sp, {sLdx, sLdg, sLdt, sRdx, sRdg} + + add Lx, sLdx + add Lg, sLdg + add Lt, sLdt + add Rx, sRdx + add Rg, sRdg + + ldr sRdt, [sp, #SP_RDT] + add Rt, sRdt + + add pixel, #FRAME_WIDTH // pixel += FRAME_WIDTH (240) + + subs h, #1 + bne .scanline_start + + add tmp, sp, #SP_L + ldmia tmp, {L, R, Lh, Rh} + b .loop + +.exit: + add sp, #(SP_SIZE + 4) // revert reserved space for [Ldx, Ldg, Ldt, Rdx, Rdg, Rdt, TILE] + ldmfd sp!, {r4-r11, pc} \ No newline at end of file diff --git a/src/platform/gba/asm/rasterizeGTA.s b/src/platform/gba/asm/rasterizeGTA.s new file mode 100644 index 00000000..5afd6bed --- /dev/null +++ b/src/platform/gba/asm/rasterizeGTA.s @@ -0,0 +1,330 @@ +#include "common_asm.inc" + +pixel .req r0 +L .req r1 +R .req r2 + +Lh .req r3 +Rh .req r4 + +Lx .req r5 +Rx .req r6 + +Lg .req r7 +Rg .req r8 + +Lt .req r9 +Rt .req r10 + +tmp .req r11 +N .req r12 + +TILE .req lr + +h .req N + +LMAP .req tmp + +Ldx .req h +Rdx .req h + +Ldg .req h +Rdg .req h + +Ldt .req h +Rdt .req h + +indexA .req Lh +indexB .req tmp + +Rxy .req tmp +Ry2 .req Rh +Lxy .req tmp +Ly2 .req Lh + +inv .req Lh + +ptr .req Lx +width .req Rh + +g .req Lg +dgdx .req L + +t .req Lt +dtdx .req R + +duv .req R +du .req L +dv .req R + +Ldu .req TILE +Ldv .req N + +Rdu .req TILE +Rdv .req N + +Rti .req tmp +Rgi .req tmp + +sLdx .req L +sLdg .req R +sLdt .req Lh +sRdx .req Rh +sRdg .req tmp +sRdt .req tmp // not enough regs for one ldmia + +SP_LDX = 0 +SP_LDG = 4 +SP_LDT = 8 +SP_RDX = 12 +SP_RDG = 16 +SP_RDT = 20 +SP_L = 24 +SP_R = 28 +SP_LH = 32 +SP_RH = 36 +SP_SIZE = 40 +SP_TILE = SP_SIZE + +.macro PUT_PIXELS + bic LMAP, g, #255 + add g, dgdx + + tex indexA, t + add t, dtdx, lsl #1 + cmp indexA, #0 + ldrneb indexA, [LMAP, indexA] + strneb indexA, [ptr] + add ptr, #2 +.endm + +.global rasterizeGTA_asm +rasterizeGTA_asm: + ldr r3, =gTile + ldr r3, [r3] + + stmfd sp!, {r3-r11, lr} + sub sp, #SP_SIZE // reserve stack space for [Ldx, Ldg, Ldt, Rdx, Rdg, Rdt] + + mov Lh, #0 // Lh = 0 + mov Rh, #0 // Rh = 0 + +.loop: + + cmp Lh, #0 + bne .calc_left_end // if (Lh != 0) end with left + + .calc_left_start: + ldrsb N, [L, #VERTEX_PREV] // N = L + L->prev + add N, L, N, lsl #VERTEX_SIZEOF_SHIFT + ldr Lxy, [L, #VERTEX_X] // Lxy = (L->v.y << 16) | (L->v.x) + ldrsh Ly2, [N, #VERTEX_Y] // Ly2 = N->v.y + + subs Lh, Ly2, Lxy, asr #16 // Lh = N->v.y - L->v.y + blt .exit // if (Lh < 0) return + ldrneb Lg, [L, #VERTEX_G] // Lg = L->v.g + ldrne Lt, [L, #VERTEX_T] // Lt = L->t + mov L, N // L = N + beq .calc_left_start + + lsl Lx, Lxy, #16 // Lx = L->v.x << 16 + lsl Lg, #8 // Lg <<= 8 + cmp Lh, #1 // if (Lh <= 1) skip Ldx calc + beq .calc_left_end + + divLUT tmp, Lh // tmp = FixedInvU(Lh) + + ldrsh Ldx, [L, #VERTEX_X] + sub Ldx, Lx, asr #16 + mul Ldx, tmp // Ldx = tmp * (N->v.x - Lx) + str Ldx, [sp, #SP_LDX] // store Ldx to stack + + ldrb Ldg, [L, #VERTEX_G] + sub Ldg, Lg, lsr #8 + mul Ldg, tmp // Ldg = tmp * (N->v.g - Lg) + asr Ldg, #8 // 8-bit for fractional part + str Ldg, [sp, #SP_LDG] // store Ldg to stack + + ldr Ldt, [L, #VERTEX_T] + sub Ldt, Lt // Ldt = N->v.t - Lt + scaleUV Ldt, Ldu, Ldv, tmp + str Ldt, [sp, #SP_LDT] // store Ldt to stack + .calc_left_end: + + cmp Rh, #0 + bne .calc_right_end // if (Rh != 0) end with right + + .calc_right_start: + ldrsb N, [R, #VERTEX_NEXT] // N = R + R->next + add N, R, N, lsl #VERTEX_SIZEOF_SHIFT + ldr Rxy, [R, #VERTEX_X] // Rxy = (R->v.y << 16) | (R->v.x) + ldrsh Ry2, [N, #VERTEX_Y] // Ry2 = N->v.y + + subs Rh, Ry2, Rxy, asr #16 // Rh = N->v.y - R->v.y + blt .exit // if (Rh < 0) return + ldrb Rg, [R, #VERTEX_G] // Rg = R->v.g + ldr Rt, [R, #VERTEX_T] // Rt = R->t + mov R, N // R = N + beq .calc_right_start + + lsl Rx, Rxy, #16 // Rx = R->v.x << 16 + lsl Rg, #8 // Rg <<= 8 + cmp Rh, #1 // if (Rh <= 1) skip Rdx calc + beq .calc_right_end + + divLUT tmp, Rh // tmp = FixedInvU(Rh) + + ldrsh Rdx, [R, #VERTEX_X] + sub Rdx, Rx, asr #16 + mul Rdx, tmp // Rdx = tmp * (N->v.x - Rx) + str Rdx, [sp, #SP_RDX] // store Rdx to stack + + ldrb Rdg, [R, #VERTEX_G] + sub Rdg, Rg, lsr #8 + mul Rdg, tmp // Rdg = tmp * (N->v.g - Rg) + asr Rdg, #8 // 8-bit for fractional part + str Rdg, [sp, #SP_RDG] // store Ldg to stack + + ldr Rdt, [R, #VERTEX_T] + sub Rdt, Rt // Rdt = N->v.t - Rt + scaleUV Rdt, Rdu, Rdv, tmp + str Rdt, [sp, #SP_RDT] // store Rdt to stack + .calc_right_end: + + orr Lg, #LMAP_ADDR + orr Rg, #LMAP_ADDR + + cmp Rh, Lh // if (Rh < Lh) + movlt h, Rh // h = Rh + movge h, Lh // else h = Lh + sub Lh, h // Lh -= h + sub Rh, h // Rh -= h + + ldr TILE, [sp, #SP_TILE] + + add tmp, sp, #SP_L + stmia tmp, {L, R, Lh, Rh} + +.scanline_start: + stmfd sp!, {Lx, Lg, Lt} + + asr Lx, Lx, #16 // x1 = (Lx >> 16) + rsbs width, Lx, Rx, asr #16 // width = (Rx >> 16) - x1 + ble .scanline_end // if (width <= 0) go next scanline + + add ptr, pixel, Lx // ptr = pixel + x1 + + divLUT inv, width // inv = FixedInvU(width) + + sub dtdx, Rt, Lt // dtdx = Rt - Lt + scaleUV dtdx, du, dv, inv + // t == Lt (alias) + + sub dgdx, Rg, Lg // dgdx = Rg - Lg + mul dgdx, inv // dgdx *= FixedInvU(width) + asr dgdx, #15 // dgdx >>= 15 + // g == Lg (alias) + + // 2 bytes alignment (VRAM write requirement) +.align_left: + tst ptr, #1 // if (ptr & 1) + beq .align_right + + tex indexA, t + + cmp indexA, #0 + beq .skip_left + + bic LMAP, g, #255 + ldrb indexA, [LMAP, indexA] + + ldrb indexB, [ptr, #-1]! // read pal index from VRAM (byte) + orr indexB, indexA, lsl #8 + strh indexB, [ptr], #1 + +.skip_left: + add ptr, #1 + add t, dtdx + add g, dgdx, asr #1 + + subs width, #1 // width-- + beq .scanline_end // if (width == 0) + +.align_right: + tst width, #1 + beq .align_block_4px + + sub Rti, Rt, dtdx + tex indexA, Rti + + cmp indexA, #0 + subeq width, #1 + beq .skip_right + + sub Rgi, Rg, dgdx, asr #1 + bic LMAP, Rgi, #255 + lit indexA + + ldrb indexB, [ptr, width] + sub width, #1 // width-- + orr indexB, indexA, indexB, lsl #8 + strh indexB, [ptr, width] + +.skip_right: + cmp width, #0 + beq .scanline_end // if (width == 0) + +.align_block_4px: + tst width, #2 + beq .align_block_8px + + PUT_PIXELS + + subs width, #2 + beq .scanline_end + +.align_block_8px: + tst width, #4 + beq .scanline_block_8px + + PUT_PIXELS + PUT_PIXELS + + subs width, #4 + beq .scanline_end + +.scanline_block_8px: + PUT_PIXELS + PUT_PIXELS + PUT_PIXELS + PUT_PIXELS + + subs width, #8 + bne .scanline_block_8px + +.scanline_end: + ldmfd sp!, {Lx, Lg, Lt} + + ldmia sp, {sLdx, sLdg, sLdt, sRdx, sRdg} + + add Lx, sLdx + add Lg, sLdg + add Lt, sLdt + add Rx, sRdx + add Rg, sRdg + + ldr sRdt, [sp, #SP_RDT] + add Rt, sRdt + + add pixel, #FRAME_WIDTH // pixel += FRAME_WIDTH (240) + + subs h, #1 + bne .scanline_start + + add tmp, sp, #SP_L + ldmia tmp, {L, R, Lh, Rh} + b .loop + +.exit: + add sp, #(SP_SIZE + 4) // revert reserved space for [Ldx, Ldg, Ldt, Rdx, Rdg, Rdt, TILE] + ldmfd sp!, {r4-r11, pc} \ No newline at end of file diff --git a/src/platform/gba/asm/rasterizeLineH.s b/src/platform/gba/asm/rasterizeLineH.s new file mode 100644 index 00000000..fab7a0fc --- /dev/null +++ b/src/platform/gba/asm/rasterizeLineH.s @@ -0,0 +1,43 @@ +#include "common_asm.inc" + +pixel .req r0 +L .req r1 +R .req r2 +tmp .req r12 +index .req L +width .req R + +.global rasterizeLineH_asm +rasterizeLineH_asm: + add R, #VERTEX_SIZEOF + ldrsh tmp, [L, #VERTEX_X] + add pixel, tmp + ldrb index, [L, #VERTEX_G] + ldrsh width, [R, #VERTEX_X] + + // 2 bytes alignment (VRAM write requirement) +.align_left: + tst pixel, #1 + beq .align_right + ldrb tmp, [pixel, #-1]! + orr tmp, index, lsl #8 + strh tmp, [pixel], #2 + subs width, #1 + beq .scanline_end + +.align_right: + tst width, #1 + beq .scanline_block_2px + ldrb tmp, [pixel, width] + subs width, #1 + orr tmp, index, tmp, lsl #8 + strh tmp, [pixel, width] + beq .scanline_end + +.scanline_block_2px: + strb index, [pixel], #2 // VRAM one as two bytes write hack + subs width, #2 + bne .scanline_block_2px + +.scanline_end: + mov pc, lr diff --git a/src/platform/gba/asm/rasterizeLineV.s b/src/platform/gba/asm/rasterizeLineV.s new file mode 100644 index 00000000..b7bea1ee --- /dev/null +++ b/src/platform/gba/asm/rasterizeLineV.s @@ -0,0 +1,36 @@ +#include "common_asm.inc" + +pixel .req r0 +L .req r1 +R .req r2 +tmp .req r12 +index .req L +height .req R + +.global rasterizeLineV_asm +rasterizeLineV_asm: + add R, #VERTEX_SIZEOF + ldrsh tmp, [L, #VERTEX_X] + ldrb index, [L, #VERTEX_G] + ldrsh height, [R, #VERTEX_Y] + add pixel, tmp + + tst pixel, #1 + beq .right + + sub pixel, #1 +.left: + ldrb tmp, [pixel] + orr tmp, index, lsl #8 + strh tmp, [pixel], #FRAME_WIDTH + subs height, #1 + bne .left + mov pc, lr + +.right: + ldrb tmp, [pixel, #1] + orr tmp, index, tmp, lsl #8 + strh tmp, [pixel], #FRAME_WIDTH + subs height, #1 + bne .right + mov pc, lr diff --git a/src/platform/gba/asm/rasterizeS.s b/src/platform/gba/asm/rasterizeS.s new file mode 100644 index 00000000..f232aad8 --- /dev/null +++ b/src/platform/gba/asm/rasterizeS.s @@ -0,0 +1,149 @@ +#include "common_asm.inc" + +pixel .req r0 +L .req r1 +R .req r2 +LMAP .req r3 +Lh .req r4 +Rh .req r5 +Lx .req r6 +Rx .req r7 +Ldx .req r8 +Rdx .req r9 +N .req r10 +tmp .req r11 +pair .req r12 +width .req lr +h .req N +Rxy .req tmp +Ry2 .req Rh +Lxy .req tmp +Ly2 .req Lh +indexA .req Lh +indexB .req pair + +.global rasterizeS_asm +rasterizeS_asm: + stmfd sp!, {r4-r11, lr} + + mov LMAP, #LMAP_ADDR + add LMAP, #0x1A00 + + mov Lh, #0 // Lh = 0 + mov Rh, #0 // Rh = 0 + +.loop: + + cmp Lh, #0 + bne .calc_left_end // if (Lh != 0) end with left + + .calc_left_start: + ldr Lxy, [L, #VERTEX_X] // Lxy = (L->v.y << 16) | (L->v.x) + ldrsb N, [L, #VERTEX_PREV] // N = L + L->prev + add L, L, N, lsl #VERTEX_SIZEOF_SHIFT + ldrsh Ly2, [L, #VERTEX_Y] // Ly2 = N->v.y + + subs Lh, Ly2, Lxy, asr #16 // Lh = N->v.y - L->v.y + blt .exit // if (Lh < 0) return + beq .calc_left_start + + lsl Lx, Lxy, #16 // Lx = L->v.x << 16 + cmp Lh, #1 // if (Lh == 1) skip Ldx calc + beq .calc_left_end + + divLUT tmp, Lh // tmp = FixedInvU(Lh) + + ldrsh Ldx, [L, #VERTEX_X] + sub Ldx, Lx, asr #16 + mul Ldx, tmp // Ldx = tmp * (N->v.x - Lx) + .calc_left_end: + + cmp Rh, #0 + bne .calc_right_end // if (Rh != 0) end with right + + .calc_right_start: + ldr Rxy, [R, #VERTEX_X] // Rxy = (R->v.y << 16) | (R->v.x) + ldrsb N, [R, #VERTEX_NEXT] // N = R + R->next + add R, R, N, lsl #VERTEX_SIZEOF_SHIFT + ldrsh Ry2, [R, #VERTEX_Y] // Ry2 = N->v.y + + subs Rh, Ry2, Rxy, asr #16 // Rh = N->v.y - R->v.y + blt .exit // if (Rh < 0) return + beq .calc_right_start + + lsl Rx, Rxy, #16 // Rx = R->v.x << 16 + cmp Rh, #1 // if (Rh == 1) skip Rdx calc + beq .calc_right_end + + divLUT tmp, Rh // tmp = FixedInvU(Rh) + + ldrsh Rdx, [R, #VERTEX_X] + sub Rdx, Rx, asr #16 + mul Rdx, tmp // Rdx = tmp * (N->v.x - Rx) + .calc_right_end: + + cmp Rh, Lh // if (Rh < Lh) + movlt h, Rh // h = Rh + movge h, Lh // else h = Lh + sub Lh, h // Lh -= h + sub Rh, h // Rh -= h + + stmfd sp!, {Lh} + +.scanline_start: + asr tmp, Lx, #16 // x1 = (Lx >> 16) + rsbs width, tmp, Rx, asr #16 // width = (Rx >> 16) - x1 + ble .scanline_end // if (width <= 0) go next scanline + + add tmp, pixel, tmp // tmp = pixel + x1 + + // 2 bytes alignment (VRAM write requirement) +.align_left: + tst tmp, #1 + beq .align_right + + ldrh pair, [tmp, #-1]! + ldrb indexA, [LMAP, pair, lsr #8] + and pair, #0xFF + orr pair, indexA, lsl #8 + strh pair, [tmp], #2 + + subs width, #1 // width-- + beq .scanline_end + +.align_right: + tst width, #1 + beq .scanline + subs width, #1 // width-- + ldrh pair, [tmp, width] + and indexA, pair, #0xFF + ldrb indexA, [LMAP, indexA] + and pair, #0xFF00 + orr pair, indexA + strh pair, [tmp, width] + beq .scanline_end // width == 0 + +.scanline: + ldrh pair, [tmp] + ldrb indexA, [LMAP, pair, lsr #8] + and pair, #0xFF + ldrb indexB, [LMAP, pair] + orr pair, indexB, indexA, lsl #8 + strh pair, [tmp], #2 + + subs width, #2 + bne .scanline + +.scanline_end: + add Lx, Ldx // Lx += Ldx + add Rx, Rdx // Rx += Rdx + add pixel, #FRAME_WIDTH // pixel += FRAME_WIDTH (240) + + subs h, #1 + bne .scanline_start + + ldmfd sp!, {Lh} + b .loop + +.exit: + ldmfd sp!, {r4-r11, pc} \ No newline at end of file diff --git a/src/platform/gba/asm/rasterize_dummy.s b/src/platform/gba/asm/rasterize_dummy.s new file mode 100644 index 00000000..0a889b48 --- /dev/null +++ b/src/platform/gba/asm/rasterize_dummy.s @@ -0,0 +1,5 @@ +#include "common_asm.inc" + +.global rasterize_dummy +rasterize_dummy: + mov pc, lr \ No newline at end of file diff --git a/src/platform/gba/sdiv32.s b/src/platform/gba/asm/sdiv32.s similarity index 100% rename from src/platform/gba/sdiv32.s rename to src/platform/gba/asm/sdiv32.s diff --git a/src/platform/gba/asm/sndIMA.s b/src/platform/gba/asm/sndIMA.s new file mode 100644 index 00000000..35e2457b --- /dev/null +++ b/src/platform/gba/asm/sndIMA.s @@ -0,0 +1,64 @@ +#include "common_asm.inc" + +state .req r0 +buffer .req r1 +data .req r2 +size .req r3 +smp .req r4 +idx .req r5 +stepLUT .req r6 +step .req r7 +n .req r8 +index .req r9 +outA .req r12 +outB .req lr +tmp .req outB + +IMA_STEP_SIZE = 88 + +.macro decode4 n, out + ldr step, [stepLUT, idx, lsl #2] + + and index, \n, #7 + mov tmp, step, lsl #1 + mla step, index, tmp, step + tst \n, #8 + subne smp, smp, step, lsr #3 + addeq smp, smp, step, lsr #3 + + subs index, #3 + suble idx, idx, #1 + bicle idx, idx, idx, asr #31 + addgt idx, idx, index, lsl #1 + cmpgt idx, #IMA_STEP_SIZE + movgt idx, #IMA_STEP_SIZE + + mov \out, smp, asr #2 +.endm + +.global sndIMA_asm +sndIMA_asm: + stmfd sp!, {r4-r9, lr} + + ldmia state, {smp, idx} + + ldr stepLUT, =IMA_STEP + +.loop: + ldrb n, [data], #1 + + decode4 n, outA + + mov n, n, lsr #4 + + decode4 n, outB + + stmia buffer!, {outA, outB} + + subs size, #1 + bne .loop + + stmia state, {smp, idx} + + ldmfd sp!, {r4-r9, lr} + bx lr diff --git a/src/platform/gba/asm/sndPCM.s b/src/platform/gba/asm/sndPCM.s new file mode 100644 index 00000000..97cd4021 --- /dev/null +++ b/src/platform/gba/asm/sndPCM.s @@ -0,0 +1,48 @@ +#include "common_asm.inc" + +pos .req r0 +inc .req r1 +size .req r2 +volume .req r3 + +data .req r4 +buffer .req r5 +count .req r6 +ampA .req r7 +ampB .req r8 +outA .req r9 +outB .req r12 +last .req count +tmp .req outB + +.global sndPCM_asm +sndPCM_asm: + mov tmp, sp + stmfd sp!, {r4-r9} + + ldmia tmp, {data, buffer, count} + + mla last, inc, count, pos + cmp last, size + movgt last, size + +.loop: + ldrb ampA, [data, pos, lsr #8] + add pos, pos, inc + ldrb ampB, [data, pos, lsr #8] + add pos, pos, inc + cmp pos, last + + sub ampA, ampA, #128 + sub ampB, ampB, #128 + + ldmia buffer, {outA, outB} + mla outA, volume, ampA, outA + mla outB, volume, ampB, outB + stmia buffer!, {outA, outB} + + blt .loop + +.done: + ldmfd sp!, {r4-r9} + bx lr diff --git a/src/platform/gba/asm/sndWrite.s b/src/platform/gba/asm/sndWrite.s new file mode 100644 index 00000000..a57b6e44 --- /dev/null +++ b/src/platform/gba/asm/sndWrite.s @@ -0,0 +1,44 @@ +#include "common_asm.inc" + +buffer .req r0 +count .req r1 +data .req r2 +vA .req r3 +vB .req r4 +vC .req r5 +vD .req r12 + +SND_VOL_SHIFT = 6 + +.macro encode amp + mov \amp, \amp, asr #SND_VOL_SHIFT + cmp \amp, #-128 + movlt \amp, #-128 + cmp \amp, #127 + movgt \amp, #127 +.endm + +.global sndWrite_asm +sndWrite_asm: + stmfd sp!, {r4-r5} +.loop: + ldmia data!, {vA, vB, vC, vD} + + encode vA + encode vB + encode vC + encode vD + + and vA, vA, #0xFF + and vB, vB, #0xFF + and vC, vC, #0xFF + orr vA, vA, vB, lsl #8 + orr vA, vA, vC, lsl #16 + orr vA, vA, vD, lsl #24 + str vA, [buffer], #4 + + subs count, #4 + bne .loop + + ldmfd sp!, {r4-r5} + bx lr diff --git a/src/platform/gba/asm/sphereIsVisible.s b/src/platform/gba/asm/sphereIsVisible.s new file mode 100644 index 00000000..1a343eef --- /dev/null +++ b/src/platform/gba/asm/sphereIsVisible.s @@ -0,0 +1,84 @@ +#include "common_asm.inc" + +x .req r0 +y .req r1 +z .req r2 +r .req r3 +mx .req r4 +my .req r5 +mz .req r6 +vx .req r7 +vy .req r8 +vz .req r12 +m .req lr +tmp .req m +vp .req m +vMinXY .req z +vMaxXY .req r + +rMinX .req vx +rMaxX .req x +rMinY .req vy +rMaxY .req y + +.global sphereIsVisible_asm +sphereIsVisible_asm: + stmfd sp!, {r4-r8, lr} + + ldr m, =gMatrixPtr + ldr m, [m] + + ldmia m!, {mx, my, mz, vx} + mla vx, mx, x, vx + mla vx, my, y, vx + mla vx, mz, z, vx + ldmia m!, {mx, my, mz, vy} + mla vy, mx, x, vy + mla vy, my, y, vy + mla vy, mz, z, vy + ldmia m!, {mx, my, mz, vz} + mla vz, mx, x, vz + mla vz, my, y, vz + mla vz, mz, z, vz + + cmp vz, #VIEW_MAX_F + bhi .fail + + mov x, vx, asr #FIXED_SHIFT + mov y, vy, asr #FIXED_SHIFT + + mov z, vz, lsr #(FIXED_SHIFT + 6) + add z, z, vz, lsr #(FIXED_SHIFT + 4) + divLUT tmp, z + mul x, tmp, x + mul y, tmp, y + mul r, tmp, r + + mov x, x, asr #(16 - PROJ_SHIFT) + mov y, y, lsl #(PROJ_SHIFT) + + sub rMinX, x, r, lsr #(16 - PROJ_SHIFT) + add rMaxX, x, r, lsr #(16 - PROJ_SHIFT) + sub rMinY, y, r, lsl #PROJ_SHIFT + add rMaxY, y, r, lsl #PROJ_SHIFT + + ldr vp, =viewportRel + ldmia vp, {vMinXY, vMaxXY} + + cmp rMaxX, vMinXY, asr #16 + blt .fail + cmp rMaxY, vMinXY, lsl #16 + blt .fail + cmp rMinX, vMaxXY, asr #16 + bgt .fail + cmp rMinY, vMaxXY, lsl #16 + bgt .fail + + mov r0, #1 + ldmfd sp!, {r4-r8, lr} + bx lr + +.fail: + mov r0, #0 + ldmfd sp!, {r4-r8, lr} + bx lr diff --git a/src/platform/gba/asm/transformMesh.s b/src/platform/gba/asm/transformMesh.s new file mode 100644 index 00000000..e28fb896 --- /dev/null +++ b/src/platform/gba/asm/transformMesh.s @@ -0,0 +1,131 @@ +#include "common_asm.inc" + +vertices .req r0 +count .req r1 +intensity .req r2 +m .req r3 +vg .req intensity +vx .req r4 +vy .req r5 +vz .req r6 +mx .req r7 +my .req r8 +mz .req r9 +x .req r10 +y .req r11 +z .req r12 +res .req lr + +ambient .req vx +vp .req vx +minXY .req vx +maxXY .req vy +tmp .req vy +dz .req vx + +SP_MINXY = 0 +SP_MAXXY = 4 +SP_SIZE = 8 + +.global transformMesh_asm +transformMesh_asm: + stmfd sp!, {r4-r11, lr} + + ldr res, =gVerticesBase + ldr res, [res] + + ldr ambient, =gLightAmbient + ldr ambient, [ambient] + add vg, ambient, intensity + asr vg, #8 + // clamp spAmbient to 0..31 + cmp vg, #31 + movge vg, #31 + bic vg, vg, asr #31 + + ldr vp, =viewportRel + ldmia vp, {minXY, maxXY} + + stmfd sp!, {minXY, maxXY} + + ldr m, =gMatrixPtr + ldr m, [m] + +.loop: + // unpack vertex + ldrsh vx, [vertices], #2 + ldrsh vy, [vertices], #2 + ldrsh vz, [vertices], #2 + + bic vg, #CLIP_MASK // clear clipping flags + + // transform x + ldmia m!, {mx, my, mz, x} + mla x, mx, vx, x + mla x, my, vy, x + mla x, mz, vz, x + asr x, #FIXED_SHIFT + + // transform y + ldmia m!, {mx, my, mz, y} + mla y, mx, vx, y + mla y, my, vy, y + mla y, mz, vz, y + asr y, #FIXED_SHIFT + + // transform z + ldmia m!, {mx, my, mz, z} + mla z, mx, vx, z + mla z, my, vy, z + mla z, mz, vz, z + asr z, #FIXED_SHIFT + + sub m, #(12 * 4) // restore matrix ptr + + // z clipping + cmp z, #VIEW_MIN + movle z, #VIEW_MIN + orrle vg, #CLIP_NEAR + cmp z, #VIEW_MAX + movge z, #VIEW_MAX + orrge vg, #CLIP_FAR + + // project + mov dz, z, lsr #4 + add dz, z, lsr #6 + divLUT tmp, dz + mul x, tmp, x + mul y, tmp, y + asr x, #(16 - PROJ_SHIFT) + asr y, #(16 - PROJ_SHIFT) + + // viewport clipping + ldmia sp, {minXY, maxXY} + + cmp x, minXY, asr #16 + orrle vg, #CLIP_LEFT + cmp x, maxXY, asr #16 + orrge vg, #CLIP_RIGHT + + lsl minXY, #16 + lsl maxXY, #16 + + cmp y, minXY, asr #16 + orrle vg, #CLIP_TOP + cmp y, maxXY, asr #16 + orrge vg, #CLIP_BOTTOM + + add x, #(FRAME_WIDTH >> 1) + add y, #(FRAME_HEIGHT >> 1) + + // store the result + strh x, [res], #2 + strh y, [res], #2 + strh z, [res], #2 + strh vg, [res], #2 + + subs count, #1 + bne .loop + + add sp, #SP_SIZE + ldmfd sp!, {r4-r11, pc} diff --git a/src/platform/gba/asm/transformRoom.s b/src/platform/gba/asm/transformRoom.s new file mode 100644 index 00000000..c4082685 --- /dev/null +++ b/src/platform/gba/asm/transformRoom.s @@ -0,0 +1,155 @@ +#include "common_asm.inc" + +vertices .req r0 +count .req r1 +m .req r2 +v .req r3 +vx .req r4 +vy .req r5 +vz .req r6 +vg .req v +mx .req r7 +my .req r8 +mz .req r9 +x .req r10 +y .req r11 +z .req r12 +res .req lr +t .req y + +spMinXY .req x +spMaxXY .req y + +mask .req x +vp .req vx +minXY .req vx +maxXY .req vy + +tmp .req my +dz .req mz +fog .req mz + +SP_MINXY = 0 +SP_MAXXY = 4 +SP_SIZE = 8 + +.global transformRoom_asm +transformRoom_asm: + stmfd sp!, {r4-r11, lr} + + ldr res, =gVerticesBase + ldr res, [res] + add res, #VERTEX_G + + ldr m, =gMatrixPtr + ldr m, [m] + + ldr vp, =viewportRel + ldmia vp, {spMinXY, spMaxXY} + + stmfd sp!, {spMinXY, spMaxXY} + + // preload mask, matrix and z-row + mov mask, #(0xFF << 10) + add m, #(12 * 4) + ldmdb m!, {mx, my, mz, z} + +.loop: + // unpack vertex + ldmia vertices!, {v} + + and vz, mask, v, lsr #6 + and vy, v, #0xFF00 + and vx, mask, v, lsl #10 + + // transform z + mla t, mx, vx, z + mla t, my, vy, t + mla t, mz, vz, t + asr t, #FIXED_SHIFT + + // skip if vertex is out of z-range + add t, #VIEW_OFF + cmp t, #(VIEW_OFF + VIEW_OFF + VIEW_MAX) + movhi vg, #(CLIP_NEAR + CLIP_FAR) + bhi .skip + + and vg, mask, v, lsr #14 + sub z, t, #VIEW_OFF + + // transform y + ldmdb m!, {mx, my, mz, y} + mla y, mx, vx, y + mla y, my, vy, y + mla y, mz, vz, y + asr y, #FIXED_SHIFT + + // transform x + ldmdb m!, {mx, my, mz, x} + mla x, mx, vx, x + mla x, my, vy, x + mla x, mz, vz, x + asr x, #FIXED_SHIFT + + // fog + cmp z, #FOG_MIN + subgt fog, z, #FOG_MIN + addgt vg, fog, lsl #6 + lsr vg, #13 + cmp vg, #31 + movgt vg, #31 + + // z clipping + cmp z, #VIEW_MIN + movle z, #VIEW_MIN + orrle vg, #CLIP_NEAR + cmp z, #VIEW_MAX + movge z, #VIEW_MAX + orrge vg, #CLIP_FAR + + // project + mov dz, z, lsr #6 + add dz, z, lsr #4 + divLUT tmp, dz + mul x, tmp, x + mul y, tmp, y + asr x, #(16 - PROJ_SHIFT) + asr y, #(16 - PROJ_SHIFT) + + // viewport clipping + ldmia sp, {minXY, maxXY} + + cmp x, minXY, asr #16 + orrle vg, #CLIP_LEFT + cmp x, maxXY, asr #16 + orrge vg, #CLIP_RIGHT + + lsl minXY, #16 + lsl maxXY, #16 + + cmp y, minXY, asr #16 + orrle vg, #CLIP_TOP + cmp y, maxXY, asr #16 + orrge vg, #CLIP_BOTTOM + + add x, #(FRAME_WIDTH >> 1) + add y, #(FRAME_HEIGHT >> 1) + + // store the result + strh x, [res, #-6] + strh y, [res, #-4] + strh z, [res, #-2] + + // preload mask, matrix and z-row + mov mask, #(0xFF << 10) + add m, #(12 * 4) + ldmdb m!, {mx, my, mz, z} + +.skip: + strh vg, [res], #8 + + subs count, #1 + bne .loop + + add sp, #SP_SIZE + ldmfd sp!, {r4-r11, pc} diff --git a/src/platform/gba/asm/transformRoomUW.s b/src/platform/gba/asm/transformRoomUW.s new file mode 100644 index 00000000..6291de4a --- /dev/null +++ b/src/platform/gba/asm/transformRoomUW.s @@ -0,0 +1,181 @@ +#include "common_asm.inc" + +vertices .req r0 +count .req r1 +m .req r2 +v .req r3 +vx .req r4 +vy .req r5 +vz .req r6 +vg .req v +mx .req r7 +my .req r8 +mz .req r9 +x .req r10 +y .req r11 +z .req r12 +res .req lr +t .req y + +spMinXY .req mx +spMaxXY .req my +spFrame .req mz +spCaustLUT .req x +spRandLUT .req y + +mask .req x +vp .req vx +minXY .req vx +maxXY .req vy + +dz .req mz +fog .req mz + +frame .req vx +caust .req vy +rand .req vz +tmp .req mx + +SP_MINXY = 0 +SP_MAXXY = 4 +SP_FRAME = 8 +SP_CAUST = 12 +SP_RAND = 16 +SP_SIZE = 20 + +.global transformRoomUW_asm +transformRoomUW_asm: + stmfd sp!, {r4-r11, lr} + + ldr res, =gVerticesBase + ldr res, [res] + add res, #VERTEX_G + + ldr m, =gMatrixPtr + ldr m, [m] + + ldr vp, =viewportRel + ldmia vp, {spMinXY, spMaxXY} + + ldr spFrame, =gCausticsFrame + ldr spFrame, [spFrame] + + ldr spCaustLUT, =gCaustics + ldr spRandLUT, =gRandTable + + stmfd sp!, {spMinXY, spMaxXY, spFrame, spCaustLUT, spRandLUT} + + // preload mask, matrix and z-row + mov mask, #(0xFF << 10) + add m, #(12 * 4) + ldmdb m!, {mx, my, mz, z} + +.loop: + // unpack vertex + ldmia vertices!, {v} + + and vz, mask, v, lsr #6 + and vy, v, #0xFF00 + and vx, mask, v, lsl #10 + + // transform z + mla t, mx, vx, z + mla t, my, vy, t + mla t, mz, vz, t + asr t, #FIXED_SHIFT + + // skip if vertex is out of z-range + add t, #VIEW_OFF + cmp t, #(VIEW_OFF + VIEW_OFF + VIEW_MAX) + movhi vg, #(CLIP_NEAR + CLIP_FAR) + bhi .skip + + and vg, mask, v, lsr #14 + sub z, t, #VIEW_OFF + + // transform y + ldmdb m!, {mx, my, mz, y} + mla y, mx, vx, y + mla y, my, vy, y + mla y, mz, vz, y + asr y, #FIXED_SHIFT + + // transform x + ldmdb m!, {mx, my, mz, x} + mla x, mx, vx, x + mla x, my, vy, x + mla x, mz, vz, x + asr x, #FIXED_SHIFT + + // caustics + add tmp, sp, #SP_FRAME + ldmia tmp, {frame, caust, rand} + and tmp, count, #(MAX_RAND_TABLE - 1) + ldr rand, [rand, tmp, lsl #2] + add rand, frame + and rand, #(MAX_CAUSTICS - 1) + ldr caust, [caust, rand, lsl #2] + add vg, caust, lsl #5 + + // fog + cmp z, #FOG_MIN + subgt fog, z, #FOG_MIN + addgt vg, fog, lsl #6 + lsr vg, #13 + cmp vg, #31 + movgt vg, #31 + + // z clipping + cmp z, #VIEW_MIN + movle z, #VIEW_MIN + orrle vg, #CLIP_NEAR + cmp z, #VIEW_MAX + movge z, #VIEW_MAX + orrge vg, #CLIP_FAR + + // project + mov dz, z, lsr #6 + add dz, z, lsr #4 + divLUT tmp, dz + mul x, tmp, x + mul y, tmp, y + asr x, #(16 - PROJ_SHIFT) + asr y, #(16 - PROJ_SHIFT) + + // viewport clipping + ldmia sp, {minXY, maxXY} + + cmp x, minXY, asr #16 + orrle vg, #CLIP_LEFT + cmp x, maxXY, asr #16 + orrge vg, #CLIP_RIGHT + + lsl minXY, #16 + lsl maxXY, #16 + + cmp y, minXY, asr #16 + orrle vg, #CLIP_TOP + cmp y, maxXY, asr #16 + orrge vg, #CLIP_BOTTOM + + add x, #(FRAME_WIDTH >> 1) + add y, #(FRAME_HEIGHT >> 1) + + // store the result + strh x, [res, #-6] + strh y, [res, #-4] + strh z, [res, #-2] + + // preload mask, matrix and z-row + mov mask, #(0xFF << 10) + add m, #(12 * 4) + ldmdb m!, {mx, my, mz, z} + +.skip: + strh vg, [res], #8 + + subs count, #1 + bne .loop + + add sp, #SP_SIZE + ldmfd sp!, {r4-r11, pc} diff --git a/src/platform/gba/udiv32.s b/src/platform/gba/asm/udiv32.s similarity index 100% rename from src/platform/gba/udiv32.s rename to src/platform/gba/asm/udiv32.s diff --git a/src/platform/gba/camera.h b/src/platform/gba/camera.h deleted file mode 100644 index 04da9bf2..00000000 --- a/src/platform/gba/camera.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef H_CAMERA -#define H_CAMERA - -#include "common.h" - -#define CAM_SPEED (1 << 2) -#define CAM_ROT_SPEED (1 << 8) -#define CAM_ROT_X_MAX int16(85 * 0x8000 / 180) - -struct Camera { - vec3i pos; - int16 rotX, rotY; - int32 room; - - void init() { - pos.x = 75162; - pos.y = 2048; - pos.z = 5000; - - rotX = 0; - rotY = 16 << 8; - } - - void update() { - if (keys[IK_UP]) rotX -= CAM_ROT_SPEED; - if (keys[IK_DOWN]) rotX += CAM_ROT_SPEED; - if (keys[IK_LEFT]) rotY -= CAM_ROT_SPEED; - if (keys[IK_RIGHT]) rotY += CAM_ROT_SPEED; - - rotX = clamp(rotX, -CAM_ROT_X_MAX, CAM_ROT_X_MAX); - - matrixSetView(pos, rotX, rotY); - - Matrix &m = matrixGet(); - - if (keys[IK_R]) { - pos.x += m[0].x * CAM_SPEED >> 10; - pos.y += m[0].y * CAM_SPEED >> 10; - pos.z += m[0].z * CAM_SPEED >> 10; - } - - if (keys[IK_L]) { - pos.x -= m[0].x * CAM_SPEED >> 10; - pos.y -= m[0].y * CAM_SPEED >> 10; - pos.z -= m[0].z * CAM_SPEED >> 10; - } - - if (keys[IK_A]) { - pos.x += m[2].x * CAM_SPEED >> 10; - pos.y += m[2].y * CAM_SPEED >> 10; - pos.z += m[2].z * CAM_SPEED >> 10; - } - - if (keys[IK_B]) { - pos.x -= m[2].x * CAM_SPEED >> 10; - pos.y -= m[2].y * CAM_SPEED >> 10; - pos.z -= m[2].z * CAM_SPEED >> 10; - } - - room = getRoomIndex(room, pos); - } -}; - -Camera camera; - -#endif \ No newline at end of file diff --git a/src/platform/gba/checkROM.cpp b/src/platform/gba/checkROM.cpp new file mode 100644 index 00000000..8290e8fb --- /dev/null +++ b/src/platform/gba/checkROM.cpp @@ -0,0 +1,52 @@ +#include + +#define MEM_CHECK_MAGIC 14021968 +#define MEM_CHECK_SIZE 16 + +const int ROM_MAGIC[MEM_CHECK_SIZE] = { + MEM_CHECK_MAGIC + 0, + MEM_CHECK_MAGIC + 1, + MEM_CHECK_MAGIC + 2, + MEM_CHECK_MAGIC + 3, + MEM_CHECK_MAGIC + 4, + MEM_CHECK_MAGIC + 5, + MEM_CHECK_MAGIC + 6, + MEM_CHECK_MAGIC + 7, + MEM_CHECK_MAGIC + 8, + MEM_CHECK_MAGIC + 9, + MEM_CHECK_MAGIC + 10, + MEM_CHECK_MAGIC + 11, + MEM_CHECK_MAGIC + 12, + MEM_CHECK_MAGIC + 13, + MEM_CHECK_MAGIC + 14, + MEM_CHECK_MAGIC + 15, +}; + +EWRAM_CODE bool checkROM(unsigned int mask) +{ + REG_WSCNT = mask; + + // check sequential read (S) + for (int i = 0; i < MEM_CHECK_SIZE; i++) + { + if (*(volatile int*)&ROM_MAGIC[i] != (MEM_CHECK_MAGIC + i)) + { + REG_WSCNT = WS_ROM0_N4 | WS_ROM0_S2 | WS_PREFETCH; + return false; + } + } + + // check non-sequential read (N) + for (int i = 0, j = MEM_CHECK_SIZE - 1; i < MEM_CHECK_SIZE; i++, j--) + { + bool L = *(volatile int*)&ROM_MAGIC[i] == (MEM_CHECK_MAGIC + i); + bool R = *(volatile int*)&ROM_MAGIC[j] == (MEM_CHECK_MAGIC + j); + + if (L && R) continue; + + REG_WSCNT = WS_ROM0_N4 | WS_ROM0_S2 | WS_PREFETCH; + return false; + } + + return true; +} \ No newline at end of file diff --git a/src/platform/gba/common.h b/src/platform/gba/common.h deleted file mode 100644 index 48213311..00000000 --- a/src/platform/gba/common.h +++ /dev/null @@ -1,322 +0,0 @@ -#ifndef H_COMMON -#define H_COMMON - -#ifdef _WIN32 -#define _CRT_SECURE_NO_WARNINGS -#include -#else -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif - -#include -#include -#include -#include -#include - -#define USE_MODE_5 -//#define DEBUG_OVERDRAW -//#define DEBUG_FACES - -#define SCALE 1 - -#ifdef USE_MODE_5 - #define WIDTH 160 - #define HEIGHT 128 - #define FRAME_WIDTH 160 - #define FRAME_HEIGHT 128 - #define PIXEL_SIZE 1 -#else // MODE_4 - #define WIDTH 240 - #define HEIGHT 160 - #define FRAME_WIDTH (WIDTH/SCALE) - #define FRAME_HEIGHT (HEIGHT/SCALE) - #define PIXEL_SIZE 2 -#endif - -#ifdef _WIN32 - #define INLINE inline -#else - #define INLINE __attribute__((always_inline)) inline -#endif - -typedef signed char int8; -typedef signed short int16; -typedef signed int int32; -typedef signed long long int64; -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned int uint32; -typedef int16 Index; - -#define PI 3.14159265358979323846f -#define PIH (PI * 0.5f) -#define PI2 (PI * 2.0f) -#define DEG2RAD (PI / 180.0f) -#define RAD2DEG (180.0f / PI) - -#ifdef _WIN32 - #define IWRAM_CODE - #define EWRAM_DATA - - #define dmaCopy(src,dst,size) memcpy(dst,src,size) - #define ALIGN4 -#else - #define ALIGN4 __attribute__ ((aligned (4))) -#endif - -enum InputKey { - IK_UP, - IK_RIGHT, - IK_DOWN, - IK_LEFT, - IK_A, - IK_B, - IK_L, - IK_R, - IK_MAX -}; - -extern bool keys[IK_MAX]; - -struct vec3i { - int32 x, y, z; - - vec3i() = default; - INLINE vec3i(int32 x, int32 y, int32 z) : x(x), y(y), z(z) {} -}; - -struct vec3s { - int16 x, y, z; -}; - -struct vec4i { - int32 x, y, z, w; -}; - -typedef vec4i Matrix[3]; - -struct Quad { - Index indices[4]; - uint16 flags; -}; - -struct Triangle { - Index indices[3]; - uint16 flags; -}; - -struct Room { - struct Info { - int32 x, z; - int32 yBottom, yTop; - }; - - struct Vertex { - vec3s pos; - uint16 lighting; - }; - - struct Sprite { - Index index; - uint16 texture; - }; - - struct Portal { - uint16 roomIndex; - vec3s n; - vec3s v[4]; - }; - - struct Sector { - uint16 floorIndex; - uint16 boxIndex; - uint8 roomBelow; - int8 floor; - uint8 roomAbove; - int8 ceiling; - }; - - struct Light { - // int32 x, y, z; - // uint16 intensity; - // uint32 radius; - uint8 dummy[18]; - }; - - struct Mesh { - // int32 x, y, z; - // uint16 rotation; - // uint16 intensity; - // uint16 meshID; - uint8 dummy[18]; - }; - - Info info; - uint32 dataSize; -/* - uint16 vCount; - Vertex* vertices; - - uint16 qCount; - Quad* quads; - - uint16 tCount; - Triangle* triangles; - - uint16 sCount; - Sprite* sprites; -*/ -}; - -struct Node { - uint32 flags; - vec3i pos; -}; - -struct Model { - uint32 type; - uint16 mCount; - uint16 mStart; - uint32 node; - uint32 frame; - uint16 animation; - uint16 paddding; -}; - -#define FILE_MODEL_SIZE (sizeof(Model) - 2) // -padding - -struct Entity { - uint16 type; - uint16 room; - vec3i pos; - int16 rotation; - uint16 flags; -}; - -struct EntityDesc { // 32 bytes - uint16 type; - uint16 flags; - - vec3i pos; - - vec3s rot; - uint8 state; - uint8 targetState; - - uint8 vSpeed; - uint8 hSpeed; - uint8 room; - uint8 modelIndex; - - uint16 animIndex; - uint16 frameIndex; -}; - -struct Texture { - uint16 attribute; - uint16 tile:14, :2; - uint32 uv0; - uint32 uv1; - uint32 uv2; - uint32 uv3; -}; - -struct Sprite { - uint16 tile; - uint8 u, v; - uint16 w, h; - int16 l, t, r, b; -}; - -struct SpriteSeq { - uint16 type; - uint16 unused; - int16 sCount; - int16 sStart; -}; - -struct Rect { - int32 x0; - int32 y0; - int32 x1; - int32 y1; -}; - -struct Vertex { - int16 x, y, z; - uint8 g, clip; -}; - -struct VertexUV { - Vertex v; - uint32 uv; -}; - -struct Face { - uint16 flags; - int16 depth; - int16 start; - int8 indices[4]; -}; - -#define FIXED_SHIFT 14 -#define FOV_SHIFT (7 - (SCALE - 1)) - -#define MAX_MATRICES 8 -#define MAX_MODELS 64 -#define MAX_ENTITY 190 -#define MAX_VERTICES 1024 -#define MAX_FACES 512 -#define FOG_SHIFT 2 -#define FOG_MAX (10 * 1024) -#define FOG_MIN (FOG_MAX - (8192 >> FOG_SHIFT)) -#define VIEW_MIN_F (32 << FIXED_SHIFT) -#define VIEW_MAX_F (FOG_MAX << FIXED_SHIFT) - -#define FACE_TRIANGLE 0x8000 -#define FACE_COLORED 0x4000 -#define FACE_CLIPPED 0x2000 -#define FACE_TEXTURE 0x1FFF - -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#define MAX(a,b) ((a) > (b) ? (a) : (b)) -#define SQR(x) ((x) * (x)) - -#define DP43(a,b) ((a).x * (b).x + (a).y * (b).y + (a).z * (b).z + (a).w) -#define DP33(a,b) ((a).x * (b).x + (a).y * (b).y + (a).z * (b).z) - -int32 clamp(int32 x, int32 a, int32 b); -int32 phd_sin(int32 x); -int32 phd_cos(int32 x); - -Matrix& matrixGet(); -void matrixPush(); -void matrixPop(); -void matrixTranslate(const vec3i &offset); -void matrixTranslateAbs(const vec3i &offset); -void matrixRotate(int16 rotX, int16 rotY, int16 rotZ); -void matrixSetView(const vec3i &pos, int16 rotX, int16 rotY); - -void drawGlyph(const Sprite *sprite, int32 x, int32 y); - -void clear(); -void transform(const vec3s &v, int32 vg); -void faceAddTriangle(uint16 flags, const Index* indices, int32 startVertex); -void faceAddQuad(uint16 flags, const Index* indices, int32 startVertex); -void flush(); -void initRender(); - -void readLevel(const uint8 *data); -const Room::Sector* getSector(int32 roomIndex, int32 x, int32 z); -int32 getRoomIndex(int32 roomIndex, const vec3i &pos); - -#endif \ No newline at end of file diff --git a/src/platform/gba/data/GYM.PKD b/src/platform/gba/data/GYM.PKD new file mode 100644 index 00000000..7f1aeb46 Binary files /dev/null and b/src/platform/gba/data/GYM.PKD differ diff --git a/src/platform/gba/data/LEVEL1.PHD b/src/platform/gba/data/LEVEL1.PKD similarity index 62% rename from src/platform/gba/data/LEVEL1.PHD rename to src/platform/gba/data/LEVEL1.PKD index b9ae5632..c5969a35 100644 Binary files a/src/platform/gba/data/LEVEL1.PHD and b/src/platform/gba/data/LEVEL1.PKD differ diff --git a/src/platform/gba/data/LEVEL2.PKD b/src/platform/gba/data/LEVEL2.PKD new file mode 100644 index 00000000..3074ca96 Binary files /dev/null and b/src/platform/gba/data/LEVEL2.PKD differ diff --git a/src/platform/gba/data/TITLE.PKD b/src/platform/gba/data/TITLE.PKD new file mode 100644 index 00000000..5bf0464c Binary files /dev/null and b/src/platform/gba/data/TITLE.PKD differ diff --git a/src/platform/gba/data/TITLE.SCR b/src/platform/gba/data/TITLE.SCR new file mode 100644 index 00000000..3a25b4a8 Binary files /dev/null and b/src/platform/gba/data/TITLE.SCR differ diff --git a/src/platform/gba/data/TRACKS.IMA b/src/platform/gba/data/TRACKS.IMA new file mode 100644 index 00000000..cafca2d7 Binary files /dev/null and b/src/platform/gba/data/TRACKS.IMA differ diff --git a/src/platform/gba/deploy.sh b/src/platform/gba/deploy.sh new file mode 100644 index 00000000..3b290301 --- /dev/null +++ b/src/platform/gba/deploy.sh @@ -0,0 +1,2 @@ +make +./mGBA.exe C:\\Projects\\OpenLara\\src\\platform\\gba\\OpenLara.gba diff --git a/src/platform/gba/icon.png b/src/platform/gba/icon.png deleted file mode 100644 index 1c882e35..00000000 Binary files a/src/platform/gba/icon.png and /dev/null differ diff --git a/src/platform/gba/labels.png b/src/platform/gba/labels.png new file mode 100644 index 00000000..819d2e63 Binary files /dev/null and b/src/platform/gba/labels.png differ diff --git a/src/platform/gba/level.h b/src/platform/gba/level.h deleted file mode 100644 index 433833f3..00000000 --- a/src/platform/gba/level.h +++ /dev/null @@ -1,563 +0,0 @@ -#ifndef H_LEVEL -#define H_LEVEL - -#include "common.h" -#include "camera.h" - -// level file data ------------------- -uint32 tilesCount; -extern const uint8* tiles[15]; - -#if defined(USE_MODE_5) || defined(_WIN32) - extern uint16 palette[256]; -#endif - -extern uint8 lightmap[256 * 32]; - -uint16 roomsCount; - -const uint16* floors; - -uint32 texturesCount; -extern const Texture* textures; - -const Sprite* sprites; - -uint32 spritesSeqCount; -const SpriteSeq* spritesSeq; - -const uint8* meshData; -const uint32* meshOffsets; - -const int32* nodes; - -uint32 modelsCount; -EWRAM_DATA Model models[MAX_MODELS]; -EWRAM_DATA int16 modelsMap[MAX_ENTITY]; - -uint32 entitiesCount; -const Entity* entities; -// ----------------------------------- - -struct RoomDesc { - Rect clip; - bool visible; - int32 x, z; - uint16 vCount; - uint16 qCount; - uint16 tCount; - uint16 pCount; - uint16 zSectors; - uint16 xSectors; - const Room::Vertex* vertices; - const Quad* quads; - const Triangle* triangles; - const Room::Portal* portals; - const Room::Sector* sectors; - - INLINE void reset() { - visible = false; - clip = { FRAME_WIDTH, FRAME_HEIGHT, 0, 0 }; - } -}; - -EWRAM_DATA RoomDesc rooms[64]; - -int32 visRoomsCount; -int32 visRooms[16]; - -#define ROOM_VISIBLE (1 << 15) - -#define ENTITY_LARA 0 - -#define SEQ_GLYPH 190 - -enum FloorType { - FLOOR_TYPE_NONE, - FLOOR_TYPE_PORTAL, - FLOOR_TYPE_FLOOR, - FLOOR_TYPE_CEILING, -}; - -int32 seqGlyphs; -int32 entityLara; - -extern uint32 gVerticesCount; -extern Rect clip; - -void readLevel(const uint8 *data) { // TODO non-hardcode level loader, added *_OFF alignment bytes - tilesCount = *((uint32*)(data + 4)); - for (uint32 i = 0; i < tilesCount; i++) { - tiles[i] = data + 8 + 256 * 256 * i; - } - - #define MDL_OFF 2 - #define ENT_OFF 2 - - roomsCount = *((uint16*)(data + 720908)); - const Room* roomsPtr = (Room*)(data + 720908 + 2); - - floors = (uint16*)(data + 899492 + 4); - - meshData = data + 908172 + 4; - meshOffsets = (uint32*)(data + 975724 + 4); - - nodes = (int32*)(data + 990318); - - modelsCount = *((uint32*)(data + 1270666 + MDL_OFF)); - const uint8* modelsPtr = (uint8*)(data + 1270666 + 4 + MDL_OFF); - - texturesCount = *((uint32*)(data + 1271686 + MDL_OFF)); - textures = (Texture*)(data + 1271686 + 4 + MDL_OFF); - - sprites = (Sprite*)(data + 1289634 + MDL_OFF); - - spritesSeqCount = *((uint32*)(data + 1292130 + MDL_OFF)); - spritesSeq = (SpriteSeq*)(data + 1292130 + 4 + MDL_OFF); - - entitiesCount = *((uint32*)(data + 1319252 + MDL_OFF + ENT_OFF)); - entities = (Entity*)(data + 1319252 + 4 + MDL_OFF + ENT_OFF); - -// prepare lightmap - const uint8* f_lightmap = data + 1320576 + MDL_OFF + ENT_OFF; - memcpy(lightmap, f_lightmap, sizeof(lightmap)); - -// prepare palette -#if !(defined(USE_MODE_5) || defined(_WIN32)) - uint16 palette[256]; -#endif - const uint8* f_palette = data + 1328768 + MDL_OFF + ENT_OFF; - - const uint8* p = f_palette; - - for (int i = 0; i < 256; i++) { - palette[i] = (p[0] >> 1) | ((p[1] >> 1) << 5) | ((p[2] >> 1) << 10); - p += 3; - } - -#ifndef _WIN32 - #ifndef USE_MODE_5 - SetPalette(palette); - #endif -#endif -// prepare models - for (uint32 i = 0; i < modelsCount; i++) { - dmaCopy(modelsPtr, models + i, sizeof(Model)); // sizeof(Model) is faster than FILE_MODEL_SIZE - modelsPtr += FILE_MODEL_SIZE; - modelsMap[models[i].type] = i; - } - -// prepare entities - for (uint32 i = 0; i < entitiesCount; i++) { - if (entities[i].type == ENTITY_LARA) { - entityLara = i; - break; - } - } - -// prepare glyphs - for (uint32 i = 0; i < spritesSeqCount; i++) { - if (spritesSeq[i].type == SEQ_GLYPH) { - seqGlyphs = i; - break; - } - } - -// prepare rooms - uint8 *ptr = (uint8*)roomsPtr; - - for (uint16 roomIndex = 0; roomIndex < roomsCount; roomIndex++) { - const Room *room = (Room*)ptr; - ptr += sizeof(Room); - - uint32 dataSize; - memcpy(&dataSize, &room->dataSize, sizeof(dataSize)); - uint8* skipPtr = ptr + dataSize * 2; - - RoomDesc &desc = rooms[roomIndex]; - desc.reset(); - - // offset - memcpy(&desc.x, &room->info.x, sizeof(room->info.x)); - memcpy(&desc.z, &room->info.z, sizeof(room->info.z)); - - // vertices - desc.vCount = *((uint16*)ptr); - ptr += 2; - desc.vertices = (Room::Vertex*)ptr; - ptr += sizeof(Room::Vertex) * desc.vCount; - - // quads - desc.qCount = *((uint16*)ptr); - ptr += 2; - desc.quads = (Quad*)ptr; - ptr += sizeof(Quad) * desc.qCount; - - // triangles - desc.tCount = *((uint16*)ptr); - ptr += 2; - desc.triangles = (Triangle*)ptr; - ptr += sizeof(Triangle) * desc.tCount; - - ptr = skipPtr; - - // portals - desc.pCount = *((uint16*)ptr); - ptr += 2; - desc.portals = (Room::Portal*)ptr; - ptr += sizeof(Room::Portal) * desc.pCount; - - desc.zSectors = *((uint16*)ptr); - ptr += 2; - desc.xSectors = *((uint16*)ptr); - ptr += 2; - desc.sectors = (Room::Sector*)ptr; - ptr += sizeof(Room::Sector) * desc.zSectors * desc.xSectors; - - //ambient = *((uint16*)ptr); - ptr += 2; - - uint16 lightsCount = *((uint16*)ptr); - ptr += 2; - //lights = (Room::Light*)ptr; - ptr += sizeof(Room::Light) * lightsCount; - - uint16 meshesCount = *((uint16*)ptr); - ptr += 2; - //meshes = (Room::Mesh*)ptr; - ptr += sizeof(Room::Mesh) * meshesCount; - - ptr += 2 + 2; // skip alternateRoom and flags - } - - camera.init(); - camera.room = entities[entityLara].room; -} - -void drawMesh(int16 meshIndex) { - uint32 offset = meshOffsets[meshIndex]; - const uint8* ptr = meshData + offset; - - ptr += 2 * 5; // skip [cx, cy, cz, radius, flags] - - int16 vCount = *(int16*)ptr; ptr += 2; - const vec3s* vertices = (vec3s*)ptr; - ptr += vCount * 3 * sizeof(int16); - - int16 nCount = *(int16*)ptr; ptr += 2; - //const int16* normals = (int16*)ptr; - if (nCount > 0) { // normals - ptr += nCount * 3 * sizeof(int16); - } else { // intensity - ptr += vCount * sizeof(int16); - } - - int16 rCount = *(int16*)ptr; ptr += 2; - Quad* rFaces = (Quad*)ptr; ptr += rCount * sizeof(Quad); - - int16 tCount = *(int16*)ptr; ptr += 2; - Triangle* tFaces = (Triangle*)ptr; ptr += tCount * sizeof(Triangle); - - int16 crCount = *(int16*)ptr; ptr += 2; - Quad* crFaces = (Quad*)ptr; ptr += crCount * sizeof(Quad); - - int16 ctCount = *(int16*)ptr; ptr += 2; - Triangle* ctFaces = (Triangle*)ptr; ptr += ctCount * sizeof(Triangle); - - int32 startVertex = gVerticesCount; - - for (uint16 i = 0; i < vCount; i++) { - transform(*vertices++, 4096); - } - - for (int i = 0; i < rCount; i++) { - faceAddQuad(rFaces[i].flags, rFaces[i].indices, startVertex); - } - - for (int i = 0; i < crCount; i++) { - faceAddQuad(crFaces[i].flags | FACE_COLORED, crFaces[i].indices, startVertex); - } - - for (int i = 0; i < tCount; i++) { - faceAddTriangle(tFaces[i].flags, tFaces[i].indices, startVertex); - } - - for (int i = 0; i < ctCount; i++) { - faceAddTriangle(ctFaces[i].flags | FACE_COLORED, ctFaces[i].indices, startVertex); - } -} - -void drawModel(int32 modelIndex) { - const Model* model = models + modelIndex; - - // non-aligned access - uint32 node, frame; - memcpy(&node, &model->node, sizeof(node)); - memcpy(&frame, &model->frame, sizeof(frame)); - - Node bones[32]; - memcpy(bones, nodes + node, (model->mCount - 1) * sizeof(Node)); - - const Node* n = bones; - - drawMesh(model->mStart); - - for (int i = 1; i < model->mCount; i++) { - if (n->flags & 1) { - matrixPop(); - } - - if (n->flags & 2) { - matrixPush(); - } - - matrixTranslate(n->pos); - n++; - - drawMesh(model->mStart + i); - } -} - -void drawEntity(int32 entityIndex) { - const Entity &e = entities[entityIndex]; - - matrixPush(); - matrixTranslateAbs(vec3i(e.pos.x, e.pos.y - 512, e.pos.z)); // TODO animation - - drawModel(modelsMap[e.type]); - - matrixPop(); -} - -void drawNumber(int32 number, int32 x, int32 y) { - const int32 widths[] = { 12, 8, 10, 10, 10, 10, 10, 10, 10, 10 }; - - const Sprite *glyphSprites = sprites + spritesSeq[seqGlyphs].sStart; - - while (number > 0) { - x -= widths[number % 10]; - drawGlyph(glyphSprites + 52 + (number % 10), x, y); - number /= 10; - } -} - -void drawRoom(int16 roomIndex) { - RoomDesc &room = rooms[roomIndex]; - - clip = room.clip; - - int32 startVertex = gVerticesCount; - - matrixPush(); - matrixTranslateAbs(vec3i(room.x, 0, room.z)); - - const Room::Vertex* vertex = room.vertices; - for (uint16 i = 0; i < room.vCount; i++) { - transform(vertex->pos, vertex->lighting); - vertex++; - } - - matrixPop(); - - const Quad* quads = room.quads; - for (uint16 i = 0; i < room.qCount; i++) { - faceAddQuad(quads[i].flags, quads[i].indices, startVertex); - } - - const Triangle* triangles = room.triangles; - for (uint16 i = 0; i < room.tCount; i++) { - faceAddTriangle(triangles[i].flags, triangles[i].indices, startVertex); - } - - if (roomIndex == entityLara) { // TODO draw all entities in the room - drawEntity(entityLara); - } - - room.reset(); - - flush(); -} - -const Room::Sector* getSector(int32 roomIndex, int32 x, int32 z) { - RoomDesc &room = rooms[roomIndex]; - - int32 sx = clamp((x - room.x) >> 10, 0, room.xSectors); - int32 sz = clamp((z - room.z) >> 10, 0, room.zSectors); - - return room.sectors + sx * room.zSectors + sz; -} - -int32 getRoomIndex(int32 roomIndex, const vec3i &pos) { - const Room::Sector *sector = getSector(roomIndex, pos.x, pos.z); - - if (sector->floorIndex) { - const uint16 *data = floors + sector->floorIndex; - int16 type = *data++; - - if (type == FLOOR_TYPE_FLOOR) { - data++; - type = *data++; - } - - if (type == FLOOR_TYPE_CEILING) { - data++; - type = *data++; - } - - if ((type & 0xFF) == FLOOR_TYPE_PORTAL) { - roomIndex = *data; - } - } - - while (sector->roomAbove != 0xFF && pos.y < (sector->ceiling << 8)) { - roomIndex = sector->roomAbove; - sector = getSector(roomIndex, pos.x, pos.z); - } - - while (sector->roomBelow != 0xFF && pos.y >= (sector->floor << 8)) { - roomIndex = sector->roomBelow; - sector = getSector(roomIndex, pos.x, pos.z); - } - - return roomIndex; -} - -bool checkPortal(int32 roomIndex, const Room::Portal &portal) { - RoomDesc &room = rooms[roomIndex]; - - vec3i d; - d.x = portal.v[0].x - camera.pos.x + room.x; - d.y = portal.v[0].y - camera.pos.y; - d.z = portal.v[0].z - camera.pos.z + room.z; - - if (DP33(portal.n, d) >= 0) { - return false; - } - - int32 x0 = room.clip.x1; - int32 y0 = room.clip.y1; - int32 x1 = room.clip.x0; - int32 y1 = room.clip.y0; - - int32 znear = 0, zfar = 0; - - Matrix &m = matrixGet(); - - vec3i pv[4]; - - for (int32 i = 0; i < 4; i++) { - const vec3s &v = portal.v[i]; - - int32 x = DP43(m[0], v); - int32 y = DP43(m[1], v); - int32 z = DP43(m[2], v); - - pv[i].x = x; - pv[i].y = y; - pv[i].z = z; - - if (z <= VIEW_MIN_F) { - znear++; - continue; - } - - if (z >= VIEW_MAX_F) { - zfar++; - } - - if (z != 0) { - z >>= FOV_SHIFT; - x = (x / z) + (FRAME_WIDTH / 2); - y = (y / z) + (FRAME_HEIGHT / 2); - } else { - x = (x > 0) ? clip.x1 : clip.x0; - y = (y > 0) ? clip.y1 : clip.y0; - } - - if (x < x0) x0 = x; - if (x > x1) x1 = x; - if (y < y0) y0 = y; - if (y > y1) y1 = y; - } - - if (znear == 4 || zfar == 4) return false; - - if (znear) { - vec3i *a = pv; - vec3i *b = pv + 3; - for (int32 i = 0; i < 4; i++) { - if ((a->z < 0) ^ (b->z < 0)) { - if (a->x < 0 && b->x < 0) { - x0 = 0; - } else if (a->x > 0 && b->x > 0) { - x1 = FRAME_WIDTH; - } else { - x0 = 0; - x1 = FRAME_WIDTH; - } - - if (a->y < 0 && b->y < 0) { - y0 = 0; - } else if (a->y > 0 && b->y > 0) { - y1 = FRAME_HEIGHT; - } else { - y0 = 0; - y1 = FRAME_HEIGHT; - } - } - b = a; - a++; - } - } - - if (x0 < room.clip.x0) x0 = room.clip.x0; - if (x1 > room.clip.x1) x1 = room.clip.x1; - if (y0 < room.clip.y0) y0 = room.clip.y0; - if (y1 > room.clip.y1) y1 = room.clip.y1; - - if (x0 >= x1 || y0 >= y1) return false; - - RoomDesc &nextRoom = rooms[portal.roomIndex]; - - if (x0 < nextRoom.clip.x0) nextRoom.clip.x0 = x0; - if (x1 > nextRoom.clip.x1) nextRoom.clip.x1 = x1; - if (y0 < nextRoom.clip.y0) nextRoom.clip.y0 = y0; - if (y1 > nextRoom.clip.y1) nextRoom.clip.y1 = y1; - - if (!nextRoom.visible) { - nextRoom.visible = true; - visRooms[visRoomsCount++] = portal.roomIndex; - } - - return true; -} - -void getVisibleRooms(int32 roomIndex) { - RoomDesc &room = rooms[roomIndex]; - - matrixPush(); - matrixTranslateAbs(vec3i(room.x, 0, room.z)); - - for (int32 i = 0; i < room.pCount; i++) { - const Room::Portal &portal = room.portals[i]; - if (checkPortal(roomIndex, portal)) { - getVisibleRooms(portal.roomIndex); - } - } - - matrixPop(); -} - -void drawRooms() { - rooms[camera.room].clip = { 0, 0, FRAME_WIDTH, FRAME_HEIGHT }; - visRoomsCount = 0; - visRooms[visRoomsCount++] = camera.room; - - getVisibleRooms(camera.room); - - while (visRoomsCount--) { - drawRoom(visRooms[visRoomsCount]); - } -} - -#endif diff --git a/src/platform/gba/main.cpp b/src/platform/gba/main.cpp index b379c728..4ccadbc1 100644 --- a/src/platform/gba/main.cpp +++ b/src/platform/gba/main.cpp @@ -1,310 +1,624 @@ -#ifndef _WIN32 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "LEVEL1_PHD.h" -#endif - -//#define PROFILE +#include "game.h" -#include "common.h" -#include "level.h" -#include "camera.h" +EWRAM_DATA int32 fps; +EWRAM_DATA int32 frameIndex = 0; +EWRAM_DATA int32 fpsCounter = 0; +EWRAM_DATA uint32 curSoundBuffer = 0; #ifdef _WIN32 - uint8* LEVEL1_PHD; +const void* TRACKS_IMA; +const void* TITLE_SCR; +const void* levelData; - uint32 SCREEN[WIDTH * HEIGHT]; +HWND hWnd; - extern uint8 fb[WIDTH * HEIGHT * 2]; +LARGE_INTEGER g_timer; +LARGE_INTEGER g_current; - #define WND_SCALE 4 -#else - extern uint32 fb; -#endif +#define WND_WIDTH 240*4 +#define WND_HEIGHT 160*4 -bool keys[IK_MAX] = {}; +uint16 MEM_PAL_BG[256]; +uint32 SCREEN[FRAME_WIDTH * FRAME_HEIGHT]; -int32 fps; -int32 frameIndex = 0; -int32 fpsCounter = 0; +void osSetPalette(const uint16* palette) +{ + memcpy(MEM_PAL_BG, palette, 256 * 2); +} -void update(int32 frames) { - for (int32 i = 0; i < frames; i++) { - camera.update(); - } +int32 osGetSystemTimeMS() +{ + return GetTickCount(); } -#ifdef WIN32 -extern Vertex gVertices[MAX_VERTICES]; +bool osSaveSettings() +{ + FILE* f = fopen("settings.dat", "wb"); + if (!f) return false; + fwrite(&gSettings, sizeof(gSettings), 1, f); + fclose(f); + return true; +} -INLINE int32 classify(const Vertex* v) { - return (v->x < clip.x0 ? 1 : 0) | - (v->x > clip.x1 ? 2 : 0) | - (v->y < clip.y0 ? 4 : 0) | - (v->y > clip.y1 ? 8 : 0); +bool osLoadSettings() +{ + FILE* f = fopen("settings.dat", "rb"); + if (!f) return false; + uint8 version; + fread(&version, 1, 1, f); + if (version != gSettings.version) { + fclose(f); + return false; + } + fread((uint8*)&gSettings + 1, sizeof(gSettings) - 1, 1, f); + fclose(f); + return true; } -void drawTest() { - static Rect testClip = { 0, 0, FRAME_WIDTH, FRAME_HEIGHT }; - static int32 testTile = 707; // 712 +bool osCheckSave() +{ + FILE* f = fopen("savegame.dat", "rb"); + if (!f) return false; + fclose(f); + return true; +} - int dx = 0; - int dy = 0; +bool osSaveGame() +{ + FILE* f = fopen("savegame.dat", "wb"); + if (!f) return false; + fwrite(&gSaveGame, sizeof(gSaveGame), 1, f); + fwrite(&gSaveData, gSaveGame.dataSize, 1, f); + fclose(f); + return true; +} - if (GetAsyncKeyState(VK_LEFT)) dx--; - if (GetAsyncKeyState(VK_RIGHT)) dx++; - if (GetAsyncKeyState(VK_UP)) dy--; - if (GetAsyncKeyState(VK_DOWN)) dy++; +bool osLoadGame() +{ + FILE* f = fopen("savegame.dat", "rb"); + if (!f) return false; - if (GetAsyncKeyState('T')) { - testClip.x0 += dx; - testClip.y0 += dy; - } + uint32 version; + fread(&version, sizeof(version), 1, f); - if (GetAsyncKeyState('B')) { - testClip.x1 += dx; - testClip.y1 += dy; - } - - if (GetAsyncKeyState('U')) { - testTile += dx; - if (testTile < 0) testTile = 0; - if (testTile >= texturesCount) testTile = texturesCount - 1; + if (SAVEGAME_VER != version) + { + fclose(f); + return false; } - clip = testClip; - - gVertices[0].x = 50 + 50; - gVertices[0].y = 50; - - gVertices[1].x = FRAME_WIDTH - 50 - 50; - gVertices[1].y = 50; + fread(&gSaveGame.dataSize, sizeof(gSaveGame) - sizeof(version), 1, f); + fread(&gSaveData, gSaveGame.dataSize, 1, f); + fclose(f); + return true; +} - gVertices[2].x = FRAME_WIDTH - 50; - gVertices[2].y = FRAME_HEIGHT - 50; +void osJoyVibrate(int32 index, int32 L, int32 R) {} - gVertices[3].x = 50; - gVertices[3].y = FRAME_HEIGHT - 50; +extern uint8 soundBuffer[2 * SND_SAMPLES + 32]; // 32 bytes of silence for DMA overrun while interrupt - for (int i = 0; i < 4; i++) { - gVertices[i].z = 100; - gVertices[i].g = 128; - gVertices[i].clip = classify(gVertices + i); - } - gVerticesCount = 4; +HWAVEOUT waveOut; +WAVEFORMATEX waveFmt = { WAVE_FORMAT_PCM, 1, SND_OUTPUT_FREQ, SND_OUTPUT_FREQ, 1, 8, sizeof(waveFmt) }; +WAVEHDR waveBuf[2]; - Index indices[] = { 0, 1, 2, 3, 0, 2, 3 }; +void soundInit() +{ + sndInit(); - faceAddQuad(testTile, indices, 0); + if (waveOutOpen(&waveOut, WAVE_MAPPER, &waveFmt, (INT_PTR)hWnd, 0, CALLBACK_WINDOW) != MMSYSERR_NOERROR) + return; - for (int y = 0; y < FRAME_HEIGHT; y++) { - for (int x = 0; x < FRAME_WIDTH; x++) { - if (x == clip.x0 || x == clip.x1 - 1 || y == clip.y0 || y == clip.y1 - 1) - fb[y * FRAME_WIDTH + x] = 255; - } + memset(&waveBuf, 0, sizeof(waveBuf)); + for (int i = 0; i < 2; i++) + { + WAVEHDR *waveHdr = waveBuf + i; + waveHdr->dwBufferLength = SND_SAMPLES; + waveHdr->lpData = (LPSTR)(soundBuffer + i * SND_SAMPLES); + waveOutPrepareHeader(waveOut, waveHdr, sizeof(WAVEHDR)); + waveOutWrite(waveOut, waveHdr, sizeof(WAVEHDR)); } - - flush(); - - Sleep(16); } -#endif - -void render() { - clear(); - drawRooms(); - //drawTest(); - - drawNumber(fps, FRAME_WIDTH, 16); +void soundFill() +{ + WAVEHDR *waveHdr = waveBuf + curSoundBuffer; + waveOutUnprepareHeader(waveOut, waveHdr, sizeof(WAVEHDR)); + sndFill((uint8*)waveHdr->lpData, SND_SAMPLES); + waveOutPrepareHeader(waveOut, waveHdr, sizeof(WAVEHDR)); + waveOutWrite(waveOut, waveHdr, sizeof(WAVEHDR)); + curSoundBuffer ^= 1; } -#ifdef _WIN32 HDC hDC; -void blit() { - #ifdef USE_MODE_5 - for (int i = 0; i < WIDTH * HEIGHT; i++) { - uint16 c = ((uint16*)fb)[i]; - SCREEN[i] = (((c << 3) & 0xFF) << 16) | ((((c >> 5) << 3) & 0xFF) << 8) | ((c >> 10 << 3) & 0xFF) | 0xFF000000; - } - #else - for (int i = 0; i < WIDTH * HEIGHT; i++) { - #ifdef DEBUG_OVERDRAW - uint8 c = ((uint8*)fb)[i]; - SCREEN[i] = c | (c << 8) | (c << 16) | 0xFF000000; - #else - uint16 c = palette[((uint8*)fb)[i]]; - SCREEN[i] = (((c << 3) & 0xFF) << 16) | ((((c >> 5) << 3) & 0xFF) << 8) | ((c >> 10 << 3) & 0xFF) | 0xFF000000; - #endif - } - #endif - - const BITMAPINFO bmi = { sizeof(BITMAPINFOHEADER), WIDTH, -HEIGHT, 1, 32, BI_RGB, 0, 0, 0, 0, 0 }; - StretchDIBits(hDC, 0, 0, 240 * WND_SCALE, 160 * WND_SCALE, 0, 0, WIDTH, HEIGHT, SCREEN, &bmi, DIB_RGB_COLORS, SRCCOPY); +void blit() +{ + for (int i = 0; i < FRAME_WIDTH * FRAME_HEIGHT; i++) + { + uint16 c = MEM_PAL_BG[((uint8*)fb)[i]]; + SCREEN[i] = (((c << 3) & 0xFF) << 16) | ((((c >> 5) << 3) & 0xFF) << 8) | ((c >> 10 << 3) & 0xFF) | 0xFF000000; + } + const BITMAPINFO bmi = { sizeof(BITMAPINFOHEADER), FRAME_WIDTH, -FRAME_HEIGHT, 1, 32, BI_RGB, 0, 0, 0, 0, 0 }; + StretchDIBits(hDC, 0, 0, WND_WIDTH, WND_HEIGHT, 0, 0, FRAME_WIDTH, FRAME_HEIGHT, SCREEN, &bmi, DIB_RGB_COLORS, SRCCOPY); } -LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { - switch (msg) { +LRESULT CALLBACK wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_ACTIVATE: + { + keys = 0; + break; + } + case WM_DESTROY : + { PostQuitMessage(0); break; - case WM_KEYDOWN : - case WM_KEYUP : { - InputKey key = IK_MAX; + } + + case WM_KEYDOWN : + case WM_KEYUP : + case WM_SYSKEYUP : + case WM_SYSKEYDOWN : + { + InputKey key = IK_NONE; switch (wParam) { - case VK_UP : key = IK_UP; break; - case VK_RIGHT : key = IK_RIGHT; break; - case VK_DOWN : key = IK_DOWN; break; - case VK_LEFT : key = IK_LEFT; break; - case 'Z' : key = IK_A; break; - case 'X' : key = IK_B; break; - case 'A' : key = IK_L; break; - case 'S' : key = IK_R; break; + case VK_UP : key = IK_UP; break; + case VK_RIGHT : key = IK_RIGHT; break; + case VK_DOWN : key = IK_DOWN; break; + case VK_LEFT : key = IK_LEFT; break; + case 'A' : key = IK_B; break; + case 'S' : key = IK_A; break; + case 'Q' : key = IK_L; break; + case 'W' : key = IK_R; break; + case VK_RETURN : key = IK_START; break; + case VK_SPACE : key = IK_SELECT; break; } - if (key != IK_MAX) { - keys[key] = msg != WM_KEYUP; + + if (wParam == '1') players[0]->extraL->goalWeapon = WEAPON_PISTOLS; + if (wParam == '2') players[0]->extraL->goalWeapon = WEAPON_MAGNUMS; + if (wParam == '3') players[0]->extraL->goalWeapon = WEAPON_UZIS; + if (wParam == '4') players[0]->extraL->goalWeapon = WEAPON_SHOTGUN; + + if (msg != WM_KEYUP && msg != WM_SYSKEYUP) { + keys |= key; + } else { + keys &= ~key; } break; } + + case MM_WOM_DONE : + { + soundFill(); + break; + } + default : return DefWindowProc(hWnd, msg, wParam, lParam); } return 0; } -#endif -void vblank() { - frameIndex++; -} +void* osLoadLevel(const char* name) +{ + // level1 + char buf[32]; + + delete[] levelData; + + sprintf(buf, "data/%s.PKD", name); + + FILE *f = fopen(buf, "rb"); + + if (!f) + return NULL; -int main(void) { -#ifdef _WIN32 { - FILE *f = fopen("data/LEVEL1.PHD", "rb"); fseek(f, 0, SEEK_END); int32 size = ftell(f); fseek(f, 0, SEEK_SET); - LEVEL1_PHD = new uint8[size]; - fread(LEVEL1_PHD, 1, size, f); + uint8* data = new uint8[size]; + fread(data, 1, size, f); fclose(f); + + levelData = data; } -#else - // set low latency mode via WAITCNT register (thanks to GValiente) - #define BIT_SET(y, flag) (y |= (flag)) - #define REG_WAITCNT_NV *(u16*)(REG_BASE + 0x0204) - BIT_SET(REG_WAITCNT_NV, 0x0008 | 0x0010 | 0x4000); -#endif +// tracks + if (!TRACKS_IMA) + { + FILE *f = fopen("data/TRACKS.IMA", "rb"); + if (!f) + return NULL; - initRender(); + fseek(f, 0, SEEK_END); + int32 size = ftell(f); + fseek(f, 0, SEEK_SET); + uint8* data = new uint8[size]; + fread(data, 1, size, f); + fclose(f); - readLevel(LEVEL1_PHD); + TRACKS_IMA = data; + } -#ifdef _WIN32 - RECT r = { 0, 0, 240 * WND_SCALE, 160 * WND_SCALE }; + if (!TITLE_SCR) + { + FILE *f = fopen("data/TITLE.SCR", "rb"); + if (!f) + return NULL; + + fseek(f, 0, SEEK_END); + int32 size = ftell(f); + fseek(f, 0, SEEK_SET); + uint8* data = new uint8[size]; + fread(data, 1, size, f); + fclose(f); + + TITLE_SCR = data; + } + + return (void*)levelData; +} + +int main(void) +{ + RECT r = { 0, 0, WND_WIDTH, WND_HEIGHT }; AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, false); int wx = (GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left)) / 2; int wy = (GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) / 2; - HWND hWnd = CreateWindow("static", "OpenLara GBA", WS_OVERLAPPEDWINDOW, wx + r.left, wy + r.top, r.right - r.left, r.bottom - r.top, 0, 0, 0, 0); + hWnd = CreateWindow("static", "OpenLara GBA", WS_OVERLAPPEDWINDOW, wx + r.left, wy + r.top, r.right - r.left, r.bottom - r.top, 0, 0, 0, 0); hDC = GetDC(hWnd); SetWindowLong(hWnd, GWL_WNDPROC, (LONG)&wndProc); ShowWindow(hWnd, SW_SHOWDEFAULT); + soundInit(); + + gameInit(gLevelInfo[gLevelID].name); + MSG msg; - int startTime = GetTickCount(); - int lastTime = -15; + int32 startTime = GetTickCount() - 33; + int32 lastFrame = 0; do { if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { + int32 frame = (GetTickCount() - startTime) / 33; + if (GetAsyncKeyState('R')) frame /= 10; - int time = GetTickCount() - startTime; - update((time - lastTime) / 16); - lastTime = time; + int32 count = frame - lastFrame; + if (GetAsyncKeyState('T')) count *= 10; + gameUpdate(count); + lastFrame = frame; - render(); + gameRender(); blit(); } } while (msg.message != WM_QUIT); + return 0; +} #else - irqInit(); - irqSet(IRQ_VBLANK, vblank); - irqEnable(IRQ_VBLANK); +void osSetPalette(const uint16* palette) +{ + memcpy((uint16*)MEM_PAL_BG, palette, 256 * 2); +} - uint16 mode = BG2_ON | BACKBUFFER; +int32 osGetSystemTimeMS() +{ + return 0; // TODO +} - #ifdef USE_MODE_5 - mode |= MODE_5; +const uint8 SRAM_MAGIC[4] = { 14, 02, 19, 68 }; - REG_BG2PA = 256 - 64 - 16 - 4 - 1; - REG_BG2PD = 256 - 48 - 2; - #else - mode |= MODE_4; +int32 byteCopy(volatile uint8* dst, const volatile uint8* src, uint32 count) +{ + for (uint32 i = 0; i < count; i++) + { + *dst++ = *src++; + } + return count; +} - REG_BG2PA = 256 / SCALE; - REG_BG2PD = 256 / SCALE; - #endif +bool checkSRAM(volatile uint8* src) +{ + for (uint32 i = 0; i < sizeof(SRAM_MAGIC); i++) + { + if (SRAM_MAGIC[i] != *src++) + return false; + } + return true; +} + +bool osSaveSettings() +{ + volatile uint8* ptr = (uint8*)MEM_SRAM; + + byteCopy(ptr, SRAM_MAGIC, 4); + if (!checkSRAM(ptr)) + return false; + ptr += 4; + + volatile uint8* data = (uint8*)&gSettings; + byteCopy(ptr, data, sizeof(gSettings)); + + return true; +} + +bool osLoadSettings() +{ + volatile uint8* ptr = (uint8*)MEM_SRAM; + + if (!checkSRAM(ptr)) + return false; + ptr += 4; + + if (SETTINGS_VER != *ptr) + return false; + + volatile uint8* data = (uint8*)&gSettings; + byteCopy(data, ptr, sizeof(gSettings)); + + return true; +} + +bool osCheckSave() +{ + volatile uint8* ptr = (uint8*)MEM_SRAM + SETTINGS_SIZE; + + if (!checkSRAM(ptr)) + return false; + ptr += 4; + + uint32 version; + byteCopy((uint8*)&version, ptr, sizeof(version)); + + return (SAVEGAME_VER == version); +} + +bool osSaveGame() +{ + volatile uint8* ptr = (uint8*)MEM_SRAM + SETTINGS_SIZE; + + byteCopy(ptr, SRAM_MAGIC, 4); + if (!checkSRAM(ptr)) + return false; + + ptr += 4; + ptr += byteCopy(ptr, (uint8*)&gSaveGame, sizeof(gSaveGame)); + byteCopy(ptr, (uint8*)&gSaveData, gSaveGame.dataSize); + return true; +} + +bool osLoadGame() +{ + if (!osCheckSave()) + return false; + + volatile uint8* ptr = (uint8*)MEM_SRAM + SETTINGS_SIZE + 4; // skip magic + + ptr += byteCopy((uint8*)&gSaveGame, ptr, sizeof(gSaveGame)); + byteCopy((uint8*)&gSaveData, ptr, gSaveGame.dataSize); + + return true; +} + +#define GPIO_RUMBLE_DATA (*(vu16*)0x80000C4) +#define GPIO_RUMBLE_DIRECTION (*(vu16*)0x80000C6) +#define GPIO_RUMBLE_CONTROL (*(vu16*)0x80000C8) +#define GPIO_RUMBLE_MASK (1 << 3) + +#define CART_RUMBLE_TICKS 6 + +EWRAM_DATA int32 cartRumbleTick = 0; + +void rumbleInit() +{ + GPIO_RUMBLE_DIRECTION = GPIO_RUMBLE_MASK; + GPIO_RUMBLE_CONTROL = 1; +} + +void rumbleSet(bool enable) +{ + if (enable) { + GPIO_RUMBLE_DATA |= GPIO_RUMBLE_MASK; + cartRumbleTick = CART_RUMBLE_TICKS; + } else { + GPIO_RUMBLE_DATA &= ~GPIO_RUMBLE_MASK; + cartRumbleTick = 0; + } +} + +void rumbleUpdate(int32 frames) +{ + if (!cartRumbleTick) + return; + + cartRumbleTick -= frames; + + if (cartRumbleTick <= 0) { + rumbleSet(false); + } +} + +void osJoyVibrate(int32 index, int32 L, int32 R) +{ + if (!gSettings.controls_vibration) + return; + rumbleSet(X_MAX(L, R) > 0); +} + +void updateInput() +{ + keys = 0; + key_poll(); + if (key_is_down(KEY_UP)) keys |= IK_UP; + if (key_is_down(KEY_RIGHT)) keys |= IK_RIGHT; + if (key_is_down(KEY_DOWN)) keys |= IK_DOWN; + if (key_is_down(KEY_LEFT)) keys |= IK_LEFT; + if (key_is_down(KEY_A)) keys |= IK_A; + if (key_is_down(KEY_B)) keys |= IK_B; + if (key_is_down(KEY_L)) keys |= IK_L; + if (key_is_down(KEY_R)) keys |= IK_R; + if (key_is_down(KEY_START)) keys |= IK_START; + if (key_is_down(KEY_SELECT)) keys |= IK_SELECT; +} + +extern uint8* soundBuffer; + +void soundInit() +{ + sndInit(); + + REG_SOUNDCNT_X = SSTAT_ENABLE; + REG_SOUNDCNT_H = SDS_ATMR0 | SDS_AL | SDS_AR | SDS_ARESET | SDS_A100; + REG_TM0D = 65536 - (16777216 / SND_OUTPUT_FREQ); + REG_TM0CNT = TM_ENABLE; + REG_DMA1DAD = (u32)®_FIFO_A; +} + +void soundFill() +{ + if (curSoundBuffer == 1) { + REG_DMA1CNT = 0; + REG_DMA1SAD = (u32)soundBuffer; + REG_DMA1CNT = DMA_DST_FIXED | DMA_REPEAT | DMA_16 | DMA_AT_FIFO | DMA_ENABLE; + } + + sndFill(soundBuffer + curSoundBuffer * SND_SAMPLES, SND_SAMPLES); + curSoundBuffer ^= 1; +} + +void vblank() +{ + frameIndex++; + soundFill(); +} + +#define MEM_CHECK_MAGIC 14021968 + +extern bool checkROM(uint32 mask); // should be in IWRAM + +const uint32 WSCNT_MASK[] = { + WS_ROM0_N2 | WS_ROM0_S1 | WS_PREFETCH, + WS_ROM0_N3 | WS_ROM0_S1 | WS_PREFETCH, +}; + +void boostROM() +{ + for (int32 i = 0; i < X_COUNT(WSCNT_MASK); i++) + { + if (checkROM(WSCNT_MASK[i])) { + break; + } + } +} + +void boostEWRAM() +{ + // Undocumented - Internal Memory Control (R/W) + vu32& fastAccessReg = *(vu32*)(REG_BASE+0x0800); + + fastAccessReg = 0x0E000020; // fast EWRAM + + vu32* fastAccessMem = (vu32*)gSpheres; // check EWRAM access + // write + for (int32 i = 0; i < 16; i++) + { + fastAccessMem[i] = MEM_CHECK_MAGIC + i; + } + // read + for (int32 i = 0; i < 16; i++) + { + if (fastAccessMem[i] != vu32(MEM_CHECK_MAGIC + i)) + { + fastAccessReg = 0x0D000020; // device doesn't support this feature, revert reg value + } + } +} + +void* osLoadLevel(const char* name) +{ + for (int32 i = 0; i < LVL_MAX; i++) + { + if (strcmp(name, gLevelInfo[i].name) == 0) + return (void*)gLevelInfo[i].data; + } + + gLevelID = LVL_TR1_TITLE; + return (void*)gLevelInfo[gLevelID].data; +} + +int main(void) +{ + if (intptr_t(divTable) != MEM_EWRAM) return 0; + if (intptr_t(gLightmap) != MEM_IWRAM) return 0; + + irq_init(NULL); + irq_add(II_VBLANK, vblank); + irq_enable(II_VBLANK); + + boostROM(); + boostEWRAM(); + + rumbleInit(); + soundInit(); + + gameInit(gLevelInfo[gLevelID].name); + + uint16 mode = DCNT_BG2 | DCNT_PAGE | DCNT_MODE4; int32 lastFrameIndex = -1; - #ifdef PROFILE - int counter = 0; - #endif - - while (1) { - //VBlankIntrWait(); - - #ifdef PROFILE - if (counter++ >= 10) return 0; - #endif - - SetMode(mode ^= BACKBUFFER); - fb ^= 0xA000; - - scanKeys(); - uint16 key = keysDown() | keysHeld(); - keys[IK_UP] = (key & KEY_UP); - keys[IK_RIGHT] = (key & KEY_RIGHT); - keys[IK_DOWN] = (key & KEY_DOWN); - keys[IK_LEFT] = (key & KEY_LEFT); - keys[IK_A] = (key & KEY_A); - keys[IK_B] = (key & KEY_B); - keys[IK_L] = (key & KEY_L); - keys[IK_R] = (key & KEY_R); - - int32 frame = frameIndex; - update(frame - lastFrameIndex); + while (1) + { + updateInput(); + + int32 frame = frameIndex / 2; + int32 delta = frame - lastFrameIndex; + + if (!delta) + continue; lastFrameIndex = frame; - render(); + rumbleUpdate(delta); + + gameUpdate(delta); + + #ifdef PROFILING + VBlankIntrWait(); + #else + if (gSettings.video_vsync) { + VBlankIntrWait(); + } + #endif + + REG_DISPCNT = (mode ^= DCNT_PAGE); + fb ^= VRAM_PAGE_SIZE; + + gameRender(); fpsCounter++; - if (frameIndex >= 60) { + if (frameIndex >= 60) + { frameIndex -= 60; - lastFrameIndex -= 60; + lastFrameIndex -= 30; fps = fpsCounter; fpsCounter = 0; } - } -#endif + + return 0; } +#endif diff --git a/src/platform/gba/packer/IMA.h b/src/platform/gba/packer/IMA.h new file mode 100644 index 00000000..c38b761e --- /dev/null +++ b/src/platform/gba/packer/IMA.h @@ -0,0 +1,163 @@ +#ifndef H_IMA +#define H_IMA + +#include "common.h" + +#define CLIP(x,lo,hi) \ + if ( x < lo ) \ + { \ + x = lo; \ + } \ + else if ( x > hi ) \ + { \ + x = hi; \ + } + +// ADPCM.h - Common ADPCM definitions +static short gIndexDeltas[16] = { + -1,-1,-1,-1, 2, 4, 6, 8, + -1,-1,-1,-1, 2, 4, 6, 8 +}; + +/* DVI ADPCM step table */ +static short gStepSizes[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, + 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, + 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, + 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, + 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, + 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, + 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, +11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, +32767 }; + +// Intel DVI ADPCM (ADP4) encoder based on the original SoundHack code https://github.com/tomerbe/SoundHack +long lastEstimateL, stepSizeL, stepIndexL; +long lastEstimateR, stepSizeR, stepIndexR; + +char EncodeDelta( long stepSize, long delta ) +{ + char encodedSample = 0; + + if ( delta < 0L ) + { + encodedSample = 8; + delta = -delta; + } + if ( delta >= stepSize ) + { + encodedSample |= 4; + delta -= stepSize; + } + stepSize = stepSize >> 1; + if ( delta >= stepSize ) + { + encodedSample |= 2; + delta -= stepSize; + } + stepSize = stepSize >> 1; + if ( delta >= stepSize ) encodedSample |= 1; + + return ( encodedSample ); +} + +long DecodeDelta( long stepSize, char encodedSample ) +{ + long delta = 0; + + if( encodedSample & 4) delta = stepSize; + if( encodedSample & 2) delta += (stepSize >> 1); + if( encodedSample & 1) delta += (stepSize >> 2); + delta += (stepSize >> 3); + if (encodedSample & 8) delta = -delta; + + return( delta ); +} + +char ADDVIEncode(short shortOne, short shortTwo, long channels) +{ + long delta; + unsigned char encodedSample, outputByte; + + outputByte = 0; + +/* First sample or left sample to be packed in first nibble */ +/* calculate delta */ + delta = shortOne - lastEstimateL; + CLIP(delta, -32768L, 32767L); + +/* encode delta relative to the current stepsize */ + encodedSample = EncodeDelta(stepSizeL, delta); + +/* pack first nibble */ + outputByte = 0x00F0 & (encodedSample<<4); + +/* decode ADPCM code value to reproduce delta and generate an estimated InputSample */ + lastEstimateL += DecodeDelta(stepSizeL, encodedSample); + CLIP(lastEstimateL, -32768L, 32767L); + +/* adapt stepsize */ + stepIndexL += gIndexDeltas[encodedSample]; + CLIP(stepIndexL, 0, 88); + stepSizeL = gStepSizes[stepIndexL]; + + if(channels == 2L) + { +/* calculate delta for second sample */ + delta = shortTwo - lastEstimateR; + CLIP(delta, -32768L, 32767L); + +/* encode delta relative to the current stepsize */ + encodedSample = EncodeDelta(stepSizeR, delta); + +/* pack second nibble */ + outputByte |= 0x000F & encodedSample; + +/* decode ADPCM code value to reproduce delta and generate an estimated InputSample */ + lastEstimateR += DecodeDelta(stepSizeR, encodedSample); + CLIP(lastEstimateR, -32768L, 32767L); + +/* adapt stepsize */ + stepIndexR += gIndexDeltas[encodedSample]; + CLIP(stepIndexR, 0, 88); + stepSizeR = gStepSizes[stepIndexR]; + } + else + { +/* calculate delta for second sample */ + delta = shortTwo - lastEstimateL; + CLIP(delta, -32768L, 32767L); + +/* encode delta relative to the current stepsize */ + encodedSample = EncodeDelta(stepSizeL, delta); + +/* pack second nibble */ + outputByte |= 0x000F & encodedSample; + +/* decode ADPCM code value to reproduce delta and generate an estimated InputSample */ + lastEstimateL += DecodeDelta(stepSizeL, encodedSample); + CLIP(lastEstimateL, -32768L, 32767L); + +/* adapt stepsize */ + stepIndexL += gIndexDeltas[encodedSample]; + CLIP(stepIndexL, 0, 88); + stepSizeL = gStepSizes[stepIndexL]; + } + return(outputByte); +} + +void BlockADDVIEncode(uint8 *buffer, short *samples, long numSamples, long channels) +{ + long i, j; + + lastEstimateL = lastEstimateR = 0L; + stepSizeL = stepSizeR = 7L; + stepIndexL = stepIndexR = 0L; + + for(i = j = 0; i < numSamples; i += 2, j++) + { + buffer[j] = ADDVIEncode(samples[i + 0], samples[i + 1], channels); + } +} + +#endif \ No newline at end of file diff --git a/src/platform/gba/packer/TR1_PC.h b/src/platform/gba/packer/TR1_PC.h new file mode 100644 index 00000000..bb63214a --- /dev/null +++ b/src/platform/gba/packer/TR1_PC.h @@ -0,0 +1,1053 @@ +#ifndef H_TR1_PC +#define H_TR1_PC + +#include "common.h" + +#pragma pack(1) +struct TR1_PC +{ + struct Tile + { + uint8 indices[256 * 256]; + }; + + struct Palette + { + uint8 colors[256 * 3]; + }; + + struct Quad + { + uint16 indices[4]; + uint16 flags; + }; + + struct Triangle + { + uint16 indices[3]; + uint16 flags; + }; + + struct Room + { + struct Info + { + int32 x; + int32 z; + int32 yBottom; + int32 yTop; + int32 dataSize; + }; + + struct Vertex + { + vec3s pos; + int16 lighting; + }; + + struct Sprite + { + uint16 index; + uint16 texture; + }; + + struct Portal + { + int16 roomIndex; + vec3s normal; + vec3s vertices[4]; + + void write(FileStream &f) const + { + f.write(roomIndex); + f.write(normal.x); + f.write(normal.y); + f.write(normal.z); + for (int32 i = 0; i < 4; i++) + { + f.write(vertices[i].x); + f.write(vertices[i].y); + f.write(vertices[i].z); + } + } + }; + + struct Sector + { + uint16 floorIndex; + uint16 boxIndex; + uint8 roomBelow; + int8 floor; + uint8 roomAbove; + int8 ceiling; + + void write(FileStream &f) const + { + f.write(floorIndex); + f.write(boxIndex); + f.write(roomBelow); + f.write(floor); + f.write(roomAbove); + f.write(ceiling); + } + }; + + struct Light + { + vec3i pos; + uint16 intensity; + int32 radius; + }; + + struct Mesh + { + vec3i pos; + int16 angleY; + uint16 intensity; + uint16 id; + }; + + Info info; + + int16 vCount; + Vertex* vertices; + + int16 qCount; + Quad* quads; + + int16 tCount; + Triangle* triangles; + + int16 sCount; + Sprite* sprites; + + int16 pCount; + Portal* portals; + + uint16 zSectors; + uint16 xSectors; + Sector* sectors; + + uint16 ambient; + + uint16 lCount; + Light* lights; + + uint16 mCount; + Mesh* meshes; + + int16 alternateRoom; + uint16 flags; + }; + + struct FloorData + { + uint16 value; + + void write(FileStream &f) const + { + f.write(value); + } + }; + + struct Animation + { + uint32 frameOffset; + + uint8 frameRate; + uint8 frameSize; + uint16 state; + + uint32 speed; + uint32 accel; + + uint16 frameBegin; + uint16 frameEnd; + + uint16 nextAnimIndex; + uint16 nextFrameIndex; + + uint16 statesCount; + uint16 statesStart; + + uint16 commandsCount; + uint16 commandsStart; + + void write(FileStream &f) const + { + f.write(frameOffset); + f.write(frameRate); + f.write(frameSize); + f.write(state); + f.write(speed); + f.write(accel); + f.write(frameBegin); + f.write(frameEnd); + f.write(nextAnimIndex); + f.write(nextFrameIndex); + f.write(statesCount); + f.write(statesStart); + f.write(commandsCount); + f.write(commandsStart); + } + }; + + struct AnimState + { + uint16 state; + uint16 rangesCount; + uint16 rangesStart; + }; + + struct AnimRange + { + int16 frameBegin; + int16 frameEnd; + int16 nextAnimIndex; + int16 nextFrameIndex; + + void write(FileStream &f) const + { + f.write(frameBegin); + f.write(frameEnd); + f.write(nextAnimIndex); + f.write(nextFrameIndex); + } + }; + + struct Model + { + uint32 type; + uint16 count; + uint16 start; + uint32 nodeIndex; + uint32 frameIndex; + uint16 animIndex; + }; + + struct MinMax + { + int16 minX, maxX; + int16 minY, maxY; + int16 minZ, maxZ; + }; + + struct StaticMesh + { + uint32 id; + uint16 meshIndex; + MinMax vbox; + MinMax cbox; + uint16 flags; + }; + + struct ObjectTexture + { + uint16 attribute; + uint16 tile; + union { + struct { uint8 xh0, x0, yh0, y0; }; + uint32 uv0; + }; + + union { + struct { uint8 xh1, x1, yh1, y1; }; + uint32 uv1; + }; + + union { + struct { uint8 xh2, x2, yh2, y2; }; + uint32 uv2; + }; + + union { + struct { uint8 xh3, x3, yh3, y3; }; + uint32 isQuad; + uint32 uv3; + }; + }; + + struct SpriteTexture + { + uint16 tile; + uint8 u, v; + uint16 w, h; + int16 l, t, r, b; + }; + + struct SpriteSequence + { + uint16 type; + uint16 unused; + int16 count; + int16 start; + + void write(FileStream &f) const + { + f.write(type); + f.write(unused); + f.write(count); + f.write(start); + } + }; + + struct Camera + { + vec3i pos; + int16 roomIndex; + uint16 flags; + + void write(FileStream &f) const + { + f.write(pos.x); + f.write(pos.y); + f.write(pos.z); + f.write(roomIndex); + f.write(flags); + } + }; + + struct SoundSource + { + vec3i pos; + uint16 id; + uint16 flags; + + void write(FileStream &f) const + { + f.write(pos.x); + f.write(pos.y); + f.write(pos.z); + f.write(id); + f.write(flags); + } + }; + + struct Box + { + int32 minZ, maxZ; + int32 minX, maxX; + int16 floor; + int16 overlap; + }; + + struct Zone + { + uint16* ground1; + uint16* ground2; + uint16* fly; + }; + + struct Item + { + uint16 type; + int16 roomIndex; + vec3i pos; + int16 angleY; + int16 intensity; + uint16 flags; + }; + + struct CameraFrame + { + vec3s target; + vec3s pos; + int16 fov; + int16 roll; + + void write(FileStream &f) const + { + f.write(target.x); + f.write(target.y); + f.write(target.z); + f.write(pos.x); + f.write(pos.y); + f.write(pos.z); + f.write(fov); + f.write(roll); + } + }; + + struct SoundInfo + { + uint16 index; + uint16 volume; + uint16 chance; + + union { + struct { + uint16 mode:2, count:4, unused:6, camera:1, pitch:1, gain:1, :1; + }; + + uint16 value; + } flags; + + void write(FileStream &f) const + { + f.write(index); + f.write(volume); + f.write(chance); + f.write(flags.value); + } + }; + + enum NodeFlag + { + NODE_FLAG_POP = (1 << 0), + NODE_FLAG_PUSH = (1 << 1), + NODE_FLAG_ROTX = (1 << 2), + NODE_FLAG_ROTY = (1 << 3), + NODE_FLAG_ROTZ = (1 << 4), + }; + + struct Node + { + uint32 flags; + vec3i pos; + }; + + LevelID id; + + int32 tilesCount; + Tile* tiles; + + int16 roomsCount; + Room* rooms; + + int32 floorsCount; + FloorData* floors; + + int32 meshDataSize; + uint16* meshData; + + int32 meshOffsetsCount; + uint32* meshOffsets; + + int32 animsCount; + Animation* anims; + + int32 statesCount; + AnimState* states; + + int32 rangesCount; + AnimRange* ranges; + + int32 commandsCount; + int16* commands; + + int32 nodesDataSize; + uint32* nodesData; + + int32 frameDataSize; + uint16* frameData; + + int32 modelsCount; + Model* models; + + int32 staticMeshesCount; + StaticMesh* staticMeshes; + + int32 objectTexturesCount; + ObjectTexture* objectTextures; + + int32 spriteTexturesCount; + SpriteTexture* spriteTextures; + + int32 spriteSequencesCount; + SpriteSequence* spriteSequences; + + int32 camerasCount; + Camera* cameras; + + int32 soundSourcesCount; + SoundSource* soundSources; + + int32 boxesCount; + Box* boxes; + + int32 overlapsCount; + uint16* overlaps; + + Zone zones[2]; + + int32 animTexDataSize; + uint16* animTexData; + + int32 itemsCount; + Item* items; + + uint8 lightmap[32 * 256]; + Palette palette; + + uint16 cameraFramesCount; + CameraFrame* cameraFrames; + + uint16 demoDataSize; + uint8* demoData; + + int16 soundMap[256]; + int32 soundInfoCount; + SoundInfo* soundInfo; + + int32 soundDataSize; + uint8* soundData; + + int32 soundOffsetsCount; + uint32* soundOffsets; + + TR1_PC(FileStream &f, LevelID id) + { + this->id = id; + tiles = NULL; + + uint32 magic; + f.read(magic); + + if (magic != 0x00000020) + { + printf("Unsupported level format\n"); + return; + } + + f.readArray(tiles, tilesCount); + f.seek(4); + + f.read(roomsCount); + rooms = new Room[roomsCount]; + + for (int32 i = 0; i < roomsCount; i++) + { + Room* room = rooms + i; + + f.read(room->info); + f.readArray(room->vertices, room->vCount); + f.readArray(room->quads, room->qCount); + f.readArray(room->triangles, room->tCount); + f.readArray(room->sprites, room->sCount); + f.readArray(room->portals, room->pCount); + f.read(room->zSectors); + f.read(room->xSectors); + f.read(room->sectors, room->zSectors * room->xSectors); + f.read(room->ambient); + f.readArray(room->lights, room->lCount); + f.readArray(room->meshes, room->mCount); + f.read(room->alternateRoom); + f.read(room->flags); + } + + f.readArray(floors, floorsCount); + + f.readArray(meshData, meshDataSize); + f.readArray(meshOffsets, meshOffsetsCount); + f.readArray(anims, animsCount); + f.readArray(states, statesCount); + f.readArray(ranges, rangesCount); + f.readArray(commands, commandsCount); + f.readArray(nodesData, nodesDataSize); + f.readArray(frameData, frameDataSize); + f.readArray(models, modelsCount); + f.readArray(staticMeshes, staticMeshesCount); + f.readArray(objectTextures, objectTexturesCount); + f.readArray(spriteTextures, spriteTexturesCount); + f.readArray(spriteSequences, spriteSequencesCount); + + f.readArray(cameras, camerasCount); + f.readArray(soundSources, soundSourcesCount); + f.readArray(boxes, boxesCount); + f.readArray(overlaps, overlapsCount); + + for (int32 i = 0; i < 2; i++) + { + f.read(zones[i].ground1, boxesCount); + f.read(zones[i].ground2, boxesCount); + f.read(zones[i].fly, boxesCount); + } + + f.readArray(animTexData, animTexDataSize); + f.readArray(items, itemsCount); + f.read(lightmap); + f.read(palette); + f.readArray(cameraFrames, cameraFramesCount); + f.readArray(demoData, demoDataSize); + + f.read(soundMap); + f.readArray(soundInfo, soundInfoCount); + f.readArray(soundData, soundDataSize); + f.readArray(soundOffsets, soundOffsetsCount); + + palette.colors[0] = 0; + palette.colors[1] = 0; + palette.colors[2] = 0; + } + + ~TR1_PC() + { + delete[] tiles; + + for (int32 i = 0; i < roomsCount; i++) + { + Room* room = rooms + i; + delete[] room->vertices; + delete[] room->quads; + delete[] room->triangles; + delete[] room->sprites; + delete[] room->portals; + delete[] room->sectors; + delete[] room->lights; + delete[] room->meshes; + } + + delete[] rooms; + delete[] floors; + delete[] meshData; + delete[] meshOffsets; + delete[] anims; + delete[] states; + delete[] ranges; + delete[] commands; + delete[] nodesData; + delete[] frameData; + delete[] models; + delete[] staticMeshes; + delete[] objectTextures; + delete[] spriteTextures; + delete[] spriteSequences; + delete[] cameras; + delete[] soundSources; + delete[] boxes; + delete[] overlaps; + + for (int32 i = 0; i < 2; i++) + { + delete[] zones[i].ground1; + delete[] zones[i].ground2; + delete[] zones[i].fly; + } + + delete[] animTexData; + delete[] items; + delete[] cameraFrames; + delete[] demoData; + delete[] soundInfo; + delete[] soundData; + delete[] soundOffsets; + } + + void fixHeadMask() + { + #define SET_ROT(joint, mask) (((Node*)nodesData)[models[i].nodeIndex / 4 + joint]).flags |= mask; + + for (int32 i = 0; i < modelsCount; i++) + { + switch (models[i].type) + { + case ITEM_WOLF : SET_ROT(2, NODE_FLAG_ROTY); break; + case ITEM_BEAR : SET_ROT(13, NODE_FLAG_ROTY); break; + //case ITEM_BAT : break; + case ITEM_CROCODILE_LAND : SET_ROT(7, NODE_FLAG_ROTY); break; + case ITEM_CROCODILE_WATER : SET_ROT(7, NODE_FLAG_ROTY); break; + case ITEM_LION_MALE : SET_ROT(19, NODE_FLAG_ROTY); break; + case ITEM_LION_FEMALE : SET_ROT(19, NODE_FLAG_ROTY); break; + case ITEM_PUMA : SET_ROT(19, NODE_FLAG_ROTY); break; + case ITEM_GORILLA : SET_ROT(13, NODE_FLAG_ROTY); break; + case ITEM_RAT_LAND : SET_ROT(1, NODE_FLAG_ROTY); break; + case ITEM_RAT_WATER : SET_ROT(1, NODE_FLAG_ROTY); break; + case ITEM_REX : SET_ROT(10, NODE_FLAG_ROTY); SET_ROT(11, NODE_FLAG_ROTY); break; + case ITEM_RAPTOR : SET_ROT(21, NODE_FLAG_ROTY); break; + case ITEM_MUTANT_1 : SET_ROT(0, NODE_FLAG_ROTY); SET_ROT(2, NODE_FLAG_ROTY); break; + case ITEM_MUTANT_2 : SET_ROT(0, NODE_FLAG_ROTY); SET_ROT(2, NODE_FLAG_ROTY); break; + case ITEM_MUTANT_3 : SET_ROT(0, NODE_FLAG_ROTY); SET_ROT(2, NODE_FLAG_ROTY); break; + case ITEM_CENTAUR : SET_ROT(10, NODE_FLAG_ROTX | NODE_FLAG_ROTY); break; + case ITEM_MUMMY : SET_ROT(2, NODE_FLAG_ROTY); break; + case ITEM_LARSON : SET_ROT(6, NODE_FLAG_ROTY); break; + case ITEM_PIERRE : SET_ROT(6, NODE_FLAG_ROTY); break; + case ITEM_SKATER : SET_ROT(0, NODE_FLAG_ROTY); break; + case ITEM_COWBOY : SET_ROT(0, NODE_FLAG_ROTY); break; + case ITEM_MR_T : SET_ROT(0, NODE_FLAG_ROTY); break; + case ITEM_NATLA : SET_ROT(2, NODE_FLAG_ROTX | NODE_FLAG_ROTZ); break; + case ITEM_ADAM : SET_ROT(1, NODE_FLAG_ROTY); break; + default : break; + } + } + + #undef SET_ROT + } + + int32 getModelIndex(int32 type) + { + for (int32 i = 0; i < modelsCount; i++) + { + if (models[i].type == type) { + return i; + } + } + return -1; + } + + template + T* addElements(T* &a, int32 &count, int32 size) + { + T* ptr = new T[count + size]; + memcpy(ptr, a, sizeof(a[0]) * count); + delete[] a; + a = ptr; + count += size; + return &a[count - size]; + } + + int32 getMeshTexture(uint16* meshPtr) + { + meshPtr += 3 + 1 + 1; // skip center, radius, flags + int16 vCount = *(int16*)meshPtr; + meshPtr += 1; // skip vCount + meshPtr += vCount * 3; // skip vertices + int16 nCount = *(int16*)meshPtr; + meshPtr += 1; // skip nCount + if (nCount > 0) { + meshPtr += nCount * 3; // skip normals + } else { + meshPtr -= nCount; // skip intensity + } + int16 rCount = *(int16*)meshPtr; + meshPtr += 1; // skip rCount + if (rCount > 0) { + meshPtr += 4; // skip indices + return (*(uint16*)meshPtr) & 0x07FF; + } + int16 tCount = *(int16*)meshPtr; + meshPtr += 1; // skip tCount + if (tCount > 0) + { + meshPtr += 3; // skip indices + return (*(uint16*)meshPtr) & 0x07FF; + } + // no textured quads or triangles + ASSERT(false); + return -1; + } + + int32 getMaxTexture(int32 tile, int32 x, int32 y, int32 &minU, int32 &minV, int32 &maxU, int32 &maxV) + { + int32 index = -1; + int32 maxW = 0; + int32 maxH = 0; + + for (int32 i = 0; i < objectTexturesCount; i++) + { + ObjectTexture *tex = objectTextures + i; + + //if (!tex->isQuad) + // continue; + + if (tex->tile != tile) + continue; + + int32 minX = MIN(MIN(tex->x0, tex->x1), tex->x2); + int32 minY = MIN(MIN(tex->y0, tex->y1), tex->y2); + int32 maxX = MAX(MAX(tex->x0, tex->x1), tex->x2); + int32 maxY = MAX(MAX(tex->y0, tex->y1), tex->y2); + + if (tex->isQuad) + { + minX = MIN(minX, tex->x3); + minY = MIN(minY, tex->y3); + maxX = MAX(maxX, tex->x3); + maxY = MAX(maxY, tex->y3); + } + + if (x >= minX && x <= maxX && y >= minY && y <= maxY) + { + int32 w = maxX - minX; + int32 h = maxY - minY; + + if (w >= maxW && h >= maxH) + { + index = i; + maxW = w; + maxH = h; + + minU = minX; + minV = minY; + maxU = maxX; + maxV = maxY; + } + } + } + + ASSERT(index >= 0); + + return index; + } + + void generateLODs() + { + struct Quad { + uint16 indices[4]; + uint16 flags; + }; + + struct Mesh { + vec3s center; + int16 radius; + uint16 flags; + int16 vCount; + vec3s vertices[4]; + int16 nCount; + int16 intensity[4]; + int16 rCount; + Quad rFaces[2]; + int16 tCount; + int16 crCount; + int16 ctCount; + }; + + struct AnimFrame { + MinMax box; + vec3s pos; + uint16 angles[2]; + }; + + AnimFrame meshPlaneFrame; + meshPlaneFrame.box.minX = -512; + meshPlaneFrame.box.maxX = 512; + meshPlaneFrame.box.minY = -512; + meshPlaneFrame.box.maxY = -512; + meshPlaneFrame.box.minZ = -512; + meshPlaneFrame.box.maxZ = 512; + + meshPlaneFrame.pos = vec3s(0, -512, 0); + meshPlaneFrame.angles[0] = meshPlaneFrame.angles[1] = 0; + + Mesh meshPlane; + meshPlane.center = vec3s(-512, 0, 512); + meshPlane.radius = 727; + meshPlane.flags = 1; + meshPlane.vCount = 4; + meshPlane.vertices[0] = vec3s(-512, 0, -512); + meshPlane.vertices[1] = vec3s( 512, 0, -512); + meshPlane.vertices[2] = vec3s( 512, 0, 512); + meshPlane.vertices[3] = vec3s(-512, 0, 512); + meshPlane.nCount = -4; + meshPlane.intensity[0] = 3800; + meshPlane.intensity[1] = 3800; + meshPlane.intensity[2] = 3800; + meshPlane.intensity[3] = 3800; + meshPlane.rCount = 2; + meshPlane.rFaces[0].indices[0] = 3; + meshPlane.rFaces[0].indices[1] = 2; + meshPlane.rFaces[0].indices[2] = 1; + meshPlane.rFaces[0].indices[3] = 0; + meshPlane.rFaces[1].indices[0] = 0; + meshPlane.rFaces[1].indices[1] = 1; + meshPlane.rFaces[1].indices[2] = 2; + meshPlane.rFaces[1].indices[3] = 3; + meshPlane.tCount = 0; + meshPlane.crCount = 0; + meshPlane.ctCount = 0; + + // trap floor lod + int32 index = getModelIndex(ITEM_TRAP_FLOOR); + + if (index > -1) + { + Model* model = addElements(models, modelsCount, 1); + *model = models[index]; + model->type = ITEM_TRAP_FLOOR_LOD; + + int32 texture = getMeshTexture((uint16*)((uint8*)meshData + meshOffsets[model->start])); + ObjectTexture* objTex = objectTextures + texture; + + int32 minU, minV, maxU, maxV; + + texture = getMaxTexture(objTex->tile, (objTex->x0 + objTex->x1 + objTex->x2) / 3, (objTex->y0 + objTex->y1 + objTex->y2) / 3, minU, minV, maxU, maxV); + + objTex = addElements(objectTextures, objectTexturesCount, 1); + *objTex = objectTextures[texture]; + objTex->x0 = minU; + objTex->y0 = minV; + objTex->x1 = maxU; + objTex->y1 = minV; + objTex->x2 = maxU; + objTex->y2 = maxV; + objTex->x3 = minU; + objTex->y3 = maxV; + + meshPlane.rFaces[0].flags = objectTexturesCount - 1; + meshPlane.rFaces[1].flags = objectTexturesCount - 1; + + uint32 *meshOffset = addElements(meshOffsets, meshOffsetsCount, 1); + + uint16* mesh = addElements(meshData, meshDataSize, sizeof(meshPlane) / sizeof(uint16)); + memcpy(mesh, &meshPlane, sizeof(meshPlane)); + + *meshOffset = uint32((mesh - meshData) * sizeof(uint16)); + + uint16* frame = addElements(frameData, frameDataSize, sizeof(meshPlaneFrame) / sizeof(uint16)); + memcpy(frame, &meshPlaneFrame, sizeof(meshPlaneFrame)); + + Animation* anim = addElements(anims, animsCount, 1); + memset(anim, 0, sizeof(anim[0])); + anim->frameRate = 1; + anim->frameOffset = uint32((frame - frameData) << 1); + + Node* node = (Node*)addElements(nodesData, nodesDataSize, sizeof(Node) / sizeof(uint32)); + node->flags = 0; + node->pos.x = 0; + node->pos.y = 0; + node->pos.z = 0; + + model->count = 1; + model->start = meshOffsetsCount - 1; + model->animIndex = animsCount - 1; + model->nodeIndex = uint32((uint32*)node - nodesData); + } + } + + void hideRoom(int32 roomIndex) + { + Room &room = rooms[roomIndex]; + room.vCount = 0; + room.qCount = 0; + room.tCount = 0; + room.sCount = 0; + room.pCount = 0; + room.lCount = 0; + room.mCount = 0; + room.zSectors = 0; + room.xSectors = 0; + room.alternateRoom = -1; + + for (int32 i = 0; i < roomsCount; i++) + { + Room &room = rooms[i]; + + int32 j = room.pCount - 1; + while (j >= 0) + { + if (room.portals[j].roomIndex == roomIndex) + { + room.pCount--; + room.portals[j] = room.portals[room.pCount]; + } + j--; + } + } + } + + void removeSound(int32 id) + { + if (soundMap[id] == -1) + return; + + SoundInfo info = soundInfo[soundMap[id]]; + + for (int32 index = info.index; index < info.index + info.flags.count; index++) + { + int32 offset = soundOffsets[index]; + + int32 size; + if (index == soundOffsetsCount - 1) { + size = soundDataSize - offset; + } else { + size = soundOffsets[index + 1] - offset; + } + + for (int i = index + 1; i < soundOffsetsCount; i++) + { + soundOffsets[i - 1] = soundOffsets[i] - size; + } + soundOffsetsCount--; + + for (int32 i = 0; i < soundInfoCount; i++) + { + if (soundInfo[i].index >= index) + { + soundInfo[i].index--; + } + } + + uint8* data = new uint8[soundDataSize - size]; + memcpy(data, soundData, offset); + memcpy(data + offset, soundData + offset + size, soundDataSize - offset - size); + + delete[] soundData; + soundData = data; + soundDataSize -= size; + } + + soundMap[id] = -1; + } + + void cutData() + { + removeSound(60); // underwater + removeSound(173); // secret + + if (id == LVL_TR1_GYM) + { + hideRoom(0); + hideRoom(1); + hideRoom(2); + hideRoom(3); + hideRoom(4); + hideRoom(5); + hideRoom(6); + hideRoom(15); + hideRoom(16); + hideRoom(17); + hideRoom(18); + + for (int32 i = 174; i <= 204; i++) // remove tutorial sounds (we use sound tracks instead) + removeSound(i); + + // disable transparency + objectTextures[93].attribute = + objectTextures[167].attribute = + objectTextures[175].attribute = + objectTextures[190].attribute = + objectTextures[191].attribute = + objectTextures[211].attribute = + objectTextures[220].attribute = + objectTextures[221].attribute = + objectTextures[580].attribute = + objectTextures[581].attribute = 0; + } + + if (id == LVL_TR1_1) + { + objectTextures[271].attribute = + objectTextures[272].attribute = + objectTextures[331].attribute = + objectTextures[333].attribute = + objectTextures[334].attribute = + objectTextures[335].attribute = + objectTextures[517].attribute = + objectTextures[518].attribute = + objectTextures[569].attribute = + objectTextures[571].attribute = + objectTextures[685].attribute = + objectTextures[686].attribute = 0; + } + + if (id == LVL_TR1_2) + { + objectTextures[247].attribute = + objectTextures[248].attribute = + objectTextures[307].attribute = + objectTextures[309].attribute = + objectTextures[310].attribute = + objectTextures[311].attribute = + objectTextures[547].attribute = + objectTextures[661].attribute = + objectTextures[662].attribute = + objectTextures[688].attribute = + objectTextures[905].attribute = + objectTextures[906].attribute = + objectTextures[923].attribute = 0; + } + + // TODO remove unused textures & models + } +}; + +#endif \ No newline at end of file diff --git a/src/platform/gba/packer/TR1_PSX.h b/src/platform/gba/packer/TR1_PSX.h new file mode 100644 index 00000000..a5089764 --- /dev/null +++ b/src/platform/gba/packer/TR1_PSX.h @@ -0,0 +1,9 @@ +#ifndef H_TR1_PSX +#define H_TR1_PSX + +struct TR1_PSX +{ + // +}; + +#endif \ No newline at end of file diff --git a/src/platform/gba/packer/common.h b/src/platform/gba/packer/common.h new file mode 100644 index 00000000..c9222190 --- /dev/null +++ b/src/platform/gba/packer/common.h @@ -0,0 +1,705 @@ +#ifndef H_COMMON +#define H_COMMON + +#include +#include +#include +#include +#include + +#include "libimagequant.h" + +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include "stb_image_resize.h" + +#define ASSERT(x) { if (!(x)) { DebugBreak(); } } + +typedef signed char int8; +typedef signed short int16; +typedef signed int int32; +typedef signed long long int64; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned long long uint64; + +inline uint16 swap16(uint16 x) { + return ((x & 0x00FF) << 8) | ((x & 0xFF00) >> 8); +} + +inline uint32 swap32(uint32 x) { + return ((x & 0x000000FF) << 24) | ((x & 0x0000FF00) << 8) | ((x & 0x00FF0000) >> 8) | ((x & 0xFF000000) >> 24); +} + +template +inline void swap(T &a, T &b) { + T tmp = a; + a = b; + b = tmp; +} + +#define SQR(x) ((x) * (x)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define CLAMP(x, a, b) ((x) < (a) ? (a) : ((x) > (b) ? (b) : (x))) +#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) + +struct FileStream +{ + FILE* f; + + bool bigEndian; + + FileStream(const char* fileName, bool write) : bigEndian(false) + { + f = fopen(fileName, write ? "wb" : "rb"); + } + + ~FileStream() + { + if (f) fclose(f); + } + + bool isValid() + { + return f != NULL; + } + + void seek(int32 offset) + { + fseek(f, offset, SEEK_CUR); + } + + uint32 getPos() + { + return ftell(f); + } + + void setPos(uint32 pos) + { + fseek(f, pos, SEEK_SET); + } + + uint32 align4() + { + uint32 pos = getPos(); + uint32 aligned = (pos + 3) & ~3; + + if (aligned != pos) { + static const uint32 zero = 0; + fwrite(&zero, 1, aligned - pos, f); + } + + return aligned; + } + + uint32 align16() + { + uint32 pos = getPos(); + uint32 aligned = (pos + 15) & ~15; + + if (aligned != pos) { + static const uint32 zero = 0; + fwrite(&zero, 1, aligned - pos, f); + } + + return aligned; + } + + template + void read(T &result) + { + fread(&result, sizeof(result), 1, f); + } + + template + void read(T* &elements, C count) + { + if (count) { + elements = new T[count]; + fread(&elements[0], sizeof(elements[0]), count, f); + } else { + elements = NULL; + } + } + + template + void readArray(T* &elements, C &count) + { + read(count); + read(elements, count); + } + + void write(int8 value) + { + writeRaw(value); + } + + void write(uint8 value) + { + writeRaw(value); + } + + void write(int16 value) + { + if (bigEndian) { + value = (int16)swap16((uint16)value); + } + writeRaw(value); + } + + void write(uint16 value) + { + if (bigEndian) { + value = swap16(value); + } + writeRaw(value); + } + + void write(int32 value) + { + if (bigEndian) { + value = (int32)swap32((uint32)value); + } + writeRaw(value); + } + + void write(uint32 value) + { + if (bigEndian) { + value = swap32(value); + } + writeRaw(value); + } + + template + void write(const T* elements, C count) + { + if (!elements || !count) + return; + + for (int32 i = 0; i < int32(count); i++) + { + write(elements[i]); + } + } + + template + void writeObj(const T* elements, C count) + { + if (!elements || !count) + return; + + for (int32 i = 0; i < count; i++) + { + elements[i].write(*this); + } + } + + template + void writeRaw(const T &result) + { + fwrite(&result, sizeof(result), 1, f); + } + +private: + template + void writeArray(const T* elements, C count) + { + write(count); + write(elements, count); + } +}; + +template +struct Array +{ + int32 count; + T** items; + + Array() : count(0), items(NULL) {} + + ~Array() + { + delete[] items; + } + + T* operator [] (int index) const { return items[index]; } + + int32 add(T* item) + { + count++; + T** tmp = new T*[count]; + memcpy(tmp, items, (count - 1) * sizeof(T*)); + tmp[count - 1] = item; + delete[] items; + items = tmp; + return count - 1; + } + + int32 find(T* item) + { + for (int32 i = 0; i < count; i++) + { + if (items[i]->isEqual(item)) + return i; + } + return -1; + } + + void qsort(T** v, int L, int R) { + int i = L; + int j = R; + const T* m = v[(L + R) / 2]; + + while (i <= j) { + while (T::cmp(v[i], m) < 0) i++; + while (T::cmp(m, v[j]) < 0) j--; + + if (i <= j) + { + T* tmp = v[i]; + v[i] = v[j]; + v[j] = tmp; + i++; + j--; + } + } + + if (L < j) qsort(v, L, j); + if (i < R) qsort(v, i, R); + } + + void sort() { + if (count) { + qsort(items, 0, count - 1); + } + } +}; + +enum LevelID +{ + LVL_TR1_TITLE, + LVL_TR1_GYM, + LVL_TR1_1, + LVL_TR1_2, + LVL_TR1_3A, + LVL_TR1_3B, + LVL_TR1_CUT_1, + LVL_TR1_4, + LVL_TR1_5, + LVL_TR1_6, + LVL_TR1_7A, + LVL_TR1_7B, + LVL_TR1_CUT_2, + LVL_TR1_8A, + LVL_TR1_8B, + LVL_TR1_8C, + LVL_TR1_10A, + LVL_TR1_CUT_3, + LVL_TR1_10B, + LVL_TR1_CUT_4, + LVL_TR1_10C, + LVL_MAX +}; + +const char* levelNames[LVL_MAX] = { + "TITLE", + "GYM", + "LEVEL1", + "LEVEL2", + "LEVEL3A", + "LEVEL3B", + "CUT1", + "LEVEL4", + "LEVEL5", + "LEVEL6", + "LEVEL7A", + "LEVEL7B", + "CUT2", + "LEVEL8A", + "LEVEL8B", + "LEVEL8C", + "LEVEL10A", + "CUT3", + "LEVEL10B", + "CUT4", + "LEVEL10C" +}; + +#define ITEM_TYPES(E) \ + E( LARA ) \ + E( LARA_PISTOLS ) \ + E( LARA_SHOTGUN ) \ + E( LARA_MAGNUMS ) \ + E( LARA_UZIS ) \ + E( LARA_SPEC ) \ + E( DOPPELGANGER ) \ + E( WOLF ) \ + E( BEAR ) \ + E( BAT ) \ + E( CROCODILE_LAND ) \ + E( CROCODILE_WATER ) \ + E( LION_MALE ) \ + E( LION_FEMALE ) \ + E( PUMA ) \ + E( GORILLA ) \ + E( RAT_LAND ) \ + E( RAT_WATER ) \ + E( REX ) \ + E( RAPTOR ) \ + E( MUTANT_1 ) \ + E( MUTANT_2 ) \ + E( MUTANT_3 ) \ + E( CENTAUR ) \ + E( MUMMY ) \ + E( UNUSED_1 ) \ + E( UNUSED_2 ) \ + E( LARSON ) \ + E( PIERRE ) \ + E( SKATEBOARD ) \ + E( SKATER ) \ + E( COWBOY ) \ + E( MR_T ) \ + E( NATLA ) \ + E( ADAM ) \ + E( TRAP_FLOOR ) \ + E( TRAP_SWING_BLADE ) \ + E( TRAP_SPIKES ) \ + E( TRAP_BOULDER ) \ + E( DART ) \ + E( TRAP_DART_EMITTER ) \ + E( DRAWBRIDGE ) \ + E( TRAP_SLAM ) \ + E( TRAP_SWORD ) \ + E( HAMMER_HANDLE ) \ + E( HAMMER_BLOCK ) \ + E( LIGHTNING ) \ + E( MOVING_OBJECT ) \ + E( BLOCK_1 ) \ + E( BLOCK_2 ) \ + E( BLOCK_3 ) \ + E( BLOCK_4 ) \ + E( MOVING_BLOCK ) \ + E( TRAP_CEILING ) \ + E( TRAP_FLOOR_LOD ) \ + E( SWITCH ) \ + E( SWITCH_WATER ) \ + E( DOOR_1 ) \ + E( DOOR_2 ) \ + E( DOOR_3 ) \ + E( DOOR_4 ) \ + E( DOOR_5 ) \ + E( DOOR_6 ) \ + E( DOOR_7 ) \ + E( DOOR_8 ) \ + E( TRAP_DOOR_1 ) \ + E( TRAP_DOOR_2 ) \ + E( TRAP_DOOR_LOD ) \ + E( BRIDGE_FLAT ) \ + E( BRIDGE_TILT_1 ) \ + E( BRIDGE_TILT_2 ) \ + E( INV_PASSPORT ) \ + E( INV_COMPASS ) \ + E( INV_HOME ) \ + E( GEARS_1 ) \ + E( GEARS_2 ) \ + E( GEARS_3 ) \ + E( CUT_1 ) \ + E( CUT_2 ) \ + E( CUT_3 ) \ + E( CUT_4 ) \ + E( INV_PASSPORT_CLOSED ) \ + E( INV_MAP ) \ + E( CRYSTAL ) \ + E( PISTOLS ) \ + E( SHOTGUN ) \ + E( MAGNUMS ) \ + E( UZIS ) \ + E( AMMO_PISTOLS ) \ + E( AMMO_SHOTGUN ) \ + E( AMMO_MAGNUMS ) \ + E( AMMO_UZIS ) \ + E( EXPLOSIVE ) \ + E( MEDIKIT_SMALL ) \ + E( MEDIKIT_BIG ) \ + E( INV_DETAIL ) \ + E( INV_SOUND ) \ + E( INV_CONTROLS ) \ + E( INV_GAMMA ) \ + E( INV_PISTOLS ) \ + E( INV_SHOTGUN ) \ + E( INV_MAGNUMS ) \ + E( INV_UZIS ) \ + E( INV_AMMO_PISTOLS ) \ + E( INV_AMMO_SHOTGUN ) \ + E( INV_AMMO_MAGNUMS ) \ + E( INV_AMMO_UZIS ) \ + E( INV_EXPLOSIVE ) \ + E( INV_MEDIKIT_SMALL ) \ + E( INV_MEDIKIT_BIG ) \ + E( PUZZLE_1 ) \ + E( PUZZLE_2 ) \ + E( PUZZLE_3 ) \ + E( PUZZLE_4 ) \ + E( INV_PUZZLE_1 ) \ + E( INV_PUZZLE_2 ) \ + E( INV_PUZZLE_3 ) \ + E( INV_PUZZLE_4 ) \ + E( PUZZLEHOLE_1 ) \ + E( PUZZLEHOLE_2 ) \ + E( PUZZLEHOLE_3 ) \ + E( PUZZLEHOLE_4 ) \ + E( PUZZLEHOLE_DONE_1 ) \ + E( PUZZLEHOLE_DONE_2 ) \ + E( PUZZLEHOLE_DONE_3 ) \ + E( PUZZLEHOLE_DONE_4 ) \ + E( LEADBAR ) \ + E( INV_LEADBAR ) \ + E( MIDAS_HAND ) \ + E( KEY_ITEM_1 ) \ + E( KEY_ITEM_2 ) \ + E( KEY_ITEM_3 ) \ + E( KEY_ITEM_4 ) \ + E( INV_KEY_ITEM_1 ) \ + E( INV_KEY_ITEM_2 ) \ + E( INV_KEY_ITEM_3 ) \ + E( INV_KEY_ITEM_4 ) \ + E( KEYHOLE_1 ) \ + E( KEYHOLE_2 ) \ + E( KEYHOLE_3 ) \ + E( KEYHOLE_4 ) \ + E( UNUSED_5 ) \ + E( UNUSED_6 ) \ + E( SCION_PICKUP_QUALOPEC ) \ + E( SCION_PICKUP_DROP ) \ + E( SCION_TARGET ) \ + E( SCION_PICKUP_HOLDER ) \ + E( SCION_HOLDER ) \ + E( UNUSED_7 ) \ + E( UNUSED_8 ) \ + E( INV_SCION ) \ + E( EXPLOSION ) \ + E( UNUSED_9 ) \ + E( SPLASH ) \ + E( UNUSED_10 ) \ + E( BUBBLE ) \ + E( UNUSED_11 ) \ + E( UNUSED_12 ) \ + E( BLOOD ) \ + E( UNUSED_13 ) \ + E( SMOKE ) \ + E( CENTAUR_STATUE ) \ + E( CABIN ) \ + E( MUTANT_EGG_SMALL ) \ + E( RICOCHET ) \ + E( SPARKLES ) \ + E( MUZZLE_FLASH ) \ + E( UNUSED_14 ) \ + E( UNUSED_15 ) \ + E( VIEW_TARGET ) \ + E( WATERFALL ) \ + E( NATLA_BULLET ) \ + E( MUTANT_BULLET ) \ + E( CENTAUR_BULLET ) \ + E( UNUSED_16 ) \ + E( UNUSED_17 ) \ + E( LAVA_PARTICLE ) \ + E( LAVA_EMITTER ) \ + E( FLAME ) \ + E( FLAME_EMITTER ) \ + E( TRAP_LAVA ) \ + E( MUTANT_EGG_BIG ) \ + E( BOAT ) \ + E( EARTHQUAKE ) \ + E( UNUSED_18 ) \ + E( UNUSED_19 ) \ + E( UNUSED_20 ) \ + E( UNUSED_21 ) \ + E( UNUSED_22 ) \ + E( LARA_BRAID ) \ + E( GLYPHS ) + +#define DECL_ENUM(v) ITEM_##v, + +enum ItemType { + ITEM_TYPES(DECL_ENUM) + TR1_ITEM_MAX, + ITEM_MAX = TR1_ITEM_MAX +}; + +#undef DECL_ENUM + +#define MAX_TRACKS 256 +#define MAX_ROOMS 256 +#define MAX_MESHES 512 +#define MAX_MODELS 256 +#define MAX_ANIMS 512 +#define MAX_ROOM_VERTICES 0xFFFF +#define MAX_ROOM_QUADS 2048 +#define MAX_ROOM_TRIANGLES 128 +#define MAX_ROOM_SPRITES 64 +#define MAX_ROOM_PORTALS 16 +#define MAX_ROOM_SECTORS (20*20) +#define MAX_FLOORS (9 * 1024) +#define MAX_BOXES 1024 +#define MAX_OVERLAPS (6 * 1024) +#define MAX_ITEMS 240 +#define MAX_NODES 32 +#define MAX_TEXTURES 1536 * 2 + +#define TEX_ATTR_AKILL 0x0001 +#define FACE_TEXTURE 0x07FF + +struct vec3s +{ + int16 x, y, z; + + vec3s() {} + vec3s(int16 x, int16 y, int16 z) : x(x), y(y), z(z) {} +}; + +struct vec3i +{ + int32 x, y, z; +}; + +void launchApp(const char* cmdline) +{ + STARTUPINFOA si; + PROCESS_INFORMATION pi; + + memset(&si, 0, sizeof(si)); + memset(&pi, 0, sizeof(pi)); + si.cb = sizeof(si); + + CreateProcess( + NULL, + (char*)cmdline, + NULL, + NULL, + FALSE, + 0, + NULL, + NULL, + &si, + &pi + ); + + WaitForSingleObject(pi.hProcess, INFINITE); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); +} + +struct _BITMAPFILEHEADER { + uint32 bfSize; + uint16 bfReserved1; + uint16 bfReserved2; + uint32 bfOffBits; +}; + +struct _BITMAPINFOHEADER { + uint32 biSize; + uint32 biWidth; + uint32 biHeight; + uint16 biPlanes; + uint16 biBitCount; + uint32 biCompression; + uint32 biSizeImage; + uint32 biXPelsPerMeter; + uint32 biYPelsPerMeter; + uint32 biClrUsed; + uint32 biClrImportant; +}; + +void saveBitmap(const char* fileName, uint8* data, int32 width, int32 height, int32 bpp = 24) +{ + _BITMAPFILEHEADER fhdr; + _BITMAPINFOHEADER ihdr; + + memset(&fhdr, 0, sizeof(fhdr)); + memset(&ihdr, 0, sizeof(ihdr)); + + ihdr.biSize = sizeof(ihdr); + ihdr.biWidth = width; + ihdr.biHeight = height; + ihdr.biPlanes = 1; + ihdr.biBitCount = bpp; + ihdr.biSizeImage = width * height * bpp / 8; + + fhdr.bfOffBits = 2 + sizeof(fhdr) + ihdr.biSize; + fhdr.bfSize = fhdr.bfOffBits + ihdr.biSizeImage; + + FILE *f = fopen(fileName, "wb"); + if (f) { + uint16 type = 'B' + ('M' << 8); + fwrite(&type, sizeof(type), 1, f); + fwrite(&fhdr, sizeof(fhdr), 1, f); + fwrite(&ihdr, sizeof(ihdr), 1, f); + for (int32 i = 0; i < height; i++) + { + fwrite(data + (height - i - 1) * width * bpp / 8, bpp / 8, width, f); + } + fclose(f); + } +} + +uint8* loadBitmap(const char* fileName, int32* width, int32* height, int32* bpp) +{ + _BITMAPFILEHEADER fhdr; + _BITMAPINFOHEADER ihdr; + + FILE *f = fopen(fileName, "rb"); + if (!f) return NULL; + + uint16 type; + fread(&type, sizeof(type), 1, f); + fread(&fhdr, sizeof(fhdr), 1, f); + fread(&ihdr, sizeof(ihdr), 1, f); + + *width = ihdr.biWidth; + *height = ihdr.biHeight; + *bpp = ihdr.biBitCount; + + uint8* data = new uint8[ihdr.biWidth * ihdr.biHeight * ihdr.biBitCount / 8]; + data += ihdr.biWidth * ihdr.biHeight * ihdr.biBitCount / 8; + + for (uint32 i = 0; i < ihdr.biHeight; i++) + { + data -= ihdr.biWidth * ihdr.biBitCount / 8; + fread(data, ihdr.biWidth * ihdr.biBitCount / 8, 1, f); + } + + fclose(f); + + return data; +} + +void fixTexCoord(uint32 uv0, uint32 &uv1) +{ + int32 u0 = uv0 >> 24; + int32 u1 = uv1 >> 24; + int32 v0 = (uv0 >> 8) & 0xFF; + int32 v1 = (uv1 >> 8) & 0xFF; + + if (abs(u1 - u0) > 127) { + if (u1 > u0) { + u1 = u0 + 127; + } else { + u1 = u0 - 127; + } + } + + if (abs(v1 - v0) > 127) { + if (v1 > v0) { + v1 = v0 + 127; + } else { + v1 = v0 - 127; + } + } + + uv1 = (u1 << 24) | (v1 << 8); +} + +#endif diff --git a/src/platform/gba/packer/main.cpp b/src/platform/gba/packer/main.cpp new file mode 100644 index 00000000..8577a297 --- /dev/null +++ b/src/platform/gba/packer/main.cpp @@ -0,0 +1,57 @@ +#include "common.h" +#include "IMA.h" +#include "TR1_PC.h" +#include "TR1_PSX.h" +#include "out_GBA.h" +#include "out_3DO.h" + +TR1_PC* pc[LVL_MAX]; +TR1_PSX* psx[LVL_MAX]; + +int main(int argc, char** argv) +{ + if (argc < 3) { + printf("usage: packer.exe [gba|3do] directory\n"); + return 0; + } + + for (int32 i = 0; i < LVL_MAX; i++) + { + char fileName[64]; + + { // load PC level + sprintf(fileName, "TR1_PC/DATA/%s.PHD", levelNames[i]); + + FileStream f(fileName, false); + + if (f.isValid()) { + pc[i] = new TR1_PC(f, LevelID(i)); + pc[i]->generateLODs(); + pc[i]->cutData(); + } else { + printf("can't open \"%s\"", fileName); + } + } + } + + if (strcmp(argv[1], "gba") == 0) + { + out_GBA* out = new out_GBA(); + out->process(argv[2], pc, NULL); + delete out; + } + + if (strcmp(argv[1], "3do") == 0) + { + out_3DO* out = new out_3DO(); + out->process(argv[2], pc, NULL); + delete out; + } + + for (int32 i = 0; i < LVL_MAX; i++) + { + delete pc[i]; + } + + return 0; +} diff --git a/src/platform/gba/packer/out_3DO.h b/src/platform/gba/packer/out_3DO.h new file mode 100644 index 00000000..28255412 --- /dev/null +++ b/src/platform/gba/packer/out_3DO.h @@ -0,0 +1,3197 @@ +#ifndef H_OUT_3DO +#define H_OUT_3DO + +#include "common.h" +#include "TR1_PC.h" +#include "TR1_PSX.h" + +// TODO use PSX format as source +struct out_3DO +{ + void process(const char* dir, TR1_PC** pc, TR1_PSX** psx) + { + // + } +}; + + +#if 0 + +#pragma pack(1) +struct LevelPC +{ + struct RoomQuad3DO + { + uint16 indices[4]; + uint32 flags; + + void write(FileStream &f) const + { + f.write(flags); + f.write(indices[0]); + f.write(indices[1]); + f.write(indices[2]); + f.write(indices[3]); + } + }; + + struct MeshQuad3DO + { + uint8 indices[4]; + uint32 flags; + + void write(FileStream &f) const + { + f.write(flags); + f.write(indices[3]); + f.write(indices[2]); + f.write(indices[1]); + f.write(indices[0]); + } + }; + + struct Quad + { + uint16 indices[4]; + uint16 flags; + + void write(FileStream &f) const + { + f.write(indices[0]); + f.write(indices[1]); + f.write(indices[2]); + f.write(indices[3]); + f.write(flags); + } + }; + + struct RoomTriangle3DO + { + uint16 indices[3]; + uint16 _unused; + uint32 flags; + + void write(FileStream &f) const + { + f.write(flags); + f.write(indices[0]); + f.write(indices[1]); + f.write(indices[2]); + f.write(_unused); + } + }; + + struct MeshTriangle3DO + { + uint8 indices[3]; + uint8 _unused; + uint32 flags; + + void write(FileStream &f) const + { + f.write(flags); + uint8 unused = 0; + f.write(unused); + f.write(indices[2]); + f.write(indices[1]); + f.write(indices[0]); + } + }; + + struct Triangle + { + uint16 indices[3]; + uint16 flags; + + void write(FileStream &f) const + { + f.write(indices[0]); + f.write(indices[1]); + f.write(indices[2]); + f.write(flags); + } + }; + + struct Room + { + struct Info + { + int32 x; + int32 z; + int32 yBottom; + int32 yTop; + int32 dataSize; + }; + + struct Vertex + { + vec3s pos; + int16 lighting; + + bool isEqual(const Vertex *v) + { + return pos.x == v->pos.x && pos.y == v->pos.y && pos.z == v->pos.z && lighting == v->lighting; + } + }; + + struct VertexComp + { + int8 x, y, z; + uint8 g; + + void write(FileStream &f) const + { + f.write(x); + f.write(y); + f.write(z); + f.write(g); + } + }; + + struct Sprite + { + uint16 index; + uint16 texture; + }; + + struct SpriteComp + { + int16 x, y, z; + uint8 g; + uint8 index; + + void write(FileStream &f) const + { + f.write(x); + f.write(y); + f.write(z); + f.write(g); + f.write(index); + } + }; + /* + struct MeshComp { + vec3s center; + int16 radius; + + int16 vCount; + int16 nCount; + + int16 rCount; + int16 tCount; + + vec3s* vertices; + Quad* quads; + Triangle* triangles; + };*/ + + struct Portal + { + int16 roomIndex; + vec3s normal; + vec3s vertices[4]; + + void write(FileStream &f) const + { + f.write(roomIndex); + f.write(normal.x); + f.write(normal.y); + f.write(normal.z); + for (int32 i = 0; i < 4; i++) + { + f.write(vertices[i].x); + f.write(vertices[i].y); + f.write(vertices[i].z); + } + } + }; + + struct PortalComp + { + uint32 roomIndex; + uint32 normalMask; + vec3i vertices[4]; + + void write(FileStream &f) const + { + f.write(roomIndex); + f.write(normalMask); + for (int32 i = 0; i < 4; i++) + { + f.write(vertices[i].x); + f.write(vertices[i].y); + f.write(vertices[i].z); + } + } + }; + + struct Sector + { + uint16 floorIndex; + uint16 boxIndex; + uint8 roomBelow; + int8 floor; + uint8 roomAbove; + int8 ceiling; + + void write(FileStream &f) const + { + f.write(floorIndex); + f.write(boxIndex); + f.write(roomBelow); + f.write(floor); + f.write(roomAbove); + f.write(ceiling); + } + }; + + struct Light + { + vec3i pos; + uint16 intensity; + int32 radius; + }; + + struct LightComp + { + vec3s pos; + uint8 radius; + uint8 intensity; + + void write(FileStream &f) const + { + f.write(pos.x); + f.write(pos.y); + f.write(pos.z); + f.write(radius); + f.write(intensity); + } + }; + + struct Mesh + { + vec3i pos; + int16 angleY; + uint16 intensity; + uint16 id; + }; + + struct MeshComp + { + vec3s pos; + uint8 intensity; + uint8 flags; + + void write(FileStream &f) const + { + uint32 xy = (pos.x << 16) | uint16(pos.y); + uint32 zf = (pos.z << 16) | (intensity << 8) | flags; + f.write(xy); + f.write(zf); + } + }; + + Info info; + + int16 vCount; + Vertex* vertices; + + int16 qCount; + Quad* quads; + + int16 tCount; + Triangle* triangles; + + int16 sCount; + Sprite* sprites; + + int16 pCount; + Portal* portals; + + uint16 zSectors; + uint16 xSectors; + Sector* sectors; + + uint16 ambient; + + uint16 lCount; + Light* lights; + + uint16 mCount; + Mesh* meshes; + + int16 alternateRoom; + uint16 flags; + }; + + struct FloorData + { + uint16 value; + + void write(FileStream &f) const + { + f.write(value); + } + }; + + struct Animation + { + uint32 frameOffset; + + uint8 frameRate; + uint8 frameSize; + uint16 state; + + uint32 speed; + uint32 accel; + + uint16 frameBegin; + uint16 frameEnd; + + uint16 nextAnimIndex; + uint16 nextFrameIndex; + + uint16 statesCount; + uint16 statesStart; + + uint16 commandsCount; + uint16 commandsStart; + + void write(FileStream &f) const + { + f.write(frameOffset); + f.write(frameRate); + f.write(frameSize); + f.write(state); + f.write(speed); + f.write(accel); + f.write(frameBegin); + f.write(frameEnd); + f.write(nextAnimIndex); + f.write(nextFrameIndex); + f.write(statesCount); + f.write(statesStart); + f.write(commandsCount); + f.write(commandsStart); + } + }; + + struct AnimState + { + uint16 state; + uint16 rangesCount; + uint16 rangesStart; + }; + + struct AnimStateComp + { + uint8 state; + uint8 rangesCount; + uint16 rangesStart; + + void write(FileStream &f) const + { + f.write(state); + f.write(rangesCount); + f.write(rangesStart); + } + }; + + struct AnimRange + { + int16 frameBegin; + int16 frameEnd; + int16 nextAnimIndex; + int16 nextFrameIndex; + + void write(FileStream &f) const + { + f.write(frameBegin); + f.write(frameEnd); + f.write(nextAnimIndex); + f.write(nextFrameIndex); + } + }; + + struct Model + { + uint32 type; + uint16 count; + uint16 start; + uint32 nodeIndex; + uint32 frameIndex; + uint16 animIndex; + }; + + struct ModelComp + { + uint8 type; + uint8 count; + uint16 start; + uint16 nodeIndex; + uint16 animIndex; + + void write(FileStream &f) const + { + f.write(type); + f.write(count); + f.write(start); + f.write(nodeIndex); + f.write(animIndex); + } + }; + + struct MinMax + { + int16 minX, maxX; + int16 minY, maxY; + int16 minZ, maxZ; + + void write(FileStream &f) const + { + f.write(minX); f.write(maxX); + f.write(minY); f.write(maxY); + f.write(minZ); f.write(maxZ); + } + }; + + struct Sphere16 + { + int16 x, y, z, radius; + + void write(FileStream &f) const + { + uint32 xy = (x << 16) | uint16(y); + uint32 zr = (z << 16) | uint16(radius); + f.write(xy); + f.write(zr); + } + }; + + struct StaticMesh + { + uint32 id; + uint16 meshIndex; + MinMax vbox; + MinMax cbox; + uint16 flags; + }; + + struct StaticMeshComp + { + uint16 id; + uint16 meshIndex; + uint32 flags; + //Sphere16 vs; + MinMax vbox; + MinMax cbox; + + void write(FileStream &f) const + { + Sphere16 vs; + vs.x = (vbox.maxX + vbox.minX) >> 1; + vs.y = (vbox.maxY + vbox.minY) >> 1; + vs.z = (vbox.maxZ + vbox.minZ) >> 1; + + int32 dx = (vbox.maxX - vbox.minX) >> 1; + int32 dy = (vbox.maxY - vbox.minY) >> 1; + int32 dz = (vbox.maxZ - vbox.minZ) >> 1; + + vs.radius = int32(sqrtf(float(dx * dx + dy * dy + dz * dz))); + + f.write(id); + f.write(meshIndex); + f.write(flags); + vs.write(f); + vbox.write(f); + cbox.write(f); + } + }; + + struct ObjectTexture + { + uint16 attribute; + uint16 tile; + union { + struct { uint8 xh0, x0, yh0, y0; }; + uint32 uv0; + }; + + union { + struct { uint8 xh1, x1, yh1, y1; }; + uint32 uv1; + }; + + union { + struct { uint8 xh2, x2, yh2, y2; }; + uint32 uv2; + }; + + union { + struct { uint8 xh3, x3, yh3, y3; }; + uint32 isQuad; + uint32 uv3; + }; + }; + + struct ObjectTextureComp + { + uint16 attribute; + uint16 tile; + uint32 uv0; + uint32 uv1; + uint32 uv2; + uint32 uv3; + + void write(FileStream &f) const + { + f.write(attribute); + f.write(tile); + f.write(uv0); + f.write(uv1); + f.write(uv2); + f.write(uv3); +/* + union TexCoord { + struct { uint16 v, u; }; + uint32 uv; + } t; + + + t.uv = uv0; + f.write(t.v); + f.write(t.u); + + t.uv = uv1; + f.write(t.v); + f.write(t.u); + + t.uv = uv2; + f.write(t.v); + f.write(t.u); + + t.uv = uv3; + f.write(t.v); + f.write(t.u); +*/ + } + }; + + struct SpriteTexture + { + uint16 tile; + uint8 u, v; + uint16 w, h; + int16 l, t, r, b; + + void write(FileStream &f) const + { + f.write(tile); + f.write(u); + f.write(v); + f.write(w); + f.write(h); + f.write(l); + f.write(t); + f.write(r); + f.write(b); + } + }; + + struct SpriteTextureComp + { + uint16 tile; + uint8 u, v; + uint8 w, h; + int16 l, t, r, b; + + void write(FileStream &f) const + { + f.write(tile); + f.write(u); + f.write(v); + f.write(w); + f.write(h); + f.write(l); + f.write(t); + f.write(r); + f.write(b); + } + }; + + struct SpriteTexture3DO + { + uint32 texture; + int32 l, t, r, b; + + void write(FileStream &f) const + { + f.write(texture); + f.write(l); + f.write(t); + f.write(r); + f.write(b); + } + }; + + struct SpriteSequence + { + uint16 type; + uint16 unused; + int16 count; + int16 start; + + void write(FileStream &f) const + { + f.write(type); + f.write(unused); + f.write(count); + f.write(start); + } + }; + + struct Camera + { + vec3i pos; + int16 roomIndex; + uint16 flags; + + void write(FileStream &f) const + { + f.write(pos.x); + f.write(pos.y); + f.write(pos.z); + f.write(roomIndex); + f.write(flags); + } + }; + + struct SoundSource + { + vec3i pos; + uint16 id; + uint16 flags; + + void write(FileStream &f) const + { + f.write(pos.x); + f.write(pos.y); + f.write(pos.z); + f.write(id); + f.write(flags); + } + }; + + struct Box + { + int32 minZ, maxZ; + int32 minX, maxX; + int16 floor; + int16 overlap; + }; + + struct BoxComp + { + uint8 minZ, maxZ; + uint8 minX, maxX; + int16 floor; + int16 overlap; + + void write(FileStream &f) const + { + f.write(minZ); + f.write(maxZ); + f.write(minX); + f.write(maxX); + f.write(floor); + f.write(overlap); + } + }; + + struct Zone + { + uint16* ground1; + uint16* ground2; + uint16* fly; + }; + + struct Item + { + uint16 type; + int16 roomIndex; + vec3i pos; + int16 angleY; + int16 intensity; + uint16 flags; + }; + + struct ItemComp + { + uint8 type; + uint8 roomIndex; + vec3s pos; + int16 intensity; + uint16 flags; + + void write(FileStream &f) const + { + f.write(type); + f.write(roomIndex); + f.write(pos.x); + f.write(pos.y); + f.write(pos.z); + f.write(intensity); + f.write(flags); + } + }; + + struct CameraFrame + { + vec3s target; + vec3s pos; + int16 fov; + int16 roll; + + void write(FileStream &f) const + { + f.write(target.x); + f.write(target.y); + f.write(target.z); + f.write(pos.x); + f.write(pos.y); + f.write(pos.z); + f.write(fov); + f.write(roll); + } + }; + + struct SoundInfo + { + uint16 index; + uint16 volume; + uint16 chance; + + union { + struct { + uint16 mode:2, count:4, unused:6, camera:1, pitch:1, gain:1, :1; + }; + + uint16 value; + } flags; + + void write(FileStream &f) const + { + f.write(index); + f.write(volume); + f.write(chance); + f.write(flags.value); + } + }; + + enum NodeFlag + { + NODE_FLAG_POP = (1 << 0), + NODE_FLAG_PUSH = (1 << 1), + NODE_FLAG_ROTX = (1 << 2), + NODE_FLAG_ROTY = (1 << 3), + NODE_FLAG_ROTZ = (1 << 4), + }; + + struct Node + { + uint32 flags; + vec3i pos; + }; + + struct NodeComp + { + vec3s pos; + uint16 flags; + + void write(FileStream &f) + { + f.write(pos.x); + f.write(pos.y); + f.write(pos.z); + f.write(flags); + } + }; + + int32 tilesCount; + Tile* tiles; + + int16 roomsCount; + Room* rooms; + + int32 floorsCount; + FloorData* floors; + + int32 meshDataSize; + uint16* meshData; + + int32 meshOffsetsCount; + uint32* meshOffsets; + + int32 animsCount; + Animation* anims; + + int32 statesCount; + AnimState* states; + + int32 rangesCount; + AnimRange* ranges; + + int32 commandsCount; + int16* commands; + + int32 nodesDataSize; + uint32* nodesData; + + int32 frameDataSize; + uint16* frameData; + + int32 modelsCount; + Model* models; + + int32 staticMeshesCount; + StaticMesh* staticMeshes; + + int32 objectTexturesCount; + ObjectTexture* objectTextures; + + int32 spriteTexturesCount; + SpriteTexture* spriteTextures; + + int32 spriteSequencesCount; + SpriteSequence* spriteSequences; + + int32 camerasCount; + Camera* cameras; + + int32 soundSourcesCount; + SoundSource* soundSources; + + int32 boxesCount; + Box* boxes; + + int32 overlapsCount; + uint16* overlaps; + + Zone zones[2]; + + int32 animTexDataSize; + uint16* animTexData; + + int32 itemsCount; + Item* items; + + uint8 lightmap[32 * 256]; + Palette palette; + + uint16 cameraFramesCount; + CameraFrame* cameraFrames; + + uint16 demoDataSize; + uint8* demoData; + + int16 soundMap[256]; + int32 soundInfoCount; + SoundInfo* soundInfo; + + int32 soundDataSize; + uint8* soundData; + + int32 soundOffsetsCount; + uint32* soundOffsets; + + LevelPC(const char* fileName) + { + tiles = NULL; + + FileStream f(fileName, false); + + if (!f.isValid()) return; + + uint32 magic; + f.read(magic); + + if (magic != 0x00000020) + { + printf("Unsupported level format\n"); + return; + } + + f.readArray(tiles, tilesCount); + f.seek(4); + + f.read(roomsCount); + rooms = new Room[roomsCount]; + + for (int32 i = 0; i < roomsCount; i++) + { + Room* room = rooms + i; + + f.read(room->info); + f.readArray(room->vertices, room->vCount); + f.readArray(room->quads, room->qCount); + f.readArray(room->triangles, room->tCount); + f.readArray(room->sprites, room->sCount); + f.readArray(room->portals, room->pCount); + f.read(room->zSectors); + f.read(room->xSectors); + f.read(room->sectors, room->zSectors * room->xSectors); + f.read(room->ambient); + f.readArray(room->lights, room->lCount); + f.readArray(room->meshes, room->mCount); + f.read(room->alternateRoom); + f.read(room->flags); + } + + f.readArray(floors, floorsCount); + + f.readArray(meshData, meshDataSize); + f.readArray(meshOffsets, meshOffsetsCount); + f.readArray(anims, animsCount); + f.readArray(states, statesCount); + f.readArray(ranges, rangesCount); + f.readArray(commands, commandsCount); + f.readArray(nodesData, nodesDataSize); + f.readArray(frameData, frameDataSize); + f.readArray(models, modelsCount); + f.readArray(staticMeshes, staticMeshesCount); + f.readArray(objectTextures, objectTexturesCount); + f.readArray(spriteTextures, spriteTexturesCount); + f.readArray(spriteSequences, spriteSequencesCount); + + f.readArray(cameras, camerasCount); + f.readArray(soundSources, soundSourcesCount); + f.readArray(boxes, boxesCount); + f.readArray(overlaps, overlapsCount); + + for (int32 i = 0; i < 2; i++) + { + f.read(zones[i].ground1, boxesCount); + f.read(zones[i].ground2, boxesCount); + f.read(zones[i].fly, boxesCount); + } + + f.readArray(animTexData, animTexDataSize); + f.readArray(items, itemsCount); + f.read(lightmap); + f.read(palette); + f.readArray(cameraFrames, cameraFramesCount); + f.readArray(demoData, demoDataSize); + + f.read(soundMap); + f.readArray(soundInfo, soundInfoCount); + f.readArray(soundData, soundDataSize); + f.readArray(soundOffsets, soundOffsetsCount); + + markRoomTextures(); + } + + ~LevelPC() + { + delete[] tiles; + + for (int32 i = 0; i < roomsCount; i++) + { + Room* room = rooms + i; + delete[] room->vertices; + delete[] room->quads; + delete[] room->triangles; + delete[] room->sprites; + delete[] room->portals; + delete[] room->sectors; + delete[] room->lights; + delete[] room->meshes; + } + + delete[] rooms; + delete[] floors; + delete[] meshData; + delete[] meshOffsets; + delete[] anims; + delete[] states; + delete[] ranges; + delete[] commands; + delete[] nodesData; + delete[] frameData; + delete[] models; + delete[] staticMeshes; + delete[] objectTextures; + delete[] spriteTextures; + delete[] spriteSequences; + delete[] cameras; + delete[] soundSources; + delete[] boxes; + delete[] overlaps; + + for (int32 i = 0; i < 2; i++) + { + delete[] zones[i].ground1; + delete[] zones[i].ground2; + delete[] zones[i].fly; + } + + delete[] animTexData; + delete[] items; + delete[] cameraFrames; + delete[] demoData; + delete[] soundInfo; + delete[] soundData; + delete[] soundOffsets; + } + + void markRoomTextures() + { + for (int32 i = 0; i < roomsCount; i++) + { + Room* room = rooms + i; + + for (int32 j = 0; j < room->qCount; j++) + { + Quad* q = room->quads + j; + objectTextures[q->flags & FACE_TEXTURE].attribute |= TEX_ATTR_MIPS; + } + + for (int32 j = 0; j < room->tCount; j++) + { + Triangle* t = room->triangles + j; + objectTextures[t->flags & FACE_TEXTURE].attribute |= TEX_ATTR_MIPS; + } + } + } + + int32 addRoomVertex(int32 yOffset, const Room::Vertex &v, bool ignoreG = false) + { + Room::VertexComp comp; + int32 px = v.pos.x >> 10; + int32 py = (v.pos.y - yOffset) >> 8; + int32 pz = v.pos.z >> 10; + + ASSERT(py >= 0); + ASSERT(px < 32); + ASSERT(py < 64); + ASSERT(pz < 32); + + comp.x = px; + comp.y = py; + comp.z = pz; + comp.g = ignoreG ? 0 : (v.lighting >> 5); + + for (int32 i = 0; i < roomVerticesCount; i++) + { + if (memcmp(roomVertices + i, &comp, sizeof(comp)) == 0) + { + return i; + } + } + + roomVertices[roomVerticesCount] = comp; + + return roomVerticesCount++; + } + +// 3DO ======================================================================== + struct PLUT { + uint16 colors[16]; + } PLUTs[MAX_TEXTURES]; + int32 plutsCount; + + struct Texture3DO { + int32 data; + int32 plut; + + uint8 wShift; + uint8 hShift; + uint16 color; + + uint32 pre0; + uint32 pre1; + uint8* src; + int32 w; + int32 h; + uint16 flip; + int16 mip; + uint8* image; + + void write(FileStream &f) const + { + ASSERT(plut * sizeof(PLUT) < 0xFFFF); + uint32 shift = wShift | (hShift << 8) | ((plut * sizeof(PLUT)) << 16); + f.write(data); + f.write(shift); + } + + bool cmp(const Texture3DO &t) + { + if (wShift != t.wShift || hShift != t.hShift || plut != t.plut) + return false; + + return memcmp(image, t.image, (1 << (20 - wShift)) * (1 << (16 - hShift)) / 2) == 0; + } + + } textures3DO[MAX_TEXTURES]; + + int32 spritesBaseIndex; + + uint32 nextPow2(uint32 x) { + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x++; + return x; + } + + uint32 shiftPow2(int32 x) + { + int32 count = 0; + while (x >>= 1) { + count++; + } + return count; + } + + int32 addPalette(const PLUT &p) + { + for (int32 i = 0; i < plutsCount; i++) + { + if (memcmp(&PLUTs[i], &p, sizeof(PLUT)) == 0) + { + return i; + } + } + + PLUTs[plutsCount] = p; + + return plutsCount++; + } + + template + void calcQuadFlip(T &q) + { + Texture3DO* tex = textures3DO + (q.flags & FACE_TEXTURE); + bool flip = false; + + if (tex->flip & TEX_FLIP_X) { + swap(q.indices[0], q.indices[1]); + swap(q.indices[3], q.indices[2]); + flip = !flip; + } + + if (tex->flip & TEX_FLIP_Y) { + swap(q.indices[0], q.indices[3]); + swap(q.indices[1], q.indices[2]); + flip = !flip; + } + + if (flip) { + q.flags |= FACE_CCW; + } + } + + int32 convertTextures3DO(const char* fileName) + { + #define PRE1_WOFFSET_PREFETCH 2 + #define PRE0_VCNT_PREFETCH 1 + #define PRE0_VCNT_SHIFT 6 + #define PRE0_BPP_4 3 + #define PRE1_TLHPCNT_PREFETCH 1 + #define PRE1_TLHPCNT_SHIFT 0 + #define PRE1_TLLSB_PDC0 0x00001000 + #define PRE1_WOFFSET8_SHIFT 24 + #define PRE0_BGND 0x40000000 + #define PRE0_LINEAR 0x00000010 + #define PRE0_BPP_16 0x00000006 + + ASSERT(objectTexturesCount + spriteTexturesCount < MAX_TEXTURES); + + plutsCount = 0; + + FileStream f(fileName, true); + + if (!f.isValid()) return 0; + + f.bigEndian = true; + + // reserve 4 bytes for the PLUTs offset + f.seek(4); + + // convert palette to 15-bit and fix some color gradients + uint16 pal[256]; + + for (int32 i = 0; i < 256; i++) + { + uint8 b = palette.colors[i * 3 + 0]; + uint8 g = palette.colors[i * 3 + 1]; + uint8 r = palette.colors[i * 3 + 2]; + + pal[i] = (r >> 1) | ((g >> 1) << 5) | ((b >> 1) << 10); + } + + pal[0] = 0; + + // convert palette to 16 x PLUTs + { + for (int32 i = 0; i < 16; i++) + { + memcpy(PLUTs[i].colors, &pal[i * 16], 16 * sizeof(uint16)); + } + plutsCount = 16; + } + + // convert palette to 32-bit + uint32 pal32[256]; + for (int32 i = 0; i < 256; i++) + { + uint16 p = pal[i]; + + uint8 r = (p & 31) << 3; + uint8 g = ((p >> 5) & 31) << 3; + uint8 b = ((p >> 10) & 31) << 3; + + pal32[i] = r | (g << 8) | (b << 16); + + if (pal32[i]) { + pal32[i] |= 0xFF000000; + } + } + pal32[0] = 0; + + uint32* bitmap32 = new uint32[256 * 256]; + uint32* bitmap32_tmp = new uint32[256 * 256]; + uint8* bitmap8 = new uint8[256 * 256]; + uint8* bitmap8_tmp = new uint8[256 * 256]; + + spritesBaseIndex = objectTexturesCount; + + { + LevelPC::ObjectTexture* tmp = new LevelPC::ObjectTexture[objectTexturesCount + spriteTexturesCount]; + memcpy(tmp, objectTextures, sizeof(LevelPC::ObjectTexture) * objectTexturesCount); + + for (int32 i = 0; i < spriteTexturesCount; i++) + { + LevelPC::SpriteTexture* spriteTexture = spriteTextures + i; + LevelPC::ObjectTexture* objectTexture = tmp + objectTexturesCount + i; + + int32 w = spriteTexture->w >> 8; + int32 h = spriteTexture->h >> 8; + + objectTexture->attribute = TEX_ATTR_AKILL; + objectTexture->tile = spriteTexture->tile; + objectTexture->uv0 = 0; + objectTexture->uv1 = 0; + objectTexture->uv2 = 0; + objectTexture->uv3 = 0; + objectTexture->x0 = spriteTexture->u; + objectTexture->y0 = spriteTexture->v; + objectTexture->x1 = spriteTexture->u + w; + objectTexture->y1 = spriteTexture->v; + objectTexture->x2 = spriteTexture->u + w; + objectTexture->y2 = spriteTexture->v + h; + objectTexture->x3 = spriteTexture->u; + objectTexture->y3 = spriteTexture->v + h; + } + + delete[] objectTextures; + objectTextures = tmp; + + objectTexturesCount += spriteTexturesCount; + } + + int32 mipIndex = objectTexturesCount; + + int32 dupSize = 0; + + for (int32 i = 0; i < objectTexturesCount; i++) + { + const LevelPC::ObjectTexture* objectTexture = objectTextures + i; + + int32 x0 = MIN(MIN(objectTexture->x0, objectTexture->x1), objectTexture->x2); + int32 y0 = MIN(MIN(objectTexture->y0, objectTexture->y1), objectTexture->y2); + int32 x1 = MAX(MAX(objectTexture->x0, objectTexture->x1), objectTexture->x2); + int32 y1 = MAX(MAX(objectTexture->y0, objectTexture->y1), objectTexture->y2); + + textures3DO[i].flip = 0; + + if (objectTexture->isQuad) + { + if (objectTexture->x0 > objectTexture->x1) textures3DO[i].flip |= TEX_FLIP_X; + if (objectTexture->y0 > objectTexture->y2) textures3DO[i].flip |= TEX_FLIP_Y; + } + + int32 w = x1 - x0 + 1; + int32 h = y1 - y0 + 1; + + textures3DO[i].src = tiles[objectTexture->tile & 0x3FFF].indices + 256 * y0 + x0; + textures3DO[i].w = w; + textures3DO[i].h = h; + + { // check if the texture is already converted + int32 index = -1; + + if (objectTextures[i].isQuad) + { + for (int32 j = 0; j < i; j++) + { + if (objectTextures[j].isQuad && textures3DO[j].src == textures3DO[i].src) + { + // TODO can we reuse textures with the same src and width but smaller height? + if ((textures3DO[j].w == textures3DO[i].w) && (textures3DO[j].h == textures3DO[i].h)) + { + index = j; + break; + } + } + } + } + + if (index != -1) + { + uint8 flip = textures3DO[i].flip; + textures3DO[i] = textures3DO[index]; + textures3DO[i].flip = flip; // flip flags may differ + continue; // skip texture conversion + } + } + + + { // copy tile to 32-bit image and calculate average tile color + uint8* src = textures3DO[i].src; + uint32* dst = bitmap32; + + uint32 avgR = 0; + uint32 avgG = 0; + uint32 avgB = 0; + + for (int32 y = 0; y < h; y++) + { + for (int32 x = 0; x < w; x++) + { + if (!objectTexture->isQuad) + { + float u = float(x) / float(w - 1); + float v = float(y) / float(h - 1); + + float px0 = objectTexture->x0; + float py0 = objectTexture->y0; + float px1 = objectTexture->x1; + float py1 = objectTexture->y1; + float px2 = objectTexture->x2; + float py2 = objectTexture->y2; + float px3 = objectTexture->x2; + float py3 = objectTexture->y2; + + float px = (1.0f - u) * (1.0f - v) * px0 + u * (1.0f - v) * px1 + (1 - u) * v * px2 + u * v * px3; + float py = (1.0f - u) * (1.0f - v) * py0 + u * (1.0f - v) * py1 + (1 - u) * v * py2 + u * v * py3; + + int32 ix = int32(px + 0.5) - x0; + int32 iy = int32(py + 0.5) - y0; + + ASSERT(!(ix < 0 || ix >= w || iy < 0 || iy >= h)); + + src = textures3DO[i].src + iy * 256 + ix; + } + + uint32 p = pal32[*src++]; + *dst++ = p; + + uint32 A = p >> 24; + if (A) + { + avgR += (p >> 16) & 0xFF; + avgG += (p >> 8) & 0xFF; + avgB += (p) & 0xFF; + } + } + src += 256 - w; + } + + avgR /= w * h; + avgG /= w * h; + avgB /= w * h; + + textures3DO[i].color = (avgB >> 3) | ((avgG >> 3) << 5) | ((avgR >> 3) << 10); + } + + { // resize to POT + int32 wp = nextPow2(w); + int32 hp = nextPow2(h); + + if (wp != w) { + wp /= 2; + } + + if (hp != h) { + hp /= 2; + } + + if (wp > 64) { + wp = 64; + } + + if (hp > 64) { + hp = 64; + } + + ASSERT(wp != 0 && hp != 0); + + if (w != wp || h != hp) + { + stbir_resize_uint8_generic((uint8*)bitmap32, w, h, 0, (uint8*)bitmap32_tmp, wp, hp, 0, 4, 3, 0, + STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL); + swap(bitmap32, bitmap32_tmp); + + w = wp; + h = hp; + } + } + + /*{ + char buf[128]; + sprintf(buf, "tex%d.bmp", i); + saveBitmap(buf, (uint8*)bitmap32, w, h, 32); + }*/ + + int32 rowBytes = (((w * 4) + 31) >> 5) << 2; + if (rowBytes < 8) { + rowBytes = 8; + } + int32 rowWOFFSET = (rowBytes >> 2) - PRE1_WOFFSET_PREFETCH; + + textures3DO[i].pre0 = ((h - PRE0_VCNT_PREFETCH) << PRE0_VCNT_SHIFT) | PRE0_BPP_4; + textures3DO[i].pre1 = ((w - PRE1_TLHPCNT_PREFETCH) << PRE1_TLHPCNT_SHIFT) | PRE1_TLLSB_PDC0 | (rowWOFFSET << PRE1_WOFFSET8_SHIFT); + textures3DO[i].wShift = 20 - shiftPow2(w); + textures3DO[i].hShift = 16 - shiftPow2(h); + + if (!(objectTexture->attribute & TEX_ATTR_AKILL)) { + textures3DO[i].pre0 |= PRE0_BGND; + } + + { // quantize to 16 colors + liq_attr *attr = liq_attr_create(); + liq_image *image = liq_image_create_rgba(attr, bitmap32, w, h, 0); + liq_set_max_colors(attr, 16); + + liq_result *res; + liq_image_quantize(image, attr, &res); + + liq_write_remapped_image(res, image, bitmap8, 256 * 256); + const liq_palette *pal8 = liq_get_palette(res); + + PLUT plut; + + memset(&plut, 0, sizeof(plut)); + for(int32 j = 0; j < pal8->count; j++) + { + liq_color c = pal8->entries[j]; + if (c.a < 128) { + plut.colors[j] = 0; + } else { + plut.colors[j] = (c.r >> 3) | ((c.g >> 3) << 5) | ((c.b >> 3) << 10); + } + } + + textures3DO[i].plut = addPalette(plut); + + liq_result_destroy(res); + liq_image_destroy(image); + liq_attr_destroy(attr); + } + + if (rowBytes * 2 != w) // adjust row pitch + { + uint8* src = bitmap8; + uint8* dst = bitmap8_tmp; + memset(dst, 0, (rowBytes * 2) * h); + + for (int32 y = 0; y < h; y++) { + memcpy(dst, src, w); + dst += rowBytes * 2; + src += w; + } + + swap(bitmap8, bitmap8_tmp); + } + + { // encode to 4-bit image + textures3DO[i].image = new uint8[rowBytes * h]; + + uint8* src = bitmap8; + uint8* dst = textures3DO[i].image; + for (int32 y = 0; y < h; y++) + { + for (int32 x = 0; x < rowBytes; x++, src += 2) + { + *dst++ = (src[0] << 4) | src[1]; + } + } + + textures3DO[i].data = 0; +/* + for (int32 j = 0; j < i; j++) + { + if (textures3DO[i].cmp(textures3DO[j])) + { + textures3DO[i].data = textures3DO[j].data; + + //ASSERT((objectTextures[i].attribute & TEX_ATTR_MIPS) == (objectTextures[j].attribute & TEX_ATTR_MIPS)); + + dupSize += rowBytes * h; + break; + } + } + */ + // write image + if (!textures3DO[i].data) { + textures3DO[i].data = f.getPos(); + + f.write(textures3DO[i].pre0); + f.write(textures3DO[i].pre1); + f.write(textures3DO[i].image, rowBytes * h); + } + } + + // generate mip level + if (!(objectTexture->attribute & TEX_ATTR_MIPS)) { + textures3DO[i].mip = -1; + continue; + } + + textures3DO[i].mip = mipIndex; + + Texture3DO* mip = &textures3DO[mipIndex++]; + *mip = textures3DO[i]; + + w >>= 1; + h >>= 1; + ASSERT(w > 0); + ASSERT(h > 0); + + { + stbir_resize_uint8_generic((uint8*)bitmap32, w << 1, h << 1, 0, (uint8*)bitmap32_tmp, w, h, 0, 4, 3, 0, + STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, NULL); + swap(bitmap32, bitmap32_tmp); + } + + rowBytes = (((w * 4) + 31) >> 5) << 2; + if (rowBytes < 8) { + rowBytes = 8; + } + rowWOFFSET = (rowBytes >> 2) - PRE1_WOFFSET_PREFETCH; + + mip->pre0 = ((h - PRE0_VCNT_PREFETCH) << PRE0_VCNT_SHIFT) | PRE0_BPP_4; + mip->pre1 = ((w - PRE1_TLHPCNT_PREFETCH) << PRE1_TLHPCNT_SHIFT) | PRE1_TLLSB_PDC0 | (rowWOFFSET << PRE1_WOFFSET8_SHIFT); + mip->wShift = 20 - shiftPow2(w); + mip->hShift = 16 - shiftPow2(h); + + if (!(objectTexture->attribute & TEX_ATTR_AKILL)) { + mip->pre0 |= PRE0_BGND; + } + + { // quantize to 16 colors + liq_attr *attr = liq_attr_create(); + liq_image *image = liq_image_create_rgba(attr, bitmap32, w, h, 0); + liq_set_max_colors(attr, 16); + + liq_result *res; + liq_image_quantize(image, attr, &res); + + liq_write_remapped_image(res, image, bitmap8, 256 * 256); + const liq_palette *pal8 = liq_get_palette(res); + + PLUT plut; + + memset(&plut, 0, sizeof(plut)); + for(int32 j = 0; j < pal8->count; j++) + { + liq_color c = pal8->entries[j]; + if (c.a < 128) { + plut.colors[j] = 0; + } else { + plut.colors[j] = (c.r >> 3) | ((c.g >> 3) << 5) | ((c.b >> 3) << 10); + } + } + + mip->plut = addPalette(plut); + + liq_result_destroy(res); + liq_image_destroy(image); + liq_attr_destroy(attr); + } + + if (rowBytes * 2 != w) // adjust row pitch + { + uint8* src = bitmap8; + uint8* dst = bitmap8_tmp; + memset(dst, 0, (rowBytes * 2) * h); + + for (int32 y = 0; y < h; y++) { + memcpy(dst, src, w); + dst += rowBytes * 2; + src += w; + } + + swap(bitmap8, bitmap8_tmp); + } + + { // encode to 4-bit image + uint8* src = bitmap8; + uint8* dst = bitmap8_tmp; + for (int32 y = 0; y < h; y++) + { + for (int32 x = 0; x < rowBytes; x++, src += 2) + { + *dst++ = (src[0] << 4) | src[1]; + } + } + + // write image + mip->data = f.getPos(); + f.write(mip->pre0); + f.write(mip->pre1); + f.write(bitmap8_tmp, rowBytes * h); + } + } + + objectTexturesCount = mipIndex; + + printf("duplicate size: %d\n", dupSize); + + uint32 paletteOffset = f.align4(); + + // write PLUTs + f.write((uint16*)PLUTs, sizeof(PLUT) / 2 * plutsCount); + + // calculate underwater PLUTs (blue tint = (0.5, 0.8, 0.8)) + { + uint16* src = PLUTs[0].colors; + for (int32 i = 0; i < plutsCount * 16; i++) + { + uint16 p = *src; + + uint32 b = (p & 31) << 3; + uint32 g = ((p >> 5) & 31) << 3; + uint32 r = ((p >> 10) & 31) << 3; + + r = int32(r * 0.5f); + g = int32(g * 0.8f); + b = int32(b * 0.8f); + + *src++ = (b >> 3) | ((g >> 3) << 5) | ((r >> 3) << 10); + } + } + f.write((uint16*)PLUTs, sizeof(PLUT) / 2 * plutsCount); + + int32 texFileSize = f.getPos(); + + // write palette offset at the file start + f.setPos(0); + f.write(paletteOffset); + + delete[] bitmap32; + delete[] bitmap32_tmp; + delete[] bitmap8; + delete[] bitmap8_tmp; + + return texFileSize; + } + + void getSample(const char* base, const char* prefix, int32 id, int32 sub, uint8* buffer, int32 &size) + { + // 57 == 38? + size = 0; + + char path[256]; + sprintf(path, "%s\\%s%03d_%d.wav", base, prefix, id, sub); + + FILE* f = fopen(path, "rb"); + + if (!f) + { + if (prefix[0] == '_') // try to open file without the prefix + { + sprintf(path, "%s\\%s%03d_%d.wav", base, "", id, sub); + f = fopen(path, "rb"); + if (!f) + { + printf("%s not found!\n", path); + return; + } + } else { + printf("%s not found!\n", path); + return; + } + } + + fseek(f, 12, SEEK_SET); // skip RIFF header + + struct { + unsigned int id; + unsigned int size; + } chunk; + + while (1) + { + fread(&chunk, sizeof(chunk), 1, f); + if (chunk.id == 0x61746164) // data + { + int numSamples = chunk.size / (1 * sizeof(short)); + size = numSamples / 2; // 4 bits per sample + + short* data = new short[chunk.size / sizeof(short)]; + + fread(data, 1, chunk.size, f); + + BlockADDVIEncode(buffer, data, numSamples, 1); // mono block + + delete[] data; + + break; + } else { + fseek(f, chunk.size, SEEK_CUR); + } + } + + fclose(f); + } + + bool getSoundID(int32 index, int32 &id, int32 &sub) + { + for (int32 i = 0; i < 256; i++) + { + SoundInfo &s = soundInfo[soundMap[i]]; + if (s.index <= index && s.index + s.flags.count > index) + { + id = i; + sub = index - s.index; + return true; + } + } + + return false; + } + + void convert3DO(const char* name) + { + char path[256]; + sprintf(path, "../../3do/CD/data/%s.V", name); + int32 texFileSize = convertTextures3DO(path); + + sprintf(path, "../../3do/CD/data/%s.D", name); + + FileStream f(path, true); + + if (!f.isValid()) return; + + f.bigEndian = true; + + Header header; + f.seek(sizeof(Header)); // will be rewritten at the end + + header.magic = 0x33444F20; + header.tilesCount = plutsCount; + header.roomsCount = roomsCount; + header.modelsCount = modelsCount; + header.meshesCount = meshOffsetsCount; + header.staticMeshesCount = staticMeshesCount; + header.spriteSequencesCount = spriteSequencesCount; + header.soundSourcesCount = soundSourcesCount; + header.boxesCount = boxesCount; + header.texturesCount = objectTexturesCount; + header.itemsCount = itemsCount; + header.camerasCount = camerasCount; + header.cameraFramesCount = cameraFramesCount; + header.soundOffsetsCount = soundOffsetsCount; + header._reserved = 0; + + header.palette = 0; + header.lightmap = 0; + + fixHeadMask(); + + header.rooms = f.align4(); + { + f.seek(sizeof(Room::InfoComp) * roomsCount); + + Room::InfoComp infoComp[255]; + + for (int32 i = 0; i < roomsCount; i++) + { + const LevelPC::Room* room = rooms + i; + + Room::InfoComp &info = infoComp[i]; + + ASSERT(room->info.x % 256 == 0); + ASSERT(room->info.z % 256 == 0); + ASSERT(room->info.yBottom >= -32768 && room->info.yBottom <= 32767); + ASSERT(room->info.yTop >= -32768 && room->info.yTop <= 32767); + info.x = room->info.x / 256; + info.z = room->info.z / 256; + info.yBottom = -32768; + info.yTop = 32767; + + for (int32 j = 0; j < room->vCount; j++) + { + Room::Vertex &v = room->vertices[j]; + if (v.pos.y < info.yTop) { + info.yTop = v.pos.y; + } + if (v.pos.y > info.yBottom) { + info.yBottom = v.pos.y; + } + } + + info.spritesCount = room->sCount; + info.quadsCount = room->qCount; + info.trianglesCount = room->tCount; + info.portalsCount = uint8(room->pCount); + info.lightsCount = uint8(room->lCount); + info.meshesCount = uint8(room->mCount); + info.ambient = room->ambient >> 5; + info.xSectors = uint8(room->xSectors); + info.zSectors = uint8(room->zSectors); + info.alternateRoom = uint8(room->alternateRoom); + + info.flags = 0; + if (room->flags & 1) info.flags |= 1; + if (room->flags & 256) info.flags |= 2; + + ASSERT((room->flags & ~257) == 0); + ASSERT(info.portalsCount == room->pCount); + ASSERT(info.lightsCount == room->lCount); + ASSERT(info.meshesCount == room->mCount); + ASSERT(info.xSectors == room->xSectors); + ASSERT(info.zSectors == room->zSectors); + + roomVerticesCount = 0; + + info.quads = f.align4(); + for (int32 i = 0; i < room->qCount; i++) + { + Quad q = room->quads[i]; + + // get intensity + const Room::Vertex &v0 = room->vertices[q.indices[0]]; + const Room::Vertex &v1 = room->vertices[q.indices[1]]; + const Room::Vertex &v2 = room->vertices[q.indices[2]]; + const Room::Vertex &v3 = room->vertices[q.indices[3]]; + + uint32 intensity = ((v0.lighting + v1.lighting + v2.lighting + v3.lighting) / 4) >> 5; + ASSERT(intensity <= 255); + + q.indices[0] = addRoomVertex(info.yTop, v0, true); + q.indices[1] = addRoomVertex(info.yTop, v1, true); + q.indices[2] = addRoomVertex(info.yTop, v2, true); + q.indices[3] = addRoomVertex(info.yTop, v3, true); + + ASSERT((int32)q.indices[0] * 12 < 0xFFFF); + ASSERT((int32)q.indices[1] * 12 < 0xFFFF); + ASSERT((int32)q.indices[2] * 12 < 0xFFFF); + ASSERT((int32)q.indices[3] * 12 < 0xFFFF); + + RoomQuad3DO comp; + comp.indices[0] = q.indices[0] * 12; + comp.indices[1] = q.indices[1] * 12; + comp.indices[2] = q.indices[2] * 12; + comp.indices[3] = q.indices[3] * 12; + comp.flags = q.flags; + // add ccw flag and swap indices + calcQuadFlip(comp); + ASSERT((comp.flags & FACE_CCW) == 0); + // add intensity + comp.flags |= (intensity << (FACE_MIP_SHIFT + FACE_MIP_SHIFT)); + if (textures3DO[comp.flags & FACE_TEXTURE].pre0 & PRE0_BGND) { + comp.flags |= FACE_OPAQUE; // set opaque flag + } + // add mip level + Texture3DO* tex = textures3DO + (comp.flags & FACE_TEXTURE); + if (tex->mip != -1) { + comp.flags |= (tex->mip << FACE_MIP_SHIFT); + } + + comp.write(f); + } + + info.triangles = f.align4(); + for (int32 i = 0; i < room->tCount; i++) + { + Triangle t = room->triangles[i]; + + // get intensity + const Room::Vertex &v0 = room->vertices[t.indices[0]]; + const Room::Vertex &v1 = room->vertices[t.indices[1]]; + const Room::Vertex &v2 = room->vertices[t.indices[2]]; + + uint32 intensity = ((v0.lighting + v1.lighting + v2.lighting) / 3) >> 5; + ASSERT(intensity <= 255); + + t.indices[0] = addRoomVertex(info.yTop, v0, true); + t.indices[1] = addRoomVertex(info.yTop, v1, true); + t.indices[2] = addRoomVertex(info.yTop, v2, true); + + ASSERT((int32)t.indices[0] * 12 < 0xFFFF); + ASSERT((int32)t.indices[1] * 12 < 0xFFFF); + ASSERT((int32)t.indices[2] * 12 < 0xFFFF); + + RoomTriangle3DO comp; + comp.indices[0] = t.indices[0] * 12; + comp.indices[1] = t.indices[1] * 12; + comp.indices[2] = t.indices[2] * 12; + comp._unused = 0; + comp.flags = t.flags; + // add intensity + comp.flags |= (intensity << (FACE_MIP_SHIFT + FACE_MIP_SHIFT)); + if (textures3DO[comp.flags & FACE_TEXTURE].pre0 & PRE0_BGND) { + comp.flags |= FACE_OPAQUE; // set opaque flag + } + // add mip level + Texture3DO* tex = textures3DO + (comp.flags & FACE_TEXTURE); + if (tex->mip != -1) { + comp.flags |= (tex->mip << FACE_MIP_SHIFT); + } + comp.write(f); + } + + info.vertices = f.align4(); + info.verticesCount = roomVerticesCount; + for (int32 i = 0; i < roomVerticesCount; i += 4) + { + Room::VertexComp v[4]; + + for (int32 j = 0; j < 4; j++) + { + if (i + j < roomVerticesCount) { + v[j] = roomVertices[i + j]; + } else { + memset(&v[j], 0, sizeof(v[j])); + } + } + + { + uint32 value = v[0].x | (v[0].y << 5) | (v[0].z << 11); + value |= (v[1].x | (v[1].y << 5) | (v[1].z << 11)) << 16; + f.write(value); + } + + { + uint32 value = v[2].x | (v[2].y << 5) | (v[2].z << 11); + value |= (v[3].x | (v[3].y << 5) | (v[3].z << 11)) << 16; + f.write(value); + } + } + + info.sprites = f.align4(); + for (int32 i = 0; i < room->sCount; i++) + { + const Room::Sprite* sprite = room->sprites + i; + const Room::Vertex* v = room->vertices + sprite->index; + + Room::SpriteComp comp; + comp.x = v->pos.x; + comp.y = v->pos.y; + comp.z = v->pos.z; + comp.g = v->lighting >> 5; + comp.index = uint8(sprite->texture); + + ASSERT(sprite->texture <= 255); + + comp.write(f); + } + + info.portals = f.align4(); + f.writeObj(room->portals, room->pCount); + /* + for (int32 i = 0; i < room->pCount; i++) + { + const Room::Portal* portal = room->portals + i; + + Room::PortalComp comp; + + comp.roomIndex = portal->roomIndex; + + static const struct { + int32 x, y, z; + int32 mask; + } normals[9] = { + { -1, 0, 0, 2 << 0 }, + { 1, 0, 0, 1 << 0 }, + { 0, -1, 0, 2 << 2 }, + { 0, 1, 0, 1 << 2 }, + { 0, 0, -1, 2 << 4 }, + { 0, 0, 1, 1 << 4 } + }; + + comp.normalMask = 255; + for (int32 i = 0; i < 9; i++) + { + if (portal->normal.x == normals[i].x && + portal->normal.y == normals[i].y && + portal->normal.z == normals[i].z) + { + comp.normalMask = normals[i].mask; + break; + } + } + + ASSERT(comp.normalMask != 255); + + for (int32 i = 0; i < 4; i++) + { + comp.vertices[i].x = portal->vertices[i].x; + comp.vertices[i].y = portal->vertices[i].y; + comp.vertices[i].z = portal->vertices[i].z; + } + + comp.write(f); + }*/ + + info.sectors = f.align4(); + f.writeObj(room->sectors, room->zSectors * room->xSectors); + + info.lights = f.align4(); + for (int32 i = 0; i < room->lCount; i++) + { + const Room::Light* light = room->lights + i; + + Room::LightComp comp; + comp.pos.x = light->pos.x - room->info.x; + comp.pos.y = light->pos.y; + comp.pos.z = light->pos.z - room->info.z; + comp.radius = light->radius >> 8; + comp.intensity = light->intensity >> 5; + + comp.write(f); + } + + info.meshes = f.align4(); + for (int32 i = 0; i < room->mCount; i++) + { + const Room::Mesh* mesh = room->meshes + i; + + Room::MeshComp comp; + comp.pos.x = mesh->pos.x - room->info.x; + comp.pos.y = mesh->pos.y; + comp.pos.z = mesh->pos.z - room->info.z; + comp.intensity = mesh->intensity >> 5; + comp.flags = ((mesh->angleY / 0x4000 + 2) << 6) | mesh->id; + + ASSERT(mesh->id <= 63); + ASSERT(mesh->angleY % 0x4000 == 0); + ASSERT(mesh->angleY / 0x4000 + 2 >= 0); + + comp.write(f); + } + } + + int32 pos = f.getPos(); + f.setPos(header.rooms); + f.writeObj(infoComp, roomsCount); + f.setPos(pos); + } + + header.floors = f.align4(); + f.writeObj(floors, floorsCount); + + header.meshData = f.align4(); + + int32 mOffsets[2048]; + for (int32 i = 0; i < 2048; i++) { + mOffsets[i] = -1; + } + + for (int32 i = 0; i < meshOffsetsCount; i++) + { + if (mOffsets[i] != -1) + continue; + + mOffsets[i] = f.align4() - header.meshData; + + const uint8* ptr = (uint8*)meshData + meshOffsets[i]; + + vec3s center = *(vec3s*)ptr; ptr += sizeof(center); + int16 radius = *(int16*)ptr; ptr += sizeof(radius); + uint16 flags = *(uint16*)ptr; ptr += sizeof(flags); + + int16 vCount = *(int16*)ptr; ptr += 2; + const vec3s* vertices = (vec3s*)ptr; + ptr += vCount * sizeof(vec3s); + + const uint16* vIntensity = NULL; + const vec3s* vNormal = NULL; + + int16 nCount = *(int16*)ptr; ptr += 2; + //const int16* normals = (int16*)ptr; + if (nCount > 0) { // normals + vNormal = (vec3s*)ptr; + ptr += nCount * 3 * sizeof(int16); + } else { // intensity + vIntensity = (uint16*)ptr; + ptr += vCount * sizeof(uint16); + } + + int16 rCount = *(int16*)ptr; ptr += 2; + Quad* rFaces = (Quad*)ptr; ptr += rCount * sizeof(Quad); + + int16 tCount = *(int16*)ptr; ptr += 2; + Triangle* tFaces = (Triangle*)ptr; ptr += tCount * sizeof(Triangle); + + int16 crCount = *(int16*)ptr; ptr += 2; + Quad* crFaces = (Quad*)ptr; ptr += crCount * sizeof(Quad); + + int16 ctCount = *(int16*)ptr; ptr += 2; + Triangle* ctFaces = (Triangle*)ptr; ptr += ctCount * sizeof(Triangle); + + uint16 intensity = 0; + + if (vIntensity) + { + uint32 sum = 0; + for (int32 i = 0; i < vCount; i++) + { + sum += vIntensity[i]; + } + intensity = sum / vCount; + } + + f.write(center.x); + f.write(center.y); + f.write(center.z); + f.write(radius); + f.write(intensity); + f.write(vCount); + f.write(rCount); + f.write(tCount); + f.write(crCount); + f.write(ctCount); + + for (int32 j = 0; j < vCount; j++) + { + struct MeshVertex3DO { + int16 x, y, z; + } v; + + v.x = vertices[j].x << 2; + v.y = vertices[j].y << 2; + v.z = vertices[j].z << 2; + + f.write(v.x); + f.write(v.y); + f.write(v.z); + } + + if (vCount % 2) { // add one vertex for the data alignment + int16 zero = 0; + f.write(zero); + f.write(zero); + f.write(zero); + } + + for (int32 j = 0; j < rCount; j++) + { + Quad q = rFaces[j]; + + ASSERT(q.indices[0] < 256); + ASSERT(q.indices[1] < 256); + ASSERT(q.indices[2] < 256); + ASSERT(q.indices[3] < 256); + + MeshQuad3DO comp; + comp.indices[0] = q.indices[0]; + comp.indices[1] = q.indices[1]; + comp.indices[2] = q.indices[2]; + comp.indices[3] = q.indices[3]; + comp.flags = q.flags; + if (textures3DO[comp.flags & FACE_TEXTURE].pre0 & PRE0_BGND) { + comp.flags |= FACE_OPAQUE; // set opaque flag + } + calcQuadFlip(comp); + comp.write(f); + } + + for (int32 j = 0; j < tCount; j++) + { + Triangle t = tFaces[j]; + + ASSERT(t.indices[0] < 256); + ASSERT(t.indices[1] < 256); + ASSERT(t.indices[2] < 256); + + MeshTriangle3DO comp; + comp.indices[0] = t.indices[0]; + comp.indices[1] = t.indices[1]; + comp.indices[2] = t.indices[2]; + comp._unused = 0; + comp.flags = t.flags; + if (textures3DO[comp.flags & FACE_TEXTURE].pre0 & PRE0_BGND) { + comp.flags |= FACE_OPAQUE; // set opaque flag + } + comp.write(f); + } + + for (int32 j = 0; j < crCount; j++) + { + Quad q = crFaces[j]; + + ASSERT(q.indices[0] < 256); + ASSERT(q.indices[1] < 256); + ASSERT(q.indices[2] < 256); + ASSERT(q.indices[3] < 256); + + MeshQuad3DO comp; + comp.indices[0] = q.indices[0]; + comp.indices[1] = q.indices[1]; + comp.indices[2] = q.indices[2]; + comp.indices[3] = q.indices[3]; + comp.flags = q.flags; + comp.write(f); + } + + for (int32 j = 0; j < ctCount; j++) + { + Triangle t = ctFaces[j]; + + ASSERT(t.indices[0] < 256); + ASSERT(t.indices[1] < 256); + ASSERT(t.indices[2] < 256); + + MeshTriangle3DO comp; + comp.indices[0] = t.indices[0]; + comp.indices[1] = t.indices[1]; + comp.indices[2] = t.indices[2]; + comp._unused = 0; + comp.flags = t.flags; + comp.write(f); + } + + + for (int32 j = i + 1; j < meshOffsetsCount; j++) + { + if (meshOffsets[i] == meshOffsets[j]) + { + mOffsets[j] = mOffsets[i]; + } + } + } + + header.meshOffsets = f.align4(); + f.write(mOffsets, meshOffsetsCount); + + header.anims = f.align4(); + f.writeObj(anims, animsCount); + + header.states = f.align4(); + for (int32 i = 0; i < statesCount; i++) + { + const LevelPC::AnimState* state = states + i; + + LevelPC::AnimStateComp comp; + comp.state = uint8(state->state); + comp.rangesCount = uint8(state->rangesCount); + comp.rangesStart = state->rangesStart; + + comp.write(f); + } + + header.ranges = f.align4(); + f.writeObj(ranges, rangesCount); + + header.commands = f.align4(); + f.write(commands, commandsCount); + + header.nodes = f.align4(); + for (int32 i = 0; i < nodesDataSize / 4; i++) + { + const Node* node = (Node*)(nodesData + i * 4); + + ASSERT(node->pos.x > -32768); + ASSERT(node->pos.x < 32767); + ASSERT(node->pos.y > -32768); + ASSERT(node->pos.y < 32767); + ASSERT(node->pos.z > -32768); + ASSERT(node->pos.z < 32767); + ASSERT(node->flags < 0xFFFF); + + LevelPC::NodeComp comp; + comp.flags = uint16(node->flags); + comp.pos.x = int16(node->pos.x); + comp.pos.y = int16(node->pos.y); + comp.pos.z = int16(node->pos.z); + + comp.write(f); + } + //f.write(nodesData, nodesDataSize); + + header.frameData = f.align4(); + f.write(frameData, frameDataSize); + + header.models = f.align4(); + for (int32 i = 0; i < modelsCount; i++) + { + const LevelPC::Model* model = models + i; + + LevelPC::ModelComp comp; + comp.type = uint8(model->type); + comp.count = uint8(model->count); + comp.start = model->start; + comp.nodeIndex = model->nodeIndex / 4; + comp.animIndex = model->animIndex; + + comp.write(f); + } + + header.staticMeshes = f.align4(); + for (int32 i = 0; i < staticMeshesCount; i++) + { + const LevelPC::StaticMesh* staticMesh = staticMeshes + i; + + LevelPC::StaticMeshComp comp; + comp.id = staticMesh->id; + comp.meshIndex = staticMesh->meshIndex; + comp.flags = staticMesh->flags; + comp.vbox = staticMesh->vbox; + comp.cbox = staticMesh->cbox; + + comp.write(f); + } + + header.objectTextures = f.align4(); + for (int32 i = 0; i < objectTexturesCount; i++) + { + textures3DO[i].write(f); + } + + header.spriteTextures = f.align4(); + for (int32 i = 0; i < spriteTexturesCount; i++) + { + const LevelPC::SpriteTexture* spriteTexture = spriteTextures + i; + + SpriteTexture3DO comp; + comp.texture = spritesBaseIndex + i; + comp.l = spriteTexture->l; + comp.t = spriteTexture->t; + comp.r = spriteTexture->r; + comp.b = spriteTexture->b; + + comp.write(f); + } + + f.writeObj(spriteTextures, spriteTexturesCount); + + header.spriteSequences = f.align4(); + f.writeObj(spriteSequences, spriteSequencesCount); + + header.cameras = f.align4(); + f.writeObj(cameras, camerasCount); + + header.soundSources = f.align4(); + f.writeObj(soundSources, soundSourcesCount); + + header.boxes = f.align4(); + for (int32 i = 0; i < boxesCount; i++) + { + const LevelPC::Box* box = boxes + i; + + BoxComp comp; + comp.minX = box->minX >> 10; + comp.minZ = box->minZ >> 10; + comp.maxX = (box->maxX + 1) >> 10; + comp.maxZ = (box->maxZ + 1) >> 10; + comp.floor = box->floor; + comp.overlap = box->overlap; + + comp.write(f); + } + + header.overlaps = f.align4(); + f.write(overlaps, overlapsCount); + + for (int32 i = 0; i < 2; i++) + { + header.zones[i][0] = f.align4(); + f.write(zones[i].ground1, boxesCount); + + header.zones[i][1] = f.align4(); + f.write(zones[i].ground2, boxesCount); + + header.zones[i][2] = f.align4(); + f.write(zones[i].fly, boxesCount); + } + + header.animTexData = f.align4(); + { + int32 lastPos = f.getPos(); + + uint16 rangesCount = *animTexData++; + struct TexAnimRange + { + uint16 count; + uint16 indices[256]; + } ranges[64]; + ASSERT(rangesCount <= 64); + + int32 newRangesCount = rangesCount; + + for (int32 i = 0; i < rangesCount; i++) + { + bool mips = true; + + TexAnimRange &range = ranges[i]; + range.count = *animTexData++; + for (int32 j = 0; j <= range.count; j++) + { + range.indices[j] = *animTexData++; + + if (textures3DO[range.indices[j]].mip < 0) { + mips = false; + } + } + + // add the new anim range for mip textures + if (mips) + { + TexAnimRange &mipRange = ranges[newRangesCount++]; + mipRange.count = range.count; + for (int32 j = 0; j <= range.count; j++) + { + mipRange.indices[j] = textures3DO[range.indices[j]].mip; + } + } + } + rangesCount = newRangesCount; + + f.write(rangesCount); + for (int32 i = 0; i < rangesCount; i++) + { + f.write(ranges[i].count); + f.write(ranges[i].indices, ranges[i].count + 1); + } + + lastPos = f.getPos() - lastPos; + } + + header.items = f.align4(); + for (int32 i = 0; i < itemsCount; i++) + { + const LevelPC::Item* item = items + i; + const LevelPC::Room* room = rooms + item->roomIndex; + + ItemComp comp; + comp.type = uint8(item->type); + comp.roomIndex = uint8(item->roomIndex); + comp.pos.x = int16(item->pos.x - room->info.x); + comp.pos.y = int16(item->pos.y); + comp.pos.z = int16(item->pos.z - room->info.z); + comp.intensity = item->intensity < 0 ? 0 : (item->intensity >> 5); + comp.flags = item->flags | ((item->angleY / 0x4000 + 2) << 14); + + ASSERT((item->flags & ~(0x3F1F)) == 0); + + comp.write(f); + } + + header.cameraFrames = f.align4(); + f.writeObj(cameraFrames, cameraFramesCount); + + //f.writeArray(demoData, demoDataSize); + + header.soundMap = f.align4(); + f.write(soundMap, 256); + + header.soundInfos = f.align4(); + f.writeObj(soundInfo, soundInfoCount); + + header.soundData = f.align4(); + + uint8* soundBuf = new uint8[2 * 1024 * 1024]; + + bool isHome = strcmp(name, "GYM") == 0; + + for (int32 i = 0; i < soundOffsetsCount; i++) + { + soundOffsets[i] = f.align4() - header.soundData; + + int32 id, sub, size; + if (getSoundID(i, id, sub)) + { + getSample("C:\\Projects\\OpenLara\\src\\platform\\gba\\packer\\sounds\\conv_3do", isHome ? "_" : "", id, sub, soundBuf, size); + } else { + ASSERT(false); + } + + int32 numSamples = size * 2; + f.write(numSamples); + + if (size) { + f.write(soundBuf, size); + } + } + + delete[] soundBuf; + + header.soundOffsets = f.align4(); + f.write(soundOffsets, soundOffsetsCount); + + f.setPos(0); + header.write(f); + } + +}; + +#define COLOR_THRESHOLD_SQ (8 * 8) +#endif + + +#if 0 +struct WAD +{ + + + struct LevelWAD + { + struct Room + { + struct Vertex + { + int8 x, y, z; + uint8 lighting; + }; + + struct Quad + { + uint16 flags; + uint16 indices[4]; + }; + + struct Triangle + { + uint16 flags; + uint16 indices[3]; + }; + + struct Sprite + { + int16 x, y, z; + uint16 texture; + }; + + struct Portal + { + uint8 roomIndex; + uint8 normalIndex; + uint16 x, y, z; + uint8 a, b; + + Portal() {} + Portal(const LevelPC::Room::Portal &portal) + { + + const vec3s &v0 = portal.vertices[0]; + const vec3s &v1 = portal.vertices[1]; + const vec3s &v2 = portal.vertices[2]; + const vec3s &v3 = portal.vertices[3]; + const vec3s &n = portal.normal; + + normalIndex = 0xFF; + + if (n.x == -1) normalIndex = 0; + if (n.x == 1) normalIndex = 1; + if (n.y == -1) normalIndex = 2; + if (n.y == 1) normalIndex = 3; + if (n.z == -1) normalIndex = 4; + if (n.z == 1) normalIndex = 5; + + ASSERT(normalIndex != 0xFF); + + int32 minX = MIN(MIN(MIN(v0.x, v1.x), v2.x), v3.x); + int32 minY = MIN(MIN(MIN(v0.y, v1.y), v2.y), v3.y); + int32 minZ = MIN(MIN(MIN(v0.z, v1.z), v2.z), v3.z); + int32 maxX = MAX(MAX(MAX(v0.x, v1.x), v2.x), v3.x); + int32 maxY = MAX(MAX(MAX(v0.y, v1.y), v2.y), v3.y); + int32 maxZ = MAX(MAX(MAX(v0.z, v1.z), v2.z), v3.z); + + x = (maxX + minX) / 2; + y = (maxY + minY) / 2; + z = (maxZ + minZ) / 2; + + int32 sx = (maxX - minX) / 256; + int32 sy = (maxY - minY) / 256; + int32 sz = (maxZ - minZ) / 256; + + switch (normalIndex / 2) + { + case 0 : // x + a = sy; + b = sz; + break; + case 1 : // y + a = sx; + b = sz; + break; + case 2 : // z + a = sx; + b = sy; + break; + } + } + }; + + struct Sector + { + uint16 floorIndex; + uint16 boxIndex; + uint8 roomBelow; + int8 floor; + uint8 roomAbove; + int8 ceiling; + }; + + int16 vCount; + int16 qCount; + int16 tCount; + uint8 sCount; + uint8 pCount; + uint8 zSectors; + uint8 xSectors; + + Vertex vertices[MAX_ROOM_VERTICES]; + Quad quads[MAX_ROOM_QUADS]; + Triangle triangles[MAX_ROOM_TRIANGLES]; + Sprite sprites[MAX_ROOM_SPRITES]; + Portal portals[MAX_ROOM_PORTALS]; + Sector sectors[MAX_ROOM_SECTORS]; + + Room() {} + + Room(const LevelPC::Room &room) : vCount(0), qCount(0), tCount(0), sCount(0) + { + for (int32 i = 0; i < room.qCount; i++) + { + addQuad(room.quads[i], room.vertices); + } + + for (int32 i = 0; i < room.tCount; i++) + { + addTriangle(room.triangles[i], room.vertices); + } + + for (int32 i = 0; i < room.sCount; i++) + { + addSprite(room.sprites[i], room.vertices); + } + + pCount = uint8(room.pCount); + for (int32 i = 0; i < pCount; i++) + { + portals[i] = room.portals[i]; + } + + zSectors = uint8(room.zSectors); + xSectors = uint8(room.xSectors); + for (int32 i = 0; i < zSectors * xSectors; i++) + { + const LevelPC::Room::Sector &src = room.sectors[i]; + Sector &dst = sectors[i]; + dst.floorIndex = src.floorIndex; + dst.boxIndex = src.boxIndex; + dst.roomBelow = src.roomBelow; + dst.floor = src.floor; + dst.roomAbove = src.roomAbove; + dst.ceiling = src.ceiling; + } + } + + int32 addVertex(const LevelPC::Room::Vertex &v) + { + Vertex n; + n.x = v.pos.x / 1024; + n.y = v.pos.y / 256; + n.z = v.pos.z / 1024; + n.lighting = v.lighting >> 5; + + for (int32 i = 0; i < vCount; i++) + { + if (vertices[i].x == n.x && + vertices[i].y == n.y && + vertices[i].z == n.z && + vertices[i].lighting == n.lighting) + { + return i; + } + } + + vertices[vCount++] = n; + + return vCount - 1; + } + + void addQuad(const LevelPC::Quad &q, const LevelPC::Room::Vertex* verts) + { + Quad n; + n.flags = q.flags; + n.indices[0] = addVertex(verts[q.indices[0]]); + n.indices[1] = addVertex(verts[q.indices[1]]); + n.indices[2] = addVertex(verts[q.indices[2]]); + n.indices[3] = addVertex(verts[q.indices[3]]); + quads[qCount++] = n; + } + + void addTriangle(const LevelPC::Triangle &t, const LevelPC::Room::Vertex* verts) + { + Triangle n; + n.flags = t.flags; + n.indices[0] = addVertex(verts[t.indices[0]]); + n.indices[1] = addVertex(verts[t.indices[1]]); + n.indices[2] = addVertex(verts[t.indices[2]]); + triangles[tCount++] = n; + } + + void addSprite(const LevelPC::Room::Sprite &s, const LevelPC::Room::Vertex* verts) + { + Sprite n; + n.texture = s.texture; + n.x = verts[s.index].pos.x; + n.y = verts[s.index].pos.y; + n.z = verts[s.index].pos.z; + // lighting? + sprites[sCount++] = n; + } + }; + + struct FloorData + { + uint16 value; + }; + + struct Box + { + int8 minZ, maxZ; + int8 minX, maxX; + int16 floor; + int16 overlap; + + Box() {} + + Box(const LevelPC::Box &b) + { + minX = b.minX / 1024; + minZ = b.minZ / 1024; + maxX = (b.maxX + 1) / 1024; + maxZ = (b.maxZ + 1) / 1024; + + floor = int16(b.floor); + overlap = int16(b.overlap); + + ASSERT(b.minX == minX * 1024); + ASSERT(b.minZ == minZ * 1024); + ASSERT(b.maxX == maxX * 1024 - 1); + ASSERT(b.maxZ == maxZ * 1024 - 1); + } + }; + + struct Overlap + { + uint16 value; + }; + + struct Zone + { + uint16 ground1[MAX_BOXES]; + uint16 ground2[MAX_BOXES]; + uint16 fly[MAX_BOXES]; + }; + + struct Item + { + uint8 type; + uint8 roomIndex; + vec3s pos; + int8 angleY; + uint8 intensity; + uint16 flags; + + Item() {} + + Item(const LevelPC::Item &item) + { + type = uint8(item.type); + roomIndex = uint8(item.roomIndex); + pos.x = int16(item.pos.x); + pos.y = int16(item.pos.y); + pos.z = int16(item.pos.z); + angleY = item.angleY / 0x4000; + intensity = item.intensity >> 5; + flags = item.flags; + } + }; + + int16 roomsCount; + int16 floorsCount; + int16 boxesCount; + int16 overlapsCount; + int16 itemsCount; + + Room rooms[MAX_ROOMS]; + FloorData floors[MAX_FLOORS]; + Box boxes[MAX_BOXES]; + Overlap overlaps[MAX_OVERLAPS]; + Zone zones[2]; + Item items[MAX_ITEMS]; + + LevelWAD() {} + + LevelWAD(const LevelPC &level) + { + roomsCount = int16(level.roomsCount); + for (int32 i = 0; i < level.roomsCount; i++) + { + rooms[i] = level.rooms[i]; + } + + floorsCount = int16(level.floorsCount); + memcpy(floors, level.floors, floorsCount * sizeof(floors[0])); + + boxesCount = int16(level.boxesCount); + for (int32 i = 0; i < level.boxesCount; i++) + { + boxes[i] = level.boxes[i]; + } + + overlapsCount = int16(level.overlapsCount); + memcpy(overlaps, level.overlaps, overlapsCount * sizeof(overlaps[0])); + + for (int32 i = 0; i < 2; i++) + { + memcpy(zones[i].ground1, level.zones[i].ground1, level.boxesCount * sizeof(uint16)); + memcpy(zones[i].ground2, level.zones[i].ground2, level.boxesCount * sizeof(uint16)); + memcpy(zones[i].fly, level.zones[i].fly, level.boxesCount * sizeof(uint16)); + } + + itemsCount = int16(level.itemsCount); + for (int32 i = 0; i < itemsCount; i++) + { + LevelPC::Item item = level.items[i]; + const LevelPC::Room &room = level.rooms[item.roomIndex]; + + item.pos.x -= room.info.x; + item.pos.z -= room.info.z; + + items[i] = item; + } + } + }; + + + + struct Model + { + uint16 count; + uint16 start; + uint16 nodeIndex; + uint16 frameIndex; + uint16 animIndex; + }; + + + Model* models[MAX_ITEMS]; + + bool itemsUsed[MAX_ITEMS]; + + LevelWAD* levels[MAX_LEVELS]; + + WAD() + { + memset(models, 0, sizeof(models)); + memset(itemsUsed, 0, sizeof(itemsUsed)); + } + + + + int32 addNodes(const LevelPC::Node *nodes, int32 count) + { + if (count == 0) + return 0; + + int32 index = 0; + + for (int32 i = 0; i < nodeLists.count; i++) + { + if (nodeLists[i]->count == count) + { + bool equal = true; + + for (int32 j = 0; j < nodeLists[i]->count; j++) + { + const Node &n = nodeLists[i]->nodes[j]; + if (n.flags != nodes[j].flags || + n.pos.x != nodes[j].pos.x || + n.pos.y != nodes[j].pos.y || + n.pos.z != nodes[j].pos.z) + { + equal = false; + break; + } + } + + if (equal) { + return index; + } + } + + index += nodeLists[i]->count; + } + + NodeList* list = new NodeList(); + list->count = count; + for (int32 i = 0; i < count; i++) + { + Node &n = list->nodes[i]; + n.flags = nodes[i].flags; + n.pos.x = nodes[i].pos.x; + n.pos.y = nodes[i].pos.y; + n.pos.z = nodes[i].pos.z; + } + + nodeLists.add(list); + + return index; + } + + void packTiles() + { + textures.sort(); + + for (int32 i = 0; i < textures.count; i++) + { + WAD::Texture* tex = textures[i]; + + bool placed = false; + + for (int32 j = 0; j < tiles.count; j++) + { + if (tiles[j]->root->insert(tex)) { + placed = true; + break; + } + } + + if (!placed) + { + Tile24* tile = new Tile24(); + tiles.add(tile); + if (!tile->root->insert(tex)) + { + printf("Can't pack texture %d x %d", tex->width, tex->height); + break; + } + } + } + + uint8* data = new uint8[256 * 256 * 3 * tiles.count]; + + for (int32 i = 0; i < tiles.count; i++) + { + tiles[i]->fill(data + i * 256 * 256 * 3); + } + saveBitmap("tiles.bmp", data, 256, 256 * tiles.count); + + delete[] data; + } +}; + + +void convertTracks3DO(const char* inDir, const char* outDir) +{ + WIN32_FIND_DATA fd; + HANDLE h = FindFirstFile(inDir, &fd); + + if (h == INVALID_HANDLE_VALUE) + return; + + char buf[256]; + + do + { + if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + const char* src = fd.cFileName; + char* dst = buf; + + while (*src) + { + if (*src >= '0' && *src <= '9') + { + *dst++ = *src; + } + src++; + } + *dst++ = 0; + + int32 index = atoi(buf); + + if (index != 0) + { + strcpy(buf, inDir); + buf[strlen(buf) - 1] = 0; + strcat(buf, fd.cFileName); + + char cmdline[256]; + sprintf(cmdline, "C:\\Program Files\\ffmpeg\\ffmpeg.exe -y -i \"%s\" -ac 1 -ar 22050 -acodec pcm_s16be %s\\%d.aiff", buf, outDir, index); + + launchApp(cmdline); + /* TODO SDXC encoder + FILE* f = fopen("tmp.wav", "rb"); + ASSERT(f); + + fseek(f, 0, SEEK_END); + int32 size = ftell(f); + fseek(f, 0, SEEK_SET); + uint8* samples = new uint8[size]; + fread(samples, 1, size, f); + fclose(f); + + int32 numSamples = size / sizeof(short); + + int32 outputSize = (size + 3) / 4; + uint8* output = new uint8[outputSize]; + memset(output, 0, outputSize); + + BlockADDVIEncode(output, (short*)samples, numSamples, 1); // mono block + + sprintf(buf, "%s\\%d.S", outDir, index); + f = fopen(buf, "wb"); + ASSERT(f); + fwrite(&numSamples, sizeof(numSamples), 1, f); + fwrite(output, 1, outputSize, f); + fclose(f); + + delete[] output; + delete[] samples; + */ + } + } + } + while (FindNextFile(h, &fd)); + FindClose(h); +} + + + +// 3DO face flags +// 1:ccw, 1:opaque, 8:intensity, 11:mipTexIndex, 11:texIndex +#define FACE_MIP_SHIFT 11 +#define FACE_OPAQUE (1 << 30) +#define FACE_CCW (1 << 31) + +#define TEX_FLIP_X 1 +#define TEX_FLIP_Y 2 +#define TEX_ATTR_MIPS 0x8000 + +void process3DO(const char* dir, TR1_PC* pc, TR1_PSX* psx) +{ +} + +// BIG TODO! + //pack_tracks("tracks/conv_demo/*.ima"); return 0; + + + /* + uint32 palDump[32][256]; + memset(palDump, 0, sizeof(palDump)); + + + for (int32 i = 0; i < MAX_LEVELS; i++) + { + char path[64]; + sprintf(path, "levels/%s.PHD", levelNames[i]); + levels[i] = new LevelPC(path); + + for (int32 j = 0; j < 256; j++) + { + int32 r = levels[i]->palette.colors[j * 3 + 0] << 2; + int32 g = levels[i]->palette.colors[j * 3 + 1] << 2; + int32 b = levels[i]->palette.colors[j * 3 + 2] << 2; + palDump[i][j] = b | (g << 8) | (r << 16) | (0xFF << 24); + } + + levels[i]->generateLODs(); + //levels[i]->cutData(levelNames[i]); + + sprintf(path, "../data/%s.PKD", levelNames[i]); + levels[i]->convertGBA(path); + + //levels[i]->convert3DO(levelNames[i]); + }*/ + +// saveBitmap("pal.bmp", (uint8*)palDump, 256, 32, 32); + +// convertTracks3DO("C:\\Projects\\OpenLara\\src\\platform\\gba\\packer\\tracks\\orig\\*", "C:\\Projects\\OpenLara\\src\\platform\\3do\\tracks"); + + +/* + WAD* wad = new WAD(); + + int32 size = 0; + int32 maxItems = 0; + + int32 maxVertices = 0; + + for (int32 i = 0; i < MAX_LEVELS; i++) + { + LevelPC* level = levels[i]; + + wad->addLevel(i, *level); + + for (int32 j = 0; j < level->roomsCount; j++) + { + WAD::LevelWAD::Room &room = wad->levels[i]->rooms[j]; + + size += room.vCount * sizeof(WAD::LevelWAD::Room::Vertex); + size += room.qCount * sizeof(WAD::LevelWAD::Room::Quad); + size += room.tCount * sizeof(WAD::LevelWAD::Room::Triangle); + size += room.sCount * sizeof(WAD::LevelWAD::Room::Sprite); + size += room.pCount * sizeof(WAD::LevelWAD::Room::Portal); + size += room.xSectors * room.zSectors * sizeof(room.sectors[0]); + } + + size += wad->levels[i]->floorsCount * sizeof(WAD::LevelWAD::FloorData); + size += wad->levels[i]->boxesCount * sizeof(WAD::LevelWAD::Box); + size += wad->levels[i]->overlapsCount * sizeof(WAD::LevelWAD::Overlap); + size += wad->levels[i]->boxesCount * sizeof(uint16) * 3 * 2; // zones + size += wad->levels[i]->itemsCount * sizeof(WAD::LevelWAD::Item); + + size += level->frameDataSize * 2; + } + + int32 nodes = 0; + for (int32 i = 0; i < wad->nodeLists.count; i++) + { + nodes += wad->nodeLists[i]->count; + } + + printf("roomsSize: %d bytes %d\n", size, nodes); + + wad->packTiles(); + + printf("tiles: %d (%d bytes)\n", wad->tiles.count, wad->tiles.count * 256 * 256); + +/* + int32 texSize = 0; + for (int32 i = 0; i < wad.textures.count; i++) + { + texSize += wad.textures[i]->width * wad.textures[i]->height; + + char texName[64]; + sprintf(texName, "textures/%d.bmp", i); + wad.textures[i]->save(texName); + } +*/ + +#endif + + +#endif \ No newline at end of file diff --git a/src/platform/gba/packer/out_GBA.h b/src/platform/gba/packer/out_GBA.h new file mode 100644 index 00000000..caacd889 --- /dev/null +++ b/src/platform/gba/packer/out_GBA.h @@ -0,0 +1,2478 @@ +#ifndef H_OUT_GBA +#define H_OUT_GBA + +#include "common.h" +#include "TR1_PC.h" +#include "TR1_PSX.h" + +struct out_GBA +{ + enum FaceType { // 2 high bits of face flags + FACE_TYPE_SHADOW, + FACE_TYPE_F, + FACE_TYPE_FT, + FACE_TYPE_FTA + }; + + enum { + FACE_TYPE_SHIFT = 14, + FACE_TRIANGLE = (1 << 13) + }; + + struct Header + { + uint32 magic; + + uint16 tilesCount; + uint16 roomsCount; + uint16 modelsCount; + uint16 meshesCount; + uint16 staticMeshesCount; + uint16 spriteSequencesCount; + uint16 soundSourcesCount; + uint16 boxesCount; + uint16 texturesCount; + uint16 spritesCount; + uint16 itemsCount; + uint16 camerasCount; + uint16 cameraFramesCount; + uint16 soundOffsetsCount; + + uint32 palette; + uint32 lightmap; + uint32 tiles; + uint32 rooms; + uint32 floors; + uint32 meshData; + uint32 meshOffsets; + uint32 anims; + uint32 states; + uint32 ranges; + uint32 commands; + uint32 nodes; + uint32 frameData; + uint32 models; + uint32 staticMeshes; + uint32 objectTextures; + uint32 spriteTextures; + uint32 spriteSequences; + uint32 cameras; + uint32 soundSources; + uint32 boxes; + uint32 overlaps; + uint32 zones[2][3]; + uint32 animTexData; + uint32 items; + uint32 cameraFrames; + uint32 soundMap; + uint32 soundInfos; + uint32 soundData; + uint32 soundOffsets; + + void write(FileStream &f) const + { + f.write(magic); + f.write(tilesCount); + f.write(roomsCount); + f.write(modelsCount); + f.write(meshesCount); + f.write(staticMeshesCount); + f.write(spriteSequencesCount); + f.write(soundSourcesCount); + f.write(boxesCount); + f.write(texturesCount); + f.write(spritesCount); + f.write(itemsCount); + f.write(camerasCount); + f.write(cameraFramesCount); + f.write(soundOffsetsCount); + + f.write(palette); + f.write(lightmap); + f.write(tiles); + f.write(rooms); + f.write(floors); + f.write(meshData); + f.write(meshOffsets); + f.write(anims); + f.write(states); + f.write(ranges); + f.write(commands); + f.write(nodes); + f.write(frameData); + f.write(models); + f.write(staticMeshes); + f.write(objectTextures); + f.write(spriteTextures); + f.write(spriteSequences); + f.write(cameras); + f.write(soundSources); + f.write(boxes); + f.write(overlaps); + + for (int32 i = 0; i < 2; i++) + { + for (int32 j = 0; j < 3; j++) + { + f.write(zones[i][j]); + } + } + + f.write(animTexData); + f.write(items); + f.write(cameraFrames); + f.write(soundMap); + f.write(soundInfos); + f.write(soundData); + f.write(soundOffsets); + } + }; + + struct Info + { + int16 x; + int16 z; + int16 yBottom; + int16 yTop; + + uint16 quadsCount; + uint16 trianglesCount; + + uint16 verticesCount; + uint16 spritesCount; + + uint8 portalsCount; + uint8 lightsCount; + uint8 meshesCount; + uint8 ambient; + + uint8 xSectors; + uint8 zSectors; + uint8 alternateRoom; + uint8 flags; + + uint32 quads; + uint32 triangles; + uint32 vertices; + uint32 sprites; + uint32 portals; + uint32 sectors; + uint32 lights; + uint32 meshes; + + void write(FileStream &f) const + { + f.write(x); + f.write(z); + f.write(yBottom); + f.write(yTop); + f.write(quadsCount); + f.write(trianglesCount); + f.write(verticesCount); + f.write(spritesCount); + f.write(portalsCount); + f.write(lightsCount); + f.write(meshesCount); + f.write(ambient); + f.write(xSectors); + f.write(zSectors); + f.write(alternateRoom); + f.write(flags); + f.write(quads); + f.write(triangles); + f.write(vertices); + f.write(sprites); + f.write(portals); + f.write(sectors); + f.write(lights); + f.write(meshes); + } + }; + + struct RoomVertex + { + uint8 x, y, z, g; + + void write(FileStream &f) const + { + f.write(x); + f.write(y); + f.write(z); + f.write(g); + } + }; + + struct RoomQuad + { + uint16 flags; + uint16 indices[4]; + + void write(FileStream &f) const + { + f.write(flags); + f.write(indices[0]); + f.write(indices[1]); + f.write(indices[2]); + f.write(indices[3]); + } + }; + + struct RoomTriangle + { + uint16 flags; + uint16 indices[3]; + + void write(FileStream &f) const + { + f.write(flags); + f.write(indices[0]); + f.write(indices[1]); + f.write(indices[2]); + } + }; + + struct MeshQuad + { + uint16 flags; + uint8 indices[4]; + + void write(FileStream &f) const + { + f.write(flags); + f.write(indices[0]); + f.write(indices[1]); + f.write(indices[2]); + f.write(indices[3]); + } + }; + + struct MeshTriangle + { + uint16 flags; + uint8 indices[4]; + + void write(FileStream &f) const + { + f.write(flags); + f.write(indices[0]); + f.write(indices[1]); + f.write(indices[2]); + f.write(indices[3]); + } + }; + + struct AnimState + { + uint8 state; + uint8 rangesCount; + uint16 rangesStart; + + void write(FileStream &f) const + { + f.write(state); + f.write(rangesCount); + f.write(rangesStart); + } + }; + + struct Node + { + vec3s pos; + uint16 flags; + + void write(FileStream &f) + { + f.write(pos.x); + f.write(pos.y); + f.write(pos.z); + f.write(flags); + } + }; + + struct Sprite + { + int16 x, y, z; + uint8 g; + uint8 index; + + void write(FileStream &f) const + { + f.write(x); + f.write(y); + f.write(z); + f.write(g); + f.write(index); + } + }; + + struct Portal + { + uint8 roomIndex; + uint8 normalIndex; + uint16 x, y, z; + uint8 a, b; + + Portal(const TR1_PC::Room::Portal &portal) + { + + const vec3s &v0 = portal.vertices[0]; + const vec3s &v1 = portal.vertices[1]; + const vec3s &v2 = portal.vertices[2]; + const vec3s &v3 = portal.vertices[3]; + const vec3s &n = portal.normal; + + normalIndex = 0xFF; + + if (n.x == -1) normalIndex = 0; + if (n.x == 1) normalIndex = 1; + if (n.y == -1) normalIndex = 2; + if (n.y == 1) normalIndex = 3; + if (n.z == -1) normalIndex = 4; + if (n.z == 1) normalIndex = 5; + + ASSERT(normalIndex != 0xFF); + + int32 minX = MIN(MIN(MIN(v0.x, v1.x), v2.x), v3.x); + int32 minY = MIN(MIN(MIN(v0.y, v1.y), v2.y), v3.y); + int32 minZ = MIN(MIN(MIN(v0.z, v1.z), v2.z), v3.z); + int32 maxX = MAX(MAX(MAX(v0.x, v1.x), v2.x), v3.x); + int32 maxY = MAX(MAX(MAX(v0.y, v1.y), v2.y), v3.y); + int32 maxZ = MAX(MAX(MAX(v0.z, v1.z), v2.z), v3.z); + + x = (maxX + minX) / 2; + y = (maxY + minY) / 2; + z = (maxZ + minZ) / 2; + + int32 sx = (maxX - minX) / 256 >> 1; + int32 sy = (maxY - minY) / 256 >> 1; + int32 sz = (maxZ - minZ) / 256 >> 1; + + switch (normalIndex / 2) + { + case 0 : // x + a = sy; + b = sz; + break; + case 1 : // y + a = sx; + b = sz; + break; + case 2 : // z + a = sx; + b = sy; + break; + } + } + + void write(FileStream &f) const + { + f.write(roomIndex); + f.write(normalIndex); + f.write(x); + f.write(y); + f.write(z); + f.write(a); + f.write(b); + } + }; + + struct Light + { + vec3s pos; + uint8 radius; + uint8 intensity; + + void write(FileStream &f) const + { + f.write(pos.x); + f.write(pos.y); + f.write(pos.z); + f.write(radius); + f.write(intensity); + } + }; + + struct RoomMesh + { + vec3s pos; + uint8 intensity; + uint8 flags; + + void write(FileStream &f) const + { + uint32 xy = (pos.x << 16) | uint16(pos.y); + uint32 zf = (pos.z << 16) | (intensity << 8) | flags; + f.write(xy); + f.write(zf); + } + }; + + struct Sphere16 + { + int16 x, y, z, radius; + + void write(FileStream &f) const + { + uint32 xy = (x << 16) | uint16(y); + uint32 zr = (z << 16) | uint16(radius); + f.write(xy); + f.write(zr); + } + }; + + struct MinMax + { + int16 minX, maxX; + int16 minY, maxY; + int16 minZ, maxZ; + + void write(FileStream &f) const + { + f.write(minX); f.write(maxX); + f.write(minY); f.write(maxY); + f.write(minZ); f.write(maxZ); + } + }; + + struct StaticMesh + { + uint16 id; + uint16 meshIndex; + uint32 flags; + //Sphere16 vs; + MinMax vbox; + MinMax cbox; + + void write(FileStream &f) const + { + Sphere16 vs; + vs.x = (vbox.maxX + vbox.minX) >> 1; + vs.y = (vbox.maxY + vbox.minY) >> 1; + vs.z = (vbox.maxZ + vbox.minZ) >> 1; + + int32 dx = (vbox.maxX - vbox.minX) >> 1; + int32 dy = (vbox.maxY - vbox.minY) >> 1; + int32 dz = (vbox.maxZ - vbox.minZ) >> 1; + + vs.radius = int32(sqrtf(float(dx * dx + dy * dy + dz * dz))); + + f.write(id); + f.write(meshIndex); + f.write(flags); + vs.write(f); + vbox.write(f); + cbox.write(f); + } + }; + + struct ObjectTexture + { + uint32 tile; + uint32 uv01; + uint32 uv23; + + void write(FileStream &f) const + { + f.write(tile); + f.write(uv01); + f.write(uv23); + } + }; + + struct SpriteTexture + { + uint32 tile; + uint32 uwvh; + int16 l, t, r, b; + + void write(FileStream &f) const + { + f.write(tile); + f.write(uwvh); + f.write(l); + f.write(t); + f.write(r); + f.write(b); + } + }; + + struct Box + { + uint8 minZ, maxZ; + uint8 minX, maxX; + int16 floor; + int16 overlap; + + void write(FileStream &f) const + { + f.write(minZ); + f.write(maxZ); + f.write(minX); + f.write(maxX); + f.write(floor); + f.write(overlap); + } + }; + + struct Item + { + uint8 type; + uint8 roomIndex; + vec3s pos; + int16 intensity; + uint16 flags; + + void write(FileStream &f) const + { + f.write(type); + f.write(roomIndex); + f.write(pos.x); + f.write(pos.y); + f.write(pos.z); + f.write(intensity); + f.write(flags); + } + }; + + struct Mesh + { + int32 offset; + TR1_PC* level; + vec3s center; + int16 radius; + uint16 intensity; + int16 vCount; + int16 rCount; + int16 crCount; + int16 tCount; + int16 ctCount; + const vec3s* vertices; + const TR1_PC::Quad* rFaces; + const TR1_PC::Quad* crFaces; + const TR1_PC::Triangle* tFaces; + const TR1_PC::Triangle* ctFaces; + + Mesh(TR1_PC* level, const uint8* ptr) + { + this->level = level; + + center = *(vec3s*)ptr; ptr += sizeof(center); + radius = *(int16*)ptr; ptr += sizeof(radius); + uint16 flags = *(uint16*)ptr; ptr += sizeof(flags); + + vCount = *(int16*)ptr; ptr += 2; + vertices = (vec3s*)ptr; + ptr += vCount * sizeof(vec3s); + + const uint16* vIntensity = NULL; + const vec3s* vNormal = NULL; + + int16 nCount = *(int16*)ptr; ptr += 2; + //const int16* normals = (int16*)ptr; + if (nCount > 0) { // normals + vNormal = (vec3s*)ptr; + ptr += nCount * 3 * sizeof(int16); + } else { // intensity + vIntensity = (uint16*)ptr; + ptr += vCount * sizeof(uint16); + } + + rCount = *(int16*)ptr; ptr += 2; + rFaces = (TR1_PC::Quad*)ptr; ptr += rCount * sizeof(TR1_PC::Quad); + + tCount = *(int16*)ptr; ptr += 2; + tFaces = (TR1_PC::Triangle*)ptr; ptr += tCount * sizeof(TR1_PC::Triangle); + + crCount = *(int16*)ptr; ptr += 2; + crFaces = (TR1_PC::Quad*)ptr; ptr += crCount * sizeof(TR1_PC::Quad); + + ctCount = *(int16*)ptr; ptr += 2; + ctFaces = (TR1_PC::Triangle*)ptr; ptr += ctCount * sizeof(TR1_PC::Triangle); + + intensity = 0; + + if (vIntensity) + { + uint32 sum = 0; + for (int32 i = 0; i < vCount; i++) + { + sum += vIntensity[i]; + } + intensity = sum / vCount; + } + } + + void write(FileStream &f) const + { + ASSERT(vCount < 256); + + f.write(center.x); + f.write(center.y); + f.write(center.z); + f.write(radius); + f.write(intensity); + f.write(vCount); + f.write(int16(rCount + crCount)); + f.write(int16(tCount + ctCount)); + f.write(int16(0)); + f.write(int16(0)); + + for (int32 j = 0; j < vCount; j++) + { + struct MeshVertexGBA { + int16 x, y, z; + } v; + + v.x = vertices[j].x; + v.y = vertices[j].y; + v.z = vertices[j].z; + + f.write(v.x); + f.write(v.y); + f.write(v.z); + } + + for (int32 j = 0; j < rCount; j++) + { + TR1_PC::Quad q = rFaces[j]; + + MeshQuad comp; + comp.indices[0] = uint8(q.indices[0]); + comp.indices[1] = uint8(q.indices[1]); + comp.indices[2] = uint8(q.indices[2]); + comp.indices[3] = uint8(q.indices[3]); + comp.flags = q.flags & FACE_TEXTURE; + if (level->objectTextures[comp.flags].attribute & TEX_ATTR_AKILL) { + comp.flags |= (FACE_TYPE_FTA << FACE_TYPE_SHIFT); + } else { + comp.flags |= (FACE_TYPE_FT << FACE_TYPE_SHIFT); + } + + comp.write(f); + } + + for (int32 j = 0; j < crCount; j++) + { + TR1_PC::Quad q = crFaces[j]; + + MeshQuad comp; + comp.indices[0] = uint8(q.indices[0]); + comp.indices[1] = uint8(q.indices[1]); + comp.indices[2] = uint8(q.indices[2]); + comp.indices[3] = uint8(q.indices[3]); + comp.flags = q.flags & FACE_TEXTURE; + comp.flags |= (FACE_TYPE_F << FACE_TYPE_SHIFT); + + comp.write(f); + } + + for (int32 j = 0; j < tCount; j++) + { + TR1_PC::Triangle t = tFaces[j]; + + MeshTriangle comp; + comp.indices[0] = uint8(t.indices[0]); + comp.indices[1] = uint8(t.indices[1]); + comp.indices[2] = uint8(t.indices[2]); + comp.indices[3] = 0; + comp.flags = t.flags & FACE_TEXTURE; + if (level->objectTextures[comp.flags].attribute & TEX_ATTR_AKILL) { + comp.flags |= (FACE_TYPE_FTA << FACE_TYPE_SHIFT); + } else { + comp.flags |= (FACE_TYPE_FT << FACE_TYPE_SHIFT); + } + comp.flags |= FACE_TRIANGLE; + + comp.write(f); + } + + for (int32 j = 0; j < ctCount; j++) + { + TR1_PC::Triangle t = ctFaces[j]; + + MeshTriangle comp; + comp.indices[0] = uint8(t.indices[0]); + comp.indices[1] = uint8(t.indices[1]); + comp.indices[2] = uint8(t.indices[2]); + comp.indices[3] = 0; + comp.flags = t.flags & FACE_TEXTURE; + comp.flags |= (FACE_TYPE_F << FACE_TYPE_SHIFT); + comp.flags |= FACE_TRIANGLE; + + comp.write(f); + } + } + }; + + struct Model + { + TR1_PC* level; + int32 objTexIndex; + uint8 type; + uint8 count; + uint16 start; + uint16 nodeIndex; + uint16 animIndex; + + void init(TR1_PC* level, const TR1_PC::Model &model) + { + this->level = level; + } + + void write(FileStream &f) const + { + f.write(type); + f.write(count); + f.write(start); + f.write(nodeIndex); + f.write(animIndex); + } + }; + + struct Animation { + TR1_PC::Animation anim; + + }; + + struct AnimFrames + { + const uint8* data; + uint32 size; + + AnimFrames(const uint8* startPtr, const uint8* endPtr) + { + data = startPtr; + size = uint32(endPtr - startPtr); + } + + void write(FileStream &f) const + { + f.write(data, size); + } + }; + + struct Texture + { + const TR1_PC* level; + TR1_PC::ObjectTexture* tex; + TR1_PC::SpriteTexture* spr; + Texture* link; + int32 uid; + int32 width; + int32 height; + int32 akill; + uint8* indices; + uint32 indexSum; + + Texture(const TR1_PC* level, TR1_PC::ObjectTexture* tex, int32 uid) + { + this->level = level; + this->tex = tex; + this->spr = NULL; + this->link = NULL; + this->uid = uid; + + TR1_PC::Tile *tile = level->tiles + (tex->tile & 0x3FFF); + + int32 minX = MIN(MIN(tex->x0, tex->x1), tex->x2); + int32 minY = MIN(MIN(tex->y0, tex->y1), tex->y2); + int32 maxX = MAX(MAX(tex->x0, tex->x1), tex->x2); + int32 maxY = MAX(MAX(tex->y0, tex->y1), tex->y2); + + if (tex->isQuad) + { + minX = MIN(minX, tex->x3); + minY = MIN(minY, tex->y3); + maxX = MAX(maxX, tex->x3); + maxY = MAX(maxY, tex->y3); + } + + width = maxX - minX + 1; + height = maxY - minY + 1; + akill = 0; + + bool transp = (tex->attribute & TEX_ATTR_AKILL); + + indices = new uint8[width * height]; + const uint8* src = tile->indices + 256 * minY; + uint8* dst = indices; + + for (int32 y = minY; y <= maxY; y++) + { + for (int32 x = minX; x <= maxX; x++) + { + uint8 index = src[x]; + *dst++ = index; + + if ((index == 0) && transp) { + akill++; + } + } + src += 256; + } + } + + Texture(const TR1_PC* level, TR1_PC::SpriteTexture* spr, int32 uid) + { + this->level = level; + this->uid = uid; + this->tex = NULL; + this->spr = spr; + this->link = NULL; + + TR1_PC::Tile *tile = level->tiles + (spr->tile & 0x3FF); + + int32 minX = spr->u; + int32 minY = spr->v; + int32 maxX = spr->u + (spr->w >> 8); + int32 maxY = spr->v + (spr->h >> 8); + + width = maxX - minX + 1; + height = maxY - minY + 1; + akill = 0; + indexSum = 0; + + indices = new uint8[width * height]; + const uint8* src = tile->indices + 256 * minY; + uint8* dst = indices; + + for (int32 y = minY; y <= maxY; y++) + { + for (int32 x = minX; x <= maxX; x++) + { + uint8 index = src[x]; + *dst++ = index; + + indexSum += index; + + if (index == 0) { + akill++; + } + } + src += 256; + } + } + + ~Texture() + { + delete[] indices; + } + + static int cmp(const Texture* a, const Texture* b) + { + int32 i = b->akill - a->akill; + + if (i == 0) + { + int32 max1 = MAX(a->width, a->height); + int32 max2 = MAX(b->width, b->height); + i = max2 - max1; + } + + if (i == 0) { + i = int32(a->level - b->level); + } + + if (i == 0) { + i = a->uid - b->uid; + } + + return i; + } + }; + + struct Tile24 + { + struct Node + { + Node* childs[2]; + Texture* tex; + int32 l, t, r, b; + + Node(short l, short t, short r, short b) : l(l), t(t), r(r), b(b), tex(NULL) { + childs[0] = childs[1] = NULL; + } + + ~Node() { + delete childs[0]; + delete childs[1]; + } + + Node* insert(Texture* tex) + { + if (childs[0] != NULL && childs[1] != NULL) + { + Node* node = childs[0]->insert(tex); + if (node != NULL) + return node; + return childs[1]->insert(tex); + } + + if (this->tex != NULL) + return NULL; + + int16 nw = r - l; + int16 nh = b - t; + int16 tw = tex->width; + int16 th = tex->height; + + if (nw < tw || nh < th) + return NULL; + + if (nw == tw && nh == th) { + this->tex = tex; + return this; + } + + int16 dx = nw - tw; + int16 dy = nh - th; + + if (dx > dy) { + childs[0] = new Node(l, t, l + tw, b); + childs[1] = new Node(l + tw, t, r, b); + } else { + childs[0] = new Node(l, t, r, t + th); + childs[1] = new Node(l, t + th, r, b); + } + + return childs[0]->insert(tex); + } + + void fill24(uint8* data) + { + if (childs[0] != NULL && childs[1] != NULL) + { + childs[0]->fill24(data); + childs[1]->fill24(data); + } + + if (!tex) { + return; + } + + // fill code + const TR1_PC::Palette &pal = tex->level->palette; + + uint8* index = tex->indices; + + uint8* dst = data + (t * 256 + l) * 3; + + for (int32 y = 0; y < tex->height; y++) + { + for (int32 x = 0; x < tex->width; x++) + { + if ((*index == 0) && tex->akill) { + dst[x * 3 + 0] = 255; + dst[x * 3 + 1] = 0; + dst[x * 3 + 2] = 255; + } else { + dst[x * 3 + 0] = pal.colors[*index * 3 + 2] << 2; + dst[x * 3 + 1] = pal.colors[*index * 3 + 1] << 2; + dst[x * 3 + 2] = pal.colors[*index * 3 + 0] << 2; + } + *index++; + } + dst += 256 * 3; + } + } + + void fill8(uint8* data) + { + if (childs[0] != NULL && childs[1] != NULL) + { + childs[0]->fill8(data); + childs[1]->fill8(data); + } + + if (!tex) { + return; + } + + // fill code + for (int32 y = 0; y < tex->height; y++) + { + memcpy(data + ((t + y) * 256 + l), tex->indices + y * tex->width, tex->width); + } + } + }; + + Node* root; + + Tile24() + { + root = new Node(0, 0, 256, 256); + } + + ~Tile24() + { + delete root; + } + + void fill24(uint8* data) + { + root->fill24(data); + } + + void fill8(uint8* data) + { + root->fill8(data); + } + + static int cmp(const Tile24* a, const Tile24* b) + { + return 0; + } + }; + + RoomVertex* roomVertices; + int32 roomVerticesCount; + + struct Remap { + int32 meshes[MAX_MESHES]; + int32 models[MAX_MODELS]; + int32 animFrames[MAX_ANIMS]; + int32 textures[MAX_TEXTURES]; + int32 sprites[MAX_TEXTURES]; + }; + + Remap remap[LVL_MAX]; + + Array meshes; + Array models; + Array animFrames; + + Array textures; + Array tiles; + + void packTiles(FileStream &f) + { + for (int32 i = 0; i < textures.count; i++) + { + Texture* tex = textures[i]; + + if (tex->link) + continue; + + bool placed = false; + + for (int32 j = 0; j < tiles.count; j++) + { + if (tiles[j]->root->insert(tex)) { + placed = true; + break; + } + } + + if (!placed) + { + Tile24* tile = new Tile24(); + tiles.add(tile); + if (!tile->root->insert(tex)) + { + printf("Can't pack texture %d x %d", tex->width, tex->height); + break; + } + } + } + + uint8* data = new uint8[256 * 256 * 3 * tiles.count]; + + // save bitmap (debug) + for (int32 i = 0; i < 256 * 256 * tiles.count; i++) + { + data[i * 3 + 0] = 255; + data[i * 3 + 1] = 0; + data[i * 3 + 2] = 255; + } + + for (int32 i = 0; i < tiles.count; i++) + { + tiles[i]->fill24(data + i * 256 * 256 * 3); + } + saveBitmap("tiles.bmp", data, 256, 256 * tiles.count); + + memset(data, 0, 256 * 256 * tiles.count); + + for (int32 i = 0; i < tiles.count; i++) + { + tiles[i]->fill8(data + i * 256 * 256); + } + + f.write(data, 256 * 256 * tiles.count); + + delete[] data; + } + + void addTextures(TR1_PC* level) + { + // check object textures usage + bool* used = new bool[level->objectTexturesCount]; + memset(used, 0, sizeof(bool) * level->objectTexturesCount); + + for (int32 i = 0; i < level->roomsCount; i++) + { + TR1_PC::Room &room = level->rooms[i]; + + for (int32 j = 0; j < room.qCount; j++) + { + used[room.quads[j].flags & FACE_TEXTURE] = true; + } + + for (int32 j = 0; j < room.tCount; j++) + { + used[room.triangles[j].flags & FACE_TEXTURE] = true; + } + } + + for (int32 i = 0; i < level->meshOffsetsCount; i++) + { + const uint8* ptr = (uint8*)level->meshData + level->meshOffsets[i]; + + Mesh mesh(level, ptr); + + for (int32 j = 0; j < mesh.rCount; j++) + { + used[mesh.rFaces[j].flags & FACE_TEXTURE] = true; + } + + for (int32 j = 0; j < mesh.tCount; j++) + { + used[mesh.tFaces[j].flags & FACE_TEXTURE] = true; + } + } + + static int32 texUID = 0; + + // textures + for (int32 i = 0; i < level->objectTexturesCount; i++) + { + if (!used[i]) continue; + Texture* tex = new Texture(level, level->objectTextures + i, texUID++); + textures.add(tex); + } + + // sprites + for (int32 i = 0; i < level->spriteTexturesCount; i++) + { + Texture* tex = new Texture(level, level->spriteTextures + i, texUID++); + textures.add(tex); + } + + delete[] used; + } + + void linkTextures() + { + textures.sort(); + + for (int32 i = 0; i < textures.count; i++) + { + Texture* src = textures[i]; + ASSERT(src->link == NULL); + + for (int32 j = 0; j < i; j++) + { + Texture* dst = textures[j]; + + if (dst->link) { + dst = dst->link; + ASSERT(dst->link == NULL); + } + + if (src->tex) // is ObjectTexture + { + if (src->width != dst->width || src->height > dst->height) + continue; + + int32 dy = dst->height - src->height; + + for (int32 y = 0; y <= dy; y++) + { + if (memcmp(dst->indices + dst->width * y, src->indices, src->width * src->height) == 0) + { + src->link = dst; + break; + } + } + } + else // is SpriteTexture + { + if (src->width != dst->width || src->height != dst->height || src->indexSum != dst->indexSum) + continue; + + if (memcmp(dst->indices, src->indices, src->width * src->height) == 0) + { + src->link = dst; + break; + } + } + } + } + + //remap.textures[i] = textures.add(tex); + //remap.sprites[i] = textures.add(tex); + } + + int32 addRoomVertex(int32 yOffset, const TR1_PC::Room::Vertex &v) + { + RoomVertex comp; + int32 px = v.pos.x >> 10; + int32 py = (v.pos.y - yOffset) >> 8; + int32 pz = v.pos.z >> 10; + + ASSERT(py >= 0); + ASSERT(px < 32); + ASSERT(py < 64); + ASSERT(pz < 32); + + comp.x = px; + comp.y = py; + comp.z = pz; + comp.g = v.lighting >> 5; + + for (int32 i = 0; i < roomVerticesCount; i++) + { + if (memcmp(roomVertices + i, &comp, sizeof(comp)) == 0) + { + return i; + } + } + + ASSERT(roomVerticesCount < 0xFFFF); + + roomVertices[roomVerticesCount] = comp; + return roomVerticesCount++; + } + + int32 addMesh(Mesh* mesh) + { + for (int32 i = 0; i < meshes.count; i++) + { + const Mesh* m = meshes[i]; + if (m->center.x != mesh->center.x || + m->center.y != mesh->center.y || + m->center.z != mesh->center.z || + m->radius != mesh->radius || + m->intensity != mesh->intensity || // TODO move to static mesh to save 5k + m->vCount < mesh->vCount || + m->rCount < mesh->rCount || + m->crCount < mesh->crCount || + m->tCount < mesh->tCount || + m->ctCount < mesh->ctCount) continue; + + if (memcmp(m->vertices, mesh->vertices, mesh->vCount * sizeof(mesh->vertices[0])) != 0) + continue; + + delete mesh; + + return i; + } + + return meshes.add(mesh); + } + + int32 addModel(const Model &model) + { + return 0; + } + + int32 addAnimFrames(AnimFrames* anim) + { + for (int32 i = 0; i < animFrames.count; i++) + { + const AnimFrames* af = animFrames[i]; + + if (af->size < anim->size) + continue; + + if (memcmp(af->data, anim->data, anim->size) != 0) + continue; + + delete anim; + + return i; + } + + return animFrames.add(anim); + } + + void fixObjectTexture(ObjectTexture &tex, int32 idx) + { + uint32 uv0 = tex.uv01 & 0xFF00FF00; + uint32 uv1 = (tex.uv01 << 8) & 0xFF00FF00; + uint32 uv2 = tex.uv23 & 0xFF00FF00; + uint32 uv3 = (tex.uv23 << 8) & 0xFF00FF00; + + fixTexCoord(uv0, uv1); + fixTexCoord(uv0, uv3); + fixTexCoord(uv1, uv2); + + tex.uv01 = uv0 | (uv1 >> 8); + tex.uv23 = uv2 | (uv3 >> 8); + } + + uint32 writePalette(FileStream &f, TR1_PC* level) + { + uint32 offset = f.align4(); + + uint16 pal[256]; + + for (int32 i = 0; i < 256; i++) + { + uint8 r = level->palette.colors[i * 3 + 0]; + uint8 g = level->palette.colors[i * 3 + 1]; + uint8 b = level->palette.colors[i * 3 + 2]; + + pal[i] = (r >> 1) | ((g >> 1) << 5) | ((b >> 1) << 10); + } + + pal[0] = 0; + + f.write(pal, 256); + + return offset; + } + + uint32 writeLightmap(FileStream &f, TR1_PC* level) + { + uint32 offset = f.align4(); + + for (int32 i = 0; i < 32; i++) { + level->lightmap[i * 256] = 0; + } + f.write(level->lightmap, 32 * 256); + + return offset; + } + + uint32 writeRooms(FileStream &f, TR1_PC* level) + { + uint32 offset = f.align4(); + + f.seek(sizeof(Info) * level->roomsCount); + + Info infos[255]; + + for (int32 i = 0; i < level->roomsCount; i++) + { + const TR1_PC::Room* room = level->rooms + i; + + Info &info = infos[i]; + + ASSERT(room->info.x % 256 == 0); + ASSERT(room->info.z % 256 == 0); + ASSERT(room->info.yBottom >= -32768 && room->info.yBottom <= 32767); + ASSERT(room->info.yTop >= -32768 && room->info.yTop <= 32767); + info.x = room->info.x / 256; + info.z = room->info.z / 256; + info.yBottom = -32768; + info.yTop = 32767; + + for (int32 j = 0; j < room->vCount; j++) + { + TR1_PC::Room::Vertex &v = room->vertices[j]; + if (v.pos.y < info.yTop) { + info.yTop = v.pos.y; + } + if (v.pos.y > info.yBottom) { + info.yBottom = v.pos.y; + } + } + + info.spritesCount = room->sCount; + info.quadsCount = room->qCount; + info.trianglesCount = room->tCount; + info.portalsCount = uint8(room->pCount); + info.lightsCount = uint8(room->lCount); + info.meshesCount = uint8(room->mCount); + info.ambient = room->ambient >> 5; + info.xSectors = uint8(room->xSectors); + info.zSectors = uint8(room->zSectors); + info.alternateRoom = uint8(room->alternateRoom); + + info.flags = 0; + if (room->flags & 1) info.flags |= 1; + if (room->flags & 256) info.flags |= 2; + + ASSERT((room->flags & ~257) == 0); + ASSERT(info.portalsCount == room->pCount); + ASSERT(info.lightsCount == room->lCount); + ASSERT(info.meshesCount == room->mCount); + ASSERT(info.xSectors == room->xSectors); + ASSERT(info.zSectors == room->zSectors); + + roomVerticesCount = 0; + + info.quads = f.align4(); + for (int32 i = 0; i < room->qCount; i++) + { + TR1_PC::Quad q = room->quads[i]; + RoomQuad comp; + comp.indices[0] = addRoomVertex(info.yTop, room->vertices[q.indices[0]]); + comp.indices[1] = addRoomVertex(info.yTop, room->vertices[q.indices[1]]); + comp.indices[2] = addRoomVertex(info.yTop, room->vertices[q.indices[2]]); + comp.indices[3] = addRoomVertex(info.yTop, room->vertices[q.indices[3]]); + + comp.flags = q.flags & FACE_TEXTURE; + if (level->objectTextures[comp.flags].attribute & TEX_ATTR_AKILL) { + comp.flags |= (FACE_TYPE_FTA << FACE_TYPE_SHIFT); + } else { + comp.flags |= (FACE_TYPE_FT << FACE_TYPE_SHIFT); + } + + comp.write(f); + } + + info.triangles = f.align4(); + for (int32 i = 0; i < room->tCount; i++) + { + TR1_PC::Triangle t = room->triangles[i]; + RoomTriangle comp; + + comp.indices[0] = addRoomVertex(info.yTop, room->vertices[t.indices[0]]); + comp.indices[1] = addRoomVertex(info.yTop, room->vertices[t.indices[1]]); + comp.indices[2] = addRoomVertex(info.yTop, room->vertices[t.indices[2]]); + + comp.flags = t.flags & FACE_TEXTURE; + if (level->objectTextures[comp.flags].attribute & TEX_ATTR_AKILL) { + comp.flags |= (FACE_TYPE_FTA << FACE_TYPE_SHIFT); + } else { + comp.flags |= (FACE_TYPE_FT << FACE_TYPE_SHIFT); + } + comp.flags |= FACE_TRIANGLE; + + comp.write(f); + } + + info.vertices = f.align4(); + info.verticesCount = roomVerticesCount; + for (int32 i = 0; i < roomVerticesCount; i++) + { + roomVertices[i].write(f); + } + + info.sprites = f.align4(); + for (int32 i = 0; i < room->sCount; i++) + { + const TR1_PC::Room::Sprite* sprite = room->sprites + i; + const TR1_PC::Room::Vertex* v = room->vertices + sprite->index; + + Sprite comp; + comp.x = v->pos.x; + comp.y = v->pos.y; + comp.z = v->pos.z; + comp.g = v->lighting >> 5; + comp.index = uint8(sprite->texture); + + ASSERT(sprite->texture <= 255); + + comp.write(f); + } + + info.portals = f.align4(); + #if 0 // -72k + for (int32 i = 0; i < room->pCount; i++) + { + Portal p = room->portals[i]; + p.write(f); + } + #else + f.writeObj(room->portals, room->pCount); + #endif + + info.sectors = f.align4(); + f.writeObj(room->sectors, room->zSectors * room->xSectors); + + info.lights = f.align4(); + for (int32 i = 0; i < room->lCount; i++) + { + const TR1_PC::Room::Light* light = room->lights + i; + + Light comp; + comp.pos.x = light->pos.x - room->info.x; + comp.pos.y = light->pos.y; + comp.pos.z = light->pos.z - room->info.z; + comp.radius = light->radius >> 8; + comp.intensity = light->intensity >> 5; + + comp.write(f); + } + + info.meshes = f.align4(); + for (int32 i = 0; i < room->mCount; i++) + { + const TR1_PC::Room::Mesh* mesh = room->meshes + i; + + RoomMesh comp; + comp.pos.x = mesh->pos.x - room->info.x; + comp.pos.y = mesh->pos.y; + comp.pos.z = mesh->pos.z - room->info.z; + comp.intensity = mesh->intensity >> 5; + comp.flags = ((mesh->angleY / 0x4000 + 2) << 6) | mesh->id; + + ASSERT(mesh->id <= 63); + ASSERT(mesh->angleY % 0x4000 == 0); + ASSERT(mesh->angleY / 0x4000 + 2 >= 0); + + comp.write(f); + } + } + + int32 pos = f.getPos(); + f.setPos(offset); + f.writeObj(infos, level->roomsCount); + f.setPos(pos); + + return offset; + } + + uint32 writeFloors(FileStream &f, TR1_PC* level) + { + uint32 offset = f.align4(); + + f.writeObj(level->floors, level->floorsCount); + + return offset; + } + + uint32 writeStaticMeshes(FileStream &f, TR1_PC* level) + { + uint32 offset = f.align4(); + + for (int32 i = 0; i < level->staticMeshesCount; i++) + { + const TR1_PC::StaticMesh* staticMesh = level->staticMeshes + i; + + StaticMesh comp; + comp.id = staticMesh->id; + comp.meshIndex = staticMesh->meshIndex; + comp.flags = staticMesh->flags; + + comp.vbox.minX = staticMesh->vbox.minX; + comp.vbox.maxX = staticMesh->vbox.maxX; + comp.vbox.minY = staticMesh->vbox.minY; + comp.vbox.maxY = staticMesh->vbox.maxY; + comp.vbox.minZ = staticMesh->vbox.minZ; + comp.vbox.maxZ = staticMesh->vbox.maxZ; + + comp.cbox.minX = staticMesh->cbox.minX; + comp.cbox.maxX = staticMesh->cbox.maxX; + comp.cbox.minY = staticMesh->cbox.minY; + comp.cbox.maxY = staticMesh->cbox.maxY; + comp.cbox.minZ = staticMesh->cbox.minZ; + comp.cbox.maxZ = staticMesh->cbox.maxZ; + + comp.write(f); + } + + return offset; + } + + uint32 writeCameras(FileStream &f, TR1_PC* level) + { + uint32 offset = f.align4(); + + f.writeObj(level->cameras, level->camerasCount); + + return offset; + } + + uint32 writeSoundSources(FileStream &f, TR1_PC* level) + { + uint32 offset = f.align4(); + + f.writeObj(level->soundSources, level->soundSourcesCount); + + return offset; + } + + uint32 writeBoxes(FileStream &f, TR1_PC* level) + { + uint32 offset = f.align4(); + + for (int32 i = 0; i < level->boxesCount; i++) + { + const TR1_PC::Box* box = level->boxes + i; + + Box comp; + comp.minX = box->minX / 1024; + comp.minZ = box->minZ / 1024; + comp.maxX = (box->maxX + 1) / 1024; + comp.maxZ = (box->maxZ + 1) / 1024; + comp.floor = box->floor; + comp.overlap = box->overlap; + + comp.write(f); + } + + return offset; + } + + uint32 writeOverlaps(FileStream &f, TR1_PC* level) + { + uint32 offset = f.align4(); + + f.write(level->overlaps, level->overlapsCount); + + return offset; + } + + void writeZones(FileStream &f, TR1_PC* level, uint32* zoneOffset) + { + for (int32 i = 0; i < 2; i++) + { + *zoneOffset++ = f.align4(); + f.write(level->zones[i].ground1, level->boxesCount); + + *zoneOffset++ = f.align4(); + f.write(level->zones[i].ground2, level->boxesCount); + + *zoneOffset++ = f.align4(); + f.write(level->zones[i].fly, level->boxesCount); + } + } + + uint32 writeAnimTex(FileStream &f, TR1_PC* level) + { + uint32 offset = f.align4(); + + f.write(level->animTexData, level->animTexDataSize); + + return offset; + } + + uint32 writeItems(FileStream &f, TR1_PC* level) + { + uint32 offset = f.align4(); + + for (int32 i = 0; i < level->itemsCount; i++) + { + const TR1_PC::Item* item = level->items + i; + const TR1_PC::Room* room = level->rooms + item->roomIndex; + + Item comp; + comp.type = uint8(item->type); + comp.roomIndex = uint8(item->roomIndex); + comp.pos.x = int16(item->pos.x - room->info.x); + comp.pos.y = int16(item->pos.y); + comp.pos.z = int16(item->pos.z - room->info.z); + comp.intensity = item->intensity < 0 ? 0 : (item->intensity >> 5); + comp.flags = item->flags | ((item->angleY / 0x4000 + 2) << 14); + + ASSERT((item->flags & ~(0x3F1F)) == 0); + + comp.write(f); + } + + return offset; + } + + uint32 writeCameraFrames(FileStream &f, TR1_PC* level) + { + uint32 offset = f.align4(); + + f.writeObj(level->cameraFrames, level->cameraFramesCount); + + return offset; + } + + void convertGBA(FileStream &f, TR1_PC* pc) + { + pc->fixHeadMask(); + + Header header; + f.seek(sizeof(Header)); // will be rewritten at the end + + header.magic = 0x20414247; + header.tilesCount = pc->tilesCount; + header.roomsCount = pc->roomsCount; + header.modelsCount = pc->modelsCount; + header.meshesCount = pc->meshOffsetsCount; + header.staticMeshesCount = pc->staticMeshesCount; + header.spriteSequencesCount = pc->spriteSequencesCount; + header.soundSourcesCount = pc->soundSourcesCount; + header.boxesCount = pc->boxesCount; + header.texturesCount = pc->objectTexturesCount; + header.spritesCount = pc->spriteTexturesCount; + header.itemsCount = pc->itemsCount; + header.camerasCount = pc->camerasCount; + header.cameraFramesCount = pc->cameraFramesCount; + header.soundOffsetsCount = pc->soundOffsetsCount; + + header.palette = writePalette(f, pc); + header.lightmap = writeLightmap(f, pc); + + header.tiles = f.align4(); + f.write((uint8*)pc->tiles, header.tilesCount * 256 * 256); + + header.rooms = writeRooms(f, pc); + header.floors = writeFloors(f, pc); + + header.meshData = f.align4(); + + int32 mOffsets[2048]; + for (int32 i = 0; i < 2048; i++) { + mOffsets[i] = -1; + } + + for (int32 i = 0; i < pc->meshOffsetsCount; i++) + { + if (mOffsets[i] != -1) + continue; + + mOffsets[i] = f.align4() - header.meshData; + + const uint8* ptr = (uint8*)pc->meshData + pc->meshOffsets[i]; + + Mesh* mesh = new Mesh(pc, ptr); + mesh->write(f); + delete[] mesh; + + for (int32 j = i + 1; j < pc->meshOffsetsCount; j++) + { + if (pc->meshOffsets[i] == pc->meshOffsets[j]) + { + mOffsets[j] = mOffsets[i]; + } + } + } + + header.meshOffsets = f.align4(); + f.write(mOffsets, pc->meshOffsetsCount); + + header.anims = f.align4(); + f.writeObj(pc->anims, pc->animsCount); + + header.states = f.align4(); + for (int32 i = 0; i < pc->statesCount; i++) + { + const TR1_PC::AnimState* state = pc->states + i; + + AnimState comp; + comp.state = uint8(state->state); + comp.rangesCount = uint8(state->rangesCount); + comp.rangesStart = state->rangesStart; + + comp.write(f); + } + + header.ranges = f.align4(); + f.writeObj(pc->ranges, pc->rangesCount); + + header.commands = f.align4(); + f.write(pc->commands, pc->commandsCount); + + header.nodes = f.align4(); + for (int32 i = 0; i < pc->nodesDataSize / 4; i++) + { + const TR1_PC::Node* node = (TR1_PC::Node*)(pc->nodesData + i * 4); + + ASSERT(node->pos.x > -32768); + ASSERT(node->pos.x < 32767); + ASSERT(node->pos.y > -32768); + ASSERT(node->pos.y < 32767); + ASSERT(node->pos.z > -32768); + ASSERT(node->pos.z < 32767); + ASSERT(node->flags < 0xFFFF); + + Node comp; + comp.flags = uint16(node->flags); + comp.pos.x = int16(node->pos.x); + comp.pos.y = int16(node->pos.y); + comp.pos.z = int16(node->pos.z); + + comp.write(f); + } + + header.frameData = f.align4(); + f.write(pc->frameData, pc->frameDataSize); + + header.models = f.align4(); + for (int32 i = 0; i < pc->modelsCount; i++) + { + const TR1_PC::Model* model = pc->models + i; + + Model comp; + comp.type = uint8(model->type); + comp.count = uint8(model->count); + comp.start = model->start; + comp.nodeIndex = model->nodeIndex / 4; + comp.animIndex = model->animIndex; + + comp.write(f); + } + + header.staticMeshes = writeStaticMeshes(f, pc); + + header.objectTextures = f.align4(); + for (int32 i = 0; i < pc->objectTexturesCount; i++) + { + const TR1_PC::ObjectTexture* objectTexture = pc->objectTextures + i; + + ObjectTexture comp; + comp.tile = (objectTexture->tile & 0x3FFF) << 16; + uint32 uv0 = ((objectTexture->uv0 << 16) | (objectTexture->uv0 >> 16)) & 0xFF00FF00; + uint32 uv1 = ((objectTexture->uv1 << 16) | (objectTexture->uv1 >> 16)) & 0xFF00FF00; + uint32 uv2 = ((objectTexture->uv2 << 16) | (objectTexture->uv2 >> 16)) & 0xFF00FF00; + uint32 uv3 = ((objectTexture->uv3 << 16) | (objectTexture->uv3 >> 16)) & 0xFF00FF00; + comp.uv01 = uv0 | (uv1 >> 8); + comp.uv23 = uv2 | (uv3 >> 8); + + // GBA rasterizer doesn't support UV deltas over 127 + fixObjectTexture(comp, i); + + comp.write(f); + } + + header.spriteTextures = f.align4(); + for (int32 i = 0; i < pc->spriteTexturesCount; i++) + { + const TR1_PC::SpriteTexture* spriteTexture = pc->spriteTextures + i; + + SpriteTexture comp; + comp.tile = spriteTexture->tile << 16; + uint32 u = spriteTexture->u; + uint32 v = spriteTexture->v; + uint32 w = (spriteTexture->w + 255) >> 8; + uint32 h = (spriteTexture->h + 255) >> 8; + comp.uwvh = (u << 24) | (w << 16) | (v << 8) | h; + comp.l = spriteTexture->l; + comp.t = spriteTexture->t; + comp.r = spriteTexture->r; + comp.b = spriteTexture->b; + + comp.write(f); + } + + header.spriteSequences = f.align4(); + f.writeObj(pc->spriteSequences, pc->spriteSequencesCount); + + header.cameras = writeCameras(f, pc); + header.soundSources = writeSoundSources(f, pc); + header.boxes = writeBoxes(f, pc); + header.overlaps = writeOverlaps(f, pc); + writeZones(f, pc, header.zones[0]); + header.animTexData = writeAnimTex(f, pc); + header.items = writeItems(f, pc); + header.cameraFrames = writeCameraFrames(f, pc); + + //f.writeArray(demoData, demoDataSize); + + for (int32 i = 0; i < pc->soundOffsetsCount; i++) + { + uint8* ptr = pc->soundData + pc->soundOffsets[i]; + int32 size = *(int32*)(ptr + 40); + uint8* src = ptr + 44; + uint8* dst = ptr; + + while ((dst - pc->soundData) % 4 != 0) { + dst++; + } + dst += 4; + + for (int32 j = 0; j < size; j++) + { + dst[j] = src[j]; + } + + while ((size % 4) != 0) + { + dst[size] = dst[size - 1]; + size++; + } + + dst -= 4; + *(int32*)dst = size; + + pc->soundOffsets[i] = uint32(dst - pc->soundData); + } + + header.soundMap = f.align4(); + f.write(pc->soundMap, 256); + + header.soundInfos = f.align4(); + f.writeObj(pc->soundInfo, pc->soundInfoCount); + + header.soundData = f.align4(); + f.write(pc->soundData, pc->soundDataSize); + + header.soundOffsets = f.align4(); + f.write(pc->soundOffsets, pc->soundOffsetsCount); + + f.setPos(0); + header.write(f); + } + + void convertTracks(FileStream &f, const char* from) + { + char buf[256]; + sprintf(buf, "%s/*.ima", from); + + WIN32_FIND_DATA fd; + HANDLE h = FindFirstFile(buf, &fd); + + if (h == INVALID_HANDLE_VALUE) + return; + + struct Track { + int32 size; + char* data; + }; + Track tracks[MAX_TRACKS]; + memset(tracks, 0, sizeof(tracks)); + + do + { + if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + const char* src = fd.cFileName; + char* dst = buf; + + while (*src) + { + if (*src >= '0' && *src <= '9') + { + *dst++ = *src; + } + src++; + } + *dst++ = 0; + + int32 index = atoi(buf); + + if (index != 0) + { + strcpy(buf, from); + strcat(buf, "/"); + strcat(buf, fd.cFileName); + + FILE* f = fopen(buf, "rb"); + + if (!f) + continue; + + fseek(f, 0, SEEK_END); + int32 size = ftell(f); + fseek(f, 0, SEEK_SET); + tracks[index].data = new char[size + 4]; + fread(tracks[index].data, 1, size, f); + fclose(f); + + tracks[index].size = ALIGN(*((int32*)tracks[index].data + 2), 4) - 4; + + ASSERT(tracks[index].size % 4 == 0); + } + } + } + while (FindNextFile(h, &fd)); + FindClose(h); + + int32 offset = MAX_TRACKS * (4 + 4); + + for (int32 i = 0; i < MAX_TRACKS; i++) + { + if (tracks[i].size == 0) { + int32 zero = 0; + f.write(zero); + } else { + f.write(offset); + } + f.write(tracks[i].size); + offset += tracks[i].size; + } + + for (int32 i = 0; i < MAX_TRACKS; i++) + { + if (tracks[i].size == 0) + continue; + f.write((uint8*)tracks[i].data + 16, tracks[i].size); + delete[] tracks[i].data; + } + } + + void convertScreen(const char* dir, const char* name, const TR1_PC::Palette &pal) + { + char path[256]; + sprintf(path, "screens/%s.bmp", name); + + int32 width, height, bpp; + uint32* data = (uint32*)loadBitmap(path, &width, &height, &bpp); + + ASSERT(data); + ASSERT(width == 240 && height == 160 && bpp == 32); + + uint32* uniqueColors = new uint32[width * height]; + int32 count = 0; + + for (int32 i = 0; i < width * height; i++) + { + uint32 c = data[i]; + + int32 index = -1; + + for (int32 j = 0; j < count; j++) + { + if (uniqueColors[j] == c) { + index = j; + break; + } + } + + if (index == -1) { + index = count++; + uniqueColors[index] = c; + } + + data[i] = index; + } + + for (int32 i = 0; i < count; i++) + { + uint32 c = uniqueColors[i]; + + int32 cr = (c >> 16) & 0xFF; + int32 cg = (c >> 8) & 0xFF; + int32 cb = c & 0xFF; + + float dist = 256 * 256 * 256; + int32 index = 0; + + for (int32 j = 0; j < 256; j++) + { + int32 r = pal.colors[j * 3 + 0] << 2; + int32 g = pal.colors[j * 3 + 1] << 2; + int32 b = pal.colors[j * 3 + 2] << 2; + + float d = sqrtf(float(SQR(cr - r) + SQR(cg - g) + SQR(cb - b))); + if (d < dist) + { + dist = d; + index = j; + } + } + + uniqueColors[i] = index; + } + + uint8* indices = new uint8[width * height]; + for (int32 i = 0; i < width * height; i++) + { + indices[i] = uniqueColors[data[i]]; + } + + sprintf(path, "%s/%s.SCR", dir, name); + + FILE *f = fopen(path, "wb"); + fwrite(indices, 1, width * height, f); + fclose(f); + + delete[] data; + delete[] uniqueColors; + delete[] indices; + } + + void convertWAD(FileStream &f, TR1_PC** pc, TR1_PSX** psx) + { + TR1_PC* level; + int32 id; + + #define LEVELS_LOOP() for (id = 0, level = pc[id]; id < LVL_MAX; id++, level = pc[id]) + + Header headers[LVL_MAX]; + f.seek(sizeof(headers)); + + // audio tracks + convertTracks(f, "tracks/conv"); + + // collect unique textures + LEVELS_LOOP() + { + addTextures(level); + } + + linkTextures(); + + packTiles(f); + + printf("textures: %d\n", textures.count); + + // collect unique meshes + LEVELS_LOOP() + { + ASSERT(level->meshOffsetsCount < MAX_MESHES); + + for (int32 i = 0; i < level->meshOffsetsCount; i++) + { + const uint8* ptr = (uint8*)level->meshData + level->meshOffsets[i]; + + Mesh* mesh = new Mesh(level, ptr); + remap[id].meshes[i] = addMesh(mesh); + } + } + + // collect unique animations + LEVELS_LOOP() + { + ASSERT(level->animsCount < MAX_ANIMS); + + for (int32 i = 0; i < level->modelsCount; i++) + { + const TR1_PC::Model &curModel = level->models[i]; + if (curModel.animIndex == 0xFFFF) + continue; + + uint8* startPtr = (uint8*)&level->frameData[level->anims[curModel.animIndex].frameOffset >> 1]; + uint8* endPtr = (uint8*)&level->frameData[level->frameDataSize]; + + for (int32 j = i + 1; j < level->modelsCount; j++) + { + const TR1_PC::Model &nextModel = level->models[j]; + if (nextModel.animIndex == 0xFFFF) + continue; + + endPtr = (uint8*)&level->frameData[level->anims[nextModel.animIndex].frameOffset >> 1]; + break; + } + + AnimFrames* af = new AnimFrames(startPtr, endPtr); + remap[id].animFrames[i] = addAnimFrames(af); + } + } + + // collect unique models + LEVELS_LOOP() + { + printf("anims: %d\n", level->animsCount); + + for (int32 i = 0; i < level->modelsCount; i++) + { + Model model; + model.init(level, level->models[i]); + remap[id].models[i] = addModel(model); + } + } + + // collect unique object textures + LEVELS_LOOP() + { + // TODO + } + + printf("Meshes: %d\n", meshes.count); + printf("Models: %d\n", models.count); + printf("Animations: %d\n", animFrames.count); + + // write meshes + //header.meshes = f.align4(); + for (int32 i = 0; i < meshes.count; i++) + { + meshes[i]->offset = f.align4(); + meshes[i]->write(f); + } + + // write models + //header.models = f.align4(); + for (int32 i = 0; i < models.count; i++) + { + models[i]->write(f); + } + + // write anims + //header.frames = f.align4(); + for (int32 i = 0; i < animFrames.count; i++) + { + animFrames[i]->write(f); + } + + // palette + LEVELS_LOOP() + { + headers[id].palette = writePalette(f, level); + } + + // lightmaps + LEVELS_LOOP() + { + headers[id].lightmap = writeLightmap(f, level); + } + + // rooms + LEVELS_LOOP() + { + headers[id].rooms = writeRooms(f, level); + } + + // floors data + LEVELS_LOOP() + { + headers[id].floors = writeFloors(f, level); + } + + // mesh offsets + LEVELS_LOOP() + { + headers[id].meshesCount = level->meshOffsetsCount; + headers[id].meshOffsets = f.align4(); + for (int32 i = 0; i < level->meshOffsetsCount; i++) + { + f.write(meshes[remap[id].meshes[i]]->offset); + } + } + +/* + LEVELS_LOOP() + { + //header.anims = f.align4(); + f.writeObj(level->anims, level->animsCount); + + //header.states = f.align4(); + for (int32 i = 0; i < level->statesCount; i++) + { + const TR1_PC::AnimState* state = level->states + i; + + AnimState comp; + comp.state = uint8(state->state); + comp.rangesCount = uint8(state->rangesCount); + comp.rangesStart = state->rangesStart; + + comp.write(f); + } + + //header.ranges = f.align4(); + f.writeObj(level->ranges, level->rangesCount); + + //header.commands = f.align4(); + f.write(level->commands, level->commandsCount); + + //header.nodes = f.align4(); + for (int32 i = 0; i < level->nodesDataSize / 4; i++) + { + const TR1_PC::Node* node = (TR1_PC::Node*)(level->nodesData + i * 4); + + ASSERT(node->pos.x > -32768); + ASSERT(node->pos.x < 32767); + ASSERT(node->pos.y > -32768); + ASSERT(node->pos.y < 32767); + ASSERT(node->pos.z > -32768); + ASSERT(node->pos.z < 32767); + ASSERT(node->flags < 0xFFFF); + + Node comp; + comp.flags = uint16(node->flags); + comp.pos.x = int16(node->pos.x); + comp.pos.y = int16(node->pos.y); + comp.pos.z = int16(node->pos.z); + + comp.write(f); + } + + //header.frameData = f.align4(); + f.write(level->frameData, level->frameDataSize); + + //header.models = f.align4(); + for (int32 i = 0; i < level->modelsCount; i++) + { + const TR1_PC::Model* model = level->models + i; + + Model comp; + comp.type = uint8(model->type); + comp.count = uint8(model->count); + comp.start = model->start; + comp.nodeIndex = model->nodeIndex / 4; + comp.animIndex = model->animIndex; + + comp.write(f); + } + } +*/ + LEVELS_LOOP() + { + headers[id].staticMeshes = writeStaticMeshes(f, level); + } + /* + header.objectTextures = f.align4(); + for (int32 i = 0; i < pc->objectTexturesCount; i++) + { + const TR1_PC::ObjectTexture* objectTexture = pc->objectTextures + i; + + ObjectTexture comp; + comp.attribute = objectTexture->attribute; + comp.tile = objectTexture->tile & 0x3FFF; + comp.uv0 = ((objectTexture->uv0 << 16) | (objectTexture->uv0 >> 16)) & 0xFF00FF00; + comp.uv1 = ((objectTexture->uv1 << 16) | (objectTexture->uv1 >> 16)) & 0xFF00FF00; + comp.uv2 = ((objectTexture->uv2 << 16) | (objectTexture->uv2 >> 16)) & 0xFF00FF00; + comp.uv3 = ((objectTexture->uv3 << 16) | (objectTexture->uv3 >> 16)) & 0xFF00FF00; + + // GBA rasterizer doesn't support UV deltas over 127, due performance reason, so we clamp it + fixObjectTexture(comp, i); + + comp.write(f); + } + + header.spriteTextures = f.align4(); + for (int32 i = 0; i < pc->spriteTexturesCount; i++) + { + const TR1_PC::SpriteTexture* spriteTexture = pc->spriteTextures + i; + + SpriteTexture comp; + comp.tile = spriteTexture->tile; + comp.u = spriteTexture->u; + comp.v = spriteTexture->v; + comp.w = spriteTexture->w >> 8; + comp.h = spriteTexture->h >> 8; + comp.l = spriteTexture->l; + comp.t = spriteTexture->t; + comp.r = spriteTexture->r; + comp.b = spriteTexture->b; + + comp.write(f); + } + + header.spriteSequences = f.align4(); + f.writeObj(pc->spriteSequences, pc->spriteSequencesCount); + */ + + // fixed cameras + LEVELS_LOOP() + { + headers[id].cameras = writeCameras(f, level); + } + + // static sounds + LEVELS_LOOP() + { + headers[id].soundSources = writeSoundSources(f, level); + } + + // boxes + LEVELS_LOOP() + { + headers[id].boxes = writeBoxes(f, level); + } + + // overlaps + LEVELS_LOOP() + { + headers[id].overlaps = writeOverlaps(f, level); + } + + // zones + LEVELS_LOOP() + { + writeZones(f, level, headers[id].zones[0]); + } + + // animated textures + LEVELS_LOOP() + { + headers[id].animTexData = writeAnimTex(f, level); + } + + // items + LEVELS_LOOP() + { + headers[id].items = writeItems(f, level); + } + + // animated camera frames + LEVELS_LOOP() + { + headers[id].cameraFrames = writeCameraFrames(f, level); + } + + /* + for (int32 i = 0; i < pc->soundOffsetsCount; i++) + { + uint8* ptr = pc->soundData + pc->soundOffsets[i]; + int32 size = *(int32*)(ptr + 40); + uint8* src = ptr + 44; + uint8* dst = ptr; + + while ((dst - pc->soundData) % 4 != 0) { + dst++; + } + dst += 4; + + for (int32 j = 0; j < size; j++) + { + dst[j] = src[j]; + } + + while ((size % 4) != 0) + { + dst[size] = dst[size - 1]; + size++; + } + + dst -= 4; + *(int32*)dst = size; + + pc->soundOffsets[i] = dst - pc->soundData; + } + + header.soundMap = f.align4(); + f.write(pc->soundMap, 256); + + header.soundInfos = f.align4(); + f.writeObj(pc->soundInfo, pc->soundInfoCount); + + header.soundData = f.align4(); + f.write(pc->soundData, pc->soundDataSize); + + header.soundOffsets = f.align4(); + f.write(pc->soundOffsets, pc->soundOffsetsCount); + + f.setPos(0); + header.write(f); + */ + f.writeRaw(headers); + } + + //#define GBA_WAD + + void process(const char* dir, TR1_PC** pc, TR1_PSX** psx) + { + roomVerticesCount = 0; + roomVertices = new RoomVertex[MAX_ROOM_VERTICES]; + + char buf[256]; + #ifdef GBA_WAD + sprintf(buf, "%s/TR1.WAD", dir); + FileStream f(buf, true); + + if (!f.isValid()) { + printf("can't save \"%s\"\n", buf); + return; + } + + convertWAD(f, pc, psx); + #else + for (int32 i = 0; i < LVL_MAX; i++) + { + sprintf(buf, "%s/%s.PKD", dir, levelNames[i]); + FileStream f(buf, true); + + if (!f.isValid()) { + printf("can't save \"%s\"\n", buf); + continue; + } + + convertGBA(f, pc[i]); + } + + // title screen + convertScreen(dir, "TITLE", pc[LVL_TR1_TITLE]->palette); + + // audio tracks + { + sprintf(buf, "%s/TRACKS.IMA", dir); + FileStream f(buf, true); + convertTracks(f, "tracks/conv_demo"); + } + #endif + + delete[] roomVertices; + } +}; + +#endif \ No newline at end of file diff --git a/src/platform/gba/packer/packer.sln b/src/platform/gba/packer/packer.sln new file mode 100644 index 00000000..ccebf56b --- /dev/null +++ b/src/platform/gba/packer/packer.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30907.101 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "packer", "packer.vcxproj", "{CED88939-237F-4F70-872C-704A51F7B8E5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CED88939-237F-4F70-872C-704A51F7B8E5}.Debug|x64.ActiveCfg = Debug|x64 + {CED88939-237F-4F70-872C-704A51F7B8E5}.Debug|x64.Build.0 = Debug|x64 + {CED88939-237F-4F70-872C-704A51F7B8E5}.Debug|x86.ActiveCfg = Debug|Win32 + {CED88939-237F-4F70-872C-704A51F7B8E5}.Debug|x86.Build.0 = Debug|Win32 + {CED88939-237F-4F70-872C-704A51F7B8E5}.Release|x64.ActiveCfg = Release|x64 + {CED88939-237F-4F70-872C-704A51F7B8E5}.Release|x64.Build.0 = Release|x64 + {CED88939-237F-4F70-872C-704A51F7B8E5}.Release|x86.ActiveCfg = Release|Win32 + {CED88939-237F-4F70-872C-704A51F7B8E5}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A0748762-B1DD-4990-8016-21AD34BA8501} + EndGlobalSection +EndGlobal diff --git a/src/platform/gba/packer/packer.vcxproj b/src/platform/gba/packer/packer.vcxproj new file mode 100644 index 00000000..cf4e576e --- /dev/null +++ b/src/platform/gba/packer/packer.vcxproj @@ -0,0 +1,168 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {ced88939-237f-4f70-872c-704a51f7b8e5} + packer + 10.0 + + + + Application + true + v142 + NotSet + + + Application + false + v142 + true + NotSet + + + Application + true + v142 + NotSet + + + Application + false + v142 + true + NotSet + + + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)\libimagequant;$(ProjectDir)\stb;$(IncludePath) + + + false + $(ProjectDir)\libimagequant;$(ProjectDir)\stb;$(IncludePath) + + + true + $(ProjectDir)\libimagequant;$(ProjectDir)\stb;$(IncludePath) + + + false + $(ProjectDir)\libimagequant;$(ProjectDir)\stb;$(IncludePath) + + + + Level3 + true + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + Console + true + + + + + Level3 + true + true + true + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + Console + true + + + + + Level3 + true + true + true + _CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/platform/gba/packer/packer.vcxproj.filters b/src/platform/gba/packer/packer.vcxproj.filters new file mode 100644 index 00000000..c82fa042 --- /dev/null +++ b/src/platform/gba/packer/packer.vcxproj.filters @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/platform/gba/profile.bat b/src/platform/gba/profile.bat deleted file mode 100644 index 8cf71a25..00000000 --- a/src/platform/gba/profile.bat +++ /dev/null @@ -1 +0,0 @@ -NO$GBA C:\Projects\OpenLara\src\platform\gba\OpenLara.elf \ No newline at end of file diff --git a/src/platform/gba/rasterizer.h b/src/platform/gba/rasterizer.h new file mode 100644 index 00000000..53ab6ac9 --- /dev/null +++ b/src/platform/gba/rasterizer.h @@ -0,0 +1,1188 @@ +#ifndef H_RASTERIZER_MODE4 +#define H_RASTERIZER_MODE4 + +#include "common.h" + +extern uint8 gLightmap[256 * 32]; +extern const uint8* gTile; + +#ifdef USE_ASM + extern "C" { + void rasterize_dummy(uint16* pixel, const VertexLink* L, const VertexLink* R); + void rasterizeS_asm(uint16* pixel, const VertexLink* L, const VertexLink* R); + void rasterizeF_asm(uint16* pixel, const VertexLink* L, const VertexLink* R); + //void rasterizeG_asm(uint16* pixel, const VertexLink* L, const VertexLink* R); + void rasterizeFT_asm(uint16* pixel, const VertexLink* L, const VertexLink* R); + void rasterizeGT_asm(uint16* pixel, const VertexLink* L, const VertexLink* R); + void rasterizeFTA_asm(uint16* pixel, const VertexLink* L, const VertexLink* R); + void rasterizeGTA_asm(uint16* pixel, const VertexLink* L, const VertexLink* R); + void rasterizeLineH_asm(uint16* pixel, const VertexLink* L, const VertexLink* R); + void rasterizeLineV_asm(uint16* pixel, const VertexLink* L, const VertexLink* R); + void rasterizeFillS_asm(uint16* pixel, const VertexLink* L, const VertexLink* R); + } + + #define rasterizeS rasterizeS_asm + #define rasterizeF rasterizeF_asm + //#define rasterizeG rasterizeG_asm + #define rasterizeFT rasterizeFT_asm + #define rasterizeGT rasterizeGT_asm + #define rasterizeFTA rasterizeFTA_asm + #define rasterizeGTA rasterizeGTA_asm + #define rasterizeSprite rasterizeSprite_c + #define rasterizeLineH rasterizeLineH_asm + #define rasterizeLineV rasterizeLineV_asm + #define rasterizeFillS rasterizeFillS_asm +#else + #define rasterizeS rasterizeS_c + #define rasterizeF rasterizeF_c + //#define rasterizeG rasterizeG_c + #define rasterizeFT rasterizeFT_c + #define rasterizeGT rasterizeGT_c + #define rasterizeFTA rasterizeFTA_c + #define rasterizeGTA rasterizeGTA_c + #define rasterizeSprite rasterizeSprite_c + #define rasterizeLineH rasterizeLineH_c + #define rasterizeLineV rasterizeLineV_c + #define rasterizeFillS rasterizeFillS_c + +void rasterizeS_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + const uint8* ft_lightmap = &gLightmap[0x1A00]; + + int32 Lh = 0; + int32 Rh = 0; + int32 Ldx = 0; + int32 Rdx = 0; + int32 Rx; + int32 Lx; + + while (1) + { + while (!Lh) + { + const VertexLink* N = L + L->prev; + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + + if (Lh > 1) + { + uint32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + } + + Lx <<= 16; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R + R->next; + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + + if (Rh > 1) { + uint32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + } + + Rx <<= 16; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + volatile uint16* ptr = pixel + (x1 >> 1); + + if (x1 & 1) + { + uint16 p = ptr[0]; + + uint16 index = ft_lightmap[p >> 8]; + + ptr[0] = (p & 0x00FF) | (index << 8); + ptr++; + width--; + } + + if (width & 1) + { + uint16 p = ptr[width >> 1]; + + uint16 index = ft_lightmap[p & 0xFF]; + + ptr[width >> 1] = (p & 0xFF00) | index; + width--; + } + + while (width) + { + uint16 p = *ptr; + + uint16 index = ft_lightmap[p & 0xFF]; + index |= ft_lightmap[p >> 8] << 8; + + *ptr++ = index; + width -= 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + } + } +} + +void rasterizeF_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + uint32 color = (uint32)R; + color = gLightmap[(L->v.g << 8) | color]; + color |= (color << 8); + + int32 Lh = 0; + int32 Rh = 0; + int32 Ldx = 0; + int32 Rdx = 0; + int32 Rx; + int32 Lx; + + R = L; + + while (1) + { + while (!Lh) + { + const VertexLink* N = L + L->prev; + + ASSERT(L->v.y >= 0); + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + + if (Lh > 1) + { + uint32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + } + + Lx <<= 16; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R + R->next; + + ASSERT(R->v.y >= 0); + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + + if (Rh > 1) { + uint32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + } + + Rx <<= 16; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + volatile uint8* ptr = (uint8*)pixel + x1; + + if (intptr_t(ptr) & 1) + { + ptr--; + *(uint16*)ptr = *ptr | (color << 8); + ptr += 2; + width--; + } + + if (width & 1) + { + *(uint16*)(ptr + width - 1) = (ptr[width] << 8) | (color >> 8); + } + + if (width & 2) + { + *(uint16*)ptr = color; + ptr += 2; + } + + width >>= 2; + while (width--) + { + *(uint16*)ptr = color; + ptr += 2; + *(uint16*)ptr = color; + ptr += 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + } + } +} + +void rasterizeG_c(uint16* pixel, const VertexLink* L, const VertexLink* R, int32 index) +{ + int32 Lh = 0, Rh = 0; + int32 Lx, Rx, Ldx = 0, Rdx = 0; + int32 Lg, Rg, Ldg = 0, Rdg = 0; + + const uint8* ft_lightmap = gLightmap + index; + + while (1) + { + while (!Lh) + { + const VertexLink* N = L + L->prev; + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + Lg = L->v.g; + + if (Lh > 1) + { + int32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + Ldg = tmp * (N->v.g - Lg); + } + + Lx <<= 16; + Lg <<= 16; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R + R->next; + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + Rg = R->v.g; + + if (Rh > 1) + { + int32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + Rdg = tmp * (N->v.g - Rg); + } + + Rx <<= 16; + Rg <<= 16; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + int32 tmp = FixedInvU(width); + + int32 dgdx = tmp * ((Rg - Lg) >> 5) >> 10; + + int32 g = Lg; + + volatile uint8* ptr = (uint8*)pixel + x1; + + if (intptr_t(ptr) & 1) + { + ptr--; + *(uint16*)ptr = *ptr | (ft_lightmap[g >> 16 << 8] << 8); + g += dgdx >> 1; + ptr += 2; + width--; + } + + if (width & 1) + { + *(uint16*)(ptr + width - 1) = (ptr[width] << 8) | ft_lightmap[Rg >> 16 << 8]; + } + + if (width & 2) + { + uint8 p = ft_lightmap[g >> 16 << 8]; + g += dgdx; + *(uint16*)ptr = p | (p << 8); + ptr += 2; + } + + width >>= 2; + while (width--) + { + uint8 p; + + p = ft_lightmap[g >> 16 << 8]; + *(uint16*)ptr = p | (p << 8); + g += dgdx; + ptr += 2; + + p = ft_lightmap[g >> 16 << 8]; + *(uint16*)ptr = p | (p << 8); + g += dgdx; + ptr += 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + Lg += Ldg; + Rg += Rdg; + } + } +} + +void rasterizeFT_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + const uint8* ft_lightmap = &gLightmap[L->v.g << 8]; + + int32 Lh = 0, Rh = 0; + int32 Lx, Rx, Ldx = 0, Rdx = 0; + uint32 Lt, Rt, Ldt, Rdt; + Ldt = 0; + Rdt = 0; + + while (1) + { + while (!Lh) + { + const VertexLink* N = L + L->prev; + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + Lt = L->t.t; + + if (Lh > 1) + { + int32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + + uint32 duv = N->t.t - Lt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Ldt = (du & 0xFFFF0000) | (dv >> 16); + } + + Lx <<= 16; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R + R->next; + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + Rt = R->t.t; + + if (Rh > 1) + { + int32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + + uint32 duv = N->t.t - Rt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Rdt = (du & 0xFFFF0000) | (dv >> 16); + } + + Rx <<= 16; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + uint32 tmp = FixedInvU(width); + + uint32 duv = Rt - Lt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + uint32 dtdx = (du & 0xFFFF0000) | (dv >> 16); + + uint32 t = Lt; + + volatile uint8* ptr = (uint8*)pixel + x1; + + if (intptr_t(ptr) & 1) + { + ptr--; + *(uint16*)ptr = *ptr | (ft_lightmap[gTile[(t & 0xFF00) | (t >> 24)]] << 8); + ptr += 2; + t += dtdx; + width--; + } + + if (width & 1) + { + uint32 tmp = Rt - dtdx; + *(uint16*)(ptr + width - 1) = (ptr[width] << 8) | ft_lightmap[gTile[(tmp & 0xFF00) | (tmp >> 24)]]; + } + + width >>= 1; + while (width--) + { + uint16 p; + + p = ft_lightmap[gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + p |= ft_lightmap[gTile[(t & 0xFF00) | (t >> 24)]] << 8; + t += dtdx; + + *(uint16*)ptr = p; + ptr += 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + Lt += Ldt; + Rt += Rdt; + } + } +} + +void rasterizeGT_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ +#ifdef ALIGNED_LIGHTMAP + ASSERT((intptr_t(lightmap) & 0xFFFF) == 0); // lightmap should be 64k aligned +#endif + + int32 Lh = 0, Rh = 0; + int32 Lx, Rx, Lg, Rg, Ldx = 0, Rdx = 0, Ldg = 0, Rdg = 0; + uint32 Lt, Rt, Ldt, Rdt; + Ldt = 0; + Rdt = 0; + + // 8-bit fractional part precision for Gouraud component + // has some artifacts but allow to save one reg for inner loop + // with aligned by 64k address of lightmap array + + while (1) + { + while (!Lh) + { + const VertexLink* N = L + L->prev; + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + Lg = L->v.g; + Lt = L->t.t; + + if (Lh > 1) + { + int32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + Ldg = tmp * (N->v.g - Lg) >> 8; + + uint32 duv = N->t.t - Lt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Ldt = (du & 0xFFFF0000) | (dv >> 16); + } + + Lx <<= 16; + Lg <<= 8; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R + R->next; + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + Rg = R->v.g; + Rt = R->t.t; + + if (Rh > 1) + { + int32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + Rdg = tmp * (N->v.g - Rg) >> 8; + + uint32 duv = N->t.t - Rt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Rdt = (du & 0xFFFF0000) | (dv >> 16); + } + + Rx <<= 16; + Rg <<= 8; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + int32 tmp = FixedInvU(width); + + int32 dgdx = tmp * (Rg - Lg) >> 15; + + uint32 duv = Rt - Lt; + uint32 u = tmp * int16(duv >> 16); + uint32 v = tmp * int16(duv); + uint32 dtdx = (u & 0xFFFF0000) | (v >> 16); + + int32 g = Lg; + uint32 t = Lt; + + volatile uint8* ptr = (uint8*)pixel + x1; + + if (intptr_t(ptr) & 1) + { + ptr--; + *(uint16*)ptr = *ptr | (gLightmap[(g >> 8 << 8) | gTile[(t & 0xFF00) | (t >> 24)]] << 8); + ptr += 2; + t += dtdx; + g += dgdx >> 1; + width--; + } + + if (width & 1) + { + uint32 tmp = Rt - dtdx; + *(uint16*)(ptr + width - 1) = (ptr[width] << 8) | gLightmap[(Rg >> 8 << 8) | gTile[(tmp & 0xFF00) | (tmp >> 24)]]; + } + + #ifdef ALIGNED_LIGHTMAP + g += intptr_t(lightmap); + #endif + + width >>= 1; + + while (width--) + { + #ifdef ALIGNED_LIGHTMAP + const uint8* LMAP = (uint8*)(g >> 8 << 8); + + uint16 p = LMAP[gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + p |= LMAP[gTile[(t & 0xFF00) | (t >> 24)]] << 8; + t += dtdx; + g += dgdx; + #else + uint16 p = gLightmap[(g >> 8 << 8) | gTile[(t & 0xFF00) | (t >> 24)]]; + t += dtdx; + p |= gLightmap[(g >> 8 << 8) | gTile[(t & 0xFF00) | (t >> 24)]] << 8; + t += dtdx; + g += dgdx; + #endif + + *(uint16*)ptr = p; + ptr += 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + Lg += Ldg; + Rg += Rdg; + Lt += Ldt; + Rt += Rdt; + } + } +} + +void rasterizeFTA_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + const uint8* ft_lightmap = &gLightmap[L->v.g << 8]; + + int32 Lh = 0, Rh = 0; + int32 Lx, Rx, Ldx = 0, Rdx = 0; + uint32 Lt, Rt, Ldt, Rdt; + Ldt = 0; + Rdt = 0; + + while (1) + { + while (!Lh) + { + const VertexLink* N = L + L->prev; + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + Lt = L->t.t; + + if (Lh > 1) + { + int32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + + uint32 duv = N->t.t - Lt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Ldt = (du & 0xFFFF0000) | (dv >> 16); + } + + Lx <<= 16; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R + R->next; + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + Rt = R->t.t; + + if (Rh > 1) + { + int32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + + uint32 duv = N->t.t - Rt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Rdt = (du & 0xFFFF0000) | (dv >> 16); + } + + Rx <<= 16; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + uint32 tmp = FixedInvU(width); + + uint32 duv = Rt - Lt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + uint32 dtdx = (du & 0xFFFF0000) | (dv >> 16); + + uint32 t = Lt; + + volatile uint8* ptr = (uint8*)pixel + x1; + + if (intptr_t(ptr) & 1) + { + uint8 p = gTile[(t & 0xFF00) | (t >> 24)]; + ptr--; + if (p) { + *(uint16*)ptr = *ptr | (ft_lightmap[p] << 8); + } + ptr += 2; + t += dtdx; + width--; + } + + if (width & 1) + { + uint32 tmp = Rt - dtdx; + uint8 p = gTile[(tmp & 0xFF00) | (tmp >> 24)]; + if (p) { + *(uint16*)(ptr + width - 1) = (ptr[width] << 8) | ft_lightmap[p]; + } + } + + width >>= 1; + while (width--) + { + uint8 indexA = gTile[(t & 0xFF00) | (t >> 24)]; + t += dtdx; + uint8 indexB = gTile[(t & 0xFF00) | (t >> 24)]; + t += dtdx; + + if (indexA && indexB) { + *(uint16*)ptr = ft_lightmap[indexA] | (ft_lightmap[indexB] << 8); + }/* else if (indexA) { + *(uint16*)ptr = (*(uint16*)ptr & 0xFF00) | ft_lightmap[indexA]; + } else if (indexB) { + *(uint16*)ptr = (*(uint16*)ptr & 0x00FF) | (ft_lightmap[indexB] << 8); + }*/ + + ptr += 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + Lt += Ldt; + Rt += Rdt; + } + } +} + +void rasterizeGTA_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ +#ifdef ALIGNED_LIGHTMAP + ASSERT((intptr_t(lightmap) & 0xFFFF) == 0); // lightmap should be 64k aligned +#endif + + int32 Lh = 0, Rh = 0; + int32 Lx, Rx, Lg, Rg, Ldx = 0, Rdx = 0, Ldg = 0, Rdg = 0; + uint32 Lt, Rt, Ldt, Rdt; + Ldt = 0; + Rdt = 0; + + // 8-bit fractional part precision for Gouraud component + // has some artifacts but allow to save one reg for inner loop + // with aligned by 64k address of lightmap array + + while (1) + { + while (!Lh) + { + const VertexLink* N = L + L->prev; + + if (N->v.y < L->v.y) return; + + Lh = N->v.y - L->v.y; + Lx = L->v.x; + Lg = L->v.g; + Lt = L->t.t; + + if (Lh > 1) + { + int32 tmp = FixedInvU(Lh); + Ldx = tmp * (N->v.x - Lx); + Ldg = tmp * (N->v.g - Lg) >> 8; + + uint32 duv = N->t.t - Lt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Ldt = (du & 0xFFFF0000) | (dv >> 16); + } + + Lx <<= 16; + Lg <<= 8; + L = N; + } + + while (!Rh) + { + const VertexLink* N = R + R->next; + + if (N->v.y < R->v.y) return; + + Rh = N->v.y - R->v.y; + Rx = R->v.x; + Rg = R->v.g; + Rt = R->t.t; + + if (Rh > 1) + { + int32 tmp = FixedInvU(Rh); + Rdx = tmp * (N->v.x - Rx); + Rdg = tmp * (N->v.g - Rg) >> 8; + + uint32 duv = N->t.t - Rt; + uint32 du = tmp * int16(duv >> 16); + uint32 dv = tmp * int16(duv); + Rdt = (du & 0xFFFF0000) | (dv >> 16); + } + + Rx <<= 16; + Rg <<= 8; + R = N; + } + + int32 h = X_MIN(Lh, Rh); + Lh -= h; + Rh -= h; + + while (h--) + { + int32 x1 = Lx >> 16; + int32 x2 = Rx >> 16; + + int32 width = x2 - x1; + + if (width > 0) + { + int32 tmp = FixedInvU(width); + + int32 dgdx = tmp * (Rg - Lg) >> 15; + + uint32 duv = Rt - Lt; + uint32 u = tmp * int16(duv >> 16); + uint32 v = tmp * int16(duv); + uint32 dtdx = (u & 0xFFFF0000) | (v >> 16); + + int32 g = Lg; + uint32 t = Lt; + + volatile uint8* ptr = (uint8*)pixel + x1; + + if (intptr_t(ptr) & 1) + { + ptr--; + + uint8 indexB = gTile[(t & 0xFF00) | (t >> 24)]; + + if (indexB) { + *(uint16*)ptr = *ptr | (gLightmap[(g >> 8 << 8) | indexB] << 8); + } + + ptr += 2; + t += dtdx; + g += dgdx >> 1; + width--; + } + + if (width & 1) + { + uint32 tmp = Rt - dtdx; + + uint8 indexA = gTile[(tmp & 0xFF00) | (tmp >> 24)]; + + if (indexA) { + *(uint16*)(ptr + width - 1) = (ptr[width] << 8) | gLightmap[(Rg >> 8 << 8) | indexA]; + } + } + + #ifdef ALIGNED_LIGHTMAP + g += intptr_t(lightmap); + #endif + + width >>= 1; + + while (width--) + { + #ifdef ALIGNED_LIGHTMAP + uint8 indexA = gTile[(t & 0xFF00) | (t >> 24)]; + t += dtdx; + uint8 indexB = gTile[(t & 0xFF00) | (t >> 24)]; + t += dtdx; + g += dgdx; + + + if (indexA && indexB) { + const uint8* LMAP = (uint8*)(g >> 8 << 8); + *(uint16*)ptr = LMAP[indexA] | (LMAP[indexB] << 8); + } + #else + uint8 indexA = gTile[(t & 0xFF00) | (t >> 24)]; + t += dtdx; + uint8 indexB = gTile[(t & 0xFF00) | (t >> 24)]; + t += dtdx; + g += dgdx; + + if (indexA && indexB) { + *(uint16*)ptr = gLightmap[(g >> 8 << 8) | indexA] | (gLightmap[(g >> 8 << 8) | indexB] << 8); + } + #endif + + ptr += 2; + } + } + + pixel += VRAM_WIDTH; + + Lx += Ldx; + Rx += Rdx; + Lg += Ldg; + Rg += Rdg; + Lt += Ldt; + Rt += Rdt; + } + } +} + +X_NOINLINE void rasterizeLineH_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + R++; + int32 x = L->v.x; + int32 index = L->v.g; + int32 width = R->v.x; + + volatile uint8* ptr = (uint8*)pixel + x; + + if (intptr_t(ptr) & 1) + { + ptr--; + *(uint16*)ptr = *ptr | (index << 8); + ptr += 2; + width--; + } + + if (width & 1) + { + *(uint16*)(ptr + width - 1) = index | (ptr[width] << 8); + } + + for (int32 i = 0; i < width / 2; i++) + { + *(uint16*)ptr = index | (index << 8); + ptr += 2; + } +} + +X_NOINLINE void rasterizeLineV_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + R++; + int32 x = L->v.x; + int32 index = L->v.g; + int32 height = R->v.y; + + volatile uint8* ptr = (uint8*)pixel + x; + + for (int32 i = 0; i < height; i++) + { + if (intptr_t(ptr) & 1) { + *(uint16*)(ptr - 1) = *(ptr - 1) | (index << 8); + } else { + *(uint16*)ptr = index | (*ptr << 8); + } + ptr += FRAME_WIDTH; + } +} + +X_NOINLINE void rasterizeFillS_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + R++; + int32 x = L->v.x; + int32 shade = L->v.g; + int32 width = R->v.x; + int32 height = R->v.y; + + const uint8* lm = &gLightmap[shade << 8]; + + for (int32 i = 0; i < height; i++) + { + volatile uint8* ptr = (uint8*)pixel + x; + int32 w = width; + + if (intptr_t(ptr) & 1) + { + ptr--; + *(uint16*)ptr = ptr[0] | (lm[ptr[1]] << 8); + ptr += 2; + w--; + } + + if (w & 1) + { + *(uint16*)(ptr + w - 1) = lm[ptr[w - 1]] | (ptr[w] << 8); + } + + for (int32 i = 0; i < w / 2; i++) + { + uint16 p = *(uint16*)ptr; + *(uint16*)ptr = lm[p & 0xFF] | (lm[p >> 8] << 8); + ptr += 2; + } + + pixel += FRAME_WIDTH / 2; + } +} +#endif + +// TODO ARM version +extern "C" X_NOINLINE void rasterizeSprite_c(uint16* pixel, const VertexLink* L, const VertexLink* R) +{ + R++; + const uint8* ft_lightmap = &gLightmap[L->v.g << 8]; + + int32 w = R->v.x - L->v.x; + if (w <= 0 || w >= DIV_TABLE_SIZE) return; + + int32 h = R->v.y - L->v.y; + if (h <= 0 || h >= DIV_TABLE_SIZE) return; + + int32 u = L->t.uv.u; + int32 v = L->t.uv.v; + + int32 iw = FixedInvU(w); + int32 ih = FixedInvU(h); + + int32 du = R->t.uv.u * iw >> 8; + int32 dv = R->t.uv.v * ih >> 8; + + if (L->v.y < 0) + { + pixel -= L->v.y * VRAM_WIDTH; + v -= L->v.y * dv; + h += L->v.y; + } + + if (R->v.y > FRAME_HEIGHT) + { + h -= R->v.y - FRAME_HEIGHT; + } + + uint8* ptr = (uint8*)pixel; + + if (h <= 0) return; + + ptr += L->v.x; + + if (L->v.x < 0) + { + ptr -= L->v.x; + u -= L->v.x * du; + w += L->v.x; + } + + if (R->v.x > FRAME_WIDTH) + { + w -= R->v.x - FRAME_WIDTH; + } + + if (w <= 0) return; + + bool alignL = intptr_t(ptr) & 1; + if (alignL) + { + ptr--; + w--; + } + + bool alignR = w & 1; + + w >>= 1; + + for (int32 y = 0; y < h; y++) + { + const uint8* xtile = gTile + (v & 0xFF00); + + volatile uint8* xptr = ptr; + + int32 xu = u; + + if (alignL) + { + uint8 indexB = xtile[xu >> 8]; + if (indexB) { + *(uint16*)xptr = *xptr | (ft_lightmap[indexB] << 8); + } + + xptr += 2; + xu += du; + } + + for (int32 x = 0; x < w; x++) + { + uint8 indexA = xtile[xu >> 8]; + xu += du; + uint8 indexB = xtile[xu >> 8]; + xu += du; + + if (indexA | indexB) + { + indexA = (indexA) ? ft_lightmap[indexA] : xptr[0]; + indexB = (indexB) ? ft_lightmap[indexB] : xptr[1]; + *(uint16*)xptr = indexA | (indexB << 8); + } + + xptr += 2; + } + + if (alignR) + { + uint8 indexA = xtile[xu >> 8]; + if (indexA) { + *(uint16*)xptr = ft_lightmap[indexA] | (xptr[1] << 8); + } + } + + v += dv; + + ptr += FRAME_WIDTH; + } +} + +#endif diff --git a/src/platform/gba/render.iwram.cpp b/src/platform/gba/render.iwram.cpp index b470b94d..ae31db78 100644 --- a/src/platform/gba/render.iwram.cpp +++ b/src/platform/gba/render.iwram.cpp @@ -1,1243 +1,1318 @@ #include "common.h" -#define DIV_TABLE_SIZE 641 +struct Vertex +{ + int16 x; + int16 y; + int16 z; + uint8 g; + uint8 clip; +}; -uint16 divTable[DIV_TABLE_SIZE]; +struct VertexLink +{ + Vertex v; + TexCoord t; + int8 prev; + int8 next; + uint16 padding; +}; -#ifdef _WIN32 - uint8 fb[WIDTH * HEIGHT * 2]; -#else - uint32 fb = VRAM; -#endif +struct ViewportRel { + int32 minXY; + int32 maxXY; +}; -#define FixedInvS(x) ((x < 0) ? -divTable[abs(x)] : divTable[x]) -#define FixedInvU(x) divTable[x] +ViewportRel viewportRel; -#if defined(USE_MODE_5) || defined(_WIN32) - uint16 palette[256]; +#if defined(_WIN32) + uint16 fb[VRAM_WIDTH * FRAME_HEIGHT]; +#elif defined(__GBA__) + uint32 fb = MEM_VRAM; +#elif defined(__TNS__) + uint16 fb[VRAM_WIDTH * FRAME_HEIGHT]; +#elif defined(__DOS__) + uint16 fb[VRAM_WIDTH * FRAME_HEIGHT]; #endif -uint8 lightmap[256 * 32]; - -const uint8* tiles[15]; - -const Texture* textures; - -uint32 gVerticesCount = 0; -Vertex gVertices[MAX_VERTICES]; - -int32 gFacesCount = 0; -Face* gFacesSorted[MAX_FACES]; -EWRAM_DATA Face gFaces[MAX_FACES]; - -const uint8* curTile; -uint16 mipMask; - -Rect clip; - -const int16 sin_table[] = { // 1025 - 0x0000, 0x0019, 0x0032, 0x004B, 0x0065, 0x007E, 0x0097, 0x00B0, - 0x00C9, 0x00E2, 0x00FB, 0x0114, 0x012E, 0x0147, 0x0160, 0x0179, - 0x0192, 0x01AB, 0x01C4, 0x01DD, 0x01F7, 0x0210, 0x0229, 0x0242, - 0x025B, 0x0274, 0x028D, 0x02A6, 0x02C0, 0x02D9, 0x02F2, 0x030B, - 0x0324, 0x033D, 0x0356, 0x036F, 0x0388, 0x03A1, 0x03BB, 0x03D4, - 0x03ED, 0x0406, 0x041F, 0x0438, 0x0451, 0x046A, 0x0483, 0x049C, - 0x04B5, 0x04CE, 0x04E7, 0x0500, 0x051A, 0x0533, 0x054C, 0x0565, - 0x057E, 0x0597, 0x05B0, 0x05C9, 0x05E2, 0x05FB, 0x0614, 0x062D, - 0x0646, 0x065F, 0x0678, 0x0691, 0x06AA, 0x06C3, 0x06DC, 0x06F5, - 0x070E, 0x0727, 0x0740, 0x0759, 0x0772, 0x078B, 0x07A4, 0x07BD, - 0x07D6, 0x07EF, 0x0807, 0x0820, 0x0839, 0x0852, 0x086B, 0x0884, - 0x089D, 0x08B6, 0x08CF, 0x08E8, 0x0901, 0x0919, 0x0932, 0x094B, - 0x0964, 0x097D, 0x0996, 0x09AF, 0x09C7, 0x09E0, 0x09F9, 0x0A12, - 0x0A2B, 0x0A44, 0x0A5C, 0x0A75, 0x0A8E, 0x0AA7, 0x0AC0, 0x0AD8, - 0x0AF1, 0x0B0A, 0x0B23, 0x0B3B, 0x0B54, 0x0B6D, 0x0B85, 0x0B9E, - 0x0BB7, 0x0BD0, 0x0BE8, 0x0C01, 0x0C1A, 0x0C32, 0x0C4B, 0x0C64, - 0x0C7C, 0x0C95, 0x0CAE, 0x0CC6, 0x0CDF, 0x0CF8, 0x0D10, 0x0D29, - 0x0D41, 0x0D5A, 0x0D72, 0x0D8B, 0x0DA4, 0x0DBC, 0x0DD5, 0x0DED, - 0x0E06, 0x0E1E, 0x0E37, 0x0E4F, 0x0E68, 0x0E80, 0x0E99, 0x0EB1, - 0x0ECA, 0x0EE2, 0x0EFB, 0x0F13, 0x0F2B, 0x0F44, 0x0F5C, 0x0F75, - 0x0F8D, 0x0FA5, 0x0FBE, 0x0FD6, 0x0FEE, 0x1007, 0x101F, 0x1037, - 0x1050, 0x1068, 0x1080, 0x1099, 0x10B1, 0x10C9, 0x10E1, 0x10FA, - 0x1112, 0x112A, 0x1142, 0x115A, 0x1173, 0x118B, 0x11A3, 0x11BB, - 0x11D3, 0x11EB, 0x1204, 0x121C, 0x1234, 0x124C, 0x1264, 0x127C, - 0x1294, 0x12AC, 0x12C4, 0x12DC, 0x12F4, 0x130C, 0x1324, 0x133C, - 0x1354, 0x136C, 0x1384, 0x139C, 0x13B4, 0x13CC, 0x13E4, 0x13FB, - 0x1413, 0x142B, 0x1443, 0x145B, 0x1473, 0x148B, 0x14A2, 0x14BA, - 0x14D2, 0x14EA, 0x1501, 0x1519, 0x1531, 0x1549, 0x1560, 0x1578, - 0x1590, 0x15A7, 0x15BF, 0x15D7, 0x15EE, 0x1606, 0x161D, 0x1635, - 0x164C, 0x1664, 0x167C, 0x1693, 0x16AB, 0x16C2, 0x16DA, 0x16F1, - 0x1709, 0x1720, 0x1737, 0x174F, 0x1766, 0x177E, 0x1795, 0x17AC, - 0x17C4, 0x17DB, 0x17F2, 0x180A, 0x1821, 0x1838, 0x184F, 0x1867, - 0x187E, 0x1895, 0x18AC, 0x18C3, 0x18DB, 0x18F2, 0x1909, 0x1920, - 0x1937, 0x194E, 0x1965, 0x197C, 0x1993, 0x19AA, 0x19C1, 0x19D8, - 0x19EF, 0x1A06, 0x1A1D, 0x1A34, 0x1A4B, 0x1A62, 0x1A79, 0x1A90, - 0x1AA7, 0x1ABE, 0x1AD4, 0x1AEB, 0x1B02, 0x1B19, 0x1B30, 0x1B46, - 0x1B5D, 0x1B74, 0x1B8A, 0x1BA1, 0x1BB8, 0x1BCE, 0x1BE5, 0x1BFC, - 0x1C12, 0x1C29, 0x1C3F, 0x1C56, 0x1C6C, 0x1C83, 0x1C99, 0x1CB0, - 0x1CC6, 0x1CDD, 0x1CF3, 0x1D0A, 0x1D20, 0x1D36, 0x1D4D, 0x1D63, - 0x1D79, 0x1D90, 0x1DA6, 0x1DBC, 0x1DD3, 0x1DE9, 0x1DFF, 0x1E15, - 0x1E2B, 0x1E42, 0x1E58, 0x1E6E, 0x1E84, 0x1E9A, 0x1EB0, 0x1EC6, - 0x1EDC, 0x1EF2, 0x1F08, 0x1F1E, 0x1F34, 0x1F4A, 0x1F60, 0x1F76, - 0x1F8C, 0x1FA2, 0x1FB7, 0x1FCD, 0x1FE3, 0x1FF9, 0x200F, 0x2024, - 0x203A, 0x2050, 0x2065, 0x207B, 0x2091, 0x20A6, 0x20BC, 0x20D1, - 0x20E7, 0x20FD, 0x2112, 0x2128, 0x213D, 0x2153, 0x2168, 0x217D, - 0x2193, 0x21A8, 0x21BE, 0x21D3, 0x21E8, 0x21FE, 0x2213, 0x2228, - 0x223D, 0x2253, 0x2268, 0x227D, 0x2292, 0x22A7, 0x22BC, 0x22D2, - 0x22E7, 0x22FC, 0x2311, 0x2326, 0x233B, 0x2350, 0x2365, 0x237A, - 0x238E, 0x23A3, 0x23B8, 0x23CD, 0x23E2, 0x23F7, 0x240B, 0x2420, - 0x2435, 0x244A, 0x245E, 0x2473, 0x2488, 0x249C, 0x24B1, 0x24C5, - 0x24DA, 0x24EF, 0x2503, 0x2518, 0x252C, 0x2541, 0x2555, 0x2569, - 0x257E, 0x2592, 0x25A6, 0x25BB, 0x25CF, 0x25E3, 0x25F8, 0x260C, - 0x2620, 0x2634, 0x2648, 0x265C, 0x2671, 0x2685, 0x2699, 0x26AD, - 0x26C1, 0x26D5, 0x26E9, 0x26FD, 0x2711, 0x2724, 0x2738, 0x274C, - 0x2760, 0x2774, 0x2788, 0x279B, 0x27AF, 0x27C3, 0x27D6, 0x27EA, - 0x27FE, 0x2811, 0x2825, 0x2838, 0x284C, 0x2860, 0x2873, 0x2886, - 0x289A, 0x28AD, 0x28C1, 0x28D4, 0x28E7, 0x28FB, 0x290E, 0x2921, - 0x2935, 0x2948, 0x295B, 0x296E, 0x2981, 0x2994, 0x29A7, 0x29BB, - 0x29CE, 0x29E1, 0x29F4, 0x2A07, 0x2A1A, 0x2A2C, 0x2A3F, 0x2A52, - 0x2A65, 0x2A78, 0x2A8B, 0x2A9D, 0x2AB0, 0x2AC3, 0x2AD6, 0x2AE8, - 0x2AFB, 0x2B0D, 0x2B20, 0x2B33, 0x2B45, 0x2B58, 0x2B6A, 0x2B7D, - 0x2B8F, 0x2BA1, 0x2BB4, 0x2BC6, 0x2BD8, 0x2BEB, 0x2BFD, 0x2C0F, - 0x2C21, 0x2C34, 0x2C46, 0x2C58, 0x2C6A, 0x2C7C, 0x2C8E, 0x2CA0, - 0x2CB2, 0x2CC4, 0x2CD6, 0x2CE8, 0x2CFA, 0x2D0C, 0x2D1E, 0x2D2F, - 0x2D41, 0x2D53, 0x2D65, 0x2D76, 0x2D88, 0x2D9A, 0x2DAB, 0x2DBD, - 0x2DCF, 0x2DE0, 0x2DF2, 0x2E03, 0x2E15, 0x2E26, 0x2E37, 0x2E49, - 0x2E5A, 0x2E6B, 0x2E7D, 0x2E8E, 0x2E9F, 0x2EB0, 0x2EC2, 0x2ED3, - 0x2EE4, 0x2EF5, 0x2F06, 0x2F17, 0x2F28, 0x2F39, 0x2F4A, 0x2F5B, - 0x2F6C, 0x2F7D, 0x2F8D, 0x2F9E, 0x2FAF, 0x2FC0, 0x2FD0, 0x2FE1, - 0x2FF2, 0x3002, 0x3013, 0x3024, 0x3034, 0x3045, 0x3055, 0x3066, - 0x3076, 0x3087, 0x3097, 0x30A7, 0x30B8, 0x30C8, 0x30D8, 0x30E8, - 0x30F9, 0x3109, 0x3119, 0x3129, 0x3139, 0x3149, 0x3159, 0x3169, - 0x3179, 0x3189, 0x3199, 0x31A9, 0x31B9, 0x31C8, 0x31D8, 0x31E8, - 0x31F8, 0x3207, 0x3217, 0x3227, 0x3236, 0x3246, 0x3255, 0x3265, - 0x3274, 0x3284, 0x3293, 0x32A3, 0x32B2, 0x32C1, 0x32D0, 0x32E0, - 0x32EF, 0x32FE, 0x330D, 0x331D, 0x332C, 0x333B, 0x334A, 0x3359, - 0x3368, 0x3377, 0x3386, 0x3395, 0x33A3, 0x33B2, 0x33C1, 0x33D0, - 0x33DF, 0x33ED, 0x33FC, 0x340B, 0x3419, 0x3428, 0x3436, 0x3445, - 0x3453, 0x3462, 0x3470, 0x347F, 0x348D, 0x349B, 0x34AA, 0x34B8, - 0x34C6, 0x34D4, 0x34E2, 0x34F1, 0x34FF, 0x350D, 0x351B, 0x3529, - 0x3537, 0x3545, 0x3553, 0x3561, 0x356E, 0x357C, 0x358A, 0x3598, - 0x35A5, 0x35B3, 0x35C1, 0x35CE, 0x35DC, 0x35EA, 0x35F7, 0x3605, - 0x3612, 0x3620, 0x362D, 0x363A, 0x3648, 0x3655, 0x3662, 0x366F, - 0x367D, 0x368A, 0x3697, 0x36A4, 0x36B1, 0x36BE, 0x36CB, 0x36D8, - 0x36E5, 0x36F2, 0x36FF, 0x370C, 0x3718, 0x3725, 0x3732, 0x373F, - 0x374B, 0x3758, 0x3765, 0x3771, 0x377E, 0x378A, 0x3797, 0x37A3, - 0x37B0, 0x37BC, 0x37C8, 0x37D5, 0x37E1, 0x37ED, 0x37F9, 0x3805, - 0x3812, 0x381E, 0x382A, 0x3836, 0x3842, 0x384E, 0x385A, 0x3866, - 0x3871, 0x387D, 0x3889, 0x3895, 0x38A1, 0x38AC, 0x38B8, 0x38C3, - 0x38CF, 0x38DB, 0x38E6, 0x38F2, 0x38FD, 0x3909, 0x3914, 0x391F, - 0x392B, 0x3936, 0x3941, 0x394C, 0x3958, 0x3963, 0x396E, 0x3979, - 0x3984, 0x398F, 0x399A, 0x39A5, 0x39B0, 0x39BB, 0x39C5, 0x39D0, - 0x39DB, 0x39E6, 0x39F0, 0x39FB, 0x3A06, 0x3A10, 0x3A1B, 0x3A25, - 0x3A30, 0x3A3A, 0x3A45, 0x3A4F, 0x3A59, 0x3A64, 0x3A6E, 0x3A78, - 0x3A82, 0x3A8D, 0x3A97, 0x3AA1, 0x3AAB, 0x3AB5, 0x3ABF, 0x3AC9, - 0x3AD3, 0x3ADD, 0x3AE6, 0x3AF0, 0x3AFA, 0x3B04, 0x3B0E, 0x3B17, - 0x3B21, 0x3B2A, 0x3B34, 0x3B3E, 0x3B47, 0x3B50, 0x3B5A, 0x3B63, - 0x3B6D, 0x3B76, 0x3B7F, 0x3B88, 0x3B92, 0x3B9B, 0x3BA4, 0x3BAD, - 0x3BB6, 0x3BBF, 0x3BC8, 0x3BD1, 0x3BDA, 0x3BE3, 0x3BEC, 0x3BF5, - 0x3BFD, 0x3C06, 0x3C0F, 0x3C17, 0x3C20, 0x3C29, 0x3C31, 0x3C3A, - 0x3C42, 0x3C4B, 0x3C53, 0x3C5B, 0x3C64, 0x3C6C, 0x3C74, 0x3C7D, - 0x3C85, 0x3C8D, 0x3C95, 0x3C9D, 0x3CA5, 0x3CAD, 0x3CB5, 0x3CBD, - 0x3CC5, 0x3CCD, 0x3CD5, 0x3CDD, 0x3CE4, 0x3CEC, 0x3CF4, 0x3CFB, - 0x3D03, 0x3D0B, 0x3D12, 0x3D1A, 0x3D21, 0x3D28, 0x3D30, 0x3D37, - 0x3D3F, 0x3D46, 0x3D4D, 0x3D54, 0x3D5B, 0x3D63, 0x3D6A, 0x3D71, - 0x3D78, 0x3D7F, 0x3D86, 0x3D8D, 0x3D93, 0x3D9A, 0x3DA1, 0x3DA8, - 0x3DAF, 0x3DB5, 0x3DBC, 0x3DC2, 0x3DC9, 0x3DD0, 0x3DD6, 0x3DDD, - 0x3DE3, 0x3DE9, 0x3DF0, 0x3DF6, 0x3DFC, 0x3E03, 0x3E09, 0x3E0F, - 0x3E15, 0x3E1B, 0x3E21, 0x3E27, 0x3E2D, 0x3E33, 0x3E39, 0x3E3F, - 0x3E45, 0x3E4A, 0x3E50, 0x3E56, 0x3E5C, 0x3E61, 0x3E67, 0x3E6C, - 0x3E72, 0x3E77, 0x3E7D, 0x3E82, 0x3E88, 0x3E8D, 0x3E92, 0x3E98, - 0x3E9D, 0x3EA2, 0x3EA7, 0x3EAC, 0x3EB1, 0x3EB6, 0x3EBB, 0x3EC0, - 0x3EC5, 0x3ECA, 0x3ECF, 0x3ED4, 0x3ED8, 0x3EDD, 0x3EE2, 0x3EE7, - 0x3EEB, 0x3EF0, 0x3EF4, 0x3EF9, 0x3EFD, 0x3F02, 0x3F06, 0x3F0A, - 0x3F0F, 0x3F13, 0x3F17, 0x3F1C, 0x3F20, 0x3F24, 0x3F28, 0x3F2C, - 0x3F30, 0x3F34, 0x3F38, 0x3F3C, 0x3F40, 0x3F43, 0x3F47, 0x3F4B, - 0x3F4F, 0x3F52, 0x3F56, 0x3F5A, 0x3F5D, 0x3F61, 0x3F64, 0x3F68, - 0x3F6B, 0x3F6E, 0x3F72, 0x3F75, 0x3F78, 0x3F7B, 0x3F7F, 0x3F82, - 0x3F85, 0x3F88, 0x3F8B, 0x3F8E, 0x3F91, 0x3F94, 0x3F97, 0x3F99, - 0x3F9C, 0x3F9F, 0x3FA2, 0x3FA4, 0x3FA7, 0x3FAA, 0x3FAC, 0x3FAF, - 0x3FB1, 0x3FB4, 0x3FB6, 0x3FB8, 0x3FBB, 0x3FBD, 0x3FBF, 0x3FC1, - 0x3FC4, 0x3FC6, 0x3FC8, 0x3FCA, 0x3FCC, 0x3FCE, 0x3FD0, 0x3FD2, - 0x3FD4, 0x3FD5, 0x3FD7, 0x3FD9, 0x3FDB, 0x3FDC, 0x3FDE, 0x3FE0, - 0x3FE1, 0x3FE3, 0x3FE4, 0x3FE6, 0x3FE7, 0x3FE8, 0x3FEA, 0x3FEB, - 0x3FEC, 0x3FED, 0x3FEF, 0x3FF0, 0x3FF1, 0x3FF2, 0x3FF3, 0x3FF4, - 0x3FF5, 0x3FF6, 0x3FF7, 0x3FF7, 0x3FF8, 0x3FF9, 0x3FFA, 0x3FFA, - 0x3FFB, 0x3FFC, 0x3FFC, 0x3FFD, 0x3FFD, 0x3FFE, 0x3FFE, 0x3FFE, - 0x3FFF, 0x3FFF, 0x3FFF, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000 +enum FaceType { + FACE_TYPE_SHADOW, + FACE_TYPE_F, + FACE_TYPE_FT, + FACE_TYPE_FTA, + FACE_TYPE_GT, + FACE_TYPE_GTA, + FACE_TYPE_SPRITE, + FACE_TYPE_FILL_S, + FACE_TYPE_LINE_H, + FACE_TYPE_LINE_V, + FACE_TYPE_MAX }; -int32 phd_sin(int32 x) -{ - x &= 0xFFFF; - bool neg = (x > 0x8000); - x &= 0x7FFF; +#define FACE_TRIANGLE (1 << 19) +#define FACE_CLIPPED (1 << 18) +#define FACE_TYPE_SHIFT 14 +#define FACE_TYPE_MASK 15 +#define FACE_GOURAUD (2 << FACE_TYPE_SHIFT) +#define FACE_TEXTURE 0x3FFF - if (x >= 0x4000) { - x = 0x8000 - x; - } +#include "rasterizer.h" + +extern Level level; + +const uint8* gTile; - x = sin_table[x >> 4]; +Vertex* gVerticesBase; +Face* gFacesBase; + +EWRAM_DATA uint8 gBackgroundCopy[FRAME_WIDTH * FRAME_HEIGHT]; // EWRAM 37.5k +EWRAM_DATA ALIGN8 Vertex gVertices[MAX_VERTICES]; // EWRAM 16k +EWRAM_DATA Face gFaces[MAX_FACES]; // EWRAM 30k +Face* gOT[OT_SIZE]; // IWRAM 2.5k + +enum ClipFlags { + CLIP_LEFT = 1 << 0, + CLIP_RIGHT = 1 << 1, + CLIP_TOP = 1 << 2, + CLIP_BOTTOM = 1 << 3, + CLIP_FAR = 1 << 4, + CLIP_NEAR = 1 << 5, + CLIP_MASK_VP = (CLIP_LEFT | CLIP_RIGHT | CLIP_TOP | CLIP_BOTTOM), +}; - return neg ? -x : x; +const MeshQuad gShadowQuads[] = { + { (FACE_TYPE_SHADOW << FACE_TYPE_SHIFT), {0, 1, 2, 7} }, + { (FACE_TYPE_SHADOW << FACE_TYPE_SHIFT), {7, 2, 3, 6} }, + { (FACE_TYPE_SHADOW << FACE_TYPE_SHIFT), {6, 3, 4, 5} } +}; + +void setViewport(const RectMinMax &vp) +{ + viewport = vp; + + int32 minX = vp.x0 - (FRAME_WIDTH >> 1); + int32 minY = vp.y0 - (FRAME_HEIGHT >> 1); + int32 maxX = vp.x1 - (FRAME_WIDTH >> 1); + int32 maxY = vp.y1 - (FRAME_HEIGHT >> 1); + + viewportRel.minXY = (minX << 16) | (minY & 0xFFFF); + viewportRel.maxXY = (maxX << 16) | (maxY & 0xFFFF); } -int32 phd_cos(int32 x) +void setPaletteIndex(int32 index) { - return phd_sin(x + 0x4000); + // TODO } -Matrix matrixStack[MAX_MATRICES]; -int32 matrixStackIndex = 0; -vec3i viewPos; +X_INLINE Face* faceAdd(int32 depth) +{ + ASSERT(depth >= 0 && depth < OT_SIZE); + + Face* face = gFacesBase++; + face->next = gOT[depth]; + gOT[depth] = face; -Matrix& matrixGet() { - return matrixStack[matrixStackIndex]; + return face; } -void matrixPush() { -#ifdef _WIN32 - if (matrixStackIndex >= MAX_MATRICES - 1) { - DebugBreak(); - return; - } -#endif - Matrix &a = matrixStack[matrixStackIndex++]; - Matrix &b = matrixStack[matrixStackIndex]; - memcpy(b, a, sizeof(Matrix)); +extern "C" { + X_NOINLINE void drawPoly(uint32 flags, VertexLink* v); + X_NOINLINE void drawTriangle(uint32 flags, VertexLink* v); + X_NOINLINE void drawQuad(uint32 flags, VertexLink* v); } -void matrixPop() { -#ifdef _WIN32 - if (matrixStackIndex <= 0) { - DebugBreak(); - return; +#ifdef USE_ASM + #define transformRoom transformRoom_asm + #define transformRoomUW transformRoomUW_asm + #define transformMesh transformMesh_asm + #define faceAddRoomQuads faceAddRoomQuads_asm + #define faceAddRoomTriangles faceAddRoomTriangles_asm + #define faceAddMeshQuads faceAddMeshQuads_asm + #define faceAddMeshTriangles faceAddMeshTriangles_asm + #define rasterize rasterize_asm + + extern "C" { + void transformRoom_asm(const RoomVertex* vertices, int32 count); + void transformRoomUW_asm(const RoomVertex* vertices, int32 count); + void transformMesh_asm(const MeshVertex* vertices, int32 count, int32 intensity); + void faceAddRoomQuads_asm(const RoomQuad* polys, int32 count); + void faceAddRoomTriangles_asm(const RoomTriangle* polys, int32 count); + void faceAddMeshQuads_asm(const MeshQuad* polys, int32 count); + void faceAddMeshTriangles_asm(const MeshTriangle* polys, int32 count); + void rasterize_asm(uint32 flags, VertexLink* top); } -#endif - matrixStackIndex--; +#else + #define transformRoom transformRoom_c + #define transformRoomUW transformRoomUW_c + #define transformMesh transformMesh_c + #define faceAddRoomQuads faceAddRoomQuads_c + #define faceAddRoomTriangles faceAddRoomTriangles_c + #define faceAddMeshQuads faceAddMeshQuads_c + #define faceAddMeshTriangles faceAddMeshTriangles_c + #define rasterize rasterize_c + +X_INLINE bool checkBackface(const Vertex *a, const Vertex *b, const Vertex *c) +{ + return (b->x - a->x) * (c->y - a->y) <= (c->x - a->x) * (b->y - a->y); } -void matrixTranslate(const vec3i &offset) { - Matrix &m = matrixGet(); - - m[0].w += DP33(m[0], offset); - m[1].w += DP33(m[1], offset); - m[2].w += DP33(m[2], offset); -} +void transformRoom_c(const RoomVertex* vertices, int32 count) +{ + Vertex* res = gVerticesBase; -void matrixTranslateAbs(const vec3i &offset) { - vec3i d; - d.x = offset.x - viewPos.x; - d.y = offset.y - viewPos.y; - d.z = offset.z - viewPos.z; + for (int32 i = 0; i < count; i++, res++) + { + uint32 value = *(uint32*)(vertices++); - Matrix &m = matrixGet(); - m[0].w = DP33(m[0], d); - m[1].w = DP33(m[1], d); - m[2].w = DP33(m[2], d); -} + int32 vx = (value & (0xFF)) << 10; + int32 vy = (value & (0xFF << 8)); + int32 vz = (value & (0xFF << 16)) >> 6; + int32 vg = (value & (0xFF << 24)) >> (24 - 5); -void matrixRotate(int16 rotX, int16 rotY, int16 rotZ) {} + const Matrix &m = matrixGet(); + int32 x = DP43(m.e00, m.e01, m.e02, m.e03, vx, vy, vz); + int32 y = DP43(m.e10, m.e11, m.e12, m.e13, vx, vy, vz); + int32 z = DP43(m.e20, m.e21, m.e22, m.e23, vx, vy, vz); -void matrixSetView(const vec3i &pos, int16 rotX, int16 rotY) { - int32 sx = phd_sin(rotX); - int32 cx = phd_cos(rotX); - int32 sy = phd_sin(rotY); - int32 cy = phd_cos(rotY); + uint32 clip = 0; - Matrix &m = matrixGet(); + if (z <= VIEW_MIN_F) { + clip = CLIP_NEAR; + z = VIEW_MIN_F; + } - m[0].x = cy; - m[0].y = 0; - m[0].z = -sy; - m[0].w = pos.x; + if (z >= VIEW_MAX_F) { + clip = CLIP_FAR; + z = VIEW_MAX_F; + } - m[1].x = (sx * sy) >> FIXED_SHIFT; - m[1].y = cx; - m[1].z = (sx * cy) >> FIXED_SHIFT; - m[1].w = pos.y; + x >>= FIXED_SHIFT; + y >>= FIXED_SHIFT; + z >>= FIXED_SHIFT; - m[2].x = (cx * sy) >> FIXED_SHIFT; - m[2].y = -sx; - m[2].z = (cx * cy) >> FIXED_SHIFT; - m[2].w = pos.z; + if (z > FOG_MIN) + { + vg += (z - FOG_MIN) << FOG_SHIFT; + if (vg > 8191) { + vg = 8191; + } + } - viewPos = pos; -} + PERSPECTIVE(x, y, z); -int32 clamp(int32 x, int32 a, int32 b) { - return x < a ? a : (x > b ? b : x); -} + // use this in case of overflow + //x = X_CLAMP(x, -512, 512); + //y = X_CLAMP(y, -512, 512); -template -INLINE void swap(T &a, T &b) { - T tmp = a; - a = b; - b = tmp; -} + x += (FRAME_WIDTH >> 1); + y += (FRAME_HEIGHT >> 1); -INLINE bool checkBackface(const Vertex *a, const Vertex *b, const Vertex *c) { - return (b->x - a->x) * (c->y - a->y) <= (c->x - a->x) * (b->y - a->y); -} + if (x < viewport.x0) clip |= CLIP_LEFT; + if (x > viewport.x1) clip |= CLIP_RIGHT; + if (y < viewport.y0) clip |= CLIP_TOP; + if (y > viewport.y1) clip |= CLIP_BOTTOM; -INLINE void sortVertices(VertexUV *&t, VertexUV *&m, VertexUV *&b) { - if (t->v.y > m->v.y) swap(t, m); - if (t->v.y > b->v.y) swap(t, b); - if (m->v.y > b->v.y) swap(m, b); + res->x = x; + res->y = y; + res->z = z; + res->g = vg >> 8; + res->clip = clip; + } } -INLINE int32 classify(const Vertex* v) { - return (v->x < clip.x0 ? 1 : 0) | - (v->x > clip.x1 ? 2 : 0) | - (v->y < clip.y0 ? 4 : 0) | - (v->y > clip.y1 ? 8 : 0); -} +void transformRoomUW_c(const RoomVertex* vertices, int32 count) +{ + Vertex* res = gVerticesBase; -void transform(const vec3s &v, int32 vg) { -#ifdef _WIN32 - if (gVerticesCount >= MAX_VERTICES) { - DebugBreak(); - return; - } -#endif - const Matrix &m = matrixStack[matrixStackIndex]; + for (int32 i = 0; i < count; i++, res++) + { + uint32 value = *(uint32*)(vertices++); - Vertex &res = gVertices[gVerticesCount++]; + int32 vx = (value & (0xFF)) << 10; + int32 vy = (value & (0xFF << 8)); + int32 vz = (value & (0xFF << 16)) >> 6; + int32 vg = (value & (0xFF << 24)) >> (24 - 5); - int32 z = DP43(m[2], v); + const Matrix &m = matrixGet(); + int32 x = DP43(m.e00, m.e01, m.e02, m.e03, vx, vy, vz); + int32 y = DP43(m.e10, m.e11, m.e12, m.e13, vx, vy, vz); + int32 z = DP43(m.e20, m.e21, m.e22, m.e23, vx, vy, vz); - if (z < VIEW_MIN_F || z >= VIEW_MAX_F) { // TODO znear clip - res.clip = 16; - res.z = -1; - return; - } + uint32 clip = 0; - int32 x = DP43(m[0], v); - int32 y = DP43(m[1], v); + if (z <= VIEW_MIN_F) { + clip = CLIP_NEAR; + z = VIEW_MIN_F; + } - int32 fogZ = z >> FIXED_SHIFT; - if (fogZ > FOG_MAX) { - vg = 8191; - } else if (fogZ > FOG_MIN) { - vg += (fogZ - FOG_MIN) << FOG_SHIFT; - if (vg > 8191) { - vg = 8191; + if (z >= VIEW_MAX_F) { + clip = CLIP_FAR; + z = VIEW_MAX_F; } - } - res.g = vg >> 8; - z >>= FOV_SHIFT; + int32 causticsValue = gCaustics[(gRandTable[i & (MAX_RAND_TABLE - 1)] + gCausticsFrame) & (MAX_CAUSTICS - 1)]; + vg = X_CLAMP(vg + causticsValue, 0, 8191); -#if 1 - x >>= 11; - y >>= 11; - z >>= 11; + x >>= FIXED_SHIFT; + y >>= FIXED_SHIFT; + z >>= FIXED_SHIFT; - #ifdef WIN32 - if (abs(z) >= DIV_TABLE_SIZE) { - DebugBreak(); + if (z > FOG_MIN) + { + vg += (z - FOG_MIN) << FOG_SHIFT; + if (vg > 8191) { + vg = 8191; + } } - #endif - uint32 iz = FixedInvS(z); - x = (x * iz) >> 16; - y = (y * iz) >> 16; -#else - x = (x / z); - y = (y / z); -#endif + PERSPECTIVE(x, y, z); - //x = clamp(x, -0x7FFF, 0x7FFF); - //y = clamp(y, -0x7FFF, 0x7FFF); + x += (FRAME_WIDTH >> 1); + y += (FRAME_HEIGHT >> 1); - res.x = x + (FRAME_WIDTH / 2); - res.y = y + (FRAME_HEIGHT / 2); - res.z = fogZ; - res.clip = classify(&res); -} + if (x < viewport.x0) clip |= CLIP_LEFT; + if (x > viewport.x1) clip |= CLIP_RIGHT; + if (y < viewport.y0) clip |= CLIP_TOP; + if (y > viewport.y1) clip |= CLIP_BOTTOM; -#if 0 // TODO -void clipZ(int32 znear, VertexUV *output, int32 &count, const VertexUV *a, const VertexUV *b) { - #define LERP2(a,b,t) int32((b) + (((a) - (b)) * t)) - - float t = (znear - b->v.sz) / float(a->v.sz - b->v.sz); - VertexUV* v = output + count++; -/* - int32 ax = (a->v.x - (FRAME_WIDTH / 2)) * a->v.z; - int32 ay = (a->v.y - (FRAME_HEIGHT / 2)) * a->v.z; - int32 bx = (b->v.x - (FRAME_WIDTH / 2)) * b->v.z; - int32 by = (b->v.y - (FRAME_HEIGHT / 2)) * b->v.z; - int32 x = LERP2(ax, bx, t); - int32 y = LERP2(ay, by, t); -*/ - int32 x = LERP2(a->v.sx, b->v.sx, t); - int32 y = LERP2(a->v.sy, b->v.sy, t); - int32 z = LERP2(a->v.sz, b->v.sz, t); - v->v.x = (x / znear) + (FRAME_WIDTH / 2); - v->v.y = (y / znear) + (FRAME_HEIGHT / 2); - v->v.g = LERP2(a->v.g, b->v.g, t); - v->uv = (LERP2(a->uv & 0xFFFF, b->uv & 0xFFFF, t)) | (LERP2(a->uv >> 16, b->uv >> 16, t) << 16); -} -#endif -VertexUV* clipPoly(VertexUV* poly, VertexUV* tmp, int32 &pCount) { - #define LERP(a,b,t) ((b) + (((a) - (b)) * t >> 16)) - - #define CLIP_AXIS(X, Y, edge, output) {\ - uint32 t = ((edge - b->v.X) << 16) / (a->v.X - b->v.X);\ - VertexUV* v = output + count++;\ - v->v.X = edge;\ - v->v.Y = LERP(a->v.Y, b->v.Y, t);\ - v->v.z = LERP(a->v.z, b->v.z, t);\ - v->v.g = LERP(a->v.g, b->v.g, t);\ - v->uv = (LERP(a->uv & 0xFFFF, b->uv & 0xFFFF, t)) | (LERP(a->uv >> 16, b->uv >> 16, t) << 16);\ + res->x = x; + res->y = y; + res->z = z; + res->g = vg >> 8; + res->clip = clip; } +} -/* TODO - #define CLIP_NEAR(znear, output) {\ - //clipZ(znear, output, count, a, b);\ - uint32 t = ((znear - b->v.z) << 16) / (a->v.z - b->v.z);\ - VertexUV* v = output + count++;\ - int32 ax = (a->v.x - (FRAME_WIDTH / 2)) * a->v.z;\ - int32 ay = (a->v.y - (FRAME_HEIGHT / 2)) * a->v.z;\ - int32 bx = (b->v.x - (FRAME_WIDTH / 2)) * b->v.z;\ - int32 by = (b->v.y - (FRAME_HEIGHT / 2)) * b->v.z;\ - int32 x = LERP(ax, bx, t);\ - int32 y = LERP(ay, by, t);\ - v->v.x = (x / znear) + (FRAME_WIDTH / 2);\ - v->v.y = (y / znear) + (FRAME_HEIGHT / 2);\ - v->v.z = LERP(a->v.z, b->v.z, t);\ - v->v.g = LERP(a->v.g, b->v.g, t);\ - v->uv = (LERP(a->uv & 0xFFFF, b->uv & 0xFFFF, t)) | (LERP(a->uv >> 16, b->uv >> 16, t) << 16);\ - } -*/ +void transformMesh_c(const MeshVertex* vertices, int32 count, int32 intensity) +{ + Vertex* res = gVerticesBase; - #define CLIP_XY(X, Y, X0, X1, input, output) {\ - const VertexUV *a, *b = input + pCount - 1;\ - for (int32 i = 0; i < pCount; i++) {\ - a = b;\ - b = input + i;\ - if (a->v.X < X0) {\ - if (b->v.X < X0) continue;\ - CLIP_AXIS(X, Y, X0, output);\ - } else if (a->v.X > X1) {\ - if (b->v.X > X1) continue;\ - CLIP_AXIS(X, Y, X1, output);\ - }\ - if (b->v.X < X0) {\ - CLIP_AXIS(X, Y, X0, output);\ - } else if (b->v.X > X1) {\ - CLIP_AXIS(X, Y, X1, output);\ - } else {\ - output[count++] = *b;\ - }\ - }\ - if (count < 3) return NULL;\ - } + int32 vg = X_CLAMP((intensity + gLightAmbient) >> 8, 0, 31); - #define ZNEAR (VIEW_MIN_F << FIXED_SHIFT >> FOV_SHIFT) + for (int32 i = 0; i < count; i++, res++) + { + int32 vx = vertices->x; + int32 vy = vertices->y; + int32 vz = vertices->z; + vertices++; + + const Matrix &m = matrixGet(); + int32 x = DP43(m.e00, m.e01, m.e02, m.e03, vx, vy, vz); + int32 y = DP43(m.e10, m.e11, m.e12, m.e13, vx, vy, vz); + int32 z = DP43(m.e20, m.e21, m.e22, m.e23, vx, vy, vz); + + uint32 clip = 0; + + if (z <= VIEW_MIN_F) { + clip = CLIP_NEAR; + z = VIEW_MIN_F; + } - #define CLIP_Z(input, output) {\ - const VertexUV *a, *b = input + pCount - 1;\ - for (int32 i = 0; i < pCount; i++) {\ - a = b;\ - b = input + i;\ - if (a->v.z < ZNEAR) {\ - if (b->v.z < ZNEAR) continue;\ - CLIP_NEAR(ZNEAR, output);\ - }\ - if (b->v.z < ZNEAR) {\ - CLIP_NEAR(ZNEAR, output);\ - } else {\ - output[count++] = *b;\ - }\ - }\ - if (count < 3) return NULL;\ - } + if (z >= VIEW_MAX_F) { + clip = CLIP_FAR; + z = VIEW_MAX_F; + } - int32 count = 0; + x >>= FIXED_SHIFT; + y >>= FIXED_SHIFT; + z >>= FIXED_SHIFT; - VertexUV *in = poly; - VertexUV *out = tmp; -/* - uint32 clipFlags = poly[0].v.clip | poly[1].v.clip | poly[2].v.clip; - if (pCount > 3) { - clipFlags |= poly[3].v.clip; - } + PERSPECTIVE(x, y, z); - if (clipFlags & 16) { - CLIP_Z(in, out); - swap(in, out); - pCount = count; - count = 0; - } -*/ - {//if (clipFlags & (1 | 2 | 4 | 8)) { - // clip x - CLIP_XY(x, y, clip.x0, clip.x1, in, out); + x += (FRAME_WIDTH >> 1); + y += (FRAME_HEIGHT >> 1); - pCount = count; - count = 0; + if (x < viewport.x0) clip |= CLIP_LEFT; + if (x > viewport.x1) clip |= CLIP_RIGHT; + if (y < viewport.y0) clip |= CLIP_TOP; + if (y > viewport.y1) clip |= CLIP_BOTTOM; - // clip y - CLIP_XY(y, x, clip.y0, clip.y1, out, in); - pCount = count; + res->x = x; + res->y = y; + res->z = z; + res->g = vg; + res->clip = clip; } - - return in; } -#ifdef DEBUG_OVERDRAW -#define FETCH_GT() 32 -#define FETCH_G(palIndex) 32 -#else -#define FETCH_T() curTile[(t & 0xFF00) | (t >> 24)] -#define FETCH_T_MIP() curTile[(t & 0xFF00) | (t >> 24) & mipMask] -#define FETCH_GT() lightmap[(g & 0x1F00) | FETCH_T()] -#define FETCH_G(palIndex) lightmap[(g & 0x1F00) | palIndex] -#endif -#define FETCH_GT_PAL() palette[FETCH_GT()] -#define FETCH_G_PAL(palIndex) palette[FETCH_G(palIndex)] - -struct Edge { - int32 h; - int32 x; - int32 g; - uint32 t; - int32 dx; - int32 dg; - uint32 dt; - - int32 index; - VertexUV* vert[8]; - - Edge() : h(0), dx(0), dg(0), dt(0) {} - - INLINE void stepG() { - x += dx; - g += dg; - } - - INLINE void stepGT() { - x += dx; - g += dg; - t += dt; - } +void faceAddRoomQuads_c(const RoomQuad* polys, int32 count) +{ + const Vertex* v = gVerticesBase; - INLINE bool nextG() { - if (index == 0) { - return false; + for (int32 i = 0; i < count; i++, polys++) + { + uint32 flags = polys->flags; + const Vertex* v0 = v + polys->indices[0]; + const Vertex* v1 = v + polys->indices[1]; + const Vertex* v2 = v + polys->indices[2]; + const Vertex* v3 = v + polys->indices[3]; + + uint32 c0 = v0->clip; + uint32 c1 = v1->clip; + uint32 c2 = v2->clip; + uint32 c3 = v3->clip; + + if (c0 & c1 & c2 & c3) + continue; + + if ((c0 | c1 | c2 | c3) & CLIP_MASK_VP) { + flags |= FACE_CLIPPED; } - VertexUV* v1 = vert[index--]; - VertexUV* v2 = vert[index]; + uint32 g0 = v0->g; + uint32 g1 = v1->g; + uint32 g2 = v2->g; + uint32 g3 = v3->g; - h = v2->v.y - v1->v.y; - x = v1->v.x << 16; - g = v1->v.g << 16; + if (g0 != g1 || g0 != g2 || g0 != g3) { + flags += FACE_GOURAUD; + } - if (h > 1) { - uint32 d = FixedInvU(h); + if (checkBackface(v0, v1, v2)) + continue; - dx = d * (v2->v.x - v1->v.x); - dg = d * (v2->v.g - v1->v.g); - } + int32 depth = X_MAX(v0->z, X_MAX(v1->z, X_MAX(v2->z, v3->z))) >> OT_SHIFT; - return true; + Face* f = faceAdd(depth); + f->flags = flags; + f->indices[0] = v0 - gVertices; + f->indices[1] = v1 - gVertices; + f->indices[2] = v2 - gVertices; + f->indices[3] = v3 - gVertices; } +} - INLINE bool nextGT() { - if (index == 0) { - return false; - } +void faceAddRoomTriangles_c(const RoomTriangle* polys, int32 count) +{ + const Vertex* v = gVerticesBase; - VertexUV* v1 = vert[index--]; - VertexUV* v2 = vert[index]; + for (int32 i = 0; i < count; i++, polys++) + { + uint32 flags = polys->flags; + const Vertex* v0 = v + polys->indices[0]; + const Vertex* v1 = v + polys->indices[1]; + const Vertex* v2 = v + polys->indices[2]; - h = v2->v.y - v1->v.y; - x = v1->v.x << 16; - g = v1->v.g << 16; - t = (v1->uv >> 16) | (v1->uv << 16); // TODO preprocess + uint32 c0 = v0->clip; + uint32 c1 = v1->clip; + uint32 c2 = v2->clip; - if (h > 1) { - uint32 d = FixedInvU(h); + if (c0 & c1 & c2) + continue; - dx = d * (v2->v.x - v1->v.x); - dg = d * (v2->v.g - v1->v.g); + if ((c0 | c1 | c2) & CLIP_MASK_VP) { + flags |= FACE_CLIPPED; + } - int32 du = d * ((v2->uv & 0xFFFF) - (v1->uv & 0xFFFF)); - int32 dv = d * ((v2->uv >> 16) - (v1->uv >> 16)); + uint32 g0 = v0->g; + uint32 g1 = v1->g; + uint32 g2 = v2->g; - dt = (du & 0xFFFF0000) | int16(dv >> 16); + if (g0 != g1 || g0 != g2) { + flags += FACE_GOURAUD; } + flags |= FACE_TRIANGLE; - return true; - } + if (checkBackface(v0, v1, v2)) + continue; - void build(VertexUV *vertices, int32 count, int32 t, int32 b, int32 incr) { - vert[index = 0] = vertices + b; + int32 depth = X_MAX(v0->z, X_MAX(v1->z, v2->z)) >> OT_SHIFT; - for (int i = 1; i < count; i++) { - b = (b + incr) % count; + Face* f = faceAdd(depth); + f->flags = flags; + f->indices[0] = v0 - gVertices; + f->indices[1] = v1 - gVertices; + f->indices[2] = v2 - gVertices; + } +} - VertexUV* v = vertices + b; +void faceAddMeshQuads_c(const MeshQuad* polys, int32 count) +{ + const Vertex* v = gVerticesBase; - if (vert[index]->v.x != v->v.x || vert[index]->v.y != v->v.y) { - vert[++index] = v; - } + for (int32 i = 0; i < count; i++, polys++) + { + uint32 flags = polys->flags; + const Vertex* v0 = v + polys->indices[0]; + const Vertex* v1 = v + polys->indices[1]; + const Vertex* v2 = v + polys->indices[2]; + const Vertex* v3 = v + polys->indices[3]; - if (b == t) { - break; - } - } - } -}; + if (checkBackface(v0, v1, v2)) + continue; -INLINE void scanlineG(uint16* buffer, int32 x1, int32 x2, uint8 palIndex, uint32 g, uint32 dgdx) { - #ifdef USE_MODE_5 - uint16* pixel = buffer + x1; + uint32 c0 = v0->clip; + uint32 c1 = v1->clip; + uint32 c2 = v2->clip; + uint32 c3 = v3->clip; - if (x1 & 1) { - *pixel++ = FETCH_G_PAL(palIndex); - g += dgdx; - x1++; + if (c0 & c1 & c2 & c3) + continue; - if (x1 >= x2) { - return; - } + if ((c0 | c1 | c2 | c3) & CLIP_MASK_VP) { + flags |= FACE_CLIPPED; } - int32 width2 = (x2 - x1) >> 1; + int32 depth = (v0->z + v1->z + v2->z + v3->z) >> (2 + OT_SHIFT); - dgdx <<= 1; + Face* f = faceAdd(depth); + f->flags = flags; + f->indices[0] = v0 - gVertices; + f->indices[1] = v1 - gVertices; + f->indices[2] = v2 - gVertices; + f->indices[3] = v3 - gVertices; + } +} - while (width2--) { - uint32 p = FETCH_G_PAL(palIndex); - g += dgdx; +void faceAddMeshTriangles_c(const MeshTriangle* polys, int32 count) +{ + const Vertex* v = gVerticesBase; - *(uint32*)pixel = p | (p << 16); - pixel += 2; - } + for (int32 i = 0; i < count; i++, polys++) + { + uint32 flags = polys->flags; + const Vertex* v0 = v + polys->indices[0]; + const Vertex* v1 = v + polys->indices[1]; + const Vertex* v2 = v + polys->indices[2]; - if (x2 & 1) { - *pixel++ = FETCH_G_PAL(palIndex); - } - #else - if (x1 & 1) - { - uint16 &p = *(uint16*)((uint8*)buffer + x1 - 1); - p = (p & 0x00FF) | (FETCH_G(palIndex) << 8); - g += dgdx; - x1++; - } + if (checkBackface(v0, v1, v2)) + continue; - int32 width = (x2 - x1) >> 1; - uint16* pixel = (uint16*)((uint8*)buffer + x1); + uint32 c0 = v0->clip; + uint32 c1 = v1->clip; + uint32 c2 = v2->clip; - dgdx <<= 1; + if (c0 & c1 & c2) + continue; - if (width && (x1 & 3)) - { - uint16 p = FETCH_G(palIndex); - *pixel++ = p | (FETCH_G(palIndex) << 8); + if ((c0 | c1 | c2) & CLIP_MASK_VP) { + flags |= FACE_CLIPPED; + } + flags |= FACE_TRIANGLE; - g += dgdx; + int32 depth = (v0->z + v1->z + v2->z + v2->z) >> (2 + OT_SHIFT); - width--; - } + Face* f = faceAdd(depth); + f->flags = flags; + f->indices[0] = v0 - gVertices; + f->indices[1] = v1 - gVertices; + f->indices[2] = v2 - gVertices; + } +} - while (width-- > 0) - { - uint32 p = FETCH_G(palIndex); - p |= (FETCH_G(palIndex) << 8); +bool transformBoxRect(const AABBs* box, RectMinMax* rect) +{ + const Matrix &m = matrixGet(); - g += dgdx; + if ((m.e23 < VIEW_MIN_F) || (m.e23 >= VIEW_MAX_F)) + return false; - if (width-- > 0) - { - p |= (FETCH_G(palIndex) << 16); - p |= (FETCH_G(palIndex) << 24); + vec3i v[8]; + v[0] = _vec3i( box->minX, box->minY, box->minZ ), + v[1] = _vec3i( box->maxX, box->minY, box->minZ ), + v[2] = _vec3i( box->minX, box->maxY, box->minZ ), + v[3] = _vec3i( box->maxX, box->maxY, box->minZ ), + v[4] = _vec3i( box->minX, box->minY, box->maxZ ), + v[5] = _vec3i( box->maxX, box->minY, box->maxZ ), + v[6] = _vec3i( box->minX, box->maxY, box->maxZ ), + v[7] = _vec3i( box->maxX, box->maxY, box->maxZ ); - g += dgdx; + *rect = RectMinMax( INT_MAX, INT_MAX, INT_MIN, INT_MIN ); - *(uint32*)pixel = p; - pixel += 2; - } else { - *(uint16*)pixel = p; - pixel += 1; - } - } + for (int32 i = 0; i < 8; i++) + { + int32 z = DP43(m.e20, m.e21, m.e22, m.e23, v[i].x, v[i].y, v[i].z); - if (x2 & 1) - { - *pixel = (*pixel & 0xFF00) | FETCH_G(palIndex); - } - #endif -} + if (z < VIEW_MIN_F || z >= VIEW_MAX_F) + continue; -INLINE void scanlineGT(uint16* buffer, int32 x1, int32 x2, uint32 g, uint32 t, uint32 dgdx, uint32 dtdx) { - #ifdef USE_MODE_5 - uint16* pixel = buffer + x1; + int32 x = DP43(m.e00, m.e01, m.e02, m.e03, v[i].x, v[i].y, v[i].z); + int32 y = DP43(m.e10, m.e11, m.e12, m.e13, v[i].x, v[i].y, v[i].z); - if (x1 & 1) { - *pixel++ = FETCH_GT_PAL(); - t += dtdx; - g += dgdx; - x1++; + x >>= FIXED_SHIFT; + y >>= FIXED_SHIFT; + z >>= FIXED_SHIFT; - if (x1 >= x2) { - return; - } - } + PERSPECTIVE(x, y, z); - int32 width2 = (x2 - x1) >> 1; + if (x < rect->x0) rect->x0 = x; + if (x > rect->x1) rect->x1 = x; + if (y < rect->y0) rect->y0 = y; + if (y > rect->y1) rect->y1 = y; + } - dgdx <<= 1; + rect->x0 += (FRAME_WIDTH / 2); + rect->y0 += (FRAME_HEIGHT / 2); + rect->x1 += (FRAME_WIDTH / 2); + rect->y1 += (FRAME_HEIGHT / 2); - while (width2--) { - uint32 p = FETCH_GT_PAL(); - t += dtdx; - p |= FETCH_GT_PAL() << 16; - t += dtdx; - g += dgdx; + return true; +} - *(uint32*)pixel = p; - pixel += 2; - } +int32 rectIsVisible(const RectMinMax* rect) +{ + if (rect->x0 > rect->x1 || + rect->x0 > viewport.x1 || + rect->x1 < viewport.x0 || + rect->y0 > viewport.y1 || + rect->y1 < viewport.y0) return 0; // not visible + + if (rect->x0 < viewport.x0 || + rect->x1 > viewport.x1 || + rect->y0 < viewport.y0 || + rect->y1 > viewport.y1) return -1; // clipped + + return 1; // fully visible +} - if (x2 & 1) { - *pixel++ = FETCH_GT_PAL(); - } +int32 boxIsVisible_c(const AABBs* box) +{ + RectMinMax rect; + if (!transformBoxRect(box, &rect)) + return 0; // not visible + return rectIsVisible(&rect); +} - #else +int32 sphereIsVisible_c(int32 sx, int32 sy, int32 sz, int32 r) +{ + Matrix &m = matrixGet(); - if (x1 & 1) - { - uint16 &p = *(uint16*)((uint8*)buffer + x1 - 1); - p = (p & 0x00FF) | (FETCH_GT() << 8); - t += dtdx; - g += dgdx; - x1++; - } + if (abs(sx) < r && abs(sy) < r && abs(sz) < r) + return 1; - int32 width = (x2 - x1) >> 1; - uint16* pixel = (uint16*)((uint8*)buffer + x1); + int32 z = DP33(m.e20, m.e21, m.e22, sx, sy, sz); - dgdx <<= 1; + if (z < 0) + return 0; - if (width && (x1 & 3)) - { - uint16 p = FETCH_GT(); - t += dtdx; - *pixel++ = p | (FETCH_GT() << 8); - t += dtdx; + int32 x = DP33(m.e00, m.e01, m.e02, sx, sy, sz); + int32 y = DP33(m.e10, m.e11, m.e12, sx, sy, sz); - g += dgdx; + x >>= FIXED_SHIFT; + y >>= FIXED_SHIFT; + z >>= FIXED_SHIFT; - width--; - } + z = PERSPECTIVE_DZ(z); + if (z >= DIV_TABLE_SIZE) z = DIV_TABLE_SIZE - 1; + int32 d = FixedInvU(z); + x = (x * d) >> 12; + y = (y * d) >> 12; + r = (r * d) >> 12; - while (width-- > 0) - { - uint32 p = FETCH_GT(); - t += dtdx; - p |= (FETCH_GT() << 8); - t += dtdx; + x += (FRAME_WIDTH / 2); + y += (FRAME_HEIGHT / 2); - g += dgdx; + int32 rMinX = x - r; + int32 rMinY = y - r; + int32 rMaxX = x + r; + int32 rMaxY = y + r; - if (width-- > 0) - { - p |= (FETCH_GT() << 16); - t += dtdx; - p |= (FETCH_GT() << 24); - t += dtdx; - - g += dgdx; - - *(uint32*)pixel = p; - pixel += 2; - } else { - *(uint16*)pixel = p; - pixel += 1; - } - } + if (rMinX > viewport.x1 || + rMaxX < viewport.x0 || + rMinY > viewport.y1 || + rMaxY < viewport.y0) return 0; // not visible - if (x2 & 1) - { - *pixel = (*pixel & 0xFF00) | FETCH_GT(); - } - #endif + return 1; } -void rasterizeG(int16 y, int32 palIndex, Edge &L, Edge &R) { - uint16 *buffer = (uint16*)fb + y * (WIDTH / PIXEL_SIZE); - - while (1) - { - while (L.h <= 0) - { - if (!L.nextG()) - { - return; - } - } +typedef void (*RasterProc)(uint16* pixel, const VertexLink* L, const VertexLink* R); + +RasterProc gRasterProc[FACE_TYPE_MAX] = { // IWRAM + rasterizeS, + rasterizeF, + rasterizeFT, + rasterizeFTA, + rasterizeGT, + rasterizeGTA, + rasterizeSprite, + rasterizeFillS, + rasterizeLineH, + rasterizeLineV +}; - while (R.h <= 0) - { - if (!R.nextG()) - { - return; - } - } +X_NOINLINE void rasterize_c(uint32 flags, VertexLink* top) +{ + uint8* pixel = (uint8*)fb + top->v.y * FRAME_WIDTH; - int32 h = MIN(L.h, R.h); - L.h -= h; - R.h -= h; + uint32 type = (flags >> FACE_TYPE_SHIFT) & FACE_TYPE_MASK; - while (h--) { - int32 x1 = L.x >> 16; - int32 x2 = R.x >> 16; + VertexLink* R = (type == FACE_TYPE_F) ? (VertexLink*)(flags & 0xFF) : top; - int32 width = x2 - x1; - if (width > 0) - { - uint32 d = FixedInvU(width); + gRasterProc[type]((uint16*)pixel, top, R); +} - uint32 dgdx = d * ((R.g - L.g) >> 8) >> 16; +void flush_c() +{ +#ifdef PROFILING + #if !defined(PROFILE_FRAMETIME) && !defined(PROFILE_SOUNDTIME) + gCounters[CNT_VERT] += gVerticesBase - gVertices; + gCounters[CNT_POLY] += gFacesBase - gFaces; + #endif +#endif - scanlineG(buffer, x1, x2, palIndex, L.g >> 8, dgdx); - } + gVerticesBase = gVertices; - buffer += WIDTH / PIXEL_SIZE; + if (gFacesBase == gFaces) + return; - L.stepG(); - R.stepG(); - } - } -} + gFacesBase = gFaces; -void rasterizeGT(int16 y, Edge &L, Edge &R) { - uint16 *buffer = (uint16*)fb + y * (WIDTH / PIXEL_SIZE); + PROFILE(CNT_FLUSH); - while (1) + for (int32 i = OT_SIZE - 1; i >= 0; i--) { - while (L.h <= 0) - { - if (!L.nextGT()) - { - return; - } - } + if (!gOT[i]) continue; - while (R.h <= 0) - { - if (!R.nextGT()) - { - return; - } - } + Face *face = gOT[i]; + gOT[i] = NULL; + + do { + uint32 flags = face->flags; - int32 h = MIN(L.h, R.h); - L.h -= h; - R.h -= h; + VertexLink v[16]; - while (h--) { - int32 x1 = L.x >> 16; - int32 x2 = R.x >> 16; + uint32 type = (flags >> FACE_TYPE_SHIFT) & FACE_TYPE_MASK; - int32 width = x2 - x1; - if (width > 0) + if (type <= FACE_TYPE_GTA) { - uint32 d = FixedInvU(width); + if (type > FACE_TYPE_F) + { + const Texture &tex = level.textures[flags & FACE_TEXTURE]; + gTile = (uint8*)tex.tile; + + v[0].t.t = 0xFF00FF00 & (tex.uv01); + v[1].t.t = 0xFF00FF00 & (tex.uv01 << 8); + v[2].t.t = 0xFF00FF00 & (tex.uv23); + v[3].t.t = 0xFF00FF00 & (tex.uv23 << 8); + } - uint32 dgdx = d * ((R.g - L.g) >> 8) >> 16; + v[0].v = gVertices[face->indices[0]]; + v[1].v = gVertices[face->indices[1]]; + v[2].v = gVertices[face->indices[2]]; + if (!(flags & FACE_TRIANGLE)) { + v[3].v = gVertices[face->indices[3]]; + } - uint32 u = d * ((R.t >> 16) - (L.t >> 16)); - uint32 v = d * ((R.t & 0xFFFF) - (L.t & 0xFFFF)); - uint32 dtdx = (u & 0xFFFF0000) | (v >> 16); + if (flags & FACE_CLIPPED) { + drawPoly(flags, v); + } else { + if (flags & FACE_TRIANGLE) { + drawTriangle(flags, v); + } else { + drawQuad(flags, v); + } + } + } + else + { + const Vertex *vert = gVertices + face->indices[0]; + v[0].v = vert[0]; + v[1].v = vert[1]; + + if (type == FACE_TYPE_SPRITE) + { + const Sprite &sprite = level.sprites[flags & FACE_TEXTURE]; + gTile = (uint8*)sprite.tile; + v[0].t.t = (sprite.uwvh) & (0xFF00FF00); + v[1].t.t = (sprite.uwvh) & (0xFF00FF00 >> 8); + } - scanlineGT(buffer, x1, x2, L.g >> 8, L.t, dgdx, dtdx); - }; + rasterize(flags, v); + } - buffer += WIDTH / PIXEL_SIZE; + face = face->next; - L.stepGT(); - R.stepGT(); - } + } while (face); } } +#endif -void drawTriangle(const Face* face, VertexUV *v) { - VertexUV *v1 = v + 0, - *v2 = v + 1, - *v3 = v + 2; - - sortVertices(v1, v2, v3); +VertexLink* clipPoly(VertexLink* poly, VertexLink* tmp, int32 &pCount) +{ + #define LERP_SHIFT 6 + #define LERP(a,b,t) (b + ((a - b) * t >> LERP_SHIFT)) + //#define LERP2(a,b,ta,tb) LERP(a,b,t) + #define LERP2(a,b,ta,tb) (b + (((a - b) * ta / tb) >> LERP_SHIFT) ) // less gaps between clipped polys, but slow - int32 temp = (v2->v.y - v1->v.y) * FixedInvU(v3->v.y - v1->v.y); + #define CLIP_AXIS(X, Y, edge, output) {\ + int32 ta = (edge - b->v.X) << LERP_SHIFT;\ + int32 tb = (a->v.X - b->v.X);\ + ASSERT(tb != 0);\ + int32 t = ta / tb;\ + VertexLink* v = output + count++;\ + v->v.X = edge;\ + v->v.Y = LERP2(a->v.Y, b->v.Y, ta, tb);\ + v->v.g = LERP(a->v.g, b->v.g, t);\ + v->t.uv.u = LERP(a->t.uv.u, b->t.uv.u, t);\ + v->t.uv.v = LERP(a->t.uv.v, b->t.uv.v, t);\ + } - int32 longest = ((temp * (v3->v.x - v1->v.x)) >> 16) + (v1->v.x - v2->v.x); - if (longest == 0) - { - return; + #define CLIP_XY(X, Y, X0, X1, input, output) {\ + const VertexLink *a, *b = input + pCount - 1;\ + for (int32 i = 0; i < pCount; i++) {\ + a = b;\ + b = input + i;\ + if (a->v.X < X0) {\ + if (b->v.X < X0) continue;\ + CLIP_AXIS(X, Y, X0, output);\ + } else if (a->v.X > X1) {\ + if (b->v.X > X1) continue;\ + CLIP_AXIS(X, Y, X1, output);\ + }\ + if (b->v.X < X0) {\ + CLIP_AXIS(X, Y, X0, output);\ + } else if (b->v.X > X1) {\ + CLIP_AXIS(X, Y, X1, output);\ + } else {\ + output[count++] = *b;\ + }\ + }\ + if (count < 3) return NULL;\ } - Edge L, R; + int32 count = 0; - if (longest < 0) - { - R.vert[0] = v3; - R.vert[1] = v2; - R.vert[2] = v1; - R.index = 2; - L.vert[0] = v3; - L.vert[1] = v1; - L.index = 1; - } - else - { - L.vert[0] = v3; - L.vert[1] = v2; - L.vert[2] = v1; - L.index = 2; - R.vert[0] = v3; - R.vert[1] = v1; - R.index = 1; - } + VertexLink *in = poly; + VertexLink *out = tmp; - if (face->flags & FACE_COLORED) { - rasterizeG(v1->v.y, face->flags & FACE_TEXTURE, L, R); - } else { - rasterizeGT(v1->v.y, L, R); - } -} + // clip x + CLIP_XY(x, y, viewport.x0, viewport.x1, in, out); -void drawQuad(const Face* face, VertexUV *v) { - VertexUV *v1 = v + 0, - *v2 = v + 1, - *v3 = v + 2, - *v4 = v + 3; + pCount = count; + count = 0; - int32 minY = 0x7FFF; - int32 maxY = -0x7FFF; - int32 t = 0, b = 0; + // clip y + CLIP_XY(y, x, viewport.y0, viewport.y1, out, in); + pCount = count; - VertexUV* poly[8] = { v1, v2, v3, v4, v1, v2, v3, v4 }; + return in; +} - for (int i = 0; i < 4; i++) { - VertexUV *v = poly[i]; +void renderInit() +{ + gVerticesBase = gVertices; + gFacesBase = gFaces; +} - if (v->v.y < minY) { - minY = v->v.y; - t = i; +extern "C" X_NOINLINE void drawTriangle(uint32 flags, VertexLink* v) +{ + VertexLink* v0 = v + 0; + VertexLink* v1 = v + 1; + VertexLink* v2 = v + 2; + + v0->next = v1 - v0; + v1->next = v2 - v1; + v2->next = v0 - v2; + v0->prev = v2 - v0; + v1->prev = v0 - v1; + v2->prev = v1 - v2; + + VertexLink* top; + + if (v0->v.y < v1->v.y) { + if (v0->v.y < v2->v.y) { + top = v0; + } else { + top = v2; } - - if (v->v.y > maxY) { - maxY = v->v.y; - b = i; + } else { + if (v1->v.y < v2->v.y) { + top = v1; + } else { + top = v2; } } - Edge L, R; - - v1 = poly[t]; - - L.vert[L.index = 0] = poly[b]; - R.vert[R.index = 0] = poly[b]; - - int32 ib = b; - do { - L.vert[++L.index] = poly[++b]; - } while (poly[b] != v1); - - b = ib + 4; - do { - R.vert[++R.index] = poly[--b]; - } while (poly[b] != v1); + rasterize(flags, top); +} - if (face->flags & FACE_COLORED) { - rasterizeG(v1->v.y, face->flags & FACE_TEXTURE, L, R); +extern "C" X_NOINLINE void drawQuad(uint32 flags, VertexLink* v) +{ + VertexLink* v0 = v + 0; + VertexLink* v1 = v + 1; + VertexLink* v2 = v + 2; + VertexLink* v3 = v + 3; + + v0->next = v1 - v0; + v1->next = v2 - v1; + v2->next = v3 - v2; + v3->next = v0 - v3; + v0->prev = v3 - v0; + v1->prev = v0 - v1; + v2->prev = v1 - v2; + v3->prev = v2 - v3; + + VertexLink* top; + + if (v0->v.y < v1->v.y) { + if (v0->v.y < v2->v.y) { + top = (v0->v.y < v3->v.y) ? v0 : v3; + } else { + top = (v2->v.y < v3->v.y) ? v2 : v3; + } } else { - rasterizeGT(v1->v.y, L, R); + if (v1->v.y < v2->v.y) { + top = (v1->v.y < v3->v.y) ? v1 : v3; + } else { + top = (v2->v.y < v3->v.y) ? v2 : v3; + } } + + rasterize(flags, top); } -void drawPoly(Face* face, VertexUV *v) { - VertexUV tmp[16]; +extern "C" X_NOINLINE void drawPoly(uint32 flags, VertexLink* v) +{ + VertexLink tmp[16]; - int32 count = (face->flags & FACE_TRIANGLE) ? 3 : 4; + int32 count = (flags & FACE_TRIANGLE) ? 3 : 4; v = clipPoly(v, tmp, count); if (!v) return; - if (count <= 4) { - face->indices[0] = 0; - face->indices[1] = 1; - face->indices[2] = 2; - face->indices[3] = 3; - + if (count <= 4) + { if (count == 3) { - drawTriangle(face, v); + + if (v[0].v.y == v[1].v.y && + v[0].v.y == v[2].v.y) + return; + + drawTriangle(flags, v); } else { - drawQuad(face, v); + + if (v[0].v.y == v[1].v.y && + v[0].v.y == v[2].v.y && + v[0].v.y == v[3].v.y) + return; + + drawQuad(flags, v); } return; } - int32 minY = 0x7FFF; - int32 maxY = -0x7FFF; - int32 t = 0, b = 0; + VertexLink* top = v; + top->next = (v + 1) - top; + top->prev = (v + count - 1) - top; + + bool skip = true; - for (int i = 0; i < count; i++) { - VertexUV *p = v + i; + for (int32 i = 1; i < count; i++) + { + int8 next = i + 1; + int8 prev = i - 1; - if (p->v.y < minY) { - minY = p->v.y; - t = i; + if (next >= count) { + next -= count; } - if (p->v.y > maxY) { - maxY = p->v.y; - b = i; + if (prev < 0) { + prev += count; } - } - Edge L, R; + next -= i; + prev -= i; - L.build(v, count, t, b, count + 1); - R.build(v, count, t, b, count - 1); + VertexLink *p = v + i; + p->next = next; + p->prev = prev; - if (face->flags & FACE_COLORED) { - rasterizeG(v[t].v.y, face->flags & FACE_TEXTURE, L, R); - } else { - rasterizeGT(v[t].v.y, L, R); + if (p->v.y != top->v.y) + { + if (p->v.y < top->v.y) { + top = p; + } + skip = false; + } } + + if (skip) { + return; // zero height poly + } + + rasterize(flags, top); } -void drawGlyph(const Sprite *sprite, int32 x, int32 y) { - int32 w = sprite->r - sprite->l; - int32 h = sprite->b - sprite->t; +void faceAddRoom(const Room* room) +{ + if (room->info->quadsCount > 0) { + faceAddRoomQuads(room->data.quads, room->info->quadsCount); + } - w = (w >> 1) << 1; // make it even + if (room->info->trianglesCount > 0) { + faceAddRoomTriangles(room->data.triangles, room->info->trianglesCount); + } +} - int32 ix = x + sprite->l; - int32 iy = y + sprite->t; +void faceAddMesh(const MeshQuad* quads, const MeshTriangle* triangles, int32 qCount, int32 tCount) +{ + if (qCount > 0) { + faceAddMeshQuads(quads, qCount); + } - uint16* ptr = (uint16*)fb + iy * (WIDTH / PIXEL_SIZE); + if (tCount > 0) { + faceAddMeshTriangles(triangles, tCount); + } +} -#ifdef USE_MODE_5 - ptr += ix; -#else - ptr += ix >> 1; -#endif +void clear() +{ + dmaFill((void*)fb, 0, VRAM_WIDTH * FRAME_HEIGHT * 2); +} - const uint8* glyphData = tiles[sprite->tile] + 256 * sprite->v + sprite->u; +void renderRoom(const Room* room) +{ + int32 vCount = room->info->verticesCount; + if (vCount <= 0) + return; - while (h--) + if ((gVerticesBase - gVertices) + vCount > MAX_VERTICES) { - #ifdef USE_MODE_5 - for (int i = 0; i < w; i++) { - if (glyphData[i] == 0) continue; + ASSERT(false); + return; + } - ptr[i] = palette[glyphData[i]]; + { + PROFILE(CNT_TRANSFORM); + if (ROOM_FLAG_WATER(room->info->flags)) { + transformRoomUW(room->data.vertices, vCount); + } else { + transformRoom(room->data.vertices, vCount); } - #else - const uint8* p = glyphData; + } - for (int i = 0; i < (w / 2); i++) { + { + PROFILE(CNT_ADD); + faceAddRoom(room); + } - if (p[0] || p[1]) { - uint16 d = ptr[i]; + gVerticesBase += vCount; +} - if (p[0]) d = (d & 0xFF00) | p[0]; - if (p[1]) d = (d & 0x00FF) | (p[1] << 8); +void renderMesh(const Mesh* mesh) +{ + int32 vCount = mesh->vCount; + if (vCount <= 0) + return; - ptr[i] = d; - } + if ((gVerticesBase - gVertices) + vCount > MAX_VERTICES) + { + ASSERT(false); + return; + } - p += 2; - } - #endif + int32 fCount = mesh->rCount + mesh->tCount; + if ((gFacesBase - gFaces) + fCount > MAX_FACES) + { + ASSERT(false); + return; + } + + const uint8* ptr = (uint8*)mesh + sizeof(Mesh); + + const MeshVertex* vertices = (MeshVertex*)ptr; + ptr += vCount * sizeof(vertices[0]); + + MeshQuad* quads = (MeshQuad*)ptr; + ptr += mesh->rCount * sizeof(MeshQuad); + + MeshTriangle* triangles = (MeshTriangle*)ptr; + + { + PROFILE(CNT_TRANSFORM); + transformMesh(vertices, vCount, mesh->intensity); + } - ptr += WIDTH / PIXEL_SIZE; - glyphData += 256; + { + PROFILE(CNT_ADD); + faceAddMesh(quads, triangles, mesh->rCount, mesh->tCount); } + + gVerticesBase += vCount; } -void faceAddQuad(uint16 flags, const Index* indices, int32 startVertex) { -#ifdef _WIN32 - if (gFacesCount >= MAX_FACES) { - DebugBreak(); +void renderShadow(int32 x, int32 z, int32 sx, int32 sz) +{ + if (gVerticesBase - gVertices + 8 > MAX_VERTICES) { + ASSERT(false); + return; } -#endif - Vertex* v = gVertices + startVertex; - Vertex* v1 = v + indices[0]; - Vertex* v2 = v + indices[1]; - Vertex* v3 = v + indices[2]; - Vertex* v4 = v + indices[3]; - if ((v1->clip | v2->clip | v3->clip | v4->clip) & 16) // TODO znear clip + if (gFacesBase - gFaces + 3 > MAX_FACES) { + ASSERT(false); return; + } + + int16 xns1 = x - sx; + int16 xps1 = x + sx; + int16 xns2 = xns1 - sx; + int16 xps2 = xps1 + sx; + + int16 zns1 = z - sz; + int16 zps1 = z + sz; + int16 zns2 = zns1 - sz; + int16 zps2 = zps1 + sz; + + MeshVertex v[8]; + v[0].x = xns1; v[0].y = 0; v[0].z = zps2; + v[1].x = xps1; v[1].y = 0; v[1].z = zps2; + v[2].x = xps2; v[2].y = 0; v[2].z = zps1; + v[3].x = xps2; v[3].y = 0; v[3].z = zns1; + v[4].x = xps1; v[4].y = 0; v[4].z = zns2; + v[5].x = xns1; v[5].y = 0; v[5].z = zns2; + v[6].x = xns2; v[6].y = 0; v[6].z = zns1; + v[7].x = xns2; v[7].y = 0; v[7].z = zps1; + + transformMesh(v, 8, 0); + faceAddMeshQuads(gShadowQuads, 3); + + gVerticesBase += 8; +} - if (checkBackface(v1, v2, v3)) +X_NOINLINE void renderFill(int32 x, int32 y, int32 width, int32 height, int32 shade, int32 z) +{ + if (gVerticesBase - gVertices + 2 > MAX_VERTICES) { + ASSERT(false); return; + } - if (v1->clip & v2->clip & v3->clip & v4->clip) + if (gFacesBase - gFaces + 1 > MAX_FACES) { + ASSERT(false); return; + } - int32 depth = (v1->z + v2->z + v3->z + v4->z) >> 2; + gVerticesBase[0].x = x; + gVerticesBase[0].y = y; + gVerticesBase[0].g = shade; - if (v1->clip | v2->clip | v3->clip | v4->clip) { - flags |= FACE_CLIPPED; - } + gVerticesBase[1].x = width; + gVerticesBase[1].y = height; - Face *f = gFaces + gFacesCount; - gFacesSorted[gFacesCount++] = f; - f->flags = flags; - f->depth = depth; - f->start = startVertex + indices[0]; - f->indices[0] = 0; - f->indices[1] = indices[1] - indices[0]; - f->indices[2] = indices[2] - indices[0]; - f->indices[3] = indices[3] - indices[0]; + Face* f = faceAdd(z); + f->flags = (FACE_TYPE_FILL_S << FACE_TYPE_SHIFT); + f->indices[0] = gVerticesBase - gVertices; + + gVerticesBase += 2; } -void faceAddTriangle(uint16 flags, const Index* indices, int32 startVertex) { -#ifdef _WIN32 - if (gFacesCount >= MAX_FACES) { - DebugBreak(); +X_NOINLINE void renderLine(int32 x, int32 y, int32 width, int32 height, int32 index, int32 z) +{ + if (gVerticesBase - gVertices + 2 > MAX_VERTICES) { + ASSERT(false); + return; } -#endif - Vertex* v = gVertices + startVertex; - Vertex* v1 = v + indices[0]; - Vertex* v2 = v + indices[1]; - Vertex* v3 = v + indices[2]; - if ((v1->clip | v2->clip | v3->clip) & 16) // TODO znear clip + if (gFacesBase - gFaces + 1 > MAX_FACES) { + ASSERT(false); return; + } + + ASSERT(width == 1 || height == 1); + ASSERT(width > 0); + ASSERT(height > 0); + + gVerticesBase[0].x = x; + gVerticesBase[0].y = y; + gVerticesBase[0].g = index; - if (checkBackface(v1, v2, v3)) + gVerticesBase[1].x = width; + gVerticesBase[1].y = height; + + int32 idx = gVerticesBase - gVertices; + + Face* f = faceAdd(z); + f->flags = (height == 1) ? (FACE_TYPE_LINE_H << FACE_TYPE_SHIFT) : (FACE_TYPE_LINE_V << FACE_TYPE_SHIFT); + f->indices[0] = idx; + + gVerticesBase += 2; +} + +void renderSprite(int32 vx, int32 vy, int32 vz, int32 vg, int32 index) +{ + if (gVerticesBase - gVertices + 2 > MAX_VERTICES) { + ASSERT(false); return; + } - if (v1->clip & v2->clip & v3->clip) + if (gFacesBase - gFaces + 1 > MAX_FACES) { + ASSERT(false); return; + } + + const Matrix &m = matrixGet(); + + vx -= gCameraViewPos.x; + vy -= gCameraViewPos.y; + vz -= gCameraViewPos.z; - int32 depth = (v1->z + v2->z + v3->z) / 3; + int32 z = DP33(m.e20, m.e21, m.e22, vx, vy, vz); - if (v1->clip | v2->clip | v3->clip) { - flags |= FACE_CLIPPED; + if (z < VIEW_MIN_F || z >= VIEW_MAX_F) + { + return; } - Face *f = gFaces + gFacesCount; - gFacesSorted[gFacesCount++] = f; - f->flags = flags | FACE_TRIANGLE; - f->depth = depth; - f->start = startVertex + indices[0]; - f->indices[0] = 0; - f->indices[1] = indices[1] - indices[0]; - f->indices[2] = indices[2] - indices[0]; -} + int32 x = DP33(m.e00, m.e01, m.e02, vx, vy, vz); + int32 y = DP33(m.e10, m.e11, m.e12, vx, vy, vz); -void faceSort(Face** faces, int32 L, int32 R) { - int32 i = L; - int32 j = R; - int16 depth = faces[(i + j) >> 1]->depth; + x >>= FIXED_SHIFT; + y >>= FIXED_SHIFT; + z >>= FIXED_SHIFT; - while (i <= j) { - while (faces[i]->depth > depth) i++; - while (faces[j]->depth < depth) j--; + const Sprite* sprite = level.sprites + index; - if (i <= j) { - swap(faces[i++], faces[j--]); - } - }; + int32 l = x + sprite->l; + int32 r = x + sprite->r; + int32 t = y + sprite->t; + int32 b = y + sprite->b; - if (L < j) faceSort(faces, L, j); - if (R > i) faceSort(faces, i, R); -} + // TODO one projection + PERSPECTIVE(l, t, z); -#ifdef DEBUG_FACES - int32 gFacesCountMax, gVerticesCountMax; -#endif + l += (FRAME_WIDTH >> 1); + if (l >= viewport.x1) return; -void flush() { - if (gFacesCount) { - faceSort(gFacesSorted, 0, gFacesCount - 1); + t += (FRAME_HEIGHT >> 1); + if (t >= viewport.y1) return; - //const uint16 mips[] = { 0xFFFF, 0xFEFE, 0xFCFC, 0xF8F8 }; + PERSPECTIVE(r, b, z); - for (int32 i = 0; i < gFacesCount; i++) { - Face *face = gFacesSorted[i]; + r += (FRAME_WIDTH >> 1); + if (r < viewport.x0) return; - // TODO - //mipMask = mips[MIN(3, f.depth / 2048)]; + b += (FRAME_HEIGHT >> 1); + if (b < viewport.y0) return; - VertexUV v[16]; + if (l == r) return; + if (t == b) return; - uint32 flags = face->flags; + if (z > FOG_MIN) + { + vg += (z - FOG_MIN) << FOG_SHIFT; + if (vg > 8191) { + vg = 8191; + } + } + vg >>= 8; - if (!(flags & FACE_COLORED)) { - const Texture &tex = textures[face->flags & FACE_TEXTURE]; - curTile = tiles[tex.tile]; - v[0].uv = tex.uv0; - v[1].uv = tex.uv1; - v[2].uv = tex.uv2; - v[3].uv = tex.uv3; - } + Vertex* v1 = gVerticesBase++; + v1->x = l; + v1->y = t; + //v1->z = z; + v1->g = vg; - Vertex *p = gVertices + face->start; - v[0].v = p[0]; - v[1].v = p[face->indices[1]]; - v[2].v = p[face->indices[2]]; - if (!(flags & FACE_TRIANGLE)) { - v[3].v = p[face->indices[3]]; - } + Vertex* v2 = gVerticesBase++; + v2->x = r; + v2->y = b; + //v2->z = z; + //v2->g = vg; - if (flags & FACE_CLIPPED) { - drawPoly(face, v); - } else { - if (flags & FACE_TRIANGLE) { - drawTriangle(face, v); - } else { - drawQuad(face, v); - } - }; - } + int32 depth = X_MAX(0, z - 128); // depth hack + + Face* f = faceAdd(depth >> OT_SHIFT); + f->flags = (FACE_TYPE_SPRITE << FACE_TYPE_SHIFT) | index; + f->indices[0] = v1 - gVertices; + + gVerticesBase += 2; +} + +void renderGlyph(int32 vx, int32 vy, int32 index) +{ + if (gVerticesBase - gVertices + 2 > MAX_VERTICES) { + ASSERT(false); + return; } -#ifdef DEBUG_FACES - if (gFacesCount > gFacesCountMax) gFacesCountMax = gFacesCount; - if (gVerticesCount > gVerticesCountMax) gVerticesCountMax = gVerticesCount; - printf("f: %d v: %d\n", gFacesCountMax, gVerticesCountMax); -#endif + if (gFacesBase - gFaces + 1 > MAX_FACES) { + ASSERT(false); + return; + } - gVerticesCount = 0; - gFacesCount = 0; + const Sprite* sprite = level.sprites + index; + + int32 l = vx + sprite->l; + int32 r = vx + sprite->r; + int32 t = vy + sprite->t; + int32 b = vy + sprite->b; + + if (l == r) return; + if (t == b) return; + if (r < 0) return; + if (b < 0) return; + if (l >= FRAME_WIDTH) return; + if (t >= FRAME_HEIGHT) return; + + Vertex* v1 = gVerticesBase++; + v1->x = l; + v1->y = t; + //v1->z = z; + v1->g = 16; + + Vertex* v2 = gVerticesBase++; + v2->x = r; + v2->y = b; + //v2->z = z; + //v2->g = vg; + + Face* f = faceAdd(0); + f->flags = (FACE_TYPE_SPRITE << FACE_TYPE_SHIFT) | index; + f->indices[0] = v1 - gVertices; + + gVerticesBase += 2; } -void initRender() { - divTable[0] = 0xFFFF; - divTable[1] = 0xFFFF; - for (uint32 i = 2; i < DIV_TABLE_SIZE; i++) { - divTable[i] = (1 << 16) / i; +#define BAR_HEIGHT 5 + +const int32 BAR_COLORS[BAR_MAX][5] = { + { 8, 11, 8, 6, 24 }, + { 32, 41, 32, 19, 21 }, + { 28, 29, 28, 26, 27 }, + { 43, 44, 43, 42, 41 }, +}; + +X_NOINLINE void renderBorder(int32 x, int32 y, int32 width, int32 height, int32 shade, int32 color1, int32 color2, int32 z) +{ + // background + if (shade >= 0) { + renderFill(x + 1, y + 1, width - 2, height - 2, shade, z); } + + // frame + renderLine(x + 1, y, width - 2, 1, color1, z); + renderLine(x + 1, y + height - 1, width - 2, 1, color2, z); + renderLine(x, y, 1, height, color1, z); + renderLine(x + width - 1, y, 1, height, color2, z); } -void dmaClear(uint32 *dst, uint32 count) { -#ifdef WIN32 - memset(dst, 0, count * 4); -#else - vu32 value = 0; - REG_DMA3SAD = (vu32)&value; - REG_DMA3DAD = (vu32)dst; - REG_DMA3CNT = count | (DMA_ENABLE | DMA32 | DMA_SRC_FIXED | DMA_DST_INC); -#endif +void renderBar(int32 x, int32 y, int32 width, int32 value, BarType type) +{ + // colored bar + int32 ix = x + 2; + int32 iy = y + 2; + int32 w = value * width >> 8; + + if (w > 0) + { + for (int32 i = 0; i < 5; i++) + { + renderLine(ix, iy++, w, 1, BAR_COLORS[type][i], 0); + } + } + + renderBorder(x, y, width + 4, BAR_HEIGHT + 4, 27, 19, 17, 0); } -void clear() { -#ifdef USE_MODE_5 - dmaClear((uint32*)fb, (WIDTH * HEIGHT) >> PIXEL_SIZE); -#else - dmaClear((uint32*)fb, (WIDTH * HEIGHT) >> PIXEL_SIZE); -#endif -} \ No newline at end of file +void renderBackground(const void* background) +{ + dmaCopy(background, (void*)fb, FRAME_WIDTH * FRAME_HEIGHT); +} + +void* copyBackground() +{ + dmaCopy((void*)fb, gBackgroundCopy, FRAME_WIDTH * FRAME_HEIGHT); + + palGrayRemap(gBackgroundCopy, FRAME_WIDTH * FRAME_HEIGHT); + + return gBackgroundCopy; +} diff --git a/src/platform/gba/sound.cpp b/src/platform/gba/sound.cpp new file mode 100644 index 00000000..46224798 --- /dev/null +++ b/src/platform/gba/sound.cpp @@ -0,0 +1,311 @@ +#include "common.h" + +int32 IMA_STEP[] = { // IWRAM ! + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, + 32767 +}; + +#if defined(__GBA__) && defined(USE_ASM) + extern const uint8_t TRACKS_IMA[]; + // the sound mixer works during VBlank, this is a great opportunity for exclusive access to VRAM without any perf penalties + // so we use part of offscreen VRAM as sound buffers (704 + 384 = 1088 bytes) + int32* mixerBuffer = (int32*)(MEM_VRAM + VRAM_PAGE_SIZE + FRAME_WIDTH * FRAME_HEIGHT); + uint8* soundBuffer = (uint8*)(MEM_VRAM + VRAM_PAGE_SIZE + FRAME_WIDTH * FRAME_HEIGHT + SND_SAMPLES * sizeof(int32)); // use 2k of VRAM after the first frame buffer as sound buffer +#else + extern const void* TRACKS_IMA; + int32 mixerBuffer[SND_SAMPLES]; + uint8 soundBuffer[2 * SND_SAMPLES + 32]; // 32 bytes of silence for DMA overrun while interrupt +#endif + +#ifdef USE_ASM + #define sndIMA sndIMA_asm + #define sndPCM sndPCM_asm + #define sndWrite sndWrite_asm + + extern "C" { + void sndIMA_asm(IMA_STATE &state, int32* buffer, const uint8* data, int32 size); + int32 sndPCM_asm(int32 pos, int32 inc, int32 size, int32 volume, const uint8* data, int32* buffer, int32 count); + void sndWrite_asm(uint8* buffer, int32 count, int32 *data); + } +#else + #define sndIMA sndIMA_c + #define sndPCM sndPCM_c + #define sndWrite sndWrite_c + +#define DECODE_IMA_4(n)\ + step = IMA_STEP[idx];\ + index = n & 7;\ + step += index * step << 1;\ + if (index < 4) {\ + idx = X_MAX(idx - 1, 0);\ + } else {\ + idx = X_MIN(idx + ((index - 3) << 1), X_COUNT(IMA_STEP) - 1);\ + }\ + if (n & 8) {\ + smp -= step >> 3;\ + } else {\ + smp += step >> 3;\ + }\ + *buffer++ = smp >> (16 - (8 + SND_VOL_SHIFT)); + +void sndIMA_c(IMA_STATE &state, int32* buffer, const uint8* data, int32 size) +{ + uint32 step, index; + + int32 smp = state.smp; + int32 idx = state.idx; + + for (int32 i = 0; i < size; i++) + { + uint32 n = *data++; + DECODE_IMA_4(n); + n >>= 4; + DECODE_IMA_4(n); + } + + state.smp = smp; + state.idx = idx; +} + +int32 sndPCM_c(int32 pos, int32 inc, int32 size, int32 volume, const uint8* data, int32* buffer, int32 count) +{ + int32 last = pos + count * inc; + if (last > size) { + last = size; + } + + while (pos < last) + { + *buffer++ += SND_DECODE(data[pos >> SND_FIXED_SHIFT]) * volume; + pos += inc; + } + + return pos; +} + +void sndWrite_c(uint8* buffer, int32 count, int32 *data) +{ + for (int32 i = 0; i < count; i++) + { + int32 samp = X_CLAMP(data[i] >> SND_VOL_SHIFT, SND_MIN, SND_MAX); + buffer[i] = SND_ENCODE(samp); + } +} +#endif + + +struct Music +{ + const uint8* data; + int32 size; + int32 pos; + IMA_STATE state; + + void fill(int32* buffer, int32 count) + { + int32 len = X_MIN(size - pos, count >> 1); + + sndIMA(state, buffer, data + pos, len); + + pos += len; + + if (pos >= size) + { + data = NULL; + memset(buffer, 0, (count - (len << 1)) * sizeof(buffer[0])); + } + } +}; + +struct Sample +{ + int32 pos; + int32 inc; + int32 size; + int32 volume; + const uint8* data; + + void fill(int32* buffer, int32 count) + { + pos = sndPCM(pos, inc, size, volume, data, buffer, count); + + if (pos >= size) + { + data = NULL; + } + } +}; + +EWRAM_DATA Music music; +EWRAM_DATA Sample channels[SND_CHANNELS]; +EWRAM_DATA int32 channelsCount; + +#define CALC_INC (((SND_SAMPLE_FREQ << SND_FIXED_SHIFT) / SND_OUTPUT_FREQ) * pitch >> SND_PITCH_SHIFT) + +void sndInit() +{ + // initialized in main.cpp +} + +void sndInitSamples() +{ + // nothing to do +} + +void sndFreeSamples() +{ + // nothing to do +} + +void* sndPlaySample(int32 index, int32 volume, int32 pitch, int32 mode) +{ + if (!gSettings.audio_sfx) + return NULL; + + const uint8 *data = level.soundData + level.soundOffsets[index]; + int32 size = *(int32*)data; + data += 4; + + if (mode == UNIQUE || mode == REPLAY) + { + for (int32 i = 0; i < channelsCount; i++) + { + Sample* sample = channels + i; + + if (sample->data != data) + continue; + + sample->inc = CALC_INC; + sample->volume = volume; + + if (mode == REPLAY) + { + sample->pos = 0; + } + + return sample; + } + } + + if (channelsCount >= SND_CHANNELS) + return NULL; + + Sample* sample = channels + channelsCount++; + sample->data = data; + sample->size = size << SND_FIXED_SHIFT; + sample->pos = 0; + sample->inc = CALC_INC; + sample->volume = volume; + + return sample; +} + +void sndPlayTrack(int32 track) +{ + if (!gSettings.audio_music) + return; + + if (track == gCurTrack) + return; + + gCurTrack = track; + + if (track == -1) { + sndStopTrack(); + return; + } + + struct TrackInfo { + int32 offset; + int32 size; + }; + + const TrackInfo* info = (const TrackInfo*)TRACKS_IMA + track; + + if (!info->size) + return; + + music.data = (uint8*)TRACKS_IMA + info->offset; + music.size = info->size; + music.pos = 0; + //music.volume = (1 << SND_VOL_SHIFT); + music.state.smp = 0; + music.state.idx = 0; +} + +void sndStopTrack() +{ + music.data = NULL; + music.size = 0; + music.pos = 0; +} + +bool sndTrackIsPlaying() +{ + return music.data != NULL; +} + +void sndStopSample(int32 index) +{ + const uint8 *data = level.soundData + level.soundOffsets[index] + 4; + + int32 i = channelsCount; + + while (--i >= 0) + { + if (channels[i].data == data) + { + channels[i] = channels[--channelsCount]; + } + } +} + +void sndStop() +{ + channelsCount = 0; + music.data = NULL; +} + +void sndFill(uint8* buffer, int32 count) +{ +#ifdef PROFILE_SOUNDTIME + PROFILE_CLEAR(); + PROFILE(CNT_SOUND); +#endif + + if ((channelsCount == 0) && !music.data) + { + dmaFill(buffer, SND_ENCODE(0), count); + return; + } + + if (music.data) { + music.fill(mixerBuffer, count); + } else { + dmaFill(mixerBuffer, 0, SND_SAMPLES * sizeof(int32)); + } + + int32 ch = channelsCount; + while (ch--) + { + Sample* sample = channels + ch; + + sample->fill(mixerBuffer, count); + + if (!sample->data) { + channels[ch] = channels[--channelsCount]; + } + } + + sndWrite(buffer, count, mixerBuffer); +} diff --git a/src/platform/nix/main.cpp b/src/platform/nix/main.cpp index 7592edf0..e36c1a0a 100644 --- a/src/platform/nix/main.cpp +++ b/src/platform/nix/main.cpp @@ -27,7 +27,7 @@ int osGetTimeMS() { // sound #define SND_FRAME_SIZE 4 -#define SND_DATA_SIZE (1024 * SND_FRAME_SIZE) +#define SND_DATA_SIZE (2352 * SND_FRAME_SIZE) pa_simple *sndOut; pthread_t sndThread; @@ -78,7 +78,7 @@ void sndFree() { } // Input -InputKey keyToInputKey(XKeyEvent event) { +InputKey keyToInputKey(Display *dpy, XKeyEvent event) { KeySym code = XLookupKeysym(&event, 0); if (code == XK_Shift_R) code = XK_Shift_L; @@ -96,7 +96,7 @@ InputKey keyToInputKey(XKeyEvent event) { }; for (int i = 0; i < COUNT(codes); i++) { - if (codes[i] == code) { + if (XKeysymToKeycode(dpy, codes[i]) == event.keycode) { return (InputKey)(ikLeft + i); } } @@ -338,7 +338,7 @@ void toggle_fullscreen(Display* dpy, Window win) { XSendEvent(dpy, DefaultRootWindow(dpy), False, SubstructureNotifyMask, &xev); } -void WndProc(const XEvent &e,Display*dpy,Window wnd) { +void WndProc(const XEvent &e, Display* dpy, Window wnd) { switch (e.type) { case ConfigureNotify : Core::width = e.xconfigure.width; @@ -350,7 +350,7 @@ void WndProc(const XEvent &e,Display*dpy,Window wnd) { toggle_fullscreen(dpy,wnd); break; } - Input::setDown(keyToInputKey(e.xkey), e.type == KeyPress); + Input::setDown(keyToInputKey(dpy, e.xkey), e.type == KeyPress); break; case ButtonPress : case ButtonRelease : { @@ -387,6 +387,8 @@ int checkLanguage() { if (id == TWOCC("fi")) return STR_LANG_FI - STR_LANG_EN; if (id == TWOCC("cs")) return STR_LANG_CZ - STR_LANG_EN; if (id == TWOCC("zh")) return STR_LANG_CN - STR_LANG_EN; + if (id == TWOCC("hu")) return STR_LANG_HU - STR_LANG_EN; + if (id == TWOCC("sv")) return STR_LANG_SV - STR_LANG_EN; return 0; } @@ -455,7 +457,7 @@ int main(int argc, char **argv) { XNextEvent(dpy, &event); if (event.type == ClientMessage && *event.xclient.data.l == WM_DELETE_WINDOW) Core::quit(); - WndProc(event,dpy,wnd); + WndProc(event, dpy, wnd); } else { joyUpdate(); bool updated = Game::update(); diff --git a/src/platform/nix/premake5.lua b/src/platform/nix/premake5.lua new file mode 100644 index 00000000..7c3519d3 --- /dev/null +++ b/src/platform/nix/premake5.lua @@ -0,0 +1,23 @@ +workspace "OpenLara" + configurations { "Debug", "Release" } + +project "OpenLara" + kind "ConsoleApp" + language "C++" + includedirs { "../../" } + exceptionhandling "Off" + rtti "Off" + + files { "main.cpp", "../../libs/stb_vorbis/stb_vorbis.c", "../../libs/minimp3/minimp3.cpp", "../../libs/tinf/tinflate.c" } + + filter { "system:Linux", "toolset:gcc or clang" } + links { "X11", "GL", "m", "pthread", "pulse-simple", "pulse" } + defines { "POSIX_THREADS", "POSIX_READER_WRITER_LOCKS" } + + filter "configurations:Debug" + defines { "DEBUG" } + symbols "On" + + filter "configurations:Release" + defines { "NDEBUG" } + optimize "Size" diff --git a/src/platform/nx/main.cpp b/src/platform/nx/main.cpp index e70226d0..7070fd68 100644 --- a/src/platform/nx/main.cpp +++ b/src/platform/nx/main.cpp @@ -96,7 +96,7 @@ EGLContext context; NWindow *window; void configureResolution() { - if (appletGetOperationMode() == AppletOperationMode_Docked) { + if (appletGetOperationMode() == AppletOperationMode_Console) { Core::width = 1920; Core::height = 1080; } @@ -184,19 +184,19 @@ void osJoyVibrate(int index, float L, float R) { bool joyIsSplit; int joySplitTime; +PadState pads[2]; void joySplit(bool split) { joyIsSplit = split; joySplitTime = Core::getTime(); + if (split) { - hidSetNpadJoyHoldType(HidJoyHoldType_Horizontal); - hidSetNpadJoyAssignmentModeSingleByDefault(CONTROLLER_PLAYER_1); - hidSetNpadJoyAssignmentModeSingleByDefault(CONTROLLER_PLAYER_2); + hidSetNpadJoyAssignmentModeSingle(HidNpadIdType_No1, HidNpadJoyDeviceType_Left); + hidSetNpadJoyAssignmentModeSingle(HidNpadIdType_No2, HidNpadJoyDeviceType_Right); } else { - hidSetNpadJoyHoldType(HidJoyHoldType_Default); - hidSetNpadJoyAssignmentModeDual(CONTROLLER_PLAYER_1); - hidSetNpadJoyAssignmentModeDual(CONTROLLER_PLAYER_2); - hidMergeSingleJoyAsDualJoy(CONTROLLER_PLAYER_1, CONTROLLER_PLAYER_2); + hidSetNpadJoyAssignmentModeDual(HidNpadIdType_No1); + hidSetNpadJoyAssignmentModeDual(HidNpadIdType_No2); + hidMergeSingleJoyAsDualJoy(HidNpadIdType_No1, HidNpadIdType_No2); if (Game::level && Game::level->players[1]) { Game::level->addPlayer(1); // add existing player == remove player @@ -205,6 +205,12 @@ void joySplit(bool split) { } void joyInit() { + hidSetNpadJoyHoldType(HidNpadJoyHoldType_Horizontal); + + padConfigureInput(2, HidNpadStyleSet_NpadStandard); + padInitialize(&pads[0], HidNpadIdType_No1, HidNpadIdType_Handheld); + padInitialize(&pads[1], HidNpadIdType_No2, HidNpadIdType_Handheld); + joySplit(false); } @@ -215,18 +221,12 @@ void joyUpdate() { KEY_DLEFT, KEY_DRIGHT, KEY_DUP, KEY_DDOWN, }; - if (hidGetHandheldMode() && joyIsSplit) { - joySplit(false); - } - - hidScanInput(); - bool waitForSplit = false; for (int i = 0; i < (joyIsSplit ? 2 : 1); i++) { - HidControllerID ctrl = (i == 0 ? CONTROLLER_P1_AUTO : CONTROLLER_PLAYER_2); + padUpdate(&pads[i]); - u64 mask = hidKeysDown(ctrl) | hidKeysHeld(ctrl); + u64 mask = padGetButtonsDown(&pads[i]) | padGetButtons(&pads[i]); for (int j = 1; j < jkMAX; j++) { if (j != jkSelect && j != jkStart) { @@ -237,15 +237,14 @@ void joyUpdate() { Input::setJoyDown(i, jkSelect, (mask & (KEY_MINUS | KEY_PLUS)) != 0); Input::setJoyDown(i, jkStart, (mask & (KEY_LSTICK | KEY_RSTICK)) != 0); - JoystickPosition sL, sR; + HidAnalogStickState sL = padGetStickPos(&pads[i], 0); + HidAnalogStickState sR = padGetStickPos(&pads[i], 1); - hidJoystickRead(&sL, ctrl, JOYSTICK_LEFT); - hidJoystickRead(&sR, ctrl, JOYSTICK_RIGHT); - Input::setJoyPos(i, jkL, vec2(float(sL.dx), float(-sL.dy)) / 32767.0f); - Input::setJoyPos(i, jkR, vec2(float(sR.dx), float(-sR.dy)) / 32767.0f); + Input::setJoyPos(i, jkL, vec2(float(sL.x), float(-sL.y)) / 32767.0f); + Input::setJoyPos(i, jkR, vec2(float(sR.x), float(-sR.y)) / 32767.0f); if ((mask & (KEY_L | KEY_R)) == (KEY_L | KEY_R)) { // hold L and R to split/merge joy-con's - if (joySplitTime + 1000 < Core::getTime()) { // 1 sec timer + if (Core::getTime() - joySplitTime > 1000) { // 1 sec timer joySplit(!joyIsSplit); } waitForSplit = true; @@ -258,7 +257,8 @@ void joyUpdate() { } void touchUpdate() { - int touchesCount = hidTouchCount(); + HidTouchScreenState state; + hidGetTouchScreenStates(&state, 1); bool touchState[COUNT(Input::touch)]; @@ -266,14 +266,11 @@ void touchUpdate() { touchState[i] = Input::down[ikTouchA + i]; } - for (int i = 0; i < touchesCount; i++) { - touchPosition touch; - hidTouchRead(&touch, i); - - InputKey key = Input::getTouch(touch.id); + for (int i = 0; i < state.count; i++) { + InputKey key = Input::getTouch(state.touches[i].finger_id); if (key == ikNone) continue; - Input::setPos(key, vec2(float(touch.px), float(touch.py))); + Input::setPos(key, vec2(float(state.touches[i].x), float(state.touches[i].y))); Input::setDown(key, true); touchState[key - ikTouchA] = false; diff --git a/src/platform/psv/CMakeLists.txt b/src/platform/psv/CMakeLists.txt index 733d8429..863aac8f 100644 --- a/src/platform/psv/CMakeLists.txt +++ b/src/platform/psv/CMakeLists.txt @@ -17,8 +17,8 @@ set(VITA_APP_NAME "OpenLara") set(VITA_TITLEID "OPENLARA1") set(VITA_VERSION "01.00") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -std=c++11") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O3 -Wall") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O3 -std=c++11") set(VITA_MKSFOEX_FLAGS "${VITA_MKSFOEX_FLAGS} -d PARENTAL_LEVEL=1") include_directories( @@ -44,6 +44,7 @@ target_link_libraries(OpenLara SceCtrl_stub SceAudio_stub SceTouch_stub + SceAppUtil_stub ) diff --git a/src/platform/psv/deploy.sh b/src/platform/psv/deploy.sh new file mode 100644 index 00000000..8ac45f49 --- /dev/null +++ b/src/platform/psv/deploy.sh @@ -0,0 +1,3 @@ +make +curl --ftp-method nocwd -T OpenLara.self ftp://192.168.1.59:1337/ux0:/app/OPENLARA1/eboot.bin +echo launch OPENLARA1 | ./nc.exe 192.168.1.59 1338 diff --git a/src/platform/psv/main.cpp b/src/platform/psv/main.cpp index 6bf003ff..60150add 100644 --- a/src/platform/psv/main.cpp +++ b/src/platform/psv/main.cpp @@ -1,37 +1,40 @@ #include #include #include +#include #include "debugScreen.h" - -#include -#include -#include -#include -#include -#include -#include -#include +#include #include "game.h" // multi-threading -void* osMutexInit() { - SceUID *mutex = new SceUID(); - *mutex = sceKernelCreateMutex(NULL, 0, 0, NULL); +void* osMutexInit() +{ + SceUID mutex = sceKernelCreateMutex(NULL, SCE_KERNEL_MUTEX_ATTR_RECURSIVE, 0, NULL); + if (mutex < 0) + { + return NULL; + } + SceUID* obj = new SceUID(); + *obj = mutex; + return obj; } -void osMutexFree(void *obj) { +void osMutexFree(void *obj) +{ sceKernelDeleteMutex(*(SceUID*)obj); delete (SceUID*)obj; } -void osMutexLock(void *obj) { +void osMutexLock(void *obj) +{ sceKernelLockMutex(*(SceUID*)obj, 1, NULL); } -void osMutexUnlock(void *obj) { +void osMutexUnlock(void *obj) +{ sceKernelUnlockMutex(*(SceUID*)obj, 1); } @@ -39,28 +42,33 @@ void osMutexUnlock(void *obj) { int osStartTime = 0; int osTimerFreq; -int osGetTimeMS() { +int osGetTimeMS() +{ SceRtcTick current; sceRtcGetCurrentTick(¤t); return int(current.tick * 1000 / osTimerFreq - osStartTime); } // input -bool osJoyReady(int index) { +bool osJoyReady(int index) +{ return index == 0; } -void osJoyVibrate(int index, float L, float R) { +void osJoyVibrate(int index, float L, float R) +{ // } -void inputInit() { +void inputInit() +{ sceCtrlSetSamplingMode(SCE_CTRL_MODE_ANALOG); sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, SCE_TOUCH_SAMPLING_STATE_START); sceTouchEnableTouchForce(SCE_TOUCH_PORT_FRONT); } -void inputUpdate() { +void inputUpdate() +{ // gamepad SceCtrlData pad; sceCtrlReadBufferPositive(0, &pad, 1); @@ -91,9 +99,12 @@ void inputUpdate() { bool touchState[COUNT(Input::touch)]; for (int i = 0; i < COUNT(Input::touch); i++) + { touchState[i] = Input::down[ikTouchA + i]; + } - for (int i = 0; i < touch.reportNum; i++) { + for (int i = 0; i < touch.reportNum; i++) + { SceTouchReport &t = touch.report[i]; InputKey key = Input::getTouch(t.id); @@ -106,8 +117,12 @@ void inputUpdate() { } for (int i = 0; i < COUNT(Input::touch); i++) + { if (touchState[i]) + { Input::setDown(InputKey(ikTouchA + i), false); + } + } } bool sndTerm; @@ -119,8 +134,10 @@ Sound::Frame *sndBuffer; #define SND_FRAMES 2048 -int sndPrepThread(SceSize args, void *argp) { - while (!sndTerm) { +int sndPrepThread(SceSize args, void *argp) +{ + while (!sndTerm) + { sceKernelWaitSema(sndSema, 1, NULL); sndPartIndex ^= 1; Sound::Frame *part = sndBuffer + SND_FRAMES * sndPartIndex; @@ -129,8 +146,10 @@ int sndPrepThread(SceSize args, void *argp) { return 0; } -int sndOutThread(SceSize args, void *argp) { - while (!sndTerm) { +int sndOutThread(SceSize args, void *argp) +{ + while (!sndTerm) + { Sound::Frame *part = sndBuffer + SND_FRAMES * sndPartIndex; sceKernelSignalSema(sndSema, 1); sceAudioOutOutput(sndPort, part); @@ -138,7 +157,8 @@ int sndOutThread(SceSize args, void *argp) { return 0; } -void sndInit() { +void sndInit() +{ sndTerm = false; sndPartIndex = 0; @@ -158,7 +178,8 @@ void sndInit() { sceKernelStartThread(sndOutTID, 0, NULL); } -void sndFree() { +void sndFree() +{ sndTerm = true; sceKernelSignalSema(sndSema, 1); @@ -173,8 +194,37 @@ void sndFree() { free(sndBuffer); } +int checkLanguage() +{ + int id; + sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_LANG, &id); + + int str = STR_LANG_EN; + switch (id) + { + case SCE_SYSTEM_PARAM_LANG_ENGLISH_US : + case SCE_SYSTEM_PARAM_LANG_ENGLISH_GB : str = STR_LANG_EN; break; + case SCE_SYSTEM_PARAM_LANG_FRENCH : str = STR_LANG_FR; break; + case SCE_SYSTEM_PARAM_LANG_GERMAN : str = STR_LANG_DE; break; + case SCE_SYSTEM_PARAM_LANG_SPANISH : str = STR_LANG_ES; break; + case SCE_SYSTEM_PARAM_LANG_ITALIAN : str = STR_LANG_IT; break; + case SCE_SYSTEM_PARAM_LANG_POLISH : str = STR_LANG_PL; break; + case SCE_SYSTEM_PARAM_LANG_PORTUGUESE_PT : + case SCE_SYSTEM_PARAM_LANG_PORTUGUESE_BR : str = STR_LANG_PT; break; + case SCE_SYSTEM_PARAM_LANG_RUSSIAN : str = STR_LANG_RU; break; + case SCE_SYSTEM_PARAM_LANG_JAPANESE : str = STR_LANG_JA; break; + case SCE_SYSTEM_PARAM_LANG_FINNISH : str = STR_LANG_FI; break; + case SCE_SYSTEM_PARAM_LANG_CHINESE_T : + case SCE_SYSTEM_PARAM_LANG_CHINESE_S : str = STR_LANG_CN; break; + case SCE_SYSTEM_PARAM_LANG_SWEDISH : str = STR_LANG_SV; break; + } + return str - STR_LANG_EN; +} -int main() { +extern "C" int32_t sceKernelChangeThreadVfpException(int32_t clear, int32_t set); + +int main() +{ psvDebugScreenInit(); scePowerSetArmClockFrequency(444); @@ -182,30 +232,45 @@ int main() { scePowerSetGpuClockFrequency(222); scePowerSetGpuXbarClockFrequency(166); + { + SceAppUtilInitParam initParam = {}; + SceAppUtilBootParam bootParam = {}; + sceAppUtilInit(&initParam, &bootParam); + } + + sceKernelChangeThreadVfpException(0x0800009FU, 0x0); + cacheDir[0] = saveDir[0] = contentDir[0] = 0; strcpy(cacheDir, "ux0:data/OpenLara/"); strcpy(saveDir, "ux0:data/OpenLara/"); strcpy(contentDir, "ux0:data/OpenLara/"); + Stream::init(); + + Core::defLang = checkLanguage(); + sndInit(); inputInit(); osTimerFreq = sceRtcGetTickResolution(); - osStartTime = Core::getTime(); + osStartTime = osGetTimeMS(); - Game::init("PSXDATA/LEVEL2.PSX"); -// sceRazorGpuCaptureSetTrigger(100, "ux0:data/OpenLara/capture.sgx"); + Game::init(); while (!Core::isQuit) { inputUpdate(); if (Input::joy[0].down[jkStart]) + { Core::quit(); + } Game::update(); Game::render(); GAPI::present(); + + sceKernelPowerTick(SCE_KERNEL_POWER_TICK_DEFAULT); } sndFree(); diff --git a/src/platform/psv/sce_sys/icon0.png b/src/platform/psv/sce_sys/icon0.png index bb54cf58..94c68fa3 100644 Binary files a/src/platform/psv/sce_sys/icon0.png and b/src/platform/psv/sce_sys/icon0.png differ diff --git a/src/platform/psv/sce_sys/livearea/contents/bg.png b/src/platform/psv/sce_sys/livearea/contents/bg.png index 23a9f3b5..259af4a3 100644 Binary files a/src/platform/psv/sce_sys/livearea/contents/bg.png and b/src/platform/psv/sce_sys/livearea/contents/bg.png differ diff --git a/src/platform/psv/sce_sys/livearea/contents/startup.png b/src/platform/psv/sce_sys/livearea/contents/startup.png index 9f2bf25e..ce0ec882 100644 Binary files a/src/platform/psv/sce_sys/livearea/contents/startup.png and b/src/platform/psv/sce_sys/livearea/contents/startup.png differ diff --git a/src/platform/psv/sce_sys/livearea/contents/template.xml b/src/platform/psv/sce_sys/livearea/contents/template.xml index a4d43f01..b934a299 100644 --- a/src/platform/psv/sce_sys/livearea/contents/template.xml +++ b/src/platform/psv/sce_sys/livearea/contents/template.xml @@ -1,6 +1,6 @@ - + bg.png diff --git a/src/platform/rpi/main.cpp b/src/platform/rpi/main.cpp index 26128207..2f28dc64 100644 --- a/src/platform/rpi/main.cpp +++ b/src/platform/rpi/main.cpp @@ -491,6 +491,8 @@ int checkLanguage() { if (id == TWOCC("fi")) return STR_LANG_FI - STR_LANG_EN; if (id == TWOCC("cs")) return STR_LANG_CZ - STR_LANG_EN; if (id == TWOCC("zh")) return STR_LANG_CN - STR_LANG_EN; + if (id == TWOCC("hu")) return STR_LANG_HU - STR_LANG_EN; + if (id == TWOCC("sv")) return STR_LANG_SV - STR_LANG_EN; return 0; } diff --git a/src/platform/sdl2/Makefile b/src/platform/sdl2/Makefile index 3b5040af..1471c563 100644 --- a/src/platform/sdl2/Makefile +++ b/src/platform/sdl2/Makefile @@ -1,31 +1,27 @@ -SRCS=main.cpp ../../libs/stb_vorbis/stb_vorbis.c ../../libs/minimp3/minimp3.cpp ../../libs/tinf/tinflate.c +SRCS=main.cpp \ + ../../libs/stb_vorbis/stb_vorbis.c \ + ../../libs/minimp3/minimp3.cpp \ + ../../libs/tinf/tinflate.c CC=g++ -OBJS=OpenLara.o -BIN=OpenLara -CFLAGS+=-D__SDL2__ -LDFLAGS+=-lGLESv2 -lEGL -lSDL2 -lpthread -lrt -lm +SDL_CFLAGS := $(shell sdl2-config --cflags) +SDL_LDFLAGS := $(shell sdl2-config --libs) -INCLUDES+=-I./ +CFLAGS+=-DSDL2_GLES -D_GAPI_GLES2 -std=c++11 -O3 -fno-exceptions -fno-rtti -ffunction-sections -fdata-sections -Wl,--gc-sections -D__SDL2__ $(SDL_CFLAGS) -Wall +LDFLAGS+=-lGLESv2 -lEGL -lm -lrt -lpthread -lasound -ludev $(SDL_LDFLAGS) -openlara : $(OBJS) - $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ +INCLUDES+=-I../../ -%.o: %.c - @rm -f $@ - $(CC) $(CFLAGS) $(INCLUDES) -g -c $< -o $@ -Wno-deprecated-declarations +openlara : + $(CC) $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(SRCS) -o $@ -%.o: %.cpp - @rm -f $@ - $(CXX) $(CFLAGS) $(INCLUDES) -g -c $< -o $@ -Wno-deprecated-declarations +install : openlara + install -d $(DESTDIR)$(PREFIX)/bin/ + install openlara $(DESTDIR)$(PREFIX)/bin/openlara -%.bin: $(OBJS) - $(CC) -o $@ -Wl,--whole-archive $(OBJS) $(LDFLAGS) -Wl,--no-whole-archive -rdynamic - -%.a: $(OBJS) - $(AR) r $@ $^ +uninstall : + rm -f $(DESTDIR)$(PREFIX)/bin/openlara clean: - for i in $(OBJS); do (if test -e "$$i"; then ( rm $$i ); fi ); done - @rm -f $(BIN) $(LIB) + rm -f openlara diff --git a/src/platform/sdl2/main.cpp b/src/platform/sdl2/main.cpp index f99cbd96..ce563539 100644 --- a/src/platform/sdl2/main.cpp +++ b/src/platform/sdl2/main.cpp @@ -88,7 +88,6 @@ int sdl_numjoysticks, sdl_numcontrollers; SDL_Joystick *sdl_joysticks[MAX_JOYS]; SDL_GameController *sdl_controllers[MAX_JOYS]; SDL_Window *sdl_window; -SDL_Renderer *sdl_renderer; SDL_DisplayMode sdl_displaymode; bool fullscreen; @@ -199,6 +198,10 @@ JoyKey controllerCodeToJoyKey(int code) { case SDL_CONTROLLER_BUTTON_START : return jkStart; case SDL_CONTROLLER_BUTTON_LEFTSTICK : return jkL; case SDL_CONTROLLER_BUTTON_RIGHTSTICK : return jkR; + case SDL_CONTROLLER_BUTTON_DPAD_UP : return jkUp; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN : return jkDown; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT : return jkLeft; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT : return jkRight; } return jkNone; } @@ -267,10 +270,10 @@ void joyRemove(Sint32 instanceID) { if (joyIsController(instanceID)) { for (i = 0; i < sdl_numcontrollers; i++) { if (SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(sdl_controllers[i])) == instanceID) { - SDL_GameControllerClose(sdl_controllers[i]); + SDL_GameControllerClose(sdl_controllers[i]); sdl_controllers[i] = NULL; - sdl_numcontrollers--; - sdl_numjoysticks--; + sdl_numcontrollers--; + sdl_numjoysticks--; } } } @@ -293,9 +296,8 @@ bool inputInit() { for (index = 0; index < MAX_JOYS; index++) sdl_joysticks[index] = NULL; - for (index = 0; index < sdl_numjoysticks; index++) { + for (index = 0; index < sdl_numjoysticks; index++) joyAdd(index); - } return true; } @@ -333,11 +335,11 @@ void inputUpdate() { while (SDL_PollEvent(&event) == 1) { // while there are still events to be processed switch (event.type) { case SDL_KEYDOWN: { - int scancode = event.key.keysym.scancode; + int scancode = event.key.keysym.scancode; InputKey key = codeToInputKey(scancode); - if (key != ikNone) { - Input::setDown(key, 1); - } + if (key != ikNone) { + Input::setDown(key, 1); + } #ifndef _GAPI_GLES if (scancode == SDL_SCANCODE_RETURN) { @@ -346,90 +348,177 @@ void inputUpdate() { } } #endif - - break; - } - case SDL_KEYUP: { - int scancode = event.key.keysym.scancode; + break; + } + + case SDL_KEYUP: { + int scancode = event.key.keysym.scancode; InputKey key = codeToInputKey(scancode); - if (key != ikNone) { - Input::setDown(key, 0); - } + if (key != ikNone) + Input::setDown(key, 0); + break; + } + + // Joystick reading using the modern SDL GameController interface + case SDL_CONTROLLERBUTTONDOWN: { + joyIndex = joyGetIndex(event.cbutton.which); + JoyKey key = controllerCodeToJoyKey(event.cbutton.button); + Input::setJoyDown(joyIndex, key, 1); + break; + } + case SDL_CONTROLLERBUTTONUP: { + joyIndex = joyGetIndex(event.cbutton.which); + JoyKey key = controllerCodeToJoyKey(event.cbutton.button); + Input::setJoyDown(joyIndex, key, 0); break; - } - // Joystick reading using the modern GameController interface - case SDL_CONTROLLERBUTTONDOWN: { - joyIndex = joyGetIndex(event.cbutton.which); - JoyKey key = controllerCodeToJoyKey(event.cbutton.button); - Input::setJoyDown(joyIndex, key, 1); + } + + case SDL_CONTROLLERAXISMOTION: { + joyIndex = joyGetIndex(event.caxis.which); + switch (event.caxis.axis) { + case SDL_CONTROLLER_AXIS_LEFTX: + joyL.x = joyAxisValue(event.caxis.value); break; - } - case SDL_CONTROLLERBUTTONUP: { - joyIndex = joyGetIndex(event.cbutton.which); - JoyKey key = controllerCodeToJoyKey(event.cbutton.button); - Input::setJoyDown(joyIndex, key, 0); + case SDL_CONTROLLER_AXIS_LEFTY: + joyL.y = joyAxisValue(event.caxis.value); break; - } - case SDL_CONTROLLERAXISMOTION: { - joyIndex = joyGetIndex(event.caxis.which); - switch (event.caxis.axis) { - case SDL_CONTROLLER_AXIS_LEFTX: joyL.x = joyAxisValue(event.caxis.value); break; - case SDL_CONTROLLER_AXIS_LEFTY: joyL.y = joyAxisValue(event.caxis.value); break; - case SDL_CONTROLLER_AXIS_RIGHTX: joyR.x = joyAxisValue(event.caxis.value); break; - case SDL_CONTROLLER_AXIS_RIGHTY: joyR.y = joyAxisValue(event.caxis.value); break; - } - Input::setJoyPos(joyIndex, jkL, joyDir(joyL)); - Input::setJoyPos(joyIndex, jkR, joyDir(joyR)); - break; - } - // Joystick reading using the classic Joystick interface - case SDL_JOYBUTTONDOWN: { - joyIndex = joyGetIndex(event.jbutton.which); - JoyKey key = joyCodeToJoyKey(event.jbutton.button); - Input::setJoyDown(joyIndex, key, 1); + case SDL_CONTROLLER_AXIS_RIGHTX: + joyR.x = joyAxisValue(event.caxis.value); break; - } - case SDL_JOYBUTTONUP: { - joyIndex = joyGetIndex(event.jbutton.which); - JoyKey key = joyCodeToJoyKey(event.jbutton.button); - Input::setJoyDown(joyIndex, key, 0); + case SDL_CONTROLLER_AXIS_RIGHTY: + joyR.y = joyAxisValue(event.caxis.value); break; - } - case SDL_JOYAXISMOTION: { - joyIndex = joyGetIndex(event.jaxis.which); - switch (event.jaxis.axis) { - // In the classic joystick interface we know what axis changed by it's number, - // they have no names like on the fancy GameController interface. - case 0: joyL.x = joyAxisValue(event.jaxis.value); break; - case 1: joyL.y = joyAxisValue(event.jaxis.value); break; - case 2: joyR.x = joyAxisValue(event.jaxis.value); break; - case 3: joyR.y = joyAxisValue(event.jaxis.value); break; - } - Input::setJoyPos(joyIndex, jkL, joyDir(joyL)); - Input::setJoyPos(joyIndex, jkR, joyDir(joyR)); - break; - } - // Joystick connection or disconnection - case SDL_JOYDEVICEADDED : { + } + Input::setJoyPos(joyIndex, jkL, joyDir(joyL)); + Input::setJoyPos(joyIndex, jkR, joyDir(joyR)); + break; + } + + // GameController connection or disconnection + case SDL_CONTROLLERDEVICEADDED: { // Upon connection, 'which' is the internal SDL2 joystick index, // but on disconnection, 'which' is the instanceID. // We store the joysticks in their corresponding position on the joysticks array, // IE: joystick with index 3 will be in sdl_joysticks[3]. - joyAdd(event.jdevice.which); - break; - } - case SDL_JOYDEVICEREMOVED : { - joyRemove(event.jdevice.which); + joyAdd(event.cdevice.which); break; - } - } - } + } + case SDL_CONTROLLERDEVICEREMOVED: { + joyRemove(event.cdevice.which); + break; + + // Joystick reading using the old SDL Joystick interface + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + case SDL_JOYAXISMOTION: + case SDL_JOYDEVICEADDED: + case SDL_JOYDEVICEREMOVED: + // Only handle the event if the joystick is incompatible with the SDL_GameController interface. + // (Otherwise it will interfere with the normal action of the SDL_GameController API, because + // the event is both a GameController event AND a Joystick event.) + if (SDL_IsGameController(joyIndex)) { + break; + } + + switch (event.type) { + case SDL_JOYBUTTONDOWN: { + joyIndex = joyGetIndex(event.jbutton.which); + JoyKey key = joyCodeToJoyKey(event.jbutton.button); + Input::setJoyDown(joyIndex, key, 1); + break; + } + case SDL_JOYBUTTONUP: { + joyIndex = joyGetIndex(event.jbutton.which); + JoyKey key = joyCodeToJoyKey(event.jbutton.button); + Input::setJoyDown(joyIndex, key, 0); + break; + } + case SDL_JOYAXISMOTION: { + joyIndex = joyGetIndex(event.jaxis.which); + switch (event.jaxis.axis) + { + // In the classic joystick interface we know what axis changed by it's number, + // they have no names like on the fancy GameController interface. + case 0: joyL.x = joyAxisValue(event.jaxis.value); break; + case 1: joyL.y = joyAxisValue(event.jaxis.value); break; + case 2: joyR.x = joyAxisValue(event.jaxis.value); break; + case 3: joyR.y = joyAxisValue(event.jaxis.value); break; + } + Input::setJoyPos(joyIndex, jkL, joyDir(joyL)); + Input::setJoyPos(joyIndex, jkR, joyDir(joyR)); + break; + } + + // Joystick connection or disconnection + case SDL_JOYDEVICEADDED: { + // Upon connection, 'which' is the internal SDL2 joystick index, + // but on disconnection, 'which' is the instanceID. + // We store the joysticks in their corresponding position on the joysticks array, + // IE: joystick with index 3 will be in sdl_joysticks[3]. + joyAdd(event.jdevice.which); + break; + } + case SDL_JOYDEVICEREMOVED: { + joyRemove(event.jdevice.which); + break; + } + break; + } + } + } + } +} + +void print_help(int argc, char **argv) { + + printf("%s [OPTION]\nA open source re-implementation of the classic Tomb Raider engine.\n", + argc ? argv[0] : "OpenLara"); + puts("-d [DIRECTORY] directory where data files are"); + puts("-l [FILE] load a specific level file"); + puts("-h print this help"); } int main(int argc, char **argv) { + + cacheDir[0] = saveDir[0] = contentDir[0] = 0; + char *lvlName = nullptr; + + int option; + while ((option = getopt(argc, argv, "hl:d:")) != -1) { + switch(option) { + case 'h': + print_help(argc, argv); + return 0; + case 'l': + lvlName = optarg; + break; + case 'd': + strncpy(contentDir, optarg, 254); + break; + case ':': + printf("option %c needs a value\n", optopt); + print_help(argc, argv); + return -1; + case '?': + printf("unknown option: %c\n", optopt); + print_help(argc, argv); + return -1; + default: + break; + } + } - int w, h; + size_t contentDirLen = strlen(contentDir); + if (contentDirLen > 0 && + contentDir[contentDirLen-1] != '/' && contentDir[contentDirLen-1] != '\\' && + contentDirLen < 254) { + contentDir[contentDirLen] = '/'; + contentDir[contentDirLen+1] = '\0'; + } + + int w, h; + SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt"); SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_EVENTS|SDL_INIT_GAMECONTROLLER); SDL_GetCurrentDisplayMode(0, &sdl_displaymode); @@ -448,7 +537,7 @@ int main(int argc, char **argv) { #ifdef _GAPI_GLES sdl_displaymode.w, sdl_displaymode.h, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_FULLSCREEN_DESKTOP #else - WIN_W, WIN_H, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN + WIN_W, WIN_H, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN #endif ); @@ -459,15 +548,9 @@ int main(int argc, char **argv) { Core::height = h; SDL_GLContext context = SDL_GL_CreateContext(sdl_window); - SDL_GL_SetSwapInterval(0); - - sdl_renderer = SDL_CreateRenderer(sdl_window, -1, - SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); SDL_ShowCursor(SDL_DISABLE); - cacheDir[0] = saveDir[0] = contentDir[0] = 0; - const char *home; if (!(home = getenv("HOME"))) home = getpwuid(getuid())->pw_dir; @@ -487,8 +570,6 @@ int main(int argc, char **argv) { inputInit(); - char *lvlName = argc > 1 ? argv[1] : NULL; - Game::init(lvlName); while (!Core::isQuit) { @@ -497,14 +578,13 @@ int main(int argc, char **argv) { if (Game::update()) { Game::render(); Core::waitVBlank(); - SDL_GL_SwapWindow(sdl_window); + SDL_GL_SwapWindow(sdl_window); } }; sndFree(); Game::deinit(); - SDL_DestroyRenderer(sdl_renderer); SDL_DestroyWindow(sdl_window); SDL_Quit(); diff --git a/src/platform/tns/Makefile b/src/platform/tns/Makefile new file mode 100644 index 00000000..df8aa6f3 --- /dev/null +++ b/src/platform/tns/Makefile @@ -0,0 +1,50 @@ +DEBUG = FALSE + +GCC = nspire-gcc +AS = nspire-as +GXX = nspire-g++ +LD = nspire-ld +GENZEHN = genzehn + +GCCFLAGS = -marm -march=armv5te -mtune=arm926ej-s -std=c++11 -flto -ffast-math -fomit-frame-pointer -fno-exceptions -fno-rtti -ffunction-sections -fdata-sections -D__TNS__ -I../../ +LDFLAGS = -Wl,--gc-sections -Wl,--as-needed -flto -Wno-alloc-size-larger-than +ZEHNFLAGS = --name "OpenLara" + +ifeq ($(DEBUG),FALSE) + GCCFLAGS += -Ofast +else + GCCFLAGS += -O0 -g +endif + +OBJS = $(patsubst %.c, %.o, $(shell find . -name \*.c)) +OBJS += $(patsubst %.cpp, %.o, $(shell find . -name \*.cpp)) +OBJS += $(patsubst %.s, %.o, $(shell find . -name \*.s)) +EXE = OpenLara +DISTDIR = . +vpath %.tns $(DISTDIR) +vpath %.elf $(DISTDIR) + +all: $(EXE).prg.tns + +%.o: %.c + $(GCC) $(GCCFLAGS) -c $< + +%.o: %.cpp + $(GXX) $(GCCFLAGS) -c $< + +%.o: %.s + $(AS) -c $< + +$(EXE).elf: $(OBJS) + copy ../gba/render.iwram.cpp render.cpp /Y + mkdir -p $(DISTDIR) + $(LD) $^ -o $(DISTDIR)/$@ $(LDFLAGS) + +$(EXE).tns: $(EXE).elf + $(GENZEHN) --input $(DISTDIR)/$^ --output $(DISTDIR)/$@ $(ZEHNFLAGS) + +$(EXE).prg.tns: $(EXE).tns + make-prg $(DISTDIR)/$^ $(DISTDIR)/$@ + +clean: + rm -f *.o $(DISTDIR)/$(EXE).tns $(DISTDIR)/$(EXE).elf $(DISTDIR)/$(EXE).prg.tns diff --git a/src/platform/tns/main.cpp b/src/platform/tns/main.cpp new file mode 100644 index 00000000..879b66e3 --- /dev/null +++ b/src/platform/tns/main.cpp @@ -0,0 +1,192 @@ +#if defined(_WIN32) || defined(__DOS__) + const void* TRACKS_IMA; + const void* TITLE_SCR; + const void* levelData; +#endif + +#include "game.h" + +int32 fps; +int32 frameIndex = 0; +int32 fpsCounter = 0; +uint32 curSoundBuffer = 0; + +unsigned int osTime; +volatile unsigned int *timerBUS; +volatile unsigned int *timerCLK; +volatile unsigned int *timerCTR; +volatile unsigned int *timerDIV; + +void timerInit() +{ + timerBUS = (unsigned int*)0x900B0018; + timerCLK = (unsigned int*)0x900C0004; + timerCTR = (unsigned int*)0x900C0008; + timerDIV = (unsigned int*)0x900C0080; + + *timerBUS &= ~(1 << 11); + *timerDIV = 0x0A; + *timerCTR = 0x82; + + osTime = *timerCLK; +} + +int32 GetTickCount() +{ + return (osTime - *timerCLK) / 33; +} + +int32 osGetSystemTimeMS() +{ + return *timerCLK / 33; +} + +bool osSaveSettings() +{ + return false; +} + +bool osLoadSettings() +{ + return false; +} + +bool osCheckSave() +{ + return false; +} + +bool osSaveGame() +{ + return false; +} + +bool osLoadGame() +{ + return false; +} + +void osJoyVibrate(int32 index, int32 L, int32 R) {} + +void osSetPalette(const uint16* palette) +{ + memcpy((uint16*)0xC0000200, palette, 256 * 2); +} + +touchpad_info_t* touchInfo; +touchpad_report_t touchReport; +uint8 inputData[0x20]; + +bool keyDown(const t_key &key) +{ + return (*(short*)(inputData + key.tpad_row)) & key.tpad_col; +} + +void inputInit() +{ + touchInfo = is_touchpad ? touchpad_getinfo() : NULL; +} + +void inputUpdate() +{ + keys = 0; + + if (touchInfo) + { + touchpad_scan(&touchReport); + } + + memcpy(inputData, (void*)0x900E0000, 0x20); + + if (touchInfo && touchReport.contact) + { + float tx = float(touchReport.x) / float(touchInfo->width) * 2.0f - 1.0f; + float ty = float(touchReport.y) / float(touchInfo->height) * 2.0f - 1.0f; + + if (tx < -0.5f) keys |= IK_LEFT; + if (tx > 0.5f) keys |= IK_RIGHT; + if (ty > 0.5f) keys |= IK_UP; + if (ty < -0.5f) keys |= IK_DOWN]; + } + + if (keyDown(KEY_NSPIRE_2)) keys |= IK_A; + if (keyDown(KEY_NSPIRE_3)) keys |= IK_B; + if (keyDown(KEY_NSPIRE_7)) keys |= IK_L; + if (keyDown(KEY_NSPIRE_9)) keys |= IK_R; + if (keyDown(KEY_NSPIRE_ENTER)) keys |= IK_START; + if (keyDown(KEY_NSPIRE_SPACE)) keys |= IK_SELECT; +} + +void* osLoadLevel(const char* name) +{ +// level1 + char buf[32]; + + delete[] levelData; + + sprintf(buf, "/documents/OpenLara/%s.PKD.tns", name); + + FILE *f = fopen(buf, "rb"); + + if (!f) + return NULL; + + { + fseek(f, 0, SEEK_END); + int32 size = ftell(f); + fseek(f, 0, SEEK_SET); + uint8* data = new uint8[size]; + fread(data, 1, size, f); + fclose(f); + + levelData = data; + } + + return (void*)levelData; +} + +int main(void) +{ + if (!has_colors) + return 0; + + lcd_init(SCR_320x240_8); + + timerInit(); + inputInit(); + + gameInit(gLevelInfo[gLevelID].name); + + int startTime = GetTickCount(); + int lastTime = -16; + int fpsTime = startTime; + + while (1) + { + inputUpdate(); + + if (keyDown(KEY_NSPIRE_ESC)) + { + break; + } + + int time = GetTickCount() - startTime; + gameUpdate((time - lastTime) / 16); + lastTime = time; + + gameRender(); + + lcd_blit(fb, SCR_320x240_8); + //msleep(16); + + fpsCounter++; + if (lastTime - fpsTime >= 1000) + { + fps = fpsCounter; + fpsCounter = 0; + fpsTime = lastTime - ((lastTime - fpsTime) - 1000); + } + } + + return 0; +} diff --git a/src/platform/tns/sound.cpp b/src/platform/tns/sound.cpp new file mode 100644 index 00000000..2512df5c --- /dev/null +++ b/src/platform/tns/sound.cpp @@ -0,0 +1,51 @@ +#include "common.h" + +void sndInit() +{ + // TODO +} + +void sndInitSamples() +{ + // TODO +} + +void sndFreeSamples() +{ + // TODO +} + +void* sndPlaySample(int32 index, int32 volume, int32 pitch, int32 mode) +{ + return NULL; // TODO +} + +void sndPlayTrack(int32 track) +{ + // TODO +} + +void sndStopTrack() +{ + // TODO +} + +bool sndTrackIsPlaying() +{ + return false; // TODO +} + +void sndStopSample(int32 index) +{ + // TODO +} + +void sndStop() +{ + // TODO +} + +void sndFill(uint8* buffer, int32 count) +{ + // TODO +} diff --git a/src/platform/web/index.php b/src/platform/web/index.php index 08550752..01d8939b 100644 --- a/src/platform/web/index.php +++ b/src/platform/web/index.php @@ -1,16 +1,15 @@ - + OpenLara