From 73c69e8eda4ade55ea95c89a9062340376918af4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 25 Sep 2012 02:35:50 +0200 Subject: [PATCH 001/378] video playback --- apps/openmw/CMakeLists.txt | 7 +- apps/openmw/mwbase/world.hpp | 4 + apps/openmw/mwgui/mode.hpp | 4 +- apps/openmw/mwgui/windowmanagerimp.cpp | 3 + apps/openmw/mwrender/renderingmanager.cpp | 11 + apps/openmw/mwrender/renderingmanager.hpp | 5 + apps/openmw/mwrender/videoplayer.cpp | 395 ++++++++++++++++++++++ apps/openmw/mwrender/videoplayer.hpp | 105 ++++++ apps/openmw/mwscript/docs/vmformat.txt | 3 +- apps/openmw/mwscript/miscextensions.cpp | 16 + apps/openmw/mwworld/worldimp.cpp | 5 + apps/openmw/mwworld/worldimp.hpp | 3 + files/gbuffer/gbuffer.compositor | 2 +- 13 files changed, 559 insertions(+), 4 deletions(-) create mode 100644 apps/openmw/mwrender/videoplayer.cpp create mode 100644 apps/openmw/mwrender/videoplayer.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 66844b280..da1b3e15d 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -16,7 +16,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender renderingmanager debugging sky player animation npcanimation creatureanimation actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows - compositors characterpreview externalrendering globalmap + compositors characterpreview externalrendering globalmap videoplayer ) add_openmw_dir (mwinput @@ -108,6 +108,11 @@ target_link_libraries(openmw components ) +if (USE_FFMPEG) + target_link_libraries(openmw + "swscale") +endif() + # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT}) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 521bbb988..0ba4b84dd 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -291,6 +291,10 @@ namespace MWBase /// 1 - only waiting \n /// 2 - player is underwater \n /// 3 - enemies are nearby (not implemented) + + + /// \todo this does not belong here + virtual void playVideo(const std::string& name) = 0; }; } diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index 64aa1dc21..74ecd531a 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -41,7 +41,9 @@ namespace MWGui GM_Loading, GM_LoadingWallpaper, - GM_QuickKeysMenu + GM_QuickKeysMenu, + + GM_Video }; // Windows shown in inventory mode diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 5a04a90c0..6ff4cee07 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -371,6 +371,9 @@ void WindowManager::updateVisible() case GM_Loading: MyGUI::PointerManager::getInstance().setVisible(false); break; + case GM_Video: + mHud->setVisible(false); + break; default: // Unsupported mode, switch back to game break; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index f0833e82d..98fc4175e 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -36,6 +36,7 @@ #include "npcanimation.hpp" #include "externalrendering.hpp" #include "globalmap.hpp" +#include "videoplayer.hpp" using namespace MWRender; using namespace Ogre; @@ -159,6 +160,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); + mVideoPlayer = new VideoPlayer(mRendering.getScene ()); + mSun = 0; mDebugging = new Debugging(mMwRoot, engine); @@ -180,6 +183,7 @@ RenderingManager::~RenderingManager () delete mOcclusionQuery; delete mCompositors; delete mWater; + delete mVideoPlayer; } MWRender::SkyManager* RenderingManager::getSkyManager() @@ -339,6 +343,8 @@ void RenderingManager::update (float duration) mRendering.update(duration); + mVideoPlayer->update (); + MWWorld::RefData &data = MWBase::Environment::get() .getWorld() @@ -898,4 +904,9 @@ void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rend rendering.setup (mRendering.getScene()); } +void RenderingManager::playVideo(const std::string& name) +{ + mVideoPlayer->play ("video/" + name); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 359809b71..d0ef3f593 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -45,6 +45,7 @@ namespace MWRender class Compositors; class ExternalRendering; class GlobalMap; + class VideoPlayer; class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener { @@ -195,6 +196,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void setupExternalRendering (MWRender::ExternalRendering& rendering); + void playVideo(const std::string& name); + protected: virtual void windowResized(Ogre::RenderWindow* rw); virtual void windowClosed(Ogre::RenderWindow* rw); @@ -248,6 +251,8 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList MWRender::Shadows* mShadows; MWRender::Compositors* mCompositors; + + VideoPlayer* mVideoPlayer; }; } diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp new file mode 100644 index 000000000..04a6de30b --- /dev/null +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -0,0 +1,395 @@ +#include "videoplayer.hpp" + +//#ifdef OPENMW_USE_FFMPEG + +#include +#include +#include +#include + + +extern "C" +{ +#include +#include +#include +} + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/environment.hpp" + + +namespace MWRender +{ + + int OgreResource_Read(void *opaque, uint8_t *buf, int buf_size) + { + Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + + int num_read = stream->size() - stream->tell(); + + if (num_read > buf_size) + num_read = buf_size; + + stream->read(buf, num_read); + return num_read; + } + + int OgreResource_Write(void *opaque, uint8_t *buf, int buf_size) + { + Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + + int num_write = stream->size() - stream->tell(); + + if (num_write > buf_size) + num_write = buf_size; + + stream->write (buf, num_write); + return num_write; + } + + int64_t OgreResource_Seek(void *opaque, int64_t offset, int whence) + { + Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + + switch (whence) + { + case SEEK_SET: + stream->seek(offset); + case SEEK_CUR: + stream->seek(stream->tell() + offset); + case SEEK_END: + stream->seek(stream->size() + offset); + case AVSEEK_SIZE: + return stream->size(); + default: + return -1; + } + + return stream->tell(); + } + + VideoPlayer::VideoPlayer(Ogre::SceneManager *sceneMgr) + : mAvContext(NULL) + , mVideoStreamId(-1) + { + Ogre::MaterialPtr videoMaterial = Ogre::MaterialManager::getSingleton ().create("VideoMaterial", "General"); + videoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + videoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); + videoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); + mTextureUnit = videoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(); + + mRectangle = new Ogre::Rectangle2D(true); + mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); + mRectangle->setMaterial("VideoMaterial"); + mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+1); + // Use infinite AAB to always stay visible + Ogre::AxisAlignedBox aabInf; + aabInf.setInfinite(); + mRectangle->setBoundingBox(aabInf); + // Attach background to the scene + Ogre::SceneNode* node = sceneMgr->getRootSceneNode()->createChildSceneNode(); + node->attachObject(mRectangle); + mRectangle->setVisible(false); + mRectangle->setVisibilityFlags (0x1); + } + + VideoPlayer::~VideoPlayer() + { + if (mAvContext) + deleteContext(); + + delete mRectangle; + } + + void VideoPlayer::play (const std::string& resourceName) + { + mStream = Ogre::ResourceGroupManager::getSingleton ().openResource (resourceName); + + + mVideoStreamId = -1; + mEOF = false; + + // if something is already playing, close it + if (mAvContext) + close(); + + mRectangle->setVisible(true); + + MWBase::Environment::get().getWindowManager ()->pushGuiMode (MWGui::GM_Video); + + + // BASIC INITIALIZATION + + // Load all the decoders + av_register_all(); + + AVIOContext *ioContext = 0; + + int err = 0; + + mAvContext = avformat_alloc_context(); + if (!mAvContext) + throwError(0); + + ioContext = avio_alloc_context(NULL, 0, 0, &mStream, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if (!ioContext) + throwError(0); + + mAvContext->pb = ioContext; + + err = avformat_open_input(&mAvContext, resourceName.c_str(), NULL, NULL); + if (err != 0) + throwError(err); + + err = avformat_find_stream_info(mAvContext, 0); + if (err < 0) + throwError(err); + + + + + // INIT VIDEO + + // Find the video stream among the different streams + for (unsigned int i = 0; i < mAvContext->nb_streams; i++) + { + if (mAvContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) + { + mVideoStreamId = i; + break; + } + } + + if (-1 == mVideoStreamId) + throw std::runtime_error("No video stream found in the video"); + + // Get the video codec + mVideoCodecContext = mAvContext->streams[mVideoStreamId]->codec; + if (!mVideoCodecContext) + throw std::runtime_error("Stream doesn't have a codec"); + + // Get the video decoder + mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id); + if (NULL == mVideoCodec) + throw std::runtime_error("No decoder found"); + + // Load the video codec + err = avcodec_open2(mVideoCodecContext, mVideoCodec, 0); + if (err < 0) + throwError (err); + + // Create the frame buffers + mRawFrame = avcodec_alloc_frame(); + mRGBAFrame = avcodec_alloc_frame(); + if (!mRawFrame || !mRGBAFrame) + { + throw std::runtime_error("Can't allocate video frames"); + } + + + avpicture_alloc ((AVPicture *)mRGBAFrame, PIX_FMT_RGBA, mVideoCodecContext->width, mVideoCodecContext->height); + + + // Setup the image scaler + // All this does is convert from YUV to RGB - note it would be faster to do this in a shader, + // but i'm not worried about performance just yet + mSwsContext = sws_getContext(mVideoCodecContext->width, mVideoCodecContext->height, + mVideoCodecContext->pix_fmt, + mVideoCodecContext->width, mVideoCodecContext->height, + PIX_FMT_RGBA, + SWS_BICUBIC, NULL, NULL, NULL); + if (!mSwsContext) + throw std::runtime_error("Can't create SWS Context"); + + // Get the frame time we need for this video + AVRational r = mAvContext->streams[mVideoStreamId]->avg_frame_rate; + AVRational r2 = mAvContext->streams[mVideoStreamId]->r_frame_rate; + if ((!r.num || !r.den) && + (!r2.num || !r2.den)) + { + std::cerr << "Warning - unable to get the video frame rate. Using standard NTSC frame rate : 29.97 fps." << std::endl; + mWantedFrameTime = 1.f / 29.97f; + } + else + { + if (r.num && r.den) + mWantedFrameTime = 1.f/((float)r.num / r.den); + else + mWantedFrameTime = 1.f/((float)r2.num / r2.den); + } + + + mTextureUnit->setTextureName (""); + + if (!Ogre::TextureManager::getSingleton ().getByName("VideoTexture").isNull()) + Ogre::TextureManager::getSingleton().remove("VideoTexture"); + + mVideoTexture = Ogre::TextureManager::getSingleton().createManual( + "VideoTexture", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, + mVideoCodecContext->width, mVideoCodecContext->height, + 0, + Ogre::PF_BYTE_RGBA, + Ogre::TU_DEFAULT); + + mTextureUnit->setTextureName ("VideoTexture"); + + } + + void VideoPlayer::throwError(int error) + { + char buffer[4096] = {0}; + + if (0 == av_strerror(error, buffer, sizeof(buffer))) + { + std::stringstream msg; + msg << "FFMPEG error: "; + msg << buffer << std::endl; + throw std::runtime_error(msg.str()); + } + else + throw std::runtime_error("Unknown FFMPEG error"); + } + + bool VideoPlayer::readFrameAndQueue () + { + bool ret = true; + AVPacket *pkt = NULL; + + // check we're not at eof + if (mEOF) + ret = false; + else + { + // read frame + pkt = (AVPacket *)av_malloc(sizeof(*pkt)); + int res = av_read_frame(mAvContext, pkt); + + // check we didn't reach eof right now + if (res < 0) + { + mEOF = true; + ret = false; + av_free(pkt); + } + else + { + // When a frame has been read, save it + if (!saveFrame(pkt)) + { + // we failed to save it, which means it was unknown type of frame - delete it here + av_free_packet(pkt); + av_free(pkt); + } + } + } + + return ret; + } + + bool VideoPlayer::saveFrame(AVPacket* frame) + { + bool saved = false; + + if (frame->stream_index == mVideoStreamId) + { + // If it was a video frame... + mVideoPacketQueue.push(frame); + saved = true; + } + + return saved; + + } + + void VideoPlayer::update() + { + if (!mAvContext) + return; + + + if (!mVideoPacketQueue.size() && mEOF) + close(); + + Ogre::Timer timer; + + if (!mVideoPacketQueue.size()) + { + if (readFrameAndQueue()) + decodeFrontFrame(); + } + else + decodeFrontFrame(); + + mDecodingTime = timer.getMilliseconds ()/1000.f; + } + + void VideoPlayer::decodeFrontFrame () + { + int didDecodeFrame = 0; + + // Make sure there is something to decode + if (!mVideoPacketQueue.size()) + return; + + // Get the front frame and decode it + AVPacket *videoPacket = mVideoPacketQueue.front(); + int res; + res = avcodec_decode_video2(mVideoCodecContext, mRawFrame, &didDecodeFrame, videoPacket); + + if (res < 0 || !didDecodeFrame) + throw std::runtime_error ("an error occured while decoding the video frame"); + + // Convert the frame to RGB + sws_scale(mSwsContext, + mRawFrame->data, mRawFrame->linesize, + 0, mVideoCodecContext->height, + mRGBAFrame->data, mRGBAFrame->linesize); + + + Ogre::HardwarePixelBufferSharedPtr pixelBuffer = mVideoTexture->getBuffer(); + Ogre::PixelBox pb(mVideoCodecContext->width, mVideoCodecContext->height, 1, Ogre::PF_BYTE_RGBA, mRGBAFrame->data[0]); + pixelBuffer->blitFromMemory(pb); + + //m_displayedFrameCount++; + av_free_packet(mVideoPacketQueue.front()); + av_free(mVideoPacketQueue.front()); + mVideoPacketQueue.pop(); + } + + void VideoPlayer::close () + { + mRectangle->setVisible (false); + MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video); + deleteContext(); + } + + void VideoPlayer::deleteContext() + { + while (mVideoPacketQueue.size()) + { + av_free_packet(mVideoPacketQueue.front()); + av_free(mVideoPacketQueue.front()); + mVideoPacketQueue.pop(); + } + + avcodec_close(mVideoCodecContext); + + avpicture_free((AVPicture *)mRGBAFrame); + + if (mRawFrame) + av_free(mRawFrame); + if (mRGBAFrame) + av_free(mRGBAFrame); + + sws_freeContext(mSwsContext); + + avformat_close_input(&mAvContext); + + mAvContext = NULL; + } +} + +//#endif diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp new file mode 100644 index 000000000..05c04c96b --- /dev/null +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -0,0 +1,105 @@ +#ifndef MWRENDER_VIDEOPLAYER_H +#define MWRENDER_VIDEOPLAYER_H + +//#ifdef OPENMW_USE_FFMPEG + + + +#include + +#include +#include + +namespace Ogre +{ + class Rectangle2D; + class SceneManager; + class TextureUnitState; +} + +struct AVFormatContext; +struct AVCodecContext; +struct AVCodec; +struct AVFrame; +struct SwsContext; +struct AVPacket; + +namespace MWRender +{ + + class VideoPlayer + { + public: + VideoPlayer(Ogre::SceneManager* sceneMgr); + ~VideoPlayer(); + + void play (const std::string& resourceName); + + void update(); + + private: + Ogre::Rectangle2D* mRectangle; + Ogre::TextureUnitState* mTextureUnit; + + Ogre::DataStreamPtr mStream; + + Ogre::TexturePtr mVideoTexture; + + + private: + + AVFormatContext* mAvContext; + + + bool mEOF; + + // VIDEO + AVCodecContext* mVideoCodecContext; + AVCodec* mVideoCodec; + int mVideoStreamId; + AVFrame* mRawFrame; + AVFrame* mRGBAFrame; + SwsContext* mSwsContext; + float mWantedFrameTime; + float mDecodingTime; + std::queue mVideoPacketQueue; + + + bool readFrameAndQueue(); + bool saveFrame(AVPacket* frame); + + void decodeFrontFrame(); + + void close(); + void deleteContext(); + + + void throwError(int error); + }; + +} + +//#else +/* + + +// If FFMPEG not available, dummy implentation that does nothing + +namespace MWRender +{ + + class VideoPlayer + { + public: + VideoPlayer(Ogre::SceneManager* sceneMgr){} + + void play (const std::string& resourceName) {} + void update() {} + }; + +} +*/ +//#endif + + +#endif diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index a4a9e99fd..1c74a713f 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -205,5 +205,6 @@ op 0x200019e: PlaceAtMe Explicit op 0x200019f: GetPcSleep op 0x20001a0: ShowMap op 0x20001a1: FillMap -opcodes 0x20001a2-0x3ffffff unused +op 0x20001a2: PlayBink +opcodes 0x20001a3-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index a869f882b..330c1c79f 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -21,6 +21,19 @@ namespace MWScript { namespace Misc { + class OpPlayBink : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + std::string name = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWBase::Environment::get().getWorld ()->playVideo (name); + } + }; + class OpGetPcSleep : public Interpreter::Opcode0 { public: @@ -261,6 +274,7 @@ namespace MWScript const int opcodeDontSaveObject = 0x2000153; const int opcodeToggleVanityMode = 0x2000174; const int opcodeGetPcSleep = 0x200019f; + const int opcodePlayBink = 0x20001a2; void registerExtensions (Compiler::Extensions& extensions) { @@ -286,6 +300,7 @@ namespace MWScript extensions.registerInstruction ("togglevanitymode", "", opcodeToggleVanityMode); extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode); extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep); + extensions.registerInstruction ("playbink", "S", opcodePlayBink); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -307,6 +322,7 @@ namespace MWScript interpreter.installSegment5 (opcodeDontSaveObject, new OpDontSaveObject); interpreter.installSegment5 (opcodeToggleVanityMode, new OpToggleVanityMode); interpreter.installSegment5 (opcodeGetPcSleep, new OpGetPcSleep); + interpreter.installSegment5 (opcodePlayBink, new OpPlayBink); } } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 834dffe79..06b58c183 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1281,4 +1281,9 @@ namespace MWWorld return 0; } + + void World::playVideo (const std::string &name) + { + mRendering->playVideo(name); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 90cd2151b..5d8c689db 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -322,6 +322,9 @@ namespace MWWorld /// 1 - only waiting \n /// 2 - player is underwater \n /// 3 - enemies are nearby (not implemented) + + /// \todo this does not belong here + virtual void playVideo(const std::string& name); }; } diff --git a/files/gbuffer/gbuffer.compositor b/files/gbuffer/gbuffer.compositor index 04600ce9b..0a0675fa0 100644 --- a/files/gbuffer/gbuffer.compositor +++ b/files/gbuffer/gbuffer.compositor @@ -72,7 +72,7 @@ compositor gbufferFinalizer pass render_scene { first_render_queue 51 - last_render_queue 100 + last_render_queue 105 } } target_output From 05eb307bfbe6b15ac39f5eae564b69797ba7f849 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 25 Sep 2012 02:54:29 +0200 Subject: [PATCH 002/378] added video timing --- apps/openmw/mwgui/windowmanagerimp.cpp | 1 + apps/openmw/mwrender/videoplayer.cpp | 14 +++++++++++++- apps/openmw/mwrender/videoplayer.hpp | 5 +++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 6ff4cee07..378003e39 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -372,6 +372,7 @@ void WindowManager::updateVisible() MyGUI::PointerManager::getInstance().setVisible(false); break; case GM_Video: + MyGUI::PointerManager::getInstance().setVisible(false); mHud->setVisible(false); break; default: diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 04a6de30b..195369a2d 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -109,6 +109,7 @@ namespace MWRender mVideoStreamId = -1; mEOF = false; + mDisplayedFrameCount = 0; // if something is already playing, close it if (mAvContext) @@ -236,6 +237,8 @@ namespace MWRender mTextureUnit->setTextureName ("VideoTexture"); + mTimer.reset(); + } void VideoPlayer::throwError(int error) @@ -309,6 +312,14 @@ namespace MWRender if (!mAvContext) return; + // Time elapsed since the video started + float realTime = mTimer.getMilliseconds ()/1000.f; + + // Here is the time we're at in the video + float movieTime = mDisplayedFrameCount * mWantedFrameTime; + + if (movieTime >= realTime) + return; if (!mVideoPacketQueue.size() && mEOF) close(); @@ -353,10 +364,11 @@ namespace MWRender Ogre::PixelBox pb(mVideoCodecContext->width, mVideoCodecContext->height, 1, Ogre::PF_BYTE_RGBA, mRGBAFrame->data[0]); pixelBuffer->blitFromMemory(pb); - //m_displayedFrameCount++; av_free_packet(mVideoPacketQueue.front()); av_free(mVideoPacketQueue.front()); mVideoPacketQueue.pop(); + + ++mDisplayedFrameCount; } void VideoPlayer::close () diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 05c04c96b..1d3010e8e 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -9,6 +9,7 @@ #include #include +#include namespace Ogre { @@ -53,6 +54,8 @@ namespace MWRender bool mEOF; + Ogre::Timer mTimer; + // VIDEO AVCodecContext* mVideoCodecContext; AVCodec* mVideoCodec; @@ -64,6 +67,8 @@ namespace MWRender float mDecodingTime; std::queue mVideoPacketQueue; + int mDisplayedFrameCount; + bool readFrameAndQueue(); bool saveFrame(AVPacket* frame); From c3cd6e8a8aaccf74a8e62d6460a938584be4fc4c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 21 Nov 2012 17:31:18 +0100 Subject: [PATCH 003/378] new attempt at the editor --- CMakeLists.txt | 7 +++- apps/opencs/CMakeLists.txt | 44 +++++++++++++++++++++++ apps/opencs/main.cpp | 5 +++ apps/opencs/model/doc/document.cpp | 4 +++ apps/opencs/model/doc/document.hpp | 17 +++++++++ apps/opencs/model/doc/documentmanager.cpp | 37 +++++++++++++++++++ apps/opencs/model/doc/documentmanager.hpp | 31 ++++++++++++++++ 7 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 apps/opencs/CMakeLists.txt create mode 100644 apps/opencs/main.cpp create mode 100644 apps/opencs/model/doc/document.cpp create mode 100644 apps/opencs/model/doc/document.hpp create mode 100644 apps/opencs/model/doc/documentmanager.cpp create mode 100644 apps/opencs/model/doc/documentmanager.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 78388e20f..d657de6ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binaries option(BUILD_ESMTOOL "build ESM inspector" ON) option(BUILD_LAUNCHER "build Launcher" ON) option(BUILD_MWINIIMPORTER "build MWiniImporter" ON) +option(BUILD_OPENCS "build OpenMW Construction Set" ON) option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest ang GMock frameworks" OFF) @@ -244,7 +245,7 @@ if (APPLE) else () set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_DBG}) endif () - + #set(OGRE_PLUGIN_DIR "${OGRE_PLUGIN_DIR}/") configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist @@ -462,6 +463,10 @@ if (BUILD_MWINIIMPORTER) add_subdirectory( apps/mwiniimporter ) endif() +if (BUILD_OPENCS) + add_subdirectory (apps/opencs) +endif() + # UnitTests if (BUILD_UNITTESTS) add_subdirectory( apps/openmw_test_suite ) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt new file mode 100644 index 000000000..d213f68f9 --- /dev/null +++ b/apps/opencs/CMakeLists.txt @@ -0,0 +1,44 @@ + +set (OPENCS_SRC + main.cpp + + model/doc/documentmanager.cpp model/doc/document.cpp + ) + +set (OPENCS_HDR + model/doc/documentmanager.hpp model/doc/document.hpp + ) + +set (OPENCS_US + ) + +set (OPENCS_RES + ) + +source_group (opencs FILES ${OPENCS_SRC} ${OPENCS_HDR}) + +if(WIN32) + set(QT_USE_QTMAIN TRUE) +endif(WIN32) + +find_package(Qt4 COMPONENTS QtCore QtGui QtXml QtXmlPatterns REQUIRED) +include(${QT_USE_FILE}) + +qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI}) +qt4_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR}) +qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES}) + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +add_executable(opencs + ${OPENCS_SRC} + ${OPENCS_UI_HDR} + ${OPENCS_MOC_SRC} + ${OPENCS_RES_SRC} +) + +target_link_libraries(opencs + ${Boost_LIBRARIES} + ${QT_LIBRARIES} + components +) \ No newline at end of file diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp new file mode 100644 index 000000000..cd443603b --- /dev/null +++ b/apps/opencs/main.cpp @@ -0,0 +1,5 @@ + +int main(int argc, char *argv[]) +{ + +} \ No newline at end of file diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp new file mode 100644 index 000000000..7154c484b --- /dev/null +++ b/apps/opencs/model/doc/document.cpp @@ -0,0 +1,4 @@ + +#include "document.hpp" + +CSMDoc::Document::Document() {} \ No newline at end of file diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp new file mode 100644 index 000000000..7badf8fd7 --- /dev/null +++ b/apps/opencs/model/doc/document.hpp @@ -0,0 +1,17 @@ +#ifndef CSM_DOCUMENT_H +#define CSM_DOCUMENT_H + +namespace CSMDoc +{ + class Document + { + Document (const Document&); + Document& operator= (const Document&); + + public: + + Document(); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp new file mode 100644 index 000000000..68595f2d4 --- /dev/null +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -0,0 +1,37 @@ + +#include "documentmanager.hpp" + +#include +#include + +#include "document.hpp" + +CSMDoc::DocumentManager::DocumentManager() {} + +CSMDoc::DocumentManager::~DocumentManager() +{ + for (std::vector::iterator iter (mDocuments.begin()); iter!=mDocuments.end(); ++iter) + delete *iter; +} + +CSMDoc::Document *CSMDoc::DocumentManager::addDocument() +{ + Document *document = new Document; + + mDocuments.push_back (document); + + return document; +} + +bool CSMDoc::DocumentManager::removeDocument (Document *document) +{ + std::vector::iterator iter = std::find (mDocuments.begin(), mDocuments.end(), document); + + if (iter==mDocuments.end()) + throw std::runtime_error ("removing invalid document"); + + mDocuments.erase (iter); + delete document; + + return mDocuments.empty(); +} \ No newline at end of file diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp new file mode 100644 index 000000000..7f0d388bc --- /dev/null +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -0,0 +1,31 @@ +#ifndef CSM_DOCUMENTMGR_H +#define CSM_DOCUMENTMGR_H + +#include + +namespace CSMDoc +{ + class Document; + + class DocumentManager + { + std::vector mDocuments; + + DocumentManager (const DocumentManager&); + DocumentManager& operator= (const DocumentManager&); + + public: + + DocumentManager(); + + ~DocumentManager(); + + Document *addDocument(); + ///< The ownership of the returned document is not transferred to the caller. + + bool removeDocument (Document *document); + ///< \return last document removed? + }; +} + +#endif \ No newline at end of file From 9834bb3ad52db108a11d6afe1a26d31f6833d925 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 22 Nov 2012 13:30:02 +0100 Subject: [PATCH 004/378] basic document handling --- apps/opencs/CMakeLists.txt | 8 ++++- apps/opencs/editor.cpp | 21 +++++++++++++ apps/opencs/editor.hpp | 29 +++++++++++++++++ apps/opencs/main.cpp | 8 +++++ apps/opencs/model/doc/document.hpp | 5 +-- apps/opencs/model/doc/documentmanager.hpp | 4 +-- apps/opencs/view/doc/view.cpp | 8 +++++ apps/opencs/view/doc/view.hpp | 30 ++++++++++++++++++ apps/opencs/view/doc/viewmanager.cpp | 26 ++++++++++++++++ apps/opencs/view/doc/viewmanager.hpp | 38 +++++++++++++++++++++++ 10 files changed, 172 insertions(+), 5 deletions(-) create mode 100644 apps/opencs/editor.cpp create mode 100644 apps/opencs/editor.hpp create mode 100644 apps/opencs/view/doc/view.cpp create mode 100644 apps/opencs/view/doc/view.hpp create mode 100644 apps/opencs/view/doc/viewmanager.cpp create mode 100644 apps/opencs/view/doc/viewmanager.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index d213f68f9..d7f113447 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -1,12 +1,18 @@ set (OPENCS_SRC - main.cpp + main.cpp editor.cpp model/doc/documentmanager.cpp model/doc/document.cpp + + view/doc/viewmanager.cpp view/doc/view.cpp ) set (OPENCS_HDR + editor.hpp + model/doc/documentmanager.hpp model/doc/document.hpp + + view/doc/viewmanager.hpp view/doc/view.hpp ) set (OPENCS_US diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp new file mode 100644 index 000000000..0a8a137f6 --- /dev/null +++ b/apps/opencs/editor.cpp @@ -0,0 +1,21 @@ + +#include "editor.hpp" + +#include + +CS::Editor::Editor() +{ +} + +void CS::Editor::createDocument() +{ + CSMDoc::Document *document = mDocumentManager.addDocument(); + mViewManager.addView (document); +} + +int CS::Editor::run() +{ + createDocument(); + + return QApplication::exec(); +} \ No newline at end of file diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp new file mode 100644 index 000000000..6e183b843 --- /dev/null +++ b/apps/opencs/editor.hpp @@ -0,0 +1,29 @@ +#ifndef CS_EDITOR_H +#define CS_EDITOR_H + +#include "model/doc/documentmanager.hpp" +#include "view/doc/viewmanager.hpp" + +namespace CS +{ + class Editor + { + CSMDoc::DocumentManager mDocumentManager; + CSVDoc::ViewManager mViewManager; + + // not implemented + Editor (const Editor&); + Editor& operator= (const Editor&); + + public: + + Editor(); + + void createDocument(); + + int run(); + ///< \return error status + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index cd443603b..15772eba0 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -1,5 +1,13 @@ +#include "editor.hpp" + +#include + int main(int argc, char *argv[]) { + QApplication mApplication (argc, argv); + + CS::Editor editor; + return editor.run(); } \ No newline at end of file diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 7badf8fd7..e44085962 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -1,10 +1,11 @@ -#ifndef CSM_DOCUMENT_H -#define CSM_DOCUMENT_H +#ifndef CSM_DOC_DOCUMENT_H +#define CSM_DOC_DOCUMENT_H namespace CSMDoc { class Document { + // not implemented Document (const Document&); Document& operator= (const Document&); diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp index 7f0d388bc..f20f3101d 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -1,5 +1,5 @@ -#ifndef CSM_DOCUMENTMGR_H -#define CSM_DOCUMENTMGR_H +#ifndef CSM_DOC_DOCUMENTMGR_H +#define CSM_DOC_DOCUMENTMGR_H #include diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp new file mode 100644 index 000000000..2bad69bad --- /dev/null +++ b/apps/opencs/view/doc/view.cpp @@ -0,0 +1,8 @@ + +#include "view.hpp" + +CSVDoc::View::View (CSMDoc::Document *document) : mDocument (document) +{ + resize (200, 200); + setWindowTitle ("New Document"); +} \ No newline at end of file diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp new file mode 100644 index 000000000..797eef083 --- /dev/null +++ b/apps/opencs/view/doc/view.hpp @@ -0,0 +1,30 @@ +#ifndef CSV_DOC_VIEW_H +#define CSV_DOC_VIEW_H + +#include + +namespace CSMDoc +{ + class Document; +} + +namespace CSVDoc +{ + class View : public QWidget + { + Q_OBJECT + + CSMDoc::Document *mDocument; + + // not implemented + View (const View&); + View& operator= (const View&); + + public: + + View (CSMDoc::Document *document); + ///< The ownership of \a document is not transferred to *this. + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp new file mode 100644 index 000000000..59b79be47 --- /dev/null +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -0,0 +1,26 @@ + +#include "viewmanager.hpp" + +#include "view.hpp" + +CSVDoc::ViewManager::ViewManager() +{ + +} + +CSVDoc::ViewManager::~ViewManager() +{ + for (std::vector::iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) + delete *iter; +} + +CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) +{ + View *view = new View (document); + + mViews.push_back (view); + + view->show(); + + return view; +} \ No newline at end of file diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp new file mode 100644 index 000000000..dc6a07bce --- /dev/null +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -0,0 +1,38 @@ +#ifndef CSV_DOC_VIEWMANAGER_H +#define CSV_DOC_VIEWMANAGER_H + +#include + +namespace CSMDoc +{ + class Document; +} + +namespace CSVDoc +{ + class View; + + class ViewManager + { + std::vector mViews; + + // not implemented + ViewManager (const ViewManager&); + ViewManager& operator= (const ViewManager&); + + public: + + ViewManager(); + + ~ViewManager(); + + View *addView (CSMDoc::Document *document); + ///< The ownership of the returned view is not transferred. + + + + }; + +} + +#endif \ No newline at end of file From 758371d7e4c346f92b715bb3cb26ec9dfbd58e55 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 22 Nov 2012 14:10:23 +0100 Subject: [PATCH 005/378] implemented view/document closing --- apps/opencs/editor.cpp | 2 +- apps/opencs/view/doc/view.cpp | 25 ++++++++++++++++- apps/opencs/view/doc/view.hpp | 13 ++++++++- apps/opencs/view/doc/viewmanager.cpp | 41 ++++++++++++++++++++++++++-- apps/opencs/view/doc/viewmanager.hpp | 10 ++++++- 5 files changed, 85 insertions(+), 6 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 0a8a137f6..264ae7543 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -3,7 +3,7 @@ #include -CS::Editor::Editor() +CS::Editor::Editor() : mViewManager (mDocumentManager) { } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 2bad69bad..51078d448 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -1,8 +1,31 @@ #include "view.hpp" -CSVDoc::View::View (CSMDoc::Document *document) : mDocument (document) +#include + +#include + +#include "viewmanager.hpp" + +void CSVDoc::View::closeEvent (QCloseEvent *event) +{ + if (!mViewManager.closeRequest (this)) + event->ignore(); +} + +CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document) +: mViewManager (viewManager), mDocument (document) { resize (200, 200); setWindowTitle ("New Document"); +} + +const CSMDoc::Document *CSVDoc::View::getDocument() const +{ + return mDocument; +} + +CSMDoc::Document *CSVDoc::View::getDocument() +{ + return mDocument; } \ No newline at end of file diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 797eef083..b12bb7e41 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -10,20 +10,31 @@ namespace CSMDoc namespace CSVDoc { + class ViewManager; + class View : public QWidget { Q_OBJECT + ViewManager& mViewManager; CSMDoc::Document *mDocument; // not implemented View (const View&); View& operator= (const View&); + private: + + void closeEvent (QCloseEvent *event); + public: - View (CSMDoc::Document *document); + View (ViewManager& viewManager, CSMDoc::Document *document); ///< The ownership of \a document is not transferred to *this. + + const CSMDoc::Document *getDocument() const; + + CSMDoc::Document *getDocument(); }; } diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 59b79be47..da73213f7 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -1,9 +1,12 @@ #include "viewmanager.hpp" +#include "../../model/doc/documentmanager.hpp" + #include "view.hpp" -CSVDoc::ViewManager::ViewManager() +CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) +: mDocumentManager (documentManager) { } @@ -12,15 +15,49 @@ CSVDoc::ViewManager::~ViewManager() { for (std::vector::iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) delete *iter; + + for (std::vector::iterator iter (mClosed.begin()); iter!=mClosed.end(); ++iter) + delete *iter; } CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) { - View *view = new View (document); + View *view = new View (*this, document); mViews.push_back (view); view->show(); return view; +} + +int CSVDoc::ViewManager::countViews (const CSMDoc::Document *document) const +{ + int count = 0; + + for (std::vector::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) + if ((*iter)->getDocument()==document) + ++count; + + return count; +} + +bool CSVDoc::ViewManager::closeRequest (View *view) +{ + std::vector::iterator iter = std::find (mViews.begin(), mViews.end(), view); + + if (iter!=mViews.end()) + { + bool last = countViews (view->getDocument())<=1; + + /// \todo check if document has not been saved -> return false and start close dialogue + + mViews.erase (iter); + mClosed.push_back (view); + + if (last) + mDocumentManager.removeDocument (view->getDocument()); + } + + return true; } \ No newline at end of file diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp index dc6a07bce..6901590ed 100644 --- a/apps/opencs/view/doc/viewmanager.hpp +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -6,6 +6,7 @@ namespace CSMDoc { class Document; + class DocumentManager; } namespace CSVDoc @@ -14,7 +15,9 @@ namespace CSVDoc class ViewManager { + CSMDoc::DocumentManager& mDocumentManager; std::vector mViews; + std::vector mClosed; // not implemented ViewManager (const ViewManager&); @@ -22,13 +25,18 @@ namespace CSVDoc public: - ViewManager(); + ViewManager (CSMDoc::DocumentManager& documentManager); ~ViewManager(); View *addView (CSMDoc::Document *document); ///< The ownership of the returned view is not transferred. + int countViews (const CSMDoc::Document *document) const; + ///< Return number of views for \a document. + + bool closeRequest (View *view); + }; From 789cecb9df5616fd49717ec006b3c7962a73b408 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 22 Nov 2012 14:49:58 +0100 Subject: [PATCH 006/378] added main menu and implemented new view function --- apps/opencs/view/doc/view.cpp | 20 ++++++++++++++++++++ apps/opencs/view/doc/view.hpp | 12 ++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 51078d448..847462964 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "viewmanager.hpp" @@ -13,11 +14,25 @@ void CSVDoc::View::closeEvent (QCloseEvent *event) event->ignore(); } +void CSVDoc::View::setupUi() +{ + // window menu + QMenu *view = menuBar()->addMenu (tr ("&View")); + + QAction *newWindow = new QAction (tr ("&New View"), this); + connect (newWindow, SIGNAL (triggered()), this, SLOT (newView())); + + view->addAction (newWindow); +} + CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document) : mViewManager (viewManager), mDocument (document) { + setCentralWidget (new QWidget); resize (200, 200); setWindowTitle ("New Document"); + + setupUi(); } const CSMDoc::Document *CSVDoc::View::getDocument() const @@ -28,4 +43,9 @@ const CSMDoc::Document *CSVDoc::View::getDocument() const CSMDoc::Document *CSVDoc::View::getDocument() { return mDocument; +} + +void CSVDoc::View::newView() +{ + mViewManager.addView (mDocument); } \ No newline at end of file diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index b12bb7e41..df88ff2cc 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -1,7 +1,9 @@ #ifndef CSV_DOC_VIEW_H #define CSV_DOC_VIEW_H -#include +#include + +class QAction; namespace CSMDoc { @@ -12,7 +14,7 @@ namespace CSVDoc { class ViewManager; - class View : public QWidget + class View : public QMainWindow { Q_OBJECT @@ -27,6 +29,8 @@ namespace CSVDoc void closeEvent (QCloseEvent *event); + void setupUi(); + public: View (ViewManager& viewManager, CSMDoc::Document *document); @@ -35,6 +39,10 @@ namespace CSVDoc const CSMDoc::Document *getDocument() const; CSMDoc::Document *getDocument(); + + private slots: + + void newView(); }; } From 1ddcea1f072e5cd727f5066c911f0a1015cfd983 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 22 Nov 2012 15:09:04 +0100 Subject: [PATCH 007/378] display view indices in title bar --- apps/opencs/view/doc/view.cpp | 30 +++++++++++++++++++++++----- apps/opencs/view/doc/view.hpp | 8 +++++++- apps/opencs/view/doc/viewmanager.cpp | 26 +++++++++++++++++++++++- apps/opencs/view/doc/viewmanager.hpp | 2 ++ 4 files changed, 59 insertions(+), 7 deletions(-) diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 847462964..406ce7dc7 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -1,7 +1,7 @@ #include "view.hpp" -#include +#include #include #include @@ -16,7 +16,7 @@ void CSVDoc::View::closeEvent (QCloseEvent *event) void CSVDoc::View::setupUi() { - // window menu + // view menu QMenu *view = menuBar()->addMenu (tr ("&View")); QAction *newWindow = new QAction (tr ("&New View"), this); @@ -25,12 +25,25 @@ void CSVDoc::View::setupUi() view->addAction (newWindow); } -CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document) -: mViewManager (viewManager), mDocument (document) +void CSVDoc::View::updateTitle() +{ + std::ostringstream stream; + + stream << "New Document "; + + if (mViewTotal>1) + stream << " [" << (mViewIndex+1) << "/" << mViewTotal << "]"; + + setWindowTitle (stream.str().c_str()); +} + +CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) +: mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), mViewTotal (totalViews) { setCentralWidget (new QWidget); resize (200, 200); - setWindowTitle ("New Document"); + + updateTitle(); setupUi(); } @@ -45,6 +58,13 @@ CSMDoc::Document *CSVDoc::View::getDocument() return mDocument; } +void CSVDoc::View::setIndex (int viewIndex, int totalViews) +{ + mViewIndex = viewIndex; + mViewTotal = totalViews; + updateTitle(); +} + void CSVDoc::View::newView() { mViewManager.addView (mDocument); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index df88ff2cc..78ec45a5d 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -20,6 +20,8 @@ namespace CSVDoc ViewManager& mViewManager; CSMDoc::Document *mDocument; + int mViewIndex; + int mViewTotal; // not implemented View (const View&); @@ -31,15 +33,19 @@ namespace CSVDoc void setupUi(); + void updateTitle(); + public: - View (ViewManager& viewManager, CSMDoc::Document *document); + View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews); ///< The ownership of \a document is not transferred to *this. const CSMDoc::Document *getDocument() const; CSMDoc::Document *getDocument(); + void setIndex (int viewIndex, int totalViews); + private slots: void newView(); diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index da73213f7..f6ac6e43d 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -1,10 +1,30 @@ #include "viewmanager.hpp" +#include + #include "../../model/doc/documentmanager.hpp" #include "view.hpp" +void CSVDoc::ViewManager::updateIndices() +{ + std::map > documents; + + for (std::vector::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) + { + std::map >::iterator document = documents.find ((*iter)->getDocument()); + + if (document==documents.end()) + document = + documents.insert ( + std::make_pair ((*iter)->getDocument(), std::make_pair (0, countViews ((*iter)->getDocument())))). + first; + + (*iter)->setIndex (document->second.first++, document->second.second); + } +} + CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) : mDocumentManager (documentManager) { @@ -22,12 +42,14 @@ CSVDoc::ViewManager::~ViewManager() CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) { - View *view = new View (*this, document); + View *view = new View (*this, document, countViews (document)+1); mViews.push_back (view); view->show(); + updateIndices(); + return view; } @@ -57,6 +79,8 @@ bool CSVDoc::ViewManager::closeRequest (View *view) if (last) mDocumentManager.removeDocument (view->getDocument()); + else + updateIndices(); } return true; diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp index 6901590ed..765fafbea 100644 --- a/apps/opencs/view/doc/viewmanager.hpp +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -23,6 +23,8 @@ namespace CSVDoc ViewManager (const ViewManager&); ViewManager& operator= (const ViewManager&); + void updateIndices(); + public: ViewManager (CSMDoc::DocumentManager& documentManager); From ed3d8b8ca2a4cd5dea2870f5bbbcf3f7de6abb0c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 22 Nov 2012 15:54:31 +0100 Subject: [PATCH 008/378] added undo stack and undo/redo actions --- apps/opencs/model/doc/document.cpp | 7 ++++++- apps/opencs/model/doc/document.hpp | 6 ++++++ apps/opencs/view/doc/view.cpp | 25 ++++++++++++++++++++++--- apps/opencs/view/doc/view.hpp | 4 ++++ 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 7154c484b..448598a4e 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -1,4 +1,9 @@ #include "document.hpp" -CSMDoc::Document::Document() {} \ No newline at end of file +CSMDoc::Document::Document() {} + +QUndoStack& CSMDoc::Document::getUndoStack() +{ + return mUndoStack; +} \ No newline at end of file diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index e44085962..1dea7afc2 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -1,10 +1,14 @@ #ifndef CSM_DOC_DOCUMENT_H #define CSM_DOC_DOCUMENT_H +#include + namespace CSMDoc { class Document { + QUndoStack mUndoStack; + // not implemented Document (const Document&); Document& operator= (const Document&); @@ -12,6 +16,8 @@ namespace CSMDoc public: Document(); + + QUndoStack& getUndoStack(); }; } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 406ce7dc7..7a2296fed 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -6,6 +6,8 @@ #include #include +#include "../../model/doc/document.hpp" + #include "viewmanager.hpp" void CSVDoc::View::closeEvent (QCloseEvent *event) @@ -14,17 +16,34 @@ void CSVDoc::View::closeEvent (QCloseEvent *event) event->ignore(); } -void CSVDoc::View::setupUi() +void CSVDoc::View::setupEditMenu() +{ + QMenu *edit = menuBar()->addMenu (tr ("&Edit")); + + QAction *undo = mDocument->getUndoStack().createUndoAction (this, tr("&Undo")); + undo->setShortcuts (QKeySequence::Undo); + edit->addAction (undo); + + QAction *redo = mDocument->getUndoStack().createRedoAction (this, tr("&Redo")); + redo->setShortcuts (QKeySequence::Redo); + edit->addAction (redo); +} + +void CSVDoc::View::setupViewMenu() { - // view menu QMenu *view = menuBar()->addMenu (tr ("&View")); QAction *newWindow = new QAction (tr ("&New View"), this); connect (newWindow, SIGNAL (triggered()), this, SLOT (newView())); - view->addAction (newWindow); } +void CSVDoc::View::setupUi() +{ + setupEditMenu(); + setupViewMenu(); +} + void CSVDoc::View::updateTitle() { std::ostringstream stream; diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 78ec45a5d..457cc84fa 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -31,6 +31,10 @@ namespace CSVDoc void closeEvent (QCloseEvent *event); + void setupEditMenu(); + + void setupViewMenu(); + void setupUi(); void updateTitle(); From 8e546ebd30a6fb8c983bf11ccd9ce22f073b31ed Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 22 Nov 2012 16:00:23 +0100 Subject: [PATCH 009/378] added test command --- apps/opencs/view/doc/view.cpp | 10 ++++++++++ apps/opencs/view/doc/view.hpp | 2 ++ 2 files changed, 12 insertions(+) diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 7a2296fed..c5792b27a 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -27,6 +27,11 @@ void CSVDoc::View::setupEditMenu() QAction *redo = mDocument->getUndoStack().createRedoAction (this, tr("&Redo")); redo->setShortcuts (QKeySequence::Redo); edit->addAction (redo); + + // test + QAction *test = new QAction (tr ("&Test Command"), this); + connect (test, SIGNAL (triggered()), this, SLOT (test())); + edit->addAction (test); } void CSVDoc::View::setupViewMenu() @@ -87,4 +92,9 @@ void CSVDoc::View::setIndex (int viewIndex, int totalViews) void CSVDoc::View::newView() { mViewManager.addView (mDocument); +} + +void CSVDoc::View::test() +{ + mDocument->getUndoStack().push (new QUndoCommand()); } \ No newline at end of file diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 457cc84fa..2c312715a 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -53,6 +53,8 @@ namespace CSVDoc private slots: void newView(); + + void test(); }; } From d7c63d4c74472e7bffd47531319c94343feb646f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 22 Nov 2012 23:42:17 +0100 Subject: [PATCH 010/378] track document modification state and display it in the top level window title bar --- apps/opencs/model/doc/document.cpp | 20 +++++++++++++++++++- apps/opencs/model/doc/document.hpp | 22 +++++++++++++++++++++- apps/opencs/view/doc/view.cpp | 8 ++++++++ apps/opencs/view/doc/view.hpp | 2 ++ apps/opencs/view/doc/viewmanager.cpp | 15 +++++++++++++++ apps/opencs/view/doc/viewmanager.hpp | 11 ++++++++--- 6 files changed, 73 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 448598a4e..8770b3b90 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -1,9 +1,27 @@ #include "document.hpp" -CSMDoc::Document::Document() {} +CSMDoc::Document::Document() +{ + connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool))); +} QUndoStack& CSMDoc::Document::getUndoStack() { return mUndoStack; +} + +int CSMDoc::Document::getState() const +{ + int state = 0; + + if (!mUndoStack.isClean()) + state |= State_Modified; + + return state; +} + +void CSMDoc::Document::modificationStateChanged (bool clean) +{ + emit stateChanged (getState(), this); } \ No newline at end of file diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 1dea7afc2..0499aaf9f 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -2,11 +2,21 @@ #define CSM_DOC_DOCUMENT_H #include +#include namespace CSMDoc { - class Document + class Document : public QObject { + Q_OBJECT + + public: + + enum State + { + State_Modified = 1 + }; + QUndoStack mUndoStack; // not implemented @@ -18,6 +28,16 @@ namespace CSMDoc Document(); QUndoStack& getUndoStack(); + + int getState() const; + + signals: + + void stateChanged (int state, CSMDoc::Document *document); + + private slots: + + void modificationStateChanged (bool clean); }; } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index c5792b27a..be753e216 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -55,6 +55,9 @@ void CSVDoc::View::updateTitle() stream << "New Document "; + if (mDocument->getState() & CSMDoc::Document::State_Modified) + stream << " *"; + if (mViewTotal>1) stream << " [" << (mViewIndex+1) << "/" << mViewTotal << "]"; @@ -89,6 +92,11 @@ void CSVDoc::View::setIndex (int viewIndex, int totalViews) updateTitle(); } +void CSVDoc::View::updateDocumentState() +{ + updateTitle(); +} + void CSVDoc::View::newView() { mViewManager.addView (mDocument); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 2c312715a..67eafe2d5 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -50,6 +50,8 @@ namespace CSVDoc void setIndex (int viewIndex, int totalViews); + void updateDocumentState(); + private slots: void newView(); diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index f6ac6e43d..ad391dabe 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -4,6 +4,7 @@ #include #include "../../model/doc/documentmanager.hpp" +#include "../../model/doc/document.hpp" #include "view.hpp" @@ -42,6 +43,13 @@ CSVDoc::ViewManager::~ViewManager() CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) { + if (countViews (document)==0) + { + // new document + connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), + this, SLOT (documentStateChanged (int, CSMDoc::Document *))); + } + View *view = new View (*this, document, countViews (document)+1); mViews.push_back (view); @@ -84,4 +92,11 @@ bool CSVDoc::ViewManager::closeRequest (View *view) } return true; +} + +void CSVDoc::ViewManager::documentStateChanged (int state, CSMDoc::Document *document) +{ + for (std::vector::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) + if ((*iter)->getDocument()==document) + (*iter)->updateDocumentState(); } \ No newline at end of file diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp index 765fafbea..1565b7c37 100644 --- a/apps/opencs/view/doc/viewmanager.hpp +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -3,6 +3,8 @@ #include +#include + namespace CSMDoc { class Document; @@ -13,8 +15,10 @@ namespace CSVDoc { class View; - class ViewManager + class ViewManager : public QObject { + Q_OBJECT + CSMDoc::DocumentManager& mDocumentManager; std::vector mViews; std::vector mClosed; @@ -29,7 +33,7 @@ namespace CSVDoc ViewManager (CSMDoc::DocumentManager& documentManager); - ~ViewManager(); + virtual ~ViewManager(); View *addView (CSMDoc::Document *document); ///< The ownership of the returned view is not transferred. @@ -39,8 +43,9 @@ namespace CSVDoc bool closeRequest (View *view); + private slots: - + void documentStateChanged (int state, CSMDoc::Document *document); }; } From 5838929371d37b2f4b8771d3c1a8fb17b3989b06 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 23 Nov 2012 00:36:01 +0100 Subject: [PATCH 011/378] implemented a dummy save function (does not actually save anything) --- apps/opencs/model/doc/document.cpp | 30 ++++++++++++++++++++++++++++ apps/opencs/model/doc/document.hpp | 15 +++++++++++++- apps/opencs/view/doc/view.cpp | 15 ++++++++++++++ apps/opencs/view/doc/view.hpp | 4 ++++ apps/opencs/view/doc/viewmanager.cpp | 2 +- 5 files changed, 64 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 8770b3b90..8788ecb9f 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -4,6 +4,10 @@ CSMDoc::Document::Document() { connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool))); + + // dummy implementation -> remove when proper save is implemented. + mSaveCount = 0; + connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving())); } QUndoStack& CSMDoc::Document::getUndoStack() @@ -18,10 +22,36 @@ int CSMDoc::Document::getState() const if (!mUndoStack.isClean()) state |= State_Modified; + if (mSaveCount) + state |= State_Locked | State_Saving; + return state; } +void CSMDoc::Document::save() +{ + mSaveCount = 1; + mSaveTimer.start (500); +} + +void CSMDoc::Document::abortSave() +{ + mSaveTimer.stop(); +} + void CSMDoc::Document::modificationStateChanged (bool clean) { emit stateChanged (getState(), this); +} + +void CSMDoc::Document::saving() +{ + ++mSaveCount; + + if (mSaveCount>15) + { + mSaveCount = 0; + mSaveTimer.stop(); + mUndoStack.setClean(); + } } \ No newline at end of file diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 0499aaf9f..90589b09c 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace CSMDoc { @@ -14,11 +15,16 @@ namespace CSMDoc enum State { - State_Modified = 1 + State_Modified = 1, + State_Locked = 2, + State_Saving = 4 }; QUndoStack mUndoStack; + int mSaveCount; ///< dummy implementation -> remove when proper save is implemented. + QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented. + // not implemented Document (const Document&); Document& operator= (const Document&); @@ -31,6 +37,10 @@ namespace CSMDoc int getState() const; + void save(); + + void abortSave(); + signals: void stateChanged (int state, CSMDoc::Document *document); @@ -38,6 +48,9 @@ namespace CSMDoc private slots: void modificationStateChanged (bool clean); + + void saving(); + ///< dummy implementation -> remove when proper save is implemented. }; } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index be753e216..59852bef0 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -16,6 +16,15 @@ void CSVDoc::View::closeEvent (QCloseEvent *event) event->ignore(); } +void CSVDoc::View::setupFileMenu() +{ + QMenu *file = menuBar()->addMenu (tr ("&File")); + + QAction *save = new QAction (tr ("&Save"), this); + connect (save, SIGNAL (triggered()), this, SLOT (save())); + file->addAction (save); +} + void CSVDoc::View::setupEditMenu() { QMenu *edit = menuBar()->addMenu (tr ("&Edit")); @@ -45,6 +54,7 @@ void CSVDoc::View::setupViewMenu() void CSVDoc::View::setupUi() { + setupFileMenu(); setupEditMenu(); setupViewMenu(); } @@ -105,4 +115,9 @@ void CSVDoc::View::newView() void CSVDoc::View::test() { mDocument->getUndoStack().push (new QUndoCommand()); +} + +void CSVDoc::View::save() +{ + mDocument->save(); } \ No newline at end of file diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 67eafe2d5..be7d23fd0 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -31,6 +31,8 @@ namespace CSVDoc void closeEvent (QCloseEvent *event); + void setupFileMenu(); + void setupEditMenu(); void setupViewMenu(); @@ -57,6 +59,8 @@ namespace CSVDoc void newView(); void test(); + + void save(); }; } diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index ad391dabe..c7f147660 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -99,4 +99,4 @@ void CSVDoc::ViewManager::documentStateChanged (int state, CSMDoc::Document *doc for (std::vector::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) if ((*iter)->getDocument()==document) (*iter)->updateDocumentState(); -} \ No newline at end of file +} From 931eb08114184442f856239f1f32f1da25ddde6d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 23 Nov 2012 00:51:04 +0100 Subject: [PATCH 012/378] implemented edit locking (used during saves) --- apps/opencs/model/doc/document.cpp | 3 +++ apps/opencs/view/doc/view.cpp | 26 ++++++++++++++++++++------ apps/opencs/view/doc/view.hpp | 7 +++++++ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 8788ecb9f..16af492a9 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -32,11 +32,13 @@ void CSMDoc::Document::save() { mSaveCount = 1; mSaveTimer.start (500); + emit stateChanged (getState(), this); } void CSMDoc::Document::abortSave() { mSaveTimer.stop(); + emit stateChanged (getState(), this); } void CSMDoc::Document::modificationStateChanged (bool clean) @@ -53,5 +55,6 @@ void CSMDoc::Document::saving() mSaveCount = 0; mSaveTimer.stop(); mUndoStack.setClean(); + emit stateChanged (getState(), this); } } \ No newline at end of file diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 59852bef0..85db5e51f 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -23,24 +23,26 @@ void CSVDoc::View::setupFileMenu() QAction *save = new QAction (tr ("&Save"), this); connect (save, SIGNAL (triggered()), this, SLOT (save())); file->addAction (save); + mEditingActions.push_back (save); } void CSVDoc::View::setupEditMenu() { QMenu *edit = menuBar()->addMenu (tr ("&Edit")); - QAction *undo = mDocument->getUndoStack().createUndoAction (this, tr("&Undo")); - undo->setShortcuts (QKeySequence::Undo); - edit->addAction (undo); + mUndo = mDocument->getUndoStack().createUndoAction (this, tr("&Undo")); + mUndo->setShortcuts (QKeySequence::Undo); + edit->addAction (mUndo); - QAction *redo = mDocument->getUndoStack().createRedoAction (this, tr("&Redo")); - redo->setShortcuts (QKeySequence::Redo); - edit->addAction (redo); + mRedo= mDocument->getUndoStack().createRedoAction (this, tr("&Redo")); + mRedo->setShortcuts (QKeySequence::Redo); + edit->addAction (mRedo); // test QAction *test = new QAction (tr ("&Test Command"), this); connect (test, SIGNAL (triggered()), this, SLOT (test())); edit->addAction (test); + mEditingActions.push_back (test); } void CSVDoc::View::setupViewMenu() @@ -74,6 +76,17 @@ void CSVDoc::View::updateTitle() setWindowTitle (stream.str().c_str()); } +void CSVDoc::View::updateActions() +{ + bool editing = !(mDocument->getState() & CSMDoc::Document::State_Locked); + + for (std::vector::iterator iter (mEditingActions.begin()); iter!=mEditingActions.end(); ++iter) + (*iter)->setEnabled (editing); + + mUndo->setEnabled (editing & mDocument->getUndoStack().canUndo()); + mRedo->setEnabled (editing & mDocument->getUndoStack().canRedo()); +} + CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), mViewTotal (totalViews) { @@ -105,6 +118,7 @@ void CSVDoc::View::setIndex (int viewIndex, int totalViews) void CSVDoc::View::updateDocumentState() { updateTitle(); + updateActions(); } void CSVDoc::View::newView() diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index be7d23fd0..bab142959 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -1,6 +1,8 @@ #ifndef CSV_DOC_VIEW_H #define CSV_DOC_VIEW_H +#include + #include class QAction; @@ -22,6 +24,9 @@ namespace CSVDoc CSMDoc::Document *mDocument; int mViewIndex; int mViewTotal; + QAction *mUndo; + QAction *mRedo; + std::vector mEditingActions; // not implemented View (const View&); @@ -41,6 +46,8 @@ namespace CSVDoc void updateTitle(); + void updateActions(); + public: View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews); From 03aacd326395c8548d5091bf5b83b92e6b969d69 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 23 Nov 2012 10:25:34 +0100 Subject: [PATCH 013/378] added progress tracking --- apps/opencs/model/doc/document.cpp | 5 ++++- apps/opencs/model/doc/document.hpp | 5 ++++- apps/opencs/view/doc/view.cpp | 5 +++++ apps/opencs/view/doc/view.hpp | 2 ++ apps/opencs/view/doc/viewmanager.cpp | 11 +++++++++++ apps/opencs/view/doc/viewmanager.hpp | 2 ++ 6 files changed, 28 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 16af492a9..3c2c5cd8a 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -23,7 +23,7 @@ int CSMDoc::Document::getState() const state |= State_Modified; if (mSaveCount) - state |= State_Locked | State_Saving; + state |= State_Locked | State_Saving | State_Progress; return state; } @@ -33,6 +33,7 @@ void CSMDoc::Document::save() mSaveCount = 1; mSaveTimer.start (500); emit stateChanged (getState(), this); + emit progress (1, 16, this); } void CSMDoc::Document::abortSave() @@ -50,6 +51,8 @@ void CSMDoc::Document::saving() { ++mSaveCount; + emit progress (mSaveCount, 16, this); + if (mSaveCount>15) { mSaveCount = 0; diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 90589b09c..b1c9ead86 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -17,7 +17,8 @@ namespace CSMDoc { State_Modified = 1, State_Locked = 2, - State_Saving = 4 + State_Saving = 4, + State_Progress = 8 }; QUndoStack mUndoStack; @@ -45,6 +46,8 @@ namespace CSMDoc void stateChanged (int state, CSMDoc::Document *document); + void progress (int current, int max, CSMDoc::Document *document); + private slots: void modificationStateChanged (bool clean); diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 85db5e51f..8f3efc673 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -121,6 +121,11 @@ void CSVDoc::View::updateDocumentState() updateActions(); } +void CSVDoc::View::updateProgress (int current, int max) +{ + +} + void CSVDoc::View::newView() { mViewManager.addView (mDocument); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index bab142959..ab945188f 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -61,6 +61,8 @@ namespace CSVDoc void updateDocumentState(); + void updateProgress (int current, int max); + private slots: void newView(); diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index c7f147660..752501b19 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -48,6 +48,9 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) // new document connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (documentStateChanged (int, CSMDoc::Document *))); + + connect (document, SIGNAL (progress (int, int, CSMDoc::Document *)), + this, SLOT (progress (int, int, CSMDoc::Document *))); } View *view = new View (*this, document, countViews (document)+1); @@ -80,6 +83,7 @@ bool CSVDoc::ViewManager::closeRequest (View *view) { bool last = countViews (view->getDocument())<=1; + /// \todo check if save is in progress -> warn user about possible data loss /// \todo check if document has not been saved -> return false and start close dialogue mViews.erase (iter); @@ -100,3 +104,10 @@ void CSVDoc::ViewManager::documentStateChanged (int state, CSMDoc::Document *doc if ((*iter)->getDocument()==document) (*iter)->updateDocumentState(); } + +void CSVDoc::ViewManager::progress (int current, int max, CSMDoc::Document *document) +{ + for (std::vector::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) + if ((*iter)->getDocument()==document) + (*iter)->updateProgress (current, max); +} \ No newline at end of file diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp index 1565b7c37..8cfc36f3f 100644 --- a/apps/opencs/view/doc/viewmanager.hpp +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -46,6 +46,8 @@ namespace CSVDoc private slots: void documentStateChanged (int state, CSMDoc::Document *document); + + void progress (int current, int max, CSMDoc::Document *document); }; } From 04158d03b05cc63ba238835821a4125ea0ba8869 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 23 Nov 2012 10:30:14 +0100 Subject: [PATCH 014/378] clean up properly after closing a top level window --- apps/opencs/view/doc/viewmanager.cpp | 5 +---- apps/opencs/view/doc/viewmanager.hpp | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 752501b19..7f94f52a2 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -36,9 +36,6 @@ CSVDoc::ViewManager::~ViewManager() { for (std::vector::iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) delete *iter; - - for (std::vector::iterator iter (mClosed.begin()); iter!=mClosed.end(); ++iter) - delete *iter; } CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) @@ -87,7 +84,7 @@ bool CSVDoc::ViewManager::closeRequest (View *view) /// \todo check if document has not been saved -> return false and start close dialogue mViews.erase (iter); - mClosed.push_back (view); + view->deleteLater(); if (last) mDocumentManager.removeDocument (view->getDocument()); diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp index 8cfc36f3f..bcf6dc59e 100644 --- a/apps/opencs/view/doc/viewmanager.hpp +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -21,7 +21,6 @@ namespace CSVDoc CSMDoc::DocumentManager& mDocumentManager; std::vector mViews; - std::vector mClosed; // not implemented ViewManager (const ViewManager&); From eaa58e0530c3624ee186f5f43ef02f63203354a3 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 23 Nov 2012 10:52:46 +0100 Subject: [PATCH 015/378] preparations for multiple parallel progress-type operations --- apps/opencs/model/doc/document.cpp | 6 +++--- apps/opencs/model/doc/document.hpp | 5 ++--- apps/opencs/view/doc/view.cpp | 11 ++++++----- apps/opencs/view/doc/view.hpp | 3 ++- apps/opencs/view/doc/viewmanager.cpp | 8 ++++---- apps/opencs/view/doc/viewmanager.hpp | 2 +- 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 3c2c5cd8a..c4f830531 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -23,7 +23,7 @@ int CSMDoc::Document::getState() const state |= State_Modified; if (mSaveCount) - state |= State_Locked | State_Saving | State_Progress; + state |= State_Locked | State_Saving; return state; } @@ -33,7 +33,7 @@ void CSMDoc::Document::save() mSaveCount = 1; mSaveTimer.start (500); emit stateChanged (getState(), this); - emit progress (1, 16, this); + emit progress (1, 16, State_Saving, this); } void CSMDoc::Document::abortSave() @@ -51,7 +51,7 @@ void CSMDoc::Document::saving() { ++mSaveCount; - emit progress (mSaveCount, 16, this); + emit progress (mSaveCount, 16, State_Saving, this); if (mSaveCount>15) { diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index b1c9ead86..8553427a1 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -17,8 +17,7 @@ namespace CSMDoc { State_Modified = 1, State_Locked = 2, - State_Saving = 4, - State_Progress = 8 + State_Saving = 4 }; QUndoStack mUndoStack; @@ -46,7 +45,7 @@ namespace CSMDoc void stateChanged (int state, CSMDoc::Document *document); - void progress (int current, int max, CSMDoc::Document *document); + void progress (int current, int max, int type, CSMDoc::Document *document); private slots: diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 8f3efc673..3e9701669 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -20,10 +20,9 @@ void CSVDoc::View::setupFileMenu() { QMenu *file = menuBar()->addMenu (tr ("&File")); - QAction *save = new QAction (tr ("&Save"), this); - connect (save, SIGNAL (triggered()), this, SLOT (save())); - file->addAction (save); - mEditingActions.push_back (save); + mSave = new QAction (tr ("&Save"), this); + connect (mSave, SIGNAL (triggered()), this, SLOT (save())); + file->addAction (mSave); } void CSVDoc::View::setupEditMenu() @@ -85,6 +84,8 @@ void CSVDoc::View::updateActions() mUndo->setEnabled (editing & mDocument->getUndoStack().canUndo()); mRedo->setEnabled (editing & mDocument->getUndoStack().canRedo()); + + mSave->setEnabled (!(mDocument->getState() & CSMDoc::Document::State_Saving)); } CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) @@ -121,7 +122,7 @@ void CSVDoc::View::updateDocumentState() updateActions(); } -void CSVDoc::View::updateProgress (int current, int max) +void CSVDoc::View::updateProgress (int current, int max, int type) { } diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index ab945188f..1836ced8b 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -26,6 +26,7 @@ namespace CSVDoc int mViewTotal; QAction *mUndo; QAction *mRedo; + QAction *mSave; std::vector mEditingActions; // not implemented @@ -61,7 +62,7 @@ namespace CSVDoc void updateDocumentState(); - void updateProgress (int current, int max); + void updateProgress (int current, int max, int type); private slots: diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 7f94f52a2..ed31fdb75 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -46,8 +46,8 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (documentStateChanged (int, CSMDoc::Document *))); - connect (document, SIGNAL (progress (int, int, CSMDoc::Document *)), - this, SLOT (progress (int, int, CSMDoc::Document *))); + connect (document, SIGNAL (progress (int, int, int, CSMDoc::Document *)), + this, SLOT (progress (int, int, int, CSMDoc::Document *))); } View *view = new View (*this, document, countViews (document)+1); @@ -102,9 +102,9 @@ void CSVDoc::ViewManager::documentStateChanged (int state, CSMDoc::Document *doc (*iter)->updateDocumentState(); } -void CSVDoc::ViewManager::progress (int current, int max, CSMDoc::Document *document) +void CSVDoc::ViewManager::progress (int current, int max, int type, CSMDoc::Document *document) { for (std::vector::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) if ((*iter)->getDocument()==document) - (*iter)->updateProgress (current, max); + (*iter)->updateProgress (current, max, type); } \ No newline at end of file diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp index bcf6dc59e..eac490250 100644 --- a/apps/opencs/view/doc/viewmanager.hpp +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -46,7 +46,7 @@ namespace CSVDoc void documentStateChanged (int state, CSMDoc::Document *document); - void progress (int current, int max, CSMDoc::Document *document); + void progress (int current, int max, int type, CSMDoc::Document *document); }; } From 2fc183d595a59ff9d84b67dc32448234ebe4d6ff Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 23 Nov 2012 12:20:35 +0100 Subject: [PATCH 016/378] added operations progress bar --- apps/opencs/CMakeLists.txt | 4 +-- apps/opencs/model/doc/document.cpp | 13 +++++--- apps/opencs/model/doc/document.hpp | 4 +-- apps/opencs/view/doc/operation.cpp | 49 ++++++++++++++++++++++++++++ apps/opencs/view/doc/operation.hpp | 31 ++++++++++++++++++ apps/opencs/view/doc/operations.cpp | 47 ++++++++++++++++++++++++++ apps/opencs/view/doc/operations.hpp | 37 +++++++++++++++++++++ apps/opencs/view/doc/view.cpp | 20 ++++++++++-- apps/opencs/view/doc/view.hpp | 4 ++- apps/opencs/view/doc/viewmanager.cpp | 8 ++--- apps/opencs/view/doc/viewmanager.hpp | 2 +- 11 files changed, 202 insertions(+), 17 deletions(-) create mode 100644 apps/opencs/view/doc/operation.cpp create mode 100644 apps/opencs/view/doc/operation.hpp create mode 100644 apps/opencs/view/doc/operations.cpp create mode 100644 apps/opencs/view/doc/operations.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index d7f113447..66b9f4942 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -4,7 +4,7 @@ set (OPENCS_SRC model/doc/documentmanager.cpp model/doc/document.cpp - view/doc/viewmanager.cpp view/doc/view.cpp + view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp ) set (OPENCS_HDR @@ -12,7 +12,7 @@ set (OPENCS_HDR model/doc/documentmanager.hpp model/doc/document.hpp - view/doc/viewmanager.hpp view/doc/view.hpp + view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp ) set (OPENCS_US diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index c4f830531..473d72375 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -33,13 +33,16 @@ void CSMDoc::Document::save() mSaveCount = 1; mSaveTimer.start (500); emit stateChanged (getState(), this); - emit progress (1, 16, State_Saving, this); + emit progress (1, 16, State_Saving, 1, this); } -void CSMDoc::Document::abortSave() +void CSMDoc::Document::abortOperation (int type) { - mSaveTimer.stop(); - emit stateChanged (getState(), this); + if (type==State_Saving) + { + mSaveTimer.stop(); + emit stateChanged (getState(), this); + } } void CSMDoc::Document::modificationStateChanged (bool clean) @@ -51,7 +54,7 @@ void CSMDoc::Document::saving() { ++mSaveCount; - emit progress (mSaveCount, 16, State_Saving, this); + emit progress (mSaveCount, 16, State_Saving, 1, this); if (mSaveCount>15) { diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 8553427a1..3bed0151d 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -39,13 +39,13 @@ namespace CSMDoc void save(); - void abortSave(); + void abortOperation (int type); signals: void stateChanged (int state, CSMDoc::Document *document); - void progress (int current, int max, int type, CSMDoc::Document *document); + void progress (int current, int max, int type, int threads, CSMDoc::Document *document); private slots: diff --git a/apps/opencs/view/doc/operation.cpp b/apps/opencs/view/doc/operation.cpp new file mode 100644 index 000000000..cd9477557 --- /dev/null +++ b/apps/opencs/view/doc/operation.cpp @@ -0,0 +1,49 @@ + +#include "operation.hpp" + +#include + +#include "../../model/doc/document.hpp" + +void CSVDoc::Operation::updateLabel (int threads) +{ + if (threads==-1 || ((threads==0)!=mStalling)) + { + std::string name ("unknown operation"); + + switch (mType) + { + case CSMDoc::Document::State_Saving: name = "saving"; break; + } + + std::ostringstream stream; + + if ((mStalling = (threads<=0))) + { + stream << name << " (waiting for a free worker thread)"; + } + else + { + stream << name << " (%p%)"; + } + + setFormat (stream.str().c_str()); + } +} + +CSVDoc::Operation::Operation (int type) : mType (type), mStalling (false) +{ + updateLabel(); +} + +void CSVDoc::Operation::setProgress (int current, int max, int threads) +{ + updateLabel (threads); + setRange (0, max); + setValue (current); +} + +int CSVDoc::Operation::getType() const +{ + return mType; +} \ No newline at end of file diff --git a/apps/opencs/view/doc/operation.hpp b/apps/opencs/view/doc/operation.hpp new file mode 100644 index 000000000..362725b6f --- /dev/null +++ b/apps/opencs/view/doc/operation.hpp @@ -0,0 +1,31 @@ +#ifndef CSV_DOC_OPERATION_H +#define CSV_DOC_OPERATION_H + +#include + +namespace CSVDoc +{ + class Operation : public QProgressBar + { + Q_OBJECT + + int mType; + bool mStalling; + + // not implemented + Operation (const Operation&); + Operation& operator= (const Operation&); + + void updateLabel (int threads = -1); + + public: + + Operation (int type); + + void setProgress (int current, int max, int threads); + + int getType() const; + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/doc/operations.cpp b/apps/opencs/view/doc/operations.cpp new file mode 100644 index 000000000..ba444a119 --- /dev/null +++ b/apps/opencs/view/doc/operations.cpp @@ -0,0 +1,47 @@ + +#include "operations.hpp" + +#include + +#include "operation.hpp" + +CSVDoc::Operations::Operations() +{ + /// \todo make widget height fixed (exactly the height required to display all operations) + + setFeatures (QDockWidget::NoDockWidgetFeatures); + + QWidget *widget = new QWidget; + setWidget (widget); + + mLayout = new QVBoxLayout; + + widget->setLayout (mLayout); +} + +void CSVDoc::Operations::setProgress (int current, int max, int type, int threads) +{ + for (std::vector::iterator iter (mOperations.begin()); iter!=mOperations.end(); ++iter) + if ((*iter)->getType()==type) + { + (*iter)->setProgress (current, max, threads); + return; + } + + Operation *operation = new Operation (type); + + mLayout->addWidget (operation); + mOperations.push_back (operation); + operation->setProgress (current, max, threads); +} + +void CSVDoc::Operations::quitOperation (int type) +{ + for (std::vector::iterator iter (mOperations.begin()); iter!=mOperations.end(); ++iter) + if ((*iter)->getType()==type) + { + delete *iter; + mOperations.erase (iter); + break; + } +} \ No newline at end of file diff --git a/apps/opencs/view/doc/operations.hpp b/apps/opencs/view/doc/operations.hpp new file mode 100644 index 000000000..b96677450 --- /dev/null +++ b/apps/opencs/view/doc/operations.hpp @@ -0,0 +1,37 @@ +#ifndef CSV_DOC_OPERATIONS_H +#define CSV_DOC_OPERATIONS_H + +#include + +#include + +class QVBoxLayout; + +namespace CSVDoc +{ + class Operation; + + class Operations : public QDockWidget + { + Q_OBJECT + + QVBoxLayout *mLayout; + std::vector mOperations; + + // not implemented + Operations (const Operations&); + Operations& operator= (const Operations&); + + public: + + Operations(); + + void setProgress (int current, int max, int type, int threads); + ///< Implicitly starts the operation, if it is not running already. + + void quitOperation (int type); + ///< Calling this function for an operation that is not running is a no-op. + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 3e9701669..fe018ab3b 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -9,6 +9,7 @@ #include "../../model/doc/document.hpp" #include "viewmanager.hpp" +#include "operations.hpp" void CSVDoc::View::closeEvent (QCloseEvent *event) { @@ -94,6 +95,9 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to setCentralWidget (new QWidget); resize (200, 200); + mOperations = new Operations; + addDockWidget (Qt::BottomDockWidgetArea, mOperations); + updateTitle(); setupUi(); @@ -120,11 +124,23 @@ void CSVDoc::View::updateDocumentState() { updateTitle(); updateActions(); + + static const int operations[] = + { + CSMDoc::Document::State_Saving, + -1 // end marker + }; + + int state = mDocument->getState() ; + + for (int i=0; operations[i]!=-1; ++i) + if (!(state & operations[i])) + mOperations->quitOperation (operations[i]); } -void CSVDoc::View::updateProgress (int current, int max, int type) +void CSVDoc::View::updateProgress (int current, int max, int type, int threads) { - + mOperations->setProgress (current, max, type, threads); } void CSVDoc::View::newView() diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 1836ced8b..191af36b5 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -15,6 +15,7 @@ namespace CSMDoc namespace CSVDoc { class ViewManager; + class Operations; class View : public QMainWindow { @@ -28,6 +29,7 @@ namespace CSVDoc QAction *mRedo; QAction *mSave; std::vector mEditingActions; + Operations *mOperations; // not implemented View (const View&); @@ -62,7 +64,7 @@ namespace CSVDoc void updateDocumentState(); - void updateProgress (int current, int max, int type); + void updateProgress (int current, int max, int type, int threads); private slots: diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index ed31fdb75..673afbad8 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -46,8 +46,8 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)), this, SLOT (documentStateChanged (int, CSMDoc::Document *))); - connect (document, SIGNAL (progress (int, int, int, CSMDoc::Document *)), - this, SLOT (progress (int, int, int, CSMDoc::Document *))); + connect (document, SIGNAL (progress (int, int, int, int, CSMDoc::Document *)), + this, SLOT (progress (int, int, int, int, CSMDoc::Document *))); } View *view = new View (*this, document, countViews (document)+1); @@ -102,9 +102,9 @@ void CSVDoc::ViewManager::documentStateChanged (int state, CSMDoc::Document *doc (*iter)->updateDocumentState(); } -void CSVDoc::ViewManager::progress (int current, int max, int type, CSMDoc::Document *document) +void CSVDoc::ViewManager::progress (int current, int max, int type, int threads, CSMDoc::Document *document) { for (std::vector::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter) if ((*iter)->getDocument()==document) - (*iter)->updateProgress (current, max, type); + (*iter)->updateProgress (current, max, type, threads); } \ No newline at end of file diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp index eac490250..caf75a0cd 100644 --- a/apps/opencs/view/doc/viewmanager.hpp +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -46,7 +46,7 @@ namespace CSVDoc void documentStateChanged (int state, CSMDoc::Document *document); - void progress (int current, int max, int type, CSMDoc::Document *document); + void progress (int current, int max, int type, int threads, CSMDoc::Document *document); }; } From 997386d8736b036bdb3dd1a7a38e904d4793a755 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 23 Nov 2012 13:15:45 +0100 Subject: [PATCH 017/378] implemented world verify function (doesn't do anything yet; mostly meant as a test for multi-operation interface) --- apps/opencs/model/doc/document.cpp | 34 ++++++++++++++++++++++++++++++ apps/opencs/model/doc/document.hpp | 11 +++++++++- apps/opencs/view/doc/operation.cpp | 3 +++ apps/opencs/view/doc/view.cpp | 18 +++++++++++++++- apps/opencs/view/doc/view.hpp | 5 +++++ 5 files changed, 69 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 473d72375..5c39e16c0 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -8,6 +8,10 @@ CSMDoc::Document::Document() // dummy implementation -> remove when proper save is implemented. mSaveCount = 0; connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving())); + + // dummy implementation -> remove when proper verify is implemented. + mVerifyCount = 0; + connect (&mVerifyTimer, SIGNAL(timeout()), this, SLOT (verifying())); } QUndoStack& CSMDoc::Document::getUndoStack() @@ -25,6 +29,9 @@ int CSMDoc::Document::getState() const if (mSaveCount) state |= State_Locked | State_Saving; + if (mVerifyCount) + state |= State_Locked | State_Verifying; + return state; } @@ -36,6 +43,14 @@ void CSMDoc::Document::save() emit progress (1, 16, State_Saving, 1, this); } +void CSMDoc::Document::verify() +{ + mVerifyCount = 1; + mVerifyTimer.start (500); + emit stateChanged (getState(), this); + emit progress (1, 20, State_Verifying, 1, this); +} + void CSMDoc::Document::abortOperation (int type) { if (type==State_Saving) @@ -43,6 +58,11 @@ void CSMDoc::Document::abortOperation (int type) mSaveTimer.stop(); emit stateChanged (getState(), this); } + else if (type==State_Verifying) + { + mVerifyTimer.stop(); + emit stateChanged (getState(), this); + } } void CSMDoc::Document::modificationStateChanged (bool clean) @@ -63,4 +83,18 @@ void CSMDoc::Document::saving() mUndoStack.setClean(); emit stateChanged (getState(), this); } +} + +void CSMDoc::Document::verifying() +{ + ++mVerifyCount; + + emit progress (mVerifyCount, 20, State_Verifying, 1, this); + + if (mVerifyCount>19) + { + mVerifyCount = 0; + mVerifyTimer.stop(); + emit stateChanged (getState(), this); + } } \ No newline at end of file diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 3bed0151d..e691bb38f 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -17,7 +17,8 @@ namespace CSMDoc { State_Modified = 1, State_Locked = 2, - State_Saving = 4 + State_Saving = 4, + State_Verifying = 8 }; QUndoStack mUndoStack; @@ -25,6 +26,9 @@ namespace CSMDoc int mSaveCount; ///< dummy implementation -> remove when proper save is implemented. QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented. + int mVerifyCount; ///< dummy implementation -> remove when proper verify is implemented. + QTimer mVerifyTimer; ///< dummy implementation -> remove when proper verify is implemented. + // not implemented Document (const Document&); Document& operator= (const Document&); @@ -39,6 +43,8 @@ namespace CSMDoc void save(); + void verify(); + void abortOperation (int type); signals: @@ -53,6 +59,9 @@ namespace CSMDoc void saving(); ///< dummy implementation -> remove when proper save is implemented. + + void verifying(); + ///< dummy implementation -> remove when proper verify is implemented. }; } diff --git a/apps/opencs/view/doc/operation.cpp b/apps/opencs/view/doc/operation.cpp index cd9477557..02a93f9c5 100644 --- a/apps/opencs/view/doc/operation.cpp +++ b/apps/opencs/view/doc/operation.cpp @@ -14,6 +14,7 @@ void CSVDoc::Operation::updateLabel (int threads) switch (mType) { case CSMDoc::Document::State_Saving: name = "saving"; break; + case CSMDoc::Document::State_Verifying: name = "verifying"; break; } std::ostringstream stream; @@ -34,6 +35,8 @@ void CSVDoc::Operation::updateLabel (int threads) CSVDoc::Operation::Operation (int type) : mType (type), mStalling (false) { updateLabel(); + + /// \todo assign different progress bar colours to allow the user to distinguish easily between operation types } void CSVDoc::Operation::setProgress (int current, int max, int threads) diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index fe018ab3b..8ed1f83b8 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -54,11 +54,21 @@ void CSVDoc::View::setupViewMenu() view->addAction (newWindow); } +void CSVDoc::View::setupWorldMenu() +{ + QMenu *world = menuBar()->addMenu (tr ("&World")); + + mVerify = new QAction (tr ("&Verify"), this); + connect (mVerify, SIGNAL (triggered()), this, SLOT (verify())); + world->addAction (mVerify); +} + void CSVDoc::View::setupUi() { setupFileMenu(); setupEditMenu(); setupViewMenu(); + setupWorldMenu(); } void CSVDoc::View::updateTitle() @@ -87,6 +97,7 @@ void CSVDoc::View::updateActions() mRedo->setEnabled (editing & mDocument->getUndoStack().canRedo()); mSave->setEnabled (!(mDocument->getState() & CSMDoc::Document::State_Saving)); + mVerify->setEnabled (!(mDocument->getState() & CSMDoc::Document::State_Verifying)); } CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) @@ -127,7 +138,7 @@ void CSVDoc::View::updateDocumentState() static const int operations[] = { - CSMDoc::Document::State_Saving, + CSMDoc::Document::State_Saving, CSMDoc::Document::State_Verifying, -1 // end marker }; @@ -156,4 +167,9 @@ void CSVDoc::View::test() void CSVDoc::View::save() { mDocument->save(); +} + +void CSVDoc::View::verify() +{ + mDocument->verify(); } \ No newline at end of file diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 191af36b5..15394a7d8 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -28,6 +28,7 @@ namespace CSVDoc QAction *mUndo; QAction *mRedo; QAction *mSave; + QAction *mVerify; std::vector mEditingActions; Operations *mOperations; @@ -45,6 +46,8 @@ namespace CSVDoc void setupViewMenu(); + void setupWorldMenu(); + void setupUi(); void updateTitle(); @@ -73,6 +76,8 @@ namespace CSVDoc void test(); void save(); + + void verify(); }; } From 303506d24b00a0bbcbb7f796fa071b5f750c3be9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 23 Nov 2012 14:05:49 +0100 Subject: [PATCH 018/378] added new document function --- apps/opencs/editor.cpp | 12 ++++++++++-- apps/opencs/editor.hpp | 14 +++++++++++--- apps/opencs/model/doc/document.cpp | 9 ++++++++- apps/opencs/model/doc/document.hpp | 9 ++++++++- apps/opencs/model/doc/documentmanager.cpp | 4 ++-- apps/opencs/model/doc/documentmanager.hpp | 3 ++- apps/opencs/view/doc/view.cpp | 8 ++++++-- apps/opencs/view/doc/view.hpp | 4 ++++ apps/opencs/view/doc/viewmanager.cpp | 2 ++ apps/opencs/view/doc/viewmanager.hpp | 4 ++++ 10 files changed, 57 insertions(+), 12 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 264ae7543..c03a1dc5d 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -1,20 +1,28 @@ #include "editor.hpp" +#include + #include -CS::Editor::Editor() : mViewManager (mDocumentManager) +CS::Editor::Editor() : mViewManager (mDocumentManager), mNewDocumentIndex (0) { + connect (&mViewManager, SIGNAL (newDocumentRequest ()), this, SLOT (createDocument ())); } void CS::Editor::createDocument() { - CSMDoc::Document *document = mDocumentManager.addDocument(); + std::ostringstream stream; + + stream << "NewDocument" << (++mNewDocumentIndex); + + CSMDoc::Document *document = mDocumentManager.addDocument (stream.str()); mViewManager.addView (document); } int CS::Editor::run() { + /// \todo Instead of creating an empty document, open a small welcome dialogue window with buttons for new/load/recent projects createDocument(); return QApplication::exec(); diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 6e183b843..60f7beaf1 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -1,13 +1,19 @@ #ifndef CS_EDITOR_H #define CS_EDITOR_H +#include + #include "model/doc/documentmanager.hpp" #include "view/doc/viewmanager.hpp" namespace CS { - class Editor + class Editor : public QObject { + Q_OBJECT + + int mNewDocumentIndex; ///< \todo remove when the proper new document dialogue is implemented. + CSMDoc::DocumentManager mDocumentManager; CSVDoc::ViewManager mViewManager; @@ -19,10 +25,12 @@ namespace CS Editor(); - void createDocument(); - int run(); ///< \return error status + + public slots: + + void createDocument(); }; } diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 5c39e16c0..cd65e83ae 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -1,8 +1,10 @@ #include "document.hpp" -CSMDoc::Document::Document() +CSMDoc::Document::Document (const std::string& name) { + mName = name; ///< \todo replace with ESX list + connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool))); // dummy implementation -> remove when proper save is implemented. @@ -35,6 +37,11 @@ int CSMDoc::Document::getState() const return state; } +const std::string& CSMDoc::Document::getName() const +{ + return mName; +} + void CSMDoc::Document::save() { mSaveCount = 1; diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index e691bb38f..0249d8e5c 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -1,6 +1,8 @@ #ifndef CSM_DOC_DOCUMENT_H #define CSM_DOC_DOCUMENT_H +#include + #include #include #include @@ -21,6 +23,7 @@ namespace CSMDoc State_Verifying = 8 }; + std::string mName; ///< \todo replace name with ESX list QUndoStack mUndoStack; int mSaveCount; ///< dummy implementation -> remove when proper save is implemented. @@ -35,12 +38,16 @@ namespace CSMDoc public: - Document(); + Document (const std::string& name); + ///< \todo replace name with ESX list QUndoStack& getUndoStack(); int getState() const; + const std::string& getName() const; + ///< \todo replace with ESX list + void save(); void verify(); diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp index 68595f2d4..8ae2764f2 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -14,9 +14,9 @@ CSMDoc::DocumentManager::~DocumentManager() delete *iter; } -CSMDoc::Document *CSMDoc::DocumentManager::addDocument() +CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::string& name) { - Document *document = new Document; + Document *document = new Document (name); mDocuments.push_back (document); diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp index f20f3101d..730c7fae1 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -2,6 +2,7 @@ #define CSM_DOC_DOCUMENTMGR_H #include +#include namespace CSMDoc { @@ -20,7 +21,7 @@ namespace CSMDoc ~DocumentManager(); - Document *addDocument(); + Document *addDocument (const std::string& name); ///< The ownership of the returned document is not transferred to the caller. bool removeDocument (Document *document); diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 8ed1f83b8..5c7199aa4 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -21,6 +21,10 @@ void CSVDoc::View::setupFileMenu() { QMenu *file = menuBar()->addMenu (tr ("&File")); + QAction *new_ = new QAction (tr ("New"), this); + connect (new_, SIGNAL (triggered()), this, SIGNAL (newDocumentRequest())); + file->addAction (new_); + mSave = new QAction (tr ("&Save"), this); connect (mSave, SIGNAL (triggered()), this, SLOT (save())); file->addAction (mSave); @@ -75,7 +79,7 @@ void CSVDoc::View::updateTitle() { std::ostringstream stream; - stream << "New Document "; + stream << mDocument->getName(); if (mDocument->getState() & CSMDoc::Document::State_Modified) stream << " *"; @@ -104,7 +108,7 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), mViewTotal (totalViews) { setCentralWidget (new QWidget); - resize (200, 200); + resize (200, 200); /// \todo get default size from settings and set reasonable minimal size mOperations = new Operations; addDockWidget (Qt::BottomDockWidgetArea, mOperations); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 15394a7d8..d15d942fc 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -69,6 +69,10 @@ namespace CSVDoc void updateProgress (int current, int max, int type, int threads); + signals: + + void newDocumentRequest(); + private slots: void newView(); diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 673afbad8..22847c78b 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -56,6 +56,8 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) view->show(); + connect (view, SIGNAL (newDocumentRequest ()), this, SIGNAL (newDocumentRequest())); + updateIndices(); return view; diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp index caf75a0cd..5e4b1be07 100644 --- a/apps/opencs/view/doc/viewmanager.hpp +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -42,6 +42,10 @@ namespace CSVDoc bool closeRequest (View *view); + signals: + + void newDocumentRequest(); + private slots: void documentStateChanged (int state, CSMDoc::Document *document); From 0af869c8164ebe65a3f32a1407461f5a29d708ce Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Nov 2012 20:22:51 +0100 Subject: [PATCH 019/378] GetReputation --- apps/openmw/mwscript/dialogueextensions.cpp | 18 ++++++++++++++++++ apps/openmw/mwscript/docs/vmformat.txt | 2 ++ 2 files changed, 20 insertions(+) diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index 72ae25724..ec5d49b1f 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -159,6 +159,18 @@ namespace MWScript } }; + template + class OpGetReputation : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + runtime.push (MWWorld::Class::get(ptr).getNpcStats (ptr).getReputation ()); + } + }; const int opcodeJournal = 0x2000133; const int opcodeSetJournalIndex = 0x2000134; @@ -172,6 +184,8 @@ namespace MWScript const int opcodeModReputation = 0x20001ae; const int opcodeSetReputationExplicit = 0x20001af; const int opcodeModReputationExplicit = 0x20001b0; + const int opcodeGetReputation = 0x20001b1; + const int opcodeGetReputationExplicit = 0x20001b2; void registerExtensions (Compiler::Extensions& extensions) { @@ -188,6 +202,8 @@ namespace MWScript opcodeSetReputationExplicit); extensions.registerInstruction("modreputation", "l", opcodeModReputation, opcodeModReputationExplicit); + extensions.registerFunction("getreputation", 'l', "", opcodeGetReputation, + opcodeGetReputationExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -200,10 +216,12 @@ namespace MWScript interpreter.installSegment5 (opcodeForceGreeting, new OpForceGreeting); interpreter.installSegment5 (opcodeForceGreetingExplicit, new OpForceGreeting); interpreter.installSegment5 (opcodeGoodbye, new OpGoodbye); + interpreter.installSegment5 (opcodeGetReputation, new OpGetReputation); interpreter.installSegment5 (opcodeSetReputation, new OpSetReputation); interpreter.installSegment5 (opcodeModReputation, new OpModReputation); interpreter.installSegment5 (opcodeSetReputationExplicit, new OpSetReputation); interpreter.installSegment5 (opcodeModReputationExplicit, new OpModReputation); + interpreter.installSegment5 (opcodeGetReputationExplicit, new OpGetReputation); } } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index b130aa954..fcfb33d21 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -226,5 +226,7 @@ op 0x20001ad: SetReputation op 0x20001ae: ModReputation op 0x20001af: SetReputation, explicit op 0x20001b0: ModReputation, explicit +op 0x20001b1: GetReputation +op 0x20001b2: GetReputation, explicit opcodes 0x20001b1-0x3ffffff unused From 064cb80c0ac64a229df53c39ba09d4873b98234e Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Nov 2012 20:48:53 +0100 Subject: [PATCH 020/378] fix wait dialog fading --- apps/openmw/mwrender/renderingmanager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 5f1f1ad43..8cb8e9fa8 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -332,6 +332,8 @@ void RenderingManager::update (float duration, bool paused) } mOcclusionQuery->update(duration); + mRendering.update(duration); + if(paused) { Ogre::ControllerManager::getSingleton().setTimeFactor(0.f); @@ -350,8 +352,6 @@ void RenderingManager::update (float duration, bool paused) mSkyManager->setGlare(mOcclusionQuery->getSunVisibility()); - mRendering.update(duration); - MWWorld::RefData &data = MWBase::Environment::get() .getWorld() From d54ed557bfe6bfa9be8b7397192658c615f09a2b Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Nov 2012 20:48:59 +0100 Subject: [PATCH 021/378] Equip --- apps/openmw/mwscript/containerextensions.cpp | 34 ++++++++++++++++++++ apps/openmw/mwscript/docs/vmformat.txt | 4 ++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 5e9746b2f..dd4c62a69 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -14,6 +14,8 @@ #include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" +#include "../mwworld/actionequip.hpp" +#include "../mwworld/inventorystore.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -128,12 +130,41 @@ namespace MWScript } }; + template + class OpEquip : public Interpreter::Opcode0 + { + public: + + virtual void execute(Interpreter::Runtime &runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string item = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore (ptr); + MWWorld::ContainerStoreIterator it = invStore.begin(); + for (; it != invStore.end(); ++it) + { + if (toLower(it->getCellRef().mRefID) == toLower(item)) + break; + } + if (it == invStore.end()) + throw std::runtime_error("Item to equip not found"); + + MWWorld::ActionEquip action (*it); + action.execute(ptr); + } + }; + const int opcodeAddItem = 0x2000076; const int opcodeAddItemExplicit = 0x2000077; const int opcodeGetItemCount = 0x2000078; const int opcodeGetItemCountExplicit = 0x2000079; const int opcodeRemoveItem = 0x200007a; const int opcodeRemoveItemExplicit = 0x200007b; + const int opcodeEquip = 0x20001b3; + const int opcodeEquipExplicit = 0x20001b4; void registerExtensions (Compiler::Extensions& extensions) { @@ -142,6 +173,7 @@ namespace MWScript opcodeGetItemCountExplicit); extensions.registerInstruction ("removeitem", "cl", opcodeRemoveItem, opcodeRemoveItemExplicit); + extensions.registerInstruction ("equip", "c", opcodeEquip, opcodeEquipExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -152,6 +184,8 @@ namespace MWScript interpreter.installSegment5 (opcodeGetItemCountExplicit, new OpGetItemCount); interpreter.installSegment5 (opcodeRemoveItem, new OpRemoveItem); interpreter.installSegment5 (opcodeRemoveItemExplicit, new OpRemoveItem); + interpreter.installSegment5 (opcodeEquip, new OpEquip); + interpreter.installSegment5 (opcodeEquipExplicit, new OpEquip); } } } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index fcfb33d21..eda08cae0 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -228,5 +228,7 @@ op 0x20001af: SetReputation, explicit op 0x20001b0: ModReputation, explicit op 0x20001b1: GetReputation op 0x20001b2: GetReputation, explicit -opcodes 0x20001b1-0x3ffffff unused +op 0x20001b3: Equip +op 0x20001b4: Equip, explicit +opcodes 0x20001b5-0x3ffffff unused From 9c170af30cd3b15051705af405d5490d268f250a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Nov 2012 20:57:08 +0100 Subject: [PATCH 022/378] SameFaction --- apps/openmw/mwscript/dialogueextensions.cpp | 22 +++++++++++++++++++++ apps/openmw/mwscript/docs/vmformat.txt | 4 +++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index ec5d49b1f..f63623275 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -12,6 +12,7 @@ #include "../mwbase/journal.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" #include "../mwmechanics/npcstats.hpp" #include "interpretercontext.hpp" @@ -172,6 +173,21 @@ namespace MWScript } }; + template + class OpSameFaction : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); + + runtime.push (MWWorld::Class::get(ptr).getNpcStats (ptr).isSameFaction (MWWorld::Class::get(player).getNpcStats (player))); + } + }; + const int opcodeJournal = 0x2000133; const int opcodeSetJournalIndex = 0x2000134; const int opcodeGetJournalIndex = 0x2000135; @@ -186,6 +202,8 @@ namespace MWScript const int opcodeModReputationExplicit = 0x20001b0; const int opcodeGetReputation = 0x20001b1; const int opcodeGetReputationExplicit = 0x20001b2; + const int opcodeSameFaction = 0x20001b5; + const int opcodeSameFactionExplicit = 0x20001b6; void registerExtensions (Compiler::Extensions& extensions) { @@ -204,6 +222,8 @@ namespace MWScript opcodeModReputationExplicit); extensions.registerFunction("getreputation", 'l', "", opcodeGetReputation, opcodeGetReputationExplicit); + extensions.registerFunction("samefaction", 'l', "", opcodeSameFaction, + opcodeSameFactionExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -222,6 +242,8 @@ namespace MWScript interpreter.installSegment5 (opcodeSetReputationExplicit, new OpSetReputation); interpreter.installSegment5 (opcodeModReputationExplicit, new OpModReputation); interpreter.installSegment5 (opcodeGetReputationExplicit, new OpGetReputation); + interpreter.installSegment5 (opcodeSameFaction, new OpSameFaction); + interpreter.installSegment5 (opcodeSameFactionExplicit, new OpSameFaction); } } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index eda08cae0..f45b75bb1 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -230,5 +230,7 @@ op 0x20001b1: GetReputation op 0x20001b2: GetReputation, explicit op 0x20001b3: Equip op 0x20001b4: Equip, explicit -opcodes 0x20001b5-0x3ffffff unused +op 0x20001b5: SameFaction +op 0x20001b6: SameFaction, explicit +opcodes 0x20001b7-0x3ffffff unused From d418e2137190d049ff90cdb5503bbe092a9ede81 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Nov 2012 21:14:32 +0100 Subject: [PATCH 023/378] --- apps/openmw/mwscript/aiextensions.cpp | 103 ++++++++++++++++--------- apps/openmw/mwscript/docs/vmformat.txt | 18 ++++- 2 files changed, 82 insertions(+), 39 deletions(-) diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 787962ad1..aa1d2d030 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -114,71 +114,56 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; i - class OpSetHello : public Interpreter::Opcode0 + class OpGetAiSetting : public Interpreter::Opcode0 { + int mIndex; public: + OpGetAiSetting(int index) : mIndex(index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; - runtime.pop(); - - MWWorld::Class::get (ptr).getCreatureStats (ptr).setAiSetting (0, value); + runtime.push(MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSetting (mIndex)); } }; - template - class OpSetFight : public Interpreter::Opcode0 + class OpModAiSetting : public Interpreter::Opcode0 { + int mIndex; public: + OpModAiSetting(int index) : mIndex(index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWWorld::Class::get (ptr).getCreatureStats (ptr).setAiSetting (1, value); + MWWorld::Class::get (ptr).getCreatureStats (ptr).setAiSetting (mIndex, + MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSetting (mIndex) + value); } }; - template - class OpSetFlee : public Interpreter::Opcode0 + class OpSetAiSetting : public Interpreter::Opcode0 { + int mIndex; public: + OpSetAiSetting(int index) : mIndex(index) {} virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); - - Interpreter::Type_Integer value = runtime[0].mInteger; - runtime.pop(); - - MWWorld::Class::get (ptr).getCreatureStats (ptr).setAiSetting (2, value); - } - }; - - template - class OpSetAlarm : public Interpreter::Opcode0 - { - public: - - virtual void execute (Interpreter::Runtime& runtime) - { - MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - MWWorld::Class::get (ptr).getCreatureStats (ptr).setAiSetting (3, value); + MWWorld::Class::get (ptr).getCreatureStats (ptr).setAiSetting (mIndex, + value); } }; @@ -199,6 +184,22 @@ namespace MWScript const int opcodeSetFleeExplicit = 0x2000161; const int opcodeSetAlarm = 0x2000162; const int opcodeSetAlarmExplicit = 0x2000163; + const int opcodeModHello = 0x20001b7; + const int opcodeModHelloExplicit = 0x20001b8; + const int opcodeModFight = 0x20001b9; + const int opcodeModFightExplicit = 0x20001ba; + const int opcodeModFlee = 0x20001bb; + const int opcodeModFleeExplicit = 0x20001bc; + const int opcodeModAlarm = 0x20001bd; + const int opcodeModAlarmExplicit = 0x20001be; + const int opcodeGetHello = 0x20001bf; + const int opcodeGetHelloExplicit = 0x20001c0; + const int opcodeGetFight = 0x20001c1; + const int opcodeGetFightExplicit = 0x20001c2; + const int opcodeGetFlee = 0x20001c3; + const int opcodeGetFleeExplicit = 0x20001c4; + const int opcodeGetAlarm = 0x20001c5; + const int opcodeGetAlarmExplicit = 0x20001c6; void registerExtensions (Compiler::Extensions& extensions) { @@ -216,6 +217,14 @@ namespace MWScript extensions.registerInstruction ("setfight", "l", opcodeSetFight, opcodeSetFightExplicit); extensions.registerInstruction ("setflee", "l", opcodeSetFlee, opcodeSetFleeExplicit); extensions.registerInstruction ("setalarm", "l", opcodeSetAlarm, opcodeSetAlarmExplicit); + extensions.registerInstruction ("modhello", "l", opcodeModHello, opcodeModHelloExplicit); + extensions.registerInstruction ("modfight", "l", opcodeModFight, opcodeModFightExplicit); + extensions.registerInstruction ("modflee", "l", opcodeModFlee, opcodeModFleeExplicit); + extensions.registerInstruction ("modalarm", "l", opcodeModAlarm, opcodeModAlarmExplicit); + extensions.registerFunction ("gethello", 'l', "", opcodeGetHello, opcodeGetHelloExplicit); + extensions.registerFunction ("getfight", 'l', "", opcodeGetFight, opcodeGetFightExplicit); + extensions.registerFunction ("getflee", 'l', "", opcodeGetFlee, opcodeGetFleeExplicit); + extensions.registerFunction ("getalarm", 'l', "", opcodeGetAlarm, opcodeGetAlarmExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -229,14 +238,32 @@ namespace MWScript interpreter.installSegment5 (opcodeGetAiPackageDone, new OpGetAiPackageDone); interpreter.installSegment5 (opcodeGetAiPackageDoneExplicit, new OpGetAiPackageDone); - interpreter.installSegment5 (opcodeSetHello, new OpSetHello); - interpreter.installSegment5 (opcodeSetHelloExplicit, new OpSetHello); - interpreter.installSegment5 (opcodeSetFight, new OpSetFight); - interpreter.installSegment5 (opcodeSetFightExplicit, new OpSetFight); - interpreter.installSegment5 (opcodeSetFlee, new OpSetFlee); - interpreter.installSegment5 (opcodeSetFleeExplicit, new OpSetFlee); - interpreter.installSegment5 (opcodeSetAlarm, new OpSetAlarm); - interpreter.installSegment5 (opcodeSetAlarmExplicit, new OpSetAlarm); + interpreter.installSegment5 (opcodeSetHello, new OpSetAiSetting(0)); + interpreter.installSegment5 (opcodeSetHelloExplicit, new OpSetAiSetting(0)); + interpreter.installSegment5 (opcodeSetFight, new OpSetAiSetting(1)); + interpreter.installSegment5 (opcodeSetFightExplicit, new OpSetAiSetting(1)); + interpreter.installSegment5 (opcodeSetFlee, new OpSetAiSetting(2)); + interpreter.installSegment5 (opcodeSetFleeExplicit, new OpSetAiSetting(2)); + interpreter.installSegment5 (opcodeSetAlarm, new OpSetAiSetting(3)); + interpreter.installSegment5 (opcodeSetAlarmExplicit, new OpSetAiSetting(3)); + + interpreter.installSegment5 (opcodeModHello, new OpModAiSetting(0)); + interpreter.installSegment5 (opcodeModHelloExplicit, new OpModAiSetting(0)); + interpreter.installSegment5 (opcodeModFight, new OpModAiSetting(1)); + interpreter.installSegment5 (opcodeModFightExplicit, new OpModAiSetting(1)); + interpreter.installSegment5 (opcodeModFlee, new OpModAiSetting(2)); + interpreter.installSegment5 (opcodeModFleeExplicit, new OpModAiSetting(2)); + interpreter.installSegment5 (opcodeModAlarm, new OpModAiSetting(3)); + interpreter.installSegment5 (opcodeModAlarmExplicit, new OpModAiSetting(3)); + + interpreter.installSegment5 (opcodeGetHello, new OpGetAiSetting(0)); + interpreter.installSegment5 (opcodeGetHelloExplicit, new OpGetAiSetting(0)); + interpreter.installSegment5 (opcodeGetFight, new OpGetAiSetting(1)); + interpreter.installSegment5 (opcodeGetFightExplicit, new OpGetAiSetting(1)); + interpreter.installSegment5 (opcodeGetFlee, new OpGetAiSetting(2)); + interpreter.installSegment5 (opcodeGetFleeExplicit, new OpGetAiSetting(2)); + interpreter.installSegment5 (opcodeGetAlarm, new OpGetAiSetting(3)); + interpreter.installSegment5 (opcodeGetAlarmExplicit, new OpGetAiSetting(3)); } } } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index f45b75bb1..541cffcf4 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -232,5 +232,21 @@ op 0x20001b3: Equip op 0x20001b4: Equip, explicit op 0x20001b5: SameFaction op 0x20001b6: SameFaction, explicit -opcodes 0x20001b7-0x3ffffff unused +op 0x20001b7: ModHello +op 0x20001b8: ModHello, explicit reference +op 0x20001b9: ModFight +op 0x20001ba: ModFight, explicit reference +op 0x20001bb: ModFlee +op 0x20001bc: ModFlee, explicit reference +op 0x20001bd: ModAlarm +op 0x20001be: ModAlarm, explicit reference +op 0x20001bf: GetHello +op 0x20001c0: GetHello, explicit reference +op 0x20001c1: GetFight +op 0x20001c2: GetFight, explicit reference +op 0x20001c3: GetFlee +op 0x20001c4: GetFlee, explicit reference +op 0x20001c5: GetAlarm +op 0x20001c6: GetAlarm, explicit reference +opcodes 0x20001c7-0x3ffffff unused From 10329c780d9f4b3278e69cf7e4d59b107476e84b Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 23 Nov 2012 21:31:10 +0100 Subject: [PATCH 024/378] GetLocked --- apps/openmw/mwscript/docs/vmformat.txt | 2 ++ apps/openmw/mwscript/miscextensions.cpp | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 541cffcf4..d6aafc8d8 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -248,5 +248,7 @@ op 0x20001c3: GetFlee op 0x20001c4: GetFlee, explicit reference op 0x20001c5: GetAlarm op 0x20001c6: GetAlarm, explicit reference +op 0x20001c7: GetLocked +op 0x20001c8: GetLocked, explicit reference opcodes 0x20001c7-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index c09f0c860..11637c65b 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -272,6 +272,19 @@ namespace MWScript }; bool OpToggleVanityMode::sActivate = true; + template + class OpGetLocked : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + runtime.push (ptr.getCellRef ().mLockLevel > 0); + } + }; + const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeActivate = 0x2000075; @@ -291,6 +304,8 @@ namespace MWScript const int opcodeToggleVanityMode = 0x2000174; const int opcodeGetPcSleep = 0x200019f; const int opcodeWakeUpPc = 0x20001a2; + const int opcodeGetLocked = 0x20001c7; + const int opcodeGetLockedExplicit = 0x20001c8; void registerExtensions (Compiler::Extensions& extensions) { @@ -317,6 +332,7 @@ namespace MWScript extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode); extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep); extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc); + extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -340,6 +356,8 @@ namespace MWScript interpreter.installSegment5 (opcodeToggleVanityMode, new OpToggleVanityMode); interpreter.installSegment5 (opcodeGetPcSleep, new OpGetPcSleep); interpreter.installSegment5 (opcodeWakeUpPc, new OpWakeUpPc); + interpreter.installSegment5 (opcodeGetLocked, new OpGetLocked); + interpreter.installSegment5 (opcodeGetLockedExplicit, new OpGetLocked); } } } From e8ef4dba1ef68e25a656646e952838da0930194c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 24 Nov 2012 02:02:49 +0100 Subject: [PATCH 025/378] getPcRunning, getPcSneaking, getForceRun, getForceSneak --- apps/openmw/mwscript/docs/vmformat.txt | 8 ++- apps/openmw/mwscript/miscextensions.cpp | 71 +++++++++++++++++++++++++ apps/openmw/mwworld/player.hpp | 1 + 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index d6aafc8d8..11405cbcb 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -250,5 +250,11 @@ op 0x20001c5: GetAlarm op 0x20001c6: GetAlarm, explicit reference op 0x20001c7: GetLocked op 0x20001c8: GetLocked, explicit reference -opcodes 0x20001c7-0x3ffffff unused +op 0x20001c9: GetPcRunning +op 0x20001ca: GetPcSneaking +op 0x20001cb: GetForceRun +op 0x20001cc: GetForceSneak +op 0x20001cd: GetForceRun, explicit +op 0x20001ce: GetForceSneak, explicit +opcodes 0x20001cd-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 11637c65b..60451c03f 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -13,6 +13,9 @@ #include "../mwbase/windowmanager.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/player.hpp" + +#include "../mwmechanics/npcstats.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -285,6 +288,58 @@ namespace MWScript } }; + template + class OpGetForceRun : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + MWMechanics::NpcStats& npcStats = MWWorld::Class::get(ptr).getNpcStats (ptr); + + runtime.push (npcStats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)); + } + }; + + template + class OpGetForceSneak : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + MWMechanics::NpcStats& npcStats = MWWorld::Class::get(ptr).getNpcStats (ptr); + + runtime.push (npcStats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); + } + }; + + class OpGetPcRunning : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); + runtime.push (MWWorld::Class::get(ptr).getStance (ptr, MWWorld::Class::Run)); + } + }; + + class OpGetPcSneaking : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); + runtime.push (MWWorld::Class::get(ptr).getStance (ptr, MWWorld::Class::Sneak)); + } + }; + const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeActivate = 0x2000075; @@ -306,6 +361,12 @@ namespace MWScript const int opcodeWakeUpPc = 0x20001a2; const int opcodeGetLocked = 0x20001c7; const int opcodeGetLockedExplicit = 0x20001c8; + const int opcodeGetPcRunning = 0x20001c9; + const int opcodeGetPcSneaking = 0x20001ca; + const int opcodeGetForceRun = 0x20001cb; + const int opcodeGetForceSneak = 0x20001cc; + const int opcodeGetForceRunExplicit = 0x20001cd; + const int opcodeGetForceSneakExplicit = 0x20001ce; void registerExtensions (Compiler::Extensions& extensions) { @@ -333,6 +394,10 @@ namespace MWScript extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep); extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc); extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); + extensions.registerFunction ("getpcrunning", 'l', "", opcodeGetPcRunning); + extensions.registerFunction ("getpcsneaking", 'l', "", opcodeGetPcSneaking); + extensions.registerFunction ("getforcerun", 'l', "", opcodeGetForceRun, opcodeGetForceRunExplicit); + extensions.registerFunction ("getforcesneak", 'l', "", opcodeGetForceSneak, opcodeGetForceSneakExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -358,6 +423,12 @@ namespace MWScript interpreter.installSegment5 (opcodeWakeUpPc, new OpWakeUpPc); interpreter.installSegment5 (opcodeGetLocked, new OpGetLocked); interpreter.installSegment5 (opcodeGetLockedExplicit, new OpGetLocked); + interpreter.installSegment5 (opcodeGetPcRunning, new OpGetPcRunning); + interpreter.installSegment5 (opcodeGetPcSneaking, new OpGetPcSneaking); + interpreter.installSegment5 (opcodeGetForceRun, new OpGetForceRun); + interpreter.installSegment5 (opcodeGetForceRunExplicit, new OpGetForceRun); + interpreter.installSegment5 (opcodeGetForceSneak, new OpGetForceSneak); + interpreter.installSegment5 (opcodeGetForceSneakExplicit, new OpGetForceSneak); } } } diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 1c1ef76cf..eab6d6b8d 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -66,6 +66,7 @@ namespace MWWorld void setUpDown(int value); void toggleRunning(); + void getRunning(); }; } #endif From d7811624d53b49ce7b018ccb0e2f4b39c08c1c2d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 24 Nov 2012 02:15:55 +0100 Subject: [PATCH 026/378] GetEffect --- apps/openmw/mwscript/docs/vmformat.txt | 4 +++- apps/openmw/mwscript/miscextensions.cpp | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 11405cbcb..365779a9a 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -256,5 +256,7 @@ op 0x20001cb: GetForceRun op 0x20001cc: GetForceSneak op 0x20001cd: GetForceRun, explicit op 0x20001ce: GetForceSneak, explicit -opcodes 0x20001cd-0x3ffffff unused +op 0x20001cf: GetEffect +op 0x20001d0: GetEffect, explicit +opcodes 0x20001d1-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 60451c03f..5a342026e 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -16,6 +16,7 @@ #include "../mwworld/player.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/creaturestats.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -340,6 +341,23 @@ namespace MWScript } }; + template + class OpGetEffect : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + int key = runtime[0].mInteger; + runtime.pop(); + + runtime.push (MWWorld::Class::get(ptr).getCreatureStats (ptr).getMagicEffects ().get ( + MWMechanics::EffectKey(key)).mMagnitude > 0); + } + }; + const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeActivate = 0x2000075; @@ -367,6 +385,8 @@ namespace MWScript const int opcodeGetForceSneak = 0x20001cc; const int opcodeGetForceRunExplicit = 0x20001cd; const int opcodeGetForceSneakExplicit = 0x20001ce; + const int opcodeGetEffect = 0x20001cf; + const int opcodeGetEffectExplicit = 0x20001d0; void registerExtensions (Compiler::Extensions& extensions) { @@ -398,6 +418,7 @@ namespace MWScript extensions.registerFunction ("getpcsneaking", 'l', "", opcodeGetPcSneaking); extensions.registerFunction ("getforcerun", 'l', "", opcodeGetForceRun, opcodeGetForceRunExplicit); extensions.registerFunction ("getforcesneak", 'l', "", opcodeGetForceSneak, opcodeGetForceSneakExplicit); + extensions.registerFunction ("geteffect", 'l', "l", opcodeGetEffect, opcodeGetEffectExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -429,6 +450,8 @@ namespace MWScript interpreter.installSegment5 (opcodeGetForceRunExplicit, new OpGetForceRun); interpreter.installSegment5 (opcodeGetForceSneak, new OpGetForceSneak); interpreter.installSegment5 (opcodeGetForceSneakExplicit, new OpGetForceSneak); + interpreter.installSegment5 (opcodeGetEffect, new OpGetEffect); + interpreter.installSegment5 (opcodeGetEffectExplicit, new OpGetEffect); } } } From a596d23203149d5468423e0f6f095f4ebf8a99ec Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 24 Nov 2012 02:38:10 +0100 Subject: [PATCH 027/378] GetArmorType --- apps/openmw/mwscript/containerextensions.cpp | 79 ++++++++++++++++++++ apps/openmw/mwscript/docs/vmformat.txt | 4 +- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index dd4c62a69..c5566c92b 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -157,6 +157,79 @@ namespace MWScript } }; + template + class OpGetArmorType : public Interpreter::Opcode0 + { + public: + + virtual void execute(Interpreter::Runtime &runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Integer location = runtime[0].mInteger; + runtime.pop(); + + int slot; + switch (location) + { + case 0: + slot = MWWorld::InventoryStore::Slot_Helmet; + break; + case 1: + slot = MWWorld::InventoryStore::Slot_Cuirass; + break; + case 2: + slot = MWWorld::InventoryStore::Slot_LeftPauldron; + break; + case 3: + slot = MWWorld::InventoryStore::Slot_RightPauldron; + break; + case 4: + slot = MWWorld::InventoryStore::Slot_Greaves; + break; + case 5: + slot = MWWorld::InventoryStore::Slot_Boots; + break; + case 6: + slot = MWWorld::InventoryStore::Slot_LeftGauntlet; + break; + case 7: + slot = MWWorld::InventoryStore::Slot_RightGauntlet; + break; + case 8: + slot = MWWorld::InventoryStore::Slot_CarriedLeft; // shield + break; + case 9: + slot = MWWorld::InventoryStore::Slot_LeftGauntlet; + break; + case 10: + slot = MWWorld::InventoryStore::Slot_RightGauntlet; + break; + default: + throw std::runtime_error ("armor index out of range"); + } + + MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore (ptr); + + MWWorld::ContainerStoreIterator it = invStore.getSlot (slot); + if (it == invStore.end()) + { + runtime.push(-1); + return; + } + + int skill = MWWorld::Class::get(*it).getEquipmentSkill (*it) ; + if (skill == ESM::Skill::HeavyArmor) + runtime.push(2); + else if (skill == ESM::Skill::MediumArmor) + runtime.push(1); + else if (skill == ESM::Skill::LightArmor) + runtime.push(0); + else + runtime.push(-1); + } + }; + const int opcodeAddItem = 0x2000076; const int opcodeAddItemExplicit = 0x2000077; const int opcodeGetItemCount = 0x2000078; @@ -165,6 +238,8 @@ namespace MWScript const int opcodeRemoveItemExplicit = 0x200007b; const int opcodeEquip = 0x20001b3; const int opcodeEquipExplicit = 0x20001b4; + const int opcodeGetArmorType = 0x20001d1; + const int opcodeGetArmorTypeExplicit = 0x20001d2; void registerExtensions (Compiler::Extensions& extensions) { @@ -174,6 +249,7 @@ namespace MWScript extensions.registerInstruction ("removeitem", "cl", opcodeRemoveItem, opcodeRemoveItemExplicit); extensions.registerInstruction ("equip", "c", opcodeEquip, opcodeEquipExplicit); + extensions.registerFunction ("getarmortype", 'l', "l", opcodeGetArmorType, opcodeGetArmorTypeExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -186,6 +262,9 @@ namespace MWScript interpreter.installSegment5 (opcodeRemoveItemExplicit, new OpRemoveItem); interpreter.installSegment5 (opcodeEquip, new OpEquip); interpreter.installSegment5 (opcodeEquipExplicit, new OpEquip); + interpreter.installSegment5 (opcodeGetArmorType, new OpGetArmorType); + interpreter.installSegment5 (opcodeGetArmorTypeExplicit, new OpGetArmorType); + } } } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 365779a9a..894fd5f05 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -258,5 +258,7 @@ op 0x20001cd: GetForceRun, explicit op 0x20001ce: GetForceSneak, explicit op 0x20001cf: GetEffect op 0x20001d0: GetEffect, explicit -opcodes 0x20001d1-0x3ffffff unused +op 0x20001d1: GetArmorType +op 0x20001d2: GetArmorType, explicit +opcodes 0x20001d3-0x3ffffff unused From 50baf6dac73ee16a76ea6e87c7387adeadda51ae Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 24 Nov 2012 02:45:25 +0100 Subject: [PATCH 028/378] moved to controlextensions --- apps/openmw/mwscript/controlextensions.cpp | 68 ++++++++++++++++++++++ apps/openmw/mwscript/miscextensions.cpp | 68 ---------------------- 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index 0096c69ab..8d65dfdd5 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -106,6 +106,58 @@ namespace MWScript } }; + template + class OpGetForceRun : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + MWMechanics::NpcStats& npcStats = MWWorld::Class::get(ptr).getNpcStats (ptr); + + runtime.push (npcStats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)); + } + }; + + template + class OpGetForceSneak : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + MWMechanics::NpcStats& npcStats = MWWorld::Class::get(ptr).getNpcStats (ptr); + + runtime.push (npcStats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); + } + }; + + class OpGetPcRunning : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); + runtime.push (MWWorld::Class::get(ptr).getStance (ptr, MWWorld::Class::Run)); + } + }; + + class OpGetPcSneaking : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); + runtime.push (MWWorld::Class::get(ptr).getStance (ptr, MWWorld::Class::Sneak)); + } + }; + const int numberOfControls = 7; const int opcodeEnable = 0x200007e; @@ -120,6 +172,12 @@ namespace MWScript const int opcodeForceSneak = 0x200015a; const int opcodeForceSneakExplicit = 0x200015b; const int opcodeGetDisabled = 0x2000175; + const int opcodeGetPcRunning = 0x20001c9; + const int opcodeGetPcSneaking = 0x20001ca; + const int opcodeGetForceRun = 0x20001cb; + const int opcodeGetForceSneak = 0x20001cc; + const int opcodeGetForceRunExplicit = 0x20001cd; + const int opcodeGetForceSneakExplicit = 0x20001ce; const char *controls[numberOfControls] = { @@ -151,6 +209,10 @@ namespace MWScript opcodeClearForceSneakExplicit); extensions.registerInstruction ("forcesneak", "", opcodeForceSneak, opcodeForceSneakExplicit); + extensions.registerFunction ("getpcrunning", 'l', "", opcodeGetPcRunning); + extensions.registerFunction ("getpcsneaking", 'l', "", opcodeGetPcSneaking); + extensions.registerFunction ("getforcerun", 'l', "", opcodeGetForceRun, opcodeGetForceRunExplicit); + extensions.registerFunction ("getforcesneak", 'l', "", opcodeGetForceSneak, opcodeGetForceSneakExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -181,6 +243,12 @@ namespace MWScript new OpClearMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); interpreter.installSegment5 (opcodeForceSneakExplicit, new OpSetMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); + interpreter.installSegment5 (opcodeGetPcRunning, new OpGetPcRunning); + interpreter.installSegment5 (opcodeGetPcSneaking, new OpGetPcSneaking); + interpreter.installSegment5 (opcodeGetForceRun, new OpGetForceRun); + interpreter.installSegment5 (opcodeGetForceRunExplicit, new OpGetForceRun); + interpreter.installSegment5 (opcodeGetForceSneak, new OpGetForceSneak); + interpreter.installSegment5 (opcodeGetForceSneakExplicit, new OpGetForceSneak); } } } diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 5a342026e..85e6ab494 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -289,58 +289,6 @@ namespace MWScript } }; - template - class OpGetForceRun : public Interpreter::Opcode0 - { - public: - - virtual void execute (Interpreter::Runtime& runtime) - { - MWWorld::Ptr ptr = R()(runtime); - - MWMechanics::NpcStats& npcStats = MWWorld::Class::get(ptr).getNpcStats (ptr); - - runtime.push (npcStats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceRun)); - } - }; - - template - class OpGetForceSneak : public Interpreter::Opcode0 - { - public: - - virtual void execute (Interpreter::Runtime& runtime) - { - MWWorld::Ptr ptr = R()(runtime); - - MWMechanics::NpcStats& npcStats = MWWorld::Class::get(ptr).getNpcStats (ptr); - - runtime.push (npcStats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak)); - } - }; - - class OpGetPcRunning : public Interpreter::Opcode0 - { - public: - - virtual void execute (Interpreter::Runtime& runtime) - { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); - runtime.push (MWWorld::Class::get(ptr).getStance (ptr, MWWorld::Class::Run)); - } - }; - - class OpGetPcSneaking : public Interpreter::Opcode0 - { - public: - - virtual void execute (Interpreter::Runtime& runtime) - { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); - runtime.push (MWWorld::Class::get(ptr).getStance (ptr, MWWorld::Class::Sneak)); - } - }; - template class OpGetEffect : public Interpreter::Opcode0 { @@ -379,12 +327,6 @@ namespace MWScript const int opcodeWakeUpPc = 0x20001a2; const int opcodeGetLocked = 0x20001c7; const int opcodeGetLockedExplicit = 0x20001c8; - const int opcodeGetPcRunning = 0x20001c9; - const int opcodeGetPcSneaking = 0x20001ca; - const int opcodeGetForceRun = 0x20001cb; - const int opcodeGetForceSneak = 0x20001cc; - const int opcodeGetForceRunExplicit = 0x20001cd; - const int opcodeGetForceSneakExplicit = 0x20001ce; const int opcodeGetEffect = 0x20001cf; const int opcodeGetEffectExplicit = 0x20001d0; @@ -414,10 +356,6 @@ namespace MWScript extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep); extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc); extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); - extensions.registerFunction ("getpcrunning", 'l', "", opcodeGetPcRunning); - extensions.registerFunction ("getpcsneaking", 'l', "", opcodeGetPcSneaking); - extensions.registerFunction ("getforcerun", 'l', "", opcodeGetForceRun, opcodeGetForceRunExplicit); - extensions.registerFunction ("getforcesneak", 'l', "", opcodeGetForceSneak, opcodeGetForceSneakExplicit); extensions.registerFunction ("geteffect", 'l', "l", opcodeGetEffect, opcodeGetEffectExplicit); } @@ -444,12 +382,6 @@ namespace MWScript interpreter.installSegment5 (opcodeWakeUpPc, new OpWakeUpPc); interpreter.installSegment5 (opcodeGetLocked, new OpGetLocked); interpreter.installSegment5 (opcodeGetLockedExplicit, new OpGetLocked); - interpreter.installSegment5 (opcodeGetPcRunning, new OpGetPcRunning); - interpreter.installSegment5 (opcodeGetPcSneaking, new OpGetPcSneaking); - interpreter.installSegment5 (opcodeGetForceRun, new OpGetForceRun); - interpreter.installSegment5 (opcodeGetForceRunExplicit, new OpGetForceRun); - interpreter.installSegment5 (opcodeGetForceSneak, new OpGetForceSneak); - interpreter.installSegment5 (opcodeGetForceSneakExplicit, new OpGetForceSneak); interpreter.installSegment5 (opcodeGetEffect, new OpGetEffect); interpreter.installSegment5 (opcodeGetEffectExplicit, new OpGetEffect); } From 600ed5f38a370bc357bfb3532b2d3504aa783ddf Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 24 Nov 2012 02:48:53 +0100 Subject: [PATCH 029/378] GetAttacked --- apps/openmw/mwscript/docs/vmformat.txt | 4 +++- apps/openmw/mwscript/miscextensions.cpp | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 894fd5f05..9c1084c5c 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -260,5 +260,7 @@ op 0x20001cf: GetEffect op 0x20001d0: GetEffect, explicit op 0x20001d1: GetArmorType op 0x20001d2: GetArmorType, explicit -opcodes 0x20001d3-0x3ffffff unused +op 0x20001d3: GetAttacked +op 0x20001d4: GetAttacked, explicit +opcodes 0x20001d5-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 85e6ab494..204014e74 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -306,6 +306,19 @@ namespace MWScript } }; + template + class OpGetAttacked : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + runtime.push(MWWorld::Class::get(ptr).getCreatureStats (ptr).getAttacked ()); + } + }; + const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeActivate = 0x2000075; @@ -329,6 +342,8 @@ namespace MWScript const int opcodeGetLockedExplicit = 0x20001c8; const int opcodeGetEffect = 0x20001cf; const int opcodeGetEffectExplicit = 0x20001d0; + const int opcodeGetAttacked = 0x20001d3; + const int opcodeGetAttackedExplicit = 0x20001d4; void registerExtensions (Compiler::Extensions& extensions) { @@ -357,6 +372,7 @@ namespace MWScript extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc); extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); extensions.registerFunction ("geteffect", 'l', "l", opcodeGetEffect, opcodeGetEffectExplicit); + extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -384,6 +400,8 @@ namespace MWScript interpreter.installSegment5 (opcodeGetLockedExplicit, new OpGetLocked); interpreter.installSegment5 (opcodeGetEffect, new OpGetEffect); interpreter.installSegment5 (opcodeGetEffectExplicit, new OpGetEffect); + interpreter.installSegment5 (opcodeGetAttacked, new OpGetAttacked); + interpreter.installSegment5 (opcodeGetAttackedExplicit, new OpGetAttacked); } } } From be82d1452f0d9b51db5ba289ba931e4a8fd49bc9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 24 Nov 2012 02:59:44 +0100 Subject: [PATCH 030/378] HasItemEquipped --- apps/openmw/mwscript/containerextensions.cpp | 31 ++++++++++++++++++++ apps/openmw/mwscript/docs/vmformat.txt | 2 ++ 2 files changed, 33 insertions(+) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index c5566c92b..66cf21c5b 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -230,6 +230,32 @@ namespace MWScript } }; + template + class OpHasItemEquipped : public Interpreter::Opcode0 + { + public: + + virtual void execute(Interpreter::Runtime &runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string item = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore (ptr); + for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) + { + MWWorld::ContainerStoreIterator it = invStore.getSlot (slot); + if (it != invStore.end() && toLower(it->getCellRef().mRefID) == toLower(item)) + { + runtime.push(1); + return; + } + } + runtime.push(0); + } + }; + const int opcodeAddItem = 0x2000076; const int opcodeAddItemExplicit = 0x2000077; const int opcodeGetItemCount = 0x2000078; @@ -240,6 +266,8 @@ namespace MWScript const int opcodeEquipExplicit = 0x20001b4; const int opcodeGetArmorType = 0x20001d1; const int opcodeGetArmorTypeExplicit = 0x20001d2; + const int opcodeHasItemEquipped = 0x20001d5; + const int opcodeHasItemEquippedExplicit = 0x20001d6; void registerExtensions (Compiler::Extensions& extensions) { @@ -250,6 +278,7 @@ namespace MWScript opcodeRemoveItemExplicit); extensions.registerInstruction ("equip", "c", opcodeEquip, opcodeEquipExplicit); extensions.registerFunction ("getarmortype", 'l', "l", opcodeGetArmorType, opcodeGetArmorTypeExplicit); + extensions.registerFunction ("hasitemequipped", 'l', "c", opcodeHasItemEquipped, opcodeHasItemEquippedExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -264,6 +293,8 @@ namespace MWScript interpreter.installSegment5 (opcodeEquipExplicit, new OpEquip); interpreter.installSegment5 (opcodeGetArmorType, new OpGetArmorType); interpreter.installSegment5 (opcodeGetArmorTypeExplicit, new OpGetArmorType); + interpreter.installSegment5 (opcodeHasItemEquipped, new OpHasItemEquipped); + interpreter.installSegment5 (opcodeHasItemEquippedExplicit, new OpHasItemEquipped); } } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 9c1084c5c..e964e747e 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -262,5 +262,7 @@ op 0x20001d1: GetArmorType op 0x20001d2: GetArmorType, explicit op 0x20001d3: GetAttacked op 0x20001d4: GetAttacked, explicit +op 0x20001d5: HasItemEquipped +op 0x20001d6: HasItemEquipped, explicit opcodes 0x20001d5-0x3ffffff unused From 70aa7459f541b25ddb1ce27613e0630546722856 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 24 Nov 2012 03:04:26 +0100 Subject: [PATCH 031/378] GetWeaponDrawn --- apps/openmw/mwscript/docs/vmformat.txt | 4 +++- apps/openmw/mwscript/miscextensions.cpp | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index e964e747e..498e1572f 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -264,5 +264,7 @@ op 0x20001d3: GetAttacked op 0x20001d4: GetAttacked, explicit op 0x20001d5: HasItemEquipped op 0x20001d6: HasItemEquipped, explicit -opcodes 0x20001d5-0x3ffffff unused +op 0x20001d7: GetWeaponDrawn +op 0x20001d8: GetWeaponDrawn, explicit +opcodes 0x20001d9-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 204014e74..505dbdf7d 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -319,6 +319,19 @@ namespace MWScript } }; + template + class OpGetWeaponDrawn : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + runtime.push(MWWorld::Class::get(ptr).getNpcStats (ptr).getDrawState () == MWMechanics::DrawState_Weapon); + } + }; + const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeActivate = 0x2000075; @@ -344,6 +357,8 @@ namespace MWScript const int opcodeGetEffectExplicit = 0x20001d0; const int opcodeGetAttacked = 0x20001d3; const int opcodeGetAttackedExplicit = 0x20001d4; + const int opcodeGetWeaponDrawn = 0x20001d7; + const int opcodeGetWeaponDrawnExplicit = 0x20001d8; void registerExtensions (Compiler::Extensions& extensions) { @@ -373,6 +388,7 @@ namespace MWScript extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); extensions.registerFunction ("geteffect", 'l', "l", opcodeGetEffect, opcodeGetEffectExplicit); extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); + extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -402,6 +418,8 @@ namespace MWScript interpreter.installSegment5 (opcodeGetEffectExplicit, new OpGetEffect); interpreter.installSegment5 (opcodeGetAttacked, new OpGetAttacked); interpreter.installSegment5 (opcodeGetAttackedExplicit, new OpGetAttacked); + interpreter.installSegment5 (opcodeGetWeaponDrawnExplicit, new OpGetWeaponDrawn); + interpreter.installSegment5 (opcodeGetWeaponDrawnExplicit, new OpGetWeaponDrawn); } } } From 019146756fc0d91ab46b54c696fc33770c0d2f1a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 24 Nov 2012 10:14:43 +0100 Subject: [PATCH 032/378] minor documentation changes regarding future improvements and additions --- apps/opencs/model/doc/document.hpp | 4 +++- apps/opencs/view/doc/operation.cpp | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 0249d8e5c..1f73b5437 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -20,7 +20,9 @@ namespace CSMDoc State_Modified = 1, State_Locked = 2, State_Saving = 4, - State_Verifying = 8 + State_Verifying = 8, + State_Compiling = 16, // not implemented yet + State_Searching = 32 // not implemented yet }; std::string mName; ///< \todo replace name with ESX list diff --git a/apps/opencs/view/doc/operation.cpp b/apps/opencs/view/doc/operation.cpp index 02a93f9c5..62f006be5 100644 --- a/apps/opencs/view/doc/operation.cpp +++ b/apps/opencs/view/doc/operation.cpp @@ -34,6 +34,8 @@ void CSVDoc::Operation::updateLabel (int threads) CSVDoc::Operation::Operation (int type) : mType (type), mStalling (false) { + /// \todo Add a cancel button or a pop up menu with a cancel item + updateLabel(); /// \todo assign different progress bar colours to allow the user to distinguish easily between operation types From 4c0dcd46a19fea65c1c8bd31f82fbbe9103b7429 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 24 Nov 2012 11:01:53 +0100 Subject: [PATCH 033/378] added UniversalId class --- apps/opencs/CMakeLists.txt | 4 + apps/opencs/model/world/universalid.cpp | 161 ++++++++++++++++++++++++ apps/opencs/model/world/universalid.hpp | 80 ++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 apps/opencs/model/world/universalid.cpp create mode 100644 apps/opencs/model/world/universalid.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 66b9f4942..cb721ac54 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -4,6 +4,8 @@ set (OPENCS_SRC model/doc/documentmanager.cpp model/doc/document.cpp + model/world/universalid.cpp + view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp ) @@ -12,6 +14,8 @@ set (OPENCS_HDR model/doc/documentmanager.hpp model/doc/document.hpp + model/world/universalid.hpp + view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp ) diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp new file mode 100644 index 000000000..b56a336a6 --- /dev/null +++ b/apps/opencs/model/world/universalid.cpp @@ -0,0 +1,161 @@ + +#include "universalid.hpp" + +#include +#include +#include + +namespace +{ + struct TypeData + { + CSMWorld::UniversalId::Class mClass; + CSMWorld::UniversalId::Type mType; + const char *mName; + }; + + static const TypeData sNoArg[] = + { + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty" }, + + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker + }; + + static const TypeData sIdArg[] = + { + + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker + }; + + static const TypeData sIndexArg[] = + { + + { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker + }; +} + +CSMWorld::UniversalId::UniversalId (Type type) : mArgumentType (ArgumentType_None), mType (type), mIndex (0) +{ + for (int i=0; sNoArg[i].mName; ++i) + if (type==sNoArg[i].mType) + { + mClass = sNoArg[i].mClass; + return; + } + + throw std::logic_error ("invalid argument-less UniversalId type"); +} + +CSMWorld::UniversalId::UniversalId (Type type, const std::string& id) +: mArgumentType (ArgumentType_Id), mType (type), mId (id), mIndex (0) +{ + for (int i=0; sIdArg[i].mName; ++i) + if (type==sIdArg[i].mType) + { + mClass = sIdArg[i].mClass; + return; + } + + throw std::logic_error ("invalid ID argument UniversalId type"); +} + +CSMWorld::UniversalId::UniversalId (Type type, int index) +: mArgumentType (ArgumentType_Index), mType (type), mIndex (index) +{ + for (int i=0; sIndexArg[i].mName; ++i) + if (type==sIndexArg[i].mType) + { + mClass = sIndexArg[i].mClass; + return; + } + + throw std::logic_error ("invalid index argument UniversalId type"); +} + +CSMWorld::UniversalId::Class CSMWorld::UniversalId::getClass() const +{ + return mClass; +} + +CSMWorld::UniversalId::ArgumentType CSMWorld::UniversalId::getArgumentType() const +{ + return mArgumentType; +} + +CSMWorld::UniversalId::Type CSMWorld::UniversalId::getType() const +{ + return mType; +} + +const std::string& CSMWorld::UniversalId::getId() const +{ + if (mArgumentType!=ArgumentType_Id) + throw std::logic_error ("invalid access to ID of non-ID UniversalId"); + + return mId; +} + +int CSMWorld::UniversalId::getIndex() const +{ + if (mArgumentType!=ArgumentType_Index) + throw std::logic_error ("invalid access to index of non-index UniversalId"); + + return mIndex; +} + +bool CSMWorld::UniversalId::isEqual (const UniversalId& universalId) const +{ + if (mClass!=universalId.mClass || mArgumentType!=universalId.mArgumentType || mType!=universalId.mType) + return false; + + switch (mArgumentType) + { + case ArgumentType_Id: return mId==universalId.mId; + case ArgumentType_Index: return mIndex==universalId.mIndex; + + default: return true; + } +} + +std::string CSMWorld::UniversalId::getTypeName() const +{ + const TypeData *typeData = mArgumentType==ArgumentType_None ? sNoArg : + (mArgumentType==ArgumentType_Id ? sIdArg : sIndexArg); + + for (int i=0; typeData[i].mName; ++i) + if (typeData[i].mType==mType) + return typeData[i].mName; + + throw std::logic_error ("failed to retrieve UniversalId type name"); +} + +std::string CSMWorld::UniversalId::toString() const +{ + std::ostringstream stream; + + stream << getTypeName(); + + switch (mArgumentType) + { + case ArgumentType_None: break; + case ArgumentType_Id: stream << " " << mId; + case ArgumentType_Index: stream << " " << mIndex; + } + + return stream.str(); +} + +bool operator== (const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right) +{ + return left.isEqual (right); +} + +bool operator!= (const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right) +{ + return !left.isEqual (right); +} + +std::ostream& operator< (std::ostream& stream, const CSMWorld::UniversalId& universalId) +{ + return stream << universalId.toString(); +} \ No newline at end of file diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp new file mode 100644 index 000000000..6f085cfc2 --- /dev/null +++ b/apps/opencs/model/world/universalid.hpp @@ -0,0 +1,80 @@ +#ifndef CSM_WOLRD_UNIVERSALID_H +#define CSM_WOLRD_UNIVERSALID_H + +#include +#include + +namespace CSMWorld +{ + class UniversalId + { + public: + + enum Class + { + Class_None = 0, + Class_Record, + Class_SubRecord, + Class_RecordList, + Class_Collection, // multiple types of records combined + Class_Transient, // not part of the world data or the project data + Class_NonRecord // record like data that is not part of the world + }; + + enum ArgumentType + { + ArgumentType_None, + ArgumentType_Id, + ArgumentType_Index + }; + + enum Type + { + Type_None + }; + + private: + + Class mClass; + ArgumentType mArgumentType; + Type mType; + std::string mId; + int mIndex; + + public: + + UniversalId (Type type = Type_None); + ///< Using a type for a non-argument-less UniversalId will throw an exception. + + UniversalId (Type type, const std::string& id); + ///< Using a type for a non-ID-argument UniversalId will throw an exception. + + UniversalId (Type type, int index); + ///< Using a type for a non-index-argument UniversalId will throw an exception. + + Class getClass() const; + + ArgumentType getArgumentType() const; + + Type getType() const; + + const std::string& getId() const; + ///< Calling this function for a non-ID type will throw an exception. + + int getIndex() const; + ///< Calling this function for a non-index type will throw an exception. + + bool isEqual (const UniversalId& universalId) const; + + std::string getTypeName() const; + + std::string toString() const; + }; + + bool operator== (const UniversalId& left, const UniversalId& right); + bool operator!= (const UniversalId& left, const UniversalId& right); + + std::ostream& operator< (std::ostream& stream, const UniversalId& universalId); +} + +#endif From eece4226c0427ac2cd7ca5e7b621e6bc8799e8e7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 24 Nov 2012 13:17:21 +0100 Subject: [PATCH 034/378] basic sub view system (very incomplete) --- apps/opencs/CMakeLists.txt | 4 ++++ apps/opencs/view/doc/view.cpp | 32 ++++++++++++++++++++++++++++-- apps/opencs/view/doc/view.hpp | 9 +++++++++ apps/opencs/view/world/subview.cpp | 18 +++++++++++++++++ apps/opencs/view/world/subview.hpp | 28 ++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 apps/opencs/view/world/subview.cpp create mode 100644 apps/opencs/view/world/subview.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index cb721ac54..a56e77d08 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -7,6 +7,8 @@ set (OPENCS_SRC model/world/universalid.cpp view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp + + view/world/subview.cpp ) set (OPENCS_HDR @@ -17,6 +19,8 @@ set (OPENCS_HDR model/world/universalid.hpp view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp + + view/world/subview.hpp ) set (OPENCS_US diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 5c7199aa4..9bcb8fc5a 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -5,9 +5,12 @@ #include #include +#include #include "../../model/doc/document.hpp" +#include "../world/subview.hpp" + #include "viewmanager.hpp" #include "operations.hpp" @@ -56,6 +59,10 @@ void CSVDoc::View::setupViewMenu() QAction *newWindow = new QAction (tr ("&New View"), this); connect (newWindow, SIGNAL (triggered()), this, SLOT (newView())); view->addAction (newWindow); + + QAction *test = new QAction (tr ("Add test Subview"), this); + connect (test, SIGNAL (triggered()), this, SLOT (addTestSubView())); + view->addAction (test); } void CSVDoc::View::setupWorldMenu() @@ -107,8 +114,9 @@ void CSVDoc::View::updateActions() CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) : mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), mViewTotal (totalViews) { - setCentralWidget (new QWidget); - resize (200, 200); /// \todo get default size from settings and set reasonable minimal size + setDockOptions (QMainWindow::AllowNestedDocks); + + resize (300, 300); /// \todo get default size from settings and set reasonable minimal size mOperations = new Operations; addDockWidget (Qt::BottomDockWidgetArea, mOperations); @@ -158,6 +166,21 @@ void CSVDoc::View::updateProgress (int current, int max, int type, int threads) mOperations->setProgress (current, max, type, threads); } +void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id) +{ + /// \todo add an user setting for limiting the number of sub views per top level view. Automatically open a new top level view if this + /// number is exceeded + + /// \todo if the sub view limit setting is one, the sub view title bar should be hidden and the text in the main title bar adjusted + /// accordingly + + /// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis) + + CSVWorld::SubView *view = new CSVWorld::SubView (id); + addDockWidget (Qt::RightDockWidgetArea, view); + view->show(); +} + void CSVDoc::View::newView() { mViewManager.addView (mDocument); @@ -176,4 +199,9 @@ void CSVDoc::View::save() void CSVDoc::View::verify() { mDocument->verify(); +} + +void CSVDoc::View::addTestSubView() +{ + addSubView (CSMWorld::UniversalId::Type_None); } \ No newline at end of file diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index d15d942fc..7a9164e12 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -12,6 +12,11 @@ namespace CSMDoc class Document; } +namespace CSMWorld +{ + class UniversalId; +} + namespace CSVDoc { class ViewManager; @@ -69,6 +74,8 @@ namespace CSVDoc void updateProgress (int current, int max, int type, int threads); + void addSubView (const CSMWorld::UniversalId& id); + signals: void newDocumentRequest(); @@ -82,6 +89,8 @@ namespace CSVDoc void save(); void verify(); + + void addTestSubView(); ///< \todo remove }; } diff --git a/apps/opencs/view/world/subview.cpp b/apps/opencs/view/world/subview.cpp new file mode 100644 index 000000000..23f075980 --- /dev/null +++ b/apps/opencs/view/world/subview.cpp @@ -0,0 +1,18 @@ + +#include "subview.hpp" + +CSVWorld::SubView::SubView (const CSMWorld::UniversalId& id) : mUniversalId (id) +{ + /// \todo add a button to the title bar that clones this sub view + + setWindowTitle (mUniversalId.toString().c_str()); + + /// \todo remove (for testing only) + setMinimumWidth (100); + setMinimumHeight (60); +} + +CSMWorld::UniversalId CSVWorld::SubView::getUniversalId() const +{ + return mUniversalId; +} diff --git a/apps/opencs/view/world/subview.hpp b/apps/opencs/view/world/subview.hpp new file mode 100644 index 000000000..8488690c8 --- /dev/null +++ b/apps/opencs/view/world/subview.hpp @@ -0,0 +1,28 @@ +#ifndef CSV_WORLD_SUBVIEW_H +#define CSV_WORLD_SUBVIEW_H + +#include "../../model/world/universalid.hpp" + +#include + +namespace CSVWorld +{ + class SubView : public QDockWidget + { + Q_OBJECT + + CSMWorld::UniversalId mUniversalId; + + // not implemented + SubView (const SubView&); + SubView& operator= (SubView&); + + public: + + SubView (const CSMWorld::UniversalId& id); + + CSMWorld::UniversalId getUniversalId() const; + }; +} + +#endif \ No newline at end of file From 0db48b29c755490b7614f7197249b11b9c9e5eff Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 25 Nov 2012 01:26:29 +0100 Subject: [PATCH 035/378] GetSpellEffects, GetRace --- apps/openmw/mwmechanics/activespells.cpp | 16 ++++++++++++++ apps/openmw/mwmechanics/activespells.hpp | 3 +++ apps/openmw/mwscript/docs/vmformat.txt | 6 ++++- apps/openmw/mwscript/miscextensions.cpp | 22 ++++++++++++++++++- apps/openmw/mwscript/statsextensions.cpp | 28 ++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 989bdedd7..f53ccdce3 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -3,6 +3,8 @@ #include +#include + #include #include #include @@ -258,4 +260,18 @@ namespace MWMechanics return scaledDuration-usedUp; } + + bool ActiveSpells::isSpellActive(std::string id) const + { + boost::algorithm::to_lower(id); + for (TContainer::iterator iter = mSpells.begin(); iter != mSpells.end(); ++iter) + { + std::string left = iter->first; + boost::algorithm::to_lower(left); + + if (iter->first == id) + return true; + } + return false; + } } diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index e7a239854..6b832f4cd 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -58,6 +58,9 @@ namespace MWMechanics void removeSpell (const std::string& id); + bool isSpellActive (std::string id) const; + ///< case insensitive + const MagicEffects& getMagicEffects() const; TIterator begin() const; diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 498e1572f..73d6b5239 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -266,5 +266,9 @@ op 0x20001d5: HasItemEquipped op 0x20001d6: HasItemEquipped, explicit op 0x20001d7: GetWeaponDrawn op 0x20001d8: GetWeaponDrawn, explicit -opcodes 0x20001d9-0x3ffffff unused +op 0x20001d9: GetRace +op 0x20001da: GetRace, explicit +op 0x20001db: GetSpellEffects +op 0x20001dc: GetSpellEffects, explicit +opcodes 0x20001dd-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 505dbdf7d..81a315456 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -332,6 +332,21 @@ namespace MWScript } }; + template + class OpGetSpellEffects : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + std::string id = runtime.getStringLiteral(runtime[0].mInteger); + runtime.pop(); + + runtime.push(MWWorld::Class::get(ptr).getCreatureStats(ptr).getActiveSpells().isSpellActive(id)); + } + }; + const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeActivate = 0x2000075; @@ -359,6 +374,8 @@ namespace MWScript const int opcodeGetAttackedExplicit = 0x20001d4; const int opcodeGetWeaponDrawn = 0x20001d7; const int opcodeGetWeaponDrawnExplicit = 0x20001d8; + const int opcodeGetSpellEffects = 0x20001db; + const int opcodeGetSpellEffectsExplicit = 0x20001dc; void registerExtensions (Compiler::Extensions& extensions) { @@ -389,6 +406,7 @@ namespace MWScript extensions.registerFunction ("geteffect", 'l', "l", opcodeGetEffect, opcodeGetEffectExplicit); extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit); + extensions.registerFunction ("getspelleffects", 'l', "c", opcodeGetSpellEffects, opcodeGetSpellEffectsExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -418,8 +436,10 @@ namespace MWScript interpreter.installSegment5 (opcodeGetEffectExplicit, new OpGetEffect); interpreter.installSegment5 (opcodeGetAttacked, new OpGetAttacked); interpreter.installSegment5 (opcodeGetAttackedExplicit, new OpGetAttacked); + interpreter.installSegment5 (opcodeGetWeaponDrawn, new OpGetWeaponDrawn); interpreter.installSegment5 (opcodeGetWeaponDrawnExplicit, new OpGetWeaponDrawn); - interpreter.installSegment5 (opcodeGetWeaponDrawnExplicit, new OpGetWeaponDrawn); + interpreter.installSegment5 (opcodeGetSpellEffects, new OpGetSpellEffects); + interpreter.installSegment5 (opcodeGetSpellEffectsExplicit, new OpGetSpellEffects); } } } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index f6a31dd7f..bea41df1d 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -780,6 +780,25 @@ namespace MWScript } }; + template + class OpGetRace : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string race = runtime.getStringLiteral(runtime[0].mInteger); + boost::algorithm::to_lower(race); + runtime.pop(); + + std::string npcRace = ptr.get()->mBase->mRace; + boost::algorithm::to_lower(npcRace); + + runtime.push (npcRace == race); + } + }; const int numberOfAttributes = 8; @@ -850,6 +869,9 @@ namespace MWScript const int opcodeGetBlightDisease = 0x20001aa; const int opcodeGetBlightDiseaseExplicit = 0x20001ab; + const int opcodeGetRace = 0x20001d9; + const int opcodeGetRaceExplicit = 0x20001da; + void registerExtensions (Compiler::Extensions& extensions) { static const char *attributes[numberOfAttributes] = @@ -950,6 +972,9 @@ namespace MWScript opcodeGetCommonDiseaseExplicit); extensions.registerFunction ("getblightdisease", 'l', "", opcodeGetBlightDisease, opcodeGetBlightDiseaseExplicit); + + extensions.registerFunction ("getrace", 'l', "c", opcodeGetRace, + opcodeGetRaceExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -1045,6 +1070,9 @@ namespace MWScript interpreter.installSegment5 (opcodeGetCommonDiseaseExplicit, new OpGetCommonDisease); interpreter.installSegment5 (opcodeGetBlightDisease, new OpGetBlightDisease); interpreter.installSegment5 (opcodeGetBlightDiseaseExplicit, new OpGetBlightDisease); + + interpreter.installSegment5 (opcodeGetRace, new OpGetRace); + interpreter.installSegment5 (opcodeGetRaceExplicit, new OpGetRace); } } } From e68dc19256a413229779d3e0eec47e61c8a955b2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 25 Nov 2012 01:54:37 +0100 Subject: [PATCH 036/378] GetCurrentTime, HasSoulGem --- apps/openmw/mwscript/containerextensions.cpp | 35 ++++++++++++++++++-- apps/openmw/mwscript/docs/vmformat.txt | 5 ++- apps/openmw/mwscript/miscextensions.cpp | 13 ++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 66cf21c5b..5dba79d3f 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -256,6 +256,33 @@ namespace MWScript } }; + template + class OpHasSoulGem : public Interpreter::Opcode0 + { + public: + + virtual void execute(Interpreter::Runtime &runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string creatureName = toLower (runtime.getStringLiteral (runtime[0].mInteger)); + runtime.pop(); + + MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore (ptr); + for (MWWorld::ContainerStoreIterator it = invStore.begin(MWWorld::ContainerStore::Type_Miscellaneous); + it != invStore.end(); ++it) + { + + if (toLower(it->getCellRef().mSoul) == toLower(creatureName)) + { + runtime.push(1); + return; + } + } + runtime.push(0); + } + }; + const int opcodeAddItem = 0x2000076; const int opcodeAddItemExplicit = 0x2000077; const int opcodeGetItemCount = 0x2000078; @@ -268,6 +295,8 @@ namespace MWScript const int opcodeGetArmorTypeExplicit = 0x20001d2; const int opcodeHasItemEquipped = 0x20001d5; const int opcodeHasItemEquippedExplicit = 0x20001d6; + const int opcodeHasSoulGem = 0x20001de; + const int opcodeHasSoulGemExplicit = 0x20001df; void registerExtensions (Compiler::Extensions& extensions) { @@ -279,6 +308,7 @@ namespace MWScript extensions.registerInstruction ("equip", "c", opcodeEquip, opcodeEquipExplicit); extensions.registerFunction ("getarmortype", 'l', "l", opcodeGetArmorType, opcodeGetArmorTypeExplicit); extensions.registerFunction ("hasitemequipped", 'l', "c", opcodeHasItemEquipped, opcodeHasItemEquippedExplicit); + extensions.registerFunction ("hassoulgem", 'l', "c", opcodeHasSoulGem, opcodeHasSoulGemExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -293,9 +323,10 @@ namespace MWScript interpreter.installSegment5 (opcodeEquipExplicit, new OpEquip); interpreter.installSegment5 (opcodeGetArmorType, new OpGetArmorType); interpreter.installSegment5 (opcodeGetArmorTypeExplicit, new OpGetArmorType); - interpreter.installSegment5 (opcodeHasItemEquipped, new OpHasItemEquipped); + interpreter.installSegment5 (opcodeHasItemEquipped, new OpHasItemEquipped); interpreter.installSegment5 (opcodeHasItemEquippedExplicit, new OpHasItemEquipped); - + interpreter.installSegment5 (opcodeHasSoulGem, new OpHasSoulGem); + interpreter.installSegment5 (opcodeHasSoulGemExplicit, new OpHasSoulGem); } } } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 73d6b5239..3e8542335 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -270,5 +270,8 @@ op 0x20001d9: GetRace op 0x20001da: GetRace, explicit op 0x20001db: GetSpellEffects op 0x20001dc: GetSpellEffects, explicit -opcodes 0x20001dd-0x3ffffff unused +op 0x20001dd: GetCurrentTime +op 0x20001de: HasSoulGem +op 0x20001df: HasSoulGem, explicit +opcodes 0x20001e0-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 81a315456..1cd5ed35e 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -347,6 +347,16 @@ namespace MWScript } }; + class OpGetCurrentTime : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + runtime.push(MWBase::Environment::get().getWorld()->getTimeStamp().getHour()); + } + }; + const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeActivate = 0x2000075; @@ -376,6 +386,7 @@ namespace MWScript const int opcodeGetWeaponDrawnExplicit = 0x20001d8; const int opcodeGetSpellEffects = 0x20001db; const int opcodeGetSpellEffectsExplicit = 0x20001dc; + const int opcodeGetCurrentTime = 0x20001dd; void registerExtensions (Compiler::Extensions& extensions) { @@ -407,6 +418,7 @@ namespace MWScript extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit); extensions.registerFunction ("getspelleffects", 'l', "c", opcodeGetSpellEffects, opcodeGetSpellEffectsExplicit); + extensions.registerFunction ("getcurrenttime", 'f', "", opcodeGetCurrentTime); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -440,6 +452,7 @@ namespace MWScript interpreter.installSegment5 (opcodeGetWeaponDrawnExplicit, new OpGetWeaponDrawn); interpreter.installSegment5 (opcodeGetSpellEffects, new OpGetSpellEffects); interpreter.installSegment5 (opcodeGetSpellEffectsExplicit, new OpGetSpellEffects); + interpreter.installSegment5 (opcodeGetCurrentTime, new OpGetCurrentTime); } } } From 1ccad32877958fc43516f9ceb10943e4f3acddbe Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 25 Nov 2012 02:06:43 +0100 Subject: [PATCH 037/378] GetWeaponType --- apps/openmw/mwscript/containerextensions.cpp | 26 ++++++++++++++++++++ apps/openmw/mwscript/docs/vmformat.txt | 6 ++++- apps/openmw/mwworld/player.hpp | 1 - 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 5dba79d3f..530051b09 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -283,6 +283,27 @@ namespace MWScript } }; + template + class OpGetWeaponType : public Interpreter::Opcode0 + { + public: + + virtual void execute(Interpreter::Runtime &runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore (ptr); + MWWorld::ContainerStoreIterator it = invStore.getSlot (MWWorld::InventoryStore::Slot_CarriedRight); + if (it == invStore.end()) + { + runtime.push(-1); + return; + } + + runtime.push(it->get()->mBase->mData.mType); + } + }; + const int opcodeAddItem = 0x2000076; const int opcodeAddItemExplicit = 0x2000077; const int opcodeGetItemCount = 0x2000078; @@ -297,6 +318,8 @@ namespace MWScript const int opcodeHasItemEquippedExplicit = 0x20001d6; const int opcodeHasSoulGem = 0x20001de; const int opcodeHasSoulGemExplicit = 0x20001df; + const int opcodeGetWeaponType = 0x20001e0; + const int opcodeGetWeaponTypeExplicit = 0x20001e1; void registerExtensions (Compiler::Extensions& extensions) { @@ -309,6 +332,7 @@ namespace MWScript extensions.registerFunction ("getarmortype", 'l', "l", opcodeGetArmorType, opcodeGetArmorTypeExplicit); extensions.registerFunction ("hasitemequipped", 'l', "c", opcodeHasItemEquipped, opcodeHasItemEquippedExplicit); extensions.registerFunction ("hassoulgem", 'l', "c", opcodeHasSoulGem, opcodeHasSoulGemExplicit); + extensions.registerFunction ("getweapontype", 'l', "", opcodeGetWeaponType, opcodeGetWeaponTypeExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -327,6 +351,8 @@ namespace MWScript interpreter.installSegment5 (opcodeHasItemEquippedExplicit, new OpHasItemEquipped); interpreter.installSegment5 (opcodeHasSoulGem, new OpHasSoulGem); interpreter.installSegment5 (opcodeHasSoulGemExplicit, new OpHasSoulGem); + interpreter.installSegment5 (opcodeGetWeaponType, new OpGetWeaponType); + interpreter.installSegment5 (opcodeGetWeaponTypeExplicit, new OpGetWeaponType); } } } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 3e8542335..26c87eeee 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -273,5 +273,9 @@ op 0x20001dc: GetSpellEffects, explicit op 0x20001dd: GetCurrentTime op 0x20001de: HasSoulGem op 0x20001df: HasSoulGem, explicit -opcodes 0x20001e0-0x3ffffff unused +op 0x20001e0: GetWeaponType +op 0x20001e1: GetWeaponType, explicit +opcodes 0x20001e2-0x3ffffff unused + + diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index eab6d6b8d..1c1ef76cf 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -66,7 +66,6 @@ namespace MWWorld void setUpDown(int value); void toggleRunning(); - void getRunning(); }; } #endif From ef9575498f231e50a99a7beadc048e21ce74486d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 26 Nov 2012 12:29:22 +0100 Subject: [PATCH 038/378] basic (non-editable) subview for global variables --- apps/opencs/CMakeLists.txt | 9 +- apps/opencs/editor.cpp | 18 +++ apps/opencs/model/doc/document.cpp | 10 ++ apps/opencs/model/doc/document.hpp | 9 ++ apps/opencs/model/world/columns.hpp | 21 +++ apps/opencs/model/world/data.cpp | 47 +++++++ apps/opencs/model/world/data.hpp | 39 ++++++ apps/opencs/model/world/idcollection.cpp | 6 + apps/opencs/model/world/idcollection.hpp | 158 +++++++++++++++++++++++ apps/opencs/model/world/idtable.cpp | 55 ++++++++ apps/opencs/model/world/idtable.hpp | 37 ++++++ apps/opencs/model/world/record.hpp | 76 +++++++++++ apps/opencs/model/world/universalid.cpp | 29 ++++- apps/opencs/model/world/universalid.hpp | 7 +- apps/opencs/view/doc/view.cpp | 33 +++-- apps/opencs/view/doc/view.hpp | 11 +- apps/opencs/view/world/globals.cpp | 26 ++++ apps/opencs/view/world/globals.hpp | 17 +++ apps/opencs/view/world/subview.hpp | 22 ++++ components/esm/loadglob.hpp | 2 +- 20 files changed, 614 insertions(+), 18 deletions(-) create mode 100644 apps/opencs/model/world/columns.hpp create mode 100644 apps/opencs/model/world/data.cpp create mode 100644 apps/opencs/model/world/data.hpp create mode 100644 apps/opencs/model/world/idcollection.cpp create mode 100644 apps/opencs/model/world/idcollection.hpp create mode 100644 apps/opencs/model/world/idtable.cpp create mode 100644 apps/opencs/model/world/idtable.hpp create mode 100644 apps/opencs/model/world/record.hpp create mode 100644 apps/opencs/view/world/globals.cpp create mode 100644 apps/opencs/view/world/globals.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index a56e77d08..92fd859be 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -4,11 +4,11 @@ set (OPENCS_SRC model/doc/documentmanager.cpp model/doc/document.cpp - model/world/universalid.cpp + model/world/universalid.cpp model/world/idcollection.cpp model/world/data.cpp model/world/idtable.cpp view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp - view/world/subview.cpp + view/world/subview.cpp view/world/globals.cpp ) set (OPENCS_HDR @@ -16,11 +16,12 @@ set (OPENCS_HDR model/doc/documentmanager.hpp model/doc/document.hpp - model/world/universalid.hpp + model/world/universalid.hpp model/world/record.hpp model/world/idcollection.hpp model/world/data.hpp + model/world/idtable.hpp model/world/columns.hpp view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp - view/world/subview.hpp + view/world/subview.hpp view/world/globals.hpp ) set (OPENCS_US diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index c03a1dc5d..6977a22f0 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -5,6 +5,9 @@ #include +#include "model/doc/document.hpp" +#include "model/world/data.hpp" + CS::Editor::Editor() : mViewManager (mDocumentManager), mNewDocumentIndex (0) { connect (&mViewManager, SIGNAL (newDocumentRequest ()), this, SLOT (createDocument ())); @@ -17,6 +20,21 @@ void CS::Editor::createDocument() stream << "NewDocument" << (++mNewDocumentIndex); CSMDoc::Document *document = mDocumentManager.addDocument (stream.str()); + + static const char *sGlobals[] = + { + "Day", "DaysPassed", "GameHour", "Month", "PCRace", "PCVampire", "PCWerewolf", "PCYear", 0 + }; + + for (int i=0; sGlobals[i]; ++i) + { + ESM::Global record; + record.mId = sGlobals[i]; + record.mValue = i==0 ? 1 : 0; + record.mType = ESM::VT_Float; + document->getData().getGlobals().add (record); + } + mViewManager.addView (document); } diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index cd65e83ae..e8091b3e0 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -104,4 +104,14 @@ void CSMDoc::Document::verifying() mVerifyTimer.stop(); emit stateChanged (getState(), this); } +} + +const CSMWorld::Data& CSMDoc::Document::getData() const +{ + return mData; +} + +CSMWorld::Data& CSMDoc::Document::getData() +{ + return mData; } \ No newline at end of file diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 1f73b5437..b414bf04d 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -7,6 +7,8 @@ #include #include +#include "../world/data.hpp" + namespace CSMDoc { class Document : public QObject @@ -25,8 +27,11 @@ namespace CSMDoc State_Searching = 32 // not implemented yet }; + private: + std::string mName; ///< \todo replace name with ESX list QUndoStack mUndoStack; + CSMWorld::Data mData; int mSaveCount; ///< dummy implementation -> remove when proper save is implemented. QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented. @@ -56,6 +61,10 @@ namespace CSMDoc void abortOperation (int type); + const CSMWorld::Data& getData() const; + + CSMWorld::Data& getData(); + signals: void stateChanged (int state, CSMDoc::Document *document); diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp new file mode 100644 index 000000000..56362e4cd --- /dev/null +++ b/apps/opencs/model/world/columns.hpp @@ -0,0 +1,21 @@ +#ifndef CSM_WOLRD_COLUMNS_H +#define CSM_WOLRD_COLUMNS_H + +#include "idcollection.hpp" + +namespace CSMWorld +{ + template + struct FloatValueColumn : public Column + { + FloatValueColumn() : Column ("Value") {} + + virtual QVariant get (const Record& record) const + { + return record.get().mValue; + } + }; + +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp new file mode 100644 index 000000000..7fdb149b3 --- /dev/null +++ b/apps/opencs/model/world/data.cpp @@ -0,0 +1,47 @@ + +#include "data.hpp" + +#include + +#include + +#include + +#include "idtable.hpp" +#include "columns.hpp" + +CSMWorld::Data::Data() +{ + mGlobals.addColumn (new FloatValueColumn); + + mModels.insert (std::make_pair ( + UniversalId (UniversalId::Type_Globals), + new IdTable (&mGlobals) + )); +} + +CSMWorld::Data::~Data() +{ + for (std::map::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter) + delete iter->second; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getGlobals() const +{ + return mGlobals; +} + +CSMWorld::IdCollection& CSMWorld::Data::getGlobals() +{ + return mGlobals; +} + +QAbstractTableModel *CSMWorld::Data::getTableModel (const UniversalId& id) +{ + std::map::iterator iter = mModels.find (id); + + if (iter==mModels.end()) + throw std::logic_error ("No table model available for " + id.toString()); + + return iter->second; +} \ No newline at end of file diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp new file mode 100644 index 000000000..11073c5e3 --- /dev/null +++ b/apps/opencs/model/world/data.hpp @@ -0,0 +1,39 @@ +#ifndef CSM_WOLRD_IDLIST_H +#define CSM_WOLRD_IDLIST_H + +#include + +#include + +#include "idcollection.hpp" +#include "universalid.hpp" + +class QAbstractTableModel; + +namespace CSMWorld +{ + class Data + { + IdCollection mGlobals; + std::map mModels; + + // not implemented + Data (const Data&); + Data& operator= (const Data&); + + public: + + Data(); + + ~Data(); + + const IdCollection& getGlobals() const; + + IdCollection& getGlobals(); + + QAbstractTableModel *getTableModel (const UniversalId& id); + ///< If no table model is available for \ id, an exception is thrown. + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/world/idcollection.cpp b/apps/opencs/model/world/idcollection.cpp new file mode 100644 index 000000000..fc4bb1ef6 --- /dev/null +++ b/apps/opencs/model/world/idcollection.cpp @@ -0,0 +1,6 @@ + +#include "idcollection.hpp" + +CSMWorld::IdCollectionBase::IdCollectionBase() {} + +CSMWorld::IdCollectionBase::~IdCollectionBase() {} \ No newline at end of file diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp new file mode 100644 index 000000000..0bddda1ac --- /dev/null +++ b/apps/opencs/model/world/idcollection.hpp @@ -0,0 +1,158 @@ +#ifndef CSM_WOLRD_IDCOLLECTION_H +#define CSM_WOLRD_IDCOLLECTION_H + +#include +#include +#include +#include +#include + +#include + +#include "record.hpp" + +namespace CSMWorld +{ + template + struct Column + { + std::string mTitle; + + Column (const std::string& title) : mTitle (title) {} + + virtual ~Column() {} + + virtual QVariant get (const Record& record) const = 0; + }; + + class IdCollectionBase + { + // not implemented + IdCollectionBase (const IdCollectionBase&); + IdCollectionBase& operator= (const IdCollectionBase&); + + public: + + IdCollectionBase(); + + virtual ~IdCollectionBase(); + + virtual int getSize() const = 0; + + virtual std::string getId (int index) const = 0; + + virtual int getColumns() const = 0; + + virtual std::string getTitle (int column) const = 0; + + virtual QVariant getData (int index, int column) const = 0; + }; + + ///< \brief Collection of ID-based records + template + class IdCollection : public IdCollectionBase + { + std::vector > mRecords; + std::map mIndex; + std::vector *> mColumns; + + // not implemented + IdCollection (const IdCollection&); + IdCollection& operator= (const IdCollection&); + + public: + + IdCollection(); + + virtual ~IdCollection(); + + void add (const ESXRecordT& record); + ///< Add a new record (modified) + + virtual int getSize() const; + + virtual std::string getId (int index) const; + + virtual int getColumns() const; + + virtual QVariant getData (int index, int column) const; + + virtual std::string getTitle (int column) const; + + virtual void addColumn (Column *column); + }; + + template + IdCollection::IdCollection() + {} + + template + IdCollection::~IdCollection() + { + for (typename std::vector *>::iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter) + delete *iter; + } + + template + void IdCollection::add (const ESXRecordT& record) + { + std::string id; + + std::transform (record.mId.begin(), record.mId.end(), std::back_inserter (id), + (int(*)(int)) std::tolower); + + std::map::iterator iter = mIndex.find (id); + + if (iter==mIndex.end()) + { + Record record2; + record2.mState = Record::State_ModifiedOnly; + record2.mModified = record; + + mRecords.push_back (record2); + mIndex.insert (std::make_pair (id, mRecords.size()-1)); + } + else + { + mRecords[iter->second].setModified (record); + } + } + + template + int IdCollection::getSize() const + { + return mRecords.size(); + } + + template + std::string IdCollection::getId (int index) const + { + return mRecords.at (index).get().mId; + } + + template + int IdCollection::getColumns() const + { + return mColumns.size(); + } + + template + QVariant IdCollection::getData (int index, int column) const + { + return mColumns.at (column)->get (mRecords.at (index)); + } + + template + std::string IdCollection::getTitle (int column) const + { + return mColumns.at (column)->mTitle; + } + + template + void IdCollection::addColumn (Column *column) + { + mColumns.push_back (column); + } +} + +#endif diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp new file mode 100644 index 000000000..f8acd4f9f --- /dev/null +++ b/apps/opencs/model/world/idtable.cpp @@ -0,0 +1,55 @@ + +#include "idtable.hpp" + +#include "idcollection.hpp" + +CSMWorld::IdTable::IdTable (IdCollectionBase *idCollection) : mIdCollection (idCollection) +{ + +} + +CSMWorld::IdTable::~IdTable() +{ + +} + +int CSMWorld::IdTable::rowCount (const QModelIndex & parent) const +{ + if (parent.isValid()) + return 0; + + return mIdCollection->getSize(); +} + +int CSMWorld::IdTable::columnCount (const QModelIndex & parent) const +{ + if (parent.isValid()) + return 0; + + return 1+mIdCollection->getColumns(); +} + +QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const +{ + if (role!=Qt::DisplayRole) + return QVariant(); + + if (index.column()==0) + return QVariant (tr (mIdCollection->getId (index.row()).c_str())); + + return mIdCollection->getData (index.row(), index.column()-1); +} + +QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation, int role) const +{ + if (role!=Qt::DisplayRole) + return QVariant(); + + if (orientation==Qt::Vertical) + return QVariant(); + + if (section==0) + return QVariant (tr ("ID")); + + return tr (mIdCollection->getTitle (section-1).c_str()); +} \ No newline at end of file diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp new file mode 100644 index 000000000..4fb939fa3 --- /dev/null +++ b/apps/opencs/model/world/idtable.hpp @@ -0,0 +1,37 @@ +#ifndef CSM_WOLRD_IDTABLE_H +#define CSM_WOLRD_IDTABLE_H + +#include + +namespace CSMWorld +{ + class IdCollectionBase; + + class IdTable : public QAbstractTableModel + { + Q_OBJECT + + IdCollectionBase *mIdCollection; + + // not implemented + IdTable (const IdTable&); + IdTable& operator= (const IdTable&); + + public: + + IdTable (IdCollectionBase *idCollection); + ///< The ownership of \a idCollection is not transferred. + + virtual ~IdTable(); + + int rowCount (const QModelIndex & parent = QModelIndex()) const; + + int columnCount (const QModelIndex & parent = QModelIndex()) const; + + QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; + + QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + }; +} + +#endif diff --git a/apps/opencs/model/world/record.hpp b/apps/opencs/model/world/record.hpp new file mode 100644 index 000000000..230d39eb0 --- /dev/null +++ b/apps/opencs/model/world/record.hpp @@ -0,0 +1,76 @@ +#ifndef CSM_WOLRD_RECORD_H +#define CSM_WOLRD_RECORD_H + +#include + +namespace CSMWorld +{ + template + struct Record + { + enum state + { + State_BaseOnly, // defined in base only + State_Modified, // exists in base, but has been modified + State_ModifiedOnly, // newly created in modified + State_Deleted // exists in base, but has been deleted + }; + + ESXRecordT mBase; + ESXRecordT mModified; + state mState; + + bool isDeleted() const; + + bool isModified() const; + + const ESXRecordT& get() const; + ///< Throws an exception, if the record is deleted. + + ESXRecordT& get(); + ///< Throws an exception, if the record is deleted. + + void setModified (const ESXRecordT& modified); + }; + + template + bool Record::isDeleted() const + { + return mState==State_Deleted; + } + + template + bool Record::isModified() const + { + return mState==State_Modified || mState==State_ModifiedOnly; + } + + template + const ESXRecordT& Record::get() const + { + if (isDeleted()) + throw std::logic_error ("attempt to access a deleted record"); + + return mState==State_BaseOnly ? mBase : mModified; + } + + template + ESXRecordT& Record::get() + { + if (isDeleted()) + throw std::logic_error ("attempt to access a deleted record"); + + return mState==State_BaseOnly ? mBase : mModified; + } + + template + void Record::setModified (const ESXRecordT& modified) + { + mModified = modified; + + if (mState!=State_ModifiedOnly) + mState = State_Modified; + } +} + +#endif diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index b56a336a6..e2d8d1ffb 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -17,6 +17,7 @@ namespace static const TypeData sNoArg[] = { { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty" }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker }; @@ -117,6 +118,23 @@ bool CSMWorld::UniversalId::isEqual (const UniversalId& universalId) const } } +bool CSMWorld::UniversalId::isLess (const UniversalId& universalId) const +{ + if (mTypeuniversalId.mType) + return false; + + switch (mArgumentType) + { + case ArgumentType_Id: return mId +#include #include #include @@ -10,6 +11,7 @@ #include "../../model/doc/document.hpp" #include "../world/subview.hpp" +#include "../world/globals.hpp" #include "viewmanager.hpp" #include "operations.hpp" @@ -59,16 +61,16 @@ void CSVDoc::View::setupViewMenu() QAction *newWindow = new QAction (tr ("&New View"), this); connect (newWindow, SIGNAL (triggered()), this, SLOT (newView())); view->addAction (newWindow); - - QAction *test = new QAction (tr ("Add test Subview"), this); - connect (test, SIGNAL (triggered()), this, SLOT (addTestSubView())); - view->addAction (test); } void CSVDoc::View::setupWorldMenu() { QMenu *world = menuBar()->addMenu (tr ("&World")); + QAction *globals = new QAction (tr ("Globals"), this); + connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView())); + world->addAction (globals); + mVerify = new QAction (tr ("&Verify"), this); connect (mVerify, SIGNAL (triggered()), this, SLOT (verify())); world->addAction (mVerify); @@ -124,6 +126,16 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to updateTitle(); setupUi(); + + mSubViewFactories.insert (std::make_pair (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), + new CSVWorld::SubViewFactory())); +} + +CSVDoc::View::~View() +{ + for (std::map::iterator iter (mSubViewFactories.begin()); + iter!=mSubViewFactories.end(); ++iter) + delete iter->second; } const CSMDoc::Document *CSVDoc::View::getDocument() const @@ -176,8 +188,13 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id) /// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis) - CSVWorld::SubView *view = new CSVWorld::SubView (id); - addDockWidget (Qt::RightDockWidgetArea, view); + std::map::iterator iter = mSubViewFactories.find (id); + + if (iter==mSubViewFactories.end()) + throw std::logic_error ("can't create subview for " + id.toString()); + + CSVWorld::SubView *view = iter->second->makeSubView (id, mDocument->getData()); + addDockWidget (Qt::TopDockWidgetArea, view); view->show(); } @@ -201,7 +218,7 @@ void CSVDoc::View::verify() mDocument->verify(); } -void CSVDoc::View::addTestSubView() +void CSVDoc::View::addGlobalsSubView() { - addSubView (CSMWorld::UniversalId::Type_None); + addSubView (CSMWorld::UniversalId::Type_Globals); } \ No newline at end of file diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 7a9164e12..8e2b27293 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -2,6 +2,7 @@ #define CSV_DOC_VIEW_H #include +#include #include @@ -17,6 +18,11 @@ namespace CSMWorld class UniversalId; } +namespace CSVWorld +{ + struct SubViewFactoryBase; +} + namespace CSVDoc { class ViewManager; @@ -36,6 +42,7 @@ namespace CSVDoc QAction *mVerify; std::vector mEditingActions; Operations *mOperations; + std::map mSubViewFactories; // not implemented View (const View&); @@ -64,6 +71,8 @@ namespace CSVDoc View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews); ///< The ownership of \a document is not transferred to *this. + virtual ~View(); + const CSMDoc::Document *getDocument() const; CSMDoc::Document *getDocument(); @@ -90,7 +99,7 @@ namespace CSVDoc void verify(); - void addTestSubView(); ///< \todo remove + void addGlobalsSubView(); }; } diff --git a/apps/opencs/view/world/globals.cpp b/apps/opencs/view/world/globals.cpp new file mode 100644 index 000000000..1c0faa029 --- /dev/null +++ b/apps/opencs/view/world/globals.cpp @@ -0,0 +1,26 @@ + +#include "globals.hpp" + +#include +#include +#include + +#include "../../model/world/data.hpp" + +CSVWorld::Globals::Globals (const CSMWorld::UniversalId& id, CSMWorld::Data& data) +: SubView (id) +{ + QTableView *table = new QTableView(); + + setWidget (table); + + QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel (this); + proxyModel->setSourceModel (data.getTableModel (id)); + + table->setModel (proxyModel); + table->horizontalHeader()->setResizeMode (QHeaderView::Interactive); + table->verticalHeader()->hide(); + table->setSortingEnabled (true); + + /// \todo make initial layout fill the whole width of the table +} \ No newline at end of file diff --git a/apps/opencs/view/world/globals.hpp b/apps/opencs/view/world/globals.hpp new file mode 100644 index 000000000..c54716255 --- /dev/null +++ b/apps/opencs/view/world/globals.hpp @@ -0,0 +1,17 @@ +#ifndef CSV_WORLD_GLOBALS_H +#define CSV_WORLD_GLOBALS_H + +#include "subview.hpp" + +namespace CSVWorld +{ + class Globals : public SubView + { + + public: + + Globals (const CSMWorld::UniversalId& id, CSMWorld::Data& data); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/world/subview.hpp b/apps/opencs/view/world/subview.hpp index 8488690c8..95cbe4243 100644 --- a/apps/opencs/view/world/subview.hpp +++ b/apps/opencs/view/world/subview.hpp @@ -5,6 +5,11 @@ #include +namespace CSMWorld +{ + class Data; +} + namespace CSVWorld { class SubView : public QDockWidget @@ -23,6 +28,23 @@ namespace CSVWorld CSMWorld::UniversalId getUniversalId() const; }; + + struct SubViewFactoryBase + { + virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data) = 0; + }; + + template + struct SubViewFactory : public SubViewFactoryBase + { + virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data); + }; + + template + SubView *SubViewFactory::makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data) + { + return new SubViewT (id, data); + } } #endif \ No newline at end of file diff --git a/components/esm/loadglob.hpp b/components/esm/loadglob.hpp index 302729d2e..5cfb3c87b 100644 --- a/components/esm/loadglob.hpp +++ b/components/esm/loadglob.hpp @@ -18,7 +18,7 @@ class ESMWriter; struct Global { std::string mId; - unsigned mValue; + float mValue; VarType mType; void load(ESMReader &esm); From d6dd212ce82741c668cab87898bda91cbc6564b8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 27 Nov 2012 06:54:13 +0100 Subject: [PATCH 039/378] GetWerewolfKills, ModScale, SetDelete, GetSquareRoot --- apps/openmw/mwscript/docs/vmformat.txt | 8 ++++- apps/openmw/mwscript/miscextensions.cpp | 33 +++++++++++++++++++ apps/openmw/mwscript/statsextensions.cpp | 16 +++++++++ .../mwscript/transformationextensions.cpp | 24 +++++++++++++- 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 26c87eeee..b4bc713db 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -275,7 +275,13 @@ op 0x20001de: HasSoulGem op 0x20001df: HasSoulGem, explicit op 0x20001e0: GetWeaponType op 0x20001e1: GetWeaponType, explicit -opcodes 0x20001e2-0x3ffffff unused +op 0x20001e2: GetWerewolfKills +op 0x20001e3: ModScale +op 0x20001e4: ModScale, explicit +op 0x20001e5: SetDelete +op 0x20001e6: SetDelete, explicit +op 0x20001e7: GetSquareRoot +opcodes 0x20001e3-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 1cd5ed35e..b59916cd4 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -357,6 +357,31 @@ namespace MWScript } }; + template + class OpSetDelete : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + MWBase::Environment::get().getWorld()->deleteObject (ptr); + } + }; + + class OpGetSquareRoot : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + float param = runtime[0].mFloat; + runtime.pop(); + + runtime.push(std::sqrt (param)); + } + }; + const int opcodeXBox = 0x200000c; const int opcodeOnActivate = 0x200000d; const int opcodeActivate = 0x2000075; @@ -387,6 +412,9 @@ namespace MWScript const int opcodeGetSpellEffects = 0x20001db; const int opcodeGetSpellEffectsExplicit = 0x20001dc; const int opcodeGetCurrentTime = 0x20001dd; + const int opcodeSetDelete = 0x20001e5; + const int opcodeSetDeleteExplicit = 0x20001e6; + const int opcodeGetSquareRoot = 0x20001e7; void registerExtensions (Compiler::Extensions& extensions) { @@ -419,6 +447,8 @@ namespace MWScript extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit); extensions.registerFunction ("getspelleffects", 'l', "c", opcodeGetSpellEffects, opcodeGetSpellEffectsExplicit); extensions.registerFunction ("getcurrenttime", 'f', "", opcodeGetCurrentTime); + extensions.registerInstruction ("setdelete", "", opcodeSetDelete, opcodeSetDeleteExplicit); + extensions.registerFunction ("getsquareroot", 'f', "f", opcodeGetSquareRoot); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -453,6 +483,9 @@ namespace MWScript interpreter.installSegment5 (opcodeGetSpellEffects, new OpGetSpellEffects); interpreter.installSegment5 (opcodeGetSpellEffectsExplicit, new OpGetSpellEffects); interpreter.installSegment5 (opcodeGetCurrentTime, new OpGetCurrentTime); + interpreter.installSegment5 (opcodeSetDelete, new OpSetDelete); + interpreter.installSegment5 (opcodeSetDeleteExplicit, new OpSetDelete); + interpreter.installSegment5 (opcodeGetSquareRoot, new OpGetSquareRoot); } } } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index bea41df1d..9fa315e2f 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -800,6 +800,18 @@ namespace MWScript } }; + class OpGetWerewolfKills : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer (); + + runtime.push (MWWorld::Class::get(ptr).getNpcStats (ptr).getWerewolfKills ()); + } + }; + const int numberOfAttributes = 8; const int opcodeGetAttribute = 0x2000027; @@ -872,6 +884,8 @@ namespace MWScript const int opcodeGetRace = 0x20001d9; const int opcodeGetRaceExplicit = 0x20001da; + const int opcodeGetWerewolfKills = 0x20001e2; + void registerExtensions (Compiler::Extensions& extensions) { static const char *attributes[numberOfAttributes] = @@ -975,6 +989,7 @@ namespace MWScript extensions.registerFunction ("getrace", 'l', "c", opcodeGetRace, opcodeGetRaceExplicit); + extensions.registerFunction ("getwerewolfkills", 'f', "", opcodeGetWerewolfKills); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -1073,6 +1088,7 @@ namespace MWScript interpreter.installSegment5 (opcodeGetRace, new OpGetRace); interpreter.installSegment5 (opcodeGetRaceExplicit, new OpGetRace); + interpreter.installSegment5 (opcodeGetWerewolfKills, new OpGetWerewolfKills); } } } diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index a64651a97..2dd8f3e16 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -53,6 +53,23 @@ namespace MWScript } }; + template + class OpModScale : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Float scale = runtime[0].mFloat; + runtime.pop(); + + // add the parameter to the object's scale. + MWBase::Environment::get().getWorld()->scaleObject(ptr,ptr.getCellRef().mScale + scale); + } + }; + template class OpSetAngle : public Interpreter::Opcode0 { @@ -532,6 +549,8 @@ namespace MWScript const int opcodePlaceAtPc = 0x200019c; const int opcodePlaceAtMe = 0x200019d; const int opcodePlaceAtMeExplicit = 0x200019e; + const int opcodeModScale = 0x20001e3; + const int opcodeModScaleExplicit = 0x20001e4; void registerExtensions (Compiler::Extensions& extensions) { @@ -548,6 +567,7 @@ namespace MWScript extensions.registerInstruction("placeitem","cffff",opcodePlaceItem); extensions.registerInstruction("placeatpc","clfl",opcodePlaceAtPc); extensions.registerInstruction("placeatme","clfl",opcodePlaceAtMe,opcodePlaceAtMeExplicit); + extensions.registerInstruction("modscale","f",opcodeModScale,opcodeModScaleExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -574,7 +594,9 @@ namespace MWScript interpreter.installSegment5(opcodePlaceItem,new OpPlaceItem); interpreter.installSegment5(opcodePlaceAtPc,new OpPlaceAtPc); interpreter.installSegment5(opcodePlaceAtMe,new OpPlaceAtMe); - interpreter.installSegment5(opcodePlaceAtMeExplicit,new OpPlaceAtMe); + interpreter.installSegment5(opcodePlaceAtMeExplicit,new OpPlaceAtMe); + interpreter.installSegment5(opcodeModScale,new OpModScale); + interpreter.installSegment5(opcodeModScaleExplicit,new OpModScale); } } } From 92f70635a2bb7d26ffecd3582cfd6c18ae01516b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 27 Nov 2012 10:42:46 +0100 Subject: [PATCH 040/378] improved selection behaviour --- apps/opencs/view/world/globals.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/opencs/view/world/globals.cpp b/apps/opencs/view/world/globals.cpp index 1c0faa029..1f68a57fe 100644 --- a/apps/opencs/view/world/globals.cpp +++ b/apps/opencs/view/world/globals.cpp @@ -21,6 +21,8 @@ CSVWorld::Globals::Globals (const CSMWorld::UniversalId& id, CSMWorld::Data& dat table->horizontalHeader()->setResizeMode (QHeaderView::Interactive); table->verticalHeader()->hide(); table->setSortingEnabled (true); + table->setSelectionBehavior (QAbstractItemView::SelectRows); + table->setSelectionMode (QAbstractItemView::ExtendedSelection); /// \todo make initial layout fill the whole width of the table } \ No newline at end of file From 8b18d195bdeab4e3a6f6bd7329d7fc7d8f9437d5 Mon Sep 17 00:00:00 2001 From: Greendogo Date: Tue, 27 Nov 2012 04:50:49 -0600 Subject: [PATCH 041/378] Update apps/openmw/main.cpp Edited the description for the --script-run switch. --- apps/openmw/main.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 2da52311f..0563fdbbb 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -131,9 +131,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ->default_value(false), "enable console-only script functionality") ("script-run", bpo::value()->default_value(""), - "select a file that is executed in the console on startup\n\n" - "Note: The file contains a list of script lines, but not a complete scripts. " - "That means no begin/end and no variable declarations.") + "select a file containing a list of console commands that is executed on startup") ("new-game", bpo::value()->implicit_value(true) ->default_value(false), "activate char gen/new game mechanics") From 43d8c6c4841b6115340e6b5706051bb8e7fd3ebf Mon Sep 17 00:00:00 2001 From: Greendogo Date: Tue, 27 Nov 2012 05:15:03 -0600 Subject: [PATCH 042/378] Update readme.txt --- readme.txt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/readme.txt b/readme.txt index 9a9010994..196105393 100644 --- a/readme.txt +++ b/readme.txt @@ -69,12 +69,9 @@ Allowed options: --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue scri pts) at startup --script-console [=arg(=1)] (=0) enable console-only script functionality - --script-run arg select a file that is executed in the consol - e on startup + --script-run arg select a file containing a list of console + commands that is executed on startup - Note: The file contains a list of script - lines, but not a complete scripts. That mean - s no begin/end and no variable declarations. --new-game [=arg(=1)] (=0) activate char gen/new game mechanics --fs-strict [=arg(=1)] (=0) strict file system handling (no case folding ) From 5eb0f489e20e0102fa57b77fbabadd7fe5828d69 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 27 Nov 2012 18:39:12 +0100 Subject: [PATCH 043/378] Issue 476: fixed auto-move bypassing disabled player controls --- apps/openmw/mwinput/inputmanagerimp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index af30c9b04..6a1c3aa9b 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -624,7 +624,9 @@ namespace MWInput void InputManager::toggleAutoMove() { if (mWindows.isGuiMode()) return; - mPlayer.setAutoMove (!mPlayer.getAutoMove()); + + if (mControlSwitch["playercontrols"]) + mPlayer.setAutoMove (!mPlayer.getAutoMove()); } void InputManager::toggleWalking() From 644dacf6037629ddabcf82c5a02929bfd5652a0c Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 28 Nov 2012 01:30:18 +0100 Subject: [PATCH 044/378] suggestion by Zini for SetDelete --- apps/openmw/mwscript/miscextensions.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index b59916cd4..b687471aa 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -365,7 +365,16 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); - MWBase::Environment::get().getWorld()->deleteObject (ptr); + int parameter = runtime[0].mInteger; + runtime.pop(); + + if (parameter == 1) + { + if (ptr.isInCell()) + MWBase::Environment::get().getWorld()->deleteObject (ptr); + else + ptr.getRefData().setCount(0); + } } }; @@ -447,7 +456,7 @@ namespace MWScript extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit); extensions.registerFunction ("getspelleffects", 'l', "c", opcodeGetSpellEffects, opcodeGetSpellEffectsExplicit); extensions.registerFunction ("getcurrenttime", 'f', "", opcodeGetCurrentTime); - extensions.registerInstruction ("setdelete", "", opcodeSetDelete, opcodeSetDeleteExplicit); + extensions.registerInstruction ("setdelete", "l", opcodeSetDelete, opcodeSetDeleteExplicit); extensions.registerFunction ("getsquareroot", 'f', "f", opcodeGetSquareRoot); } From 1def60dbe178aa9563c255be4e97001e4ad73518 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 28 Nov 2012 02:15:34 +0100 Subject: [PATCH 045/378] PcExpell, PcExpelled, PcClearExpelled, RaiseRank, LowerRank --- apps/openmw/mwgui/stats_window.cpp | 70 +++++--- apps/openmw/mwgui/stats_window.hpp | 2 + apps/openmw/mwmechanics/npcstats.cpp | 2 +- apps/openmw/mwscript/docs/vmformat.txt | 15 +- apps/openmw/mwscript/statsextensions.cpp | 200 +++++++++++++++++++++++ 5 files changed, 261 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index 38a06940f..70ceed857 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -252,6 +252,7 @@ void StatsWindow::onFrame () } setFactions(PCstats.getFactionRanks()); + setExpelled(PCstats.getExpelled ()); const std::string &signId = MWBase::Environment::get().getWorld()->getPlayer().getBirthSign(); @@ -273,6 +274,15 @@ void StatsWindow::setFactions (const FactionList& factions) } } +void StatsWindow::setExpelled (const std::set& expelled) +{ + if (mExpelled != expelled) + { + mExpelled = expelled; + mChanged = true; + } +} + void StatsWindow::setBirthSign (const std::string& signId) { if (signId != mBirthSignId) @@ -462,6 +472,10 @@ void StatsWindow::updateSkillArea() if (!mSkillWidgets.empty()) addSeparator(coord1, coord2); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); + std::set& expelled = PCstats.getExpelled (); + addGroup(mWindowManager.getGameSettingString("sFaction", "Faction"), coord1, coord2); FactionList::const_iterator end = mFactions.end(); for (FactionList::const_iterator it = mFactions.begin(); it != end; ++it) @@ -473,36 +487,42 @@ void StatsWindow::updateSkillArea() std::string text; text += std::string("#DDC79E") + faction->mName; - text += std::string("\n#BF9959") + faction->mRanks[it->second]; - if (it->second < 9) + if (expelled.find(it->first) != expelled.end()) + text += "\n#{sExpelled}"; + else { - // player doesn't have max rank yet - text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->mRanks[it->second+1]; - - ESM::RankData rankData = faction->mData.mRankData[it->second+1]; - const ESM::Attribute* attr1 = store.get().find(faction->mData.mAttribute1); - const ESM::Attribute* attr2 = store.get().find(faction->mData.mAttribute2); - assert(attr1 && attr2); + text += std::string("\n#BF9959") + faction->mRanks[it->second]; - text += "\n#BF9959#{" + attr1->mName + "}: " + boost::lexical_cast(rankData.mAttribute1) - + ", #{" + attr2->mName + "}: " + boost::lexical_cast(rankData.mAttribute2); - - text += "\n\n#DDC79E#{sFavoriteSkills}"; - text += "\n#BF9959"; - for (int i=0; i<6; ++i) + if (it->second < 9) { - text += "#{"+ESM::Skill::sSkillNameIds[faction->mData.mSkillID[i]]+"}"; - if (i<5) - text += ", "; + // player doesn't have max rank yet + text += std::string("\n\n#DDC79E#{sNextRank} ") + faction->mRanks[it->second+1]; + + ESM::RankData rankData = faction->mData.mRankData[it->second+1]; + const ESM::Attribute* attr1 = store.get().find(faction->mData.mAttribute1); + const ESM::Attribute* attr2 = store.get().find(faction->mData.mAttribute2); + assert(attr1 && attr2); + + text += "\n#BF9959#{" + attr1->mName + "}: " + boost::lexical_cast(rankData.mAttribute1) + + ", #{" + attr2->mName + "}: " + boost::lexical_cast(rankData.mAttribute2); + + text += "\n\n#DDC79E#{sFavoriteSkills}"; + text += "\n#BF9959"; + for (int i=0; i<6; ++i) + { + text += "#{"+ESM::Skill::sSkillNameIds[faction->mData.mSkillID[i]]+"}"; + if (i<5) + text += ", "; + } + + text += "\n"; + + if (rankData.mSkill1 > 0) + text += "\n#{sNeedOneSkill} " + boost::lexical_cast(rankData.mSkill1); + if (rankData.mSkill2 > 0) + text += "\n#{sNeedTwoSkills} " + boost::lexical_cast(rankData.mSkill2); } - - text += "\n"; - - if (rankData.mSkill1 > 0) - text += "\n#{sNeedOneSkill} " + boost::lexical_cast(rankData.mSkill1); - if (rankData.mSkill2 > 0) - text += "\n#{sNeedTwoSkills} " + boost::lexical_cast(rankData.mSkill2); } w->setUserString("ToolTipType", "Layout"); diff --git a/apps/openmw/mwgui/stats_window.hpp b/apps/openmw/mwgui/stats_window.hpp index f43682c96..6619680fa 100644 --- a/apps/openmw/mwgui/stats_window.hpp +++ b/apps/openmw/mwgui/stats_window.hpp @@ -50,6 +50,7 @@ namespace MWGui MyGUI::Widget* addItem(const std::string& text, MyGUI::IntCoord &coord1, MyGUI::IntCoord &coord2); void setFactions (const FactionList& factions); + void setExpelled (const std::set& expelled); void setBirthSign (const std::string &signId); void onWindowResize(MyGUI::Window* window); @@ -71,6 +72,7 @@ namespace MWGui std::string mBirthSignId; int mReputation, mBounty; std::vector mSkillWidgets; //< Skills and other information + std::set mExpelled; bool mChanged; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 20a0360e7..ca58f4825 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -350,4 +350,4 @@ void MWMechanics::NpcStats::setWerewolf (bool set) int MWMechanics::NpcStats::getWerewolfKills() const { return mWerewolfKills; -} \ No newline at end of file +} diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index b4bc713db..835a3e3ff 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -37,7 +37,13 @@ op 0x20014: SetPCFacRep op 0x20015: SetPCFacRep, explicit reference op 0x20016: ModPCFacRep op 0x20017: ModPCFacRep, explicit reference -op s 0x20018-0x3ffff unused +op 0x20018: PcExpelled +op 0x20019: PcExpelled, explicit +op 0x2001a: PcExpell +op 0x2001b: PcExpell, explicit +op 0x2001c: PcClearExpelled +op 0x2001d: PcClearExpelled, explicit +op s 0x2001e-0x3ffff unused Segment 4: (not implemented yet) @@ -281,7 +287,12 @@ op 0x20001e4: ModScale, explicit op 0x20001e5: SetDelete op 0x20001e6: SetDelete, explicit op 0x20001e7: GetSquareRoot -opcodes 0x20001e3-0x3ffffff unused +op 0x20001e8: RaiseRank +op 0x20001e9: RaiseRank, explicit +op 0x20001ea: LowerRank +op 0x20001eb: LowerRank, explicit + +opcodes 0x20001ec-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 9fa315e2f..69ac7d5fe 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -812,6 +812,179 @@ namespace MWScript } }; + template + class OpPcExpelled : public Interpreter::Opcode1 + { + public: + + virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string factionID = ""; + if(arg0 >0 ) + { + factionID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + } + else + { + if(MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().empty()) + { + factionID = ""; + } + else + { + factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; + } + } + boost::algorithm::to_lower(factionID); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + if(factionID!="") + { + std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); + if (expelled.find (factionID) != expelled.end()) + { + runtime.push(1); + } + else + { + runtime.push(0); + } + } + else + { + runtime.push(0); + } + } + }; + + template + class OpPcExpell : public Interpreter::Opcode1 + { + public: + + virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string factionID = ""; + if(arg0 >0 ) + { + factionID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + } + else + { + if(MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().empty()) + { + factionID = ""; + } + else + { + factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; + } + } + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + if(factionID!="") + { + std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); + boost::algorithm::to_lower(factionID); + expelled.insert(factionID); + } + } + }; + + template + class OpPcClearExpelled : public Interpreter::Opcode1 + { + public: + + virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string factionID = ""; + if(arg0 >0 ) + { + factionID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + } + else + { + if(MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().empty()) + { + factionID = ""; + } + else + { + factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; + } + } + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + if(factionID!="") + { + std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); + boost::algorithm::to_lower(factionID); + expelled.erase (factionID); + } + } + }; + + template + class OpRaiseRank : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string factionID = ""; + if(MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().empty()) + return; + else + { + factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; + } + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + + // no-op when executed on the player + if (ptr == player) + return; + + std::map& ranks = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks (); + ranks[factionID] = ranks[factionID]+1; + } + }; + + template + class OpLowerRank : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string factionID = ""; + if(MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().empty()) + return; + else + { + factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; + } + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + + // no-op when executed on the player + if (ptr == player) + return; + + std::map& ranks = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks (); + ranks[factionID] = ranks[factionID]-1; + } + }; + const int numberOfAttributes = 8; const int opcodeGetAttribute = 0x2000027; @@ -886,6 +1059,17 @@ namespace MWScript const int opcodeGetWerewolfKills = 0x20001e2; + const int opcodePcExpelled = 0x20018; + const int opcodePcExpelledExplicit = 0x20019; + const int opcodePcExpell = 0x2001a; + const int opcodePcExpellExplicit = 0x2001b; + const int opcodePcClearExpelled = 0x2001c; + const int opcodePcClearExpelledExplicit = 0x2001d; + const int opcodeRaiseRank = 0x20001e8; + const int opcodeRaiseRankExplicit = 0x20001e9; + const int opcodeLowerRank = 0x20001ea; + const int opcodeLowerRankExplicit = 0x20001eb; + void registerExtensions (Compiler::Extensions& extensions) { static const char *attributes[numberOfAttributes] = @@ -990,6 +1174,11 @@ namespace MWScript extensions.registerFunction ("getrace", 'l', "c", opcodeGetRace, opcodeGetRaceExplicit); extensions.registerFunction ("getwerewolfkills", 'f', "", opcodeGetWerewolfKills); + extensions.registerFunction ("pcexpelled", 'l', "/S", opcodePcExpelled, opcodePcExpelledExplicit); + extensions.registerInstruction ("pcexpell", "/S", opcodePcExpell, opcodePcExpellExplicit); + extensions.registerInstruction ("pcclearexpelled", "/S", opcodePcClearExpelled, opcodePcClearExpelledExplicit); + extensions.registerInstruction ("raiserank", "", opcodeRaiseRank, opcodeRaiseRankExplicit); + extensions.registerInstruction ("lowerrank", "", opcodeLowerRank, opcodeLowerRankExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -1089,6 +1278,17 @@ namespace MWScript interpreter.installSegment5 (opcodeGetRace, new OpGetRace); interpreter.installSegment5 (opcodeGetRaceExplicit, new OpGetRace); interpreter.installSegment5 (opcodeGetWerewolfKills, new OpGetWerewolfKills); + + interpreter.installSegment3 (opcodePcExpelled, new OpPcExpelled); + interpreter.installSegment3 (opcodePcExpelledExplicit, new OpPcExpelled); + interpreter.installSegment3 (opcodePcExpell, new OpPcExpell); + interpreter.installSegment3 (opcodePcExpellExplicit, new OpPcExpell); + interpreter.installSegment3 (opcodePcClearExpelled, new OpPcClearExpelled); + interpreter.installSegment3 (opcodePcClearExpelledExplicit, new OpPcClearExpelled); + interpreter.installSegment5 (opcodeRaiseRank, new OpRaiseRank); + interpreter.installSegment5 (opcodeRaiseRankExplicit, new OpRaiseRank); + interpreter.installSegment5 (opcodeLowerRank, new OpLowerRank); + interpreter.installSegment5 (opcodeLowerRankExplicit, new OpLowerRank); } } } From 8e857587d76d73fe5eff5be98c0572637efa2281 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 28 Nov 2012 02:36:33 +0100 Subject: [PATCH 046/378] fix a text coloring bug --- apps/openmw/mwgui/dialogue.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 827da1085..258e9174c 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -316,6 +316,22 @@ void addColorInString(std::string& str, const std::string& keyword,std::string c size_t pos = 0; while((pos = find_str_ci(str,keyword, pos)) != std::string::npos) { + // do not add color if this portion of text is already colored. + { + MyGUI::TextIterator iterator (str); + MyGUI::UString colour; + while(iterator.moveNext()) + { + size_t iteratorPos = iterator.getPosition(); + iterator.getTagColour(colour); + if (iteratorPos == pos) + break; + } + + if (colour == color1) + return; + } + str.insert(pos,color1); pos += color1.length(); pos += keyword.length(); From 00e46addc29de75c53ce81df1f3822fb6e999f9a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 28 Nov 2012 02:48:21 +0100 Subject: [PATCH 047/378] GetArmorType fix --- apps/openmw/mwscript/containerextensions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 530051b09..df2568fa4 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -212,7 +212,7 @@ namespace MWScript MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore (ptr); MWWorld::ContainerStoreIterator it = invStore.getSlot (slot); - if (it == invStore.end()) + if (it == invStore.end() || it->getTypeName () != typeid(ESM::Armor).name()) { runtime.push(-1); return; From 398eecc35e38b93b322b939b2d2dd0c10a13d9e7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 28 Nov 2012 02:51:46 +0100 Subject: [PATCH 048/378] GetWeaponType fix --- apps/openmw/mwscript/containerextensions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index df2568fa4..7f4913b8a 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -294,7 +294,7 @@ namespace MWScript MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore (ptr); MWWorld::ContainerStoreIterator it = invStore.getSlot (MWWorld::InventoryStore::Slot_CarriedRight); - if (it == invStore.end()) + if (it == invStore.end() || it->getTypeName () != typeid(ESM::Weapon).name()) { runtime.push(-1); return; From 8a09e03d5c6b369bffcac7dbb16916151eb2c548 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 29 Nov 2012 14:45:34 +0100 Subject: [PATCH 049/378] global variable editing (no undo support yet) --- apps/opencs/model/world/columns.hpp | 12 +++++++++ apps/opencs/model/world/idcollection.hpp | 26 ++++++++++++++++++- apps/opencs/model/world/idtable.cpp | 33 +++++++++++++++++++++++- apps/opencs/model/world/idtable.hpp | 4 +++ apps/opencs/model/world/record.hpp | 18 ++++++++----- components/esm/loadglob.cpp | 4 +++ components/esm/loadglob.hpp | 3 +++ 7 files changed, 91 insertions(+), 9 deletions(-) diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 56362e4cd..4d0ecb58e 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -14,6 +14,18 @@ namespace CSMWorld { return record.get().mValue; } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT base = record.getBase(); + base.mValue = data.toFloat(); + record.setModified (base); + } + + virtual bool isEditable() const + { + return true; + } }; } diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 0bddda1ac..ee6a4b2ce 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -23,6 +23,10 @@ namespace CSMWorld virtual ~Column() {} virtual QVariant get (const Record& record) const = 0; + + virtual void set (Record& record, const QVariant& data) = 0; + + virtual bool isEditable() const = 0; }; class IdCollectionBase @@ -46,6 +50,10 @@ namespace CSMWorld virtual std::string getTitle (int column) const = 0; virtual QVariant getData (int index, int column) const = 0; + + virtual void setData (int index, int column, const QVariant& data) = 0; + + virtual bool isEditable (int column) const = 0; }; ///< \brief Collection of ID-based records @@ -77,9 +85,13 @@ namespace CSMWorld virtual QVariant getData (int index, int column) const; + virtual void setData (int index, int column, const QVariant& data); + virtual std::string getTitle (int column) const; - virtual void addColumn (Column *column); + virtual bool isEditable (int column) const; + + void addColumn (Column *column); }; template @@ -142,12 +154,24 @@ namespace CSMWorld return mColumns.at (column)->get (mRecords.at (index)); } + template + void IdCollection::setData (int index, int column, const QVariant& data) + { + return mColumns.at (column)->set (mRecords.at (index), data); + } + template std::string IdCollection::getTitle (int column) const { return mColumns.at (column)->mTitle; } + template + bool IdCollection::isEditable (int column) const + { + return mColumns.at (column)->isEditable(); + } + template void IdCollection::addColumn (Column *column) { diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index f8acd4f9f..8fdc4fb8f 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -31,9 +31,18 @@ int CSMWorld::IdTable::columnCount (const QModelIndex & parent) const QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const { - if (role!=Qt::DisplayRole) + if (role!=Qt::DisplayRole && role!=Qt::EditRole) return QVariant(); + if (role==Qt::EditRole) + { + if (index.column()==0) + return QVariant(); + + if (!mIdCollection->isEditable (index.column()-1)) + return QVariant(); + } + if (index.column()==0) return QVariant (tr (mIdCollection->getId (index.row()).c_str())); @@ -52,4 +61,26 @@ QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation return QVariant (tr ("ID")); return tr (mIdCollection->getTitle (section-1).c_str()); +} + +bool CSMWorld::IdTable::setData ( const QModelIndex &index, const QVariant &value, int role) +{ + if (index.column()>0 && role==Qt::EditRole) + { + mIdCollection->setData (index.row(), index.column()-1, value); + emit dataChanged (index, index); + return true; + } + + return false; +} + +Qt::ItemFlags CSMWorld::IdTable::flags (const QModelIndex & index) const +{ + Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; + + if (index.column()>0) + flags |= Qt::ItemIsEditable; + + return flags; } \ No newline at end of file diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 4fb939fa3..30af3aaf7 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -31,6 +31,10 @@ namespace CSMWorld QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + bool setData ( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + Qt::ItemFlags flags (const QModelIndex & index) const; }; } diff --git a/apps/opencs/model/world/record.hpp b/apps/opencs/model/world/record.hpp index 230d39eb0..950d7f176 100644 --- a/apps/opencs/model/world/record.hpp +++ b/apps/opencs/model/world/record.hpp @@ -8,7 +8,7 @@ namespace CSMWorld template struct Record { - enum state + enum State { State_BaseOnly, // defined in base only State_Modified, // exists in base, but has been modified @@ -18,7 +18,7 @@ namespace CSMWorld ESXRecordT mBase; ESXRecordT mModified; - state mState; + State mState; bool isDeleted() const; @@ -27,10 +27,11 @@ namespace CSMWorld const ESXRecordT& get() const; ///< Throws an exception, if the record is deleted. - ESXRecordT& get(); - ///< Throws an exception, if the record is deleted. + const ESXRecordT& getBase() const; + ///< Throws an exception, if the record is deleted. Returns modified, if there is no base. void setModified (const ESXRecordT& modified); + ///< Throws an exception, if the record is deleted. }; template @@ -55,21 +56,24 @@ namespace CSMWorld } template - ESXRecordT& Record::get() + const ESXRecordT& Record::getBase() const { if (isDeleted()) throw std::logic_error ("attempt to access a deleted record"); - return mState==State_BaseOnly ? mBase : mModified; + return mState==State_ModifiedOnly ? mModified : mBase; } template void Record::setModified (const ESXRecordT& modified) { + if (isDeleted()) + throw std::logic_error ("attempt to modify a deleted record"); + mModified = modified; if (mState!=State_ModifiedOnly) - mState = State_Modified; + mState = mBase==mModified ? State_BaseOnly : State_Modified; } } diff --git a/components/esm/loadglob.cpp b/components/esm/loadglob.cpp index 39c07fb31..0335da0df 100644 --- a/components/esm/loadglob.cpp +++ b/components/esm/loadglob.cpp @@ -44,4 +44,8 @@ void Global::save(ESMWriter &esm) esm.writeHNT("FLTV", mValue); } + bool operator== (const Global& left, const Global& right) + { + return left.mId==right.mId && left.mValue==right.mValue && left.mType==right.mType; + } } diff --git a/components/esm/loadglob.hpp b/components/esm/loadglob.hpp index 5cfb3c87b..5c5dafaec 100644 --- a/components/esm/loadglob.hpp +++ b/components/esm/loadglob.hpp @@ -24,5 +24,8 @@ struct Global void load(ESMReader &esm); void save(ESMWriter &esm); }; + +bool operator== (const Global& left, const Global& right); + } #endif From 4086b556d242280d7ad2e8bd030e7118dab5d57d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 29 Nov 2012 16:05:28 +0100 Subject: [PATCH 050/378] use commands for modifying globals --- apps/opencs/CMakeLists.txt | 2 + apps/opencs/model/world/commands.cpp | 23 +++++++++++ apps/opencs/model/world/commands.hpp | 33 ++++++++++++++++ apps/opencs/view/doc/view.cpp | 2 +- apps/opencs/view/world/globals.cpp | 59 +++++++++++++++++++++++++++- apps/opencs/view/world/globals.hpp | 42 +++++++++++++++++++- apps/opencs/view/world/subview.hpp | 12 ++++-- 7 files changed, 164 insertions(+), 9 deletions(-) create mode 100644 apps/opencs/model/world/commands.cpp create mode 100644 apps/opencs/model/world/commands.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 92fd859be..61580461f 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -5,6 +5,7 @@ set (OPENCS_SRC model/doc/documentmanager.cpp model/doc/document.cpp model/world/universalid.cpp model/world/idcollection.cpp model/world/data.cpp model/world/idtable.cpp + model/world/commands.cpp view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp @@ -20,6 +21,7 @@ set (OPENCS_HDR model/world/idtable.hpp model/world/columns.hpp view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp + model/world/commands.hpp view/world/subview.hpp view/world/globals.hpp ) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp new file mode 100644 index 000000000..a15d00619 --- /dev/null +++ b/apps/opencs/model/world/commands.cpp @@ -0,0 +1,23 @@ + +#include "commands.hpp" + +#include + +CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, + const QVariant& new_, QUndoCommand *parent) +: QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_) +{ + mOld = mModel.data (mIndex, Qt::EditRole); + + setText ("Modify " + mModel.headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString()); +} + +void CSMWorld::ModifyCommand::redo() +{ + mModel.setData (mIndex, mNew); +} + +void CSMWorld::ModifyCommand::undo() +{ + mModel.setData (mIndex, mOld); +} \ No newline at end of file diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp new file mode 100644 index 000000000..ffaee59ef --- /dev/null +++ b/apps/opencs/model/world/commands.hpp @@ -0,0 +1,33 @@ +#ifndef CSM_WOLRD_COMMANDS_H +#define CSM_WOLRD_COMMANDS_H + +#include "record.hpp" + +#include +#include +#include + +class QModelIndex; +class QAbstractItemModel; + +namespace CSMWorld +{ + class ModifyCommand : public QUndoCommand + { + QAbstractItemModel& mModel; + QModelIndex mIndex; + QVariant mNew; + QVariant mOld; + + public: + + ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, + QUndoCommand *parent = 0); + + virtual void redo(); + + virtual void undo(); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 18d58bbf5..c81ba2ba1 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -193,7 +193,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id) if (iter==mSubViewFactories.end()) throw std::logic_error ("can't create subview for " + id.toString()); - CSVWorld::SubView *view = iter->second->makeSubView (id, mDocument->getData()); + CSVWorld::SubView *view = iter->second->makeSubView (id, mDocument->getData(), mDocument->getUndoStack()); addDockWidget (Qt::TopDockWidgetArea, view); view->show(); } diff --git a/apps/opencs/view/world/globals.cpp b/apps/opencs/view/world/globals.cpp index 1f68a57fe..a57f63dff 100644 --- a/apps/opencs/view/world/globals.cpp +++ b/apps/opencs/view/world/globals.cpp @@ -4,18 +4,73 @@ #include #include #include +#include #include "../../model/world/data.hpp" -CSVWorld::Globals::Globals (const CSMWorld::UniversalId& id, CSMWorld::Data& data) +#include "../../model/world/commands.hpp" + +CSVWorld::NastyTableModelHack::NastyTableModelHack (QAbstractItemModel& model) +: mModel (model) +{} + +int CSVWorld::NastyTableModelHack::rowCount (const QModelIndex & parent) const +{ + return mModel.rowCount (parent); +} + +int CSVWorld::NastyTableModelHack::columnCount (const QModelIndex & parent) const +{ + return mModel.columnCount (parent); +} + +QVariant CSVWorld::NastyTableModelHack::data (const QModelIndex & index, int role) const +{ + return mModel.data (index, role); +} + +bool CSVWorld::NastyTableModelHack::setData ( const QModelIndex &index, const QVariant &value, int role) +{ + mData = value; + return true; +} + +QVariant CSVWorld::NastyTableModelHack::getData() const +{ + return mData; +} + +CSVWorld::CommandDelegate::CommandDelegate (QUndoStack& undoStack, QObject *parent) +: QStyledItemDelegate (parent), mUndoStack (undoStack) +{} + +void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemModel *model, + const QModelIndex& index) const +{ + NastyTableModelHack hack (*model); + QStyledItemDelegate::setModelData (editor, &hack, index); + mUndoStack.push (new CSMWorld::ModifyCommand (*model, index, hack.getData())); +} + +CSVWorld::Globals::Globals (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack) : SubView (id) { QTableView *table = new QTableView(); setWidget (table); + QAbstractTableModel *model = data.getTableModel (id); + + int columns = model->columnCount(); + + for (int i=1; isetItemDelegateForColumn (i, delegate); + } + QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel (this); - proxyModel->setSourceModel (data.getTableModel (id)); + proxyModel->setSourceModel (model); table->setModel (proxyModel); table->horizontalHeader()->setResizeMode (QHeaderView::Interactive); diff --git a/apps/opencs/view/world/globals.hpp b/apps/opencs/view/world/globals.hpp index c54716255..11ab7b6aa 100644 --- a/apps/opencs/view/world/globals.hpp +++ b/apps/opencs/view/world/globals.hpp @@ -3,14 +3,52 @@ #include "subview.hpp" +#include + +class QUndoStack; + namespace CSVWorld { - class Globals : public SubView + ///< \brief Getting the data out of an editor widget + /// + /// Really, Qt? Really? + class NastyTableModelHack : public QAbstractTableModel { + QAbstractItemModel& mModel; + QVariant mData; + + public: + + NastyTableModelHack (QAbstractItemModel& model); + int rowCount (const QModelIndex & parent = QModelIndex()) const; + + int columnCount (const QModelIndex & parent = QModelIndex()) const; + + QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; + + bool setData (const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + QVariant getData() const; + }; + + ///< \brief Use commands instead of manipulating the model directly + class CommandDelegate : public QStyledItemDelegate + { + QUndoStack& mUndoStack; + + public: + + CommandDelegate (QUndoStack& undoStack, QObject *parent); + + void setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const; + }; + + class Globals : public SubView + { public: - Globals (const CSMWorld::UniversalId& id, CSMWorld::Data& data); + Globals (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack); }; } diff --git a/apps/opencs/view/world/subview.hpp b/apps/opencs/view/world/subview.hpp index 95cbe4243..fd7a51631 100644 --- a/apps/opencs/view/world/subview.hpp +++ b/apps/opencs/view/world/subview.hpp @@ -5,6 +5,8 @@ #include +class QUndoStack; + namespace CSMWorld { class Data; @@ -31,19 +33,21 @@ namespace CSVWorld struct SubViewFactoryBase { - virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data) = 0; + virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack) + = 0; }; template struct SubViewFactory : public SubViewFactoryBase { - virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data); + virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack); }; template - SubView *SubViewFactory::makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data) + SubView *SubViewFactory::makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data, + QUndoStack& undoStack) { - return new SubViewT (id, data); + return new SubViewT (id, data, undoStack); } } From fd55c0cae2603562c7a648bf62c70bbbd33976a3 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 29 Nov 2012 18:56:28 +0100 Subject: [PATCH 051/378] record IDs are no longer handled as a special case --- apps/opencs/model/world/columns.hpp | 16 +++++++++++++++ apps/opencs/model/world/data.cpp | 1 + apps/opencs/model/world/idcollection.hpp | 6 +++++- apps/opencs/model/world/idtable.cpp | 26 +++++++----------------- apps/opencs/view/world/globals.cpp | 2 +- 5 files changed, 30 insertions(+), 21 deletions(-) diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 4d0ecb58e..3220b3bba 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -28,6 +28,22 @@ namespace CSMWorld } }; + template + struct StringIdColumn : public Column + { + StringIdColumn() : Column ("ID") {} + + virtual QVariant get (const Record& record) const + { + return record.get().mId.c_str(); + } + + virtual bool isEditable() const + { + return false; + } + }; + } #endif \ No newline at end of file diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 7fdb149b3..aeafc1676 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -12,6 +12,7 @@ CSMWorld::Data::Data() { + mGlobals.addColumn (new StringIdColumn); mGlobals.addColumn (new FloatValueColumn); mModels.insert (std::make_pair ( diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index ee6a4b2ce..41cd352ce 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -24,7 +25,10 @@ namespace CSMWorld virtual QVariant get (const Record& record) const = 0; - virtual void set (Record& record, const QVariant& data) = 0; + virtual void set (Record& record, const QVariant& data) + { + throw std::logic_error ("Column " + mTitle + " is not editable"); + } virtual bool isEditable() const = 0; }; diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 8fdc4fb8f..1fc2bfedb 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -26,7 +26,7 @@ int CSMWorld::IdTable::columnCount (const QModelIndex & parent) const if (parent.isValid()) return 0; - return 1+mIdCollection->getColumns(); + return mIdCollection->getColumns(); } QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const @@ -34,19 +34,10 @@ QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const if (role!=Qt::DisplayRole && role!=Qt::EditRole) return QVariant(); - if (role==Qt::EditRole) - { - if (index.column()==0) - return QVariant(); - - if (!mIdCollection->isEditable (index.column()-1)) + if (role==Qt::EditRole && !mIdCollection->isEditable (index.column())) return QVariant(); - } - - if (index.column()==0) - return QVariant (tr (mIdCollection->getId (index.row()).c_str())); - return mIdCollection->getData (index.row(), index.column()-1); + return mIdCollection->getData (index.row(), index.column()); } QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation, int role) const @@ -57,17 +48,14 @@ QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation if (orientation==Qt::Vertical) return QVariant(); - if (section==0) - return QVariant (tr ("ID")); - - return tr (mIdCollection->getTitle (section-1).c_str()); + return tr (mIdCollection->getTitle (section).c_str()); } bool CSMWorld::IdTable::setData ( const QModelIndex &index, const QVariant &value, int role) { - if (index.column()>0 && role==Qt::EditRole) + if (mIdCollection->isEditable (index.column()) && role==Qt::EditRole) { - mIdCollection->setData (index.row(), index.column()-1, value); + mIdCollection->setData (index.row(), index.column(), value); emit dataChanged (index, index); return true; } @@ -79,7 +67,7 @@ Qt::ItemFlags CSMWorld::IdTable::flags (const QModelIndex & index) const { Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; - if (index.column()>0) + if (mIdCollection->isEditable (index.column())) flags |= Qt::ItemIsEditable; return flags; diff --git a/apps/opencs/view/world/globals.cpp b/apps/opencs/view/world/globals.cpp index a57f63dff..459b8c357 100644 --- a/apps/opencs/view/world/globals.cpp +++ b/apps/opencs/view/world/globals.cpp @@ -63,7 +63,7 @@ CSVWorld::Globals::Globals (const CSMWorld::UniversalId& id, CSMWorld::Data& dat int columns = model->columnCount(); - for (int i=1; isetItemDelegateForColumn (i, delegate); From 8dd76b49af58ee7822fd11145cf0694a11ca51b1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 29 Nov 2012 19:09:06 +0100 Subject: [PATCH 052/378] factored out table widget from globals sub view --- apps/opencs/CMakeLists.txt | 4 +- apps/opencs/view/doc/view.cpp | 11 --- apps/opencs/view/doc/view.hpp | 2 - apps/opencs/view/world/globals.cpp | 75 +----------------- apps/opencs/view/world/globals.hpp | 37 --------- apps/opencs/view/world/table.cpp | 118 +++++++++++++++++++++++++++++ apps/opencs/view/world/table.hpp | 25 ++++++ 7 files changed, 147 insertions(+), 125 deletions(-) create mode 100644 apps/opencs/view/world/table.cpp create mode 100644 apps/opencs/view/world/table.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 61580461f..dfd987345 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -9,7 +9,7 @@ set (OPENCS_SRC view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp - view/world/subview.cpp view/world/globals.cpp + view/world/subview.cpp view/world/table.cpp view/world/globals.cpp ) set (OPENCS_HDR @@ -23,7 +23,7 @@ set (OPENCS_HDR view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp model/world/commands.hpp - view/world/subview.hpp view/world/globals.hpp + view/world/subview.hpp view/world/table.hpp view/world/globals.hpp ) set (OPENCS_US diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index c81ba2ba1..0c336376b 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -46,12 +46,6 @@ void CSVDoc::View::setupEditMenu() mRedo= mDocument->getUndoStack().createRedoAction (this, tr("&Redo")); mRedo->setShortcuts (QKeySequence::Redo); edit->addAction (mRedo); - - // test - QAction *test = new QAction (tr ("&Test Command"), this); - connect (test, SIGNAL (triggered()), this, SLOT (test())); - edit->addAction (test); - mEditingActions.push_back (test); } void CSVDoc::View::setupViewMenu() @@ -203,11 +197,6 @@ void CSVDoc::View::newView() mViewManager.addView (mDocument); } -void CSVDoc::View::test() -{ - mDocument->getUndoStack().push (new QUndoCommand()); -} - void CSVDoc::View::save() { mDocument->save(); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 8e2b27293..c86c3b362 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -93,8 +93,6 @@ namespace CSVDoc void newView(); - void test(); - void save(); void verify(); diff --git a/apps/opencs/view/world/globals.cpp b/apps/opencs/view/world/globals.cpp index 459b8c357..a8b6497db 100644 --- a/apps/opencs/view/world/globals.cpp +++ b/apps/opencs/view/world/globals.cpp @@ -1,83 +1,12 @@ #include "globals.hpp" -#include -#include -#include -#include - -#include "../../model/world/data.hpp" - -#include "../../model/world/commands.hpp" - -CSVWorld::NastyTableModelHack::NastyTableModelHack (QAbstractItemModel& model) -: mModel (model) -{} - -int CSVWorld::NastyTableModelHack::rowCount (const QModelIndex & parent) const -{ - return mModel.rowCount (parent); -} - -int CSVWorld::NastyTableModelHack::columnCount (const QModelIndex & parent) const -{ - return mModel.columnCount (parent); -} - -QVariant CSVWorld::NastyTableModelHack::data (const QModelIndex & index, int role) const -{ - return mModel.data (index, role); -} - -bool CSVWorld::NastyTableModelHack::setData ( const QModelIndex &index, const QVariant &value, int role) -{ - mData = value; - return true; -} - -QVariant CSVWorld::NastyTableModelHack::getData() const -{ - return mData; -} - -CSVWorld::CommandDelegate::CommandDelegate (QUndoStack& undoStack, QObject *parent) -: QStyledItemDelegate (parent), mUndoStack (undoStack) -{} - -void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemModel *model, - const QModelIndex& index) const -{ - NastyTableModelHack hack (*model); - QStyledItemDelegate::setModelData (editor, &hack, index); - mUndoStack.push (new CSMWorld::ModifyCommand (*model, index, hack.getData())); -} +#include "table.hpp" CSVWorld::Globals::Globals (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack) : SubView (id) { - QTableView *table = new QTableView(); + QTableView *table = new Table (id, data, undoStack); setWidget (table); - - QAbstractTableModel *model = data.getTableModel (id); - - int columns = model->columnCount(); - - for (int i=0; isetItemDelegateForColumn (i, delegate); - } - - QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel (this); - proxyModel->setSourceModel (model); - - table->setModel (proxyModel); - table->horizontalHeader()->setResizeMode (QHeaderView::Interactive); - table->verticalHeader()->hide(); - table->setSortingEnabled (true); - table->setSelectionBehavior (QAbstractItemView::SelectRows); - table->setSelectionMode (QAbstractItemView::ExtendedSelection); - - /// \todo make initial layout fill the whole width of the table } \ No newline at end of file diff --git a/apps/opencs/view/world/globals.hpp b/apps/opencs/view/world/globals.hpp index 11ab7b6aa..c4a9fd402 100644 --- a/apps/opencs/view/world/globals.hpp +++ b/apps/opencs/view/world/globals.hpp @@ -3,47 +3,10 @@ #include "subview.hpp" -#include - class QUndoStack; namespace CSVWorld { - ///< \brief Getting the data out of an editor widget - /// - /// Really, Qt? Really? - class NastyTableModelHack : public QAbstractTableModel - { - QAbstractItemModel& mModel; - QVariant mData; - - public: - - NastyTableModelHack (QAbstractItemModel& model); - - int rowCount (const QModelIndex & parent = QModelIndex()) const; - - int columnCount (const QModelIndex & parent = QModelIndex()) const; - - QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; - - bool setData (const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - - QVariant getData() const; - }; - - ///< \brief Use commands instead of manipulating the model directly - class CommandDelegate : public QStyledItemDelegate - { - QUndoStack& mUndoStack; - - public: - - CommandDelegate (QUndoStack& undoStack, QObject *parent); - - void setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const; - }; - class Globals : public SubView { public: diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp new file mode 100644 index 000000000..2c9aa431f --- /dev/null +++ b/apps/opencs/view/world/table.cpp @@ -0,0 +1,118 @@ + +#include "table.hpp" + +#include +#include +#include +#include + +#include "../../model/world/data.hpp" + +#include "../../model/world/commands.hpp" + +namespace CSVWorld +{ + ///< \brief Getting the data out of an editor widget + /// + /// Really, Qt? Really? + class NastyTableModelHack : public QAbstractTableModel + { + QAbstractItemModel& mModel; + QVariant mData; + + public: + + NastyTableModelHack (QAbstractItemModel& model); + + int rowCount (const QModelIndex & parent = QModelIndex()) const; + + int columnCount (const QModelIndex & parent = QModelIndex()) const; + + QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; + + bool setData (const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + QVariant getData() const; + }; + + ///< \brief Use commands instead of manipulating the model directly + class CommandDelegate : public QStyledItemDelegate + { + QUndoStack& mUndoStack; + + public: + + CommandDelegate (QUndoStack& undoStack, QObject *parent); + + void setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const; + }; + +} + +CSVWorld::NastyTableModelHack::NastyTableModelHack (QAbstractItemModel& model) +: mModel (model) +{} + +int CSVWorld::NastyTableModelHack::rowCount (const QModelIndex & parent) const +{ + return mModel.rowCount (parent); +} + +int CSVWorld::NastyTableModelHack::columnCount (const QModelIndex & parent) const +{ + return mModel.columnCount (parent); +} + +QVariant CSVWorld::NastyTableModelHack::data (const QModelIndex & index, int role) const +{ + return mModel.data (index, role); +} + +bool CSVWorld::NastyTableModelHack::setData ( const QModelIndex &index, const QVariant &value, int role) +{ + mData = value; + return true; +} + +QVariant CSVWorld::NastyTableModelHack::getData() const +{ + return mData; +} + +CSVWorld::CommandDelegate::CommandDelegate (QUndoStack& undoStack, QObject *parent) +: QStyledItemDelegate (parent), mUndoStack (undoStack) +{} + +void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemModel *model, + const QModelIndex& index) const +{ + NastyTableModelHack hack (*model); + QStyledItemDelegate::setModelData (editor, &hack, index); + mUndoStack.push (new CSMWorld::ModifyCommand (*model, index, hack.getData())); +} + + +CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack) +{ + QAbstractTableModel *model = data.getTableModel (id); + + int columns = model->columnCount(); + + for (int i=0; isetSourceModel (model); + + setModel (proxyModel); + horizontalHeader()->setResizeMode (QHeaderView::Interactive); + verticalHeader()->hide(); + setSortingEnabled (true); + setSelectionBehavior (QAbstractItemView::SelectRows); + setSelectionMode (QAbstractItemView::ExtendedSelection); + + /// \todo make initial layout fill the whole width of the table +} \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp new file mode 100644 index 000000000..dbb3c3443 --- /dev/null +++ b/apps/opencs/view/world/table.hpp @@ -0,0 +1,25 @@ +#ifndef CSV_WORLD_TABLE_H +#define CSV_WORLD_TABLE_H + +#include + +class QUndoStack; + +namespace CSMWorld +{ + class Data; + class UniversalId; +} + +namespace CSVWorld +{ + ///< Table widget + class Table : public QTableView + { + public: + + Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack); + }; +} + +#endif From 7079b9062f66250fa9b0790c75bfd76533cbd814 Mon Sep 17 00:00:00 2001 From: rpopovici Date: Fri, 30 Nov 2012 02:16:16 +0200 Subject: [PATCH 053/378] add AI script functions --- apps/openmw/mwmechanics/aiescort.cpp | 6 + apps/openmw/mwmechanics/aiescort.hpp | 5 + apps/openmw/mwmechanics/aifollow.cpp | 5 + apps/openmw/mwmechanics/aifollow.hpp | 4 +- apps/openmw/mwscript/aiextensions.cpp | 239 +++++++++++++++++++++++-- apps/openmw/mwscript/docs/vmformat.txt | 16 +- 6 files changed, 262 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 27cd9095d..ebbea55b0 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -5,6 +5,12 @@ MWMechanics::AiEscort::AiEscort(const std::string &actorId,int duration, float x : mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) { } + +MWMechanics::AiEscort::AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z) +: mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) +{ +} + MWMechanics::AiEscort *MWMechanics::AiEscort::clone() const { diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index fef70f508..d89a9586c 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -10,6 +10,10 @@ namespace MWMechanics { public: AiEscort(const std::string &actorId,int duration, float x, float y, float z); + ///< \implement AiEscort + AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z); + ///< \implement AiEscortCell + virtual AiEscort *clone() const; virtual bool execute (const MWWorld::Ptr& actor); @@ -19,6 +23,7 @@ namespace MWMechanics private: std::string mActorId; + std::string mCellId; float mX; float mY; float mZ; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 3fee6d98c..dab9e0283 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -5,6 +5,11 @@ MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float : mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId) { } +MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) +: mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId) +{ +} + MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const { return new AiFollow(*this); diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index ded13d780..0b37b0a2d 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -11,6 +11,7 @@ namespace MWMechanics { public: AiFollow(const std::string &ActorId,float duration, float X, float Y, float Z); + AiFollow(const std::string &ActorId,const std::string &CellId,float duration, float X, float Y, float Z); virtual AiFollow *clone() const; virtual bool execute (const MWWorld::Ptr& actor); ///< \return Package completed? @@ -21,7 +22,8 @@ namespace MWMechanics float mX; float mY; float mZ; - std::string mActorId; + std::string mActorId; + std::string mCellId; }; } #endif diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 787962ad1..a4108c17e 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -10,6 +10,11 @@ #include "../mwworld/class.hpp" #include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/aiactivate.hpp" +#include "../mwmechanics/aiescort.hpp" +#include "../mwmechanics/aifollow.hpp" +#include "../mwmechanics/aitravel.hpp" +#include "../mwmechanics/aiwander.hpp" #include "interpretercontext.hpp" #include "ref.hpp" @@ -20,6 +25,27 @@ namespace MWScript { namespace Ai { + template + class OpAiActivate : public Interpreter::Opcode1 + { + public: + + virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string objectID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + // discard additional arguments (reset), because we have no idea what they mean. + for (unsigned int i=0; i class OpAiTravel : public Interpreter::Opcode1 { @@ -41,6 +67,9 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; i + class OpAiEscortCell : public Interpreter::Opcode1 + { + public: + + virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Float duration = runtime[0].mFloat; @@ -72,6 +139,9 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; i idleList; + for (unsigned int i=0; i + class OpAiFollow : public Interpreter::Opcode1 + { + public: + + virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + Interpreter::Type_Float duration = runtime[0].mFloat; + runtime.pop(); + + Interpreter::Type_Float x = runtime[0].mFloat; + runtime.pop(); + + Interpreter::Type_Float y = runtime[0].mFloat; + runtime.pop(); + + Interpreter::Type_Float z = runtime[0].mFloat; + runtime.pop(); + + // discard additional arguments (reset), because we have no idea what they mean. + for (unsigned int i=0; i + class OpAiFollowCell : public Interpreter::Opcode1 + { + public: + + virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + Interpreter::Type_Float duration = runtime[0].mFloat; + runtime.pop(); + + Interpreter::Type_Float x = runtime[0].mFloat; + runtime.pop(); + + Interpreter::Type_Float y = runtime[0].mFloat; + runtime.pop(); + + Interpreter::Type_Float z = runtime[0].mFloat; + runtime.pop(); + + // discard additional arguments (reset), because we have no idea what they mean. + for (unsigned int i=0; i + class OpGetCurrentAIPackage : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Integer value = MWWorld::Class::get (ptr).getCreatureStats (ptr).getAiSequence().getTypeId (); + + runtime.push (value); + } + }; + + template + class OpGetDetected : public Interpreter::Opcode1 + { + public: + + virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + Interpreter::Type_Integer value = false; // TODO replace with implementation + + std::cout << "AiGetDetected: " << actorID << ", " << value << std::endl; + + runtime.push (value); + } + }; + const int opcodeAiTravel = 0x20000; const int opcodeAiTravelExplicit = 0x20001; @@ -189,8 +375,20 @@ namespace MWScript const int opcodeAiEscortExplicit = 0x20003; const int opcodeGetAiPackageDone = 0x200007c; const int opcodeGetAiPackageDoneExplicit = 0x200007d; + const int opcodeGetCurrentAiPackage = 0x20001b1; + const int opcodeGetCurrentAiPackageExplicit = 0x20001b2; + const int opcodeGetDetected = 0x20001b3; + const int opcodeGetDetectedExplicit = 0x20001b4; const int opcodeAiWander = 0x20010; const int opcodeAiWanderExplicit = 0x20011; + const int opcodeAIActivate = 0x20018; + const int opcodeAIActivateExplicit = 0x20019; + const int opcodeAiEscortCell = 0x2001a; + const int opcodeAiEscortCellExplicit = 0x2001b; + const int opcodeAiFollow = 0x2001c; + const int opcodeAiFollowExplicit = 0x2001d; + const int opcodeAiFollowCell = 0x2001e; + const int opcodeAiFollowCellExplicit = 0x2001f; const int opcodeSetHello = 0x200015e; const int opcodeSetHelloExplicit = 0x200015d; const int opcodeSetFight = 0x200015e; @@ -202,16 +400,26 @@ namespace MWScript void registerExtensions (Compiler::Extensions& extensions) { + extensions.registerInstruction ("aiactivate", "c/l", opcodeAIActivate, + opcodeAIActivateExplicit); extensions.registerInstruction ("aitravel", "fff/l", opcodeAiTravel, opcodeAiTravelExplicit); extensions.registerInstruction ("aiescort", "cffff/l", opcodeAiEscort, opcodeAiEscortExplicit); + extensions.registerInstruction ("aiescortcell", "ccffff/l", opcodeAiEscortCell, + opcodeAiEscortCellExplicit); extensions.registerInstruction ("aiwander", "fff/llllllllll", opcodeAiWander, opcodeAiWanderExplicit); - + extensions.registerInstruction ("aifollow", "cffff/l", opcodeAiFollow, + opcodeAiFollowExplicit); + extensions.registerInstruction ("aifollowcell", "ccffff/l", opcodeAiFollowCell, + opcodeAiFollowCellExplicit); extensions.registerFunction ("getaipackagedone", 'l', "", opcodeGetAiPackageDone, opcodeGetAiPackageDoneExplicit); - + extensions.registerFunction ("getcurrentaipackage", 'l', "", opcodeGetCurrentAiPackage, + opcodeGetAiPackageDoneExplicit); + extensions.registerFunction ("getdetected", 'l', "c", opcodeGetDetected, + opcodeGetDetectedExplicit); extensions.registerInstruction ("sethello", "l", opcodeSetHello, opcodeSetHelloExplicit); extensions.registerInstruction ("setfight", "l", opcodeSetFight, opcodeSetFightExplicit); extensions.registerInstruction ("setflee", "l", opcodeSetFlee, opcodeSetFleeExplicit); @@ -220,15 +428,26 @@ namespace MWScript void installOpcodes (Interpreter::Interpreter& interpreter) { + interpreter.installSegment3 (opcodeAIActivate, new OpAiActivate); + interpreter.installSegment3 (opcodeAIActivateExplicit, new OpAiActivate); interpreter.installSegment3 (opcodeAiTravel, new OpAiTravel); interpreter.installSegment3 (opcodeAiTravelExplicit, new OpAiTravel); interpreter.installSegment3 (opcodeAiEscort, new OpAiEscort); interpreter.installSegment3 (opcodeAiEscortExplicit, new OpAiEscort); + interpreter.installSegment3 (opcodeAiEscortCell, new OpAiEscortCell); + interpreter.installSegment3 (opcodeAiEscortCellExplicit, new OpAiEscortCell); interpreter.installSegment3 (opcodeAiWander, new OpAiWander); interpreter.installSegment3 (opcodeAiWanderExplicit, new OpAiWander); + interpreter.installSegment3 (opcodeAiFollow, new OpAiFollow); + interpreter.installSegment3 (opcodeAiFollowExplicit, new OpAiFollow); + interpreter.installSegment3 (opcodeAiFollowCell, new OpAiFollowCell); + interpreter.installSegment3 (opcodeAiFollowCellExplicit, new OpAiFollowCell); interpreter.installSegment5 (opcodeGetAiPackageDone, new OpGetAiPackageDone); - interpreter.installSegment5 (opcodeGetAiPackageDoneExplicit, - new OpGetAiPackageDone); + interpreter.installSegment5 (opcodeGetAiPackageDoneExplicit, new OpGetAiPackageDone); + interpreter.installSegment5 (opcodeGetCurrentAiPackage, new OpGetCurrentAIPackage); + interpreter.installSegment5 (opcodeGetCurrentAiPackageExplicit, new OpGetCurrentAIPackage); + interpreter.installSegment3 (opcodeGetDetected, new OpGetDetected); + interpreter.installSegment3 (opcodeGetDetectedExplicit, new OpGetDetected); interpreter.installSegment5 (opcodeSetHello, new OpSetHello); interpreter.installSegment5 (opcodeSetHelloExplicit, new OpSetHello); interpreter.installSegment5 (opcodeSetFight, new OpSetFight); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index b130aa954..19517ac8d 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -37,7 +37,15 @@ op 0x20014: SetPCFacRep op 0x20015: SetPCFacRep, explicit reference op 0x20016: ModPCFacRep op 0x20017: ModPCFacRep, explicit reference -op s 0x20018-0x3ffff unused +op 0x20018: AIActivate +op 0x20019: AIActivate, explicit reference +op 0x2001a: AiEscortCell +op 0x2001b: AiEscortCell, explicit reference +op 0x2001c: AiFollow +op 0x2001d: AiFollow, explicit reference +op 0x2001e: AiFollowCell +op 0x2001f: AiFollowCell, explicit reference +op s 0x20020-0x3ffff unused Segment 4: (not implemented yet) @@ -226,5 +234,9 @@ op 0x20001ad: SetReputation op 0x20001ae: ModReputation op 0x20001af: SetReputation, explicit op 0x20001b0: ModReputation, explicit -opcodes 0x20001b1-0x3ffffff unused +op 0x20001b1: GetCurrentAIPackage +op 0x20001b2: GetCurrentAIPackage, explicit reference +op 0x20001b3: GetDetected +op 0x20001b4: GetDetected, explicit reference +opcodes 0x20001b5-0x3ffffff unused From ec1f957e54887dcb153d4ab07c51348373f65bb7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 30 Nov 2012 13:58:10 +0100 Subject: [PATCH 054/378] edit lock for sub views --- apps/opencs/view/doc/view.cpp | 5 +++++ apps/opencs/view/world/globals.cpp | 7 +++++-- apps/opencs/view/world/globals.hpp | 6 ++++++ apps/opencs/view/world/subview.hpp | 2 ++ apps/opencs/view/world/table.cpp | 27 +++++++++++++++++++++++---- apps/opencs/view/world/table.hpp | 8 ++++++++ 6 files changed, 49 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 0c336376b..3d5ebc84b 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -165,6 +165,11 @@ void CSVDoc::View::updateDocumentState() for (int i=0; operations[i]!=-1; ++i) if (!(state & operations[i])) mOperations->quitOperation (operations[i]); + + QList subViews = findChildren(); + + for (QList::iterator iter (subViews.begin()); iter!=subViews.end(); ++iter) + (*iter)->setEditLock (state && CSMDoc::Document::State_Locked); } void CSVDoc::View::updateProgress (int current, int max, int type, int threads) diff --git a/apps/opencs/view/world/globals.cpp b/apps/opencs/view/world/globals.cpp index a8b6497db..68c9012aa 100644 --- a/apps/opencs/view/world/globals.cpp +++ b/apps/opencs/view/world/globals.cpp @@ -6,7 +6,10 @@ CSVWorld::Globals::Globals (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack) : SubView (id) { - QTableView *table = new Table (id, data, undoStack); + setWidget (mTable = new Table (id, data, undoStack)); +} - setWidget (table); +void CSVWorld::Globals::setEditLock (bool locked) +{ + mTable->setEditLock (locked); } \ No newline at end of file diff --git a/apps/opencs/view/world/globals.hpp b/apps/opencs/view/world/globals.hpp index c4a9fd402..33cbce47f 100644 --- a/apps/opencs/view/world/globals.hpp +++ b/apps/opencs/view/world/globals.hpp @@ -7,11 +7,17 @@ class QUndoStack; namespace CSVWorld { + class Table; + class Globals : public SubView { + Table *mTable; + public: Globals (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack); + + virtual void setEditLock (bool locked); }; } diff --git a/apps/opencs/view/world/subview.hpp b/apps/opencs/view/world/subview.hpp index fd7a51631..543a9b2a1 100644 --- a/apps/opencs/view/world/subview.hpp +++ b/apps/opencs/view/world/subview.hpp @@ -29,6 +29,8 @@ namespace CSVWorld SubView (const CSMWorld::UniversalId& id); CSMWorld::UniversalId getUniversalId() const; + + virtual void setEditLock (bool locked) = 0; }; struct SubViewFactoryBase diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 2c9aa431f..590f7ea98 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -39,12 +39,15 @@ namespace CSVWorld class CommandDelegate : public QStyledItemDelegate { QUndoStack& mUndoStack; + bool mEditLock; public: CommandDelegate (QUndoStack& undoStack, QObject *parent); void setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const; + + void setEditLock (bool locked); }; } @@ -80,15 +83,24 @@ QVariant CSVWorld::NastyTableModelHack::getData() const } CSVWorld::CommandDelegate::CommandDelegate (QUndoStack& undoStack, QObject *parent) -: QStyledItemDelegate (parent), mUndoStack (undoStack) +: QStyledItemDelegate (parent), mUndoStack (undoStack), mEditLock (false) {} void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const { - NastyTableModelHack hack (*model); - QStyledItemDelegate::setModelData (editor, &hack, index); - mUndoStack.push (new CSMWorld::ModifyCommand (*model, index, hack.getData())); + if (!mEditLock) + { + NastyTableModelHack hack (*model); + QStyledItemDelegate::setModelData (editor, &hack, index); + mUndoStack.push (new CSMWorld::ModifyCommand (*model, index, hack.getData())); + } + ///< \todo provide some kind of feedback to the user, indicating that editing is currently not possible. +} + +void CSVWorld::CommandDelegate::setEditLock (bool locked) +{ + mEditLock = locked; } @@ -101,6 +113,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q for (int i=0; i::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter) + (*iter)->setEditLock (locked); } \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index dbb3c3443..ae203f516 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -1,6 +1,8 @@ #ifndef CSV_WORLD_TABLE_H #define CSV_WORLD_TABLE_H +#include + #include class QUndoStack; @@ -13,12 +15,18 @@ namespace CSMWorld namespace CSVWorld { + class CommandDelegate; + ///< Table widget class Table : public QTableView { + std::vector mDelegates; + public: Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack); + + void setEditLock (bool locked); }; } From bd5e364ac1e29667e789d026360170f8a66734dc Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 1 Dec 2012 13:42:12 +0100 Subject: [PATCH 055/378] display record state in table --- apps/opencs/model/world/columns.hpp | 15 +++++++++++++++ apps/opencs/model/world/data.cpp | 1 + 2 files changed, 16 insertions(+) diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 3220b3bba..e81780cee 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -44,6 +44,21 @@ namespace CSMWorld } }; + template + struct RecordStateColumn : public Column + { + RecordStateColumn() : Column ("*") {} + + virtual QVariant get (const Record& record) const + { + return static_cast (record.mState); + } + + virtual bool isEditable() const + { + return false; + } + }; } #endif \ No newline at end of file diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index aeafc1676..c0df54c10 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -13,6 +13,7 @@ CSMWorld::Data::Data() { mGlobals.addColumn (new StringIdColumn); + mGlobals.addColumn (new RecordStateColumn); mGlobals.addColumn (new FloatValueColumn); mModels.insert (std::make_pair ( From d432420a324843b47a04bd181efa88d56544d98c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 1 Dec 2012 20:53:28 +0100 Subject: [PATCH 056/378] fix FindFFmpeg.cmake --- CMakeLists.txt | 5 +- apps/openmw/mwsound/ffmpeg_decoder.hpp | 4 +- cmake/FindFFMPEG.cmake | 105 ------------------ cmake/FindFFmpeg.cmake | 148 +++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 109 deletions(-) delete mode 100644 cmake/FindFFMPEG.cmake create mode 100644 cmake/FindFFmpeg.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 78388e20f..fcb5e9bbe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,8 +140,9 @@ set(SOUND_INPUT_INCLUDES "") set(SOUND_INPUT_LIBRARY "") set(SOUND_DEFINE "") if (USE_FFMPEG) - find_package(FFMPEG REQUIRED) - set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIR}) + set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE) + find_package(FFmpeg REQUIRED) + set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIRS}) set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES}) set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG) endif (USE_FFMPEG) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index 7b028e1d0..a6e80fc9b 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -10,8 +10,8 @@ #include extern "C" { -#include -#include +#include +#include } #include "sound_decoder.hpp" diff --git a/cmake/FindFFMPEG.cmake b/cmake/FindFFMPEG.cmake deleted file mode 100644 index 2e755d047..000000000 --- a/cmake/FindFFMPEG.cmake +++ /dev/null @@ -1,105 +0,0 @@ -# Find the FFmpeg library -# -# Sets -# FFMPEG_FOUND. If false, don't try to use ffmpeg -# FFMPEG_INCLUDE_DIR -# FFMPEG_LIBRARIES -# -# Modified by Nicolay Korslund for OpenMW - -SET( FFMPEG_FOUND "NO" ) - -FIND_PATH( FFMPEG_general_INCLUDE_DIR libavcodec/avcodec.h libavformat/avformat.h - HINTS - PATHS - /usr/include - /usr/local/include - /usr/include/ffmpeg - /usr/local/include/ffmpeg - /usr/include/ffmpeg/libavcodec - /usr/local/include/ffmpeg/libavcodec - /usr/include/libavcodec - /usr/local/include/libavcodec - ) - -FIND_PATH( FFMPEG_avcodec_INCLUDE_DIR avcodec.h - HINTS - PATHS - ${FFMPEG_general_INCLUDE_DIR}/libavcodec - /usr/include - /usr/local/include - /usr/include/ffmpeg - /usr/local/include/ffmpeg - /usr/include/ffmpeg/libavcodec - /usr/local/include/ffmpeg/libavcodec - /usr/include/libavcodec - /usr/local/include/libavcodec -) - -FIND_PATH( FFMPEG_avformat_INCLUDE_DIR avformat.h - HINTS - PATHS - ${FFMPEG_general_INCLUDE_DIR}/libavformat - /usr/include - /usr/local/include - /usr/include/ffmpeg - /usr/local/include/ffmpeg - /usr/include/ffmpeg/libavformat - /usr/local/include/ffmpeg/libavformat - /usr/include/libavformat - /usr/local/include/libavformat -) - -set(FFMPEG_INCLUDE_DIR ${FFMPEG_general_INCLUDE_DIR} ${FFMPEG_avcodec_INCLUDE_DIR} ${FFMPEG_avformat_INCLUDE_DIR}) - -IF( FFMPEG_INCLUDE_DIR ) - -FIND_PROGRAM( FFMPEG_CONFIG ffmpeg-config - /usr/bin - /usr/local/bin - ${HOME}/bin -) - -IF( FFMPEG_CONFIG ) - EXEC_PROGRAM( ${FFMPEG_CONFIG} ARGS "--libs avformat" OUTPUT_VARIABLE FFMPEG_LIBS ) - SET( FFMPEG_FOUND "YES" ) - SET( FFMPEG_LIBRARIES "${FFMPEG_LIBS}" ) - -ELSE( FFMPEG_CONFIG ) - - FIND_LIBRARY( FFMPEG_avcodec_LIBRARY avcodec - /usr/lib - /usr/local/lib - /usr/lib64 - /usr/local/lib64 - ) - - FIND_LIBRARY( FFMPEG_avformat_LIBRARY avformat - /usr/lib - /usr/local/lib - /usr/lib64 - /usr/local/lib64 - ) - - FIND_LIBRARY( FFMPEG_avutil_LIBRARY avutil - /usr/lib - /usr/local/lib - /usr/lib64 - /usr/local/lib64 - ) - - IF( FFMPEG_avcodec_LIBRARY ) - IF( FFMPEG_avformat_LIBRARY ) - - SET( FFMPEG_FOUND "YES" ) - SET( FFMPEG_LIBRARIES ${FFMPEG_avformat_LIBRARY} ${FFMPEG_avcodec_LIBRARY} ) - IF( FFMPEG_avutil_LIBRARY ) - SET( FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${FFMPEG_avutil_LIBRARY} ) - ENDIF( FFMPEG_avutil_LIBRARY ) - - ENDIF( FFMPEG_avformat_LIBRARY ) - ENDIF( FFMPEG_avcodec_LIBRARY ) - -ENDIF( FFMPEG_CONFIG ) - -ENDIF( FFMPEG_INCLUDE_DIR ) diff --git a/cmake/FindFFmpeg.cmake b/cmake/FindFFmpeg.cmake new file mode 100644 index 000000000..526be5f1b --- /dev/null +++ b/cmake/FindFFmpeg.cmake @@ -0,0 +1,148 @@ +# vim: ts=2 sw=2 +# - Try to find the required ffmpeg components(default: AVFORMAT, AVUTIL, AVCODEC) +# +# Once done this will define +# FFMPEG_FOUND - System has the all required components. +# FFMPEG_INCLUDE_DIRS - Include directory necessary for using the required components headers. +# FFMPEG_LIBRARIES - Link these to use the required ffmpeg components. +# FFMPEG_DEFINITIONS - Compiler switches required for using the required ffmpeg components. +# +# For each of the components it will additionaly set. +# - AVCODEC +# - AVDEVICE +# - AVFORMAT +# - AVUTIL +# - POSTPROCESS +# - SWSCALE +# the following variables will be defined +# _FOUND - System has +# _INCLUDE_DIRS - Include directory necessary for using the headers +# _LIBRARIES - Link these to use +# _DEFINITIONS - Compiler switches required for using +# _VERSION - The components version +# +# Copyright (c) 2006, Matthias Kretz, +# Copyright (c) 2008, Alexander Neundorf, +# Copyright (c) 2011, Michael Jansen, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +include(FindPackageHandleStandardArgs) + +# The default components were taken from a survey over other FindFFMPEG.cmake files +if (NOT FFmpeg_FIND_COMPONENTS) + set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL) +endif () + +# +### Macro: set_component_found +# +# Marks the given component as found if both *_LIBRARIES AND *_INCLUDE_DIRS is present. +# +macro(set_component_found _component ) + if (${_component}_LIBRARIES AND ${_component}_INCLUDE_DIRS) + # message(STATUS " - ${_component} found.") + set(${_component}_FOUND TRUE) + else () + # message(STATUS " - ${_component} not found.") + endif () +endmacro() + +# +### Macro: find_component +# +# Checks for the given component by invoking pkgconfig and then looking up the libraries and +# include directories. +# +macro(find_component _component _pkgconfig _library _header) + + if (NOT WIN32) + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(PC_${_component} ${_pkgconfig}) + endif () + endif (NOT WIN32) + + find_path(${_component}_INCLUDE_DIRS ${_header} + HINTS + ${PC_LIB${_component}_INCLUDEDIR} + ${PC_LIB${_component}_INCLUDE_DIRS} + PATH_SUFFIXES + ffmpeg + ) + + find_library(${_component}_LIBRARIES NAMES ${_library} + HINTS + ${PC_LIB${_component}_LIBDIR} + ${PC_LIB${_component}_LIBRARY_DIRS} + ) + + set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS.") + set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number.") + + set_component_found(${_component}) + + mark_as_advanced( + ${_component}_INCLUDE_DIRS + ${_component}_LIBRARIES + ${_component}_DEFINITIONS + ${_component}_VERSION) + +endmacro() + + +# Check for cached results. If there are skip the costly part. +if (NOT FFMPEG_LIBRARIES) + + # Check for all possible component. + find_component(AVCODEC libavcodec avcodec libavcodec/avcodec.h) + find_component(AVFORMAT libavformat avformat libavformat/avformat.h) + find_component(AVDEVICE libavdevice avdevice libavdevice/avdevice.h) + find_component(AVUTIL libavutil avutil libavutil/avutil.h) + find_component(SWSCALE libswscale swscale libswscale/swscale.h) + find_component(POSTPROC libpostproc postproc libpostproc/postprocess.h) + + # Check if the required components were found and add their stuff to the FFMPEG_* vars. + foreach (_component ${FFmpeg_FIND_COMPONENTS}) + if (${_component}_FOUND) + # message(STATUS "Required component ${_component} present.") + set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${${_component}_LIBRARIES}) + set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} ${${_component}_DEFINITIONS}) + list(APPEND FFMPEG_INCLUDE_DIRS ${${_component}_INCLUDE_DIRS}) + else () + # message(STATUS "Required component ${_component} missing.") + endif () + endforeach () + + # Build the include path with duplicates removed. + if (FFMPEG_INCLUDE_DIRS) + list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS) + endif () + + # cache the vars. + set(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "The FFmpeg include directories." FORCE) + set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} CACHE STRING "The FFmpeg libraries." FORCE) + set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} CACHE STRING "The FFmpeg cflags." FORCE) + + mark_as_advanced(FFMPEG_INCLUDE_DIRS + FFMPEG_LIBRARIES + FFMPEG_DEFINITIONS) + +endif () + +# Now set the noncached _FOUND vars for the components. +foreach (_component AVCODEC AVDEVICE AVFORMAT AVUTIL POSTPROCESS SWSCALE) + set_component_found(${_component}) +endforeach () + +# Compile the list of required vars +set(_FFmpeg_REQUIRED_VARS FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS) +foreach (_component ${FFmpeg_FIND_COMPONENTS}) + list(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARIES ${_component}_INCLUDE_DIRS) +endforeach () + +# Give a nice error message if some of the required vars are missing. +find_package_handle_standard_args(FFmpeg DEFAULT_MSG ${_FFmpeg_REQUIRED_VARS}) From 7e7e6e2bcb8c3e37b0edef4c1111560481f1fa80 Mon Sep 17 00:00:00 2001 From: eduard Date: Sun, 2 Dec 2012 14:08:22 +0100 Subject: [PATCH 057/378] Fix window formatting on tool tip with too log titles --- apps/openmw/mwgui/tooltips.cpp | 35 ++++++++++++++++++++++++++++------ apps/openmw/mwgui/tooltips.hpp | 3 +++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 35224613c..a050005ec 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -31,6 +31,7 @@ ToolTips::ToolTips(MWBase::WindowManager* windowManager) : , mRemainingDelay(0.0) , mLastMouseX(0) , mLastMouseY(0) + , mHorizontalScrollIndex(0) { getWidget(mDynamicToolTipBox, "DynamicToolTipBox"); @@ -52,6 +53,7 @@ void ToolTips::setEnabled(bool enabled) void ToolTips::onFrame(float frameDuration) { + while (mDynamicToolTipBox->getChildCount()) { MyGUI::Gui::getInstance().destroyWidget(mDynamicToolTipBox->getChildAt(0)); @@ -103,7 +105,7 @@ void ToolTips::onFrame(float frameDuration) else { - const MyGUI::IntPoint& lastPressed = InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); + const MyGUI::IntPoint& lastPressed = InputManager::getInstance().getLastPressedPosition(MyGUI::MouseButton::Left); if (mousePos == lastPressed) // mouseclick makes tooltip disappear return; @@ -114,11 +116,13 @@ void ToolTips::onFrame(float frameDuration) } else { + mHorizontalScrollIndex = 0; mRemainingDelay = mDelay; } mLastMouseX = mousePos.left; mLastMouseY = mousePos.top; + if (mRemainingDelay > 0) return; @@ -148,7 +152,8 @@ void ToolTips::onFrame(float frameDuration) { return; } - + + // special handling for markers on the local map: the tooltip should only be visible // if the marker is not hidden due to the fog of war. if (focus->getUserString ("IsMarker") == "true") @@ -352,7 +357,7 @@ void ToolTips::findImageExtension(std::string& image) } IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) -{ +{ mDynamicToolTipBox->setVisible(true); std::string caption = info.caption; @@ -383,6 +388,8 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) setCoord(0, 0, 300, 300); const IntPoint padding(8, 8); + + const int maximumWidth = 500; const int imageCaptionHPadding = (caption != "" ? 8 : 0); const int imageCaptionVPadding = (caption != "" ? 4 : 0); @@ -406,7 +413,7 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) IntSize textSize = textWidget->getTextSize(); captionSize += IntSize(imageSize, 0); // adjust for image - IntSize totalSize = IntSize( std::max(textSize.width, captionSize.width + ((image != "") ? imageCaptionHPadding : 0)), + IntSize totalSize = IntSize( std::min(std::max(textSize.width,captionSize.width + ((image != "") ? imageCaptionHPadding : 0)),maximumWidth), ((text != "") ? textSize.height + imageCaptionVPadding : 0) + captionHeight ); if (!info.effects.empty()) @@ -494,8 +501,24 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) (captionHeight-captionSize.height)/2, captionSize.width-imageSize, captionSize.height); + + //if its too long we do hscroll with the caption + if (captionSize.width > maximumWidth){ + mHorizontalScrollIndex = mHorizontalScrollIndex + 2; + if (mHorizontalScrollIndex > captionSize.width){ + mHorizontalScrollIndex = -totalSize.width; + } + int horizontal_scroll = mHorizontalScrollIndex; + if (horizontal_scroll < 40){ + horizontal_scroll = 40; + }else{ + horizontal_scroll = 80 - mHorizontalScrollIndex; + } + captionWidget->setPosition (IntPoint(horizontal_scroll, captionWidget->getPosition().top + padding.top)); + } else { + captionWidget->setPosition (captionWidget->getPosition() + padding); + } - captionWidget->setPosition (captionWidget->getPosition() + padding); textWidget->setPosition (textWidget->getPosition() + IntPoint(0, padding.top)); // only apply vertical padding, the horizontal works automatically due to Align::HCenter if (image != "") @@ -745,4 +768,4 @@ void ToolTips::setDelay(float delay) { mDelay = delay; mRemainingDelay = mDelay; -} +} \ No newline at end of file diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 270df4ae2..4048d0d5a 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -90,6 +90,9 @@ namespace MWGui float mFocusToolTipX; float mFocusToolTipY; + + int mHorizontalScrollIndex; + float mDelay; float mRemainingDelay; // remaining time until tooltip will show From 30d0e93cf438f5173284407909e252531b1799cf Mon Sep 17 00:00:00 2001 From: eduard Date: Sun, 2 Dec 2012 14:12:18 +0100 Subject: [PATCH 058/378] Fix window formatting on tool tip with too log titles --- apps/openmw/mwgui/tooltips.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index a050005ec..621d275bb 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -506,14 +506,14 @@ IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info) if (captionSize.width > maximumWidth){ mHorizontalScrollIndex = mHorizontalScrollIndex + 2; if (mHorizontalScrollIndex > captionSize.width){ - mHorizontalScrollIndex = -totalSize.width; + mHorizontalScrollIndex = -totalSize.width; } int horizontal_scroll = mHorizontalScrollIndex; if (horizontal_scroll < 40){ horizontal_scroll = 40; }else{ - horizontal_scroll = 80 - mHorizontalScrollIndex; - } + horizontal_scroll = 80 - mHorizontalScrollIndex; + } captionWidget->setPosition (IntPoint(horizontal_scroll, captionWidget->getPosition().top + padding.top)); } else { captionWidget->setPosition (captionWidget->getPosition() + padding); From b25f2e88ce82b1e8c59f8a2f58950fa6bfa230b7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 2 Dec 2012 15:07:22 +0100 Subject: [PATCH 059/378] Issue #492: fixed double alchemy ingredients removal --- apps/openmw/mwgui/alchemywindow.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/alchemywindow.cpp b/apps/openmw/mwgui/alchemywindow.cpp index fc06e866c..db1a81c2c 100644 --- a/apps/openmw/mwgui/alchemywindow.cpp +++ b/apps/openmw/mwgui/alchemywindow.cpp @@ -63,7 +63,7 @@ namespace MWGui void AlchemyWindow::onCancelButtonClicked(MyGUI::Widget* _sender) { mAlchemy.clear(); - + mWindowManager.removeGuiMode(GM_Alchemy); mWindowManager.removeGuiMode(GM_Inventory); } @@ -119,7 +119,6 @@ namespace MWGui if (mIngredients[i]->isUserString("ToolTipType")) { MWWorld::Ptr ingred = *mIngredients[i]->getUserData(); - ingred.getRefData().setCount(ingred.getRefData().getCount()-1); if (ingred.getRefData().getCount() == 0) removeIngredient(mIngredients[i]); } From 682fd23aea36bef5934f271535c7f3c0246880a2 Mon Sep 17 00:00:00 2001 From: greye Date: Sun, 2 Dec 2012 21:18:59 +0400 Subject: [PATCH 060/378] fix wrong increment --- apps/openmw/mwmechanics/alchemy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index c07c60209..8ab81bfdf 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -203,7 +203,7 @@ const ESM::Potion *MWMechanics::Alchemy::getRecord() const bool mismatch = false; - for (int i=0; i (iter->mEffects.mList.size()); ++iter) + for (int i=0; i (iter->mEffects.mList.size()); ++i) { const ESM::ENAMstruct& first = iter->mEffects.mList[i]; const ESM::ENAMstruct& second = mEffects[i]; From a6fd369e4e151aff8e6a9f914be51016d5a99aef Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Sun, 2 Dec 2012 22:10:47 +0000 Subject: [PATCH 061/378] Fixed wait window reappearing after sleeping --- apps/openmw/mwgui/waitdialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 31b5cce35..7626ba3f9 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -149,6 +149,7 @@ namespace MWGui mCurHour = 0; mRemainingTime = 0.05; mProgressBar.setProgress (0, mHours); + mWindowManager.popGuiMode (); } void WaitDialog::onCancelButtonClicked(MyGUI::Widget* sender) From ea2bbec76ed11e7db4d5dd1b9ce48329dff050e2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Dec 2012 05:32:12 +0100 Subject: [PATCH 062/378] better fix for bed activation --- apps/openmw/mwgui/waitdialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 7626ba3f9..4f2c98c08 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -149,7 +149,6 @@ namespace MWGui mCurHour = 0; mRemainingTime = 0.05; mProgressBar.setProgress (0, mHours); - mWindowManager.popGuiMode (); } void WaitDialog::onCancelButtonClicked(MyGUI::Widget* sender) @@ -205,6 +204,7 @@ namespace MWGui MWBase::Environment::get().getWorld ()->getFader ()->fadeIn(0.2); mProgressBar.setVisible (false); mWindowManager.removeGuiMode (GM_Rest); + mWindowManager.removeGuiMode (GM_RestBed); mWaiting = false; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); From 3db850a7d1870782a75606e682809758bc3e793a Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Dec 2012 05:45:04 +0100 Subject: [PATCH 063/378] Don't crash if no loading screens are found --- apps/openmw/mwgui/loadingscreen.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 8bcaa6ce0..d721e209a 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -226,9 +226,14 @@ namespace MWGui if (start == "splash") splash.push_back (*it); } - std::string randomSplash = splash[rand() % splash.size()]; + if (splash.size()) + { + std::string randomSplash = splash[rand() % splash.size()]; - Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, "General"); - mBackgroundImage->setImageTexture (randomSplash); + Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, "General"); + mBackgroundImage->setImageTexture (randomSplash); + } + else + std::cerr << "No loading screens found!" << std::endl; } } From db29e411c4a28805611ce7d90e0ee8294af09cb8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 3 Dec 2012 13:56:02 +0100 Subject: [PATCH 064/378] added merge functions; temporarily merge on document creation --- apps/opencs/editor.cpp | 2 ++ apps/opencs/model/world/columns.hpp | 3 +++ apps/opencs/model/world/data.cpp | 5 ++++ apps/opencs/model/world/data.hpp | 3 +++ apps/opencs/model/world/idcollection.hpp | 30 ++++++++++++++++++++++++ apps/opencs/model/world/record.hpp | 30 ++++++++++++++++++++++-- 6 files changed, 71 insertions(+), 2 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 6977a22f0..1632ed220 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -35,6 +35,8 @@ void CS::Editor::createDocument() document->getData().getGlobals().add (record); } + document->getData().merge(); /// \todo remove once proper ESX loading is implemented + mViewManager.addView (document); } diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index e81780cee..483ce929d 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -51,6 +51,9 @@ namespace CSMWorld virtual QVariant get (const Record& record) const { + if (record.mState==Record::State_Erased) + return static_cast (Record::State_Deleted); + return static_cast (record.mState); } diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index c0df54c10..f350299ec 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -46,4 +46,9 @@ QAbstractTableModel *CSMWorld::Data::getTableModel (const UniversalId& id) throw std::logic_error ("No table model available for " + id.toString()); return iter->second; +} + +void CSMWorld::Data::merge() +{ + mGlobals.merge(); } \ No newline at end of file diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 11073c5e3..a8a21e205 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -33,6 +33,9 @@ namespace CSMWorld QAbstractTableModel *getTableModel (const UniversalId& id); ///< If no table model is available for \ id, an exception is thrown. + + void merge(); + ///< Merge modified into base. }; } diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 41cd352ce..d9ab16747 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -58,6 +59,12 @@ namespace CSMWorld virtual void setData (int index, int column, const QVariant& data) = 0; virtual bool isEditable (int column) const = 0; + + virtual void merge() = 0; + ///< Merge modified into base. + + virtual void purge() = 0; + ///< Remove records that are flagged as erased. }; ///< \brief Collection of ID-based records @@ -95,6 +102,12 @@ namespace CSMWorld virtual bool isEditable (int column) const; + virtual void merge(); + ///< Merge modified into base. + + virtual void purge(); + ///< Remove records that are flagged as erased. + void addColumn (Column *column); }; @@ -181,6 +194,23 @@ namespace CSMWorld { mColumns.push_back (column); } + + template + void IdCollection::merge() + { + for (typename std::vector >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter) + iter->merge(); + + purge(); + } + + template + void IdCollection::purge() + { + mRecords.erase (std::remove_if (mRecords.begin(), mRecords.end(), + std::mem_fun_ref (&Record::isErased) // I want lambda :( + ), mRecords.end()); + } } #endif diff --git a/apps/opencs/model/world/record.hpp b/apps/opencs/model/world/record.hpp index 950d7f176..c08d2e0d1 100644 --- a/apps/opencs/model/world/record.hpp +++ b/apps/opencs/model/world/record.hpp @@ -13,7 +13,8 @@ namespace CSMWorld State_BaseOnly, // defined in base only State_Modified, // exists in base, but has been modified State_ModifiedOnly, // newly created in modified - State_Deleted // exists in base, but has been deleted + State_Deleted, // exists in base, but has been deleted + State_Erased // does not exist at all (we mostly treat that the same way as deleted) }; ESXRecordT mBase; @@ -22,6 +23,8 @@ namespace CSMWorld bool isDeleted() const; + bool isErased() const; + bool isModified() const; const ESXRecordT& get() const; @@ -32,12 +35,21 @@ namespace CSMWorld void setModified (const ESXRecordT& modified); ///< Throws an exception, if the record is deleted. + + void merge(); + ///< Merge modified into base. }; template bool Record::isDeleted() const { - return mState==State_Deleted; + return mState==State_Deleted || mState==State_Erased; + } + + template + bool Record::isErased() const + { + return mState==State_Erased; } template @@ -75,6 +87,20 @@ namespace CSMWorld if (mState!=State_ModifiedOnly) mState = mBase==mModified ? State_BaseOnly : State_Modified; } + + template + void Record::merge() + { + if (isModified()) + { + mBase = mModified; + mState = State_BaseOnly; + } + else if (mState==State_Deleted) + { + mState = State_Erased; + } + } } #endif From a77d910aaf10e90dde397c92daf8bdef2e927a41 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Dec 2012 16:44:41 +0100 Subject: [PATCH 065/378] audio codec is opened, some cleanup --- apps/openmw/mwrender/videoplayer.cpp | 77 +++++++++++++++++++--------- apps/openmw/mwrender/videoplayer.hpp | 12 +++-- 2 files changed, 61 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 4679104d8..5e7d8eb55 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -72,6 +72,7 @@ namespace MWRender VideoPlayer::VideoPlayer(Ogre::SceneManager *sceneMgr) : mAvContext(NULL) , mVideoStreamId(-1) + , mAudioStreamId(-1) { Ogre::MaterialPtr videoMaterial = Ogre::MaterialManager::getSingleton ().create("VideoMaterial", "General"); videoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); @@ -108,6 +109,7 @@ namespace MWRender mVideoStreamId = -1; + mAudioStreamId = -1; mEOF = false; mDisplayedFrameCount = 0; @@ -150,36 +152,60 @@ namespace MWRender - // INIT VIDEO - // Find the video stream among the different streams for (unsigned int i = 0; i < mAvContext->nb_streams; i++) { if (mAvContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { mVideoStreamId = i; + mVideoStream = mAvContext->streams[i]; break; } } - if (-1 == mVideoStreamId) + if (mVideoStreamId < 0) throw std::runtime_error("No video stream found in the video"); - // Get the video codec - mVideoCodecContext = mAvContext->streams[mVideoStreamId]->codec; - if (!mVideoCodecContext) - throw std::runtime_error("Stream doesn't have a codec"); - // Get the video decoder - mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id); + mVideoCodec = avcodec_find_decoder(mVideoStream->codec->codec_id); if (NULL == mVideoCodec) - throw std::runtime_error("No decoder found"); + throw std::runtime_error("No video decoder found"); // Load the video codec - err = avcodec_open2(mVideoCodecContext, mVideoCodec, 0); + err = avcodec_open2(mVideoStream->codec, mVideoCodec, 0); + if (err < 0) + throwError (err); + + + + // Find the audio stream among the different streams + for (unsigned int i = 0; i < mAvContext->nb_streams; i++) + { + if (mAvContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) + { + mAudioStreamId = i; + mAudioStream = mAvContext->streams[i]; + break; + } + } + + if (-1 == mAudioStreamId) + throw std::runtime_error("No audio stream found in the video"); + + // Get the audio decoder + mAudioCodec = avcodec_find_decoder(mAudioStream->codec->codec_id); + if (mAudioCodec == NULL) + { + throw std::runtime_error("Stream doesn't have an audio codec"); + } + + // Load the audio codec + err = avcodec_open2(mAudioStream->codec, mAudioCodec, 0); if (err < 0) throwError (err); + + // Create the frame buffers mRawFrame = avcodec_alloc_frame(); mRGBAFrame = avcodec_alloc_frame(); @@ -189,23 +215,23 @@ namespace MWRender } - avpicture_alloc ((AVPicture *)mRGBAFrame, PIX_FMT_RGBA, mVideoCodecContext->width, mVideoCodecContext->height); + avpicture_alloc ((AVPicture *)mRGBAFrame, PIX_FMT_RGBA, mVideoStream->codec->width, mVideoStream->codec->height); // Setup the image scaler // All this does is convert from YUV to RGB - note it would be faster to do this in a shader, // but i'm not worried about performance just yet - mSwsContext = sws_getContext(mVideoCodecContext->width, mVideoCodecContext->height, - mVideoCodecContext->pix_fmt, - mVideoCodecContext->width, mVideoCodecContext->height, + mSwsContext = sws_getContext(mVideoStream->codec->width, mVideoStream->codec->height, + mVideoStream->codec->pix_fmt, + mVideoStream->codec->width, mVideoStream->codec->height, PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL); if (!mSwsContext) throw std::runtime_error("Can't create SWS Context"); // Get the frame time we need for this video - AVRational r = mAvContext->streams[mVideoStreamId]->avg_frame_rate; - AVRational r2 = mAvContext->streams[mVideoStreamId]->r_frame_rate; + AVRational r = mVideoStream->avg_frame_rate; + AVRational r2 = mVideoStream->r_frame_rate; if ((!r.num || !r.den) && (!r2.num || !r2.den)) { @@ -230,19 +256,19 @@ namespace MWRender "VideoTexture", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, - mVideoCodecContext->width, mVideoCodecContext->height, + mVideoStream->codec->width, mVideoStream->codec->height, 0, Ogre::PF_BYTE_RGBA, Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); // initialize to (0, 0, 0, 1) std::vector buffer; - buffer.resize(mVideoCodecContext->width * mVideoCodecContext->height); - for (int p=0; pwidth * mVideoCodecContext->height; ++p) + buffer.resize(mVideoStream->codec->width * mVideoStream->codec->height); + for (int p=0; pcodec->width * mVideoStream->codec->height; ++p) { buffer[p] = (255 << 24); } - memcpy(mVideoTexture->getBuffer()->lock(Ogre::HardwareBuffer::HBL_DISCARD), &buffer[0], mVideoCodecContext->width*mVideoCodecContext->height*4); + memcpy(mVideoTexture->getBuffer()->lock(Ogre::HardwareBuffer::HBL_DISCARD), &buffer[0], mVideoStream->codec->width*mVideoStream->codec->height*4); mVideoTexture->getBuffer()->unlock(); mTextureUnit->setTextureName ("VideoTexture"); @@ -358,7 +384,7 @@ namespace MWRender // Get the front frame and decode it AVPacket *videoPacket = mVideoPacketQueue.front(); int res; - res = avcodec_decode_video2(mVideoCodecContext, mRawFrame, &didDecodeFrame, videoPacket); + res = avcodec_decode_video2(mVideoStream->codec, mRawFrame, &didDecodeFrame, videoPacket); if (res < 0 || !didDecodeFrame) throw std::runtime_error ("an error occured while decoding the video frame"); @@ -366,12 +392,12 @@ namespace MWRender // Convert the frame to RGB sws_scale(mSwsContext, mRawFrame->data, mRawFrame->linesize, - 0, mVideoCodecContext->height, + 0, mVideoStream->codec->height, mRGBAFrame->data, mRGBAFrame->linesize); Ogre::HardwarePixelBufferSharedPtr pixelBuffer = mVideoTexture->getBuffer(); - Ogre::PixelBox pb(mVideoCodecContext->width, mVideoCodecContext->height, 1, Ogre::PF_BYTE_RGBA, mRGBAFrame->data[0]); + Ogre::PixelBox pb(mVideoStream->codec->width, mVideoStream->codec->height, 1, Ogre::PF_BYTE_RGBA, mRGBAFrame->data[0]); pixelBuffer->blitFromMemory(pb); av_free_packet(mVideoPacketQueue.front()); @@ -397,7 +423,8 @@ namespace MWRender mVideoPacketQueue.pop(); } - avcodec_close(mVideoCodecContext); + if (mVideoStream && mVideoStream->codec != NULL) avcodec_close(mVideoStream->codec); + if (mAudioStream && mAudioStream->codec != NULL) avcodec_close(mAudioStream->codec); avpicture_free((AVPicture *)mRGBAFrame); diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 1d3010e8e..5010ac516 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -21,6 +21,7 @@ namespace Ogre struct AVFormatContext; struct AVCodecContext; struct AVCodec; +struct AVStream; struct AVFrame; struct SwsContext; struct AVPacket; @@ -56,10 +57,14 @@ namespace MWRender Ogre::Timer mTimer; - // VIDEO - AVCodecContext* mVideoCodecContext; AVCodec* mVideoCodec; - int mVideoStreamId; + AVCodec* mAudioCodec; + + AVStream* mVideoStream; + AVStream* mAudioStream; + int mVideoStreamId; ///< ID of the first video stream + int mAudioStreamId; ///< ID of the first audio stream + AVFrame* mRawFrame; AVFrame* mRGBAFrame; SwsContext* mSwsContext; @@ -67,6 +72,7 @@ namespace MWRender float mDecodingTime; std::queue mVideoPacketQueue; + int mDisplayedFrameCount; From bc90c751760dc9bdb894a8e781e3da335d2c92e1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Dec 2012 17:33:02 +0100 Subject: [PATCH 066/378] more clean up, video played with correct speed, videos without sound working too (mw_credits.bik) --- apps/openmw/mwrender/videoplayer.cpp | 305 +++++++++++++++++---------- apps/openmw/mwrender/videoplayer.hpp | 45 ++-- 2 files changed, 223 insertions(+), 127 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 5e7d8eb55..e7f1eccde 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -19,6 +19,9 @@ extern "C" #include "../mwbase/environment.hpp" +#define MIN_QUEUED_PACKETS 30 + + namespace MWRender { @@ -69,10 +72,76 @@ namespace MWRender return stream->tell(); } + //------------------------------------------------------------------------------------------- + + AVPacketQueue::AVPacketQueue(): + mFirstPacket(NULL), mLastPacket(NULL), mNumPackets(0), mSize(0) + + { + } + + int AVPacketQueue::put(AVPacket* pkt) + { + if(av_dup_packet(pkt) < 0) + { + return -1; + } + + AVPacketList* pkt1; + pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); + if (pkt1 == NULL) return -1; + pkt1->pkt = *pkt; + pkt1->next = NULL; + + if (mLastPacket == NULL) mFirstPacket = pkt1; + else mLastPacket->next = pkt1; + + mLastPacket = pkt1; + mNumPackets++; + mSize += pkt1->pkt.size; + + return 0; + } + + int AVPacketQueue::get(AVPacket* pkt, int block) + { + AVPacketList* pkt1; + + while (true) + { + pkt1 = mFirstPacket; + if (pkt1 != NULL) + { + mFirstPacket = pkt1->next; + + if (mFirstPacket == NULL) mLastPacket = NULL; + + mNumPackets--; + mSize -= pkt1->pkt.size; + *pkt = pkt1->pkt; + av_free(pkt1); + return 1; + } + else if (block == 0) + { + return 0; + } + else + { + return -1; + } + } + } + + //------------------------------------------------------------------------------------------- + VideoPlayer::VideoPlayer(Ogre::SceneManager *sceneMgr) : mAvContext(NULL) , mVideoStreamId(-1) , mAudioStreamId(-1) + , mVideoClock(0) + , mAudioClock(0) + , mClock(0) { Ogre::MaterialPtr videoMaterial = Ogre::MaterialManager::getSingleton ().create("VideoMaterial", "General"); videoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); @@ -110,8 +179,11 @@ namespace MWRender mVideoStreamId = -1; mAudioStreamId = -1; - mEOF = false; - mDisplayedFrameCount = 0; + mAudioStream = NULL; + mVideoStream = NULL; + mVideoClock = 0; + mAudioClock = 0; + mClock = 0; // if something is already playing, close it if (mAvContext) @@ -189,20 +261,20 @@ namespace MWRender } } - if (-1 == mAudioStreamId) - throw std::runtime_error("No audio stream found in the video"); - - // Get the audio decoder - mAudioCodec = avcodec_find_decoder(mAudioStream->codec->codec_id); - if (mAudioCodec == NULL) + if (mAudioStreamId >= 0) { - throw std::runtime_error("Stream doesn't have an audio codec"); - } + // Get the audio decoder + mAudioCodec = avcodec_find_decoder(mAudioStream->codec->codec_id); + if (mAudioCodec == NULL) + { + throw std::runtime_error("Stream doesn't have an audio codec"); + } - // Load the audio codec - err = avcodec_open2(mAudioStream->codec, mAudioCodec, 0); - if (err < 0) - throwError (err); + // Load the audio codec + err = avcodec_open2(mAudioStream->codec, mAudioCodec, 0); + if (err < 0) + throwError (err); + } @@ -229,24 +301,6 @@ namespace MWRender if (!mSwsContext) throw std::runtime_error("Can't create SWS Context"); - // Get the frame time we need for this video - AVRational r = mVideoStream->avg_frame_rate; - AVRational r2 = mVideoStream->r_frame_rate; - if ((!r.num || !r.den) && - (!r2.num || !r2.den)) - { - std::cerr << "Warning - unable to get the video frame rate. Using standard NTSC frame rate : 29.97 fps." << std::endl; - mWantedFrameTime = 1.f / 29.97f; - } - else - { - if (r.num && r.den) - mWantedFrameTime = 1.f/((float)r.num / r.den); - else - mWantedFrameTime = 1.f/((float)r2.num / r2.den); - } - - mTextureUnit->setTextureName (""); if (!Ogre::TextureManager::getSingleton ().getByName("VideoTexture").isNull()) @@ -273,8 +327,23 @@ namespace MWRender mTextureUnit->setTextureName ("VideoTexture"); - mTimer.reset(); + // Queue up some packets + while( + mVideoPacketQueue.getNumPackets()stream_index == mVideoStreamId) - { - // If it was a video frame... - mVideoPacketQueue.push(frame); - saved = true; + if (mVideoPacketQueue.getNumPackets ()) + decodeNextVideoFrame(); } - return saved; - - } - - void VideoPlayer::update() - { - if (!mAvContext) - return; - - // Time elapsed since the video started - float realTime = mTimer.getMilliseconds ()/1000.f; + mClock += dt; - // Here is the time we're at in the video - float movieTime = mDisplayedFrameCount * mWantedFrameTime; - - if (movieTime >= realTime) - return; + //curTime += fTime; - if (!mVideoPacketQueue.size() && mEOF) + if(mVideoPacketQueue.getNumPackets()==0 /* && mAudioPacketQueue.getNumPackets()==0 */) close(); - - Ogre::Timer timer; - - if (!mVideoPacketQueue.size()) - { - if (readFrameAndQueue()) - decodeFrontFrame(); - } - else - decodeFrontFrame(); - - mDecodingTime = timer.getMilliseconds ()/1000.f; } - void VideoPlayer::decodeFrontFrame () + void VideoPlayer::decodeNextVideoFrame () { - int didDecodeFrame = 0; - // Make sure there is something to decode - if (!mVideoPacketQueue.size()) - return; + assert (mVideoPacketQueue.getNumPackets ()); // Get the front frame and decode it - AVPacket *videoPacket = mVideoPacketQueue.front(); + AVPacket packet; + mVideoPacketQueue.get(&packet, 1); + int res; - res = avcodec_decode_video2(mVideoStream->codec, mRawFrame, &didDecodeFrame, videoPacket); + int didDecodeFrame = 0; + res = avcodec_decode_video2(mVideoStream->codec, mRawFrame, &didDecodeFrame, &packet); if (res < 0 || !didDecodeFrame) throw std::runtime_error ("an error occured while decoding the video frame"); + // Set video clock to the PTS of this packet (presentation timestamp) + double pts = 0; + if (packet.pts != -1.0) pts = packet.pts; + pts *= av_q2d(mVideoStream->time_base); + mVideoClock = pts; + // Convert the frame to RGB sws_scale(mSwsContext, mRawFrame->data, mRawFrame->linesize, @@ -400,11 +433,7 @@ namespace MWRender Ogre::PixelBox pb(mVideoStream->codec->width, mVideoStream->codec->height, 1, Ogre::PF_BYTE_RGBA, mRGBAFrame->data[0]); pixelBuffer->blitFromMemory(pb); - av_free_packet(mVideoPacketQueue.front()); - av_free(mVideoPacketQueue.front()); - mVideoPacketQueue.pop(); - - ++mDisplayedFrameCount; + if (packet.data != NULL) av_free_packet(&packet); } void VideoPlayer::close () @@ -416,11 +445,17 @@ namespace MWRender void VideoPlayer::deleteContext() { - while (mVideoPacketQueue.size()) + while (mVideoPacketQueue.getNumPackets ()) { - av_free_packet(mVideoPacketQueue.front()); - av_free(mVideoPacketQueue.front()); - mVideoPacketQueue.pop(); + AVPacket packet; + mVideoPacketQueue.get(&packet, 1); + if (packet.data != NULL) av_free_packet(&packet); + } + while (mAudioPacketQueue.getNumPackets ()) + { + AVPacket packet; + mAudioPacketQueue.get(&packet, 1); + if (packet.data != NULL) av_free_packet(&packet); } if (mVideoStream && mVideoStream->codec != NULL) avcodec_close(mVideoStream->codec); @@ -439,6 +474,46 @@ namespace MWRender mAvContext = NULL; } + + + + bool VideoPlayer::addToBuffer() + { + if(mAvContext) + { + AVPacket packet; + if (av_read_frame(mAvContext, &packet) >= 0) + { + if (packet.stream_index == mVideoStreamId) + { + /* + if(curTime==0) + { + curTime = packet.dts; + curTime *= av_q2d(m_pVideoSt->time_base); + std::cout << "Initializing curtime to: " << curTime << std::endl; + } + */ + + mVideoPacketQueue.put(&packet); + + return true; + } + else if (packet.stream_index == mAudioStreamId && mAudioStream) + { + mAudioPacketQueue.put(&packet); + return true; + } + else + { + av_free_packet(&packet); + return false; + } + } + } + + return false; + } } //#endif diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 5010ac516..466bb7902 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -25,10 +25,31 @@ struct AVStream; struct AVFrame; struct SwsContext; struct AVPacket; +struct AVPacketList; namespace MWRender { + /// A simple queue used to queue raw audio and video data. + class AVPacketQueue + { + public: + AVPacketQueue(); + int put(AVPacket* pkt); + int get(AVPacket* pkt, int block); + + bool isEmpty() const { return mNumPackets == 0; } + int getNumPackets() const { return mNumPackets; } + int getSize() const { return mSize; } + + private: + AVPacketList* mFirstPacket; + AVPacketList* mLastPacket; + int mNumPackets; + int mSize; + }; + + class VideoPlayer { public: @@ -52,9 +73,6 @@ namespace MWRender AVFormatContext* mAvContext; - - bool mEOF; - Ogre::Timer mTimer; AVCodec* mVideoCodec; @@ -68,24 +86,27 @@ namespace MWRender AVFrame* mRawFrame; AVFrame* mRGBAFrame; SwsContext* mSwsContext; - float mWantedFrameTime; - float mDecodingTime; - std::queue mVideoPacketQueue; - - int mDisplayedFrameCount; + double mClock; + double mVideoClock; + double mAudioClock; + AVPacketQueue mVideoPacketQueue; + AVPacketQueue mAudioPacketQueue; - bool readFrameAndQueue(); - bool saveFrame(AVPacket* frame); - - void decodeFrontFrame(); void close(); void deleteContext(); void throwError(int error); + + + + + bool addToBuffer(); ///< try to add the next audio or video packet into the queue. + + void decodeNextVideoFrame(); ///< decode the next video frame in the queue and display it. }; } From 3106db0379fabb77b8c17b601c39274360bf4d47 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 3 Dec 2012 17:41:38 +0100 Subject: [PATCH 067/378] commented out debug output --- apps/openmw/mwrender/videoplayer.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index e7f1eccde..b1fee213f 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -370,7 +370,7 @@ namespace MWRender mTimer.reset (); //UpdateAudio(fTime); - std::cout << "num packets: " << mVideoPacketQueue.getNumPackets() << " clocks: " << mVideoClock << " , " << mClock << std::endl; + //std::cout << "num packets: " << mVideoPacketQueue.getNumPackets() << " clocks: " << mVideoClock << " , " << mClock << std::endl; while (!mVideoPacketQueue.isEmpty() && mVideoClock < mClock) { while( @@ -486,12 +486,13 @@ namespace MWRender { if (packet.stream_index == mVideoStreamId) { + // I don't believe this is necessary. /* - if(curTime==0) + if(mClock==0) { - curTime = packet.dts; - curTime *= av_q2d(m_pVideoSt->time_base); - std::cout << "Initializing curtime to: " << curTime << std::endl; + mClock = packet.dts; + mClock *= av_q2d(mVideoStream->time_base); + std::cout << "Initializing clock to: " << mClock << std::endl; } */ From 49d62390045bfc3794f98da72fee9ce0351415fd Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 3 Dec 2012 21:44:16 +0100 Subject: [PATCH 068/378] added pop-up menu with create record action --- apps/opencs/CMakeLists.txt | 6 +- apps/opencs/model/world/commands.cpp | 18 ++++++ apps/opencs/model/world/commands.hpp | 18 ++++++ apps/opencs/model/world/idcollection.hpp | 56 +++++++++++++++++++ apps/opencs/model/world/idtable.cpp | 30 ++++++++++ apps/opencs/model/world/idtable.hpp | 18 ++++-- apps/opencs/model/world/idtableproxymodel.cpp | 18 ++++++ apps/opencs/model/world/idtableproxymodel.hpp | 24 ++++++++ apps/opencs/model/world/record.hpp | 17 +++--- apps/opencs/view/world/globals.cpp | 2 +- apps/opencs/view/world/table.cpp | 45 +++++++++++++-- apps/opencs/view/world/table.hpp | 18 +++++- 12 files changed, 246 insertions(+), 24 deletions(-) create mode 100644 apps/opencs/model/world/idtableproxymodel.cpp create mode 100644 apps/opencs/model/world/idtableproxymodel.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index dfd987345..6d18a1811 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -5,7 +5,7 @@ set (OPENCS_SRC model/doc/documentmanager.cpp model/doc/document.cpp model/world/universalid.cpp model/world/idcollection.cpp model/world/data.cpp model/world/idtable.cpp - model/world/commands.cpp + model/world/commands.cpp model/world/idtableproxymodel.cpp view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp @@ -18,10 +18,10 @@ set (OPENCS_HDR model/doc/documentmanager.hpp model/doc/document.hpp model/world/universalid.hpp model/world/record.hpp model/world/idcollection.hpp model/world/data.hpp - model/world/idtable.hpp model/world/columns.hpp + model/world/idtable.hpp model/world/columns.hpp model/world/idtableproxymodel.hpp + model/world/commands.hpp view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp - model/world/commands.hpp view/world/subview.hpp view/world/table.hpp view/world/globals.hpp ) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index a15d00619..7bb76acd0 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -3,6 +3,8 @@ #include +#include "idtableproxymodel.hpp" + CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand *parent) : QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_) @@ -20,4 +22,20 @@ void CSMWorld::ModifyCommand::redo() void CSMWorld::ModifyCommand::undo() { mModel.setData (mIndex, mOld); +} + +CSMWorld::CreateCommand::CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent) +: QUndoCommand (parent), mModel (model), mId (id) +{ + setText (("Create record " + id).c_str()); +} + +void CSMWorld::CreateCommand::redo() +{ + mModel.addRecord (mId); +} + +void CSMWorld::CreateCommand::undo() +{ + mModel.removeRow (mModel.getModelIndex (mId, 0).row()); } \ No newline at end of file diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index ffaee59ef..50b9045c6 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -3,6 +3,8 @@ #include "record.hpp" +#include + #include #include #include @@ -12,6 +14,8 @@ class QAbstractItemModel; namespace CSMWorld { + class IdTableProxyModel; + class ModifyCommand : public QUndoCommand { QAbstractItemModel& mModel; @@ -28,6 +32,20 @@ namespace CSMWorld virtual void undo(); }; + + class CreateCommand : public QUndoCommand + { + IdTableProxyModel& mModel; + std::string mId; + + public: + + CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent = 0); + + virtual void redo(); + + virtual void undo(); + }; } #endif \ No newline at end of file diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index d9ab16747..be7891bd2 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -50,6 +50,8 @@ namespace CSMWorld virtual std::string getId (int index) const = 0; + virtual int getIndex (const std::string& id) const = 0; + virtual int getColumns() const = 0; virtual std::string getTitle (int column) const = 0; @@ -65,6 +67,10 @@ namespace CSMWorld virtual void purge() = 0; ///< Remove records that are flagged as erased. + + virtual void removeRows (int index, int count) = 0; + + virtual void appendBlankRecord (const std::string& id) = 0; }; ///< \brief Collection of ID-based records @@ -92,6 +98,8 @@ namespace CSMWorld virtual std::string getId (int index) const; + virtual int getIndex (const std::string& id) const; + virtual int getColumns() const; virtual QVariant getData (int index, int column) const; @@ -108,6 +116,10 @@ namespace CSMWorld virtual void purge(); ///< Remove records that are flagged as erased. + virtual void removeRows (int index, int count) ; + + virtual void appendBlankRecord (const std::string& id); + void addColumn (Column *column); }; @@ -159,6 +171,17 @@ namespace CSMWorld return mRecords.at (index).get().mId; } + template + int IdCollection::getIndex (const std::string& id) const + { + std::map::const_iterator iter = mIndex.find (id); + + if (iter==mIndex.end()) + throw std::runtime_error ("invalid ID: " + id); + + return iter->second; + } + template int IdCollection::getColumns() const { @@ -211,6 +234,39 @@ namespace CSMWorld std::mem_fun_ref (&Record::isErased) // I want lambda :( ), mRecords.end()); } + + template + void IdCollection::removeRows (int index, int count) + { + mRecords.erase (mRecords.begin()+index, mRecords.begin()+index+count); + + typename std::map::iterator iter = mIndex.begin(); + + while (iter!=mIndex.end()) + { + if (iter->second>=index) + { + if (iter->second>=index+count) + { + iter->second -= count; + } + else + { + mIndex.erase (iter++); + } + } + + ++iter; + } + } + + template + void IdCollection::appendBlankRecord (const std::string& id) + { + ESXRecordT record; + record.mId = id; + add (record); + } } #endif diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 1fc2bfedb..50a6953d6 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -71,4 +71,34 @@ Qt::ItemFlags CSMWorld::IdTable::flags (const QModelIndex & index) const flags |= Qt::ItemIsEditable; return flags; +} + +bool CSMWorld::IdTable::removeRows (int row, int count, const QModelIndex& parent) +{ + if (parent.isValid()) + return false; + + beginRemoveRows (parent, row, row+count-1); + + mIdCollection->removeRows (row, count); + + endRemoveRows(); + + return true; +} + +void CSMWorld::IdTable::addRecord (const std::string& id) +{ + int index = mIdCollection->getSize(); + + beginInsertRows (QModelIndex(), index, index); + + mIdCollection->appendBlankRecord (id); + + endInsertRows(); +} + +QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const +{ + return index (mIdCollection->getIndex (id), column); } \ No newline at end of file diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 30af3aaf7..f95873c7a 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -24,17 +24,23 @@ namespace CSMWorld virtual ~IdTable(); - int rowCount (const QModelIndex & parent = QModelIndex()) const; + virtual int rowCount (const QModelIndex & parent = QModelIndex()) const; - int columnCount (const QModelIndex & parent = QModelIndex()) const; + virtual int columnCount (const QModelIndex & parent = QModelIndex()) const; - QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; + virtual QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; - QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - bool setData ( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + virtual bool setData ( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - Qt::ItemFlags flags (const QModelIndex & index) const; + virtual Qt::ItemFlags flags (const QModelIndex & index) const; + + virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex()); + + void addRecord (const std::string& id); + + QModelIndex getModelIndex (const std::string& id, int column) const; }; } diff --git a/apps/opencs/model/world/idtableproxymodel.cpp b/apps/opencs/model/world/idtableproxymodel.cpp new file mode 100644 index 000000000..78995f60b --- /dev/null +++ b/apps/opencs/model/world/idtableproxymodel.cpp @@ -0,0 +1,18 @@ + +#include "idtableproxymodel.hpp" + +#include "idtable.hpp" + +CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent) +: QSortFilterProxyModel (parent) +{} + +void CSMWorld::IdTableProxyModel::addRecord (const std::string& id) +{ + dynamic_cast (*sourceModel()).addRecord (id); +} + +QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const +{ + return mapFromSource (dynamic_cast (*sourceModel()).getModelIndex (id, column)); +} \ No newline at end of file diff --git a/apps/opencs/model/world/idtableproxymodel.hpp b/apps/opencs/model/world/idtableproxymodel.hpp new file mode 100644 index 000000000..3f1537cce --- /dev/null +++ b/apps/opencs/model/world/idtableproxymodel.hpp @@ -0,0 +1,24 @@ +#ifndef CSM_WOLRD_IDTABLEPROXYMODEL_H +#define CSM_WOLRD_IDTABLEPROXYMODEL_H + +#include + +#include + +namespace CSMWorld +{ + class IdTableProxyModel : public QSortFilterProxyModel + { + Q_OBJECT + + public: + + IdTableProxyModel (QObject *parent = 0); + + virtual void addRecord (const std::string& id); + + virtual QModelIndex getModelIndex (const std::string& id, int column) const; + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/world/record.hpp b/apps/opencs/model/world/record.hpp index c08d2e0d1..df93501f3 100644 --- a/apps/opencs/model/world/record.hpp +++ b/apps/opencs/model/world/record.hpp @@ -5,18 +5,21 @@ namespace CSMWorld { - template - struct Record + struct RecordBase { enum State { - State_BaseOnly, // defined in base only - State_Modified, // exists in base, but has been modified - State_ModifiedOnly, // newly created in modified - State_Deleted, // exists in base, but has been deleted - State_Erased // does not exist at all (we mostly treat that the same way as deleted) + State_BaseOnly = 0, // defined in base only + State_Modified = 1, // exists in base, but has been modified + State_ModifiedOnly = 2, // newly created in modified + State_Deleted = 3, // exists in base, but has been deleted + State_Erased = 4 // does not exist at all (we mostly treat that the same way as deleted) }; + }; + template + struct Record : public RecordBase + { ESXRecordT mBase; ESXRecordT mModified; State mState; diff --git a/apps/opencs/view/world/globals.cpp b/apps/opencs/view/world/globals.cpp index 68c9012aa..20cdb80f4 100644 --- a/apps/opencs/view/world/globals.cpp +++ b/apps/opencs/view/world/globals.cpp @@ -6,7 +6,7 @@ CSVWorld::Globals::Globals (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack) : SubView (id) { - setWidget (mTable = new Table (id, data, undoStack)); + setWidget (mTable = new Table (id, data, undoStack, true)); } void CSVWorld::Globals::setEditLock (bool locked) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 590f7ea98..862ebdc79 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -3,12 +3,14 @@ #include #include -#include #include +#include +#include +#include #include "../../model/world/data.hpp" - #include "../../model/world/commands.hpp" +#include "../../model/world/idtableproxymodel.hpp" namespace CSVWorld { @@ -104,7 +106,19 @@ void CSVWorld::CommandDelegate::setEditLock (bool locked) } -CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack) + void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) +{ + QMenu menu (this); + + if (mCreateAction) + menu.addAction (mCreateAction); + + menu.exec (event->globalPos()); +} + +CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, + bool createAndDelete) +: mUndoStack (undoStack), mCreateAction (0) { QAbstractTableModel *model = data.getTableModel (id); @@ -117,10 +131,10 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q setItemDelegateForColumn (i, delegate); } - QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel (this); - proxyModel->setSourceModel (model); + mModel = new CSMWorld::IdTableProxyModel (this); + mModel->setSourceModel (model); - setModel (proxyModel); + setModel (mModel); horizontalHeader()->setResizeMode (QHeaderView::Interactive); verticalHeader()->hide(); setSortingEnabled (true); @@ -128,10 +142,29 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q setSelectionMode (QAbstractItemView::ExtendedSelection); /// \todo make initial layout fill the whole width of the table + + if (createAndDelete) + { + mCreateAction = new QAction (tr ("CreateRecord"), this); + connect (mCreateAction, SIGNAL (triggered()), this, SLOT (createRecord())); + addAction (mCreateAction); + } } void CSVWorld::Table::setEditLock (bool locked) { for (std::vector::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter) (*iter)->setEditLock (locked); +} + +#include /// \todo remove +void CSVWorld::Table::createRecord() +{ + /// \todo ask the user for an ID instead. + static int index = 0; + + std::ostringstream stream; + stream << "id" << index++; + + mUndoStack.push (new CSMWorld::CreateCommand (*mModel, stream.str())); } \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index ae203f516..f07f3ff24 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -6,11 +6,13 @@ #include class QUndoStack; +class QAction; namespace CSMWorld { class Data; class UniversalId; + class IdTableProxyModel; } namespace CSVWorld @@ -20,13 +22,27 @@ namespace CSVWorld ///< Table widget class Table : public QTableView { + Q_OBJECT + std::vector mDelegates; + QUndoStack& mUndoStack; + QAction *mCreateAction; + CSMWorld::IdTableProxyModel *mModel; + + private: + + void contextMenuEvent (QContextMenuEvent *event); public: - Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack); + Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete); + ///< \param createAndDelete Allow creation and deletion of records. void setEditLock (bool locked); + + private slots: + + void createRecord(); }; } From 0a8b7602d316c63d2909270be035d2137a6551a3 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 3 Dec 2012 21:51:42 +0100 Subject: [PATCH 069/378] fixed edit lock --- apps/opencs/view/doc/view.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 3d5ebc84b..1a8d56878 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -169,7 +169,7 @@ void CSVDoc::View::updateDocumentState() QList subViews = findChildren(); for (QList::iterator iter (subViews.begin()); iter!=subViews.end(); ++iter) - (*iter)->setEditLock (state && CSMDoc::Document::State_Locked); + (*iter)->setEditLock (state & CSMDoc::Document::State_Locked); } void CSVDoc::View::updateProgress (int current, int max, int type, int threads) From 8e93bfa607ba7e38a507e7ffe346df0f5c9ecaec Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 3 Dec 2012 22:03:02 +0100 Subject: [PATCH 070/378] turned the global class into a general purpose table subview class --- apps/opencs/CMakeLists.txt | 4 ++-- apps/opencs/view/doc/view.cpp | 5 ++--- apps/opencs/view/world/globals.cpp | 15 --------------- apps/opencs/view/world/globals.hpp | 24 ------------------------ apps/opencs/view/world/subview.hpp | 23 +++++++++++++++++++++++ apps/opencs/view/world/table.cpp | 2 +- apps/opencs/view/world/tablesubview.cpp | 16 ++++++++++++++++ apps/opencs/view/world/tablesubview.hpp | 25 +++++++++++++++++++++++++ 8 files changed, 69 insertions(+), 45 deletions(-) delete mode 100644 apps/opencs/view/world/globals.cpp delete mode 100644 apps/opencs/view/world/globals.hpp create mode 100644 apps/opencs/view/world/tablesubview.cpp create mode 100644 apps/opencs/view/world/tablesubview.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 6d18a1811..4ef4f93c7 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -9,7 +9,7 @@ set (OPENCS_SRC view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp - view/world/subview.cpp view/world/table.cpp view/world/globals.cpp + view/world/subview.cpp view/world/table.cpp view/world/tablesubview.cpp ) set (OPENCS_HDR @@ -23,7 +23,7 @@ set (OPENCS_HDR view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp - view/world/subview.hpp view/world/table.hpp view/world/globals.hpp + view/world/subview.hpp view/world/table.hpp view/world/tablesubview.hpp ) set (OPENCS_US diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 1a8d56878..65ec103a2 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -10,8 +10,7 @@ #include "../../model/doc/document.hpp" -#include "../world/subview.hpp" -#include "../world/globals.hpp" +#include "../world/tablesubview.hpp" #include "viewmanager.hpp" #include "operations.hpp" @@ -122,7 +121,7 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to setupUi(); mSubViewFactories.insert (std::make_pair (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), - new CSVWorld::SubViewFactory())); + new CSVWorld::SubViewFactoryWithCreateFlag (true))); } CSVDoc::View::~View() diff --git a/apps/opencs/view/world/globals.cpp b/apps/opencs/view/world/globals.cpp deleted file mode 100644 index 20cdb80f4..000000000 --- a/apps/opencs/view/world/globals.cpp +++ /dev/null @@ -1,15 +0,0 @@ - -#include "globals.hpp" - -#include "table.hpp" - -CSVWorld::Globals::Globals (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack) -: SubView (id) -{ - setWidget (mTable = new Table (id, data, undoStack, true)); -} - -void CSVWorld::Globals::setEditLock (bool locked) -{ - mTable->setEditLock (locked); -} \ No newline at end of file diff --git a/apps/opencs/view/world/globals.hpp b/apps/opencs/view/world/globals.hpp deleted file mode 100644 index 33cbce47f..000000000 --- a/apps/opencs/view/world/globals.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef CSV_WORLD_GLOBALS_H -#define CSV_WORLD_GLOBALS_H - -#include "subview.hpp" - -class QUndoStack; - -namespace CSVWorld -{ - class Table; - - class Globals : public SubView - { - Table *mTable; - - public: - - Globals (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack); - - virtual void setEditLock (bool locked); - }; -} - -#endif \ No newline at end of file diff --git a/apps/opencs/view/world/subview.hpp b/apps/opencs/view/world/subview.hpp index 543a9b2a1..95448cb47 100644 --- a/apps/opencs/view/world/subview.hpp +++ b/apps/opencs/view/world/subview.hpp @@ -51,6 +51,29 @@ namespace CSVWorld { return new SubViewT (id, data, undoStack); } + + template + struct SubViewFactoryWithCreateFlag : public SubViewFactoryBase + { + bool mCreateAndDelete; + + SubViewFactoryWithCreateFlag (bool createAndDelete); + + virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack); + }; + + template + SubViewFactoryWithCreateFlag::SubViewFactoryWithCreateFlag (bool createAndDelete) + : mCreateAndDelete (createAndDelete) + {} + + template + SubView *SubViewFactoryWithCreateFlag::makeSubView (const CSMWorld::UniversalId& id, + CSMWorld::Data& data, + QUndoStack& undoStack) + { + return new SubViewT (id, data, undoStack, mCreateAndDelete); + } } #endif \ No newline at end of file diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 862ebdc79..4f20d9fd2 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -145,7 +145,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q if (createAndDelete) { - mCreateAction = new QAction (tr ("CreateRecord"), this); + mCreateAction = new QAction (tr ("Add Record"), this); connect (mCreateAction, SIGNAL (triggered()), this, SLOT (createRecord())); addAction (mCreateAction); } diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp new file mode 100644 index 000000000..3bc555a2d --- /dev/null +++ b/apps/opencs/view/world/tablesubview.cpp @@ -0,0 +1,16 @@ + +#include "tablesubview.hpp" + +#include "table.hpp" + +CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, + bool createAndDelete) +: SubView (id) +{ + setWidget (mTable = new Table (id, data, undoStack, createAndDelete)); +} + +void CSVWorld::TableSubView::setEditLock (bool locked) +{ + mTable->setEditLock (locked); +} \ No newline at end of file diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp new file mode 100644 index 000000000..b45b3c279 --- /dev/null +++ b/apps/opencs/view/world/tablesubview.hpp @@ -0,0 +1,25 @@ +#ifndef CSV_WORLD_TABLESUBVIEW_H +#define CSV_WORLD_TABLESUBVIEW_H + +#include "subview.hpp" + +class QUndoStack; + +namespace CSVWorld +{ + class Table; + + class TableSubView : public SubView + { + Table *mTable; + + public: + + TableSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, + bool createAndDelete); + + virtual void setEditLock (bool locked); + }; +} + +#endif \ No newline at end of file From 5cd2fe00ab41eaef4618e017061fd86630987338 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 3 Dec 2012 22:16:02 +0100 Subject: [PATCH 071/378] initialise blank global records --- apps/opencs/model/world/idcollection.hpp | 1 + components/esm/loadglob.cpp | 6 ++++++ components/esm/loadglob.hpp | 3 +++ 3 files changed, 10 insertions(+) diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index be7891bd2..e4bb31dbe 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -265,6 +265,7 @@ namespace CSMWorld { ESXRecordT record; record.mId = id; + record.blank(); add (record); } } diff --git a/components/esm/loadglob.cpp b/components/esm/loadglob.cpp index 0335da0df..ceaa86948 100644 --- a/components/esm/loadglob.cpp +++ b/components/esm/loadglob.cpp @@ -44,6 +44,12 @@ void Global::save(ESMWriter &esm) esm.writeHNT("FLTV", mValue); } + void Global::blank() + { + mValue = 0; + mType = VT_Float; + } + bool operator== (const Global& left, const Global& right) { return left.mId==right.mId && left.mValue==right.mValue && left.mType==right.mType; diff --git a/components/esm/loadglob.hpp b/components/esm/loadglob.hpp index 5c5dafaec..6111648a6 100644 --- a/components/esm/loadglob.hpp +++ b/components/esm/loadglob.hpp @@ -23,6 +23,9 @@ struct Global void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID). }; bool operator== (const Global& left, const Global& right); From 8e1a2e3a13f7f31560d8f12539ba0acb9603a269 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Dec 2012 10:58:43 +0100 Subject: [PATCH 072/378] Issue #474: adjust global variable pcrace --- apps/openmw/mwworld/worldimp.cpp | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 616a0be39..f0b2efcec 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -193,7 +193,7 @@ namespace MWWorld mRendering->attachCameraTo(mPlayer->getPlayer()); mPhysics->addActor(mPlayer->getPlayer()); - + // global variables mGlobalVariables = new Globals (mStore); @@ -203,6 +203,8 @@ namespace MWWorld mGlobalVariables->setInt ("chargenstate", 1); } + mGlobalVariables->setInt ("pcrace", 3); + mWorldScene = new Scene(*mRendering, mPhysics); setFallbackValues(fallbackMap); @@ -660,7 +662,7 @@ namespace MWWorld { MWWorld::Class::get(ptr).adjustScale(ptr,scale); ptr.getCellRef().mScale = scale; - + if(ptr.getRefData().getBaseNode() == 0) return; mRendering->scaleObject(ptr, Vector3(scale,scale,scale)); @@ -673,7 +675,7 @@ namespace MWWorld rot.x = Ogre::Degree(x).valueRadians(); rot.y = Ogre::Degree(y).valueRadians(); rot.z = Ogre::Degree(z).valueRadians(); - + float *objRot = ptr.getRefData().getPosition().rot; if(ptr.getRefData().getBaseNode() == 0 || !mRendering->rotateObject(ptr, rot, adjust)) { @@ -781,7 +783,7 @@ namespace MWWorld const ESM::Potion *World::createRecord (const ESM::Potion& record) { - return mStore.insert(record); + return mStore.insert(record); } const ESM::Class *World::createRecord (const ESM::Class& record) @@ -802,7 +804,23 @@ namespace MWWorld const ESM::NPC *World::createRecord(const ESM::NPC &record) { bool update = false; - if (StringUtils::ciEqual(record.mId, "player")) { + + if (StringUtils::ciEqual(record.mId, "player")) + { + static const char *sRaces[] = + { + "Argonian", "Breton", "Dark Elf", "High Elf", "Imperial", "Khajiit", "Nord", "Orc", "Redguard", + "Woodelf", 0 + }; + + int i=0; + + for (; sRaces[i]; ++i) + if (StringUtils::ciEqual (sRaces[i], record.mRace)) + break; + + mGlobalVariables->setInt ("pcrace", sRaces[i] ? i+1 : 0); + const ESM::NPC *player = mPlayer->getPlayer().get()->mBase; @@ -834,7 +852,7 @@ namespace MWWorld /// \todo split this function up into subfunctions mWorldScene->update (duration, paused); - + float pitch, yaw; Ogre::Vector3 eyepos; mRendering->getPlayerData(eyepos, pitch, yaw); From f07b7d17cdb1e07d907fe1518bd7249b695e64f3 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Dec 2012 20:43:47 +0100 Subject: [PATCH 073/378] improved exception handling --- apps/opencs/main.cpp | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index 15772eba0..4b1a688c2 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -1,11 +1,37 @@ #include "editor.hpp" +#include +#include + #include +class Application : public QApplication +{ + private: + + bool notify (QObject *receiver, QEvent *event) + { + try + { + return QApplication::notify (receiver, event); + } + catch (const std::exception& exception) + { + std::cerr << "An exception has been caught: " << exception.what() << std::endl; + } + + return false; + } + + public: + + Application (int& argc, char *argv[]) : QApplication (argc, argv) {} +}; + int main(int argc, char *argv[]) { - QApplication mApplication (argc, argv); + Application mApplication (argc, argv); CS::Editor editor; From 8bcd7d8fb19ad4126660e48b05b005caf91488d0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 4 Dec 2012 20:56:45 +0100 Subject: [PATCH 074/378] fixed a type in a script instruction --- apps/openmw/mwscript/statsextensions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 69ac7d5fe..c67782168 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -1090,7 +1090,7 @@ namespace MWScript "alteration", "illusion", "conjuration", "mysticism", "restoration", "alchemy", "unarmored", "security", "sneak", "acrobatics", "lightarmor", "shortblade", "marksman", - "merchantile", "speechcraft", "handtohand" + "mercantile", "speechcraft", "handtohand" }; std::string get ("get"); From 5332546541e9b52b1ce08363a04f7fa81152592c Mon Sep 17 00:00:00 2001 From: eduard Date: Thu, 6 Dec 2012 13:58:52 +0100 Subject: [PATCH 075/378] tradding skill use --- apps/openmw/mwgui/tradewindow.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index c6a21c461..bde8d9b84 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -239,6 +239,14 @@ namespace MWGui MWBase::Environment::get().getDialogueManager()->applyTemporaryDispositionChange(iBarterFailDisposition); return; } + + //skill use! + MWWorld::LiveCellRef *ref = playerPtr.get(); + const ESM::Class *class_ = + MWBase::Environment::get().getWorld()->getStore().get().find ( + ref->mBase->mClass + ); + playerSkill.useSkill(ESM::Skill::Mercantile,*class_); } int iBarterSuccessDisposition = gmst.find("iBarterSuccessDisposition")->getInt(); From ea8ee11ff723ec3deb2392347144b98a6baa5a97 Mon Sep 17 00:00:00 2001 From: eduard Date: Thu, 6 Dec 2012 14:25:53 +0100 Subject: [PATCH 076/378] tradding skill use --- apps/openmw/mwgui/tradewindow.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index bde8d9b84..945104537 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -241,13 +241,8 @@ namespace MWGui } //skill use! - MWWorld::LiveCellRef *ref = playerPtr.get(); - const ESM::Class *class_ = - MWBase::Environment::get().getWorld()->getStore().get().find ( - ref->mBase->mClass - ); - playerSkill.useSkill(ESM::Skill::Mercantile,*class_); - } + MWWorld::Class::get(playerPtr).skillUsageSucceeded(playerPtr, ESM::Skill::Mercantile, 0); + } int iBarterSuccessDisposition = gmst.find("iBarterSuccessDisposition")->getInt(); MWBase::Environment::get().getDialogueManager()->applyTemporaryDispositionChange(iBarterSuccessDisposition); From b41cc5e9e943580281b7be67262de633c41f8404 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 6 Dec 2012 14:56:04 +0100 Subject: [PATCH 077/378] added revert command --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/world/columns.hpp | 7 +- apps/opencs/model/world/commands.cpp | 34 +++++++++ apps/opencs/model/world/commands.hpp | 23 +++++++ apps/opencs/model/world/idcollection.hpp | 87 +++++++++++++++++++++++- apps/opencs/model/world/idtable.cpp | 32 ++++++++- apps/opencs/model/world/idtable.hpp | 6 ++ apps/opencs/model/world/record.cpp | 21 ++++++ apps/opencs/model/world/record.hpp | 35 ++++------ apps/opencs/view/world/table.cpp | 55 +++++++++++++-- apps/opencs/view/world/table.hpp | 7 +- 11 files changed, 275 insertions(+), 34 deletions(-) create mode 100644 apps/opencs/model/world/record.cpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 4ef4f93c7..5a9e1e99c 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -5,7 +5,7 @@ set (OPENCS_SRC model/doc/documentmanager.cpp model/doc/document.cpp model/world/universalid.cpp model/world/idcollection.cpp model/world/data.cpp model/world/idtable.cpp - model/world/commands.cpp model/world/idtableproxymodel.cpp + model/world/commands.cpp model/world/idtableproxymodel.cpp model/world/record.cpp view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 483ce929d..188d3a2ac 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -57,9 +57,14 @@ namespace CSMWorld return static_cast (record.mState); } + virtual void set (Record& record, const QVariant& data) + { + record.mState = static_cast (data.toInt()); + } + virtual bool isEditable() const { - return false; + return true; } }; } diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 7bb76acd0..96a1b639c 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -4,6 +4,7 @@ #include #include "idtableproxymodel.hpp" +#include "idtable.hpp" CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand *parent) @@ -38,4 +39,37 @@ void CSMWorld::CreateCommand::redo() void CSMWorld::CreateCommand::undo() { mModel.removeRow (mModel.getModelIndex (mId, 0).row()); +} + +CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand *parent) +: QUndoCommand (parent), mModel (model), mId (id), mOld (0) +{ + setText (("Revert record " + id).c_str()); + + mOld = model.getRecord (id).clone(); +} + +CSMWorld::RevertCommand::~RevertCommand() +{ + delete mOld; +} + +void CSMWorld::RevertCommand::redo() +{ + QModelIndex index = mModel.getModelIndex (mId, 1); + RecordBase::State state = static_cast (mModel.data (index).toInt()); + + if (state==RecordBase::State_ModifiedOnly) + { + mModel.removeRows (index.row(), 1); + } + else + { + mModel.setData (index, static_cast (RecordBase::State_BaseOnly)); + } +} + +void CSMWorld::RevertCommand::undo() +{ + mModel.setRecord (*mOld); } \ No newline at end of file diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index 50b9045c6..e916d05c9 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -15,6 +15,8 @@ class QAbstractItemModel; namespace CSMWorld { class IdTableProxyModel; + class IdTable; + class RecordBase; class ModifyCommand : public QUndoCommand { @@ -46,6 +48,27 @@ namespace CSMWorld virtual void undo(); }; + + class RevertCommand : public QUndoCommand + { + IdTable& mModel; + std::string mId; + RecordBase *mOld; + + // not implemented + RevertCommand (const RevertCommand&); + RevertCommand& operator= (const RevertCommand&); + + public: + + RevertCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0); + + virtual ~RevertCommand(); + + virtual void redo(); + + virtual void undo(); + }; } #endif \ No newline at end of file diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index e4bb31dbe..50afa715e 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -71,6 +71,25 @@ namespace CSMWorld virtual void removeRows (int index, int count) = 0; virtual void appendBlankRecord (const std::string& id) = 0; + + virtual int searchId (const std::string& id) const = 0; + ////< Search record with \a id. + /// \return index of record (if found) or -1 (not found) + + virtual void replace (int index, const RecordBase& record) = 0; + ///< If the record type does not match, an exception is thrown. + /// + /// \attention \a record must not change the ID. + + virtual void appendRecord (const RecordBase& record) = 0; + ///< If the record type does not match, an exception is thrown. + + virtual std::string getId (const RecordBase& record) const = 0; + ///< Return ID for \a record. + /// + /// \attention Throw san exception, if the type of \a record does not match. + + virtual const RecordBase& getRecord (const std::string& id) const = 0; }; ///< \brief Collection of ID-based records @@ -120,6 +139,25 @@ namespace CSMWorld virtual void appendBlankRecord (const std::string& id); + virtual int searchId (const std::string& id) const; + ////< Search record with \a id. + /// \return index of record (if found) or -1 (not found) + + virtual void replace (int index, const RecordBase& record); + ///< If the record type does not match, an exception is thrown. + /// + /// \attention \a record must not change the ID. + + virtual void appendRecord (const RecordBase& record); + ///< If the record type does not match, an exception is thrown. + + virtual std::string getId (const RecordBase& record) const; + ///< Return ID for \a record. + /// + /// \attention Throw san exception, if the type of \a record does not match. + + virtual const RecordBase& getRecord (const std::string& id) const; + void addColumn (Column *column); }; @@ -174,12 +212,12 @@ namespace CSMWorld template int IdCollection::getIndex (const std::string& id) const { - std::map::const_iterator iter = mIndex.find (id); + int index = searchId (id); - if (iter==mIndex.end()) + if (index==-1) throw std::runtime_error ("invalid ID: " + id); - return iter->second; + return index; } template @@ -268,6 +306,49 @@ namespace CSMWorld record.blank(); add (record); } + + template + int IdCollection::searchId (const std::string& id) const + { + std::string id2; + + std::transform (id.begin(), id.end(), std::back_inserter (id2), + (int(*)(int)) std::tolower); + + std::map::const_iterator iter = mIndex.find (id2); + + if (iter==mIndex.end()) + return -1; + + return iter->second; + } + + template + void IdCollection::replace (int index, const RecordBase& record) + { + mRecords.at (index) = dynamic_cast&> (record); + } + + template + void IdCollection::appendRecord (const RecordBase& record) + { + mRecords.push_back (dynamic_cast&> (record)); + mIndex.insert (std::make_pair (getId (record), mRecords.size()-1)); + } + + template + std::string IdCollection::getId (const RecordBase& record) const + { + const Record& record2 = dynamic_cast&> (record); + return (record2.isModified() ? record2.mModified : record2.mBase).mId; + } + + template + const RecordBase& IdCollection::getRecord (const std::string& id) const + { + int index = getIndex (id); + return mRecords.at (index); + } } #endif diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 50a6953d6..a2b0a3c1f 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -56,7 +56,10 @@ bool CSMWorld::IdTable::setData ( const QModelIndex &index, const QVariant &valu if (mIdCollection->isEditable (index.column()) && role==Qt::EditRole) { mIdCollection->setData (index.row(), index.column(), value); - emit dataChanged (index, index); + + emit dataChanged (CSMWorld::IdTable::index (index.row(), 0), + CSMWorld::IdTable::index (index.row(), mIdCollection->getColumns()-1)); + return true; } @@ -101,4 +104,31 @@ void CSMWorld::IdTable::addRecord (const std::string& id) QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const { return index (mIdCollection->getIndex (id), column); +} + +void CSMWorld::IdTable::setRecord (const RecordBase& record) +{ + int index = mIdCollection->searchId (mIdCollection->getId (record)); + + if (index==-1) + { + int index = mIdCollection->getSize(); + + beginInsertRows (QModelIndex(), index, index); + + mIdCollection->appendRecord (record); + + endInsertRows(); + } + else + { + mIdCollection->replace (index, record); + emit dataChanged (CSMWorld::IdTable::index (index, 0), + CSMWorld::IdTable::index (index, mIdCollection->getColumns()-1)); + } +} + +const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id) const +{ + return mIdCollection->getRecord (id); } \ No newline at end of file diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index f95873c7a..deaebaa38 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -6,6 +6,7 @@ namespace CSMWorld { class IdCollectionBase; + class RecordBase; class IdTable : public QAbstractTableModel { @@ -41,6 +42,11 @@ namespace CSMWorld void addRecord (const std::string& id); QModelIndex getModelIndex (const std::string& id, int column) const; + + void setRecord (const RecordBase& record); + ///< Add record or overwrite existing recrod. + + const RecordBase& getRecord (const std::string& id) const; }; } diff --git a/apps/opencs/model/world/record.cpp b/apps/opencs/model/world/record.cpp new file mode 100644 index 000000000..229985a8a --- /dev/null +++ b/apps/opencs/model/world/record.cpp @@ -0,0 +1,21 @@ + +#include "record.hpp" + +CSMWorld::RecordBase::~RecordBase() {} + +bool CSMWorld::RecordBase::RecordBase::isDeleted() const +{ + return mState==State_Deleted || mState==State_Erased; +} + + +bool CSMWorld::RecordBase::RecordBase::isErased() const +{ + return mState==State_Erased; +} + + +bool CSMWorld::RecordBase::RecordBase::isModified() const +{ + return mState==State_Modified || mState==State_ModifiedOnly; +} \ No newline at end of file diff --git a/apps/opencs/model/world/record.hpp b/apps/opencs/model/world/record.hpp index df93501f3..3b83836ab 100644 --- a/apps/opencs/model/world/record.hpp +++ b/apps/opencs/model/world/record.hpp @@ -15,20 +15,27 @@ namespace CSMWorld State_Deleted = 3, // exists in base, but has been deleted State_Erased = 4 // does not exist at all (we mostly treat that the same way as deleted) }; - }; - template - struct Record : public RecordBase - { - ESXRecordT mBase; - ESXRecordT mModified; State mState; + virtual ~RecordBase(); + + virtual RecordBase *clone() const = 0; + bool isDeleted() const; bool isErased() const; bool isModified() const; + }; + + template + struct Record : public RecordBase + { + ESXRecordT mBase; + ESXRecordT mModified; + + virtual RecordBase *clone() const; const ESXRecordT& get() const; ///< Throws an exception, if the record is deleted. @@ -44,21 +51,9 @@ namespace CSMWorld }; template - bool Record::isDeleted() const - { - return mState==State_Deleted || mState==State_Erased; - } - - template - bool Record::isErased() const - { - return mState==State_Erased; - } - - template - bool Record::isModified() const + RecordBase *Record::clone() const { - return mState==State_Modified || mState==State_ModifiedOnly; + return new Record (*this); } template diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 4f20d9fd2..dafcb95c0 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -11,6 +11,8 @@ #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" #include "../../model/world/idtableproxymodel.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/record.hpp" namespace CSVWorld { @@ -108,11 +110,16 @@ void CSVWorld::CommandDelegate::setEditLock (bool locked) void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) { - QMenu menu (this); + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + QMenu menu (this); if (mCreateAction) menu.addAction (mCreateAction); + if (selectedRows.size()>0) + menu.addAction (mRevertAction); + menu.exec (event->globalPos()); } @@ -120,9 +127,9 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q bool createAndDelete) : mUndoStack (undoStack), mCreateAction (0) { - QAbstractTableModel *model = data.getTableModel (id); + mModel = &dynamic_cast (*data.getTableModel (id)); - int columns = model->columnCount(); + int columns = mModel->columnCount(); for (int i=0; isetSourceModel (model); + mProxyModel = new CSMWorld::IdTableProxyModel (this); + mProxyModel->setSourceModel (mModel); - setModel (mModel); + setModel (mProxyModel); horizontalHeader()->setResizeMode (QHeaderView::Interactive); verticalHeader()->hide(); setSortingEnabled (true); @@ -149,6 +156,10 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q connect (mCreateAction, SIGNAL (triggered()), this, SLOT (createRecord())); addAction (mCreateAction); } + + mRevertAction = new QAction (tr ("Revert Record"), this); + connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord())); + addAction (mRevertAction); } void CSVWorld::Table::setEditLock (bool locked) @@ -166,5 +177,35 @@ void CSVWorld::Table::createRecord() std::ostringstream stream; stream << "id" << index++; - mUndoStack.push (new CSMWorld::CreateCommand (*mModel, stream.str())); + mUndoStack.push (new CSMWorld::CreateCommand (*mProxyModel, stream.str())); +} + +void CSVWorld::Table::revertRecord() +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + std::vector revertableIds; + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) + { + std::string id = mProxyModel->data (*iter).toString().toStdString(); + + CSMWorld::RecordBase::State state = + static_cast (mModel->data (mModel->getModelIndex (id, 1)).toInt()); + + if (state!=CSMWorld::RecordBase::State_BaseOnly) + revertableIds.push_back (id); + } + + if (revertableIds.size()>0) + { + if (revertableIds.size()>1) + mUndoStack.beginMacro (tr ("Revert multiple records")); + + for (std::vector::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) + mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter)); + + if (revertableIds.size()>1) + mUndoStack.endMacro(); + } } \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index f07f3ff24..022d4f12a 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -13,6 +13,7 @@ namespace CSMWorld class Data; class UniversalId; class IdTableProxyModel; + class IdTable; } namespace CSVWorld @@ -27,7 +28,9 @@ namespace CSVWorld std::vector mDelegates; QUndoStack& mUndoStack; QAction *mCreateAction; - CSMWorld::IdTableProxyModel *mModel; + QAction *mRevertAction; + CSMWorld::IdTableProxyModel *mProxyModel; + CSMWorld::IdTable *mModel; private: @@ -43,6 +46,8 @@ namespace CSVWorld private slots: void createRecord(); + + void revertRecord(); }; } From c12ee129f7019f5664f3a25b28e8f8961b5121bb Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 6 Dec 2012 15:18:41 +0100 Subject: [PATCH 078/378] added delete command --- apps/opencs/model/world/commands.cpp | 33 +++++++++++++++++++++++++ apps/opencs/model/world/commands.hpp | 21 ++++++++++++++++ apps/opencs/model/world/record.hpp | 6 ++--- apps/opencs/view/world/table.cpp | 37 ++++++++++++++++++++++++++++ apps/opencs/view/world/table.hpp | 3 +++ 5 files changed, 97 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 96a1b639c..e22ecf992 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -70,6 +70,39 @@ void CSMWorld::RevertCommand::redo() } void CSMWorld::RevertCommand::undo() +{ + mModel.setRecord (*mOld); +} + +CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent) +: QUndoCommand (parent), mModel (model), mId (id), mOld (0) +{ + setText (("Delete record " + id).c_str()); + + mOld = model.getRecord (id).clone(); +} + +CSMWorld::DeleteCommand::~DeleteCommand() +{ + delete mOld; +} + +void CSMWorld::DeleteCommand::redo() +{ + QModelIndex index = mModel.getModelIndex (mId, 1); + RecordBase::State state = static_cast (mModel.data (index).toInt()); + + if (state==RecordBase::State_ModifiedOnly) + { + mModel.removeRows (index.row(), 1); + } + else + { + mModel.setData (index, static_cast (RecordBase::State_Deleted)); + } +} + +void CSMWorld::DeleteCommand::undo() { mModel.setRecord (*mOld); } \ No newline at end of file diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index e916d05c9..af419215d 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -69,6 +69,27 @@ namespace CSMWorld virtual void undo(); }; + + class DeleteCommand : public QUndoCommand + { + IdTable& mModel; + std::string mId; + RecordBase *mOld; + + // not implemented + DeleteCommand (const DeleteCommand&); + DeleteCommand& operator= (const DeleteCommand&); + + public: + + DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0); + + virtual ~DeleteCommand(); + + virtual void redo(); + + virtual void undo(); + }; } #endif \ No newline at end of file diff --git a/apps/opencs/model/world/record.hpp b/apps/opencs/model/world/record.hpp index 3b83836ab..53bb7ea2c 100644 --- a/apps/opencs/model/world/record.hpp +++ b/apps/opencs/model/world/record.hpp @@ -59,7 +59,7 @@ namespace CSMWorld template const ESXRecordT& Record::get() const { - if (isDeleted()) + if (mState==State_Erased) throw std::logic_error ("attempt to access a deleted record"); return mState==State_BaseOnly ? mBase : mModified; @@ -68,7 +68,7 @@ namespace CSMWorld template const ESXRecordT& Record::getBase() const { - if (isDeleted()) + if (mState==State_Erased) throw std::logic_error ("attempt to access a deleted record"); return mState==State_ModifiedOnly ? mModified : mBase; @@ -77,7 +77,7 @@ namespace CSMWorld template void Record::setModified (const ESXRecordT& modified) { - if (isDeleted()) + if (mState==State_Erased) throw std::logic_error ("attempt to modify a deleted record"); mModified = modified; diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index dafcb95c0..1595b6926 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -120,6 +120,9 @@ void CSVWorld::CommandDelegate::setEditLock (bool locked) if (selectedRows.size()>0) menu.addAction (mRevertAction); + if (selectedRows.size()>0) + menu.addAction (mDeleteAction); + menu.exec (event->globalPos()); } @@ -160,6 +163,10 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q mRevertAction = new QAction (tr ("Revert Record"), this); connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord())); addAction (mRevertAction); + + mDeleteAction = new QAction (tr ("Delete Record"), this); + connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); + addAction (mDeleteAction); } void CSVWorld::Table::setEditLock (bool locked) @@ -205,6 +212,36 @@ void CSVWorld::Table::revertRecord() for (std::vector::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter)); + if (revertableIds.size()>1) + mUndoStack.endMacro(); + } +} + +void CSVWorld::Table::deleteRecord() +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + std::vector revertableIds; + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) + { + std::string id = mProxyModel->data (*iter).toString().toStdString(); + + CSMWorld::RecordBase::State state = + static_cast (mModel->data (mModel->getModelIndex (id, 1)).toInt()); + + if (state!=CSMWorld::RecordBase::State_Deleted) + revertableIds.push_back (id); + } + + if (revertableIds.size()>0) + { + if (revertableIds.size()>1) + mUndoStack.beginMacro (tr ("Delete multiple records")); + + for (std::vector::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) + mUndoStack.push (new CSMWorld::DeleteCommand (*mModel, *iter)); + if (revertableIds.size()>1) mUndoStack.endMacro(); } diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 022d4f12a..555dc8e1f 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -29,6 +29,7 @@ namespace CSVWorld QUndoStack& mUndoStack; QAction *mCreateAction; QAction *mRevertAction; + QAction *mDeleteAction; CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTable *mModel; @@ -48,6 +49,8 @@ namespace CSVWorld void createRecord(); void revertRecord(); + + void deleteRecord(); }; } From 2b53cf6547f9611be8e4c6508865437a331f373c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 6 Dec 2012 15:25:31 +0100 Subject: [PATCH 079/378] do not list actions in the pop up menu that do not apply to any of the selected records --- apps/opencs/view/world/table.cpp | 85 ++++++++++++++++++-------------- apps/opencs/view/world/table.hpp | 5 ++ 2 files changed, 54 insertions(+), 36 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 1595b6926..31f7d22ed 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -107,8 +107,7 @@ void CSVWorld::CommandDelegate::setEditLock (bool locked) mEditLock = locked; } - - void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) +void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) { QModelIndexList selectedRows = selectionModel()->selectedRows(); @@ -117,15 +116,55 @@ void CSVWorld::CommandDelegate::setEditLock (bool locked) if (mCreateAction) menu.addAction (mCreateAction); - if (selectedRows.size()>0) + if (listRevertableSelectedIds().size()>0) menu.addAction (mRevertAction); - if (selectedRows.size()>0) + if (listDeletableSelectedIds().size()>0) menu.addAction (mDeleteAction); menu.exec (event->globalPos()); } +std::vector CSVWorld::Table::listRevertableSelectedIds() const +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + std::vector revertableIds; + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) + { + std::string id = mProxyModel->data (*iter).toString().toStdString(); + + CSMWorld::RecordBase::State state = + static_cast (mModel->data (mModel->getModelIndex (id, 1)).toInt()); + + if (state!=CSMWorld::RecordBase::State_BaseOnly) + revertableIds.push_back (id); + } + + return revertableIds; +} + +std::vector CSVWorld::Table::listDeletableSelectedIds() const +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + std::vector deletableIds; + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) + { + std::string id = mProxyModel->data (*iter).toString().toStdString(); + + CSMWorld::RecordBase::State state = + static_cast (mModel->data (mModel->getModelIndex (id, 1)).toInt()); + + if (state!=CSMWorld::RecordBase::State_Deleted) + deletableIds.push_back (id); + } + + return deletableIds; +} + CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete) : mUndoStack (undoStack), mCreateAction (0) @@ -189,20 +228,7 @@ void CSVWorld::Table::createRecord() void CSVWorld::Table::revertRecord() { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - std::vector revertableIds; - - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) - { - std::string id = mProxyModel->data (*iter).toString().toStdString(); - - CSMWorld::RecordBase::State state = - static_cast (mModel->data (mModel->getModelIndex (id, 1)).toInt()); - - if (state!=CSMWorld::RecordBase::State_BaseOnly) - revertableIds.push_back (id); - } + std::vector revertableIds = listRevertableSelectedIds(); if (revertableIds.size()>0) { @@ -219,30 +245,17 @@ void CSVWorld::Table::revertRecord() void CSVWorld::Table::deleteRecord() { - QModelIndexList selectedRows = selectionModel()->selectedRows(); + std::vector deletableIds = listDeletableSelectedIds(); - std::vector revertableIds; - - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) - { - std::string id = mProxyModel->data (*iter).toString().toStdString(); - - CSMWorld::RecordBase::State state = - static_cast (mModel->data (mModel->getModelIndex (id, 1)).toInt()); - - if (state!=CSMWorld::RecordBase::State_Deleted) - revertableIds.push_back (id); - } - - if (revertableIds.size()>0) + if (deletableIds.size()>0) { - if (revertableIds.size()>1) + if (deletableIds.size()>1) mUndoStack.beginMacro (tr ("Delete multiple records")); - for (std::vector::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) + for (std::vector::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter) mUndoStack.push (new CSMWorld::DeleteCommand (*mModel, *iter)); - if (revertableIds.size()>1) + if (deletableIds.size()>1) mUndoStack.endMacro(); } } \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 555dc8e1f..264d2bc5c 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -2,6 +2,7 @@ #define CSV_WORLD_TABLE_H #include +#include #include @@ -37,6 +38,10 @@ namespace CSVWorld void contextMenuEvent (QContextMenuEvent *event); + std::vector listRevertableSelectedIds() const; + + std::vector listDeletableSelectedIds() const; + public: Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete); From b2a0e4b1fd4d54fc91a80d352e0d7a017832c4e5 Mon Sep 17 00:00:00 2001 From: eduard Date: Thu, 6 Dec 2012 18:19:35 +0100 Subject: [PATCH 080/378] tradding skill use, spechcraft use --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index a97cf98de..5fd0764bc 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -445,11 +445,9 @@ namespace MWDialogue else if (curDisp + mTemporaryDispositionChange > 100) mTemporaryDispositionChange = 100 - curDisp; - // practice skill + // practice skill, it doesn't need to be a success to use skill MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - - if (success) - MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Speechcraft, 0); + MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Speechcraft, 0); // add status message to dialogue window std::string text; From 2d62649dc909d9114854ef81033783c1ff547560 Mon Sep 17 00:00:00 2001 From: eduard Date: Thu, 6 Dec 2012 18:30:19 +0100 Subject: [PATCH 081/378] tradding skill use, spechcraft use --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 5fd0764bc..3d9392a97 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -431,6 +431,7 @@ namespace MWDialogue void DialogueManager::persuade(int type) { bool success; + bool skillincrease = 1; float temp, perm; MWBase::Environment::get().getMechanicsManager()->getPersuasionDispositionChange( mActor, MWBase::MechanicsManager::PersuasionType(type), mTemporaryDispositionChange, @@ -445,10 +446,6 @@ namespace MWDialogue else if (curDisp + mTemporaryDispositionChange > 100) mTemporaryDispositionChange = 100 - curDisp; - // practice skill, it doesn't need to be a success to use skill - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Speechcraft, 0); - // add status message to dialogue window std::string text; @@ -458,8 +455,16 @@ namespace MWDialogue text = "sTaunt"; else if (type == MWBase::MechanicsManager::PT_Intimidate) text = "sIntimidate"; - else + else{ text = "sBribe"; + skillincrease = success; + } + + if (skillincrease){ + // practice skill, it doesn't need to be a success to use skill + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Speechcraft, 0); + } text += (success ? "Success" : "Fail"); From e50b9aca86e0edd417d08a4f7c5f969b2934b66a Mon Sep 17 00:00:00 2001 From: eduard Date: Thu, 6 Dec 2012 20:58:33 +0100 Subject: [PATCH 082/378] tradding skill use, spechcraft use --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 3d9392a97..9c43d4a67 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -461,9 +461,9 @@ namespace MWDialogue } if (skillincrease){ - // practice skill, it doesn't need to be a success to use skill + // practice skill MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Speechcraft, 0); + MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Speechcraft, -1); } text += (success ? "Success" : "Fail"); From 386eec51c16caaac216736334644bb68c8a84910 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 7 Dec 2012 11:36:38 +0100 Subject: [PATCH 083/378] some skill usage fixes --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 13 ++++--------- apps/openmw/mwgui/tradewindow.cpp | 6 +++--- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 9c43d4a67..737d22280 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -431,7 +431,6 @@ namespace MWDialogue void DialogueManager::persuade(int type) { bool success; - bool skillincrease = 1; float temp, perm; MWBase::Environment::get().getMechanicsManager()->getPersuasionDispositionChange( mActor, MWBase::MechanicsManager::PersuasionType(type), mTemporaryDispositionChange, @@ -457,14 +456,10 @@ namespace MWDialogue text = "sIntimidate"; else{ text = "sBribe"; - skillincrease = success; - } - - if (skillincrease){ - // practice skill - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Speechcraft, -1); - } + } + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Speechcraft, success ? 0 : 1); text += (success ? "Success" : "Fail"); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 945104537..a005c618f 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -211,7 +211,7 @@ namespace MWGui + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange()),100)); MWMechanics::NpcStats sellerSkill = MWWorld::Class::get(mPtr).getNpcStats(mPtr); - MWMechanics::CreatureStats sellerStats = MWWorld::Class::get(mPtr).getCreatureStats(mPtr); + MWMechanics::CreatureStats sellerStats = MWWorld::Class::get(mPtr).getCreatureStats(mPtr); MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWMechanics::NpcStats playerSkill = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); MWMechanics::CreatureStats playerStats = MWWorld::Class::get(playerPtr).getCreatureStats(playerPtr); @@ -239,9 +239,9 @@ namespace MWGui MWBase::Environment::get().getDialogueManager()->applyTemporaryDispositionChange(iBarterFailDisposition); return; } - + //skill use! - MWWorld::Class::get(playerPtr).skillUsageSucceeded(playerPtr, ESM::Skill::Mercantile, 0); + MWWorld::Class::get(playerPtr).skillUsageSucceeded(playerPtr, ESM::Skill::Mercantile, 0); } int iBarterSuccessDisposition = gmst.find("iBarterSuccessDisposition")->getInt(); From d97d4b8ef0e9c0a237d9dbe79a51e8be756f8e62 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 8 Dec 2012 10:12:19 +0100 Subject: [PATCH 084/378] updated version number --- CMakeLists.txt | 4 ++-- readme.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 78388e20f..4b740f735 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ include (OpenMWMacros) # Version set (OPENMW_VERSION_MAJOR 0) -set (OPENMW_VERSION_MINOR 19) +set (OPENMW_VERSION_MINOR 20) set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") @@ -244,7 +244,7 @@ if (APPLE) else () set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_DBG}) endif () - + #set(OGRE_PLUGIN_DIR "${OGRE_PLUGIN_DIR}/") configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist diff --git a/readme.txt b/readme.txt index 196105393..69188b79f 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.19.0 +Version: 0.20.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org From 11700237fc5774442e76bf7445cf56850e3e8e96 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 8 Dec 2012 10:13:23 +0100 Subject: [PATCH 085/378] updated changelog --- readme.txt | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/readme.txt b/readme.txt index 69188b79f..21ae85530 100644 --- a/readme.txt +++ b/readme.txt @@ -94,6 +94,36 @@ Allowed options: CHANGELOG +0.20.0 + +Bug #366: Changing the player's race during character creation does not change the look of the player character +Bug #430: Teleporting and using loading doors linking within the same cell reloads the cell +Bug #437: Stop animations when paused +Bug #438: Time displays as "0 a.m." when it should be "12 a.m." +Bug #439: Text in "name" field of potion/spell creation window is persistent +Bug #440: Starting date at a new game is off by one day +Bug #442: Console window doesn't close properly sometimes +Bug #448: Do not break container window formatting when item names are very long +Bug #458: Topics sometimes not automatically added to known topic list +Bug #476: Auto-Moving allows player movement after using DisablePlayerControls +Bug #478: After sleeping in a bed the rest dialogue window opens automtically again +Bug #492: On creating potions the ingredients are removed twice +Feature #63: Mercantile skill +Feature #82: Persuasion Dialogue +Feature #219: Missing dialogue filters/functions +Feature #369: Add a FailedAction +Feature #377: Select head/hair on character creation +Feature #391: Dummy AI package classes +Feature #435: Global Map, 2nd Layer +Feature #450: Persuasion +Feature #457: Add more script instructions +Feature #474: update the global variable pcrace when the player's race is changed +Task #158: Move dynamically generated classes from Player class to World Class +Task #159: ESMStore rework and cleanup +Task #163: More Component Namespace Cleanup +Task #402: Move player data from MWWorld::Player to the player's NPC record +Task #446: Fix no namespace in BulletShapeLoader + 0.19.0 Bug #374: Character shakes in 3rd person mode near the origin From fdc7e93835defe57d5fca0e47515ec3aba01fda6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 8 Dec 2012 11:59:13 +0100 Subject: [PATCH 086/378] preliminary multi-threaded verify implementation (does not actually perfom any document verification yet) --- apps/opencs/CMakeLists.txt | 4 +++ apps/opencs/model/doc/document.cpp | 42 ++++++++++++------------- apps/opencs/model/doc/document.hpp | 11 +++++-- apps/opencs/model/tools/tools.cpp | 46 ++++++++++++++++++++++++++++ apps/opencs/model/tools/tools.hpp | 45 +++++++++++++++++++++++++++ apps/opencs/model/tools/verifier.cpp | 33 ++++++++++++++++++++ apps/opencs/model/tools/verifier.hpp | 33 ++++++++++++++++++++ 7 files changed, 189 insertions(+), 25 deletions(-) create mode 100644 apps/opencs/model/tools/tools.cpp create mode 100644 apps/opencs/model/tools/tools.hpp create mode 100644 apps/opencs/model/tools/verifier.cpp create mode 100644 apps/opencs/model/tools/verifier.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 5a9e1e99c..ca235042a 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -7,6 +7,8 @@ set (OPENCS_SRC model/world/universalid.cpp model/world/idcollection.cpp model/world/data.cpp model/world/idtable.cpp model/world/commands.cpp model/world/idtableproxymodel.cpp model/world/record.cpp + model/tools/tools.cpp model/tools/verifier.cpp + view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp view/world/subview.cpp view/world/table.cpp view/world/tablesubview.cpp @@ -21,6 +23,8 @@ set (OPENCS_HDR model/world/idtable.hpp model/world/columns.hpp model/world/idtableproxymodel.hpp model/world/commands.hpp + model/tools/tools.hpp model/tools/verifier.hpp + view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp view/world/subview.hpp view/world/table.hpp view/world/tablesubview.hpp diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index e8091b3e0..f85fde038 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -7,13 +7,16 @@ CSMDoc::Document::Document (const std::string& name) connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool))); + connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); + connect (&mTools, SIGNAL (done (int)), this, SLOT (operationDone (int))); + // dummy implementation -> remove when proper save is implemented. mSaveCount = 0; connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving())); // dummy implementation -> remove when proper verify is implemented. mVerifyCount = 0; - connect (&mVerifyTimer, SIGNAL(timeout()), this, SLOT (verifying())); + } QUndoStack& CSMDoc::Document::getUndoStack() @@ -53,23 +56,19 @@ void CSMDoc::Document::save() void CSMDoc::Document::verify() { mVerifyCount = 1; - mVerifyTimer.start (500); emit stateChanged (getState(), this); - emit progress (1, 20, State_Verifying, 1, this); + mTools.runVerifier(); } void CSMDoc::Document::abortOperation (int type) { + mTools.abortOperation (type); + if (type==State_Saving) { mSaveTimer.stop(); emit stateChanged (getState(), this); } - else if (type==State_Verifying) - { - mVerifyTimer.stop(); - emit stateChanged (getState(), this); - } } void CSMDoc::Document::modificationStateChanged (bool clean) @@ -77,6 +76,14 @@ void CSMDoc::Document::modificationStateChanged (bool clean) emit stateChanged (getState(), this); } +void CSMDoc::Document::operationDone (int type) +{ + if (type==State_Verifying) + mVerifyCount = 0; + + emit stateChanged (getState(), this); +} + void CSMDoc::Document::saving() { ++mSaveCount; @@ -92,20 +99,6 @@ void CSMDoc::Document::saving() } } -void CSMDoc::Document::verifying() -{ - ++mVerifyCount; - - emit progress (mVerifyCount, 20, State_Verifying, 1, this); - - if (mVerifyCount>19) - { - mVerifyCount = 0; - mVerifyTimer.stop(); - emit stateChanged (getState(), this); - } -} - const CSMWorld::Data& CSMDoc::Document::getData() const { return mData; @@ -114,4 +107,9 @@ const CSMWorld::Data& CSMDoc::Document::getData() const CSMWorld::Data& CSMDoc::Document::getData() { return mData; +} + +void CSMDoc::Document::progress (int current, int max, int type) +{ + emit progress (current, max, type, 1, this); } \ No newline at end of file diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index b414bf04d..3fe20bc80 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -9,6 +9,8 @@ #include "../world/data.hpp" +#include "../tools/tools.hpp" + namespace CSMDoc { class Document : public QObject @@ -32,12 +34,12 @@ namespace CSMDoc std::string mName; ///< \todo replace name with ESX list QUndoStack mUndoStack; CSMWorld::Data mData; + CSMTools::Tools mTools; int mSaveCount; ///< dummy implementation -> remove when proper save is implemented. QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented. int mVerifyCount; ///< dummy implementation -> remove when proper verify is implemented. - QTimer mVerifyTimer; ///< dummy implementation -> remove when proper verify is implemented. // not implemented Document (const Document&); @@ -75,11 +77,14 @@ namespace CSMDoc void modificationStateChanged (bool clean); + void operationDone (int type); + void saving(); ///< dummy implementation -> remove when proper save is implemented. - void verifying(); - ///< dummy implementation -> remove when proper verify is implemented. + public slots: + + void progress (int current, int max, int type); }; } diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp new file mode 100644 index 000000000..941a22cff --- /dev/null +++ b/apps/opencs/model/tools/tools.cpp @@ -0,0 +1,46 @@ + +#include "tools.hpp" + +#include + +#include "verifier.hpp" + +#include "../doc/document.hpp" + +CSMTools::Verifier *CSMTools::Tools::getVerifier() +{ + if (!mVerifier) + { + mVerifier = new Verifier; + + connect (mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int))); + connect (mVerifier, SIGNAL (finished()), this, SLOT (verifierDone())); + } + + return mVerifier; +} + +CSMTools::Tools::Tools() : mVerifier (0) +{ + +} + +CSMTools::Tools::~Tools() +{ + delete mVerifier; +} + +void CSMTools::Tools::runVerifier() +{ + getVerifier()->start(); +} + +void CSMTools::Tools::abortOperation (int type) +{ + +} + +void CSMTools::Tools::verifierDone() +{ + emit done (CSMDoc::Document::State_Verifying); +} \ No newline at end of file diff --git a/apps/opencs/model/tools/tools.hpp b/apps/opencs/model/tools/tools.hpp new file mode 100644 index 000000000..65b1d1684 --- /dev/null +++ b/apps/opencs/model/tools/tools.hpp @@ -0,0 +1,45 @@ +#ifndef CSM_TOOLS_TOOLS_H +#define CSM_TOOLS_TOOLS_H + +#include + +namespace CSMTools +{ + class Verifier; + + class Tools : public QObject + { + Q_OBJECT + + Verifier *mVerifier; + + // not implemented + Tools (const Tools&); + Tools& operator= (const Tools&); + + Verifier *getVerifier(); + + public: + + Tools(); + + virtual ~Tools(); + + void runVerifier(); + + void abortOperation (int type); + ///< \attention The operation is not aborted immediately. + + private slots: + + void verifierDone(); + + signals: + + void progress (int current, int max, int type); + + void done (int type); + }; +} + +#endif diff --git a/apps/opencs/model/tools/verifier.cpp b/apps/opencs/model/tools/verifier.cpp new file mode 100644 index 000000000..43a864289 --- /dev/null +++ b/apps/opencs/model/tools/verifier.cpp @@ -0,0 +1,33 @@ + +#include "verifier.hpp" + +#include + +#include "../doc/document.hpp" + +void CSMTools::Verifier::run() +{ + mStep = 0; + + QTimer timer; + + timer.connect (&timer, SIGNAL (timeout()), this, SLOT (verify())); + + timer.start (0); + + exec(); +} + +void CSMTools::Verifier::abort() +{ + exit(); +} + +void CSMTools::Verifier::verify() +{ + ++mStep; + emit progress (mStep, 1000, CSMDoc::Document::State_Verifying); + + if (mStep>=1000) + exit(); +} \ No newline at end of file diff --git a/apps/opencs/model/tools/verifier.hpp b/apps/opencs/model/tools/verifier.hpp new file mode 100644 index 000000000..0e7fae19c --- /dev/null +++ b/apps/opencs/model/tools/verifier.hpp @@ -0,0 +1,33 @@ +#ifndef CSM_TOOLS_VERIFIER_H +#define CSM_TOOLS_VERIFIER_H + +#include + +namespace CSMTools +{ + class Verifier : public QThread + { + Q_OBJECT + + int mStep; + + public: + + virtual void run(); + + signals: + + void progress (int current, int max, int type); + + public slots: + + void abort(); + + private slots: + + void verify(); + }; + +} + +#endif From a70a5282f4390cb23b1bd307648cadbdb7dc4510 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 8 Dec 2012 13:37:43 +0100 Subject: [PATCH 087/378] fixed an overzealous skill gain error check --- apps/openmw/mwmechanics/npcstats.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index ca58f4825..facfa0eb0 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -121,7 +121,7 @@ float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& cla { skillFactor = skill->mData.mUseValue[usageType]; - if (skillFactor<=0) + if (skillFactor<0) throw std::runtime_error ("invalid skill gain factor"); } From caaffd1ec2ad188c4559f64d872ca58bc7a18e93 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 8 Dec 2012 14:24:15 +0100 Subject: [PATCH 088/378] handle persuasion records properly --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 79 +++++++++++-------- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 4 +- 2 files changed, 50 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 737d22280..80316c0f5 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -238,6 +238,44 @@ namespace MWDialogue } } + void DialogueManager::executeTopic (const std::string& topic) + { + Filter filter (mActor, mChoice, mTalkedTo); + + const MWWorld::Store &dialogues = + MWBase::Environment::get().getWorld()->getStore().get(); + + const ESM::Dialogue& dialogue = *dialogues.find (topic); + + if (const ESM::DialInfo *info = filter.search (dialogue)) + { + parseText (info->mResponse); + + MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); + + if (dialogue.mType==ESM::Dialogue::Persuasion) + { + std::string modifiedTopic = "s" + topic; + + modifiedTopic.erase (std::remove (modifiedTopic.begin(), modifiedTopic.end(), ' '), modifiedTopic.end()); + + const MWWorld::Store& gmsts = + MWBase::Environment::get().getWorld()->getStore().get(); + + win->addTitle (gmsts.find (modifiedTopic)->getString()); + } + else + win->addTitle (topic); + + win->addText (info->mResponse); + + executeScript (info->mResultScript); + + mLastTopic = topic; + mLastDialogue = *info; + } + } + void DialogueManager::updateTopics() { std::list keywordList; @@ -332,24 +370,7 @@ namespace MWDialogue ESM::Dialogue ndialogue = mDialogueMap[keyword]; if (mDialogueMap[keyword].mType == ESM::Dialogue::Topic) { - Filter filter (mActor, mChoice, mTalkedTo); - - if (const ESM::DialInfo *info = filter.search (mDialogueMap[keyword])) - { - std::string text = info->mResponse; - std::string script = info->mResultScript; - - parseText (text); - - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - win->addTitle (keyword); - win->addText (info->mResponse); - - executeScript (script); - - mLastTopic = keyword; - mLastDialogue = *info; - } + executeTopic (keyword); } } } @@ -445,28 +466,22 @@ namespace MWDialogue else if (curDisp + mTemporaryDispositionChange > 100) mTemporaryDispositionChange = 100 - curDisp; - // add status message to dialogue window + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Speechcraft, success ? 0 : 1); + std::string text; if (type == MWBase::MechanicsManager::PT_Admire) - text = "sAdmire"; + text = "Admire"; else if (type == MWBase::MechanicsManager::PT_Taunt) - text = "sTaunt"; + text = "Taunt"; else if (type == MWBase::MechanicsManager::PT_Intimidate) - text = "sIntimidate"; + text = "Intimidate"; else{ - text = "sBribe"; + text = "Bribe"; } - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWWorld::Class::get(player).skillUsageSucceeded(player, ESM::Skill::Speechcraft, success ? 0 : 1); - - text += (success ? "Success" : "Fail"); - - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - win->addTitle(MyGUI::LanguageManager::getInstance().replaceTags("#{"+text+"}")); - - /// \todo text from INFO record, how to get the ID? + executeTopic (text + (success ? " Success" : " Fail")); } int DialogueManager::getTemporaryDispositionChange() const diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 9e1971b0f..98b27f774 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -23,7 +23,7 @@ namespace MWDialogue MWScript::CompilerContext mCompilerContext; std::ostream mErrorStream; Compiler::StreamErrorHandler mErrorHandler; - + MWWorld::Ptr mActor; bool mTalkedTo; @@ -46,6 +46,8 @@ namespace MWDialogue void printError (const std::string& error); + void executeTopic (const std::string& topic); + public: DialogueManager (const Compiler::Extensions& extensions, bool scriptVerbose); From 0e7ba008adff8ed321e83f1790cee880c8c2ec93 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 8 Dec 2012 14:28:56 +0100 Subject: [PATCH 089/378] another skill gain fix --- apps/openmw/mwmechanics/npcstats.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index facfa0eb0..eb2523372 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -117,12 +117,15 @@ float MWMechanics::NpcStats::getSkillGain (int skillIndex, const ESM::Class& cla if (usageType>=4) throw std::runtime_error ("skill usage type out of range"); - if (usageType>0) + if (usageType>=0) { skillFactor = skill->mData.mUseValue[usageType]; if (skillFactor<0) throw std::runtime_error ("invalid skill gain factor"); + + if (skillFactor==0) + return 0; } const MWWorld::Store &gmst = From a2b4f431760d0fcae95c1774ae7f6913acb1b0af Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 8 Dec 2012 14:44:03 +0100 Subject: [PATCH 090/378] moved document state enum to a separate file --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/doc/document.cpp | 5 ++--- apps/opencs/model/doc/document.hpp | 14 ++------------ apps/opencs/model/doc/state.hpp | 19 +++++++++++++++++++ apps/opencs/model/tools/tools.cpp | 4 ++-- apps/opencs/model/tools/verifier.cpp | 4 ++-- apps/opencs/view/doc/operation.cpp | 4 ++-- apps/opencs/view/doc/view.cpp | 12 ++++++------ 8 files changed, 36 insertions(+), 28 deletions(-) create mode 100644 apps/opencs/model/doc/state.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index ca235042a..ce83e4ea6 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -17,7 +17,7 @@ set (OPENCS_SRC set (OPENCS_HDR editor.hpp - model/doc/documentmanager.hpp model/doc/document.hpp + model/doc/documentmanager.hpp model/doc/document.hpp model/doc/state.hpp model/world/universalid.hpp model/world/record.hpp model/world/idcollection.hpp model/world/data.hpp model/world/idtable.hpp model/world/columns.hpp model/world/idtableproxymodel.hpp diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index f85fde038..2bd0f2e3e 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -16,7 +16,6 @@ CSMDoc::Document::Document (const std::string& name) // dummy implementation -> remove when proper verify is implemented. mVerifyCount = 0; - } QUndoStack& CSMDoc::Document::getUndoStack() @@ -32,10 +31,10 @@ int CSMDoc::Document::getState() const state |= State_Modified; if (mSaveCount) - state |= State_Locked | State_Saving; + state |= State_Locked | State_Saving | State_Operation; if (mVerifyCount) - state |= State_Locked | State_Verifying; + state |= State_Locked | State_Verifying | State_Operation; return state; } diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 3fe20bc80..6226e3165 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -11,24 +11,14 @@ #include "../tools/tools.hpp" +#include "state.hpp" + namespace CSMDoc { class Document : public QObject { Q_OBJECT - public: - - enum State - { - State_Modified = 1, - State_Locked = 2, - State_Saving = 4, - State_Verifying = 8, - State_Compiling = 16, // not implemented yet - State_Searching = 32 // not implemented yet - }; - private: std::string mName; ///< \todo replace name with ESX list diff --git a/apps/opencs/model/doc/state.hpp b/apps/opencs/model/doc/state.hpp new file mode 100644 index 000000000..04e6fae89 --- /dev/null +++ b/apps/opencs/model/doc/state.hpp @@ -0,0 +1,19 @@ +#ifndef CSM_DOC_STATE_H +#define CSM_DOC_STATE_H + +namespace CSMDoc +{ + enum State + { + State_Modified = 1, + State_Locked = 2, + State_Operation = 4, + + State_Saving = 8, + State_Verifying = 16, + State_Compiling = 32, // not implemented yet + State_Searching = 64 // not implemented yet + }; +} + +#endif diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 941a22cff..95375332c 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -5,7 +5,7 @@ #include "verifier.hpp" -#include "../doc/document.hpp" +#include "../doc/state.hpp" CSMTools::Verifier *CSMTools::Tools::getVerifier() { @@ -42,5 +42,5 @@ void CSMTools::Tools::abortOperation (int type) void CSMTools::Tools::verifierDone() { - emit done (CSMDoc::Document::State_Verifying); + emit done (CSMDoc::State_Verifying); } \ No newline at end of file diff --git a/apps/opencs/model/tools/verifier.cpp b/apps/opencs/model/tools/verifier.cpp index 43a864289..14cb692e9 100644 --- a/apps/opencs/model/tools/verifier.cpp +++ b/apps/opencs/model/tools/verifier.cpp @@ -3,7 +3,7 @@ #include -#include "../doc/document.hpp" +#include "../doc/state.hpp" void CSMTools::Verifier::run() { @@ -26,7 +26,7 @@ void CSMTools::Verifier::abort() void CSMTools::Verifier::verify() { ++mStep; - emit progress (mStep, 1000, CSMDoc::Document::State_Verifying); + emit progress (mStep, 1000, CSMDoc::State_Verifying); if (mStep>=1000) exit(); diff --git a/apps/opencs/view/doc/operation.cpp b/apps/opencs/view/doc/operation.cpp index 62f006be5..3f415da03 100644 --- a/apps/opencs/view/doc/operation.cpp +++ b/apps/opencs/view/doc/operation.cpp @@ -13,8 +13,8 @@ void CSVDoc::Operation::updateLabel (int threads) switch (mType) { - case CSMDoc::Document::State_Saving: name = "saving"; break; - case CSMDoc::Document::State_Verifying: name = "verifying"; break; + case CSMDoc::State_Saving: name = "saving"; break; + case CSMDoc::State_Verifying: name = "verifying"; break; } std::ostringstream stream; diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 65ec103a2..539b149cb 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -83,7 +83,7 @@ void CSVDoc::View::updateTitle() stream << mDocument->getName(); - if (mDocument->getState() & CSMDoc::Document::State_Modified) + if (mDocument->getState() & CSMDoc::State_Modified) stream << " *"; if (mViewTotal>1) @@ -94,7 +94,7 @@ void CSVDoc::View::updateTitle() void CSVDoc::View::updateActions() { - bool editing = !(mDocument->getState() & CSMDoc::Document::State_Locked); + bool editing = !(mDocument->getState() & CSMDoc::State_Locked); for (std::vector::iterator iter (mEditingActions.begin()); iter!=mEditingActions.end(); ++iter) (*iter)->setEnabled (editing); @@ -102,8 +102,8 @@ void CSVDoc::View::updateActions() mUndo->setEnabled (editing & mDocument->getUndoStack().canUndo()); mRedo->setEnabled (editing & mDocument->getUndoStack().canRedo()); - mSave->setEnabled (!(mDocument->getState() & CSMDoc::Document::State_Saving)); - mVerify->setEnabled (!(mDocument->getState() & CSMDoc::Document::State_Verifying)); + mSave->setEnabled (!(mDocument->getState() & CSMDoc::State_Saving)); + mVerify->setEnabled (!(mDocument->getState() & CSMDoc::State_Verifying)); } CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews) @@ -155,7 +155,7 @@ void CSVDoc::View::updateDocumentState() static const int operations[] = { - CSMDoc::Document::State_Saving, CSMDoc::Document::State_Verifying, + CSMDoc::State_Saving, CSMDoc::State_Verifying, -1 // end marker }; @@ -168,7 +168,7 @@ void CSVDoc::View::updateDocumentState() QList subViews = findChildren(); for (QList::iterator iter (subViews.begin()); iter!=subViews.end(); ++iter) - (*iter)->setEditLock (state & CSMDoc::Document::State_Locked); + (*iter)->setEditLock (state & CSMDoc::State_Locked); } void CSVDoc::View::updateProgress (int current, int max, int type, int threads) From af9b48f4d333839a66b96cf23e9faa62cbfaa7c8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 8 Dec 2012 15:25:50 +0100 Subject: [PATCH 091/378] added operations base class --- apps/opencs/CMakeLists.txt | 4 +-- apps/opencs/model/tools/operation.cpp | 35 +++++++++++++++++++++++++++ apps/opencs/model/tools/operation.hpp | 35 +++++++++++++++++++++++++++ apps/opencs/model/tools/verifier.cpp | 30 ++--------------------- apps/opencs/model/tools/verifier.hpp | 22 +++-------------- 5 files changed, 77 insertions(+), 49 deletions(-) create mode 100644 apps/opencs/model/tools/operation.cpp create mode 100644 apps/opencs/model/tools/operation.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index ce83e4ea6..d2b87be51 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -7,7 +7,7 @@ set (OPENCS_SRC model/world/universalid.cpp model/world/idcollection.cpp model/world/data.cpp model/world/idtable.cpp model/world/commands.cpp model/world/idtableproxymodel.cpp model/world/record.cpp - model/tools/tools.cpp model/tools/verifier.cpp + model/tools/tools.cpp model/tools/operation.cpp model/tools/verifier.cpp view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp @@ -23,7 +23,7 @@ set (OPENCS_HDR model/world/idtable.hpp model/world/columns.hpp model/world/idtableproxymodel.hpp model/world/commands.hpp - model/tools/tools.hpp model/tools/verifier.hpp + model/tools/tools.hpp model/tools/operation.hpp model/tools/verifier.hpp view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp diff --git a/apps/opencs/model/tools/operation.cpp b/apps/opencs/model/tools/operation.cpp new file mode 100644 index 000000000..ba5866168 --- /dev/null +++ b/apps/opencs/model/tools/operation.cpp @@ -0,0 +1,35 @@ + +#include "operation.hpp" + +#include + +#include "../doc/state.hpp" + +CSMTools::Operation::Operation (int type) : mType (type) {} + +void CSMTools::Operation::run() +{ + mStep = 0; + + QTimer timer; + + timer.connect (&timer, SIGNAL (timeout()), this, SLOT (verify())); + + timer.start (0); + + exec(); +} + +void CSMTools::Operation::abort() +{ + exit(); +} + +void CSMTools::Operation::verify() +{ + ++mStep; + emit progress (mStep, 1000, mType); + + if (mStep>=1000) + exit(); +} \ No newline at end of file diff --git a/apps/opencs/model/tools/operation.hpp b/apps/opencs/model/tools/operation.hpp new file mode 100644 index 000000000..b75e2898f --- /dev/null +++ b/apps/opencs/model/tools/operation.hpp @@ -0,0 +1,35 @@ +#ifndef CSM_TOOLS_OPERATION_H +#define CSM_TOOLS_OPERATION_H + +#include + +namespace CSMTools +{ + class Operation : public QThread + { + Q_OBJECT + + int mType; + int mStep; + + public: + + Operation (int type); + + virtual void run(); + + signals: + + void progress (int current, int max, int type); + + public slots: + + void abort(); + + private slots: + + void verify(); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/model/tools/verifier.cpp b/apps/opencs/model/tools/verifier.cpp index 14cb692e9..9c00d4ea7 100644 --- a/apps/opencs/model/tools/verifier.cpp +++ b/apps/opencs/model/tools/verifier.cpp @@ -1,33 +1,7 @@ #include "verifier.hpp" -#include - #include "../doc/state.hpp" -void CSMTools::Verifier::run() -{ - mStep = 0; - - QTimer timer; - - timer.connect (&timer, SIGNAL (timeout()), this, SLOT (verify())); - - timer.start (0); - - exec(); -} - -void CSMTools::Verifier::abort() -{ - exit(); -} - -void CSMTools::Verifier::verify() -{ - ++mStep; - emit progress (mStep, 1000, CSMDoc::State_Verifying); - - if (mStep>=1000) - exit(); -} \ No newline at end of file +CSMTools::Verifier::Verifier() : Operation (CSMDoc::State_Verifying) +{} diff --git a/apps/opencs/model/tools/verifier.hpp b/apps/opencs/model/tools/verifier.hpp index 0e7fae19c..054f87169 100644 --- a/apps/opencs/model/tools/verifier.hpp +++ b/apps/opencs/model/tools/verifier.hpp @@ -1,33 +1,17 @@ #ifndef CSM_TOOLS_VERIFIER_H #define CSM_TOOLS_VERIFIER_H -#include +#include "operation.hpp" namespace CSMTools { - class Verifier : public QThread + class Verifier : public Operation { - Q_OBJECT - - int mStep; - public: - virtual void run(); - - signals: - - void progress (int current, int max, int type); + Verifier(); - public slots: - - void abort(); - - private slots: - - void verify(); }; - } #endif From 8b7f342641b38c740cb78bc0f137b22f2f94ba6e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 8 Dec 2012 17:00:58 +0100 Subject: [PATCH 092/378] removed last remains of old verify implementation --- apps/opencs/model/doc/document.cpp | 13 +++--------- apps/opencs/model/doc/document.hpp | 2 -- apps/opencs/model/tools/tools.cpp | 34 ++++++++++++++++++++++++++++++ apps/opencs/model/tools/tools.hpp | 9 ++++++++ 4 files changed, 46 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 2bd0f2e3e..d8ad5ff01 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -13,9 +13,6 @@ CSMDoc::Document::Document (const std::string& name) // dummy implementation -> remove when proper save is implemented. mSaveCount = 0; connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving())); - - // dummy implementation -> remove when proper verify is implemented. - mVerifyCount = 0; } QUndoStack& CSMDoc::Document::getUndoStack() @@ -33,8 +30,8 @@ int CSMDoc::Document::getState() const if (mSaveCount) state |= State_Locked | State_Saving | State_Operation; - if (mVerifyCount) - state |= State_Locked | State_Verifying | State_Operation; + if (int operations = mTools.getRunningOperations()) + state |= State_Locked | State_Operation | operations; return state; } @@ -54,9 +51,8 @@ void CSMDoc::Document::save() void CSMDoc::Document::verify() { - mVerifyCount = 1; - emit stateChanged (getState(), this); mTools.runVerifier(); + emit stateChanged (getState(), this); } void CSMDoc::Document::abortOperation (int type) @@ -77,9 +73,6 @@ void CSMDoc::Document::modificationStateChanged (bool clean) void CSMDoc::Document::operationDone (int type) { - if (type==State_Verifying) - mVerifyCount = 0; - emit stateChanged (getState(), this); } diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 6226e3165..40f8c0ea1 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -29,8 +29,6 @@ namespace CSMDoc int mSaveCount; ///< dummy implementation -> remove when proper save is implemented. QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented. - int mVerifyCount; ///< dummy implementation -> remove when proper verify is implemented. - // not implemented Document (const Document&); Document& operator= (const Document&); diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 95375332c..608244a3c 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -7,6 +7,21 @@ #include "../doc/state.hpp" +CSMTools::Operation *CSMTools::Tools::get (int type) +{ + switch (type) + { + case CSMDoc::State_Verifying: return mVerifier; + } + + return 0; +} + +const CSMTools::Operation *CSMTools::Tools::get (int type) const +{ + return const_cast (this)->get (type); +} + CSMTools::Verifier *CSMTools::Tools::getVerifier() { if (!mVerifier) @@ -37,7 +52,26 @@ void CSMTools::Tools::runVerifier() void CSMTools::Tools::abortOperation (int type) { + if (Operation *operation = get (type)) + operation->abort(); +} + +int CSMTools::Tools::getRunningOperations() const +{ + static const int sOperations[] = + { + CSMDoc::State_Verifying, + -1 + }; + + int result = 0; + + for (int i=0; sOperations[i]!=-1; ++i) + if (const Operation *operation = get (sOperations[i])) + if (operation->isRunning()) + result |= sOperations[i]; + return result; } void CSMTools::Tools::verifierDone() diff --git a/apps/opencs/model/tools/tools.hpp b/apps/opencs/model/tools/tools.hpp index 65b1d1684..ab1863738 100644 --- a/apps/opencs/model/tools/tools.hpp +++ b/apps/opencs/model/tools/tools.hpp @@ -6,6 +6,7 @@ namespace CSMTools { class Verifier; + class Operation; class Tools : public QObject { @@ -19,6 +20,12 @@ namespace CSMTools Verifier *getVerifier(); + Operation *get (int type); + ///< Returns a 0-pointer, if operation hasn't been used yet. + + const Operation *get (int type) const; + ///< Returns a 0-pointer, if operation hasn't been used yet. + public: Tools(); @@ -30,6 +37,8 @@ namespace CSMTools void abortOperation (int type); ///< \attention The operation is not aborted immediately. + int getRunningOperations() const; + private slots: void verifierDone(); From 6c18be39f0e10b43163de194fe93d0714df40eef Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 8 Dec 2012 17:53:45 +0100 Subject: [PATCH 093/378] added stage class --- apps/opencs/CMakeLists.txt | 4 +-- apps/opencs/model/tools/operation.cpp | 49 ++++++++++++++++++++++++--- apps/opencs/model/tools/operation.hpp | 19 ++++++++++- apps/opencs/model/tools/stage.cpp | 4 +++ apps/opencs/model/tools/stage.hpp | 20 +++++++++++ 5 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 apps/opencs/model/tools/stage.cpp create mode 100644 apps/opencs/model/tools/stage.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index d2b87be51..381ce51cf 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -7,7 +7,7 @@ set (OPENCS_SRC model/world/universalid.cpp model/world/idcollection.cpp model/world/data.cpp model/world/idtable.cpp model/world/commands.cpp model/world/idtableproxymodel.cpp model/world/record.cpp - model/tools/tools.cpp model/tools/operation.cpp model/tools/verifier.cpp + model/tools/tools.cpp model/tools/operation.cpp model/tools/stage.cpp model/tools/verifier.cpp view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp @@ -23,7 +23,7 @@ set (OPENCS_HDR model/world/idtable.hpp model/world/columns.hpp model/world/idtableproxymodel.hpp model/world/commands.hpp - model/tools/tools.hpp model/tools/operation.hpp model/tools/verifier.hpp + model/tools/tools.hpp model/tools/operation.hpp model/tools/stage.hpp model/tools/verifier.hpp view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp diff --git a/apps/opencs/model/tools/operation.cpp b/apps/opencs/model/tools/operation.cpp index ba5866168..12c4a8108 100644 --- a/apps/opencs/model/tools/operation.cpp +++ b/apps/opencs/model/tools/operation.cpp @@ -5,11 +5,33 @@ #include "../doc/state.hpp" +#include "stage.hpp" + +void CSMTools::Operation::prepareStages() +{ + mCurrentStage = mStages.begin(); + mCurrentStep = 0; + mCurrentStepTotal = 0; + mTotalSteps = 0; + + for (std::vector >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter) + { + iter->second = mTotalSteps; + mTotalSteps += iter->first->setup(); + } +} + CSMTools::Operation::Operation (int type) : mType (type) {} +CSMTools::Operation::~Operation() +{ + for (std::vector >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter) + delete iter->first; +} + void CSMTools::Operation::run() { - mStep = 0; + prepareStages(); QTimer timer; @@ -20,6 +42,11 @@ void CSMTools::Operation::run() exec(); } +void CSMTools::Operation::appendStage (Stage *stage) +{ + mStages.push_back (std::make_pair (stage, 0)); +} + void CSMTools::Operation::abort() { exit(); @@ -27,9 +54,23 @@ void CSMTools::Operation::abort() void CSMTools::Operation::verify() { - ++mStep; - emit progress (mStep, 1000, mType); + while (mCurrentStage!=mStages.end()) + { + if (mCurrentStep>=mCurrentStage->second) + { + mCurrentStep = 0; + ++mCurrentStage; + } + else + { + mCurrentStage->first->perform (mCurrentStep++); + ++mCurrentStepTotal; + break; + } + } + + emit progress (mCurrentStepTotal, mTotalSteps ? mTotalSteps : 1, mType); - if (mStep>=1000) + if (mCurrentStage==mStages.end()) exit(); } \ No newline at end of file diff --git a/apps/opencs/model/tools/operation.hpp b/apps/opencs/model/tools/operation.hpp index b75e2898f..72419e00a 100644 --- a/apps/opencs/model/tools/operation.hpp +++ b/apps/opencs/model/tools/operation.hpp @@ -1,23 +1,40 @@ #ifndef CSM_TOOLS_OPERATION_H #define CSM_TOOLS_OPERATION_H +#include + #include namespace CSMTools { + class Stage; + class Operation : public QThread { Q_OBJECT int mType; - int mStep; + std::vector > mStages; // stage, number of steps + std::vector >::iterator mCurrentStage; + int mCurrentStep; + int mCurrentStepTotal; + int mTotalSteps; + + void prepareStages(); public: Operation (int type); + virtual ~Operation(); + virtual void run(); + void appendStage (Stage *stage); + ///< The ownership of \a stage is transferred to *this. + /// + /// \attention Do no call this function while this Operation is running. + signals: void progress (int current, int max, int type); diff --git a/apps/opencs/model/tools/stage.cpp b/apps/opencs/model/tools/stage.cpp new file mode 100644 index 000000000..6f4567e57 --- /dev/null +++ b/apps/opencs/model/tools/stage.cpp @@ -0,0 +1,4 @@ + +#include "stage.hpp" + +CSMTools::Stage::~Stage() {} \ No newline at end of file diff --git a/apps/opencs/model/tools/stage.hpp b/apps/opencs/model/tools/stage.hpp new file mode 100644 index 000000000..912559410 --- /dev/null +++ b/apps/opencs/model/tools/stage.hpp @@ -0,0 +1,20 @@ +#ifndef CSM_TOOLS_STAGE_H +#define CSM_TOOLS_STAGE_H + +namespace CSMTools +{ + class Stage + { + public: + + virtual ~Stage(); + + virtual int setup() = 0; + ///< \return number of steps + + virtual void perform (int stage) = 0; + }; +} + +#endif + From 89b449733128f2967cecfe47c7c15d1890e023de Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 8 Dec 2012 18:15:00 +0100 Subject: [PATCH 094/378] added missing edit locks for create/revert/delete --- apps/opencs/view/world/table.cpp | 76 +++++++++++++++++++------------- apps/opencs/view/world/table.hpp | 1 + 2 files changed, 47 insertions(+), 30 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 31f7d22ed..593dc8563 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -113,14 +113,19 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) QMenu menu (this); - if (mCreateAction) - menu.addAction (mCreateAction); + /// \todo add menu items for select all and clear selection - if (listRevertableSelectedIds().size()>0) - menu.addAction (mRevertAction); + if (!mEditLock) + { + if (mCreateAction) + menu.addAction (mCreateAction); - if (listDeletableSelectedIds().size()>0) - menu.addAction (mDeleteAction); + if (listRevertableSelectedIds().size()>0) + menu.addAction (mRevertAction); + + if (listDeletableSelectedIds().size()>0) + menu.addAction (mDeleteAction); + } menu.exec (event->globalPos()); } @@ -167,7 +172,7 @@ std::vector CSVWorld::Table::listDeletableSelectedIds() const CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete) -: mUndoStack (undoStack), mCreateAction (0) +: mUndoStack (undoStack), mCreateAction (0), mEditLock (false) { mModel = &dynamic_cast (*data.getTableModel (id)); @@ -212,50 +217,61 @@ void CSVWorld::Table::setEditLock (bool locked) { for (std::vector::iterator iter (mDelegates.begin()); iter!=mDelegates.end(); ++iter) (*iter)->setEditLock (locked); + + mEditLock = locked; } #include /// \todo remove void CSVWorld::Table::createRecord() { - /// \todo ask the user for an ID instead. - static int index = 0; + if (!mEditLock) + { + /// \todo ask the user for an ID instead. + static int index = 0; - std::ostringstream stream; - stream << "id" << index++; + std::ostringstream stream; + stream << "id" << index++; - mUndoStack.push (new CSMWorld::CreateCommand (*mProxyModel, stream.str())); + mUndoStack.push (new CSMWorld::CreateCommand (*mProxyModel, stream.str())); + } } void CSVWorld::Table::revertRecord() { - std::vector revertableIds = listRevertableSelectedIds(); - - if (revertableIds.size()>0) + if (!mEditLock) { - if (revertableIds.size()>1) - mUndoStack.beginMacro (tr ("Revert multiple records")); + std::vector revertableIds = listRevertableSelectedIds(); + + if (revertableIds.size()>0) + { + if (revertableIds.size()>1) + mUndoStack.beginMacro (tr ("Revert multiple records")); - for (std::vector::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) - mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter)); + for (std::vector::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) + mUndoStack.push (new CSMWorld::RevertCommand (*mModel, *iter)); - if (revertableIds.size()>1) - mUndoStack.endMacro(); + if (revertableIds.size()>1) + mUndoStack.endMacro(); + } } } void CSVWorld::Table::deleteRecord() { - std::vector deletableIds = listDeletableSelectedIds(); - - if (deletableIds.size()>0) + if (!mEditLock) { - if (deletableIds.size()>1) - mUndoStack.beginMacro (tr ("Delete multiple records")); + std::vector deletableIds = listDeletableSelectedIds(); + + if (deletableIds.size()>0) + { + if (deletableIds.size()>1) + mUndoStack.beginMacro (tr ("Delete multiple records")); - for (std::vector::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter) - mUndoStack.push (new CSMWorld::DeleteCommand (*mModel, *iter)); + for (std::vector::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter) + mUndoStack.push (new CSMWorld::DeleteCommand (*mModel, *iter)); - if (deletableIds.size()>1) - mUndoStack.endMacro(); + if (deletableIds.size()>1) + mUndoStack.endMacro(); + } } } \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 264d2bc5c..b58ba8328 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -33,6 +33,7 @@ namespace CSVWorld QAction *mDeleteAction; CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTable *mModel; + bool mEditLock; private: From 72623652e46dd5ce94b4e1b4f2593cb889db23b1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 8 Dec 2012 18:38:36 +0100 Subject: [PATCH 095/378] addded messages interface for operations/stages --- apps/opencs/model/tools/operation.cpp | 10 +++++++++- apps/opencs/model/tools/operation.hpp | 2 ++ apps/opencs/model/tools/stage.hpp | 6 +++++- apps/opencs/model/tools/tools.cpp | 10 ++++++++++ apps/opencs/model/tools/tools.hpp | 2 ++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/tools/operation.cpp b/apps/opencs/model/tools/operation.cpp index 12c4a8108..b68f79ca4 100644 --- a/apps/opencs/model/tools/operation.cpp +++ b/apps/opencs/model/tools/operation.cpp @@ -1,6 +1,9 @@ #include "operation.hpp" +#include +#include + #include #include "../doc/state.hpp" @@ -54,6 +57,8 @@ void CSMTools::Operation::abort() void CSMTools::Operation::verify() { + std::vector messages; + while (mCurrentStage!=mStages.end()) { if (mCurrentStep>=mCurrentStage->second) @@ -63,7 +68,7 @@ void CSMTools::Operation::verify() } else { - mCurrentStage->first->perform (mCurrentStep++); + mCurrentStage->first->perform (mCurrentStep++, messages); ++mCurrentStepTotal; break; } @@ -71,6 +76,9 @@ void CSMTools::Operation::verify() emit progress (mCurrentStepTotal, mTotalSteps ? mTotalSteps : 1, mType); + for (std::vector::const_iterator iter (messages.begin()); iter!=messages.end(); ++iter) + emit reportMessage (iter->c_str(), mType); + if (mCurrentStage==mStages.end()) exit(); } \ No newline at end of file diff --git a/apps/opencs/model/tools/operation.hpp b/apps/opencs/model/tools/operation.hpp index 72419e00a..4731c58fa 100644 --- a/apps/opencs/model/tools/operation.hpp +++ b/apps/opencs/model/tools/operation.hpp @@ -39,6 +39,8 @@ namespace CSMTools void progress (int current, int max, int type); + void reportMessage (const QString& message, int type); + public slots: void abort(); diff --git a/apps/opencs/model/tools/stage.hpp b/apps/opencs/model/tools/stage.hpp index 912559410..1ad61960a 100644 --- a/apps/opencs/model/tools/stage.hpp +++ b/apps/opencs/model/tools/stage.hpp @@ -1,6 +1,9 @@ #ifndef CSM_TOOLS_STAGE_H #define CSM_TOOLS_STAGE_H +#include +#include + namespace CSMTools { class Stage @@ -12,7 +15,8 @@ namespace CSMTools virtual int setup() = 0; ///< \return number of steps - virtual void perform (int stage) = 0; + virtual void perform (int stage, std::vector& messages) = 0; + ///< Messages resulting from this tage will be appended to \a messages. }; } diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 608244a3c..8293b7291 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -30,6 +30,8 @@ CSMTools::Verifier *CSMTools::Tools::getVerifier() connect (mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int))); connect (mVerifier, SIGNAL (finished()), this, SLOT (verifierDone())); + connect (mVerifier, SIGNAL (reportMessage (const QString&, int)), + this, SLOT (verifierMessage (const QString&, int))); } return mVerifier; @@ -77,4 +79,12 @@ int CSMTools::Tools::getRunningOperations() const void CSMTools::Tools::verifierDone() { emit done (CSMDoc::State_Verifying); +} + +#include +void CSMTools::Tools::verifierMessage (const QString& message, int type) +{ + /// \todo store it in a result model instead + + std::cout << message.toStdString() << std::endl; } \ No newline at end of file diff --git a/apps/opencs/model/tools/tools.hpp b/apps/opencs/model/tools/tools.hpp index ab1863738..04140bb91 100644 --- a/apps/opencs/model/tools/tools.hpp +++ b/apps/opencs/model/tools/tools.hpp @@ -43,6 +43,8 @@ namespace CSMTools void verifierDone(); + void verifierMessage (const QString& message, int type); + signals: void progress (int current, int max, int type); From 9fe7ff969047df565bae6b39045b6fb52fcf8b50 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 8 Dec 2012 23:27:59 +0100 Subject: [PATCH 096/378] added mandatory ID check stage --- apps/opencs/CMakeLists.txt | 2 ++ apps/opencs/model/doc/document.cpp | 1 + apps/opencs/model/doc/document.hpp | 5 ++- apps/opencs/model/tools/mandatoryid.cpp | 21 +++++++++++ apps/opencs/model/tools/mandatoryid.hpp | 38 ++++++++++++++++++++ apps/opencs/model/tools/operation.cpp | 4 +-- apps/opencs/model/tools/stage.hpp | 12 +++---- apps/opencs/model/tools/tools.cpp | 19 +++++++++- apps/opencs/model/tools/tools.hpp | 8 ++++- apps/opencs/model/world/universalid.cpp | 48 ++++++++++++++++++++++++- apps/opencs/model/world/universalid.hpp | 2 ++ 11 files changed, 148 insertions(+), 12 deletions(-) create mode 100644 apps/opencs/model/tools/mandatoryid.cpp create mode 100644 apps/opencs/model/tools/mandatoryid.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 381ce51cf..bad1e1376 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -8,6 +8,7 @@ set (OPENCS_SRC model/world/commands.cpp model/world/idtableproxymodel.cpp model/world/record.cpp model/tools/tools.cpp model/tools/operation.cpp model/tools/stage.cpp model/tools/verifier.cpp + model/tools/mandatoryid.cpp view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp @@ -24,6 +25,7 @@ set (OPENCS_HDR model/world/commands.hpp model/tools/tools.hpp model/tools/operation.hpp model/tools/stage.hpp model/tools/verifier.hpp + model/tools/mandatoryid.hpp view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index d8ad5ff01..f03344e08 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2,6 +2,7 @@ #include "document.hpp" CSMDoc::Document::Document (const std::string& name) +: mTools (mData) { mName = name; ///< \todo replace with ESX list diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 40f8c0ea1..f9d0a232c 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -22,10 +22,13 @@ namespace CSMDoc private: std::string mName; ///< \todo replace name with ESX list - QUndoStack mUndoStack; CSMWorld::Data mData; CSMTools::Tools mTools; + // It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is + // using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late. + QUndoStack mUndoStack; + int mSaveCount; ///< dummy implementation -> remove when proper save is implemented. QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented. diff --git a/apps/opencs/model/tools/mandatoryid.cpp b/apps/opencs/model/tools/mandatoryid.cpp new file mode 100644 index 000000000..87ac77e67 --- /dev/null +++ b/apps/opencs/model/tools/mandatoryid.cpp @@ -0,0 +1,21 @@ + +#include "mandatoryid.hpp" + +#include "../world/idcollection.hpp" + +CSMTools::MandatoryIdStage::MandatoryIdStage (const CSMWorld::IdCollectionBase& idCollection, + const CSMWorld::UniversalId& collectionId, const std::vector& ids) +: mIdCollection (idCollection), mCollectionId (collectionId), mIds (ids) +{} + +int CSMTools::MandatoryIdStage::setup() +{ + return mIds.size(); +} + +void CSMTools::MandatoryIdStage::perform (int stage, std::vector& messages) +{ + if (mIdCollection.searchId (mIds.at (stage))==-1 || + mIdCollection.getRecord (mIds.at (stage)).isDeleted()) + messages.push_back (mCollectionId.toString() + " Missing mandatory record: " + mIds.at (stage)); +} \ No newline at end of file diff --git a/apps/opencs/model/tools/mandatoryid.hpp b/apps/opencs/model/tools/mandatoryid.hpp new file mode 100644 index 000000000..14fcec204 --- /dev/null +++ b/apps/opencs/model/tools/mandatoryid.hpp @@ -0,0 +1,38 @@ +#ifndef CSM_TOOLS_MANDATORYID_H +#define CSM_TOOLS_MANDATORYID_H + +#include +#include + +#include "../world/universalid.hpp" + +#include "stage.hpp" + +namespace CSMWorld +{ + class IdCollectionBase; +} + +namespace CSMTools +{ + /// \brief Verify stage: make sure that records with specific IDs exist. + class MandatoryIdStage : public Stage + { + const CSMWorld::IdCollectionBase& mIdCollection; + CSMWorld::UniversalId mCollectionId; + std::vector mIds; + + public: + + MandatoryIdStage (const CSMWorld::IdCollectionBase& idCollection, const CSMWorld::UniversalId& collectionId, + const std::vector& ids); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this tage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/tools/operation.cpp b/apps/opencs/model/tools/operation.cpp index b68f79ca4..71761cdae 100644 --- a/apps/opencs/model/tools/operation.cpp +++ b/apps/opencs/model/tools/operation.cpp @@ -19,8 +19,8 @@ void CSMTools::Operation::prepareStages() for (std::vector >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter) { - iter->second = mTotalSteps; - mTotalSteps += iter->first->setup(); + iter->second = iter->first->setup(); + mTotalSteps += iter->second; } } diff --git a/apps/opencs/model/tools/stage.hpp b/apps/opencs/model/tools/stage.hpp index 1ad61960a..3020936f3 100644 --- a/apps/opencs/model/tools/stage.hpp +++ b/apps/opencs/model/tools/stage.hpp @@ -8,15 +8,15 @@ namespace CSMTools { class Stage { - public: + public: - virtual ~Stage(); + virtual ~Stage(); - virtual int setup() = 0; - ///< \return number of steps + virtual int setup() = 0; + ///< \return number of steps - virtual void perform (int stage, std::vector& messages) = 0; - ///< Messages resulting from this tage will be appended to \a messages. + virtual void perform (int stage, std::vector& messages) = 0; + ///< Messages resulting from this tage will be appended to \a messages. }; } diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 8293b7291..a3cdb7822 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -7,6 +7,10 @@ #include "../doc/state.hpp" +#include "../world/data.hpp" + +#include "mandatoryid.hpp" + CSMTools::Operation *CSMTools::Tools::get (int type) { switch (type) @@ -32,12 +36,25 @@ CSMTools::Verifier *CSMTools::Tools::getVerifier() connect (mVerifier, SIGNAL (finished()), this, SLOT (verifierDone())); connect (mVerifier, SIGNAL (reportMessage (const QString&, int)), this, SLOT (verifierMessage (const QString&, int))); + + std::vector mandatoryIds; // I want C++11, damn it! + mandatoryIds.push_back ("Day"); + mandatoryIds.push_back ("DaysPassed"); + mandatoryIds.push_back ("GameHour"); + mandatoryIds.push_back ("Month"); + mandatoryIds.push_back ("PCRace"); + mandatoryIds.push_back ("PCVampire"); + mandatoryIds.push_back ("PCWerewolf"); + mandatoryIds.push_back ("PCYear"); + + mVerifier->appendStage (new MandatoryIdStage (mData.getGlobals(), + CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds)); } return mVerifier; } -CSMTools::Tools::Tools() : mVerifier (0) +CSMTools::Tools::Tools (CSMWorld::Data& data) : mData (data), mVerifier (0) { } diff --git a/apps/opencs/model/tools/tools.hpp b/apps/opencs/model/tools/tools.hpp index 04140bb91..da49fd8b8 100644 --- a/apps/opencs/model/tools/tools.hpp +++ b/apps/opencs/model/tools/tools.hpp @@ -3,6 +3,11 @@ #include +namespace CSMWorld +{ + class Data; +} + namespace CSMTools { class Verifier; @@ -12,6 +17,7 @@ namespace CSMTools { Q_OBJECT + CSMWorld::Data& mData; Verifier *mVerifier; // not implemented @@ -28,7 +34,7 @@ namespace CSMTools public: - Tools(); + Tools (CSMWorld::Data& data); virtual ~Tools(); diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index e2d8d1ffb..229779f5a 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -35,6 +35,52 @@ namespace }; } +CSMWorld::UniversalId::UniversalId (const std::string& universalId) +{ + std::string::size_type index = universalId.find (':'); + + if (index!=std::string::npos) + { + std::string type = universalId.substr (0, index); + + for (int i=0; sNoArg[i].mName; ++i) + if (type==sNoArg[i].mName) + { + mArgumentType = ArgumentType_None; + mType = sNoArg[i].mType; + mClass = sNoArg[i].mClass; + return; + } + + for (int i=0; sIdArg[i].mName; ++i) + if (type==sIdArg[i].mName) + { + mArgumentType = ArgumentType_Id; + mType = sIdArg[i].mType; + mClass = sIdArg[i].mClass; + mId = universalId.substr (0, index); + return; + } + + for (int i=0; sIndexArg[i].mName; ++i) + if (type==sIndexArg[i].mName) + { + mArgumentType = ArgumentType_Index; + mType = sIndexArg[i].mType; + mClass = sIndexArg[i].mClass; + + std::istringstream stream (universalId.substr (0, index)); + + if (stream >> mIndex) + return; + + break; + } + } + + throw std::runtime_error ("invalid UniversalId: " + universalId); +} + CSMWorld::UniversalId::UniversalId (Type type) : mArgumentType (ArgumentType_None), mType (type), mIndex (0) { for (int i=0; sNoArg[i].mName; ++i) @@ -151,7 +197,7 @@ std::string CSMWorld::UniversalId::toString() const { std::ostringstream stream; - stream << getTypeName(); + stream << getTypeName() << ":"; switch (mArgumentType) { diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 6dc0eda1e..7b630ebc7 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -44,6 +44,8 @@ namespace CSMWorld public: + UniversalId (const std::string& universalId); + UniversalId (Type type = Type_None); ///< Using a type for a non-argument-less UniversalId will throw an exception. From 5f5addf0527a37f07d7d9e48b6e9c9b3974d33e8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 9 Dec 2012 00:12:24 +0100 Subject: [PATCH 097/378] fixed an enum --- .../openmw/mwmechanics/mechanicsmanagerimp.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 1b6e4b9f3..079f8520b 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -38,7 +38,7 @@ namespace MWMechanics creatureStats.getAttribute(5).setBase (player->mNpdt52.mEndurance); creatureStats.getAttribute(6).setBase (player->mNpdt52.mPersonality); creatureStats.getAttribute(7).setBase (player->mNpdt52.mLuck); - + const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); @@ -46,7 +46,7 @@ namespace MWMechanics if (mRaceSelected) { const ESM::Race *race = - esmStore.get().find(player->mRace); + esmStore.get().find(player->mRace); bool male = (player->mFlags & ESM::NPC::Female) == 0; @@ -72,14 +72,14 @@ namespace MWMechanics for (int i=0; i<27; ++i) { int bonus = 0; - + for (int i2=0; i2<7; ++i2) if (race->mData.mBonus[i2].mSkill==i) { bonus = race->mData.mBonus[i2].mBonus; break; } - + npcStats.getSkill (i).setBase (5 + bonus); } @@ -270,7 +270,7 @@ namespace MWMechanics // basic player profile; should not change anymore after the creation phase is finished. MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); - + MWBase::World *world = MWBase::Environment::get().getWorld(); const ESM::NPC *player = world->getPlayer().getPlayer().get()->mBase; @@ -442,7 +442,7 @@ namespace MWMechanics if (playerStats.hasCommonDisease() || playerStats.hasBlightDisease()) x += MWBase::Environment::get().getWorld()->getStore().get().find("fDispDiseaseMod")->getFloat(); - if (playerNpcStats.getDrawState() == MWMechanics::DrawState_::DrawState_Weapon) + if (playerNpcStats.getDrawState() == MWMechanics::DrawState_Weapon) x += MWBase::Environment::get().getWorld()->getStore().get().find("fDispWeaponDrawn")->getFloat(); int effective_disposition = std::max(0,std::min(int(x),100));//, normally clamped to [0..100] when used @@ -455,7 +455,7 @@ namespace MWMechanics return basePrice; MWMechanics::NpcStats sellerSkill = MWWorld::Class::get(ptr).getNpcStats(ptr); - MWMechanics::CreatureStats sellerStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); + MWMechanics::CreatureStats sellerStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); MWWorld::Ptr playerPtr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWMechanics::NpcStats playerSkill = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); @@ -471,13 +471,13 @@ namespace MWMechanics float d = std::min(sellerSkill.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); - + float pcTerm = (clampedDisposition - 50 + a + b + c) * playerStats.getFatigueTerm(); float npcTerm = (d + e + f) * sellerStats.getFatigueTerm(); float buyTerm = 0.01 * (100 - 0.5 * (pcTerm - npcTerm)); float sellTerm = 0.01 * (50 - 0.5 * (npcTerm - pcTerm)); - float x; + float x; if(buying) x = buyTerm; else x = std::min(buyTerm, sellTerm); int offerPrice; From 832fc56d34d06092bd6fc4344ca6fe71399b2205 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 9 Dec 2012 11:10:35 +0100 Subject: [PATCH 098/378] changed UniversalId to string conversion --- apps/opencs/model/tools/mandatoryid.cpp | 2 +- apps/opencs/model/world/universalid.cpp | 73 +++++++++++++------------ 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/apps/opencs/model/tools/mandatoryid.cpp b/apps/opencs/model/tools/mandatoryid.cpp index 87ac77e67..f9f2ca378 100644 --- a/apps/opencs/model/tools/mandatoryid.cpp +++ b/apps/opencs/model/tools/mandatoryid.cpp @@ -17,5 +17,5 @@ void CSMTools::MandatoryIdStage::perform (int stage, std::vector& m { if (mIdCollection.searchId (mIds.at (stage))==-1 || mIdCollection.getRecord (mIds.at (stage)).isDeleted()) - messages.push_back (mCollectionId.toString() + " Missing mandatory record: " + mIds.at (stage)); + messages.push_back (mCollectionId.toString() + "|Missing mandatory record: " + mIds.at (stage)); } \ No newline at end of file diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 229779f5a..98fb1d0b9 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -43,39 +43,44 @@ CSMWorld::UniversalId::UniversalId (const std::string& universalId) { std::string type = universalId.substr (0, index); - for (int i=0; sNoArg[i].mName; ++i) - if (type==sNoArg[i].mName) - { - mArgumentType = ArgumentType_None; - mType = sNoArg[i].mType; - mClass = sNoArg[i].mClass; - return; - } - - for (int i=0; sIdArg[i].mName; ++i) - if (type==sIdArg[i].mName) - { - mArgumentType = ArgumentType_Id; - mType = sIdArg[i].mType; - mClass = sIdArg[i].mClass; - mId = universalId.substr (0, index); - return; - } - - for (int i=0; sIndexArg[i].mName; ++i) - if (type==sIndexArg[i].mName) - { - mArgumentType = ArgumentType_Index; - mType = sIndexArg[i].mType; - mClass = sIndexArg[i].mClass; - - std::istringstream stream (universalId.substr (0, index)); - - if (stream >> mIndex) + if (index==std::string::npos) + { + for (int i=0; sNoArg[i].mName; ++i) + if (type==sNoArg[i].mName) + { + mArgumentType = ArgumentType_None; + mType = sNoArg[i].mType; + mClass = sNoArg[i].mClass; + return; + } + } + else + { + for (int i=0; sIdArg[i].mName; ++i) + if (type==sIdArg[i].mName) + { + mArgumentType = ArgumentType_Id; + mType = sIdArg[i].mType; + mClass = sIdArg[i].mClass; + mId = universalId.substr (0, index); return; + } - break; - } + for (int i=0; sIndexArg[i].mName; ++i) + if (type==sIndexArg[i].mName) + { + mArgumentType = ArgumentType_Index; + mType = sIndexArg[i].mType; + mClass = sIndexArg[i].mClass; + + std::istringstream stream (universalId.substr (0, index)); + + if (stream >> mIndex) + return; + + break; + } + } } throw std::runtime_error ("invalid UniversalId: " + universalId); @@ -197,13 +202,13 @@ std::string CSMWorld::UniversalId::toString() const { std::ostringstream stream; - stream << getTypeName() << ":"; + stream << getTypeName(); switch (mArgumentType) { case ArgumentType_None: break; - case ArgumentType_Id: stream << " " << mId; - case ArgumentType_Index: stream << " " << mIndex; + case ArgumentType_Id: stream << ": " << mId; + case ArgumentType_Index: stream << ": " << mIndex; } return stream.str(); From e3dd3d565c36a2568a6192bd6a4ca000fe63fd1b Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 10 Dec 2012 00:59:39 +0100 Subject: [PATCH 099/378] fix 2 leaks --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 5 +++++ apps/openmw/mwworld/store.hpp | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index a2cccfd13..5f61ab8f0 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -313,7 +313,12 @@ void FFmpeg_Decoder::close() mStreams.erase(mStreams.begin()); } if(mFormatCtx) + { + AVIOContext* context = mFormatCtx->pb; + av_free(context); + mFormatCtx->pb = NULL; av_close_input_file(mFormatCtx); + } mFormatCtx = NULL; mDataStream.setNull(); diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index fd93f39f1..046de8c63 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -287,6 +287,16 @@ namespace MWWorld public: typedef SharedIterator iterator; + virtual ~Store() + { + for (std::vector::const_iterator it = + mStatic.begin(); it != mStatic.end(); ++it) + { + delete *it; + } + + } + int getSize() const { return mStatic.size(); } From 4114e823acb9d51998773c22c8751d995b187b43 Mon Sep 17 00:00:00 2001 From: psi29a Date: Mon, 10 Dec 2012 10:32:00 +0100 Subject: [PATCH 100/378] Allow the ability to choose to compile against boost libs statically. --- CMakeLists.txt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b740f735..0abaf3403 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,8 @@ set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VE configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp") option(MYGUI_STATIC "Link static build of Mygui into the binaries" FALSE) -option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binaries" FALSE) +option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binaries" FALSE) +option(BOOST_STATIC "Link static build of Boost into the binaries" FALSE) # Apps and tools option(BUILD_ESMTOOL "build ESM inspector" ON) @@ -167,9 +168,9 @@ if (WIN32) set(PLATFORM_INCLUDE_DIR "platform") add_definitions(-DBOOST_ALL_NO_LIB) else (WIN32) -set(PLATFORM_INCLUDE_DIR "") -find_path (UUID_INCLUDE_DIR uuid/uuid.h) -include_directories(${UUID_INCLUDE_DIR}) + set(PLATFORM_INCLUDE_DIR "") + find_path (UUID_INCLUDE_DIR uuid/uuid.h) + include_directories(${UUID_INCLUDE_DIR}) endif (WIN32) if (MSVC10) set(PLATFORM_INCLUDE_DIR "") @@ -183,7 +184,7 @@ endif (APPLE) # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) -find_package (Threads) + find_package (Threads) endif() # find boost without components so we can use Boost_VERSION @@ -196,6 +197,10 @@ if (Boost_VERSION LESS 104900) set(BOOST_COMPONENTS ${BOOST_COMPONENTS} wave) endif() +IF(BOOST_STATIC) + set(Boost_USE_STATIC_LIBS ON) +endif() + find_package(OGRE REQUIRED) find_package(MyGUI REQUIRED) find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) From de4a03081ca3e4259971fb03b3415dacc09af69c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 10 Dec 2012 11:04:21 +0100 Subject: [PATCH 101/378] fixed skill level type in skill level up message --- apps/openmw/mwmechanics/npcstats.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index eb2523372..26c4c8e9a 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -220,7 +220,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas std::stringstream message; message << boost::format(MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", "")) % std::string("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}") - % base; + % static_cast (base); MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), std::vector()); if (mLevelProgress >= 10) From 44010974fc19ecddcc4b3b259ff2664434bed9d3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 10 Dec 2012 12:34:28 +0100 Subject: [PATCH 102/378] workaround for HLSL error X4579 --- files/materials/core.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/files/materials/core.h b/files/materials/core.h index e9577bbf3..1c9ea1d1d 100644 --- a/files/materials/core.h +++ b/files/materials/core.h @@ -1,5 +1,6 @@ -#define gammaCorrectRead(v) pow(v, float3(gammaCorrection,gammaCorrection,gammaCorrection)) -#define gammaCorrectOutput(v) pow(v, float3(1.f/gammaCorrection,1.f/gammaCorrection,1.f/gammaCorrection)) +#define gammaCorrectRead(v) pow(max(v, 0.00001f), float3(gammaCorrection,gammaCorrection,gammaCorrection)) +#define gammaCorrectOutput(v) pow(max(v, 0.00001f), float3(1.f/gammaCorrection,1.f/gammaCorrection,1.f/gammaCorrection)) + #if SH_HLSL == 1 || SH_CG == 1 From 0ed378dfb21fd3796f44a5ee0cd5542ca88e7af6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 11 Dec 2012 11:24:30 +0100 Subject: [PATCH 103/378] changed sub view factory method signature --- apps/opencs/view/doc/view.cpp | 2 +- apps/opencs/view/world/subview.hpp | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 539b149cb..c1659b388 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -191,7 +191,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id) if (iter==mSubViewFactories.end()) throw std::logic_error ("can't create subview for " + id.toString()); - CSVWorld::SubView *view = iter->second->makeSubView (id, mDocument->getData(), mDocument->getUndoStack()); + CSVWorld::SubView *view = iter->second->makeSubView (id, *mDocument); addDockWidget (Qt::TopDockWidgetArea, view); view->show(); } diff --git a/apps/opencs/view/world/subview.hpp b/apps/opencs/view/world/subview.hpp index 95448cb47..ff130828b 100644 --- a/apps/opencs/view/world/subview.hpp +++ b/apps/opencs/view/world/subview.hpp @@ -1,6 +1,8 @@ #ifndef CSV_WORLD_SUBVIEW_H #define CSV_WORLD_SUBVIEW_H +#include "../../model/doc/document.hpp" + #include "../../model/world/universalid.hpp" #include @@ -35,21 +37,20 @@ namespace CSVWorld struct SubViewFactoryBase { - virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack) + virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) = 0; }; template struct SubViewFactory : public SubViewFactoryBase { - virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack); + virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); }; template - SubView *SubViewFactory::makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data, - QUndoStack& undoStack) + SubView *SubViewFactory::makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) { - return new SubViewT (id, data, undoStack); + return new SubViewT (id, document.getData(), document.getUndoStack()); } template @@ -59,7 +60,7 @@ namespace CSVWorld SubViewFactoryWithCreateFlag (bool createAndDelete); - virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack); + virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); }; template @@ -69,10 +70,9 @@ namespace CSVWorld template SubView *SubViewFactoryWithCreateFlag::makeSubView (const CSMWorld::UniversalId& id, - CSMWorld::Data& data, - QUndoStack& undoStack) + CSMDoc::Document& document) { - return new SubViewT (id, data, undoStack, mCreateAndDelete); + return new SubViewT (id, document.getData(), document.getUndoStack(), mCreateAndDelete); } } From 2db930a5cf36f19b174b8a1c8e1cdb6789ba6a14 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 11 Dec 2012 13:22:43 +0100 Subject: [PATCH 104/378] rewrote subview factory system --- apps/opencs/CMakeLists.txt | 10 +-- apps/opencs/view/{world => doc}/subview.cpp | 4 +- apps/opencs/view/doc/subview.hpp | 41 +++++++++++ apps/opencs/view/doc/subviewfactory.cpp | 38 ++++++++++ apps/opencs/view/doc/subviewfactory.hpp | 55 ++++++++++++++ apps/opencs/view/doc/subviewfactoryimp.hpp | 50 +++++++++++++ apps/opencs/view/doc/view.cpp | 20 ++---- apps/opencs/view/doc/view.hpp | 9 +-- apps/opencs/view/world/subview.hpp | 79 --------------------- apps/opencs/view/world/subviews.cpp | 12 ++++ apps/opencs/view/world/subviews.hpp | 14 ++++ apps/opencs/view/world/tablesubview.cpp | 6 +- apps/opencs/view/world/tablesubview.hpp | 12 ++-- 13 files changed, 239 insertions(+), 111 deletions(-) rename apps/opencs/view/{world => doc}/subview.cpp (65%) create mode 100644 apps/opencs/view/doc/subview.hpp create mode 100644 apps/opencs/view/doc/subviewfactory.cpp create mode 100644 apps/opencs/view/doc/subviewfactory.hpp create mode 100644 apps/opencs/view/doc/subviewfactoryimp.hpp delete mode 100644 apps/opencs/view/world/subview.hpp create mode 100644 apps/opencs/view/world/subviews.cpp create mode 100644 apps/opencs/view/world/subviews.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index bad1e1376..67cbcc772 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -10,9 +10,10 @@ set (OPENCS_SRC model/tools/tools.cpp model/tools/operation.cpp model/tools/stage.cpp model/tools/verifier.cpp model/tools/mandatoryid.cpp - view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp + view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp view/doc/subviewfactory.cpp + view/doc/subview.cpp - view/world/subview.cpp view/world/table.cpp view/world/tablesubview.cpp + view/world/table.cpp view/world/tablesubview.cpp view/world/subviews.cpp ) set (OPENCS_HDR @@ -27,9 +28,10 @@ set (OPENCS_HDR model/tools/tools.hpp model/tools/operation.hpp model/tools/stage.hpp model/tools/verifier.hpp model/tools/mandatoryid.hpp - view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp + view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp view/doc/subviewfactory.hpp + view/doc/subview.hpp view/doc/subviewfactoryimp.hpp - view/world/subview.hpp view/world/table.hpp view/world/tablesubview.hpp + view/world/table.hpp view/world/tablesubview.hpp view/world/subviews.hpp ) set (OPENCS_US diff --git a/apps/opencs/view/world/subview.cpp b/apps/opencs/view/doc/subview.cpp similarity index 65% rename from apps/opencs/view/world/subview.cpp rename to apps/opencs/view/doc/subview.cpp index 23f075980..1c356fa73 100644 --- a/apps/opencs/view/world/subview.cpp +++ b/apps/opencs/view/doc/subview.cpp @@ -1,7 +1,7 @@ #include "subview.hpp" -CSVWorld::SubView::SubView (const CSMWorld::UniversalId& id) : mUniversalId (id) +CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id) : mUniversalId (id) { /// \todo add a button to the title bar that clones this sub view @@ -12,7 +12,7 @@ CSVWorld::SubView::SubView (const CSMWorld::UniversalId& id) : mUniversalId (id) setMinimumHeight (60); } -CSMWorld::UniversalId CSVWorld::SubView::getUniversalId() const +CSMWorld::UniversalId CSVDoc::SubView::getUniversalId() const { return mUniversalId; } diff --git a/apps/opencs/view/doc/subview.hpp b/apps/opencs/view/doc/subview.hpp new file mode 100644 index 000000000..50ec511ab --- /dev/null +++ b/apps/opencs/view/doc/subview.hpp @@ -0,0 +1,41 @@ +#ifndef CSV_DOC_SUBVIEW_H +#define CSV_DOC_SUBVIEW_H + +#include "../../model/doc/document.hpp" + +#include "../../model/world/universalid.hpp" + +#include "subviewfactory.hpp" + +#include + +class QUndoStack; + +namespace CSMWorld +{ + class Data; +} + +namespace CSVDoc +{ + class SubView : public QDockWidget + { + Q_OBJECT + + CSMWorld::UniversalId mUniversalId; + + // not implemented + SubView (const SubView&); + SubView& operator= (SubView&); + + public: + + SubView (const CSMWorld::UniversalId& id); + + CSMWorld::UniversalId getUniversalId() const; + + virtual void setEditLock (bool locked) = 0; + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/doc/subviewfactory.cpp b/apps/opencs/view/doc/subviewfactory.cpp new file mode 100644 index 000000000..8576f6b1d --- /dev/null +++ b/apps/opencs/view/doc/subviewfactory.cpp @@ -0,0 +1,38 @@ + +#include "subviewfactory.hpp" + +#include + +#include + +CSVDoc::SubViewFactoryBase::SubViewFactoryBase() {} + +CSVDoc::SubViewFactoryBase::~SubViewFactoryBase() {} + + +CSVDoc::SubViewFactoryManager::SubViewFactoryManager() {} + +CSVDoc::SubViewFactoryManager::~SubViewFactoryManager() +{ + for (std::map::iterator iter (mSubViewFactories.begin()); + iter!=mSubViewFactories.end(); ++iter) + delete iter->second; +} + +void CSVDoc::SubViewFactoryManager::add (const CSMWorld::UniversalId::Type& id, SubViewFactoryBase *factory) +{ + assert (mSubViewFactories.find (id)==mSubViewFactories.end()); + + mSubViewFactories.insert (std::make_pair (id, factory)); +} + +CSVDoc::SubView *CSVDoc::SubViewFactoryManager::makeSubView (const CSMWorld::UniversalId& id, + CSMDoc::Document& document) +{ + std::map::iterator iter = mSubViewFactories.find (id.getType()); + + if (iter==mSubViewFactories.end()) + throw std::runtime_error ("Failed to create a sub view for: " + id.toString()); + + return iter->second->makeSubView (id, document); +} \ No newline at end of file diff --git a/apps/opencs/view/doc/subviewfactory.hpp b/apps/opencs/view/doc/subviewfactory.hpp new file mode 100644 index 000000000..1f7c15480 --- /dev/null +++ b/apps/opencs/view/doc/subviewfactory.hpp @@ -0,0 +1,55 @@ +#ifndef CSV_DOC_SUBVIEWFACTORY_H +#define CSV_DOC_SUBVIEWFACTORY_H + +#include + +#include "../../model/world/universalid.hpp" + +namespace CSMDoc +{ + class Document; +} + +namespace CSVDoc +{ + class SubView; + + class SubViewFactoryBase + { + // not implemented + SubViewFactoryBase (const SubViewFactoryBase&); + SubViewFactoryBase& operator= (const SubViewFactoryBase&); + + public: + + SubViewFactoryBase(); + + virtual ~SubViewFactoryBase(); + + virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) = 0; + ///< The ownership of the returned sub view is not transferred. + }; + + class SubViewFactoryManager + { + std::map mSubViewFactories; + + // not implemented + SubViewFactoryManager (const SubViewFactoryManager&); + SubViewFactoryManager& operator= (const SubViewFactoryManager&); + + public: + + SubViewFactoryManager(); + + ~SubViewFactoryManager(); + + void add (const CSMWorld::UniversalId::Type& id, SubViewFactoryBase *factory); + ///< The ownership of \a factory is transferred to this. + + SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); + ///< The ownership of the returned sub view is not transferred. + }; +} + +#endif diff --git a/apps/opencs/view/doc/subviewfactoryimp.hpp b/apps/opencs/view/doc/subviewfactoryimp.hpp new file mode 100644 index 000000000..d16e0b2b7 --- /dev/null +++ b/apps/opencs/view/doc/subviewfactoryimp.hpp @@ -0,0 +1,50 @@ +#ifndef CSV_DOC_SUBVIEWFACTORYIMP_H +#define CSV_DOC_SUBVIEWFACTORYIMP_H + +#include "../../model/doc/document.hpp" + +#include "subviewfactory.hpp" + +namespace CSVDoc +{ + template + class SubViewFactory : public SubViewFactoryBase + { + public: + + virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); + }; + + template + CSVDoc::SubView *SubViewFactory::makeSubView (const CSMWorld::UniversalId& id, + CSMDoc::Document& document) + { + return new SubViewT (id, document); + } + + template + class SubViewFactoryWithCreateFlag : public SubViewFactoryBase + { + bool mCreateAndDelete; + + public: + + SubViewFactoryWithCreateFlag (bool createAndDelete); + + virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); + }; + + template + SubViewFactoryWithCreateFlag::SubViewFactoryWithCreateFlag (bool createAndDelete) + : mCreateAndDelete (createAndDelete) + {} + + template + CSVDoc::SubView *SubViewFactoryWithCreateFlag::makeSubView (const CSMWorld::UniversalId& id, + CSMDoc::Document& document) + { + return new SubViewT (id, document, mCreateAndDelete); + } +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index c1659b388..47053192b 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -10,10 +10,11 @@ #include "../../model/doc/document.hpp" -#include "../world/tablesubview.hpp" +#include "../world/subviews.hpp" #include "viewmanager.hpp" #include "operations.hpp" +#include "subview.hpp" void CSVDoc::View::closeEvent (QCloseEvent *event) { @@ -120,15 +121,11 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to setupUi(); - mSubViewFactories.insert (std::make_pair (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), - new CSVWorld::SubViewFactoryWithCreateFlag (true))); + CSVWorld::addSubViewFactories (mSubViewFactory); } CSVDoc::View::~View() { - for (std::map::iterator iter (mSubViewFactories.begin()); - iter!=mSubViewFactories.end(); ++iter) - delete iter->second; } const CSMDoc::Document *CSVDoc::View::getDocument() const @@ -165,9 +162,9 @@ void CSVDoc::View::updateDocumentState() if (!(state & operations[i])) mOperations->quitOperation (operations[i]); - QList subViews = findChildren(); + QList subViews = findChildren(); - for (QList::iterator iter (subViews.begin()); iter!=subViews.end(); ++iter) + for (QList::iterator iter (subViews.begin()); iter!=subViews.end(); ++iter) (*iter)->setEditLock (state & CSMDoc::State_Locked); } @@ -186,12 +183,7 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id) /// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis) - std::map::iterator iter = mSubViewFactories.find (id); - - if (iter==mSubViewFactories.end()) - throw std::logic_error ("can't create subview for " + id.toString()); - - CSVWorld::SubView *view = iter->second->makeSubView (id, *mDocument); + SubView *view = mSubViewFactory.makeSubView (id, *mDocument); addDockWidget (Qt::TopDockWidgetArea, view); view->show(); } diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index c86c3b362..f7d4dd373 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -6,6 +6,8 @@ #include +#include "subviewfactory.hpp" + class QAction; namespace CSMDoc @@ -18,11 +20,6 @@ namespace CSMWorld class UniversalId; } -namespace CSVWorld -{ - struct SubViewFactoryBase; -} - namespace CSVDoc { class ViewManager; @@ -42,7 +39,7 @@ namespace CSVDoc QAction *mVerify; std::vector mEditingActions; Operations *mOperations; - std::map mSubViewFactories; + SubViewFactoryManager mSubViewFactory; // not implemented View (const View&); diff --git a/apps/opencs/view/world/subview.hpp b/apps/opencs/view/world/subview.hpp deleted file mode 100644 index ff130828b..000000000 --- a/apps/opencs/view/world/subview.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef CSV_WORLD_SUBVIEW_H -#define CSV_WORLD_SUBVIEW_H - -#include "../../model/doc/document.hpp" - -#include "../../model/world/universalid.hpp" - -#include - -class QUndoStack; - -namespace CSMWorld -{ - class Data; -} - -namespace CSVWorld -{ - class SubView : public QDockWidget - { - Q_OBJECT - - CSMWorld::UniversalId mUniversalId; - - // not implemented - SubView (const SubView&); - SubView& operator= (SubView&); - - public: - - SubView (const CSMWorld::UniversalId& id); - - CSMWorld::UniversalId getUniversalId() const; - - virtual void setEditLock (bool locked) = 0; - }; - - struct SubViewFactoryBase - { - virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) - = 0; - }; - - template - struct SubViewFactory : public SubViewFactoryBase - { - virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); - }; - - template - SubView *SubViewFactory::makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) - { - return new SubViewT (id, document.getData(), document.getUndoStack()); - } - - template - struct SubViewFactoryWithCreateFlag : public SubViewFactoryBase - { - bool mCreateAndDelete; - - SubViewFactoryWithCreateFlag (bool createAndDelete); - - virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); - }; - - template - SubViewFactoryWithCreateFlag::SubViewFactoryWithCreateFlag (bool createAndDelete) - : mCreateAndDelete (createAndDelete) - {} - - template - SubView *SubViewFactoryWithCreateFlag::makeSubView (const CSMWorld::UniversalId& id, - CSMDoc::Document& document) - { - return new SubViewT (id, document.getData(), document.getUndoStack(), mCreateAndDelete); - } -} - -#endif \ No newline at end of file diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp new file mode 100644 index 000000000..fdc0cb31d --- /dev/null +++ b/apps/opencs/view/world/subviews.cpp @@ -0,0 +1,12 @@ + +#include "subviews.hpp" + +#include "../doc/subviewfactoryimp.hpp" + +#include "tablesubview.hpp" + +void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) +{ + manager.add (CSMWorld::UniversalId::Type_Globals, + new CSVDoc::SubViewFactoryWithCreateFlag (true)); +} \ No newline at end of file diff --git a/apps/opencs/view/world/subviews.hpp b/apps/opencs/view/world/subviews.hpp new file mode 100644 index 000000000..51e4cb083 --- /dev/null +++ b/apps/opencs/view/world/subviews.hpp @@ -0,0 +1,14 @@ +#ifndef CSV_WORLD_SUBVIEWS_H +#define CSV_WORLD_SUBVIEWS_H + +namespace CSVDoc +{ + class SubViewFactoryManager; +} + +namespace CSVWorld +{ + void addSubViewFactories (CSVDoc::SubViewFactoryManager& manager); +} + +#endif diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 3bc555a2d..4abd531d0 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -1,13 +1,15 @@ #include "tablesubview.hpp" +#include "../../model/doc/document.hpp" + #include "table.hpp" -CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, +CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete) : SubView (id) { - setWidget (mTable = new Table (id, data, undoStack, createAndDelete)); + setWidget (mTable = new Table (id, document.getData(), document.getUndoStack(), createAndDelete)); } void CSVWorld::TableSubView::setEditLock (bool locked) diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index b45b3c279..9b0785c47 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -1,22 +1,26 @@ #ifndef CSV_WORLD_TABLESUBVIEW_H #define CSV_WORLD_TABLESUBVIEW_H -#include "subview.hpp" +#include "../doc/subview.hpp" class QUndoStack; +namespace CSMDoc +{ + class Document; +} + namespace CSVWorld { class Table; - class TableSubView : public SubView + class TableSubView : public CSVDoc::SubView { Table *mTable; public: - TableSubView (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, - bool createAndDelete); + TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete); virtual void setEditLock (bool locked); }; From c75563c184de28aca46ae2cfb52a4318771cfa1e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 11 Dec 2012 15:35:47 +0100 Subject: [PATCH 105/378] report model and view --- apps/opencs/CMakeLists.txt | 8 ++- apps/opencs/model/doc/document.cpp | 10 +++- apps/opencs/model/doc/document.hpp | 7 ++- apps/opencs/model/tools/reportmodel.cpp | 66 ++++++++++++++++++++++++ apps/opencs/model/tools/reportmodel.hpp | 35 +++++++++++++ apps/opencs/model/tools/tools.cpp | 28 +++++++--- apps/opencs/model/tools/tools.hpp | 13 ++++- apps/opencs/model/world/idtable.cpp | 2 +- apps/opencs/model/world/universalid.cpp | 3 +- apps/opencs/model/world/universalid.hpp | 5 +- apps/opencs/view/doc/view.cpp | 5 +- apps/opencs/view/tools/reportsubview.cpp | 25 +++++++++ apps/opencs/view/tools/reportsubview.hpp | 29 +++++++++++ apps/opencs/view/tools/subviews.cpp | 12 +++++ apps/opencs/view/tools/subviews.hpp | 14 +++++ 15 files changed, 246 insertions(+), 16 deletions(-) create mode 100644 apps/opencs/model/tools/reportmodel.cpp create mode 100644 apps/opencs/model/tools/reportmodel.hpp create mode 100644 apps/opencs/view/tools/reportsubview.cpp create mode 100644 apps/opencs/view/tools/reportsubview.hpp create mode 100644 apps/opencs/view/tools/subviews.cpp create mode 100644 apps/opencs/view/tools/subviews.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 67cbcc772..1f9fe85a2 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -8,12 +8,14 @@ set (OPENCS_SRC model/world/commands.cpp model/world/idtableproxymodel.cpp model/world/record.cpp model/tools/tools.cpp model/tools/operation.cpp model/tools/stage.cpp model/tools/verifier.cpp - model/tools/mandatoryid.cpp + model/tools/mandatoryid.cpp model/tools/reportmodel.cpp view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp view/doc/subviewfactory.cpp view/doc/subview.cpp view/world/table.cpp view/world/tablesubview.cpp view/world/subviews.cpp + + view/tools/reportsubview.cpp view/tools/subviews.cpp ) set (OPENCS_HDR @@ -26,12 +28,14 @@ set (OPENCS_HDR model/world/commands.hpp model/tools/tools.hpp model/tools/operation.hpp model/tools/stage.hpp model/tools/verifier.hpp - model/tools/mandatoryid.hpp + model/tools/mandatoryid.hpp model/tools/reportmodel.hpp view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp view/doc/subviewfactory.hpp view/doc/subview.hpp view/doc/subviewfactoryimp.hpp view/world/table.hpp view/world/tablesubview.hpp view/world/subviews.hpp + + view/tools/reportsubview.hpp view/tools/subviews.hpp ) set (OPENCS_US diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index f03344e08..9d2694483 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -50,10 +50,11 @@ void CSMDoc::Document::save() emit progress (1, 16, State_Saving, 1, this); } -void CSMDoc::Document::verify() +CSMWorld::UniversalId CSMDoc::Document::verify() { - mTools.runVerifier(); + CSMWorld::UniversalId id = mTools.runVerifier(); emit stateChanged (getState(), this); + return id; } void CSMDoc::Document::abortOperation (int type) @@ -102,6 +103,11 @@ CSMWorld::Data& CSMDoc::Document::getData() return mData; } +CSMTools::ReportModel *CSMDoc::Document::getReport (const CSMWorld::UniversalId& id) +{ + return mTools.getReport (id); +} + void CSMDoc::Document::progress (int current, int max, int type) { emit progress (current, max, type, 1, this); diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index f9d0a232c..43e8bba37 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -13,6 +13,8 @@ #include "state.hpp" +class QAbstractItemModel; + namespace CSMDoc { class Document : public QObject @@ -50,7 +52,7 @@ namespace CSMDoc void save(); - void verify(); + CSMWorld::UniversalId verify(); void abortOperation (int type); @@ -58,6 +60,9 @@ namespace CSMDoc CSMWorld::Data& getData(); + CSMTools::ReportModel *getReport (const CSMWorld::UniversalId& id); + ///< The ownership of the returned report is not transferred. + signals: void stateChanged (int state, CSMDoc::Document *document); diff --git a/apps/opencs/model/tools/reportmodel.cpp b/apps/opencs/model/tools/reportmodel.cpp new file mode 100644 index 000000000..f142d7669 --- /dev/null +++ b/apps/opencs/model/tools/reportmodel.cpp @@ -0,0 +1,66 @@ + +#include "reportmodel.hpp" + +#include + +int CSMTools::ReportModel::rowCount (const QModelIndex & parent) const +{ + if (parent.isValid()) + return 0; + + return mRows.size(); +} + +int CSMTools::ReportModel::columnCount (const QModelIndex & parent) const +{ + if (parent.isValid()) + return 0; + + return 2; +} + +QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const +{ + if (role!=Qt::DisplayRole) + return QVariant(); + + if (index.column()==0) + return static_cast (mRows.at (index.row()).first.getType()); + else + return mRows.at (index.row()).second.c_str(); +} + +QVariant CSMTools::ReportModel::headerData (int section, Qt::Orientation orientation, int role) const +{ + if (role!=Qt::DisplayRole) + return QVariant(); + + if (orientation==Qt::Vertical) + return QVariant(); + + return tr (section==0 ? "Type" : "Description"); +} + +bool CSMTools::ReportModel::removeRows (int row, int count, const QModelIndex& parent) +{ + if (parent.isValid()) + return false; + + mRows.erase (mRows.begin()+row, mRows.begin()+row+count); + + return true; +} + +void CSMTools::ReportModel::add (const std::string& row) +{ + std::string::size_type index = row.find ('|'); + + if (index==std::string::npos) + throw std::logic_error ("invalid report message"); + + beginInsertRows (QModelIndex(), mRows.size(), mRows.size()); + + mRows.push_back (std::make_pair (row.substr (0, index), row.substr (index+1))); + + endInsertRows(); +} \ No newline at end of file diff --git a/apps/opencs/model/tools/reportmodel.hpp b/apps/opencs/model/tools/reportmodel.hpp new file mode 100644 index 000000000..8814b6d49 --- /dev/null +++ b/apps/opencs/model/tools/reportmodel.hpp @@ -0,0 +1,35 @@ +#ifndef CSM_TOOLS_REPORTMODEL_H +#define CSM_TOOLS_REPORTMODEL_H + +#include +#include + +#include + +#include "../world/universalid.hpp" + +namespace CSMTools +{ + class ReportModel : public QAbstractTableModel + { + Q_OBJECT + + std::vector > mRows; + + public: + + virtual int rowCount (const QModelIndex & parent = QModelIndex()) const; + + virtual int columnCount (const QModelIndex & parent = QModelIndex()) const; + + virtual QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; + + virtual QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex()); + + void add (const std::string& row); + }; +} + +#endif diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index a3cdb7822..8dd1c0fe6 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -8,7 +8,9 @@ #include "../doc/state.hpp" #include "../world/data.hpp" +#include "../world/universalid.hpp" +#include "reportmodel.hpp" #include "mandatoryid.hpp" CSMTools::Operation *CSMTools::Tools::get (int type) @@ -54,9 +56,10 @@ CSMTools::Verifier *CSMTools::Tools::getVerifier() return mVerifier; } -CSMTools::Tools::Tools (CSMWorld::Data& data) : mData (data), mVerifier (0) +CSMTools::Tools::Tools (CSMWorld::Data& data) : mData (data), mVerifier (0), mNextReportNumber (0) { - + for (std::map::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter) + delete iter->second; } CSMTools::Tools::~Tools() @@ -64,9 +67,14 @@ CSMTools::Tools::~Tools() delete mVerifier; } -void CSMTools::Tools::runVerifier() +CSMWorld::UniversalId CSMTools::Tools::runVerifier() { + mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel)); + mActiveReports[CSMDoc::State_Verifying] = mNextReportNumber-1; + getVerifier()->start(); + + return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_VerificationResults, mNextReportNumber-1); } void CSMTools::Tools::abortOperation (int type) @@ -93,15 +101,23 @@ int CSMTools::Tools::getRunningOperations() const return result; } +CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& id) +{ + if (id.getType()!=CSMWorld::UniversalId::Type_VerificationResults) + throw std::logic_error ("invalid request for report model: " + id.toString()); + + return mReports.at (id.getIndex()); +} + void CSMTools::Tools::verifierDone() { emit done (CSMDoc::State_Verifying); } -#include void CSMTools::Tools::verifierMessage (const QString& message, int type) { - /// \todo store it in a result model instead + std::map::iterator iter = mActiveReports.find (type); - std::cout << message.toStdString() << std::endl; + if (iter!=mActiveReports.end()) + mReports[iter->second]->add (message.toStdString()); } \ No newline at end of file diff --git a/apps/opencs/model/tools/tools.hpp b/apps/opencs/model/tools/tools.hpp index da49fd8b8..652345c6d 100644 --- a/apps/opencs/model/tools/tools.hpp +++ b/apps/opencs/model/tools/tools.hpp @@ -3,15 +3,19 @@ #include +#include + namespace CSMWorld { class Data; + class UniversalId; } namespace CSMTools { class Verifier; class Operation; + class ReportModel; class Tools : public QObject { @@ -19,6 +23,9 @@ namespace CSMTools CSMWorld::Data& mData; Verifier *mVerifier; + std::map mReports; + int mNextReportNumber; + std::map mActiveReports; // type, report number // not implemented Tools (const Tools&); @@ -38,13 +45,17 @@ namespace CSMTools virtual ~Tools(); - void runVerifier(); + CSMWorld::UniversalId runVerifier(); + ///< \return ID of the report for this verification run void abortOperation (int type); ///< \attention The operation is not aborted immediately. int getRunningOperations() const; + ReportModel *getReport (const CSMWorld::UniversalId& id); + ///< The ownership of the returned report is not transferred. + private slots: void verifierDone(); diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index a2b0a3c1f..3fe1fadfe 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -35,7 +35,7 @@ QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const return QVariant(); if (role==Qt::EditRole && !mIdCollection->isEditable (index.column())) - return QVariant(); + return QVariant(); return mIdCollection->getData (index.row(), index.column()); } diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 98fb1d0b9..81561bcf4 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -30,6 +30,7 @@ namespace static const TypeData sIndexArg[] = { + { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker }; @@ -39,7 +40,7 @@ CSMWorld::UniversalId::UniversalId (const std::string& universalId) { std::string::size_type index = universalId.find (':'); - if (index!=std::string::npos) + if (index==std::string::npos) { std::string type = universalId.substr (0, index); diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 7b630ebc7..14c086510 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -31,7 +31,10 @@ namespace CSMWorld enum Type { Type_None, - Type_Globals + + Type_Globals, + + Type_VerificationResults }; private: diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 47053192b..4ffd9d749 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -12,6 +12,8 @@ #include "../world/subviews.hpp" +#include "../tools/subviews.hpp" + #include "viewmanager.hpp" #include "operations.hpp" #include "subview.hpp" @@ -122,6 +124,7 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to setupUi(); CSVWorld::addSubViewFactories (mSubViewFactory); + CSVTools::addSubViewFactories (mSubViewFactory); } CSVDoc::View::~View() @@ -200,7 +203,7 @@ void CSVDoc::View::save() void CSVDoc::View::verify() { - mDocument->verify(); + addSubView (mDocument->verify()); } void CSVDoc::View::addGlobalsSubView() diff --git a/apps/opencs/view/tools/reportsubview.cpp b/apps/opencs/view/tools/reportsubview.cpp new file mode 100644 index 000000000..3c9283684 --- /dev/null +++ b/apps/opencs/view/tools/reportsubview.cpp @@ -0,0 +1,25 @@ + +#include "reportsubview.hpp" + +#include +#include + +#include "../../model/tools/reportmodel.hpp" + +CSVTools::ReportSubView::ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) +: CSVDoc::SubView (id) +{ + setWidget (mTable = new QTableView (this)); + mTable->setModel (document.getReport (id)); + + mTable->horizontalHeader()->setResizeMode (QHeaderView::Interactive); + mTable->verticalHeader()->hide(); + mTable->setSortingEnabled (true); + mTable->setSelectionBehavior (QAbstractItemView::SelectRows); + mTable->setSelectionMode (QAbstractItemView::ExtendedSelection); +} + +void CSVTools::ReportSubView::setEditLock (bool locked) +{ + // ignored. We don't change document state anyway. +} \ No newline at end of file diff --git a/apps/opencs/view/tools/reportsubview.hpp b/apps/opencs/view/tools/reportsubview.hpp new file mode 100644 index 000000000..f81f3c386 --- /dev/null +++ b/apps/opencs/view/tools/reportsubview.hpp @@ -0,0 +1,29 @@ +#ifndef CSV_TOOLS_REPORTSUBVIEW_H +#define CSV_TOOLS_REPORTSUBVIEW_H + +#include "../doc/subview.hpp" + +class QTableView; + +namespace CSMDoc +{ + class Document; +} + +namespace CSVTools +{ + class Table; + + class ReportSubView : public CSVDoc::SubView + { + QTableView *mTable; + + public: + + ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); + + virtual void setEditLock (bool locked); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/tools/subviews.cpp b/apps/opencs/view/tools/subviews.cpp new file mode 100644 index 000000000..781cf602e --- /dev/null +++ b/apps/opencs/view/tools/subviews.cpp @@ -0,0 +1,12 @@ + +#include "subviews.hpp" + +#include "../doc/subviewfactoryimp.hpp" + +#include "reportsubview.hpp" + +void CSVTools::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) +{ + manager.add (CSMWorld::UniversalId::Type_VerificationResults, + new CSVDoc::SubViewFactory); +} \ No newline at end of file diff --git a/apps/opencs/view/tools/subviews.hpp b/apps/opencs/view/tools/subviews.hpp new file mode 100644 index 000000000..1bac32228 --- /dev/null +++ b/apps/opencs/view/tools/subviews.hpp @@ -0,0 +1,14 @@ +#ifndef CSV_TOOLS_SUBVIEWS_H +#define CSV_TOOLS_SUBVIEWS_H + +namespace CSVDoc +{ + class SubViewFactoryManager; +} + +namespace CSVTools +{ + void addSubViewFactories (CSVDoc::SubViewFactoryManager& manager); +} + +#endif From c49966dd29c61f0f5e1bf26348e5e24a0cf25785 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 11 Dec 2012 22:49:31 +0100 Subject: [PATCH 106/378] started over --- CMakeLists.txt | 4 + apps/openmw/mwrender/renderingmanager.cpp | 2 +- apps/openmw/mwrender/videoplayer.cpp | 1079 ++++++++++++++------- apps/openmw/mwrender/videoplayer.hpp | 227 +++-- cmake/FindSDL.cmake | 177 ++++ 5 files changed, 1022 insertions(+), 467 deletions(-) create mode 100644 cmake/FindSDL.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index c11fda9f4..084363da7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,6 +163,10 @@ if (USE_MPG123) set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123) endif (USE_MPG123) +find_package (SDL REQUIRED) +set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${SDL_INCLUDE_DIR}) +set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${SDL_LIBRARY}) + # Platform specific if (WIN32) set(Boost_USE_STATIC_LIBS ON) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a1a24b7ba..5663ded09 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -928,7 +928,7 @@ void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rend void RenderingManager::playVideo(const std::string& name) { - mVideoPlayer->play ("video/" + name); + mVideoPlayer->playVideo ("video/" + name); } } // namespace diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index b1fee213f..3ddc7961c 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -1,520 +1,869 @@ #include "videoplayer.hpp" -//#ifdef OPENMW_USE_FFMPEG - -#include -#include -#include -#include - - -extern "C" -{ -#include -#include -#include -} #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" -#define MIN_QUEUED_PACKETS 30 - namespace MWRender { int OgreResource_Read(void *opaque, uint8_t *buf, int buf_size) { - Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); - int num_read = stream->size() - stream->tell(); + int num_read = stream->size() - stream->tell(); - if (num_read > buf_size) - num_read = buf_size; + if (num_read > buf_size) + num_read = buf_size; - stream->read(buf, num_read); - return num_read; + stream->read(buf, num_read); + return num_read; } int OgreResource_Write(void *opaque, uint8_t *buf, int buf_size) { - Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); - int num_write = stream->size() - stream->tell(); + int num_write = stream->size() - stream->tell(); - if (num_write > buf_size) - num_write = buf_size; + if (num_write > buf_size) + num_write = buf_size; - stream->write (buf, num_write); - return num_write; + stream->write (buf, num_write); + return num_write; } int64_t OgreResource_Seek(void *opaque, int64_t offset, int whence) { - Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); - switch (whence) - { - case SEEK_SET: - stream->seek(offset); - case SEEK_CUR: - stream->seek(stream->tell() + offset); - case SEEK_END: - stream->seek(stream->size() + offset); - case AVSEEK_SIZE: - return stream->size(); - default: - return -1; - } + switch (whence) + { + case SEEK_SET: + stream->seek(offset); + case SEEK_CUR: + stream->seek(stream->tell() + offset); + case SEEK_END: + stream->seek(stream->size() + offset); + case AVSEEK_SIZE: + return stream->size(); + default: + return -1; + } - return stream->tell(); + return stream->tell(); } - //------------------------------------------------------------------------------------------- - AVPacketQueue::AVPacketQueue(): - mFirstPacket(NULL), mLastPacket(NULL), mNumPackets(0), mSize(0) - { - } - int AVPacketQueue::put(AVPacket* pkt) - { - if(av_dup_packet(pkt) < 0) - { + /* Since we only have one decoding thread, the Big Struct + can be global in case we need it. */ + VideoState *global_video_state; + + void packet_queue_init(PacketQueue *q) { + memset(q, 0, sizeof(PacketQueue)); + } + int packet_queue_put(PacketQueue *q, AVPacket *pkt) { + AVPacketList *pkt1; + if(av_dup_packet(pkt) < 0) { return -1; } - - AVPacketList* pkt1; pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); - if (pkt1 == NULL) return -1; + if (!pkt1) + return -1; pkt1->pkt = *pkt; pkt1->next = NULL; - if (mLastPacket == NULL) mFirstPacket = pkt1; - else mLastPacket->next = pkt1; - - mLastPacket = pkt1; - mNumPackets++; - mSize += pkt1->pkt.size; + q->mutex.lock (); + if (!q->last_pkt) + q->first_pkt = pkt1; + else + q->last_pkt->next = pkt1; + q->last_pkt = pkt1; + q->nb_packets++; + q->size += pkt1->pkt.size; + q->cond.notify_one(); + q->mutex.unlock (); return 0; } + static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) { + AVPacketList *pkt1; + int ret; - int AVPacketQueue::get(AVPacket* pkt, int block) - { - AVPacketList* pkt1; + boost::unique_lock lock(q->mutex); - while (true) - { - pkt1 = mFirstPacket; - if (pkt1 != NULL) - { - mFirstPacket = pkt1->next; + for(;;) { - if (mFirstPacket == NULL) mLastPacket = NULL; + if(global_video_state->quit) { + ret = -1; + break; + } - mNumPackets--; - mSize -= pkt1->pkt.size; + pkt1 = q->first_pkt; + if (pkt1) { + q->first_pkt = pkt1->next; + if (!q->first_pkt) + q->last_pkt = NULL; + q->nb_packets--; + q->size -= pkt1->pkt.size; *pkt = pkt1->pkt; av_free(pkt1); - return 1; + ret = 1; + break; + } else if (!block) { + ret = 0; + break; + } else { + + + q->cond.wait(lock); } - else if (block == 0) - { - return 0; + } + return ret; + } + static void packet_queue_flush(PacketQueue *q) { + AVPacketList *pkt, *pkt1; + + q->mutex.lock(); + for(pkt = q->first_pkt; pkt != NULL; pkt = pkt1) { + pkt1 = pkt->next; + av_free_packet(&pkt->pkt); + av_freep(&pkt); + } + q->last_pkt = NULL; + q->first_pkt = NULL; + q->nb_packets = 0; + q->size = 0; + q->mutex.unlock (); + } + double get_audio_clock(VideoState *is) { + double pts; + int hw_buf_size, bytes_per_sec, n; + + pts = is->audio_clock; /* maintained in the audio thread */ + hw_buf_size = is->audio_buf_size - is->audio_buf_index; + bytes_per_sec = 0; + n = is->audio_st->codec->channels * 2; + if(is->audio_st) { + bytes_per_sec = is->audio_st->codec->sample_rate * n; + } + if(bytes_per_sec) { + pts -= (double)hw_buf_size / bytes_per_sec; + } + return pts; + } + double get_video_clock(VideoState *is) { + double delta; + + delta = (av_gettime() - is->video_current_pts_time) / 1000000.0; + return is->video_current_pts + delta; + } + double get_external_clock(VideoState *is) { + return av_gettime() / 1000000.0; + } + double get_master_clock(VideoState *is) { + if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) { + return get_video_clock(is); + } else if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) { + return get_audio_clock(is); + } else { + return get_external_clock(is); + } + } + /* Add or subtract samples to get a better sync, return new + audio buffer size */ + int synchronize_audio(VideoState *is, short *samples, + int samples_size, double pts) { + int n; + double ref_clock; + + n = 2 * is->audio_st->codec->channels; + + if(is->av_sync_type != AV_SYNC_AUDIO_MASTER) { + double diff, avg_diff; + int wanted_size, min_size, max_size; + // int nb_samples; + + ref_clock = get_master_clock(is); + diff = get_audio_clock(is) - ref_clock; + if(diff < AV_NOSYNC_THRESHOLD) { + // accumulate the diffs + is->audio_diff_cum = diff + is->audio_diff_avg_coef + * is->audio_diff_cum; + if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) { + is->audio_diff_avg_count++; + } else { + avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); + if(fabs(avg_diff) >= is->audio_diff_threshold) { + wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); + min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100); + max_size = samples_size * ((100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100); + if(wanted_size < min_size) { + wanted_size = min_size; + } else if (wanted_size > max_size) { + wanted_size = max_size; } - else - { - return -1; + if(wanted_size < samples_size) { + /* remove samples */ + samples_size = wanted_size; + } else if(wanted_size > samples_size) { + uint8_t *samples_end, *q; + int nb; + /* add samples by copying final sample*/ + nb = (samples_size - wanted_size); + samples_end = (uint8_t *)samples + samples_size - n; + q = samples_end + n; + while(nb > 0) { + memcpy(q, samples_end, n); + q += n; + nb -= n; + } + samples_size = wanted_size; } } + } + } else { + /* difference is TOO big; reset diff stuff */ + is->audio_diff_avg_count = 0; + is->audio_diff_cum = 0; + } + } + return samples_size; } + int audio_decode_frame(VideoState *is, uint8_t *audio_buf, int buf_size, double *pts_ptr) { + int len1, data_size, n; + AVPacket *pkt = &is->audio_pkt; + double pts; + + for(;;) { + while(is->audio_pkt_size > 0) { + data_size = buf_size; + len1 = avcodec_decode_audio3(is->audio_st->codec, + (int16_t *)audio_buf, &data_size, pkt); + + + if(len1 < 0) { + /* if error, skip frame */ + is->audio_pkt_size = 0; + break; + } + is->audio_pkt_data += len1; + is->audio_pkt_size -= len1; + if(data_size <= 0) { + /* No data yet, get more frames */ + continue; + } + pts = is->audio_clock; + *pts_ptr = pts; + n = 2 * is->audio_st->codec->channels; + is->audio_clock += (double)data_size / + (double)(n * is->audio_st->codec->sample_rate); + + /* We have data, return it and come back for more later */ + return data_size; + } + if(pkt->data) + av_free_packet(pkt); - //------------------------------------------------------------------------------------------- + if(is->quit) { + return -1; + } + /* next packet */ + if(packet_queue_get(&is->audioq, pkt, 1) < 0) { + return -1; + } + is->audio_pkt_data = pkt->data; + is->audio_pkt_size = pkt->size; + /* if update, update the audio clock w/pts */ + if(pkt->pts != AV_NOPTS_VALUE) { + is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; + } + } + } - VideoPlayer::VideoPlayer(Ogre::SceneManager *sceneMgr) - : mAvContext(NULL) - , mVideoStreamId(-1) - , mAudioStreamId(-1) - , mVideoClock(0) - , mAudioClock(0) - , mClock(0) - { - Ogre::MaterialPtr videoMaterial = Ogre::MaterialManager::getSingleton ().create("VideoMaterial", "General"); - videoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); - videoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); - videoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); - mTextureUnit = videoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(); + void audio_callback(void *userdata, Uint8 *stream, int len) { + VideoState *is = (VideoState *)userdata; + int len1, audio_size; + double pts; + + while(len > 0) { + if(is->audio_buf_index >= is->audio_buf_size) { + /* We have already sent all our data; get more */ + audio_size = audio_decode_frame(is, is->audio_buf, sizeof(is->audio_buf), &pts); + if(audio_size < 0) { + /* If error, output silence */ + is->audio_buf_size = 1024; + memset(is->audio_buf, 0, is->audio_buf_size); + } else { + audio_size = synchronize_audio(is, (int16_t *)is->audio_buf, + audio_size, pts); + is->audio_buf_size = audio_size; + } + is->audio_buf_index = 0; + } + len1 = is->audio_buf_size - is->audio_buf_index; + if(len1 > len) + len1 = len; + memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1); + len -= len1; + stream += len1; + is->audio_buf_index += len1; + } + } - mRectangle = new Ogre::Rectangle2D(true); - mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); - mRectangle->setMaterial("VideoMaterial"); - mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+1); - // Use infinite AAB to always stay visible - Ogre::AxisAlignedBox aabInf; - aabInf.setInfinite(); - mRectangle->setBoundingBox(aabInf); - // Attach background to the scene - Ogre::SceneNode* node = sceneMgr->getRootSceneNode()->createChildSceneNode(); - node->attachObject(mRectangle); - mRectangle->setVisible(false); - mRectangle->setVisibilityFlags (0x1); + /* + static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) { + SDL_Event event; + event.type = FF_REFRESH_EVENT; + event.user.data1 = opaque; + SDL_PushEvent(&event); + return 0; // 0 means stop timer } + */ - VideoPlayer::~VideoPlayer() + void timer_callback (int delay, VideoState* is) { - if (mAvContext) - deleteContext(); - - delete mRectangle; + boost::this_thread::sleep (boost::posix_time::milliseconds(delay)); + is->refresh++; } - void VideoPlayer::play (const std::string& resourceName) + /* schedule a video refresh in 'delay' ms */ + static void schedule_refresh(VideoState *is, int delay) { - mStream = Ogre::ResourceGroupManager::getSingleton ().openResource (resourceName); - - - mVideoStreamId = -1; - mAudioStreamId = -1; - mAudioStream = NULL; - mVideoStream = NULL; - mVideoClock = 0; - mAudioClock = 0; - mClock = 0; + //SDL_AddTimer(delay, sdl_refresh_timer_cb, is); + //is->refresh_queue.push_back (delay); - // if something is already playing, close it - if (mAvContext) - close(); + boost::thread (boost::bind(&timer_callback, delay, is)); + } - mRectangle->setVisible(true); + void video_display(VideoState *is) + { + VideoPicture *vp; - MWBase::Environment::get().getWindowManager ()->pushGuiMode (MWGui::GM_Video); + vp = &is->pictq[is->pictq_rindex]; + if (is->video_st->codec->width != 0 && is->video_st->codec->height != 0) + { + Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton ().getByName("VideoTexture"); + if (texture.isNull () || texture->getWidth() != is->video_st->codec->width || texture->getHeight() != is->video_st->codec->height) + { + Ogre::TextureManager::getSingleton ().remove ("VideoTexture"); + texture = Ogre::TextureManager::getSingleton().createManual( + "VideoTexture", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, + is->video_st->codec->width, is->video_st->codec->height, + 0, + Ogre::PF_BYTE_RGBA, + Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); + } + Ogre::PixelBox pb(is->video_st->codec->width, is->video_st->codec->height, 1, Ogre::PF_BYTE_RGBA, vp->data); + Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer(); + buffer->blitFromMemory(pb); + } - // BASIC INITIALIZATION + free(vp->data); + } - // Load all the decoders - av_register_all(); - AVIOContext *ioContext = 0; + void video_refresh_timer(void *userdata) { - int err = 0; + VideoState *is = (VideoState *)userdata; + VideoPicture *vp; + double actual_delay, delay, sync_threshold, ref_clock, diff; - mAvContext = avformat_alloc_context(); - if (!mAvContext) - throwError(0); + if(is->video_st) { + if(is->pictq_size == 0) { + schedule_refresh(is, 1); + } else { + vp = &is->pictq[is->pictq_rindex]; - ioContext = avio_alloc_context(NULL, 0, 0, &mStream, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if (!ioContext) - throwError(0); + is->video_current_pts = vp->pts; + is->video_current_pts_time = av_gettime(); - mAvContext->pb = ioContext; + delay = vp->pts - is->frame_last_pts; /* the pts from last time */ + if(delay <= 0 || delay >= 1.0) { + /* if incorrect delay, use previous one */ + delay = is->frame_last_delay; + } + /* save for next time */ + is->frame_last_delay = delay; + is->frame_last_pts = vp->pts; + + /* update delay to sync to audio if not master source */ + if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) { + ref_clock = get_master_clock(is); + diff = vp->pts - ref_clock; + + /* Skip or repeat the frame. Take delay into account + FFPlay still doesn't "know if this is the best guess." */ + sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; + if(fabs(diff) < AV_NOSYNC_THRESHOLD) { + if(diff <= -sync_threshold) { + delay = 0; + } else if(diff >= sync_threshold) { + delay = 2 * delay; + } + } + } - err = avformat_open_input(&mAvContext, resourceName.c_str(), NULL, NULL); - if (err != 0) - throwError(err); + is->frame_timer += delay; + /* computer the REAL delay */ + actual_delay = is->frame_timer - (av_gettime() / 1000000.0); + if(actual_delay < 0.010) { + /* Really it should skip the picture instead */ + actual_delay = 0.010; + } + schedule_refresh(is, (int)(actual_delay * 1000 + 0.5)); - err = avformat_find_stream_info(mAvContext, 0); - if (err < 0) - throwError(err); + /* show the picture! */ + video_display(is); + /* update queue for next picture! */ + if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) { + is->pictq_rindex = 0; + } + is->pictq_mutex.lock(); + is->pictq_size--; + is->pictq_cond.notify_one (); + is->pictq_mutex.unlock (); + } + } else { + schedule_refresh(is, 100); + } + } + int queue_picture(VideoState *is, AVFrame *pFrame, double pts) { + VideoPicture *vp; - // Find the video stream among the different streams - for (unsigned int i = 0; i < mAvContext->nb_streams; i++) + /* wait until we have a new pic */ { - if (mAvContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) - { - mVideoStreamId = i; - mVideoStream = mAvContext->streams[i]; - break; + boost::unique_lock lock(is->pictq_mutex); + while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && + !is->quit) { + is->pictq_cond.timed_wait(lock, boost::posix_time::milliseconds(1)); } } - if (mVideoStreamId < 0) - throw std::runtime_error("No video stream found in the video"); - - // Get the video decoder - mVideoCodec = avcodec_find_decoder(mVideoStream->codec->codec_id); - if (NULL == mVideoCodec) - throw std::runtime_error("No video decoder found"); + if(is->quit) + return -1; - // Load the video codec - err = avcodec_open2(mVideoStream->codec, mVideoCodec, 0); - if (err < 0) - throwError (err); + // windex is set to 0 initially + vp = &is->pictq[is->pictq_windex]; + + // Convert the image into YUV format that SDL uses + if(is->sws_context == NULL) { + int w = is->video_st->codec->width; + int h = is->video_st->codec->height; + is->sws_context = sws_getContext(w, h, + is->video_st->codec->pix_fmt, w, h, + PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL); + if(is->sws_context == NULL) + throw std::runtime_error("Cannot initialize the conversion context!\n"); + } + vp->data =(uint8_t*) malloc(is->video_st->codec->width * is->video_st->codec->height * 4); + sws_scale(is->sws_context, pFrame->data, pFrame->linesize, + 0, is->video_st->codec->height, &vp->data, is->rgbaFrame->linesize); - // Find the audio stream among the different streams - for (unsigned int i = 0; i < mAvContext->nb_streams; i++) - { - if (mAvContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) - { - mAudioStreamId = i; - mAudioStream = mAvContext->streams[i]; - break; - } - } - if (mAudioStreamId >= 0) - { - // Get the audio decoder - mAudioCodec = avcodec_find_decoder(mAudioStream->codec->codec_id); - if (mAudioCodec == NULL) - { - throw std::runtime_error("Stream doesn't have an audio codec"); - } + vp->pts = pts; - // Load the audio codec - err = avcodec_open2(mAudioStream->codec, mAudioCodec, 0); - if (err < 0) - throwError (err); + // now we inform our display thread that we have a pic ready + if(++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) { + is->pictq_windex = 0; } + is->pictq_mutex.lock(); + is->pictq_size++; + is->pictq_mutex.unlock(); + return 0; + } + double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) { - // Create the frame buffers - mRawFrame = avcodec_alloc_frame(); - mRGBAFrame = avcodec_alloc_frame(); - if (!mRawFrame || !mRGBAFrame) - { - throw std::runtime_error("Can't allocate video frames"); + double frame_delay; + + if(pts != 0) { + /* if we have pts, set video clock to it */ + is->video_clock = pts; + } else { + /* if we aren't given a pts, set it to the clock */ + pts = is->video_clock; } + /* update the video clock */ + frame_delay = av_q2d(is->video_st->codec->time_base); + /* if we are repeating a frame, adjust clock accordingly */ + frame_delay += src_frame->repeat_pict * (frame_delay * 0.5); + is->video_clock += frame_delay; + return pts; + } + uint64_t global_video_pkt_pts = AV_NOPTS_VALUE; + + /* These are called whenever we allocate a frame + * buffer. We use this to store the global_pts in + * a frame at the time it is allocated. + */ + int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) { + int ret = avcodec_default_get_buffer(c, pic); + uint64_t *pts = (uint64_t*)av_malloc(sizeof(uint64_t)); + *pts = global_video_pkt_pts; + pic->opaque = pts; + return ret; + } + void our_release_buffer(struct AVCodecContext *c, AVFrame *pic) { + if(pic) av_freep(&pic->opaque); + avcodec_default_release_buffer(c, pic); + } - avpicture_alloc ((AVPicture *)mRGBAFrame, PIX_FMT_RGBA, mVideoStream->codec->width, mVideoStream->codec->height); + int video_thread(void *arg) { + VideoState *is = (VideoState *)arg; + AVPacket pkt1, *packet = &pkt1; + int len1, frameFinished; + AVFrame *pFrame; + double pts; + pFrame = avcodec_alloc_frame(); - // Setup the image scaler - // All this does is convert from YUV to RGB - note it would be faster to do this in a shader, - // but i'm not worried about performance just yet - mSwsContext = sws_getContext(mVideoStream->codec->width, mVideoStream->codec->height, - mVideoStream->codec->pix_fmt, - mVideoStream->codec->width, mVideoStream->codec->height, - PIX_FMT_RGBA, - SWS_BICUBIC, NULL, NULL, NULL); - if (!mSwsContext) - throw std::runtime_error("Can't create SWS Context"); + is->rgbaFrame = avcodec_alloc_frame(); + avpicture_alloc ((AVPicture *)is->rgbaFrame, PIX_FMT_RGBA, is->video_st->codec->width, is->video_st->codec->height); - mTextureUnit->setTextureName (""); - if (!Ogre::TextureManager::getSingleton ().getByName("VideoTexture").isNull()) - Ogre::TextureManager::getSingleton().remove("VideoTexture"); + for(;;) { + if(packet_queue_get(&is->videoq, packet, 1) < 0) { + // means we quit getting packets + break; + } + pts = 0; + + // Save global pts to be stored in pFrame + global_video_pkt_pts = packet->pts; + // Decode video frame + len1 = avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, + packet); + if(packet->dts == AV_NOPTS_VALUE + && pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) { + pts = *(uint64_t *)pFrame->opaque; + } else if(packet->dts != AV_NOPTS_VALUE) { + pts = packet->dts; + } else { + pts = 0; + } + pts *= av_q2d(is->video_st->time_base); - mVideoTexture = Ogre::TextureManager::getSingleton().createManual( - "VideoTexture", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, - mVideoStream->codec->width, mVideoStream->codec->height, - 0, - Ogre::PF_BYTE_RGBA, - Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); - // initialize to (0, 0, 0, 1) - std::vector buffer; - buffer.resize(mVideoStream->codec->width * mVideoStream->codec->height); - for (int p=0; pcodec->width * mVideoStream->codec->height; ++p) - { - buffer[p] = (255 << 24); + // Did we get a video frame? + if(frameFinished) { + pts = synchronize_video(is, pFrame, pts); + if(queue_picture(is, pFrame, pts) < 0) { + break; + } + } + av_free_packet(packet); } - memcpy(mVideoTexture->getBuffer()->lock(Ogre::HardwareBuffer::HBL_DISCARD), &buffer[0], mVideoStream->codec->width*mVideoStream->codec->height*4); - mVideoTexture->getBuffer()->unlock(); - mTextureUnit->setTextureName ("VideoTexture"); + SDL_CloseAudio(); + av_free(pFrame); - // Queue up some packets - while( - mVideoPacketQueue.getNumPackets()rgbaFrame); + av_free(is->rgbaFrame); - mTimer.reset(); + return 0; } - void VideoPlayer::throwError(int error) + int stream_component_open(VideoState *is, int stream_index, AVFormatContext *pFormatCtx) { - char buffer[4096] = {0}; + AVCodecContext *codecCtx; + AVCodec *codec; + SDL_AudioSpec wanted_spec, spec; - if (0 == av_strerror(error, buffer, sizeof(buffer))) - { - std::stringstream msg; - msg << "FFMPEG error: "; - msg << buffer << std::endl; - throw std::runtime_error(msg.str()); + if(stream_index < 0 || stream_index >= pFormatCtx->nb_streams) { + return -1; } - else - throw std::runtime_error("Unknown FFMPEG error"); + + // Get a pointer to the codec context for the video stream + codecCtx = pFormatCtx->streams[stream_index]->codec; + + if(codecCtx->codec_type == AVMEDIA_TYPE_AUDIO) { + // Set audio settings from codec info + wanted_spec.freq = codecCtx->sample_rate; + wanted_spec.format = AUDIO_S16SYS; + wanted_spec.channels = codecCtx->channels; + wanted_spec.silence = 0; + wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; + wanted_spec.callback = audio_callback; + wanted_spec.userdata = is; + + if(SDL_OpenAudio(&wanted_spec, &spec) < 0) { + fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError()); + return -1; + } + is->audio_hw_buf_size = spec.size; + } + codec = avcodec_find_decoder(codecCtx->codec_id); + if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) { + fprintf(stderr, "Unsupported codec!\n"); + return -1; + } + + switch(codecCtx->codec_type) { + case AVMEDIA_TYPE_AUDIO: + is->audioStream = stream_index; + is->audio_st = pFormatCtx->streams[stream_index]; + is->audio_buf_size = 0; + is->audio_buf_index = 0; + + /* averaging filter for audio sync */ + is->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); + is->audio_diff_avg_count = 0; + /* Correct audio only if larger error than this */ + is->audio_diff_threshold = 2.0 * SDL_AUDIO_BUFFER_SIZE / codecCtx->sample_rate; + + memset(&is->audio_pkt, 0, sizeof(is->audio_pkt)); + packet_queue_init(&is->audioq); + SDL_PauseAudio(0); + break; + case AVMEDIA_TYPE_VIDEO: + is->videoStream = stream_index; + is->video_st = pFormatCtx->streams[stream_index]; + + is->frame_timer = (double)av_gettime() / 1000000.0; + is->frame_last_delay = 40e-3; + is->video_current_pts_time = av_gettime(); + + packet_queue_init(&is->videoq); + is->video_thread = boost::thread(video_thread, is); + codecCtx->get_buffer = our_get_buffer; + codecCtx->release_buffer = our_release_buffer; + + break; + default: + break; + } + + } - void VideoPlayer::update() - { - if (!mAvContext) - return; + int decode_interrupt_cb(void) { + return (global_video_state && global_video_state->quit); + } + + int decode_thread(void *arg) { + + VideoState *is = (VideoState *)arg; + AVFormatContext *pFormatCtx = avformat_alloc_context (); + AVPacket pkt1, *packet = &pkt1; + + int video_index = -1; + int audio_index = -1; + int i; + + is->videoStream=-1; + is->audioStream=-1; + is->quit = 0; + + Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton ().openResource (is->resourceName); + if(stream.isNull ()) + throw std::runtime_error("Failed to open video resource"); + + AVIOContext *ioContext = 0; + + ioContext = avio_alloc_context(NULL, 0, 0, &stream, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if (!ioContext) + throw std::runtime_error("Failed to allocate ioContext "); + + pFormatCtx->pb = ioContext; + + global_video_state = is; + // will interrupt blocking functions if we quit! + //url_set_interrupt_cb(decode_interrupt_cb); + + // Open video file + /// \todo leak here, ffmpeg or valgrind bug ? + if (avformat_open_input(&pFormatCtx, is->resourceName.c_str(), NULL, NULL)) + throw std::runtime_error("Failed to open video input"); - double dt = mTimer.getMilliseconds () / 1000.f; - mTimer.reset (); + // Retrieve stream information + if(avformat_find_stream_info(pFormatCtx, NULL)<0) + throw std::runtime_error("Failed to retrieve stream information"); - //UpdateAudio(fTime); - //std::cout << "num packets: " << mVideoPacketQueue.getNumPackets() << " clocks: " << mVideoClock << " , " << mClock << std::endl; - while (!mVideoPacketQueue.isEmpty() && mVideoClock < mClock) + // Dump information about file onto standard error + av_dump_format(pFormatCtx, 0, is->resourceName.c_str(), 0); + + for(i=0; inb_streams; i++) { + if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && + video_index < 0) { + video_index=i; + } + if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && + audio_index < 0) { + audio_index=i; + } + } + + if(audio_index >= 0) { + stream_component_open(is, audio_index, pFormatCtx); + } + if(video_index >= 0) { + stream_component_open(is, video_index, pFormatCtx); + } + + if(is->videoStream >= 0 /*|| is->audioStream < 0*/) { - while( - mVideoPacketQueue.getNumPackets()quit) { break; } + if( (is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || + is->videoq.size > MAX_VIDEOQ_SIZE) { + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + continue; + } + if(av_read_frame(pFormatCtx, packet) < 0) { + break; + } + // Is this a packet from the video stream? + if(packet->stream_index == is->videoStream) { + packet_queue_put(&is->videoq, packet); + } else if(packet->stream_index == is->audioStream) { + packet_queue_put(&is->audioq, packet); + } else { + av_free_packet(packet); + } + } + /* all done - wait for it */ + while(!is->quit) { + // EOF reached, all packets processed, we can exit now + if (is->audioq.nb_packets == 0 && is->videoq.nb_packets == 0) + break; + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); } - - if (mVideoPacketQueue.getNumPackets ()) - decodeNextVideoFrame(); } - mClock += dt; - - //curTime += fTime; + is->quit = 1; - if(mVideoPacketQueue.getNumPackets()==0 /* && mAudioPacketQueue.getNumPackets()==0 */) - close(); - } + is->audioq.cond.notify_one (); + is->videoq.cond.notify_one (); - void VideoPlayer::decodeNextVideoFrame () - { - // Make sure there is something to decode - assert (mVideoPacketQueue.getNumPackets ()); + is->video_thread.join(); - // Get the front frame and decode it - AVPacket packet; - mVideoPacketQueue.get(&packet, 1); + if (is->audioStream >= 0) + avcodec_close(is->audio_st->codec); + if (is->videoStream >= 0) + avcodec_close(is->video_st->codec); - int res; - int didDecodeFrame = 0; - res = avcodec_decode_video2(mVideoStream->codec, mRawFrame, &didDecodeFrame, &packet); + sws_freeContext (is->sws_context); - if (res < 0 || !didDecodeFrame) - throw std::runtime_error ("an error occured while decoding the video frame"); + av_close_input_file(pFormatCtx); + pFormatCtx = NULL; - // Set video clock to the PTS of this packet (presentation timestamp) - double pts = 0; - if (packet.pts != -1.0) pts = packet.pts; - pts *= av_q2d(mVideoStream->time_base); - mVideoClock = pts; + av_free(ioContext); - // Convert the frame to RGB - sws_scale(mSwsContext, - mRawFrame->data, mRawFrame->linesize, - 0, mVideoStream->codec->height, - mRGBAFrame->data, mRGBAFrame->linesize); + return 0; + } - Ogre::HardwarePixelBufferSharedPtr pixelBuffer = mVideoTexture->getBuffer(); - Ogre::PixelBox pb(mVideoStream->codec->width, mVideoStream->codec->height, 1, Ogre::PF_BYTE_RGBA, mRGBAFrame->data[0]); - pixelBuffer->blitFromMemory(pb); + VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) + : mState(NULL) + , mSceneMgr(sceneMgr) + { + mVideoMaterial = Ogre::MaterialManager::getSingleton ().create("VideoMaterial", "General"); + mVideoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); + mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); + mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(); - if (packet.data != NULL) av_free_packet(&packet); + mRectangle = new Ogre::Rectangle2D(true); + mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); + mRectangle->setMaterial("VideoMaterial"); + mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+1); + // Use infinite AAB to always stay visible + Ogre::AxisAlignedBox aabInf; + aabInf.setInfinite(); + mRectangle->setBoundingBox(aabInf); + // Attach background to the scene + Ogre::SceneNode* node = sceneMgr->getRootSceneNode()->createChildSceneNode(); + node->attachObject(mRectangle); + mRectangle->setVisible(false); + mRectangle->setVisibilityFlags (0x1); } - void VideoPlayer::close () + VideoPlayer::~VideoPlayer () { - mRectangle->setVisible (false); - MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video); - deleteContext(); + if (mState) + close(); } - void VideoPlayer::deleteContext() + void VideoPlayer::playVideo (const std::string &resourceName) { - while (mVideoPacketQueue.getNumPackets ()) - { - AVPacket packet; - mVideoPacketQueue.get(&packet, 1); - if (packet.data != NULL) av_free_packet(&packet); - } - while (mAudioPacketQueue.getNumPackets ()) - { - AVPacket packet; - mAudioPacketQueue.get(&packet, 1); - if (packet.data != NULL) av_free_packet(&packet); - } + if (mState) + close(); - if (mVideoStream && mVideoStream->codec != NULL) avcodec_close(mVideoStream->codec); - if (mAudioStream && mAudioStream->codec != NULL) avcodec_close(mAudioStream->codec); + mRectangle->setVisible(true); - avpicture_free((AVPicture *)mRGBAFrame); + MWBase::Environment::get().getWindowManager ()->pushGuiMode (MWGui::GM_Video); - if (mRawFrame) - av_free(mRawFrame); - if (mRGBAFrame) - av_free(mRGBAFrame); + mState = new VideoState; - sws_freeContext(mSwsContext); + // Register all formats and codecs + av_register_all(); - avformat_close_input(&mAvContext); + if(SDL_Init(SDL_INIT_AUDIO)) { + throw std::runtime_error("Failed to initialize SDL"); + } - mAvContext = NULL; - } + mState->refresh = 0; + mState->resourceName = resourceName; + schedule_refresh(mState, 40); + mState->av_sync_type = DEFAULT_AV_SYNC_TYPE; + mState->parse_thread = boost::thread(decode_thread, mState); + } - bool VideoPlayer::addToBuffer() + void VideoPlayer::update () { - if(mAvContext) + if (mState && mState->refresh) { - AVPacket packet; - if (av_read_frame(mAvContext, &packet) >= 0) - { - if (packet.stream_index == mVideoStreamId) - { - // I don't believe this is necessary. - /* - if(mClock==0) - { - mClock = packet.dts; - mClock *= av_q2d(mVideoStream->time_base); - std::cout << "Initializing clock to: " << mClock << std::endl; - } - */ - - mVideoPacketQueue.put(&packet); - - return true; - } - else if (packet.stream_index == mAudioStreamId && mAudioStream) - { - mAudioPacketQueue.put(&packet); - return true; - } - else - { - av_free_packet(&packet); - return false; - } - } + video_refresh_timer (mState); + mState->refresh--; + } + if (mState && mState->quit) + { + close(); } - return false; + if (!Ogre::TextureManager::getSingleton ().getByName ("VideoTexture").isNull ()) + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("VideoTexture"); + } + + void VideoPlayer::close() + { + mState->quit = 1; + + mState->parse_thread.join (); + + delete mState; + mState = NULL; + + mRectangle->setVisible (false); + MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video); } -} -//#endif + bool VideoPlayer::isPlaying () + { + return mState != NULL; + } + +} diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 466bb7902..9766ba8ee 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -1,137 +1,162 @@ -#ifndef MWRENDER_VIDEOPLAYER_H -#define MWRENDER_VIDEOPLAYER_H +#ifndef VIDEOPLAYER_H +#define VIDEOPLAYER_H -//#ifdef OPENMW_USE_FFMPEG +#include +#include +#include -#include - -#include -#include -#include - -namespace Ogre +#define __STDC_CONSTANT_MACROS +#include +extern "C" { - class Rectangle2D; - class SceneManager; - class TextureUnitState; +#include +#include +#include } -struct AVFormatContext; -struct AVCodecContext; -struct AVCodec; -struct AVStream; -struct AVFrame; -struct SwsContext; -struct AVPacket; -struct AVPacketList; +#include +#include + +#include +#include + +#define SDL_AUDIO_BUFFER_SIZE 1024 +#define MAX_AUDIOQ_SIZE (5 * 16 * 1024) +#define MAX_VIDEOQ_SIZE (5 * 256 * 1024) +#define AV_SYNC_THRESHOLD 0.01 +#define AV_NOSYNC_THRESHOLD 10.0 +#define SAMPLE_CORRECTION_PERCENT_MAX 10 +#define AUDIO_DIFF_AVG_NB 20 +#define VIDEO_PICTURE_QUEUE_SIZE 1 +#define DEFAULT_AV_SYNC_TYPE AV_SYNC_VIDEO_MASTER + + namespace MWRender { - /// A simple queue used to queue raw audio and video data. - class AVPacketQueue - { - public: - AVPacketQueue(); - int put(AVPacket* pkt); - int get(AVPacket* pkt, int block); - bool isEmpty() const { return mNumPackets == 0; } - int getNumPackets() const { return mNumPackets; } - int getSize() const { return mSize; } + struct PacketQueue { + PacketQueue () : + first_pkt(NULL), last_pkt(NULL), nb_packets(0), size(0) + {} + AVPacketList *first_pkt, *last_pkt; + int nb_packets; + int size; - private: - AVPacketList* mFirstPacket; - AVPacketList* mLastPacket; - int mNumPackets; - int mSize; + boost::mutex mutex; + boost::condition_variable cond; + }; + struct VideoPicture { + VideoPicture () : + data(NULL), pts(0) + {} + uint8_t* data; + + double pts; + }; + + static void packet_queue_flush(PacketQueue *q); + + struct VideoState { + VideoState () : + videoStream(-1), audioStream(-1), av_sync_type(0), external_clock(0), + external_clock_time(0), audio_clock(0), audio_st(NULL), audio_buf_size(0), + audio_pkt_data(NULL), audio_pkt_size(0), audio_hw_buf_size(0), audio_diff_cum(0), audio_diff_avg_coef(0), + audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), frame_last_delay(0), + video_clock(0), video_current_pts(0), video_current_pts_time(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), + pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), sws_context(NULL) + {} + + + ~VideoState() + { + packet_queue_flush (&audioq); + packet_queue_flush (&videoq); + + if (pictq_size >= 1) + free (pictq[0].data); + } + + int videoStream, audioStream; + + int av_sync_type; + double external_clock; /* external clock base */ + int64_t external_clock_time; + double audio_clock; + AVStream *audio_st; + PacketQueue audioq; + DECLARE_ALIGNED(16, uint8_t, audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2]); + unsigned int audio_buf_size; + unsigned int audio_buf_index; + AVPacket audio_pkt; + uint8_t *audio_pkt_data; + int audio_pkt_size; + int audio_hw_buf_size; + double audio_diff_cum; /* used for AV difference average computation */ + double audio_diff_avg_coef; + double audio_diff_threshold; + int audio_diff_avg_count; + double frame_timer; + double frame_last_pts; + double frame_last_delay; + double video_clock; ///. This is done for portability +# reasons because not all systems place things in SDL/ (see FreeBSD). + +#============================================================================= +# Copyright 2003-2009 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +FIND_PATH(SDL_INCLUDE_DIR SDL.h + HINTS + $ENV{SDLDIR} + PATH_SUFFIXES include/SDL include + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local/include/SDL12 + /usr/local/include/SDL11 # FreeBSD ports + /usr/include/SDL12 + /usr/include/SDL11 + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) +#MESSAGE("SDL_INCLUDE_DIR is ${SDL_INCLUDE_DIR}") + +# SDL-1.1 is the name used by FreeBSD ports... +# don't confuse it for the version number. +FIND_LIBRARY(SDL_LIBRARY_TEMP + NAMES SDL SDL-1.1 + HINTS + $ENV{SDLDIR} + PATH_SUFFIXES lib64 lib + PATHS + /sw + /opt/local + /opt/csw + /opt +) + +#MESSAGE("SDL_LIBRARY_TEMP is ${SDL_LIBRARY_TEMP}") + +IF(NOT SDL_BUILDING_LIBRARY) + IF(NOT ${SDL_INCLUDE_DIR} MATCHES ".framework") + # Non-OS X framework versions expect you to also dynamically link to + # SDLmain. This is mainly for Windows and OS X. Other (Unix) platforms + # seem to provide SDLmain for compatibility even though they don't + # necessarily need it. + FIND_LIBRARY(SDLMAIN_LIBRARY + NAMES SDLmain SDLmain-1.1 + HINTS + $ENV{SDLDIR} + PATH_SUFFIXES lib64 lib + PATHS + /sw + /opt/local + /opt/csw + /opt + ) + ENDIF(NOT ${SDL_INCLUDE_DIR} MATCHES ".framework") +ENDIF(NOT SDL_BUILDING_LIBRARY) + +# SDL may require threads on your system. +# The Apple build may not need an explicit flag because one of the +# frameworks may already provide it. +# But for non-OSX systems, I will use the CMake Threads package. +IF(NOT APPLE) + FIND_PACKAGE(Threads) +ENDIF(NOT APPLE) + +# MinGW needs an additional library, mwindows +# It's total link flags should look like -lmingw32 -lSDLmain -lSDL -lmwindows +# (Actually on second look, I think it only needs one of the m* libraries.) +IF(MINGW) + SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") +ENDIF(MINGW) + +SET(SDL_FOUND "NO") +IF(SDL_LIBRARY_TEMP) + # For SDLmain + IF(NOT SDL_BUILDING_LIBRARY) + IF(SDLMAIN_LIBRARY) + SET(SDL_LIBRARY_TEMP ${SDLMAIN_LIBRARY} ${SDL_LIBRARY_TEMP}) + ENDIF(SDLMAIN_LIBRARY) + ENDIF(NOT SDL_BUILDING_LIBRARY) + + # For OS X, SDL uses Cocoa as a backend so it must link to Cocoa. + # CMake doesn't display the -framework Cocoa string in the UI even + # though it actually is there if I modify a pre-used variable. + # I think it has something to do with the CACHE STRING. + # So I use a temporary variable until the end so I can set the + # "real" variable in one-shot. + IF(APPLE) + SET(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} "-framework Cocoa") + ENDIF(APPLE) + + # For threads, as mentioned Apple doesn't need this. + # In fact, there seems to be a problem if I used the Threads package + # and try using this line, so I'm just skipping it entirely for OS X. + IF(NOT APPLE) + SET(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) + ENDIF(NOT APPLE) + + # For MinGW library + IF(MINGW) + SET(SDL_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL_LIBRARY_TEMP}) + ENDIF(MINGW) + + # Set the final string here so the GUI reflects the final state. + SET(SDL_LIBRARY ${SDL_LIBRARY_TEMP} CACHE STRING "Where the SDL Library can be found") + # Set the temp variable to INTERNAL so it is not seen in the CMake GUI + SET(SDL_LIBRARY_TEMP "${SDL_LIBRARY_TEMP}" CACHE INTERNAL "") + + SET(SDL_FOUND "YES") +ENDIF(SDL_LIBRARY_TEMP) + +#MESSAGE("SDL_LIBRARY is ${SDL_LIBRARY}") + From 0ce5ade6d816cb4eaf9867bfb596e316d8a9806a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 11 Dec 2012 23:06:06 +0100 Subject: [PATCH 107/378] DataStreamPtr fix, indentation fixes --- apps/openmw/mwrender/videoplayer.cpp | 114 ++++++++++++++------------- apps/openmw/mwrender/videoplayer.hpp | 2 + 2 files changed, 60 insertions(+), 56 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 3ddc7961c..40d1a7188 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -11,49 +11,49 @@ namespace MWRender int OgreResource_Read(void *opaque, uint8_t *buf, int buf_size) { - Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + Ogre::DataStreamPtr stream = static_cast(opaque)->stream; - int num_read = stream->size() - stream->tell(); + int num_read = stream->size() - stream->tell(); - if (num_read > buf_size) - num_read = buf_size; + if (num_read > buf_size) + num_read = buf_size; - stream->read(buf, num_read); - return num_read; + stream->read(buf, num_read); + return num_read; } int OgreResource_Write(void *opaque, uint8_t *buf, int buf_size) { - Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + Ogre::DataStreamPtr stream = static_cast(opaque)->stream; - int num_write = stream->size() - stream->tell(); + int num_write = stream->size() - stream->tell(); - if (num_write > buf_size) - num_write = buf_size; + if (num_write > buf_size) + num_write = buf_size; - stream->write (buf, num_write); - return num_write; + stream->write (buf, num_write); + return num_write; } int64_t OgreResource_Seek(void *opaque, int64_t offset, int whence) { - Ogre::DataStreamPtr stream = *((Ogre::DataStreamPtr*)opaque); + Ogre::DataStreamPtr stream = static_cast(opaque)->stream; - switch (whence) - { + switch (whence) + { case SEEK_SET: - stream->seek(offset); + stream->seek(offset); case SEEK_CUR: - stream->seek(stream->tell() + offset); + stream->seek(stream->tell() + offset); case SEEK_END: - stream->seek(stream->size() + offset); + stream->seek(stream->size() + offset); case AVSEEK_SIZE: - return stream->size(); + return stream->size(); default: - return -1; - } + return -1; + } - return stream->tell(); + return stream->tell(); } @@ -107,7 +107,7 @@ namespace MWRender if (pkt1) { q->first_pkt = pkt1->next; if (!q->first_pkt) - q->last_pkt = NULL; + q->last_pkt = NULL; q->nb_packets--; q->size -= pkt1->pkt.size; *pkt = pkt1->pkt; @@ -247,21 +247,21 @@ namespace MWRender if(len1 < 0) { - /* if error, skip frame */ - is->audio_pkt_size = 0; - break; + /* if error, skip frame */ + is->audio_pkt_size = 0; + break; } is->audio_pkt_data += len1; is->audio_pkt_size -= len1; if(data_size <= 0) { /* No data yet, get more frames */ - continue; + continue; } pts = is->audio_clock; *pts_ptr = pts; n = 2 * is->audio_st->codec->channels; is->audio_clock += (double)data_size / - (double)(n * is->audio_st->codec->sample_rate); + (double)(n * is->audio_st->codec->sample_rate); /* We have data, return it and come back for more later */ return data_size; @@ -295,13 +295,13 @@ namespace MWRender /* We have already sent all our data; get more */ audio_size = audio_decode_frame(is, is->audio_buf, sizeof(is->audio_buf), &pts); if(audio_size < 0) { - /* If error, output silence */ - is->audio_buf_size = 1024; - memset(is->audio_buf, 0, is->audio_buf_size); + /* If error, output silence */ + is->audio_buf_size = 1024; + memset(is->audio_buf, 0, is->audio_buf_size); } else { - audio_size = synchronize_audio(is, (int16_t *)is->audio_buf, - audio_size, pts); - is->audio_buf_size = audio_size; + audio_size = synchronize_audio(is, (int16_t *)is->audio_buf, + audio_size, pts); + is->audio_buf_size = audio_size; } is->audio_buf_index = 0; } @@ -387,8 +387,8 @@ namespace MWRender delay = vp->pts - is->frame_last_pts; /* the pts from last time */ if(delay <= 0 || delay >= 1.0) { - /* if incorrect delay, use previous one */ - delay = is->frame_last_delay; + /* if incorrect delay, use previous one */ + delay = is->frame_last_delay; } /* save for next time */ is->frame_last_delay = delay; @@ -396,27 +396,27 @@ namespace MWRender /* update delay to sync to audio if not master source */ if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) { - ref_clock = get_master_clock(is); - diff = vp->pts - ref_clock; - - /* Skip or repeat the frame. Take delay into account - FFPlay still doesn't "know if this is the best guess." */ - sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; - if(fabs(diff) < AV_NOSYNC_THRESHOLD) { - if(diff <= -sync_threshold) { - delay = 0; - } else if(diff >= sync_threshold) { - delay = 2 * delay; - } - } + ref_clock = get_master_clock(is); + diff = vp->pts - ref_clock; + + /* Skip or repeat the frame. Take delay into account + FFPlay still doesn't "know if this is the best guess." */ + sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; + if(fabs(diff) < AV_NOSYNC_THRESHOLD) { + if(diff <= -sync_threshold) { + delay = 0; + } else if(diff >= sync_threshold) { + delay = 2 * delay; + } + } } is->frame_timer += delay; /* computer the REAL delay */ actual_delay = is->frame_timer - (av_gettime() / 1000000.0); if(actual_delay < 0.010) { - /* Really it should skip the picture instead */ - actual_delay = 0.010; + /* Really it should skip the picture instead */ + actual_delay = 0.010; } schedule_refresh(is, (int)(actual_delay * 1000 + 0.5)); @@ -425,14 +425,15 @@ namespace MWRender /* update queue for next picture! */ if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) { - is->pictq_rindex = 0; + is->pictq_rindex = 0; } is->pictq_mutex.lock(); is->pictq_size--; is->pictq_cond.notify_one (); is->pictq_mutex.unlock (); } - } else { + } + else { schedule_refresh(is, 100); } } @@ -563,7 +564,7 @@ namespace MWRender if(frameFinished) { pts = synchronize_video(is, pFrame, pts); if(queue_picture(is, pFrame, pts) < 0) { - break; + break; } } av_free_packet(packet); @@ -673,10 +674,11 @@ namespace MWRender Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton ().openResource (is->resourceName); if(stream.isNull ()) throw std::runtime_error("Failed to open video resource"); + is->stream = stream; AVIOContext *ioContext = 0; - ioContext = avio_alloc_context(NULL, 0, 0, &stream, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + ioContext = avio_alloc_context(NULL, 0, 0, is, OgreResource_Read, OgreResource_Write, OgreResource_Seek); if (!ioContext) throw std::runtime_error("Failed to allocate ioContext "); @@ -845,7 +847,7 @@ namespace MWRender } if (!Ogre::TextureManager::getSingleton ().getByName ("VideoTexture").isNull ()) - mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("VideoTexture"); + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("VideoTexture"); } void VideoPlayer::close() diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 9766ba8ee..67a22a506 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -108,6 +108,8 @@ namespace MWRender AVStream *video_st; PacketQueue videoq; + Ogre::DataStreamPtr stream; + SwsContext* sws_context; VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE]; From faad64b2545608a8781800ebfcf3ab265411c944 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 12 Dec 2012 01:13:53 +0100 Subject: [PATCH 108/378] Esc cancels the video --- apps/openmw/mwbase/world.hpp | 1 + apps/openmw/mwinput/inputmanagerimp.cpp | 2 ++ apps/openmw/mwrender/renderingmanager.cpp | 5 +++++ apps/openmw/mwrender/renderingmanager.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 5 +++++ apps/openmw/mwworld/worldimp.hpp | 1 + 6 files changed, 15 insertions(+) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 526f865bc..198a20d45 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -306,6 +306,7 @@ namespace MWBase /// \todo this does not belong here virtual void playVideo(const std::string& name) = 0; + virtual void stopVideo() = 0; }; } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 6a1c3aa9b..f7cc0f88e 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -508,6 +508,8 @@ namespace MWInput { if (mWindows.isGuiMode () && (mWindows.getMode () == MWGui::GM_MainMenu || mWindows.getMode () == MWGui::GM_Settings)) mWindows.popGuiMode(); + else if (mWindows.isGuiMode () && mWindows.getMode () == MWGui::GM_Video) + MWBase::Environment::get().getWorld ()->stopVideo (); else mWindows.pushGuiMode (MWGui::GM_MainMenu); } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 5663ded09..811d000a9 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -931,4 +931,9 @@ void RenderingManager::playVideo(const std::string& name) mVideoPlayer->playVideo ("video/" + name); } +void RenderingManager::stopVideo() +{ + mVideoPlayer->close (); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index ee64a371e..02b4bcef6 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -197,6 +197,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void setupExternalRendering (MWRender::ExternalRendering& rendering); void playVideo(const std::string& name); + void stopVideo(); protected: virtual void windowResized(Ogre::RenderWindow* rw); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2257b8a07..8eb121d37 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1291,4 +1291,9 @@ namespace MWWorld { mRendering->playVideo(name); } + + void World::stopVideo () + { + mRendering->stopVideo(); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 349fd163c..1c1352913 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -335,6 +335,7 @@ namespace MWWorld /// \todo this does not belong here virtual void playVideo(const std::string& name); + virtual void stopVideo(); }; } From fe384a1600b1bf057faff7d7d4f1fa5a1bc58ae1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 12 Dec 2012 01:30:34 +0100 Subject: [PATCH 109/378] pause 3d rendering while the video plays --- apps/openmw/mwrender/videoplayer.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 40d1a7188..46fa4b9e9 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -816,6 +816,17 @@ namespace MWRender MWBase::Environment::get().getWindowManager ()->pushGuiMode (MWGui::GM_Video); + // Turn off rendering except the GUI + mSceneMgr->clearSpecialCaseRenderQueues(); + // SCRQM_INCLUDE with RENDER_QUEUE_OVERLAY does not work. + for (int i = 0; i < Ogre::RENDER_QUEUE_MAX; ++i) + { + if (i > 0 && i < 96) + mSceneMgr->addSpecialCaseRenderQueue(i); + } + mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); + + mState = new VideoState; // Register all formats and codecs @@ -861,6 +872,9 @@ namespace MWRender mRectangle->setVisible (false); MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video); + + mSceneMgr->clearSpecialCaseRenderQueues(); + mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); } bool VideoPlayer::isPlaying () From 9e2d4f8b7cc4751552c8f4d9a31dbf7ed04c083c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 11 Dec 2012 19:32:10 -0800 Subject: [PATCH 110/378] Avoid potential NULL dereference --- apps/openmw/mwrender/videoplayer.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 46fa4b9e9..612b37c0a 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -142,16 +142,12 @@ namespace MWRender } double get_audio_clock(VideoState *is) { double pts; - int hw_buf_size, bytes_per_sec, n; pts = is->audio_clock; /* maintained in the audio thread */ - hw_buf_size = is->audio_buf_size - is->audio_buf_index; - bytes_per_sec = 0; - n = is->audio_st->codec->channels * 2; if(is->audio_st) { - bytes_per_sec = is->audio_st->codec->sample_rate * n; - } - if(bytes_per_sec) { + int n = is->audio_st->codec->channels * 2; + int bytes_per_sec = is->audio_st->codec->sample_rate * n; + int hw_buf_size = is->audio_buf_size - is->audio_buf_index; pts -= (double)hw_buf_size / bytes_per_sec; } return pts; From 3519934f273a2ddbe81e284c811cd577cf68e1fd Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 11 Dec 2012 19:36:04 -0800 Subject: [PATCH 111/378] Add a missing return value --- apps/openmw/mwrender/videoplayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 612b37c0a..b65458b63 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -646,7 +646,7 @@ namespace MWRender break; } - + return 0; } int decode_interrupt_cb(void) { From 277248cdcbf1caa3673af3ee45957690aec98579 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 11 Dec 2012 19:43:07 -0800 Subject: [PATCH 112/378] Fix some "comparison between signed and unsigned" warnings --- apps/openmw/mwrender/videoplayer.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index b65458b63..7e7fcd0d6 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -275,7 +275,7 @@ namespace MWRender is->audio_pkt_data = pkt->data; is->audio_pkt_size = pkt->size; /* if update, update the audio clock w/pts */ - if(pkt->pts != AV_NOPTS_VALUE) { + if(pkt->pts != (int64_t)AV_NOPTS_VALUE) { is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; } } @@ -345,7 +345,8 @@ namespace MWRender if (is->video_st->codec->width != 0 && is->video_st->codec->height != 0) { Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton ().getByName("VideoTexture"); - if (texture.isNull () || texture->getWidth() != is->video_st->codec->width || texture->getHeight() != is->video_st->codec->height) + if (texture.isNull () || texture->getWidth() != (size_t)is->video_st->codec->width || + texture->getHeight() != (size_t)is->video_st->codec->height) { Ogre::TextureManager::getSingleton ().remove ("VideoTexture"); texture = Ogre::TextureManager::getSingleton().createManual( @@ -545,10 +546,10 @@ namespace MWRender // Decode video frame len1 = avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, packet); - if(packet->dts == AV_NOPTS_VALUE + if(packet->dts == (int64_t)AV_NOPTS_VALUE && pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) { pts = *(uint64_t *)pFrame->opaque; - } else if(packet->dts != AV_NOPTS_VALUE) { + } else if(packet->dts != (int64_t)AV_NOPTS_VALUE) { pts = packet->dts; } else { pts = 0; @@ -582,7 +583,7 @@ namespace MWRender AVCodec *codec; SDL_AudioSpec wanted_spec, spec; - if(stream_index < 0 || stream_index >= pFormatCtx->nb_streams) { + if(stream_index < 0 || (unsigned int)stream_index >= pFormatCtx->nb_streams) { return -1; } @@ -661,7 +662,7 @@ namespace MWRender int video_index = -1; int audio_index = -1; - int i; + unsigned int i; is->videoStream=-1; is->audioStream=-1; From 2efdafecd9d7c6b5fb0d044cf67cefccb5db461b Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 11 Dec 2012 20:11:48 -0800 Subject: [PATCH 113/378] Indentation fixes --- apps/openmw/mwrender/videoplayer.cpp | 98 +++++++++++++--------------- 1 file changed, 46 insertions(+), 52 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 7e7fcd0d6..c09de3acc 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -188,39 +188,39 @@ namespace MWRender diff = get_audio_clock(is) - ref_clock; if(diff < AV_NOSYNC_THRESHOLD) { // accumulate the diffs - is->audio_diff_cum = diff + is->audio_diff_avg_coef - * is->audio_diff_cum; + is->audio_diff_cum = diff + is->audio_diff_avg_coef * + is->audio_diff_cum; if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) { - is->audio_diff_avg_count++; + is->audio_diff_avg_count++; } else { - avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); - if(fabs(avg_diff) >= is->audio_diff_threshold) { - wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); - min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100); - max_size = samples_size * ((100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100); - if(wanted_size < min_size) { - wanted_size = min_size; - } else if (wanted_size > max_size) { - wanted_size = max_size; - } - if(wanted_size < samples_size) { - /* remove samples */ - samples_size = wanted_size; - } else if(wanted_size > samples_size) { - uint8_t *samples_end, *q; - int nb; - /* add samples by copying final sample*/ - nb = (samples_size - wanted_size); - samples_end = (uint8_t *)samples + samples_size - n; - q = samples_end + n; - while(nb > 0) { - memcpy(q, samples_end, n); - q += n; - nb -= n; - } - samples_size = wanted_size; - } - } + avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); + if(fabs(avg_diff) >= is->audio_diff_threshold) { + wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); + min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100); + max_size = samples_size * ((100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100); + if(wanted_size < min_size) { + wanted_size = min_size; + } else if (wanted_size > max_size) { + wanted_size = max_size; + } + if(wanted_size < samples_size) { + /* remove samples */ + samples_size = wanted_size; + } else if(wanted_size > samples_size) { + uint8_t *samples_end, *q; + int nb; + /* add samples by copying final sample*/ + nb = (samples_size - wanted_size); + samples_end = (uint8_t *)samples + samples_size - n; + q = samples_end + n; + while(nb > 0) { + memcpy(q, samples_end, n); + q += n; + nb -= n; + } + samples_size = wanted_size; + } + } } } else { /* difference is TOO big; reset diff stuff */ @@ -239,7 +239,7 @@ namespace MWRender while(is->audio_pkt_size > 0) { data_size = buf_size; len1 = avcodec_decode_audio3(is->audio_st->codec, - (int16_t *)audio_buf, &data_size, pkt); + (int16_t*)audio_buf, &data_size, pkt); if(len1 < 0) { @@ -250,14 +250,14 @@ namespace MWRender is->audio_pkt_data += len1; is->audio_pkt_size -= len1; if(data_size <= 0) { - /* No data yet, get more frames */ + /* No data yet, get more frames */ continue; } pts = is->audio_clock; *pts_ptr = pts; n = 2 * is->audio_st->codec->channels; is->audio_clock += (double)data_size / - (double)(n * is->audio_st->codec->sample_rate); + (double)(n * is->audio_st->codec->sample_rate); /* We have data, return it and come back for more later */ return data_size; @@ -296,7 +296,7 @@ namespace MWRender memset(is->audio_buf, 0, is->audio_buf_size); } else { audio_size = synchronize_audio(is, (int16_t *)is->audio_buf, - audio_size, pts); + audio_size, pts); is->audio_buf_size = audio_size; } is->audio_buf_index = 0; @@ -397,7 +397,7 @@ namespace MWRender diff = vp->pts - ref_clock; /* Skip or repeat the frame. Take delay into account - FFPlay still doesn't "know if this is the best guess." */ + FFPlay still doesn't "know if this is the best guess." */ sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; if(fabs(diff) < AV_NOSYNC_THRESHOLD) { if(diff <= -sync_threshold) { @@ -442,8 +442,7 @@ namespace MWRender /* wait until we have a new pic */ { boost::unique_lock lock(is->pictq_mutex); - while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && - !is->quit) { + while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is->quit) { is->pictq_cond.timed_wait(lock, boost::posix_time::milliseconds(1)); } } @@ -458,9 +457,9 @@ namespace MWRender if(is->sws_context == NULL) { int w = is->video_st->codec->width; int h = is->video_st->codec->height; - is->sws_context = sws_getContext(w, h, - is->video_st->codec->pix_fmt, w, h, - PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL); + is->sws_context = sws_getContext(w, h, is->video_st->codec->pix_fmt, + w, h, PIX_FMT_RGBA, SWS_BICUBIC, + NULL, NULL, NULL); if(is->sws_context == NULL) throw std::runtime_error("Cannot initialize the conversion context!\n"); } @@ -468,7 +467,7 @@ namespace MWRender vp->data =(uint8_t*) malloc(is->video_st->codec->width * is->video_st->codec->height * 4); sws_scale(is->sws_context, pFrame->data, pFrame->linesize, - 0, is->video_st->codec->height, &vp->data, is->rgbaFrame->linesize); + 0, is->video_st->codec->height, &vp->data, is->rgbaFrame->linesize); vp->pts = pts; @@ -544,8 +543,7 @@ namespace MWRender // Save global pts to be stored in pFrame global_video_pkt_pts = packet->pts; // Decode video frame - len1 = avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, - packet); + len1 = avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, packet); if(packet->dts == (int64_t)AV_NOPTS_VALUE && pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) { pts = *(uint64_t *)pFrame->opaque; @@ -698,12 +696,10 @@ namespace MWRender av_dump_format(pFormatCtx, 0, is->resourceName.c_str(), 0); for(i=0; inb_streams; i++) { - if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && - video_index < 0) { + if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && video_index < 0) { video_index=i; } - if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && - audio_index < 0) { + if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && audio_index < 0) { audio_index=i; } } @@ -717,15 +713,13 @@ namespace MWRender if(is->videoStream >= 0 /*|| is->audioStream < 0*/) { - // main decode loop - for(;;) { if(is->quit) { break; } - if( (is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || - is->videoq.size > MAX_VIDEOQ_SIZE) { + if((is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || + is->videoq.size > MAX_VIDEOQ_SIZE) { boost::this_thread::sleep(boost::posix_time::milliseconds(10)); continue; } From c2e1595445b73dc349b7f05b300ee5ac1b7a9c82 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 11 Dec 2012 21:01:32 -0800 Subject: [PATCH 114/378] Treat paused sounds as still playing --- apps/openmw/mwsound/openal_output.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index e5169d878..1e862d77a 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -254,7 +254,7 @@ bool OpenAL_SoundStream::isPlaying() alGetSourcei(mSource, AL_SOURCE_STATE, &state); throwALerror(); - if(state == AL_PLAYING) + if(state == AL_PLAYING || state == AL_PAUSED) return true; return !mIsFinished; } @@ -393,7 +393,7 @@ bool OpenAL_Sound::isPlaying() alGetSourcei(mSource, AL_SOURCE_STATE, &state); throwALerror(); - return state==AL_PLAYING; + return state==AL_PLAYING || state==AL_PAUSED; } void OpenAL_Sound::update() From a62d5bbfe41f2041900df3cfce46b4b7f20515e9 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 11 Dec 2012 23:54:41 -0800 Subject: [PATCH 115/378] Sleep using the absolute time, so the thread creation doesn't add to the wait --- apps/openmw/mwrender/videoplayer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index c09de3acc..afdb5e82d 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -321,9 +321,9 @@ namespace MWRender } */ - void timer_callback (int delay, VideoState* is) + void timer_callback (boost::system_time t, VideoState* is) { - boost::this_thread::sleep (boost::posix_time::milliseconds(delay)); + boost::this_thread::sleep (t); is->refresh++; } @@ -332,8 +332,8 @@ namespace MWRender { //SDL_AddTimer(delay, sdl_refresh_timer_cb, is); //is->refresh_queue.push_back (delay); - - boost::thread (boost::bind(&timer_callback, delay, is)); + boost::system_time t = boost::get_system_time() + boost::posix_time::milliseconds(delay); + boost::thread (boost::bind(&timer_callback, t, is)); } void video_display(VideoState *is) From e4ac7a4df4d030e9dcdb6cd6a9352fa8b839a1a6 Mon Sep 17 00:00:00 2001 From: psi29a Date: Wed, 12 Dec 2012 09:27:15 +0100 Subject: [PATCH 116/378] remove libboost1-xxx from deb depends since we can compile statically --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0abaf3403..68c3204fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -361,7 +361,7 @@ if(DPKG_PROGRAM) SET(CPACK_DEBIAN_PACKAGE_NAME "openmw") SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}") SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter") - SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-filesystem1.46.1 (>= 1.46.1), libboost-program-options1.46.1 (>= 1.46.1), libboost-system1.46.1 (>= 1.46.1), libboost-thread1.46.1 (>= 1.46.1), libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libois-1.3.0 (>= 1.3.0), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)") + SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libois-1.3.0 (>= 1.3.0), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)") SET(CPACK_DEBIAN_PACKAGE_SECTION "Games") From e82c4afd500aa808a81da700d433b577020a837f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Dec 2012 00:36:52 -0800 Subject: [PATCH 117/378] close SDL when closing the video, not after the video loop is finished --- apps/openmw/mwrender/videoplayer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index afdb5e82d..8ae51deba 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -565,8 +565,6 @@ namespace MWRender av_free_packet(packet); } - SDL_CloseAudio(); - av_free(pFrame); avpicture_free((AVPicture *)is->rgbaFrame); @@ -861,6 +859,8 @@ namespace MWRender delete mState; mState = NULL; + SDL_CloseAudio(); + mRectangle->setVisible (false); MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video); From 973b5faf25405e2e6ac94c5682844b6169e976de Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Dec 2012 01:32:16 -0800 Subject: [PATCH 118/378] Keep track of all allocated sources --- apps/openmw/mwsound/openal_output.cpp | 12 ++++++------ apps/openmw/mwsound/openal_output.hpp | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 1e862d77a..abd63590f 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -504,8 +504,9 @@ void OpenAL_Output::init(const std::string &devname) ALuint src = 0; alGenSources(1, &src); throwALerror(); - mFreeSources.push_back(src); + mSources.push_back(src); } + mFreeSources.insert(mFreeSources.begin(), mSources.begin(), mSources.end()); } catch(std::exception &e) { @@ -521,11 +522,10 @@ void OpenAL_Output::deinit() { mStreamThread->removeAll(); - while(!mFreeSources.empty()) - { - alDeleteSources(1, &mFreeSources.front()); - mFreeSources.pop_front(); - } + mFreeSources.clear(); + if(mSources.size() > 0) + alDeleteSources(mSources.size(), &mSources[0]); + mSources.clear(); mBufferRefs.clear(); mUnusedBuffers.clear(); diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index fecffa575..4177c6385 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -21,6 +21,9 @@ namespace MWSound ALCdevice *mDevice; ALCcontext *mContext; + typedef std::vector IDVec; + IDVec mSources; + typedef std::deque IDDq; IDDq mFreeSources; IDDq mUnusedBuffers; From 2c1eceb9f093c209240a47be839acb6d086dae07 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Dec 2012 02:33:12 -0800 Subject: [PATCH 119/378] Add methods to pause and stop all playing sounds (and music) --- apps/openmw/mwbase/soundmanager.hpp | 6 ++++++ apps/openmw/mwrender/videoplayer.cpp | 3 +++ apps/openmw/mwsound/openal_output.cpp | 27 +++++++++++++++++++++++++ apps/openmw/mwsound/openal_output.hpp | 3 +++ apps/openmw/mwsound/sound_output.hpp | 3 +++ apps/openmw/mwsound/soundmanagerimp.cpp | 13 ++++++++++++ apps/openmw/mwsound/soundmanagerimp.hpp | 6 ++++++ 7 files changed, 61 insertions(+) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 92c177ff3..2fa98cfee 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -112,6 +112,12 @@ namespace MWBase virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const = 0; ///< Is the given sound currently playing on the given object? + virtual void pauseAllSounds() = 0; + ///< Pauses all currently playing sounds, including music. + + virtual void resumeAllSounds() = 0; + ///< Resumes all previously paused sounds. + virtual void update(float duration) = 0; virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) = 0; diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 8ae51deba..1334c1bca 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -3,6 +3,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/soundmanager.hpp" @@ -821,6 +822,7 @@ namespace MWRender // Register all formats and codecs av_register_all(); + MWBase::Environment::get().getSoundManager()->pauseAllSounds(); if(SDL_Init(SDL_INIT_AUDIO)) { throw std::runtime_error("Failed to initialize SDL"); } @@ -860,6 +862,7 @@ namespace MWRender mState = NULL; SDL_CloseAudio(); + MWBase::Environment::get().getSoundManager()->resumeAllSounds(); mRectangle->setVisible (false); MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video); diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index abd63590f..bc6102beb 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -814,6 +814,33 @@ void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 } +void OpenAL_Output::pauseAllSounds() +{ + IDVec sources = mSources; + IDDq::const_iterator iter = mFreeSources.begin(); + while(iter != mFreeSources.end()) + { + sources.erase(std::find(sources.begin(), sources.end(), *iter)); + iter++; + } + if(sources.size() > 0) + alSourcePausev(sources.size(), &sources[0]); +} + +void OpenAL_Output::resumeAllSounds() +{ + IDVec sources = mSources; + IDDq::const_iterator iter = mFreeSources.begin(); + while(iter != mFreeSources.end()) + { + sources.erase(std::find(sources.begin(), sources.end(), *iter)); + iter++; + } + if(sources.size() > 0) + alSourcePlayv(sources.size(), &sources[0]); +} + + OpenAL_Output::OpenAL_Output(SoundManager &mgr) : Sound_Output(mgr), mDevice(0), mContext(0), mBufferCacheMemSize(0), mLastEnvironment(Env_Normal), mStreamThread(new StreamThread) diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index 4177c6385..867150741 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -52,6 +52,9 @@ namespace MWSound virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env); + virtual void pauseAllSounds(); + virtual void resumeAllSounds(); + OpenAL_Output& operator=(const OpenAL_Output &rhs); OpenAL_Output(const OpenAL_Output &rhs); diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index 2680ec1db..1cc45559b 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -31,6 +31,9 @@ namespace MWSound virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env) = 0; + virtual void pauseAllSounds() = 0; + virtual void resumeAllSounds() = 0; + Sound_Output& operator=(const Sound_Output &rhs); Sound_Output(const Sound_Output &rhs); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 8c4798c9d..1414dbb76 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -404,6 +404,19 @@ namespace MWSound } + void SoundManager::pauseAllSounds() + { + if(mOutput->isInitialized()) + mOutput->pauseAllSounds(); + } + + void SoundManager::resumeAllSounds() + { + if(mOutput->isInitialized()) + mOutput->resumeAllSounds(); + } + + void SoundManager::updateRegionSound(float duration) { MWWorld::Ptr::CellStore *current = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index a84aa3b9a..aaa362d84 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -127,6 +127,12 @@ namespace MWSound virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? + virtual void pauseAllSounds(); + ///< Pauses all currently playing sounds, including music. + + virtual void resumeAllSounds(); + ///< Resumes all previously paused sounds. + virtual void update(float duration); virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up); From 18d8c767bd8c521b2f06eecadd762de00b516742 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 12 Dec 2012 15:15:55 +0100 Subject: [PATCH 120/378] fix a bunch of warnings, improved error handling, initialize texture to black --- apps/openmw/mwrender/videoplayer.cpp | 232 ++++++++++++++----------- apps/openmw/mwrender/videoplayer.hpp | 11 +- apps/openmw/mwsound/ffmpeg_decoder.cpp | 6 +- 3 files changed, 136 insertions(+), 113 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 46fa4b9e9..07fa723e2 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -125,21 +125,23 @@ namespace MWRender } return ret; } - static void packet_queue_flush(PacketQueue *q) { + + void PacketQueue::flush() { AVPacketList *pkt, *pkt1; - q->mutex.lock(); - for(pkt = q->first_pkt; pkt != NULL; pkt = pkt1) { + this->mutex.lock(); + for(pkt = this->first_pkt; pkt != NULL; pkt = pkt1) { pkt1 = pkt->next; av_free_packet(&pkt->pkt); av_freep(&pkt); } - q->last_pkt = NULL; - q->first_pkt = NULL; - q->nb_packets = 0; - q->size = 0; - q->mutex.unlock (); + this->last_pkt = NULL; + this->first_pkt = NULL; + this->nb_packets = 0; + this->size = 0; + this->mutex.unlock (); } + double get_audio_clock(VideoState *is) { double pts; int hw_buf_size, bytes_per_sec, n; @@ -279,7 +281,7 @@ namespace MWRender is->audio_pkt_data = pkt->data; is->audio_pkt_size = pkt->size; /* if update, update the audio clock w/pts */ - if(pkt->pts != AV_NOPTS_VALUE) { + if((uint64_t)pkt->pts != AV_NOPTS_VALUE) { is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; } } @@ -349,7 +351,7 @@ namespace MWRender if (is->video_st->codec->width != 0 && is->video_st->codec->height != 0) { Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton ().getByName("VideoTexture"); - if (texture.isNull () || texture->getWidth() != is->video_st->codec->width || texture->getHeight() != is->video_st->codec->height) + if (texture.isNull () || static_cast(texture->getWidth()) != is->video_st->codec->width || static_cast(texture->getHeight()) != is->video_st->codec->height) { Ogre::TextureManager::getSingleton ().remove ("VideoTexture"); texture = Ogre::TextureManager::getSingleton().createManual( @@ -364,6 +366,7 @@ namespace MWRender Ogre::PixelBox pb(is->video_st->codec->width, is->video_st->codec->height, 1, Ogre::PF_BYTE_RGBA, vp->data); Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer(); buffer->blitFromMemory(pb); + is->display_ready = 1; } free(vp->data); @@ -527,7 +530,7 @@ namespace MWRender int video_thread(void *arg) { VideoState *is = (VideoState *)arg; AVPacket pkt1, *packet = &pkt1; - int len1, frameFinished; + int frameFinished; AVFrame *pFrame; double pts; @@ -547,12 +550,15 @@ namespace MWRender // Save global pts to be stored in pFrame global_video_pkt_pts = packet->pts; // Decode video frame - len1 = avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, - packet); - if(packet->dts == AV_NOPTS_VALUE + if (avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, + packet) < 0) + { + throw std::runtime_error("Error decoding video frame"); + } + if((uint64_t)packet->dts == AV_NOPTS_VALUE && pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) { pts = *(uint64_t *)pFrame->opaque; - } else if(packet->dts != AV_NOPTS_VALUE) { + } else if((uint64_t)packet->dts != AV_NOPTS_VALUE) { pts = packet->dts; } else { pts = 0; @@ -586,7 +592,7 @@ namespace MWRender AVCodec *codec; SDL_AudioSpec wanted_spec, spec; - if(stream_index < 0 || stream_index >= pFormatCtx->nb_streams) { + if(stream_index < 0 || stream_index >= static_cast(pFormatCtx->nb_streams)) { return -1; } @@ -650,127 +656,141 @@ namespace MWRender break; } - + return 0; } int decode_interrupt_cb(void) { return (global_video_state && global_video_state->quit); } - int decode_thread(void *arg) { - + int decode_thread(void *arg) + { VideoState *is = (VideoState *)arg; - AVFormatContext *pFormatCtx = avformat_alloc_context (); - AVPacket pkt1, *packet = &pkt1; + try + { + AVFormatContext *pFormatCtx = avformat_alloc_context (); + AVPacket pkt1, *packet = &pkt1; - int video_index = -1; - int audio_index = -1; - int i; + int video_index = -1; + int audio_index = -1; + unsigned int i; - is->videoStream=-1; - is->audioStream=-1; - is->quit = 0; + is->videoStream=-1; + is->audioStream=-1; + is->quit = 0; - Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton ().openResource (is->resourceName); - if(stream.isNull ()) - throw std::runtime_error("Failed to open video resource"); - is->stream = stream; + Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton ().openResource (is->resourceName); + if(stream.isNull ()) + throw std::runtime_error("Failed to open video resource"); + is->stream = stream; - AVIOContext *ioContext = 0; + AVIOContext *ioContext = 0; - ioContext = avio_alloc_context(NULL, 0, 0, is, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if (!ioContext) - throw std::runtime_error("Failed to allocate ioContext "); + ioContext = avio_alloc_context(NULL, 0, 0, is, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if (!ioContext) + throw std::runtime_error("Failed to allocate ioContext "); - pFormatCtx->pb = ioContext; + pFormatCtx->pb = ioContext; - global_video_state = is; - // will interrupt blocking functions if we quit! - //url_set_interrupt_cb(decode_interrupt_cb); + global_video_state = is; + // will interrupt blocking functions if we quit! + //url_set_interrupt_cb(decode_interrupt_cb); - // Open video file - /// \todo leak here, ffmpeg or valgrind bug ? - if (avformat_open_input(&pFormatCtx, is->resourceName.c_str(), NULL, NULL)) - throw std::runtime_error("Failed to open video input"); + // Open video file + /// \todo leak here, ffmpeg or valgrind bug ? + if (avformat_open_input(&pFormatCtx, is->resourceName.c_str(), NULL, NULL)) + throw std::runtime_error("Failed to open video input"); - // Retrieve stream information - if(avformat_find_stream_info(pFormatCtx, NULL)<0) - throw std::runtime_error("Failed to retrieve stream information"); + // Retrieve stream information + if(avformat_find_stream_info(pFormatCtx, NULL)<0) + throw std::runtime_error("Failed to retrieve stream information"); - // Dump information about file onto standard error - av_dump_format(pFormatCtx, 0, is->resourceName.c_str(), 0); + // Dump information about file onto standard error + av_dump_format(pFormatCtx, 0, is->resourceName.c_str(), 0); - for(i=0; inb_streams; i++) { - if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && - video_index < 0) { - video_index=i; - } - if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && - audio_index < 0) { - audio_index=i; + for(i=0; inb_streams; i++) { + if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && + video_index < 0) { + video_index=i; + } + if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && + audio_index < 0) { + audio_index=i; + } } - } - if(audio_index >= 0) { - stream_component_open(is, audio_index, pFormatCtx); - } - if(video_index >= 0) { - stream_component_open(is, video_index, pFormatCtx); - } + if(audio_index >= 0) { + stream_component_open(is, audio_index, pFormatCtx); + } + if(video_index >= 0) { + stream_component_open(is, video_index, pFormatCtx); + } - if(is->videoStream >= 0 /*|| is->audioStream < 0*/) - { + if(is->videoStream >= 0 /*|| is->audioStream < 0*/) + { - // main decode loop + // main decode loop - for(;;) { - if(is->quit) { - break; - } - if( (is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || - is->videoq.size > MAX_VIDEOQ_SIZE) { - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - continue; - } - if(av_read_frame(pFormatCtx, packet) < 0) { - break; + for(;;) { + if(is->quit) { + break; + } + if( (is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || + is->videoq.size > MAX_VIDEOQ_SIZE) { + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + continue; + } + if(av_read_frame(pFormatCtx, packet) < 0) { + break; + } + // Is this a packet from the video stream? + if(packet->stream_index == is->videoStream) { + packet_queue_put(&is->videoq, packet); + } else if(packet->stream_index == is->audioStream) { + packet_queue_put(&is->audioq, packet); + } else { + av_free_packet(packet); + } } - // Is this a packet from the video stream? - if(packet->stream_index == is->videoStream) { - packet_queue_put(&is->videoq, packet); - } else if(packet->stream_index == is->audioStream) { - packet_queue_put(&is->audioq, packet); - } else { - av_free_packet(packet); + /* all done - wait for it */ + while(!is->quit) { + // EOF reached, all packets processed, we can exit now + if (is->audioq.nb_packets == 0 && is->videoq.nb_packets == 0) + break; + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); } } - /* all done - wait for it */ - while(!is->quit) { - // EOF reached, all packets processed, we can exit now - if (is->audioq.nb_packets == 0 && is->videoq.nb_packets == 0) - break; - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - } - } - is->quit = 1; - is->audioq.cond.notify_one (); - is->videoq.cond.notify_one (); + is->quit = 1; - is->video_thread.join(); + is->audioq.cond.notify_one (); + is->videoq.cond.notify_one (); - if (is->audioStream >= 0) - avcodec_close(is->audio_st->codec); - if (is->videoStream >= 0) - avcodec_close(is->video_st->codec); + is->video_thread.join(); - sws_freeContext (is->sws_context); + if (is->audioStream >= 0) + avcodec_close(is->audio_st->codec); + if (is->videoStream >= 0) + avcodec_close(is->video_st->codec); - av_close_input_file(pFormatCtx); - pFormatCtx = NULL; + sws_freeContext (is->sws_context); - av_free(ioContext); + avformat_close_input(&pFormatCtx); + pFormatCtx = NULL; + + av_free(ioContext); + } + catch (std::runtime_error& e) + { + std::cerr << "An error occured playing the video: " << e.what () << std::endl; + is->quit = 1; + } + catch (Ogre::Exception& e) + { + std::cerr << "An error occured playing the video: " << e.getFullDescription () << std::endl; + is->quit = 1; + } return 0; } @@ -857,8 +877,10 @@ namespace MWRender close(); } - if (!Ogre::TextureManager::getSingleton ().getByName ("VideoTexture").isNull ()) + if (mState && mState->display_ready && !Ogre::TextureManager::getSingleton ().getByName ("VideoTexture").isNull ()) mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("VideoTexture"); + else + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("black.png"); } void VideoPlayer::close() diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 67a22a506..d9dc6ded0 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -48,6 +48,8 @@ namespace MWRender boost::mutex mutex; boost::condition_variable cond; + + void flush (); }; struct VideoPicture { VideoPicture () : @@ -58,8 +60,6 @@ namespace MWRender double pts; }; - static void packet_queue_flush(PacketQueue *q); - struct VideoState { VideoState () : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock(0), @@ -67,14 +67,14 @@ namespace MWRender audio_pkt_data(NULL), audio_pkt_size(0), audio_hw_buf_size(0), audio_diff_cum(0), audio_diff_avg_coef(0), audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), frame_last_delay(0), video_clock(0), video_current_pts(0), video_current_pts_time(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), - pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), sws_context(NULL) + pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), sws_context(NULL), display_ready(0) {} ~VideoState() { - packet_queue_flush (&audioq); - packet_queue_flush (&videoq); + audioq.flush (); + videoq.flush(); if (pictq_size >= 1) free (pictq[0].data); @@ -126,6 +126,7 @@ namespace MWRender int quit; int refresh; + int display_ready; }; enum { AV_SYNC_AUDIO_MASTER, diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 5f61ab8f0..9d5942111 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -277,7 +277,7 @@ void FFmpeg_Decoder::open(const std::string &fname) ss << stream->mCodecCtx->codec_id; fail(ss.str()); } - if(avcodec_open(stream->mCodecCtx, codec) < 0) + if(avcodec_open2(stream->mCodecCtx, codec, NULL) < 0) fail("Failed to open audio codec " + std::string(codec->long_name)); stream->mDecodedData = (char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); @@ -293,7 +293,7 @@ void FFmpeg_Decoder::open(const std::string &fname) } catch(std::exception &e) { - av_close_input_file(mFormatCtx); + avformat_close_input(&mFormatCtx); mFormatCtx = NULL; throw; } @@ -317,7 +317,7 @@ void FFmpeg_Decoder::close() AVIOContext* context = mFormatCtx->pb; av_free(context); mFormatCtx->pb = NULL; - av_close_input_file(mFormatCtx); + avformat_close_input(&mFormatCtx); } mFormatCtx = NULL; From cc18b30e17fe054b4c7b6bd41baafe3aeaa8799a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 12 Dec 2012 22:36:20 +0100 Subject: [PATCH 121/378] open sub view on double click in report view --- apps/opencs/model/tools/reportmodel.cpp | 5 +++++ apps/opencs/model/tools/reportmodel.hpp | 2 ++ apps/opencs/model/world/universalid.hpp | 4 ++++ apps/opencs/view/doc/subview.hpp | 4 ++++ apps/opencs/view/doc/view.cpp | 4 ++++ apps/opencs/view/doc/view.hpp | 6 ++++-- apps/opencs/view/tools/reportsubview.cpp | 11 +++++++++-- apps/opencs/view/tools/reportsubview.hpp | 13 +++++++++++++ 8 files changed, 45 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/tools/reportmodel.cpp b/apps/opencs/model/tools/reportmodel.cpp index f142d7669..b12531875 100644 --- a/apps/opencs/model/tools/reportmodel.cpp +++ b/apps/opencs/model/tools/reportmodel.cpp @@ -63,4 +63,9 @@ void CSMTools::ReportModel::add (const std::string& row) mRows.push_back (std::make_pair (row.substr (0, index), row.substr (index+1))); endInsertRows(); +} + +const CSMWorld::UniversalId& CSMTools::ReportModel::getUniversalId (int row) const +{ + return mRows.at (row).first; } \ No newline at end of file diff --git a/apps/opencs/model/tools/reportmodel.hpp b/apps/opencs/model/tools/reportmodel.hpp index 8814b6d49..55c25d907 100644 --- a/apps/opencs/model/tools/reportmodel.hpp +++ b/apps/opencs/model/tools/reportmodel.hpp @@ -29,6 +29,8 @@ namespace CSMTools virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex()); void add (const std::string& row); + + const CSMWorld::UniversalId& getUniversalId (int row) const; }; } diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 14c086510..8f43bf083 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace CSMWorld { class UniversalId @@ -87,4 +89,6 @@ namespace CSMWorld std::ostream& operator< (std::ostream& stream, const UniversalId& universalId); } +Q_DECLARE_METATYPE (CSMWorld::UniversalId) + #endif diff --git a/apps/opencs/view/doc/subview.hpp b/apps/opencs/view/doc/subview.hpp index 50ec511ab..985c5eb3c 100644 --- a/apps/opencs/view/doc/subview.hpp +++ b/apps/opencs/view/doc/subview.hpp @@ -35,6 +35,10 @@ namespace CSVDoc CSMWorld::UniversalId getUniversalId() const; virtual void setEditLock (bool locked) = 0; + + signals: + + void focusId (const CSMWorld::UniversalId& universalId); }; } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 4ffd9d749..13edb6e74 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -188,6 +188,10 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id) SubView *view = mSubViewFactory.makeSubView (id, *mDocument); addDockWidget (Qt::TopDockWidgetArea, view); + + connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this, + SLOT (addSubView (const CSMWorld::UniversalId&))); + view->show(); } diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index f7d4dd373..b1dedafe9 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -80,12 +80,14 @@ namespace CSVDoc void updateProgress (int current, int max, int type, int threads); - void addSubView (const CSMWorld::UniversalId& id); - signals: void newDocumentRequest(); + public slots: + + void addSubView (const CSMWorld::UniversalId& id); + private slots: void newView(); diff --git a/apps/opencs/view/tools/reportsubview.cpp b/apps/opencs/view/tools/reportsubview.cpp index 3c9283684..fe1be85d7 100644 --- a/apps/opencs/view/tools/reportsubview.cpp +++ b/apps/opencs/view/tools/reportsubview.cpp @@ -7,19 +7,26 @@ #include "../../model/tools/reportmodel.hpp" CSVTools::ReportSubView::ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) -: CSVDoc::SubView (id) +: CSVDoc::SubView (id), mModel (document.getReport (id)) { setWidget (mTable = new QTableView (this)); - mTable->setModel (document.getReport (id)); + mTable->setModel (mModel); mTable->horizontalHeader()->setResizeMode (QHeaderView::Interactive); mTable->verticalHeader()->hide(); mTable->setSortingEnabled (true); mTable->setSelectionBehavior (QAbstractItemView::SelectRows); mTable->setSelectionMode (QAbstractItemView::ExtendedSelection); + + connect (mTable, SIGNAL (doubleClicked (const QModelIndex&)), this, SLOT (show (const QModelIndex&))); } void CSVTools::ReportSubView::setEditLock (bool locked) { // ignored. We don't change document state anyway. +} + +void CSVTools::ReportSubView::show (const QModelIndex& index) +{ + focusId (mModel->getUniversalId (index.row())); } \ No newline at end of file diff --git a/apps/opencs/view/tools/reportsubview.hpp b/apps/opencs/view/tools/reportsubview.hpp index f81f3c386..626ceb663 100644 --- a/apps/opencs/view/tools/reportsubview.hpp +++ b/apps/opencs/view/tools/reportsubview.hpp @@ -4,18 +4,27 @@ #include "../doc/subview.hpp" class QTableView; +class QModelIndex; namespace CSMDoc { class Document; } +namespace CSMTools +{ + class ReportModel; +} + namespace CSVTools { class Table; class ReportSubView : public CSVDoc::SubView { + Q_OBJECT + + CSMTools::ReportModel *mModel; QTableView *mTable; public: @@ -23,6 +32,10 @@ namespace CSVTools ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); virtual void setEditLock (bool locked); + + private slots: + + void show (const QModelIndex& index); }; } From 34e36fb85269775821efe8a07f7f47518bd1dbb1 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Dec 2012 16:50:35 -0800 Subject: [PATCH 122/378] Add a method to get the time offset from sounds --- apps/openmw/mwsound/openal_output.cpp | 17 +++++++++++++++++ apps/openmw/mwsound/sound.hpp | 1 + 2 files changed, 18 insertions(+) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index bc6102beb..4c62bb10f 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -95,6 +95,7 @@ public: virtual void stop(); virtual bool isPlaying(); + virtual double getTimeOffset(); virtual void update(); void play(); @@ -259,6 +260,11 @@ bool OpenAL_SoundStream::isPlaying() return !mIsFinished; } +double OpenAL_SoundStream::getTimeOffset() +{ + return 0.0; +} + void OpenAL_SoundStream::update() { ALfloat gain = mVolume*mBaseVolume; @@ -348,6 +354,7 @@ public: virtual void stop(); virtual bool isPlaying(); + virtual double getTimeOffset(); virtual void update(); }; @@ -396,6 +403,16 @@ bool OpenAL_Sound::isPlaying() return state==AL_PLAYING || state==AL_PAUSED; } +double OpenAL_Sound::getTimeOffset() +{ + ALfloat t; + + alGetSourcef(mSource, AL_SEC_OFFSET, &t); + throwALerror(); + + return t; +} + void OpenAL_Sound::update() { ALfloat gain = mVolume*mBaseVolume; diff --git a/apps/openmw/mwsound/sound.hpp b/apps/openmw/mwsound/sound.hpp index 729147f75..1b6f50ff4 100644 --- a/apps/openmw/mwsound/sound.hpp +++ b/apps/openmw/mwsound/sound.hpp @@ -26,6 +26,7 @@ namespace MWSound public: virtual void stop() = 0; virtual bool isPlaying() = 0; + virtual double getTimeOffset() = 0; void setPosition(const Ogre::Vector3 &pos) { mPos = pos; } void setVolume(float volume) { mVolume = volume; } From 1fb9eef27b511281e7c179012a7082dcc169a5b7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Dec 2012 22:02:33 -0800 Subject: [PATCH 123/378] Detach the thread used for frame timing --- apps/openmw/mwrender/videoplayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 9cfe703b4..27fb89130 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -336,7 +336,7 @@ namespace MWRender //SDL_AddTimer(delay, sdl_refresh_timer_cb, is); //is->refresh_queue.push_back (delay); boost::system_time t = boost::get_system_time() + boost::posix_time::milliseconds(delay); - boost::thread (boost::bind(&timer_callback, t, is)); + boost::thread (boost::bind(&timer_callback, t, is)).detach(); } void video_display(VideoState *is) From 9c831d303983c5bf77f92ec05d7f1ae8555ed1d9 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Dec 2012 22:19:44 -0800 Subject: [PATCH 124/378] Add a decoder method to get the "file" name --- apps/openmw/mwsound/audiere_decoder.cpp | 11 ++++++++++- apps/openmw/mwsound/audiere_decoder.hpp | 1 + apps/openmw/mwsound/ffmpeg_decoder.cpp | 5 +++++ apps/openmw/mwsound/ffmpeg_decoder.hpp | 1 + apps/openmw/mwsound/mpgsnd_decoder.cpp | 5 +++++ apps/openmw/mwsound/sound_decoder.hpp | 1 + 6 files changed, 23 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/audiere_decoder.cpp b/apps/openmw/mwsound/audiere_decoder.cpp index 4e73573a7..c9b3d11d1 100644 --- a/apps/openmw/mwsound/audiere_decoder.cpp +++ b/apps/openmw/mwsound/audiere_decoder.cpp @@ -53,6 +53,9 @@ public: : mStream(stream), refs(1) { } virtual ~OgreFile() { } + + Ogre::String getName() + { return mStream->getName(); } }; @@ -60,7 +63,7 @@ void Audiere_Decoder::open(const std::string &fname) { close(); - audiere::FilePtr file(new OgreFile(mResourceMgr.openResource(fname))); + mSoundFile = audiere::FilePtr(new OgreFile(mResourceMgr.openResource(fname))); mSoundSource = audiere::OpenSampleSource(file); int channels, srate; @@ -86,9 +89,15 @@ void Audiere_Decoder::open(const std::string &fname) void Audiere_Decoder::close() { + mSoundFile = NULL; mSoundSource = NULL; } +std::string Audiere_Decoder::getName() +{ + return mSoundFile->getName(); +} + void Audiere_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) { *samplerate = mSampleRate; diff --git a/apps/openmw/mwsound/audiere_decoder.hpp b/apps/openmw/mwsound/audiere_decoder.hpp index 0ad026d51..1e1528880 100644 --- a/apps/openmw/mwsound/audiere_decoder.hpp +++ b/apps/openmw/mwsound/audiere_decoder.hpp @@ -12,6 +12,7 @@ namespace MWSound { class Audiere_Decoder : public Sound_Decoder { + audiere::FilePtr mSoundFile; audiere::SampleSourcePtr mSoundSource; int mSampleRate; SampleType mSampleType; diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 9d5942111..6b3a7f9cd 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -324,6 +324,11 @@ void FFmpeg_Decoder::close() mDataStream.setNull(); } +std::string FFmpeg_Decoder::getName() +{ + return mFormatCtx->filename; +} + void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) { if(mStreams.empty()) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index a6e80fc9b..88115ce3f 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -36,6 +36,7 @@ namespace MWSound virtual void open(const std::string &fname); virtual void close(); + virtual std::string getName(); virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); virtual size_t read(char *buffer, size_t bytes); diff --git a/apps/openmw/mwsound/mpgsnd_decoder.cpp b/apps/openmw/mwsound/mpgsnd_decoder.cpp index 7f7a84889..4ec11b349 100644 --- a/apps/openmw/mwsound/mpgsnd_decoder.cpp +++ b/apps/openmw/mwsound/mpgsnd_decoder.cpp @@ -155,6 +155,11 @@ void MpgSnd_Decoder::close() mDataStream.setNull(); } +std::string MpgSnd_Decoder::getName() +{ + return mDataStream->getName(); +} + void MpgSnd_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) { if(!mSndFile && !mMpgFile) diff --git a/apps/openmw/mwsound/sound_decoder.hpp b/apps/openmw/mwsound/sound_decoder.hpp index 9c28d5ff5..d228a5e98 100644 --- a/apps/openmw/mwsound/sound_decoder.hpp +++ b/apps/openmw/mwsound/sound_decoder.hpp @@ -29,6 +29,7 @@ namespace MWSound virtual void open(const std::string &fname) = 0; virtual void close() = 0; + virtual std::string getName() = 0; virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) = 0; virtual size_t read(char *buffer, size_t bytes) = 0; From 86bf6388c64ad0784bd428de479ce9eb81edb389 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Dec 2012 22:32:02 -0800 Subject: [PATCH 125/378] Pass a decoder to the playStream sound output method --- apps/openmw/mwsound/openal_output.cpp | 8 +++----- apps/openmw/mwsound/openal_output.hpp | 4 ++-- apps/openmw/mwsound/sound_output.hpp | 4 ++-- apps/openmw/mwsound/soundmanagerimp.cpp | 6 +++++- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 4c62bb10f..521e6c357 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -764,7 +764,7 @@ MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre } -MWBase::SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volume, float pitch, int flags) +MWBase::SoundPtr OpenAL_Output::streamSound(DecoderPtr decoder, float volume, float pitch, int flags) { boost::shared_ptr sound; ALuint src; @@ -774,12 +774,10 @@ MWBase::SoundPtr OpenAL_Output::streamSound(const std::string &fname, float volu src = mFreeSources.front(); mFreeSources.pop_front(); + if((flags&MWBase::SoundManager::Play_Loop)) + std::cout <<"Warning: cannot loop stream \""<getName()<<"\""<< std::endl; try { - if((flags&MWBase::SoundManager::Play_Loop)) - std::cout <<"Warning: cannot loop stream "<open(fname); sound.reset(new OpenAL_SoundStream(*this, src, decoder)); } catch(std::exception &e) diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index 867150741..ce126035f 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -47,8 +47,8 @@ namespace MWSound virtual MWBase::SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags); virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, - float volume, float pitch, float min, float max, int flags); - virtual MWBase::SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags); + float volume, float pitch, float min, float max, int flags); + virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags); virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env); diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index 1cc45559b..1229f87d0 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -26,8 +26,8 @@ namespace MWSound virtual MWBase::SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags) = 0; virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, - float volume, float pitch, float min, float max, int flags) = 0; - virtual MWBase::SoundPtr streamSound(const std::string &fname, float volume, float pitch, int flags) = 0; + float volume, float pitch, float min, float max, int flags) = 0; + virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags) = 0; virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env) = 0; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 1414dbb76..2a489a789 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -167,7 +167,11 @@ namespace MWSound { float basevol = mMasterVolume * mMusicVolume; stopMusic(); - mMusic = mOutput->streamSound(filename, basevol, 1.0f, Play_NoEnv); + + DecoderPtr decoder = getDecoder(); + decoder->open(filename); + + mMusic = mOutput->streamSound(decoder, basevol, 1.0f, Play_NoEnv); mMusic->mBaseVolume = basevol; mMusic->mFlags = Play_NoEnv; } From 1571243ef00dce1c22753c791cfb879f2b50ef2f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 12 Dec 2012 23:13:35 -0800 Subject: [PATCH 126/378] Implement getTimeOffset for OpenAL_SoundStream --- apps/openmw/mwsound/openal_output.cpp | 130 ++++++++++++++++++-------- 1 file changed, 90 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 521e6c357..658483b02 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -65,6 +65,18 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type) return AL_NONE; } +static ALint getBufferSampleCount(ALuint buf) +{ + ALint size, bits, channels; + + alGetBufferi(buf, AL_SIZE, &size); + alGetBufferi(buf, AL_BITS, &bits); + alGetBufferi(buf, AL_CHANNELS, &channels); + throwALerror(); + + return size / channels * 8 / bits; +} + // // A streaming OpenAL sound. // @@ -82,6 +94,9 @@ class OpenAL_SoundStream : public Sound ALsizei mSampleRate; ALuint mBufferSize; + ALuint mSamplesTotal; + ALuint mSamplesQueued; + DecoderPtr mDecoder; volatile bool mIsFinished; @@ -172,7 +187,8 @@ private: OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder) - : mOutput(output), mSource(src), mDecoder(decoder), mIsFinished(true) + : mOutput(output), mSource(src), mSamplesTotal(0), mSamplesQueued(0), + mDecoder(decoder), mIsFinished(true) { throwALerror(); @@ -215,16 +231,19 @@ OpenAL_SoundStream::~OpenAL_SoundStream() void OpenAL_SoundStream::play() { std::vector data(mBufferSize); + ALuint count = 0; alSourceStop(mSource); alSourcei(mSource, AL_BUFFER, 0); throwALerror(); + mSamplesQueued = 0; for(ALuint i = 0;i < sNumBuffers;i++) { size_t got; got = mDecoder->read(&data[0], data.size()); alBufferData(mBuffers[i], mFormat, &data[0], got, mSampleRate); + count += getBufferSampleCount(mBuffers[i]); } throwALerror(); @@ -232,6 +251,9 @@ void OpenAL_SoundStream::play() alSourcePlay(mSource); throwALerror(); + mSamplesTotal += count; + mSamplesQueued = count; + mIsFinished = false; mOutput.mStreamThread->add(this); } @@ -244,8 +266,10 @@ void OpenAL_SoundStream::stop() alSourceStop(mSource); alSourcei(mSource, AL_BUFFER, 0); throwALerror(); + mSamplesQueued = 0; mDecoder->rewind(); + mSamplesTotal = 0; } bool OpenAL_SoundStream::isPlaying() @@ -262,7 +286,21 @@ bool OpenAL_SoundStream::isPlaying() double OpenAL_SoundStream::getTimeOffset() { - return 0.0; + ALint state = AL_STOPPED; + ALfloat offset = 0.0f; + double t; + + mOutput.mStreamThread->mMutex.lock(); + alGetSourcef(mSource, AL_SEC_OFFSET, &offset); + alGetSourcei(mSource, AL_SOURCE_STATE, &state); + if(state == AL_PLAYING || state == AL_PAUSED) + t = (double)(mSamplesTotal - mSamplesQueued)/(double)mSampleRate + offset; + else + t = (double)mSamplesTotal / (double)mSampleRate; + mOutput.mStreamThread->mMutex.unlock(); + + throwALerror(); + return t; } void OpenAL_SoundStream::update() @@ -285,52 +323,64 @@ void OpenAL_SoundStream::update() bool OpenAL_SoundStream::process() { - bool finished = mIsFinished; - ALint processed, state; - - alGetSourcei(mSource, AL_SOURCE_STATE, &state); - alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed); - throwALerror(); + try { + bool finished = mIsFinished; + ALint samples_unqueued = 0; + ALint samples_queued = 0; + ALint processed, state; - if(processed > 0) - { - std::vector data(mBufferSize); - do { - ALuint bufid; - size_t got; - - alSourceUnqueueBuffers(mSource, 1, &bufid); - processed--; - - if(finished) - continue; - - got = mDecoder->read(&data[0], data.size()); - finished = (got < data.size()); - if(got > 0) - { - alBufferData(bufid, mFormat, &data[0], got, mSampleRate); - alSourceQueueBuffers(mSource, 1, &bufid); - } - } while(processed > 0); + alGetSourcei(mSource, AL_SOURCE_STATE, &state); + alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed); throwALerror(); - } - if(state != AL_PLAYING && state != AL_PAUSED) - { - ALint queued; + if(processed > 0) + { + std::vector data(mBufferSize); + do { + ALuint bufid = 0; + size_t got; + + alSourceUnqueueBuffers(mSource, 1, &bufid); + samples_unqueued += getBufferSampleCount(bufid); + processed--; + + if(finished) + continue; + + got = mDecoder->read(&data[0], data.size()); + finished = (got < data.size()); + if(got > 0) + { + alBufferData(bufid, mFormat, &data[0], got, mSampleRate); + alSourceQueueBuffers(mSource, 1, &bufid); + samples_queued += getBufferSampleCount(bufid); + } + } while(processed > 0); + throwALerror(); + } - alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); - throwALerror(); - if(queued > 0) + if(state != AL_PLAYING && state != AL_PAUSED) { - alSourcePlay(mSource); + ALint queued = 0; + + alGetSourcei(mSource, AL_BUFFERS_QUEUED, &queued); + if(queued > 0) + alSourcePlay(mSource); throwALerror(); } - } - mIsFinished = finished; - return !finished; + mSamplesQueued -= samples_unqueued; + mSamplesQueued += samples_queued; + mSamplesTotal += samples_queued; + mIsFinished = finished; + } + catch(std::exception &e) { + std::cout<< "Error updating stream \""<getName()<<"\"" < Date: Thu, 13 Dec 2012 00:05:57 -0800 Subject: [PATCH 127/378] Add a method to play an audio track with a custom decoder --- apps/openmw/mwbase/soundmanager.hpp | 5 +++++ apps/openmw/mwsound/soundmanagerimp.cpp | 20 ++++++++++++++++++++ apps/openmw/mwsound/soundmanagerimp.hpp | 5 +++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 2fa98cfee..8d204bad4 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -22,6 +22,8 @@ namespace MWWorld namespace MWSound { class Sound; + class Sound_Decoder; + typedef boost::shared_ptr DecoderPtr; } namespace MWBase @@ -89,6 +91,9 @@ namespace MWBase virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()) = 0; ///< Stop an actor speaking + virtual SoundPtr playTrack(const MWSound::DecoderPtr& decoder) = 0; + ///< Play a 2D audio track, using a custom decoder + virtual SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal) = 0; ///< Play a sound, independently of 3D-position diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 2a489a789..6bb3d59dd 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -275,6 +275,26 @@ namespace MWSound } + MWBase::SoundPtr SoundManager::playTrack(const DecoderPtr& decoder) + { + MWBase::SoundPtr track; + if(!mOutput->isInitialized()) + return track; + try + { + float basevol = mMasterVolume; + + track = mOutput->streamSound(decoder, basevol, 1.0f, Play_NoEnv); + track->mBaseVolume = basevol; + track->mFlags = Play_NoEnv; + } + catch(std::exception &e) + { + std::cout <<"Sound Error: "< DecoderPtr; - enum Environment { Env_Normal, Env_Underwater @@ -105,6 +103,9 @@ namespace MWSound virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()); ///< Stop an actor speaking + virtual MWBase::SoundPtr playTrack(const DecoderPtr& decoder); + ///< Play a 2D audio track, using a custom decoder + virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal); ///< Play a sound, independently of 3D-position From 1ffaf6625a37ba8ba0f1463926dc8b94e2900623 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 01:42:20 -0800 Subject: [PATCH 128/378] Remove SDL for playing movie audio and prepare for using an audio track This breaks audio playback on movies --- apps/openmw/mwrender/videoplayer.cpp | 322 ++++++++++++++++----------- apps/openmw/mwrender/videoplayer.hpp | 71 +++--- 2 files changed, 226 insertions(+), 167 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 27fb89130..fa179f8b1 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -4,7 +4,8 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" - +#include "../mwsound/sound_decoder.hpp" +#include "../mwsound/sound.hpp" namespace MWRender @@ -143,19 +144,13 @@ namespace MWRender this->mutex.unlock (); } - double get_audio_clock(VideoState *is) { - double pts; - - pts = is->audio_clock; /* maintained in the audio thread */ - if(is->audio_st) { - int n = is->audio_st->codec->channels * 2; - int bytes_per_sec = is->audio_st->codec->sample_rate * n; - int hw_buf_size = is->audio_buf_size - is->audio_buf_index; - pts -= (double)hw_buf_size / bytes_per_sec; - } - return pts; + double get_audio_clock(VideoState *is) + { + return is->AudioTrack->getTimeOffset(); } - double get_video_clock(VideoState *is) { + + double get_video_clock(VideoState *is) + { double delta; delta = (av_gettime() - is->video_current_pts_time) / 1000000.0; @@ -173,86 +168,110 @@ namespace MWRender return get_external_clock(is); } } + +class MovieAudioDecoder : public MWSound::Sound_Decoder +{ + static void fail(const std::string &str) + { + throw std::runtime_error(str); + } + + VideoState *is; + /* Add or subtract samples to get a better sync, return new - audio buffer size */ - int synchronize_audio(VideoState *is, short *samples, - int samples_size, double pts) { - int n; - double ref_clock; + * audio buffer size */ + int synchronize_audio(uint8_t *samples, int samples_size, double pts) + { + if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) + return samples_size; + + double diff, avg_diff, ref_clock; + int wanted_size, min_size, max_size, n; + // int nb_samples; n = 2 * is->audio_st->codec->channels; - if(is->av_sync_type != AV_SYNC_AUDIO_MASTER) { - double diff, avg_diff; - int wanted_size, min_size, max_size; - // int nb_samples; - - ref_clock = get_master_clock(is); - diff = get_audio_clock(is) - ref_clock; - if(diff < AV_NOSYNC_THRESHOLD) { - // accumulate the diffs - is->audio_diff_cum = diff + is->audio_diff_avg_coef * - is->audio_diff_cum; - if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) { - is->audio_diff_avg_count++; - } else { - avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); - if(fabs(avg_diff) >= is->audio_diff_threshold) { - wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); - min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100); - max_size = samples_size * ((100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100); - if(wanted_size < min_size) { - wanted_size = min_size; - } else if (wanted_size > max_size) { - wanted_size = max_size; - } - if(wanted_size < samples_size) { - /* remove samples */ - samples_size = wanted_size; - } else if(wanted_size > samples_size) { - uint8_t *samples_end, *q; - int nb; - /* add samples by copying final sample*/ - nb = (samples_size - wanted_size); - samples_end = (uint8_t *)samples + samples_size - n; - q = samples_end + n; - while(nb > 0) { - memcpy(q, samples_end, n); - q += n; - nb -= n; - } - samples_size = wanted_size; + ref_clock = get_master_clock(is); + diff = get_audio_clock(is) - ref_clock; + if(diff < AV_NOSYNC_THRESHOLD) + { + // accumulate the diffs + is->audio_diff_cum = diff + is->audio_diff_avg_coef * + is->audio_diff_cum; + if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) + is->audio_diff_avg_count++; + else + { + avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); + if(fabs(avg_diff) >= is->audio_diff_threshold) + { + wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); + min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100); + max_size = samples_size * ((100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100); + + if(wanted_size < min_size) + wanted_size = min_size; + else if (wanted_size > max_size) + wanted_size = max_size; + + if(wanted_size < samples_size) + { + /* remove samples */ + samples_size = wanted_size; + } + else if(wanted_size > samples_size) + { + uint8_t *samples_end, *q; + int nb; + /* add samples by copying final sample*/ + nb = (samples_size - wanted_size); + samples_end = samples + samples_size - n; + q = samples_end + n; + while(nb > 0) + { + memcpy(q, samples_end, n); + q += n; + nb -= n; } + samples_size = wanted_size; } } - } else { - /* difference is TOO big; reset diff stuff */ - is->audio_diff_avg_count = 0; - is->audio_diff_cum = 0; } } + else + { + /* difference is TOO big; reset diff stuff */ + is->audio_diff_avg_count = 0; + is->audio_diff_cum = 0; + } + return samples_size; } - int audio_decode_frame(VideoState *is, uint8_t *audio_buf, int buf_size, double *pts_ptr) { - int len1, data_size, n; + + int audio_decode_frame(uint8_t *audio_buf, int buf_size, double *pts_ptr) + { AVPacket *pkt = &is->audio_pkt; + int len1, data_size, n; double pts; - for(;;) { - while(is->audio_pkt_size > 0) { + for(;;) + { + while(is->audio_pkt_size > 0) + { data_size = buf_size; + len1 = avcodec_decode_audio3(is->audio_st->codec, (int16_t*)audio_buf, &data_size, pkt); - - - if(len1 < 0) { + if(len1 < 0) + { /* if error, skip frame */ is->audio_pkt_size = 0; break; } is->audio_pkt_data += len1; is->audio_pkt_size -= len1; - if(data_size <= 0) { + if(data_size <= 0) + { /* No data yet, get more frames */ continue; } @@ -268,52 +287,111 @@ namespace MWRender if(pkt->data) av_free_packet(pkt); - if(is->quit) { + if(is->quit) return -1; - } + /* next packet */ - if(packet_queue_get(&is->audioq, pkt, 1) < 0) { + if(packet_queue_get(&is->audioq, pkt, 1) < 0) return -1; - } + is->audio_pkt_data = pkt->data; is->audio_pkt_size = pkt->size; /* if update, update the audio clock w/pts */ - if((uint64_t)pkt->pts != AV_NOPTS_VALUE) { + if((uint64_t)pkt->pts != AV_NOPTS_VALUE) is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; + } + } + + void open(const std::string&) + { fail(std::string("Invalid call to ")+__PRETTY_FUNCTION__); } + + void close() { } + + std::string getName() + { return is->stream->getName(); } + + void rewind() { } + +public: + MovieAudioDecoder(VideoState *_is) : is(_is) { } + + void getInfo(int *samplerate, MWSound::ChannelConfig *chans, MWSound::SampleType * type) + { + if(is->audio_st->codec->sample_fmt == AV_SAMPLE_FMT_U8) + *type = MWSound::SampleType_UInt8; + else if(is->audio_st->codec->sample_fmt == AV_SAMPLE_FMT_S16) + *type = MWSound::SampleType_Int16; + else + fail(std::string("Unsupported sample format: ")+ + av_get_sample_fmt_name(is->audio_st->codec->sample_fmt)); + + if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_MONO) + *chans = MWSound::ChannelConfig_Mono; + else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_STEREO) + *chans = MWSound::ChannelConfig_Stereo; + else if(is->audio_st->codec->channel_layout == 0) + { + /* Unknown channel layout. Try to guess. */ + if(is->audio_st->codec->channels == 1) + *chans = MWSound::ChannelConfig_Mono; + else if(is->audio_st->codec->channels == 2) + *chans = MWSound::ChannelConfig_Stereo; + else + { + std::stringstream sstr("Unsupported raw channel count: "); + sstr << is->audio_st->codec->channels; + fail(sstr.str()); } } + else + { + char str[1024]; + av_get_channel_layout_string(str, sizeof(str), is->audio_st->codec->channels, + is->audio_st->codec->channel_layout); + fail(std::string("Unsupported channel layout: ")+str); + } + + *samplerate = is->audio_st->codec->sample_rate; } - void audio_callback(void *userdata, Uint8 *stream, int len) { - VideoState *is = (VideoState *)userdata; - int len1, audio_size; - double pts; + size_t read(char *stream, size_t len) + { + size_t total = 0; - while(len > 0) { - if(is->audio_buf_index >= is->audio_buf_size) { + while(total < len) + { + if(is->audio_buf_index >= is->audio_buf_size) + { + int audio_size; + double pts; /* We have already sent all our data; get more */ - audio_size = audio_decode_frame(is, is->audio_buf, sizeof(is->audio_buf), &pts); - if(audio_size < 0) { - /* If error, output silence */ - is->audio_buf_size = 1024; - memset(is->audio_buf, 0, is->audio_buf_size); - } else { - audio_size = synchronize_audio(is, (int16_t *)is->audio_buf, - audio_size, pts); - is->audio_buf_size = audio_size; + audio_size = audio_decode_frame(is->audio_buf, sizeof(is->audio_buf), &pts); + if(audio_size < 0) + { + /* If error, we're done */ + break; } + + audio_size = synchronize_audio(is->audio_buf, audio_size, pts); + is->audio_buf_size = audio_size; is->audio_buf_index = 0; } - len1 = is->audio_buf_size - is->audio_buf_index; - if(len1 > len) - len1 = len; - memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1); - len -= len1; + + size_t len1 = std::min(is->audio_buf_size - is->audio_buf_index, + len - total); + memcpy(stream, (uint8_t*)is->audio_buf + is->audio_buf_index, len1); + + total += len1; stream += len1; is->audio_buf_index += len1; } + + return total; } +}; + + /* static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) { SDL_Event event; @@ -371,8 +449,8 @@ namespace MWRender } - void video_refresh_timer(void *userdata) { - + void video_refresh_timer(void *userdata) + { VideoState *is = (VideoState *)userdata; VideoPicture *vp; double actual_delay, delay, sync_threshold, ref_clock, diff; @@ -439,8 +517,8 @@ namespace MWRender } } - int queue_picture(VideoState *is, AVFrame *pFrame, double pts) { - + int queue_picture(VideoState *is, AVFrame *pFrame, double pts) + { VideoPicture *vp; /* wait until we have a new pic */ @@ -487,8 +565,8 @@ namespace MWRender return 0; } - double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) { - + double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) + { double frame_delay; if(pts != 0) { @@ -512,14 +590,16 @@ namespace MWRender * buffer. We use this to store the global_pts in * a frame at the time it is allocated. */ - int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) { + int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) + { int ret = avcodec_default_get_buffer(c, pic); uint64_t *pts = (uint64_t*)av_malloc(sizeof(uint64_t)); *pts = global_video_pkt_pts; pic->opaque = pts; return ret; } - void our_release_buffer(struct AVCodecContext *c, AVFrame *pic) { + void our_release_buffer(struct AVCodecContext *c, AVFrame *pic) + { if(pic) av_freep(&pic->opaque); avcodec_default_release_buffer(c, pic); } @@ -583,9 +663,9 @@ namespace MWRender int stream_component_open(VideoState *is, int stream_index, AVFormatContext *pFormatCtx) { + MWSound::DecoderPtr decoder; AVCodecContext *codecCtx; AVCodec *codec; - SDL_AudioSpec wanted_spec, spec; if(stream_index < 0 || stream_index >= static_cast(pFormatCtx->nb_streams)) { return -1; @@ -594,22 +674,9 @@ namespace MWRender // Get a pointer to the codec context for the video stream codecCtx = pFormatCtx->streams[stream_index]->codec; - if(codecCtx->codec_type == AVMEDIA_TYPE_AUDIO) { - // Set audio settings from codec info - wanted_spec.freq = codecCtx->sample_rate; - wanted_spec.format = AUDIO_S16SYS; - wanted_spec.channels = codecCtx->channels; - wanted_spec.silence = 0; - wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; - wanted_spec.callback = audio_callback; - wanted_spec.userdata = is; - - if(SDL_OpenAudio(&wanted_spec, &spec) < 0) { - fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError()); - return -1; - } - is->audio_hw_buf_size = spec.size; - } + if(codecCtx->codec_type == AVMEDIA_TYPE_AUDIO) + return -1; + codec = avcodec_find_decoder(codecCtx->codec_id); if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) { fprintf(stderr, "Unsupported codec!\n"); @@ -627,11 +694,13 @@ namespace MWRender is->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); is->audio_diff_avg_count = 0; /* Correct audio only if larger error than this */ - is->audio_diff_threshold = 2.0 * SDL_AUDIO_BUFFER_SIZE / codecCtx->sample_rate; + is->audio_diff_threshold = 2.0 * 0.1/* 100 ms */; memset(&is->audio_pkt, 0, sizeof(is->audio_pkt)); packet_queue_init(&is->audioq); - SDL_PauseAudio(0); + + decoder.reset(new MovieAudioDecoder(is)); + is->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); break; case AVMEDIA_TYPE_VIDEO: is->videoStream = stream_index; @@ -841,17 +910,13 @@ namespace MWRender } mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); + MWBase::Environment::get().getSoundManager()->pauseAllSounds(); mState = new VideoState; // Register all formats and codecs av_register_all(); - MWBase::Environment::get().getSoundManager()->pauseAllSounds(); - if(SDL_Init(SDL_INIT_AUDIO)) { - throw std::runtime_error("Failed to initialize SDL"); - } - mState->refresh = 0; mState->resourceName = resourceName; @@ -869,9 +934,7 @@ namespace MWRender mState->refresh--; } if (mState && mState->quit) - { close(); - } if (mState && mState->display_ready && !Ogre::TextureManager::getSingleton ().getByName ("VideoTexture").isNull ()) mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("VideoTexture"); @@ -888,7 +951,6 @@ namespace MWRender delete mState; mState = NULL; - SDL_CloseAudio(); MWBase::Environment::get().getSoundManager()->resumeAllSounds(); mRectangle->setVisible (false); diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index d9dc6ded0..eac906356 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -16,13 +16,11 @@ extern "C" #include } -#include -#include - #include #include -#define SDL_AUDIO_BUFFER_SIZE 1024 +#include "../mwbase/soundmanager.hpp" + #define MAX_AUDIOQ_SIZE (5 * 16 * 1024) #define MAX_VIDEOQ_SIZE (5 * 256 * 1024) #define AV_SYNC_THRESHOLD 0.01 @@ -33,11 +31,8 @@ extern "C" #define DEFAULT_AV_SYNC_TYPE AV_SYNC_VIDEO_MASTER - namespace MWRender { - - struct PacketQueue { PacketQueue () : first_pkt(NULL), last_pkt(NULL), nb_packets(0), size(0) @@ -64,7 +59,7 @@ namespace MWRender VideoState () : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock(0), external_clock_time(0), audio_clock(0), audio_st(NULL), audio_buf_size(0), - audio_pkt_data(NULL), audio_pkt_size(0), audio_hw_buf_size(0), audio_diff_cum(0), audio_diff_avg_coef(0), + audio_pkt_data(NULL), audio_pkt_size(0), audio_diff_cum(0), audio_diff_avg_coef(0), audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), frame_last_delay(0), video_clock(0), video_current_pts(0), video_current_pts_time(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), sws_context(NULL), display_ready(0) @@ -80,41 +75,44 @@ namespace MWRender free (pictq[0].data); } - int videoStream, audioStream; + int videoStream, audioStream; + + int av_sync_type; + double external_clock; /* external clock base */ + int64_t external_clock_time; - int av_sync_type; - double external_clock; /* external clock base */ - int64_t external_clock_time; - double audio_clock; - AVStream *audio_st; - PacketQueue audioq; + double audio_clock; + AVStream *audio_st; + PacketQueue audioq; DECLARE_ALIGNED(16, uint8_t, audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2]); - unsigned int audio_buf_size; - unsigned int audio_buf_index; - AVPacket audio_pkt; - uint8_t *audio_pkt_data; - int audio_pkt_size; - int audio_hw_buf_size; - double audio_diff_cum; /* used for AV difference average computation */ - double audio_diff_avg_coef; - double audio_diff_threshold; - int audio_diff_avg_count; - double frame_timer; - double frame_last_pts; - double frame_last_delay; - double video_clock; /// Date: Thu, 13 Dec 2012 11:24:39 +0100 Subject: [PATCH 129/378] moved two helper classes from view/world/table into a separate translation unit --- apps/opencs/CMakeLists.txt | 4 +- apps/opencs/view/world/table.cpp | 94 +------------------------------- apps/opencs/view/world/util.cpp | 57 +++++++++++++++++++ apps/opencs/view/world/util.hpp | 50 +++++++++++++++++ 4 files changed, 110 insertions(+), 95 deletions(-) create mode 100644 apps/opencs/view/world/util.cpp create mode 100644 apps/opencs/view/world/util.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 1f9fe85a2..554607c73 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -13,7 +13,7 @@ set (OPENCS_SRC view/doc/viewmanager.cpp view/doc/view.cpp view/doc/operations.cpp view/doc/operation.cpp view/doc/subviewfactory.cpp view/doc/subview.cpp - view/world/table.cpp view/world/tablesubview.cpp view/world/subviews.cpp + view/world/table.cpp view/world/tablesubview.cpp view/world/subviews.cpp view/world/util.cpp view/tools/reportsubview.cpp view/tools/subviews.cpp ) @@ -33,7 +33,7 @@ set (OPENCS_HDR view/doc/viewmanager.hpp view/doc/view.hpp view/doc/operations.hpp view/doc/operation.hpp view/doc/subviewfactory.hpp view/doc/subview.hpp view/doc/subviewfactoryimp.hpp - view/world/table.hpp view/world/tablesubview.hpp view/world/subviews.hpp + view/world/table.hpp view/world/tablesubview.hpp view/world/subviews.hpp view/world/util.hpp view/tools/reportsubview.hpp view/tools/subviews.hpp ) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 593dc8563..3a5ef7c34 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -1,7 +1,6 @@ #include "table.hpp" -#include #include #include #include @@ -14,98 +13,7 @@ #include "../../model/world/idtable.hpp" #include "../../model/world/record.hpp" -namespace CSVWorld -{ - ///< \brief Getting the data out of an editor widget - /// - /// Really, Qt? Really? - class NastyTableModelHack : public QAbstractTableModel - { - QAbstractItemModel& mModel; - QVariant mData; - - public: - - NastyTableModelHack (QAbstractItemModel& model); - - int rowCount (const QModelIndex & parent = QModelIndex()) const; - - int columnCount (const QModelIndex & parent = QModelIndex()) const; - - QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; - - bool setData (const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - - QVariant getData() const; - }; - - ///< \brief Use commands instead of manipulating the model directly - class CommandDelegate : public QStyledItemDelegate - { - QUndoStack& mUndoStack; - bool mEditLock; - - public: - - CommandDelegate (QUndoStack& undoStack, QObject *parent); - - void setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const; - - void setEditLock (bool locked); - }; - -} - -CSVWorld::NastyTableModelHack::NastyTableModelHack (QAbstractItemModel& model) -: mModel (model) -{} - -int CSVWorld::NastyTableModelHack::rowCount (const QModelIndex & parent) const -{ - return mModel.rowCount (parent); -} - -int CSVWorld::NastyTableModelHack::columnCount (const QModelIndex & parent) const -{ - return mModel.columnCount (parent); -} - -QVariant CSVWorld::NastyTableModelHack::data (const QModelIndex & index, int role) const -{ - return mModel.data (index, role); -} - -bool CSVWorld::NastyTableModelHack::setData ( const QModelIndex &index, const QVariant &value, int role) -{ - mData = value; - return true; -} - -QVariant CSVWorld::NastyTableModelHack::getData() const -{ - return mData; -} - -CSVWorld::CommandDelegate::CommandDelegate (QUndoStack& undoStack, QObject *parent) -: QStyledItemDelegate (parent), mUndoStack (undoStack), mEditLock (false) -{} - -void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemModel *model, - const QModelIndex& index) const -{ - if (!mEditLock) - { - NastyTableModelHack hack (*model); - QStyledItemDelegate::setModelData (editor, &hack, index); - mUndoStack.push (new CSMWorld::ModifyCommand (*model, index, hack.getData())); - } - ///< \todo provide some kind of feedback to the user, indicating that editing is currently not possible. -} - -void CSVWorld::CommandDelegate::setEditLock (bool locked) -{ - mEditLock = locked; -} +#include "util.hpp" void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) { diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp new file mode 100644 index 000000000..7181dd4d1 --- /dev/null +++ b/apps/opencs/view/world/util.cpp @@ -0,0 +1,57 @@ + +#include "util.hpp" + +#include + +#include "../../model/world/commands.hpp" + +CSVWorld::NastyTableModelHack::NastyTableModelHack (QAbstractItemModel& model) +: mModel (model) +{} + +int CSVWorld::NastyTableModelHack::rowCount (const QModelIndex & parent) const +{ + return mModel.rowCount (parent); +} + +int CSVWorld::NastyTableModelHack::columnCount (const QModelIndex & parent) const +{ + return mModel.columnCount (parent); +} + +QVariant CSVWorld::NastyTableModelHack::data (const QModelIndex & index, int role) const +{ + return mModel.data (index, role); +} + +bool CSVWorld::NastyTableModelHack::setData ( const QModelIndex &index, const QVariant &value, int role) +{ + mData = value; + return true; +} + +QVariant CSVWorld::NastyTableModelHack::getData() const +{ + return mData; +} + +CSVWorld::CommandDelegate::CommandDelegate (QUndoStack& undoStack, QObject *parent) +: QStyledItemDelegate (parent), mUndoStack (undoStack), mEditLock (false) +{} + +void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemModel *model, + const QModelIndex& index) const +{ + if (!mEditLock) + { + NastyTableModelHack hack (*model); + QStyledItemDelegate::setModelData (editor, &hack, index); + mUndoStack.push (new CSMWorld::ModifyCommand (*model, index, hack.getData())); + } + ///< \todo provide some kind of feedback to the user, indicating that editing is currently not possible. +} + +void CSVWorld::CommandDelegate::setEditLock (bool locked) +{ + mEditLock = locked; +} \ No newline at end of file diff --git a/apps/opencs/view/world/util.hpp b/apps/opencs/view/world/util.hpp new file mode 100644 index 000000000..136583118 --- /dev/null +++ b/apps/opencs/view/world/util.hpp @@ -0,0 +1,50 @@ +#ifndef CSV_WORLD_UTIL_H +#define CSV_WORLD_UTIL_H + +#include +#include + +class QUndoStack; + +namespace CSVWorld +{ + ///< \brief Getting the data out of an editor widget + /// + /// Really, Qt? Really? + class NastyTableModelHack : public QAbstractTableModel + { + QAbstractItemModel& mModel; + QVariant mData; + + public: + + NastyTableModelHack (QAbstractItemModel& model); + + int rowCount (const QModelIndex & parent = QModelIndex()) const; + + int columnCount (const QModelIndex & parent = QModelIndex()) const; + + QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const; + + bool setData (const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + QVariant getData() const; + }; + + ///< \brief Use commands instead of manipulating the model directly + class CommandDelegate : public QStyledItemDelegate + { + QUndoStack& mUndoStack; + bool mEditLock; + + public: + + CommandDelegate (QUndoStack& undoStack, QObject *parent); + + void setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const; + + void setEditLock (bool locked); + }; +} + +#endif From ba9c5f5b4e0c4feeea646de59e2bb9afc08c5b76 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 02:32:21 -0800 Subject: [PATCH 130/378] Don't initially fill buffers in OpenAL_SoundStream::play --- apps/openmw/mwsound/openal_output.cpp | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 658483b02..4bdbf0101 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -230,30 +230,19 @@ OpenAL_SoundStream::~OpenAL_SoundStream() void OpenAL_SoundStream::play() { - std::vector data(mBufferSize); - ALuint count = 0; - alSourceStop(mSource); alSourcei(mSource, AL_BUFFER, 0); throwALerror(); - mSamplesQueued = 0; + for(ALuint i = 0;i < sNumBuffers;i++) - { - size_t got; - got = mDecoder->read(&data[0], data.size()); - alBufferData(mBuffers[i], mFormat, &data[0], got, mSampleRate); - count += getBufferSampleCount(mBuffers[i]); - } + alBufferData(mBuffers[i], mFormat, this, 0, mSampleRate); throwALerror(); alSourceQueueBuffers(mSource, sNumBuffers, mBuffers); alSourcePlay(mSource); throwALerror(); - mSamplesTotal += count; - mSamplesQueued = count; - mIsFinished = false; mOutput.mStreamThread->add(this); } From f067b22b3f6c7c19d3e0cabb9b89b36b8975c287 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 02:33:35 -0800 Subject: [PATCH 131/378] Use a recursive mutex for the OpenAL stream thread --- apps/openmw/mwsound/openal_output.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 4bdbf0101..c7b16dd39 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -125,7 +125,7 @@ const ALfloat OpenAL_SoundStream::sBufferLength = 0.125f; struct OpenAL_Output::StreamThread { typedef std::vector StreamVec; StreamVec mStreams; - boost::mutex mMutex; + boost::recursive_mutex mMutex; boost::thread mThread; StreamThread() From d2fbae9760aeaab5b7be705ff736714937ff4ff3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 02:52:37 -0800 Subject: [PATCH 132/378] Init and deinit the VideoState synchronously, and re-enable audio playback --- apps/openmw/mwrender/videoplayer.cpp | 252 ++++++++++++++------------- apps/openmw/mwrender/videoplayer.hpp | 3 +- 2 files changed, 133 insertions(+), 122 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index fa179f8b1..0268539d5 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -68,14 +68,14 @@ namespace MWRender void packet_queue_init(PacketQueue *q) { memset(q, 0, sizeof(PacketQueue)); } - int packet_queue_put(PacketQueue *q, AVPacket *pkt) { + int packet_queue_put(PacketQueue *q, AVPacket *pkt) + { AVPacketList *pkt1; - if(av_dup_packet(pkt) < 0) { + if(av_dup_packet(pkt) < 0) return -1; - } + pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); - if (!pkt1) - return -1; + if(!pkt1) return -1; pkt1->pkt = *pkt; pkt1->next = NULL; @@ -92,21 +92,24 @@ namespace MWRender q->mutex.unlock (); return 0; } - static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) { + static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) + { AVPacketList *pkt1; int ret; boost::unique_lock lock(q->mutex); - for(;;) { - - if(global_video_state->quit) { + for(;;) + { + if(global_video_state->quit) + { ret = -1; break; } pkt1 = q->first_pkt; - if (pkt1) { + if (pkt1) + { q->first_pkt = pkt1->next; if (!q->first_pkt) q->last_pkt = NULL; @@ -116,15 +119,17 @@ namespace MWRender av_free(pkt1); ret = 1; break; - } else if (!block) { + } + + if (!block) + { ret = 0; break; - } else { - - - q->cond.wait(lock); } + + q->cond.wait(lock); } + return ret; } @@ -673,17 +678,15 @@ public: // Get a pointer to the codec context for the video stream codecCtx = pFormatCtx->streams[stream_index]->codec; - - if(codecCtx->codec_type == AVMEDIA_TYPE_AUDIO) - return -1; - codec = avcodec_find_decoder(codecCtx->codec_id); - if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) { + if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) + { fprintf(stderr, "Unsupported codec!\n"); return -1; } - switch(codecCtx->codec_type) { + switch(codecCtx->codec_type) + { case AVMEDIA_TYPE_AUDIO: is->audioStream = stream_index; is->audio_st = pFormatCtx->streams[stream_index]; @@ -701,7 +704,15 @@ public: decoder.reset(new MovieAudioDecoder(is)); is->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); + if(!is->AudioTrack) + { + is->audioStream = -1; + avcodec_close(is->audio_st->codec); + is->audio_st = NULL; + return -1; + } break; + case AVMEDIA_TYPE_VIDEO: is->videoStream = stream_index; is->video_st = pFormatCtx->streams[stream_index]; @@ -711,11 +722,12 @@ public: is->video_current_pts_time = av_gettime(); packet_queue_init(&is->videoq); - is->video_thread = boost::thread(video_thread, is); + codecCtx->get_buffer = our_get_buffer; codecCtx->release_buffer = our_release_buffer; - + is->video_thread = boost::thread(video_thread, is); break; + default: break; } @@ -732,118 +744,46 @@ public: VideoState *is = (VideoState *)arg; try { - AVFormatContext *pFormatCtx = avformat_alloc_context (); + AVFormatContext *pFormatCtx = is->format_ctx; AVPacket pkt1, *packet = &pkt1; - int video_index = -1; - int audio_index = -1; - unsigned int i; - - is->videoStream=-1; - is->audioStream=-1; - is->quit = 0; - - Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton ().openResource (is->resourceName); - if(stream.isNull ()) - throw std::runtime_error("Failed to open video resource"); - is->stream = stream; - - AVIOContext *ioContext = 0; - - ioContext = avio_alloc_context(NULL, 0, 0, is, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if (!ioContext) - throw std::runtime_error("Failed to allocate ioContext "); - - pFormatCtx->pb = ioContext; - - global_video_state = is; - // will interrupt blocking functions if we quit! - //url_set_interrupt_cb(decode_interrupt_cb); - - // Open video file - /// \todo leak here, ffmpeg or valgrind bug ? - if (avformat_open_input(&pFormatCtx, is->resourceName.c_str(), NULL, NULL)) - throw std::runtime_error("Failed to open video input"); - - // Retrieve stream information - if(avformat_find_stream_info(pFormatCtx, NULL)<0) - throw std::runtime_error("Failed to retrieve stream information"); - - // Dump information about file onto standard error - av_dump_format(pFormatCtx, 0, is->resourceName.c_str(), 0); - - for(i=0; inb_streams; i++) { - if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO && - video_index < 0) { - video_index=i; - } - if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO && - audio_index < 0) { - audio_index=i; - } - } - - if(audio_index >= 0) { - stream_component_open(is, audio_index, pFormatCtx); - } - if(video_index >= 0) { - stream_component_open(is, video_index, pFormatCtx); - } - if(is->videoStream >= 0 /*|| is->audioStream < 0*/) { - // main decode loop - - for(;;) { - if(is->quit) { + for(;;) + { + if(is->quit) break; - } - if( (is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || - is->videoq.size > MAX_VIDEOQ_SIZE) { + + if((is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || + is->videoq.size > MAX_VIDEOQ_SIZE) + { boost::this_thread::sleep(boost::posix_time::milliseconds(10)); continue; } - if(av_read_frame(pFormatCtx, packet) < 0) { + + if(av_read_frame(pFormatCtx, packet) < 0) break; - } + // Is this a packet from the video stream? - if(packet->stream_index == is->videoStream) { + if(packet->stream_index == is->videoStream) packet_queue_put(&is->videoq, packet); - } else if(packet->stream_index == is->audioStream) { + else if(packet->stream_index == is->audioStream) packet_queue_put(&is->audioq, packet); - } else { + else av_free_packet(packet); - } } /* all done - wait for it */ - while(!is->quit) { + while(!is->quit) + { // EOF reached, all packets processed, we can exit now - if (is->audioq.nb_packets == 0 && is->videoq.nb_packets == 0) + if(is->audioq.nb_packets == 0 && is->videoq.nb_packets == 0) break; boost::this_thread::sleep(boost::posix_time::milliseconds(100)); } } - is->quit = 1; - - is->audioq.cond.notify_one (); - is->videoq.cond.notify_one (); - - is->video_thread.join(); - - if (is->audioStream >= 0) - avcodec_close(is->audio_st->codec); - if (is->videoStream >= 0) - avcodec_close(is->video_st->codec); - - sws_freeContext (is->sws_context); - - avformat_close_input(&pFormatCtx); - pFormatCtx = NULL; - - av_free(ioContext); } catch (std::runtime_error& e) { @@ -859,6 +799,73 @@ public: return 0; } + void init_state(VideoState *is, const std::string& resourceName) + { + int video_index = -1; + int audio_index = -1; + unsigned int i; + + is->videoStream = -1; + is->audioStream = -1; + is->quit = 0; + + is->stream = Ogre::ResourceGroupManager::getSingleton ().openResource(resourceName); + if(is->stream.isNull()) + throw std::runtime_error("Failed to open video resource"); + + is->format_ctx->pb = avio_alloc_context(NULL, 0, 0, is, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if(!is->format_ctx->pb) + throw std::runtime_error("Failed to allocate ioContext "); + + global_video_state = is; + // will interrupt blocking functions if we quit! + //url_set_interrupt_cb(decode_interrupt_cb); + + // Open video file + /// \todo leak here, ffmpeg or valgrind bug ? + if (avformat_open_input(&is->format_ctx, resourceName.c_str(), NULL, NULL)) + throw std::runtime_error("Failed to open video input"); + + // Retrieve stream information + if(avformat_find_stream_info(is->format_ctx, NULL) < 0) + throw std::runtime_error("Failed to retrieve stream information"); + + // Dump information about file onto standard error + av_dump_format(is->format_ctx, 0, resourceName.c_str(), 0); + + for(i = 0;i < is->format_ctx->nb_streams;i++) + { + if(is->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) + video_index = i; + if(is->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) + audio_index = i; + } + + if(audio_index >= 0) + stream_component_open(is, audio_index, is->format_ctx); + if(video_index >= 0) + stream_component_open(is, video_index, is->format_ctx); + } + + void deinit_state(VideoState *is) + { + is->audioq.cond.notify_one (); + is->videoq.cond.notify_one (); + + is->parse_thread.join(); + is->video_thread.join(); + + if(is->audioStream >= 0) + avcodec_close(is->audio_st->codec); + if(is->videoStream >= 0) + avcodec_close(is->video_st->codec); + + sws_freeContext(is->sws_context); + + AVIOContext *ioContext = is->format_ctx->pb; + avformat_close_input(&is->format_ctx); + av_free(ioContext); + } VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) : mState(NULL) @@ -919,22 +926,27 @@ public: mState->refresh = 0; mState->resourceName = resourceName; + mState->av_sync_type = DEFAULT_AV_SYNC_TYPE; + mState->format_ctx = avformat_alloc_context(); schedule_refresh(mState, 40); - mState->av_sync_type = DEFAULT_AV_SYNC_TYPE; + init_state(mState, resourceName); mState->parse_thread = boost::thread(decode_thread, mState); } void VideoPlayer::update () { - if (mState && mState->refresh) + if(mState) { - video_refresh_timer (mState); - mState->refresh--; + if(mState->quit) + close(); + else if(mState->refresh) + { + video_refresh_timer(mState); + mState->refresh--; + } } - if (mState && mState->quit) - close(); if (mState && mState->display_ready && !Ogre::TextureManager::getSingleton ().getByName ("VideoTexture").isNull ()) mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("VideoTexture"); @@ -945,8 +957,7 @@ public: void VideoPlayer::close() { mState->quit = 1; - - mState->parse_thread.join (); + deinit_state(mState); delete mState; mState = NULL; @@ -964,5 +975,4 @@ public: { return mState != NULL; } - } diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index eac906356..e97fa6e13 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -62,7 +62,7 @@ namespace MWRender audio_pkt_data(NULL), audio_pkt_size(0), audio_diff_cum(0), audio_diff_avg_coef(0), audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), frame_last_delay(0), video_clock(0), video_current_pts(0), video_current_pts_time(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), - pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), sws_context(NULL), display_ready(0) + pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) {} @@ -108,6 +108,7 @@ namespace MWRender MWBase::SoundPtr AudioTrack; + AVFormatContext* format_ctx; SwsContext* sws_context; VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE]; From 7e8b844b2e46ca1b4b58acb5aa65313792aaa403 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 03:05:37 -0800 Subject: [PATCH 133/378] Clean up some unused code --- apps/openmw/mwrender/videoplayer.cpp | 92 ++++++++++++---------------- 1 file changed, 39 insertions(+), 53 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 0268539d5..d4fa7688f 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -393,20 +393,9 @@ public: return total; } - }; - /* - static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) { - SDL_Event event; - event.type = FF_REFRESH_EVENT; - event.user.data1 = opaque; - SDL_PushEvent(&event); - return 0; // 0 means stop timer - } - */ - void timer_callback (boost::system_time t, VideoState* is) { boost::this_thread::sleep (t); @@ -416,8 +405,6 @@ public: /* schedule a video refresh in 'delay' ms */ static void schedule_refresh(VideoState *is, int delay) { - //SDL_AddTimer(delay, sdl_refresh_timer_cb, is); - //is->refresh_queue.push_back (delay); boost::system_time t = boost::get_system_time() + boost::posix_time::milliseconds(delay); boost::thread (boost::bind(&timer_callback, t, is)).detach(); } @@ -460,10 +447,12 @@ public: VideoPicture *vp; double actual_delay, delay, sync_threshold, ref_clock, diff; - if(is->video_st) { - if(is->pictq_size == 0) { + if(is->video_st) + { + if(is->pictq_size == 0) schedule_refresh(is, 1); - } else { + else + { vp = &is->pictq[is->pictq_rindex]; is->video_current_pts = vp->pts; @@ -479,26 +468,28 @@ public: is->frame_last_pts = vp->pts; /* update delay to sync to audio if not master source */ - if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) { + if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) + { ref_clock = get_master_clock(is); diff = vp->pts - ref_clock; /* Skip or repeat the frame. Take delay into account FFPlay still doesn't "know if this is the best guess." */ sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; - if(fabs(diff) < AV_NOSYNC_THRESHOLD) { - if(diff <= -sync_threshold) { + if(fabs(diff) < AV_NOSYNC_THRESHOLD) + { + if(diff <= -sync_threshold) delay = 0; - } else if(diff >= sync_threshold) { + else if(diff >= sync_threshold) delay = 2 * delay; - } } } is->frame_timer += delay; /* computer the REAL delay */ actual_delay = is->frame_timer - (av_gettime() / 1000000.0); - if(actual_delay < 0.010) { + if(actual_delay < 0.010) + { /* Really it should skip the picture instead */ actual_delay = 0.010; } @@ -508,18 +499,16 @@ public: video_display(is); /* update queue for next picture! */ - if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) { + if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) is->pictq_rindex = 0; - } is->pictq_mutex.lock(); is->pictq_size--; is->pictq_cond.notify_one (); is->pictq_mutex.unlock (); } } - else { + else schedule_refresh(is, 100); - } } int queue_picture(VideoState *is, AVFrame *pFrame, double pts) @@ -529,9 +518,8 @@ public: /* wait until we have a new pic */ { boost::unique_lock lock(is->pictq_mutex); - while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is->quit) { + while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is->quit) is->pictq_cond.timed_wait(lock, boost::posix_time::milliseconds(1)); - } } if(is->quit) @@ -541,7 +529,8 @@ public: vp = &is->pictq[is->pictq_windex]; // Convert the image into YUV format that SDL uses - if(is->sws_context == NULL) { + if(is->sws_context == NULL) + { int w = is->video_st->codec->width; int h = is->video_st->codec->height; is->sws_context = sws_getContext(w, h, is->video_st->codec->pix_fmt, @@ -556,13 +545,11 @@ public: sws_scale(is->sws_context, pFrame->data, pFrame->linesize, 0, is->video_st->codec->height, &vp->data, is->rgbaFrame->linesize); - vp->pts = pts; // now we inform our display thread that we have a pic ready - if(++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) { + if(++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) is->pictq_windex = 0; - } is->pictq_mutex.lock(); is->pictq_size++; is->pictq_mutex.unlock(); @@ -574,10 +561,13 @@ public: { double frame_delay; - if(pts != 0) { + if(pts != 0) + { /* if we have pts, set video clock to it */ is->video_clock = pts; - } else { + } + else + { /* if we aren't given a pts, set it to the clock */ pts = is->video_clock; } @@ -619,11 +609,12 @@ public: pFrame = avcodec_alloc_frame(); is->rgbaFrame = avcodec_alloc_frame(); - avpicture_alloc ((AVPicture *)is->rgbaFrame, PIX_FMT_RGBA, is->video_st->codec->width, is->video_st->codec->height); - + avpicture_alloc((AVPicture*)is->rgbaFrame, PIX_FMT_RGBA, is->video_st->codec->width, is->video_st->codec->height); - for(;;) { - if(packet_queue_get(&is->videoq, packet, 1) < 0) { + for(;;) + { + if(packet_queue_get(&is->videoq, packet, 1) < 0) + { // means we quit getting packets break; } @@ -632,28 +623,24 @@ public: // Save global pts to be stored in pFrame global_video_pkt_pts = packet->pts; // Decode video frame - if (avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, - packet) < 0) - { + if (avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, packet) < 0) throw std::runtime_error("Error decoding video frame"); - } - if((uint64_t)packet->dts == AV_NOPTS_VALUE - && pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) { + + if((uint64_t)packet->dts == AV_NOPTS_VALUE && + pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) pts = *(uint64_t *)pFrame->opaque; - } else if((uint64_t)packet->dts != AV_NOPTS_VALUE) { + else if((uint64_t)packet->dts != AV_NOPTS_VALUE) pts = packet->dts; - } else { + else pts = 0; - } pts *= av_q2d(is->video_st->time_base); - // Did we get a video frame? - if(frameFinished) { + if(frameFinished) + { pts = synchronize_video(is, pFrame, pts); - if(queue_picture(is, pFrame, pts) < 0) { + if(queue_picture(is, pFrame, pts) < 0) break; - } } av_free_packet(packet); } @@ -672,9 +659,8 @@ public: AVCodecContext *codecCtx; AVCodec *codec; - if(stream_index < 0 || stream_index >= static_cast(pFormatCtx->nb_streams)) { + if(stream_index < 0 || stream_index >= static_cast(pFormatCtx->nb_streams)) return -1; - } // Get a pointer to the codec context for the video stream codecCtx = pFormatCtx->streams[stream_index]->codec; From f7ff8b33741b4a7fbf30d1e89d19b24253ec4a53 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 03:13:44 -0800 Subject: [PATCH 134/378] A bit more cleanup --- apps/openmw/mwrender/videoplayer.cpp | 16 +++++++--------- apps/openmw/mwrender/videoplayer.hpp | 3 +-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index d4fa7688f..94f40e0e9 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -791,8 +791,11 @@ public: int audio_index = -1; unsigned int i; + is->av_sync_type = DEFAULT_AV_SYNC_TYPE; + is->format_ctx = avformat_alloc_context(); is->videoStream = -1; is->audioStream = -1; + is->refresh = 0; is->quit = 0; is->stream = Ogre::ResourceGroupManager::getSingleton ().openResource(resourceName); @@ -886,6 +889,9 @@ public: void VideoPlayer::playVideo (const std::string &resourceName) { + // Register all formats and codecs + av_register_all(); + if (mState) close(); @@ -907,17 +913,9 @@ public: mState = new VideoState; - // Register all formats and codecs - av_register_all(); - - mState->refresh = 0; - mState->resourceName = resourceName; - mState->av_sync_type = DEFAULT_AV_SYNC_TYPE; - mState->format_ctx = avformat_alloc_context(); + init_state(mState, resourceName); schedule_refresh(mState, 40); - - init_state(mState, resourceName); mState->parse_thread = boost::thread(decode_thread, mState); } diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index e97fa6e13..2ce58846f 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -121,8 +121,7 @@ namespace MWRender boost::thread parse_thread; boost::thread video_thread; - std::string resourceName; - int quit; + volatile int quit; int refresh; int display_ready; From 600494eed8ee8037f4a02f3ef11287347fecfe39 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 03:37:04 -0800 Subject: [PATCH 135/378] More cleanup of unused code --- apps/openmw/mwrender/videoplayer.cpp | 57 ++++++++++++---------------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 94f40e0e9..df4d7c8ed 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -59,15 +59,9 @@ namespace MWRender } + void packet_queue_init(PacketQueue *q) + { memset(q, 0, sizeof(PacketQueue)); } - - /* Since we only have one decoding thread, the Big Struct - can be global in case we need it. */ - VideoState *global_video_state; - - void packet_queue_init(PacketQueue *q) { - memset(q, 0, sizeof(PacketQueue)); - } int packet_queue_put(PacketQueue *q, AVPacket *pkt) { AVPacketList *pkt1; @@ -92,7 +86,8 @@ namespace MWRender q->mutex.unlock (); return 0; } - static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) + + static int packet_queue_get(PacketQueue *q, AVPacket *pkt, VideoState *is, int block) { AVPacketList *pkt1; int ret; @@ -101,7 +96,7 @@ namespace MWRender for(;;) { - if(global_video_state->quit) + if(is->quit) { ret = -1; break; @@ -133,7 +128,8 @@ namespace MWRender return ret; } - void PacketQueue::flush() { + void PacketQueue::flush() + { AVPacketList *pkt, *pkt1; this->mutex.lock(); @@ -149,6 +145,7 @@ namespace MWRender this->mutex.unlock (); } + double get_audio_clock(VideoState *is) { return is->AudioTrack->getTimeOffset(); @@ -161,17 +158,19 @@ namespace MWRender delta = (av_gettime() - is->video_current_pts_time) / 1000000.0; return is->video_current_pts + delta; } - double get_external_clock(VideoState *is) { + + double get_external_clock(VideoState *is) + { return av_gettime() / 1000000.0; } - double get_master_clock(VideoState *is) { - if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) { + + double get_master_clock(VideoState *is) + { + if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) return get_video_clock(is); - } else if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) { + if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) return get_audio_clock(is); - } else { - return get_external_clock(is); - } + return get_external_clock(is); } class MovieAudioDecoder : public MWSound::Sound_Decoder @@ -296,7 +295,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder return -1; /* next packet */ - if(packet_queue_get(&is->audioq, pkt, 1) < 0) + if(packet_queue_get(&is->audioq, pkt, is, 1) < 0) return -1; is->audio_pkt_data = pkt->data; @@ -328,7 +327,7 @@ public: *type = MWSound::SampleType_Int16; else fail(std::string("Unsupported sample format: ")+ - av_get_sample_fmt_name(is->audio_st->codec->sample_fmt)); + av_get_sample_fmt_name(is->audio_st->codec->sample_fmt)); if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_MONO) *chans = MWSound::ChannelConfig_Mono; @@ -382,8 +381,8 @@ public: is->audio_buf_index = 0; } - size_t len1 = std::min(is->audio_buf_size - is->audio_buf_index, - len - total); + size_t len1 = std::min(is->audio_buf_size - is->audio_buf_index, + len - total); memcpy(stream, (uint8_t*)is->audio_buf + is->audio_buf_index, len1); total += len1; @@ -398,7 +397,7 @@ public: void timer_callback (boost::system_time t, VideoState* is) { - boost::this_thread::sleep (t); + boost::this_thread::sleep(t); is->refresh++; } @@ -406,7 +405,7 @@ public: static void schedule_refresh(VideoState *is, int delay) { boost::system_time t = boost::get_system_time() + boost::posix_time::milliseconds(delay); - boost::thread (boost::bind(&timer_callback, t, is)).detach(); + boost::thread(boost::bind(&timer_callback, t, is)).detach(); } void video_display(VideoState *is) @@ -613,7 +612,7 @@ public: for(;;) { - if(packet_queue_get(&is->videoq, packet, 1) < 0) + if(packet_queue_get(&is->videoq, packet, is, 1) < 0) { // means we quit getting packets break; @@ -721,10 +720,6 @@ public: return 0; } - int decode_interrupt_cb(void) { - return (global_video_state && global_video_state->quit); - } - int decode_thread(void *arg) { VideoState *is = (VideoState *)arg; @@ -806,10 +801,6 @@ public: if(!is->format_ctx->pb) throw std::runtime_error("Failed to allocate ioContext "); - global_video_state = is; - // will interrupt blocking functions if we quit! - //url_set_interrupt_cb(decode_interrupt_cb); - // Open video file /// \todo leak here, ffmpeg or valgrind bug ? if (avformat_open_input(&is->format_ctx, resourceName.c_str(), NULL, NULL)) From 1ea1407707c4651bcbd94eb2119e27bfde364cd4 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 04:10:19 -0800 Subject: [PATCH 136/378] Support quad, 5.1, and 7.1 with OpenAL and ffmpeg The other decoders don't guarantee any channel ordering, which makes them useless. --- apps/openmw/mwrender/videoplayer.cpp | 6 ++++++ apps/openmw/mwsound/ffmpeg_decoder.cpp | 6 ++++++ apps/openmw/mwsound/openal_output.cpp | 28 +++++++++++++++++++++++++ apps/openmw/mwsound/sound_decoder.hpp | 5 ++++- apps/openmw/mwsound/soundmanagerimp.cpp | 14 +++++++++---- 5 files changed, 54 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index df4d7c8ed..6e6db5f27 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -333,6 +333,12 @@ public: *chans = MWSound::ChannelConfig_Mono; else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_STEREO) *chans = MWSound::ChannelConfig_Stereo; + else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_QUAD) + *chans = MWSound::ChannelConfig_Quad; + else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_5POINT1) + *chans = MWSound::ChannelConfig_5point1; + else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_7POINT1) + *chans = MWSound::ChannelConfig_7point1; else if(is->audio_st->codec->channel_layout == 0) { /* Unknown channel layout. Try to guess. */ diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 6b3a7f9cd..9fee0d9b0 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -347,6 +347,12 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType * *chans = ChannelConfig_Mono; else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_STEREO) *chans = ChannelConfig_Stereo; + else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_QUAD) + *chans = ChannelConfig_Quad; + else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1) + *chans = ChannelConfig_5point1; + else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1) + *chans = ChannelConfig_7point1; else if(stream->mCodecCtx->channel_layout == 0) { /* Unknown channel layout. Try to guess. */ diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index c7b16dd39..fd4fdaa9e 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -61,6 +61,34 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type) if(fmtlist[i].chans == chans && fmtlist[i].type == type) return fmtlist[i].format; } + + if(alIsExtensionPresent("AL_EXT_MCFORMATS")) + { + static const struct { + char name[32]; + ChannelConfig chans; + SampleType type; + } mcfmtlist[] = { + { "AL_FORMAT_QUAD16", ChannelConfig_Quad, SampleType_Int16 }, + { "AL_FORMAT_QUAD8", ChannelConfig_Quad, SampleType_UInt8 }, + { "AL_FORMAT_51CHN16", ChannelConfig_5point1, SampleType_Int16 }, + { "AL_FORMAT_51CHN8", ChannelConfig_5point1, SampleType_UInt8 }, + { "AL_FORMAT_71CHN16", ChannelConfig_7point1, SampleType_Int16 }, + { "AL_FORMAT_71CHN8", ChannelConfig_7point1, SampleType_UInt8 }, + }; + static const size_t mcfmtlistsize = sizeof(mcfmtlist)/sizeof(mcfmtlist[0]); + + for(size_t i = 0;i < mcfmtlistsize;i++) + { + if(mcfmtlist[i].chans == chans && mcfmtlist[i].type == type) + { + ALenum format = alGetEnumValue(mcfmtlist[i].name); + if(format != 0 && format != -1) + return format; + } + } + } + fail(std::string("Unsupported sound format (")+getChannelConfigName(chans)+", "+getSampleTypeName(type)+")"); return AL_NONE; } diff --git a/apps/openmw/mwsound/sound_decoder.hpp b/apps/openmw/mwsound/sound_decoder.hpp index d228a5e98..f78a1cd28 100644 --- a/apps/openmw/mwsound/sound_decoder.hpp +++ b/apps/openmw/mwsound/sound_decoder.hpp @@ -15,7 +15,10 @@ namespace MWSound enum ChannelConfig { ChannelConfig_Mono, - ChannelConfig_Stereo + ChannelConfig_Stereo, + ChannelConfig_Quad, + ChannelConfig_5point1, + ChannelConfig_7point1 }; const char *getChannelConfigName(ChannelConfig config); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 6bb3d59dd..efdb6f7ea 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -622,8 +622,11 @@ namespace MWSound { switch(config) { - case ChannelConfig_Mono: return "Mono"; - case ChannelConfig_Stereo: return "Stereo"; + case ChannelConfig_Mono: return "Mono"; + case ChannelConfig_Stereo: return "Stereo"; + case ChannelConfig_Quad: return "Quad"; + case ChannelConfig_5point1: return "5.1 Surround"; + case ChannelConfig_7point1: return "7.1 Surround"; } return "(unknown channel config)"; } @@ -632,8 +635,11 @@ namespace MWSound { switch(config) { - case ChannelConfig_Mono: frames *= 1; break; - case ChannelConfig_Stereo: frames *= 2; break; + case ChannelConfig_Mono: frames *= 1; break; + case ChannelConfig_Stereo: frames *= 2; break; + case ChannelConfig_Quad: frames *= 4; break; + case ChannelConfig_5point1: frames *= 6; break; + case ChannelConfig_7point1: frames *= 8; break; } switch(type) { From cab68df2574eec34254640770ca371450b341f42 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 04:25:41 -0800 Subject: [PATCH 137/378] Use the decoded frame pts when available --- apps/openmw/mwrender/videoplayer.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 6e6db5f27..2d43e8ddc 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -631,11 +631,12 @@ public: if (avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, packet) < 0) throw std::runtime_error("Error decoding video frame"); - if((uint64_t)packet->dts == AV_NOPTS_VALUE && - pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) + if((uint64_t)pFrame->pts != AV_NOPTS_VALUE) + pts = pFrame->pts; + else if((uint64_t)packet->pts != AV_NOPTS_VALUE) + pts = packet->pts; + else if(pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) pts = *(uint64_t *)pFrame->opaque; - else if((uint64_t)packet->dts != AV_NOPTS_VALUE) - pts = packet->dts; else pts = 0; pts *= av_q2d(is->video_st->time_base); From 3d4c8c5444ae35b45537a1d877270befa62de4b9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Dec 2012 13:35:08 +0100 Subject: [PATCH 138/378] added dialogue sub view (editing of a single record; not functional yet); fixed a bug in Universal to string conversion --- apps/opencs/CMakeLists.txt | 2 ++ apps/opencs/model/world/columns.hpp | 18 ++++++++++++++++ apps/opencs/model/world/data.cpp | 1 + apps/opencs/model/world/universalid.cpp | 5 +++-- apps/opencs/model/world/universalid.hpp | 2 ++ apps/opencs/view/world/dialoguesubview.cpp | 14 +++++++++++++ apps/opencs/view/world/dialoguesubview.hpp | 24 ++++++++++++++++++++++ apps/opencs/view/world/subviews.cpp | 4 ++++ apps/opencs/view/world/table.cpp | 9 +++++++- apps/opencs/view/world/table.hpp | 2 ++ apps/opencs/view/world/tablesubview.cpp | 7 +++++++ apps/opencs/view/world/tablesubview.hpp | 8 +++++++- 12 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 apps/opencs/view/world/dialoguesubview.cpp create mode 100644 apps/opencs/view/world/dialoguesubview.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 554607c73..1cfb5d9a9 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -14,6 +14,7 @@ set (OPENCS_SRC view/doc/subview.cpp view/world/table.cpp view/world/tablesubview.cpp view/world/subviews.cpp view/world/util.cpp + view/world/dialoguesubview.cpp view/tools/reportsubview.cpp view/tools/subviews.cpp ) @@ -34,6 +35,7 @@ set (OPENCS_HDR view/doc/subview.hpp view/doc/subviewfactoryimp.hpp view/world/table.hpp view/world/tablesubview.hpp view/world/subviews.hpp view/world/util.hpp + view/world/dialoguesubview.hpp view/tools/reportsubview.hpp view/tools/subviews.hpp ) diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 188d3a2ac..0d2d42158 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -67,6 +67,24 @@ namespace CSMWorld return true; } }; + + template + struct FixedRecordTypeColumn : public Column + { + int mType; + + FixedRecordTypeColumn (int type) : Column ("Type"), mType (type) {} + + virtual QVariant get (const Record& record) const + { + return mType; + } + + virtual bool isEditable() const + { + return false; + } + }; } #endif \ No newline at end of file diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index f350299ec..0da0cba83 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -14,6 +14,7 @@ CSMWorld::Data::Data() { mGlobals.addColumn (new StringIdColumn); mGlobals.addColumn (new RecordStateColumn); + mGlobals.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Global)); mGlobals.addColumn (new FloatValueColumn); mModels.insert (std::make_pair ( diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 81561bcf4..d8775643a 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -24,6 +24,7 @@ namespace static const TypeData sIdArg[] = { + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker }; @@ -208,8 +209,8 @@ std::string CSMWorld::UniversalId::toString() const switch (mArgumentType) { case ArgumentType_None: break; - case ArgumentType_Id: stream << ": " << mId; - case ArgumentType_Index: stream << ": " << mIndex; + case ArgumentType_Id: stream << ": " << mId; break; + case ArgumentType_Index: stream << ": " << mIndex; break; } return stream.str(); diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 8f43bf083..4a73feb12 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -36,6 +36,8 @@ namespace CSMWorld Type_Globals, + Type_Global, + Type_VerificationResults }; diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp new file mode 100644 index 000000000..e2d16f1e3 --- /dev/null +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -0,0 +1,14 @@ + +#include "dialoguesubview.hpp" + +CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, + bool createAndDelete) +: SubView (id) +{ + +} + +void CSVWorld::DialogueSubView::setEditLock (bool locked) +{ + +} \ No newline at end of file diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp new file mode 100644 index 000000000..c57dab108 --- /dev/null +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -0,0 +1,24 @@ +#ifndef CSV_WORLD_DIALOGUESUBVIEW_H +#define CSV_WORLD_DIALOGUESUBVIEW_H + +#include "../doc/subview.hpp" + +namespace CSMDoc +{ + class Document; +} + +namespace CSVWorld +{ + class DialogueSubView : public CSVDoc::SubView + { + + public: + + DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete); + + virtual void setEditLock (bool locked); + }; +} + +#endif \ No newline at end of file diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index fdc0cb31d..080a175ea 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -4,9 +4,13 @@ #include "../doc/subviewfactoryimp.hpp" #include "tablesubview.hpp" +#include "dialoguesubview.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { manager.add (CSMWorld::UniversalId::Type_Globals, new CSVDoc::SubViewFactoryWithCreateFlag (true)); + + manager.add (CSMWorld::UniversalId::Type_Global, + new CSVDoc::SubViewFactoryWithCreateFlag (true)); } \ No newline at end of file diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 3a5ef7c34..6892c7f61 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -2,7 +2,7 @@ #include "table.hpp" #include -#include + #include #include #include @@ -129,6 +129,13 @@ void CSVWorld::Table::setEditLock (bool locked) mEditLock = locked; } +CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const +{ + return CSMWorld::UniversalId ( + static_cast (mProxyModel->data (mProxyModel->index (row, 2)).toInt()), + mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString()); +} + #include /// \todo remove void CSVWorld::Table::createRecord() { diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index b58ba8328..df0224583 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -50,6 +50,8 @@ namespace CSVWorld void setEditLock (bool locked); + CSMWorld::UniversalId getUniversalId (int row) const; + private slots: void createRecord(); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 4abd531d0..bb4bb76c6 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -10,9 +10,16 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D : SubView (id) { setWidget (mTable = new Table (id, document.getData(), document.getUndoStack(), createAndDelete)); + + connect (mTable, SIGNAL (doubleClicked (const QModelIndex&)), this, SLOT (rowActivated (const QModelIndex&))); } void CSVWorld::TableSubView::setEditLock (bool locked) { mTable->setEditLock (locked); +} + +void CSVWorld::TableSubView::rowActivated (const QModelIndex& index) +{ + focusId (mTable->getUniversalId (index.row())); } \ No newline at end of file diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 9b0785c47..0e7b8aa30 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -3,7 +3,7 @@ #include "../doc/subview.hpp" -class QUndoStack; +class QModelIndex; namespace CSMDoc { @@ -16,6 +16,8 @@ namespace CSVWorld class TableSubView : public CSVDoc::SubView { + Q_OBJECT + Table *mTable; public: @@ -23,6 +25,10 @@ namespace CSVWorld TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete); virtual void setEditLock (bool locked); + + private slots: + + void rowActivated (const QModelIndex& index); }; } From 640c218df392442b389b939e35a1ee7259c71caf Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Dec 2012 13:52:26 +0100 Subject: [PATCH 139/378] made record state uneditable again --- apps/opencs/model/world/columns.hpp | 5 +++++ apps/opencs/model/world/idcollection.hpp | 19 +++++++++++++++++++ apps/opencs/model/world/idtable.cpp | 2 +- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 0d2d42158..00c688484 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -66,6 +66,11 @@ namespace CSMWorld { return true; } + + virtual bool isUserEditable() const + { + return false; + } }; template diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 50afa715e..529e474d5 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -32,8 +32,17 @@ namespace CSMWorld } virtual bool isEditable() const = 0; + + virtual bool isUserEditable() const; + ///< Can this column be edited directly by the user? }; + template + bool Column::isUserEditable() const + { + return isEditable(); + } + class IdCollectionBase { // not implemented @@ -62,6 +71,8 @@ namespace CSMWorld virtual bool isEditable (int column) const = 0; + virtual bool isUserEditable (int column) const = 0; + virtual void merge() = 0; ///< Merge modified into base. @@ -129,6 +140,8 @@ namespace CSMWorld virtual bool isEditable (int column) const; + virtual bool isUserEditable (int column) const; + virtual void merge(); ///< Merge modified into base. @@ -250,6 +263,12 @@ namespace CSMWorld return mColumns.at (column)->isEditable(); } + template + bool IdCollection::isUserEditable (int column) const + { + return mColumns.at (column)->isUserEditable(); + } + template void IdCollection::addColumn (Column *column) { diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 3fe1fadfe..3ec2f3d17 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -70,7 +70,7 @@ Qt::ItemFlags CSMWorld::IdTable::flags (const QModelIndex & index) const { Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; - if (mIdCollection->isEditable (index.column())) + if (mIdCollection->isUserEditable (index.column())) flags |= Qt::ItemIsEditable; return flags; From 0a5ab977b75ddd246b8fd37d8a8a0b545d114b03 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 05:04:53 -0800 Subject: [PATCH 140/378] Use the decoder's sample offset for calculating the stream offset --- apps/openmw/mwrender/videoplayer.cpp | 5 +++++ apps/openmw/mwsound/audiere_decoder.cpp | 5 +++++ apps/openmw/mwsound/audiere_decoder.hpp | 1 + apps/openmw/mwsound/ffmpeg_decoder.cpp | 14 ++++++++++++-- apps/openmw/mwsound/ffmpeg_decoder.hpp | 2 ++ apps/openmw/mwsound/mpgsnd_decoder.cpp | 5 +++++ apps/openmw/mwsound/mpgsnd_decoder.hpp | 1 + apps/openmw/mwsound/openal_output.cpp | 11 +++-------- apps/openmw/mwsound/sound_decoder.hpp | 1 + 9 files changed, 35 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 2d43e8ddc..bd29c69bf 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -398,6 +398,11 @@ public: return total; } + + size_t getSampleOffset() + { + return (size_t)(is->audio_clock*is->audio_st->codec->sample_rate); + } }; diff --git a/apps/openmw/mwsound/audiere_decoder.cpp b/apps/openmw/mwsound/audiere_decoder.cpp index c9b3d11d1..788d5ae40 100644 --- a/apps/openmw/mwsound/audiere_decoder.cpp +++ b/apps/openmw/mwsound/audiere_decoder.cpp @@ -117,6 +117,11 @@ void Audiere_Decoder::rewind() mSoundSource->reset(); } +size_t Audiere_Decoder::getSampleOffset() +{ + return 0; +} + Audiere_Decoder::Audiere_Decoder() { } diff --git a/apps/openmw/mwsound/audiere_decoder.hpp b/apps/openmw/mwsound/audiere_decoder.hpp index 1e1528880..8623a3f2c 100644 --- a/apps/openmw/mwsound/audiere_decoder.hpp +++ b/apps/openmw/mwsound/audiere_decoder.hpp @@ -25,6 +25,7 @@ namespace MWSound virtual size_t read(char *buffer, size_t bytes); virtual void rewind(); + virtual size_t getSampleOffset(); Audiere_Decoder& operator=(const Audiere_Decoder &rhs); Audiere_Decoder(const Audiere_Decoder &rhs); diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 9fee0d9b0..6e60a7b9e 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -383,7 +383,11 @@ size_t FFmpeg_Decoder::read(char *buffer, size_t bytes) if(mStreams.empty()) fail("No audio streams"); - return mStreams.front()->readAVAudioData(buffer, bytes); + MyStream *stream = mStreams.front(); + size_t got = stream->readAVAudioData(buffer, bytes); + mSamplesRead += got / av_samples_get_buffer_size(NULL, stream->mCodecCtx->channels, 1, + stream->mCodecCtx->sample_fmt, 1); + return got; } void FFmpeg_Decoder::readAll(std::vector &output) @@ -402,9 +406,15 @@ void FFmpeg_Decoder::rewind() { av_seek_frame(mFormatCtx, -1, 0, 0); std::for_each(mStreams.begin(), mStreams.end(), std::mem_fun(&MyStream::clearPackets)); + mSamplesRead = 0; +} + +size_t FFmpeg_Decoder::getSampleOffset() +{ + return mSamplesRead; } -FFmpeg_Decoder::FFmpeg_Decoder() : mFormatCtx(NULL) +FFmpeg_Decoder::FFmpeg_Decoder() : mFormatCtx(NULL), mSamplesRead(0) { static bool done_init = false; diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index 88115ce3f..ff63edf07 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -25,6 +25,7 @@ namespace MWSound struct MyStream; std::vector mStreams; + size_t mSamplesRead; bool getNextPacket(int streamidx); @@ -42,6 +43,7 @@ namespace MWSound virtual size_t read(char *buffer, size_t bytes); virtual void readAll(std::vector &output); virtual void rewind(); + virtual size_t getSampleOffset(); FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs); FFmpeg_Decoder(const FFmpeg_Decoder &rhs); diff --git a/apps/openmw/mwsound/mpgsnd_decoder.cpp b/apps/openmw/mwsound/mpgsnd_decoder.cpp index 4ec11b349..fb187f844 100644 --- a/apps/openmw/mwsound/mpgsnd_decoder.cpp +++ b/apps/openmw/mwsound/mpgsnd_decoder.cpp @@ -218,6 +218,11 @@ void MpgSnd_Decoder::rewind() } } +size_t MpgSnd_Decoder::getSampleOffset() +{ + return 0; +} + MpgSnd_Decoder::MpgSnd_Decoder() : mSndInfo() , mSndFile(NULL) diff --git a/apps/openmw/mwsound/mpgsnd_decoder.hpp b/apps/openmw/mwsound/mpgsnd_decoder.hpp index 09082c2f4..52c37bb87 100644 --- a/apps/openmw/mwsound/mpgsnd_decoder.hpp +++ b/apps/openmw/mwsound/mpgsnd_decoder.hpp @@ -39,6 +39,7 @@ namespace MWSound virtual size_t read(char *buffer, size_t bytes); virtual void readAll(std::vector &output); virtual void rewind(); + virtual size_t getSampleOffset(); MpgSnd_Decoder& operator=(const MpgSnd_Decoder &rhs); MpgSnd_Decoder(const MpgSnd_Decoder &rhs); diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index fd4fdaa9e..672e52b56 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -122,7 +122,6 @@ class OpenAL_SoundStream : public Sound ALsizei mSampleRate; ALuint mBufferSize; - ALuint mSamplesTotal; ALuint mSamplesQueued; DecoderPtr mDecoder; @@ -215,8 +214,7 @@ private: OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder) - : mOutput(output), mSource(src), mSamplesTotal(0), mSamplesQueued(0), - mDecoder(decoder), mIsFinished(true) + : mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true) { throwALerror(); @@ -286,7 +284,6 @@ void OpenAL_SoundStream::stop() mSamplesQueued = 0; mDecoder->rewind(); - mSamplesTotal = 0; } bool OpenAL_SoundStream::isPlaying() @@ -311,9 +308,9 @@ double OpenAL_SoundStream::getTimeOffset() alGetSourcef(mSource, AL_SEC_OFFSET, &offset); alGetSourcei(mSource, AL_SOURCE_STATE, &state); if(state == AL_PLAYING || state == AL_PAUSED) - t = (double)(mSamplesTotal - mSamplesQueued)/(double)mSampleRate + offset; + t = (double)(mDecoder->getSampleOffset() - mSamplesQueued)/(double)mSampleRate + offset; else - t = (double)mSamplesTotal / (double)mSampleRate; + t = (double)mDecoder->getSampleOffset() / (double)mSampleRate; mOutput.mStreamThread->mMutex.unlock(); throwALerror(); @@ -388,13 +385,11 @@ bool OpenAL_SoundStream::process() mSamplesQueued -= samples_unqueued; mSamplesQueued += samples_queued; - mSamplesTotal += samples_queued; mIsFinished = finished; } catch(std::exception &e) { std::cout<< "Error updating stream \""<getName()<<"\"" < &output); virtual void rewind() = 0; + virtual size_t getSampleOffset() = 0; Sound_Decoder() : mResourceMgr(Ogre::ResourceGroupManager::getSingleton()) { } From faa5ef087463bd25e7e7bee47b6fcbd4f3b2de57 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Dec 2012 14:53:16 +0100 Subject: [PATCH 141/378] rewrote column class --- apps/opencs/model/world/idcollection.cpp | 11 ++++ apps/opencs/model/world/idcollection.hpp | 69 +++++++++++------------- apps/opencs/model/world/idtable.cpp | 17 +++--- 3 files changed, 53 insertions(+), 44 deletions(-) diff --git a/apps/opencs/model/world/idcollection.cpp b/apps/opencs/model/world/idcollection.cpp index fc4bb1ef6..c6469ea98 100644 --- a/apps/opencs/model/world/idcollection.cpp +++ b/apps/opencs/model/world/idcollection.cpp @@ -1,6 +1,17 @@ #include "idcollection.hpp" +CSMWorld::ColumnBase::ColumnBase (const std::string& title, int flags) +: mTitle (title), mFlags (flags) +{} + +CSMWorld::ColumnBase::~ColumnBase() {} + +bool CSMWorld::ColumnBase::isUserEditable() const +{ + return isEditable(); +} + CSMWorld::IdCollectionBase::IdCollectionBase() {} CSMWorld::IdCollectionBase::~IdCollectionBase() {} \ No newline at end of file diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 529e474d5..5d6f52aa0 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -15,21 +15,25 @@ namespace CSMWorld { - template - struct Column + struct ColumnBase { - std::string mTitle; + enum Roles + { + Role_Flags = Qt::UserRole + }; - Column (const std::string& title) : mTitle (title) {} + enum Flags + { + Flag_Table = 1, // column should be displayed in table view + Flag_Dialogue = 2 // column should be displayed in dialogue view + }; - virtual ~Column() {} + std::string mTitle; + int mFlags; - virtual QVariant get (const Record& record) const = 0; + ColumnBase (const std::string& title, int flag); - virtual void set (Record& record, const QVariant& data) - { - throw std::logic_error ("Column " + mTitle + " is not editable"); - } + virtual ~ColumnBase(); virtual bool isEditable() const = 0; @@ -38,10 +42,21 @@ namespace CSMWorld }; template - bool Column::isUserEditable() const + struct Column : public ColumnBase { - return isEditable(); - } + std::string mTitle; + int mFlags; + + Column (const std::string& title, int flags = Flag_Table | Flag_Dialogue) + : ColumnBase (title, flags) {} + + virtual QVariant get (const Record& record) const = 0; + + virtual void set (Record& record, const QVariant& data) + { + throw std::logic_error ("Column " + mTitle + " is not editable"); + } + }; class IdCollectionBase { @@ -63,16 +78,12 @@ namespace CSMWorld virtual int getColumns() const = 0; - virtual std::string getTitle (int column) const = 0; + virtual const ColumnBase& getColumn (int column) const = 0; virtual QVariant getData (int index, int column) const = 0; virtual void setData (int index, int column, const QVariant& data) = 0; - virtual bool isEditable (int column) const = 0; - - virtual bool isUserEditable (int column) const = 0; - virtual void merge() = 0; ///< Merge modified into base. @@ -136,11 +147,7 @@ namespace CSMWorld virtual void setData (int index, int column, const QVariant& data); - virtual std::string getTitle (int column) const; - - virtual bool isEditable (int column) const; - - virtual bool isUserEditable (int column) const; + virtual const ColumnBase& getColumn (int column) const; virtual void merge(); ///< Merge modified into base. @@ -252,21 +259,9 @@ namespace CSMWorld } template - std::string IdCollection::getTitle (int column) const - { - return mColumns.at (column)->mTitle; - } - - template - bool IdCollection::isEditable (int column) const - { - return mColumns.at (column)->isEditable(); - } - - template - bool IdCollection::isUserEditable (int column) const + const ColumnBase& IdCollection::getColumn (int column) const { - return mColumns.at (column)->isUserEditable(); + return *mColumns.at (column); } template diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 3ec2f3d17..2815e318c 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -34,7 +34,7 @@ QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const if (role!=Qt::DisplayRole && role!=Qt::EditRole) return QVariant(); - if (role==Qt::EditRole && !mIdCollection->isEditable (index.column())) + if (role==Qt::EditRole && !mIdCollection->getColumn (index.column()).isEditable()) return QVariant(); return mIdCollection->getData (index.row(), index.column()); @@ -42,18 +42,21 @@ QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation, int role) const { - if (role!=Qt::DisplayRole) - return QVariant(); - if (orientation==Qt::Vertical) return QVariant(); - return tr (mIdCollection->getTitle (section).c_str()); + if (role==Qt::DisplayRole) + return tr (mIdCollection->getColumn (section).mTitle.c_str()); + + if (role==ColumnBase::Role_Flags) + return mIdCollection->getColumn (section).mFlags; + + return QVariant(); } bool CSMWorld::IdTable::setData ( const QModelIndex &index, const QVariant &value, int role) { - if (mIdCollection->isEditable (index.column()) && role==Qt::EditRole) + if (mIdCollection->getColumn (index.column()).isEditable() && role==Qt::EditRole) { mIdCollection->setData (index.row(), index.column(), value); @@ -70,7 +73,7 @@ Qt::ItemFlags CSMWorld::IdTable::flags (const QModelIndex & index) const { Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; - if (mIdCollection->isUserEditable (index.column())) + if (mIdCollection->getColumn (index.column()).isUserEditable()) flags |= Qt::ItemIsEditable; return flags; From f95e72166c20cd6e64cebc5bb46b937e6ca62ccc Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 13 Dec 2012 15:03:35 +0100 Subject: [PATCH 142/378] hide type column in non-mixed type tables --- apps/opencs/model/world/columns.hpp | 2 +- apps/opencs/view/world/table.cpp | 25 ++++++++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 00c688484..f58d77c90 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -78,7 +78,7 @@ namespace CSMWorld { int mType; - FixedRecordTypeColumn (int type) : Column ("Type"), mType (type) {} + FixedRecordTypeColumn (int type) : Column ("Type", 0), mType (type) {} virtual QVariant get (const Record& record) const { diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 6892c7f61..0721ead2c 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -84,15 +84,6 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q { mModel = &dynamic_cast (*data.getTableModel (id)); - int columns = mModel->columnCount(); - - for (int i=0; isetSourceModel (mModel); @@ -103,6 +94,22 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionMode (QAbstractItemView::ExtendedSelection); + int columns = mModel->columnCount(); + + for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); + + if (flags & CSMWorld::ColumnBase::Flag_Table) + { + CommandDelegate *delegate = new CommandDelegate (undoStack, this); + mDelegates.push_back (delegate); + setItemDelegateForColumn (i, delegate); + } + else + hideColumn (i); + } + /// \todo make initial layout fill the whole width of the table if (createAndDelete) From 43481ad117da4da25aedd4aad6ff379d1ff74887 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 06:11:32 -0800 Subject: [PATCH 143/378] Use the external clock by default --- apps/openmw/mwrender/videoplayer.cpp | 3 ++- apps/openmw/mwrender/videoplayer.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index bd29c69bf..a5743356a 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -609,7 +609,8 @@ public: avcodec_default_release_buffer(c, pic); } - int video_thread(void *arg) { + int video_thread(void *arg) + { VideoState *is = (VideoState *)arg; AVPacket pkt1, *packet = &pkt1; int frameFinished; diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 2ce58846f..ace36efb9 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -28,7 +28,7 @@ extern "C" #define SAMPLE_CORRECTION_PERCENT_MAX 10 #define AUDIO_DIFF_AVG_NB 20 #define VIDEO_PICTURE_QUEUE_SIZE 1 -#define DEFAULT_AV_SYNC_TYPE AV_SYNC_VIDEO_MASTER +#define DEFAULT_AV_SYNC_TYPE AV_SYNC_EXTERNAL_MASTER namespace MWRender From 9d6f6568224578614bb3e118583786e5f2032d05 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 14 Dec 2012 00:10:54 +0100 Subject: [PATCH 144/378] fixed ogre resource functions --- apps/openmw/mwrender/videoplayer.cpp | 55 ++++++++++------------------ 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index a5743356a..21805a792 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -11,54 +11,37 @@ namespace MWRender { - int OgreResource_Read(void *opaque, uint8_t *buf, int buf_size) + int OgreResource_Read(void *user_data, uint8_t *buf, int buf_size) { - Ogre::DataStreamPtr stream = static_cast(opaque)->stream; - - int num_read = stream->size() - stream->tell(); - - if (num_read > buf_size) - num_read = buf_size; - - stream->read(buf, num_read); - return num_read; + Ogre::DataStreamPtr stream = static_cast(user_data)->stream; + return stream->read(buf, buf_size); } - int OgreResource_Write(void *opaque, uint8_t *buf, int buf_size) + int OgreResource_Write(void *user_data, uint8_t *buf, int buf_size) { - Ogre::DataStreamPtr stream = static_cast(opaque)->stream; - - int num_write = stream->size() - stream->tell(); - - if (num_write > buf_size) - num_write = buf_size; - - stream->write (buf, num_write); - return num_write; + Ogre::DataStreamPtr stream = static_cast(user_data)->stream; + return stream->write(buf, buf_size); } - int64_t OgreResource_Seek(void *opaque, int64_t offset, int whence) + int64_t OgreResource_Seek(void *user_data, int64_t offset, int whence) { - Ogre::DataStreamPtr stream = static_cast(opaque)->stream; + Ogre::DataStreamPtr stream = static_cast(user_data)->stream; - switch (whence) - { - case SEEK_SET: - stream->seek(offset); - case SEEK_CUR: - stream->seek(stream->tell() + offset); - case SEEK_END: - stream->seek(stream->size() + offset); - case AVSEEK_SIZE: - return stream->size(); - default: - return -1; - } + whence &= ~AVSEEK_FORCE; + if(whence == AVSEEK_SIZE) + return stream->size(); + if(whence == SEEK_SET) + stream->seek(offset); + else if(whence == SEEK_CUR) + stream->seek(stream->tell()+offset); + else if(whence == SEEK_END) + stream->seek(stream->size()+offset); + else + return -1; return stream->tell(); } - void packet_queue_init(PacketQueue *q) { memset(q, 0, sizeof(PacketQueue)); } From 27cd9ff7322269c7d0e8c91ac9f6a1ddc300cf2d Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 14 Dec 2012 01:03:49 +0100 Subject: [PATCH 145/378] Revert "Use the decoded frame pts when available" This reverts commit cab68df2574eec34254640770ca371450b341f42. --- apps/openmw/mwrender/videoplayer.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 21805a792..7e2be7e10 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -620,12 +620,11 @@ public: if (avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, packet) < 0) throw std::runtime_error("Error decoding video frame"); - if((uint64_t)pFrame->pts != AV_NOPTS_VALUE) - pts = pFrame->pts; - else if((uint64_t)packet->pts != AV_NOPTS_VALUE) - pts = packet->pts; - else if(pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) + if((uint64_t)packet->dts == AV_NOPTS_VALUE && + pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) pts = *(uint64_t *)pFrame->opaque; + else if((uint64_t)packet->dts != AV_NOPTS_VALUE) + pts = packet->dts; else pts = 0; pts *= av_q2d(is->video_st->time_base); From 82564e07c7edefe5ac8ab0b8e23db55ce39b0dd3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 14 Dec 2012 01:44:00 +0100 Subject: [PATCH 146/378] fix crash when video file doesn't exist --- apps/openmw/mwrender/videoplayer.cpp | 115 ++++++++++++++++----------- 1 file changed, 70 insertions(+), 45 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 7e2be7e10..299b34c32 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -777,49 +777,70 @@ public: void init_state(VideoState *is, const std::string& resourceName) { - int video_index = -1; - int audio_index = -1; - unsigned int i; - - is->av_sync_type = DEFAULT_AV_SYNC_TYPE; - is->format_ctx = avformat_alloc_context(); - is->videoStream = -1; - is->audioStream = -1; - is->refresh = 0; - is->quit = 0; - - is->stream = Ogre::ResourceGroupManager::getSingleton ().openResource(resourceName); - if(is->stream.isNull()) - throw std::runtime_error("Failed to open video resource"); - - is->format_ctx->pb = avio_alloc_context(NULL, 0, 0, is, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if(!is->format_ctx->pb) - throw std::runtime_error("Failed to allocate ioContext "); - - // Open video file - /// \todo leak here, ffmpeg or valgrind bug ? - if (avformat_open_input(&is->format_ctx, resourceName.c_str(), NULL, NULL)) - throw std::runtime_error("Failed to open video input"); - - // Retrieve stream information - if(avformat_find_stream_info(is->format_ctx, NULL) < 0) - throw std::runtime_error("Failed to retrieve stream information"); - - // Dump information about file onto standard error - av_dump_format(is->format_ctx, 0, resourceName.c_str(), 0); - - for(i = 0;i < is->format_ctx->nb_streams;i++) + try { - if(is->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) - video_index = i; - if(is->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) - audio_index = i; - } + int video_index = -1; + int audio_index = -1; + unsigned int i; + + is->av_sync_type = DEFAULT_AV_SYNC_TYPE; + is->videoStream = -1; + is->audioStream = -1; + is->refresh = 0; + is->quit = 0; + + is->stream = Ogre::ResourceGroupManager::getSingleton ().openResource(resourceName); + if(is->stream.isNull()) + throw std::runtime_error("Failed to open video resource"); + + is->format_ctx = avformat_alloc_context(); + + is->format_ctx->pb = avio_alloc_context(NULL, 0, 0, is, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if(!is->format_ctx->pb) + { + avformat_free_context(is->format_ctx); + throw std::runtime_error("Failed to allocate ioContext "); + } + + // Open video file + /// \todo leak here, ffmpeg or valgrind bug ? + if (avformat_open_input(&is->format_ctx, resourceName.c_str(), NULL, NULL)) + { + // "Note that a user-supplied AVFormatContext will be freed on failure." + is->format_ctx = NULL; + throw std::runtime_error("Failed to open video input"); + } - if(audio_index >= 0) - stream_component_open(is, audio_index, is->format_ctx); - if(video_index >= 0) - stream_component_open(is, video_index, is->format_ctx); + // Retrieve stream information + if(avformat_find_stream_info(is->format_ctx, NULL) < 0) + throw std::runtime_error("Failed to retrieve stream information"); + + // Dump information about file onto standard error + av_dump_format(is->format_ctx, 0, resourceName.c_str(), 0); + + for(i = 0;i < is->format_ctx->nb_streams;i++) + { + if(is->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) + video_index = i; + if(is->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) + audio_index = i; + } + + if(audio_index >= 0) + stream_component_open(is, audio_index, is->format_ctx); + if(video_index >= 0) + stream_component_open(is, video_index, is->format_ctx); + } + catch (std::runtime_error& e) + { + is->quit = 1; + throw; + } + catch (Ogre::Exception& e) + { + is->quit = 1; + throw; + } } void deinit_state(VideoState *is) @@ -835,11 +856,15 @@ public: if(is->videoStream >= 0) avcodec_close(is->video_st->codec); - sws_freeContext(is->sws_context); + if (is->sws_context) + sws_freeContext(is->sws_context); - AVIOContext *ioContext = is->format_ctx->pb; - avformat_close_input(&is->format_ctx); - av_free(ioContext); + if (is->format_ctx) + { + AVIOContext *ioContext = is->format_ctx->pb; + avformat_close_input(&is->format_ctx); + av_free(ioContext); + } } VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) From 606fb982a821d1805d5b51a02a9e84e6b857f033 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 17:53:22 -0800 Subject: [PATCH 147/378] Update to use avcodec_decode_audio4 --- apps/openmw/mwrender/videoplayer.cpp | 82 +++++++++++++++++----------- apps/openmw/mwrender/videoplayer.hpp | 15 ++--- 2 files changed, 56 insertions(+), 41 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 7e2be7e10..6acbe8b79 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -165,6 +165,10 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder VideoState *is; + AVFrame *mFrame; + size_t mFramePos; + size_t mFrameSize; + /* Add or subtract samples to get a better sync, return new * audio buffer size */ int synchronize_audio(uint8_t *samples, int samples_size, double pts) @@ -210,16 +214,19 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder { uint8_t *samples_end, *q; int nb; + /* add samples by copying final sample*/ nb = (samples_size - wanted_size); samples_end = samples + samples_size - n; q = samples_end + n; + while(nb > 0) { memcpy(q, samples_end, n); q += n; nb -= n; } + samples_size = wanted_size; } } @@ -235,38 +242,47 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder return samples_size; } - int audio_decode_frame(uint8_t *audio_buf, int buf_size, double *pts_ptr) + int audio_decode_frame(AVFrame *frame, double *pts_ptr) { AVPacket *pkt = &is->audio_pkt; - int len1, data_size, n; - double pts; + int len1, data_size; for(;;) { - while(is->audio_pkt_size > 0) + while(pkt->size > 0) { - data_size = buf_size; + int got_frame; - len1 = avcodec_decode_audio3(is->audio_st->codec, - (int16_t*)audio_buf, &data_size, pkt); - if(len1 < 0) + len1 = avcodec_decode_audio4(is->audio_st->codec, frame, &got_frame, pkt); + if(len1 < 0 || len1 > pkt->size) { - /* if error, skip frame */ - is->audio_pkt_size = 0; + /* if error, skip packet */ break; } - is->audio_pkt_data += len1; - is->audio_pkt_size -= len1; + + if(len1 <= pkt->size) + { + /* Move the unread data to the front and clear the end bits */ + int remaining = pkt->size - len1; + memmove(pkt->data, &pkt->data[len1], remaining); + memset(&pkt->data[remaining], 0, pkt->size - remaining); + pkt->size -= len1; + } + if(!got_frame) + continue; + + int smp_size = av_samples_get_buffer_size(NULL, is->audio_st->codec->channels, 1, + is->audio_st->codec->sample_fmt, 1); + data_size = frame->nb_samples * smp_size; if(data_size <= 0) { /* No data yet, get more frames */ continue; } - pts = is->audio_clock; - *pts_ptr = pts; - n = 2 * is->audio_st->codec->channels; - is->audio_clock += (double)data_size / - (double)(n * is->audio_st->codec->sample_rate); + + *pts_ptr = is->audio_clock; + is->audio_clock += (double)(data_size/smp_size) / + (double)is->audio_st->codec->sample_rate; /* We have data, return it and come back for more later */ return data_size; @@ -281,8 +297,6 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder if(packet_queue_get(&is->audioq, pkt, is, 1) < 0) return -1; - is->audio_pkt_data = pkt->data; - is->audio_pkt_size = pkt->size; /* if update, update the audio clock w/pts */ if((uint64_t)pkt->pts != AV_NOPTS_VALUE) is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; @@ -300,7 +314,16 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder void rewind() { } public: - MovieAudioDecoder(VideoState *_is) : is(_is) { } + MovieAudioDecoder(VideoState *_is) + : is(_is) + , mFrame(avcodec_alloc_frame()) + , mFramePos(0) + , mFrameSize(0) + { } + virtual ~MovieAudioDecoder() + { + av_freep(&mFrame); + } void getInfo(int *samplerate, MWSound::ChannelConfig *chans, MWSound::SampleType * type) { @@ -353,30 +376,29 @@ public: while(total < len) { - if(is->audio_buf_index >= is->audio_buf_size) + if(mFramePos >= mFrameSize) { int audio_size; double pts; + /* We have already sent all our data; get more */ - audio_size = audio_decode_frame(is->audio_buf, sizeof(is->audio_buf), &pts); + audio_size = audio_decode_frame(mFrame, &pts); if(audio_size < 0) { /* If error, we're done */ break; } - audio_size = synchronize_audio(is->audio_buf, audio_size, pts); - is->audio_buf_size = audio_size; - is->audio_buf_index = 0; + mFrameSize = synchronize_audio(mFrame->data[0], audio_size, pts); + mFramePos = 0; } - size_t len1 = std::min(is->audio_buf_size - is->audio_buf_index, - len - total); - memcpy(stream, (uint8_t*)is->audio_buf + is->audio_buf_index, len1); + size_t len1 = std::min(len - total, mFrameSize-mFramePos); + memcpy(stream, mFrame->data[0]+mFramePos, len1); total += len1; stream += len1; - is->audio_buf_index += len1; + mFramePos += len1; } return total; @@ -670,8 +692,6 @@ public: case AVMEDIA_TYPE_AUDIO: is->audioStream = stream_index; is->audio_st = pFormatCtx->streams[stream_index]; - is->audio_buf_size = 0; - is->audio_buf_index = 0; /* averaging filter for audio sync */ is->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index ace36efb9..689b3ad36 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -58,11 +58,11 @@ namespace MWRender struct VideoState { VideoState () : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock(0), - external_clock_time(0), audio_clock(0), audio_st(NULL), audio_buf_size(0), - audio_pkt_data(NULL), audio_pkt_size(0), audio_diff_cum(0), audio_diff_avg_coef(0), - audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), frame_last_delay(0), - video_clock(0), video_current_pts(0), video_current_pts_time(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), - pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) + external_clock_time(0), audio_clock(0), audio_st(NULL), audio_diff_cum(0), + audio_diff_avg_coef(0), audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), + frame_last_pts(0), frame_last_delay(0), video_clock(0), video_current_pts(0), + video_current_pts_time(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), + pictq_windex(0), quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) {} @@ -84,12 +84,7 @@ namespace MWRender double audio_clock; AVStream *audio_st; PacketQueue audioq; - DECLARE_ALIGNED(16, uint8_t, audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2]); - unsigned int audio_buf_size; - unsigned int audio_buf_index; AVPacket audio_pkt; - uint8_t *audio_pkt_data; - int audio_pkt_size; double audio_diff_cum; /* used for AV difference average computation */ double audio_diff_avg_coef; double audio_diff_threshold; From 6cedd64509d0b59a4714f853408b7bcf22b582a2 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 18:05:27 -0800 Subject: [PATCH 148/378] Fix audio sync correction sizes --- apps/openmw/mwrender/videoplayer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index c2781935c..38b519b21 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -197,8 +197,8 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder if(fabs(avg_diff) >= is->audio_diff_threshold) { wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); - min_size = samples_size * ((100 - SAMPLE_CORRECTION_PERCENT_MAX) / 100); - max_size = samples_size * ((100 + SAMPLE_CORRECTION_PERCENT_MAX) / 100); + min_size = samples_size/n * (100-SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; + max_size = samples_size/n * (100+SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; if(wanted_size < min_size) wanted_size = min_size; From f555dc60eb05cbf0716e3785185c4da69b22a9ed Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 18:12:17 -0800 Subject: [PATCH 149/378] Reduce some indentation --- apps/openmw/mwrender/videoplayer.cpp | 109 ++++++++++++++------------- 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 38b519b21..a6e033216 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -462,68 +462,69 @@ public: VideoPicture *vp; double actual_delay, delay, sync_threshold, ref_clock, diff; - if(is->video_st) + if(!is->video_st) { - if(is->pictq_size == 0) - schedule_refresh(is, 1); - else - { - vp = &is->pictq[is->pictq_rindex]; + schedule_refresh(is, 100); + return; + } + if(is->pictq_size == 0) + { + schedule_refresh(is, 1); + return; + } - is->video_current_pts = vp->pts; - is->video_current_pts_time = av_gettime(); + vp = &is->pictq[is->pictq_rindex]; - delay = vp->pts - is->frame_last_pts; /* the pts from last time */ - if(delay <= 0 || delay >= 1.0) { - /* if incorrect delay, use previous one */ - delay = is->frame_last_delay; - } - /* save for next time */ - is->frame_last_delay = delay; - is->frame_last_pts = vp->pts; + is->video_current_pts = vp->pts; + is->video_current_pts_time = av_gettime(); - /* update delay to sync to audio if not master source */ - if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) - { - ref_clock = get_master_clock(is); - diff = vp->pts - ref_clock; + delay = vp->pts - is->frame_last_pts; /* the pts from last time */ + if(delay <= 0 || delay >= 1.0) { + /* if incorrect delay, use previous one */ + delay = is->frame_last_delay; + } + /* save for next time */ + is->frame_last_delay = delay; + is->frame_last_pts = vp->pts; - /* Skip or repeat the frame. Take delay into account - FFPlay still doesn't "know if this is the best guess." */ - sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; - if(fabs(diff) < AV_NOSYNC_THRESHOLD) - { - if(diff <= -sync_threshold) - delay = 0; - else if(diff >= sync_threshold) - delay = 2 * delay; - } - } + /* update delay to sync to audio if not master source */ + if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) + { + ref_clock = get_master_clock(is); + diff = vp->pts - ref_clock; - is->frame_timer += delay; - /* computer the REAL delay */ - actual_delay = is->frame_timer - (av_gettime() / 1000000.0); - if(actual_delay < 0.010) - { - /* Really it should skip the picture instead */ - actual_delay = 0.010; - } - schedule_refresh(is, (int)(actual_delay * 1000 + 0.5)); - - /* show the picture! */ - video_display(is); - - /* update queue for next picture! */ - if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) - is->pictq_rindex = 0; - is->pictq_mutex.lock(); - is->pictq_size--; - is->pictq_cond.notify_one (); - is->pictq_mutex.unlock (); + /* Skip or repeat the frame. Take delay into account + FFPlay still doesn't "know if this is the best guess." */ + sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; + if(fabs(diff) < AV_NOSYNC_THRESHOLD) + { + if(diff <= -sync_threshold) + delay = 0; + else if(diff >= sync_threshold) + delay = 2 * delay; } } - else - schedule_refresh(is, 100); + + is->frame_timer += delay; + /* computer the REAL delay */ + actual_delay = is->frame_timer - (av_gettime() / 1000000.0); + if(actual_delay < 0.010) + { + /* Really it should skip the picture instead */ + actual_delay = 0.010; + } + schedule_refresh(is, (int)(actual_delay * 1000 + 0.5)); + + /* show the picture! */ + video_display(is); + + /* update queue for next picture! */ + if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) + is->pictq_rindex = 0; + is->pictq_mutex.lock(); + is->pictq_size--; + is->pictq_cond.notify_one (); + is->pictq_mutex.unlock (); } int queue_picture(VideoState *is, AVFrame *pFrame, double pts) From 90294c589ba3f79e2c760dcd3317d4b30b035710 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 13 Dec 2012 18:24:57 -0800 Subject: [PATCH 150/378] Use a volatile bool for the refresh --- apps/openmw/mwrender/videoplayer.cpp | 10 +++++----- apps/openmw/mwrender/videoplayer.hpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index a6e033216..6307a0028 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -411,10 +411,10 @@ public: }; - void timer_callback (boost::system_time t, VideoState* is) + void timer_callback(boost::system_time t, VideoState* is) { boost::this_thread::sleep(t); - is->refresh++; + is->refresh = true; } /* schedule a video refresh in 'delay' ms */ @@ -469,7 +469,7 @@ public: } if(is->pictq_size == 0) { - schedule_refresh(is, 1); + is->refresh = true; return; } @@ -807,7 +807,7 @@ public: is->av_sync_type = DEFAULT_AV_SYNC_TYPE; is->videoStream = -1; is->audioStream = -1; - is->refresh = 0; + is->refresh = false; is->quit = 0; is->stream = Ogre::ResourceGroupManager::getSingleton ().openResource(resourceName); @@ -959,8 +959,8 @@ public: close(); else if(mState->refresh) { + mState->refresh = false; video_refresh_timer(mState); - mState->refresh--; } } diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 689b3ad36..67b619ad2 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -117,8 +117,8 @@ namespace MWRender boost::thread video_thread; volatile int quit; + volatile bool refresh; - int refresh; int display_ready; }; enum { From 5221298a7f99847f3a00e22ddbbf79fb2a89f011 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 01:14:14 -0800 Subject: [PATCH 151/378] Move a couple packet queue methods into the struct --- apps/openmw/mwrender/videoplayer.cpp | 65 +++++++++++++--------------- apps/openmw/mwrender/videoplayer.hpp | 34 +++++++++------ 2 files changed, 51 insertions(+), 48 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 6307a0028..b4fd264ec 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -42,41 +42,38 @@ namespace MWRender return stream->tell(); } - void packet_queue_init(PacketQueue *q) - { memset(q, 0, sizeof(PacketQueue)); } - int packet_queue_put(PacketQueue *q, AVPacket *pkt) + void PacketQueue::put(AVPacket *pkt) { AVPacketList *pkt1; if(av_dup_packet(pkt) < 0) - return -1; + throw std::runtime_error("Failed to duplicate packet"); pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); - if(!pkt1) return -1; + if(!pkt1) throw std::bad_alloc(); pkt1->pkt = *pkt; pkt1->next = NULL; - q->mutex.lock (); + this->mutex.lock (); - if (!q->last_pkt) - q->first_pkt = pkt1; + if(!last_pkt) + this->first_pkt = pkt1; else - q->last_pkt->next = pkt1; - q->last_pkt = pkt1; - q->nb_packets++; - q->size += pkt1->pkt.size; - q->cond.notify_one(); - q->mutex.unlock (); - return 0; + this->last_pkt->next = pkt1; + this->last_pkt = pkt1; + this->nb_packets++; + this->size += pkt1->pkt.size; + this->cond.notify_one(); + + this->mutex.unlock(); } - static int packet_queue_get(PacketQueue *q, AVPacket *pkt, VideoState *is, int block) + int PacketQueue::get(AVPacket *pkt, VideoState *is, int block) { AVPacketList *pkt1; int ret; - boost::unique_lock lock(q->mutex); - + boost::unique_lock lock(this->mutex); for(;;) { if(is->quit) @@ -85,16 +82,18 @@ namespace MWRender break; } - pkt1 = q->first_pkt; - if (pkt1) + pkt1 = this->first_pkt; + if(pkt1) { - q->first_pkt = pkt1->next; - if (!q->first_pkt) - q->last_pkt = NULL; - q->nb_packets--; - q->size -= pkt1->pkt.size; + this->first_pkt = pkt1->next; + if(!this->first_pkt) + this->last_pkt = NULL; + this->nb_packets--; + this->size -= pkt1->pkt.size; + *pkt = pkt1->pkt; av_free(pkt1); + ret = 1; break; } @@ -105,7 +104,7 @@ namespace MWRender break; } - q->cond.wait(lock); + this->cond.wait(lock); } return ret; @@ -116,7 +115,8 @@ namespace MWRender AVPacketList *pkt, *pkt1; this->mutex.lock(); - for(pkt = this->first_pkt; pkt != NULL; pkt = pkt1) { + for(pkt = this->first_pkt; pkt != NULL; pkt = pkt1) + { pkt1 = pkt->next; av_free_packet(&pkt->pkt); av_freep(&pkt); @@ -294,7 +294,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder return -1; /* next packet */ - if(packet_queue_get(&is->audioq, pkt, is, 1) < 0) + if(is->audioq.get(pkt, is, 1) < 0) return -1; /* if update, update the audio clock w/pts */ @@ -630,7 +630,7 @@ public: for(;;) { - if(packet_queue_get(&is->videoq, packet, is, 1) < 0) + if(is->videoq.get(packet, is, 1) < 0) { // means we quit getting packets break; @@ -701,7 +701,6 @@ public: is->audio_diff_threshold = 2.0 * 0.1/* 100 ms */; memset(&is->audio_pkt, 0, sizeof(is->audio_pkt)); - packet_queue_init(&is->audioq); decoder.reset(new MovieAudioDecoder(is)); is->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); @@ -722,8 +721,6 @@ public: is->frame_last_delay = 40e-3; is->video_current_pts_time = av_gettime(); - packet_queue_init(&is->videoq); - codecCtx->get_buffer = our_get_buffer; codecCtx->release_buffer = our_release_buffer; is->video_thread = boost::thread(video_thread, is); @@ -764,9 +761,9 @@ public: // Is this a packet from the video stream? if(packet->stream_index == is->videoStream) - packet_queue_put(&is->videoq, packet); + is->videoq.put(packet); else if(packet->stream_index == is->audioStream) - packet_queue_put(&is->audioq, packet); + is->audioq.put(packet); else av_free_packet(packet); } diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 67b619ad2..5dfa3cf56 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -33,10 +33,13 @@ extern "C" namespace MWRender { + struct VideoState; + struct PacketQueue { - PacketQueue () : - first_pkt(NULL), last_pkt(NULL), nb_packets(0), size(0) - {} + PacketQueue() + : first_pkt(NULL), last_pkt(NULL), nb_packets(0), size(0) + { } + AVPacketList *first_pkt, *last_pkt; int nb_packets; int size; @@ -44,20 +47,23 @@ namespace MWRender boost::mutex mutex; boost::condition_variable cond; - void flush (); + void put(AVPacket *pkt); + int get(AVPacket *pkt, VideoState *is, int block); + + void flush(); }; + struct VideoPicture { - VideoPicture () : - data(NULL), pts(0) - {} - uint8_t* data; + VideoPicture () : data(NULL), pts(0) + { } + uint8_t *data; double pts; }; struct VideoState { - VideoState () : - videoStream(-1), audioStream(-1), av_sync_type(0), external_clock(0), + VideoState () + : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock(0), external_clock_time(0), audio_clock(0), audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), frame_last_delay(0), video_clock(0), video_current_pts(0), @@ -68,11 +74,11 @@ namespace MWRender ~VideoState() { - audioq.flush (); + audioq.flush(); videoq.flush(); - if (pictq_size >= 1) - free (pictq[0].data); + if(pictq_size >= 1) + free(pictq[0].data); } int videoStream, audioStream; @@ -82,7 +88,7 @@ namespace MWRender int64_t external_clock_time; double audio_clock; - AVStream *audio_st; + AVStream *audio_st; PacketQueue audioq; AVPacket audio_pkt; double audio_diff_cum; /* used for AV difference average computation */ From 26a09ee7ba3afd56b83442763c1aaaad7846da55 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 01:38:00 -0800 Subject: [PATCH 152/378] Move some methods into their respective class --- apps/openmw/mwrender/videoplayer.cpp | 289 +++++++++++++-------------- apps/openmw/mwrender/videoplayer.hpp | 8 +- 2 files changed, 151 insertions(+), 146 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index b4fd264ec..ec68359aa 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -11,122 +11,90 @@ namespace MWRender { - int OgreResource_Read(void *user_data, uint8_t *buf, int buf_size) - { - Ogre::DataStreamPtr stream = static_cast(user_data)->stream; - return stream->read(buf, buf_size); - } - - int OgreResource_Write(void *user_data, uint8_t *buf, int buf_size) - { - Ogre::DataStreamPtr stream = static_cast(user_data)->stream; - return stream->write(buf, buf_size); - } - - int64_t OgreResource_Seek(void *user_data, int64_t offset, int whence) - { - Ogre::DataStreamPtr stream = static_cast(user_data)->stream; - - whence &= ~AVSEEK_FORCE; - if(whence == AVSEEK_SIZE) - return stream->size(); - if(whence == SEEK_SET) - stream->seek(offset); - else if(whence == SEEK_CUR) - stream->seek(stream->tell()+offset); - else if(whence == SEEK_END) - stream->seek(stream->size()+offset); - else - return -1; - - return stream->tell(); - } - - - void PacketQueue::put(AVPacket *pkt) - { - AVPacketList *pkt1; - if(av_dup_packet(pkt) < 0) - throw std::runtime_error("Failed to duplicate packet"); - - pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); - if(!pkt1) throw std::bad_alloc(); - pkt1->pkt = *pkt; - pkt1->next = NULL; - - this->mutex.lock (); - - if(!last_pkt) - this->first_pkt = pkt1; - else - this->last_pkt->next = pkt1; - this->last_pkt = pkt1; - this->nb_packets++; - this->size += pkt1->pkt.size; - this->cond.notify_one(); +void PacketQueue::put(AVPacket *pkt) +{ + AVPacketList *pkt1; + if(av_dup_packet(pkt) < 0) + throw std::runtime_error("Failed to duplicate packet"); + + pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); + if(!pkt1) throw std::bad_alloc(); + pkt1->pkt = *pkt; + pkt1->next = NULL; + + this->mutex.lock (); + + if(!last_pkt) + this->first_pkt = pkt1; + else + this->last_pkt->next = pkt1; + this->last_pkt = pkt1; + this->nb_packets++; + this->size += pkt1->pkt.size; + this->cond.notify_one(); + + this->mutex.unlock(); +} - this->mutex.unlock(); - } +int PacketQueue::get(AVPacket *pkt, VideoState *is, int block) +{ + AVPacketList *pkt1; + int ret; - int PacketQueue::get(AVPacket *pkt, VideoState *is, int block) + boost::unique_lock lock(this->mutex); + for(;;) { - AVPacketList *pkt1; - int ret; - - boost::unique_lock lock(this->mutex); - for(;;) + if(is->quit) { - if(is->quit) - { - ret = -1; - break; - } - - pkt1 = this->first_pkt; - if(pkt1) - { - this->first_pkt = pkt1->next; - if(!this->first_pkt) - this->last_pkt = NULL; - this->nb_packets--; - this->size -= pkt1->pkt.size; + ret = -1; + break; + } - *pkt = pkt1->pkt; - av_free(pkt1); + pkt1 = this->first_pkt; + if(pkt1) + { + this->first_pkt = pkt1->next; + if(!this->first_pkt) + this->last_pkt = NULL; + this->nb_packets--; + this->size -= pkt1->pkt.size; - ret = 1; - break; - } + *pkt = pkt1->pkt; + av_free(pkt1); - if (!block) - { - ret = 0; - break; - } + ret = 1; + break; + } - this->cond.wait(lock); + if (!block) + { + ret = 0; + break; } - return ret; + this->cond.wait(lock); } - void PacketQueue::flush() - { - AVPacketList *pkt, *pkt1; + return ret; +} - this->mutex.lock(); - for(pkt = this->first_pkt; pkt != NULL; pkt = pkt1) - { - pkt1 = pkt->next; - av_free_packet(&pkt->pkt); - av_freep(&pkt); - } - this->last_pkt = NULL; - this->first_pkt = NULL; - this->nb_packets = 0; - this->size = 0; - this->mutex.unlock (); +void PacketQueue::flush() +{ + AVPacketList *pkt, *pkt1; + + this->mutex.lock(); + for(pkt = this->first_pkt; pkt != NULL; pkt = pkt1) + { + pkt1 = pkt->next; + av_free_packet(&pkt->pkt); + av_freep(&pkt); } + this->last_pkt = NULL; + this->first_pkt = NULL; + this->nb_packets = 0; + this->size = 0; + this->mutex.unlock (); +} double get_audio_clock(VideoState *is) @@ -411,6 +379,38 @@ public: }; +int VideoState::OgreResource_Read(void *user_data, uint8_t *buf, int buf_size) +{ + Ogre::DataStreamPtr stream = static_cast(user_data)->stream; + return stream->read(buf, buf_size); +} + +int VideoState::OgreResource_Write(void *user_data, uint8_t *buf, int buf_size) +{ + Ogre::DataStreamPtr stream = static_cast(user_data)->stream; + return stream->write(buf, buf_size); +} + +int64_t VideoState::OgreResource_Seek(void *user_data, int64_t offset, int whence) +{ + Ogre::DataStreamPtr stream = static_cast(user_data)->stream; + + whence &= ~AVSEEK_FORCE; + if(whence == AVSEEK_SIZE) + return stream->size(); + if(whence == SEEK_SET) + stream->seek(offset); + else if(whence == SEEK_CUR) + stream->seek(stream->tell()+offset); + else if(whence == SEEK_END) + stream->seek(stream->size()+offset); + else + return -1; + + return stream->tell(); +} + + void timer_callback(boost::system_time t, VideoState* is) { boost::this_thread::sleep(t); @@ -793,7 +793,7 @@ public: return 0; } - void init_state(VideoState *is, const std::string& resourceName) + void VideoState::init(const std::string& resourceName) { try { @@ -801,90 +801,90 @@ public: int audio_index = -1; unsigned int i; - is->av_sync_type = DEFAULT_AV_SYNC_TYPE; - is->videoStream = -1; - is->audioStream = -1; - is->refresh = false; - is->quit = 0; + this->av_sync_type = DEFAULT_AV_SYNC_TYPE; + this->videoStream = -1; + this->audioStream = -1; + this->refresh = false; + this->quit = 0; - is->stream = Ogre::ResourceGroupManager::getSingleton ().openResource(resourceName); - if(is->stream.isNull()) + this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); + if(this->stream.isNull()) throw std::runtime_error("Failed to open video resource"); - is->format_ctx = avformat_alloc_context(); - - is->format_ctx->pb = avio_alloc_context(NULL, 0, 0, is, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if(!is->format_ctx->pb) + this->format_ctx = avformat_alloc_context(); + this->format_ctx->pb = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if(!this->format_ctx->pb) { - avformat_free_context(is->format_ctx); + avformat_free_context(this->format_ctx); throw std::runtime_error("Failed to allocate ioContext "); } // Open video file /// \todo leak here, ffmpeg or valgrind bug ? - if (avformat_open_input(&is->format_ctx, resourceName.c_str(), NULL, NULL)) + if (avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) { // "Note that a user-supplied AVFormatContext will be freed on failure." - is->format_ctx = NULL; + this->format_ctx = NULL; throw std::runtime_error("Failed to open video input"); } // Retrieve stream information - if(avformat_find_stream_info(is->format_ctx, NULL) < 0) + if(avformat_find_stream_info(this->format_ctx, NULL) < 0) throw std::runtime_error("Failed to retrieve stream information"); // Dump information about file onto standard error - av_dump_format(is->format_ctx, 0, resourceName.c_str(), 0); + av_dump_format(this->format_ctx, 0, resourceName.c_str(), 0); - for(i = 0;i < is->format_ctx->nb_streams;i++) + for(i = 0;i < this->format_ctx->nb_streams;i++) { - if(is->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) + if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) video_index = i; - if(is->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) + if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) audio_index = i; } if(audio_index >= 0) - stream_component_open(is, audio_index, is->format_ctx); + stream_component_open(this, audio_index, this->format_ctx); if(video_index >= 0) - stream_component_open(is, video_index, is->format_ctx); + stream_component_open(this, video_index, this->format_ctx); } - catch (std::runtime_error& e) + catch(std::runtime_error& e) { - is->quit = 1; + this->quit = 1; throw; } - catch (Ogre::Exception& e) + catch(Ogre::Exception& e) { - is->quit = 1; + this->quit = 1; throw; } } - void deinit_state(VideoState *is) + void VideoState::deinit() { - is->audioq.cond.notify_one (); - is->videoq.cond.notify_one (); + this->audioq.cond.notify_one(); + this->videoq.cond.notify_one(); - is->parse_thread.join(); - is->video_thread.join(); + this->parse_thread.join(); + this->video_thread.join(); - if(is->audioStream >= 0) - avcodec_close(is->audio_st->codec); - if(is->videoStream >= 0) - avcodec_close(is->video_st->codec); + if(this->audioStream >= 0) + avcodec_close(this->audio_st->codec); + if(this->videoStream >= 0) + avcodec_close(this->video_st->codec); - if (is->sws_context) - sws_freeContext(is->sws_context); + if(this->sws_context) + sws_freeContext(this->sws_context); - if (is->format_ctx) + if(this->format_ctx) { - AVIOContext *ioContext = is->format_ctx->pb; - avformat_close_input(&is->format_ctx); + AVIOContext *ioContext = this->format_ctx->pb; + avformat_close_input(&this->format_ctx); av_free(ioContext); } } + VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) : mState(NULL) , mSceneMgr(sceneMgr) @@ -941,8 +941,7 @@ public: MWBase::Environment::get().getSoundManager()->pauseAllSounds(); mState = new VideoState; - - init_state(mState, resourceName); + mState->init(resourceName); schedule_refresh(mState, 40); mState->parse_thread = boost::thread(decode_thread, mState); @@ -970,7 +969,7 @@ public: void VideoPlayer::close() { mState->quit = 1; - deinit_state(mState); + mState->deinit(); delete mState; mState = NULL; diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 5dfa3cf56..82a7083ae 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -71,7 +71,6 @@ namespace MWRender pictq_windex(0), quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) {} - ~VideoState() { audioq.flush(); @@ -81,6 +80,13 @@ namespace MWRender free(pictq[0].data); } + void init(const std::string& resourceName); + void deinit(); + + static int OgreResource_Read(void *user_data, uint8_t *buf, int buf_size); + static int OgreResource_Write(void *user_data, uint8_t *buf, int buf_size); + static int64_t OgreResource_Seek(void *user_data, int64_t offset, int whence); + int videoStream, audioStream; int av_sync_type; From c2b711d195c6ce6702f693703c249b22add7f2e7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 02:36:29 -0800 Subject: [PATCH 153/378] Move some more methods to the class they're part of --- apps/openmw/mwrender/videoplayer.cpp | 796 +++++++++++++-------------- apps/openmw/mwrender/videoplayer.hpp | 14 + 2 files changed, 401 insertions(+), 409 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index ec68359aa..4334bc634 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -97,32 +97,33 @@ void PacketQueue::flush() } - double get_audio_clock(VideoState *is) - { - return is->AudioTrack->getTimeOffset(); - } +static double get_audio_clock(VideoState *is) +{ + return is->AudioTrack->getTimeOffset(); +} - double get_video_clock(VideoState *is) - { - double delta; +static double get_video_clock(VideoState *is) +{ + double delta; - delta = (av_gettime() - is->video_current_pts_time) / 1000000.0; - return is->video_current_pts + delta; - } + delta = (av_gettime() - is->video_current_pts_time) / 1000000.0; + return is->video_current_pts + delta; +} - double get_external_clock(VideoState *is) - { - return av_gettime() / 1000000.0; - } +static double get_external_clock(VideoState *is) +{ + return av_gettime() / 1000000.0; +} + +static double get_master_clock(VideoState *is) +{ + if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) + return get_video_clock(is); + if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) + return get_audio_clock(is); + return get_external_clock(is); +} - double get_master_clock(VideoState *is) - { - if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) - return get_video_clock(is); - if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) - return get_audio_clock(is); - return get_external_clock(is); - } class MovieAudioDecoder : public MWSound::Sound_Decoder { @@ -411,478 +412,458 @@ int64_t VideoState::OgreResource_Seek(void *user_data, int64_t offset, int whenc } - void timer_callback(boost::system_time t, VideoState* is) +void VideoState::timer_callback(VideoState* is, boost::system_time t) +{ + boost::this_thread::sleep(t); + is->refresh = true; +} + +/* schedule a video refresh in 'delay' ms */ +void VideoState::schedule_refresh(int delay) +{ + boost::system_time t = boost::get_system_time() + boost::posix_time::milliseconds(delay); + boost::thread(boost::bind(&timer_callback, this, t)).detach(); +} + + +void VideoState::video_display() +{ + VideoPicture *vp = &this->pictq[this->pictq_rindex]; + + if(this->video_st->codec->width != 0 && this->video_st->codec->height != 0) { - boost::this_thread::sleep(t); - is->refresh = true; + Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().getByName("VideoTexture"); + if(texture.isNull () || static_cast(texture->getWidth()) != this->video_st->codec->width + || static_cast(texture->getHeight()) != this->video_st->codec->height) + { + Ogre::TextureManager::getSingleton ().remove ("VideoTexture"); + texture = Ogre::TextureManager::getSingleton().createManual( + "VideoTexture", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, + this->video_st->codec->width, this->video_st->codec->height, + 0, + Ogre::PF_BYTE_RGBA, + Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); + } + Ogre::PixelBox pb(this->video_st->codec->width, this->video_st->codec->height, 1, Ogre::PF_BYTE_RGBA, vp->data); + Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer(); + buffer->blitFromMemory(pb); + this->display_ready = 1; } - /* schedule a video refresh in 'delay' ms */ - static void schedule_refresh(VideoState *is, int delay) + free(vp->data); +} + +void VideoState::video_refresh_timer() +{ + VideoPicture *vp; + double actual_delay, delay, sync_threshold, ref_clock, diff; + + if(!this->video_st) { - boost::system_time t = boost::get_system_time() + boost::posix_time::milliseconds(delay); - boost::thread(boost::bind(&timer_callback, t, is)).detach(); + this->schedule_refresh(100); + return; } - - void video_display(VideoState *is) + if(this->pictq_size == 0) { - VideoPicture *vp; + this->refresh = true; + return; + } - vp = &is->pictq[is->pictq_rindex]; + vp = &this->pictq[this->pictq_rindex]; - if (is->video_st->codec->width != 0 && is->video_st->codec->height != 0) - { - Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton ().getByName("VideoTexture"); - if (texture.isNull () || static_cast(texture->getWidth()) != is->video_st->codec->width - || static_cast(texture->getHeight()) != is->video_st->codec->height) - { - Ogre::TextureManager::getSingleton ().remove ("VideoTexture"); - texture = Ogre::TextureManager::getSingleton().createManual( - "VideoTexture", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, - is->video_st->codec->width, is->video_st->codec->height, - 0, - Ogre::PF_BYTE_RGBA, - Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); - } - Ogre::PixelBox pb(is->video_st->codec->width, is->video_st->codec->height, 1, Ogre::PF_BYTE_RGBA, vp->data); - Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer(); - buffer->blitFromMemory(pb); - is->display_ready = 1; - } + this->video_current_pts = vp->pts; + this->video_current_pts_time = av_gettime(); - free(vp->data); + delay = vp->pts - this->frame_last_pts; /* the pts from last time */ + if(delay <= 0 || delay >= 1.0) { + /* if incorrect delay, use previous one */ + delay = this->frame_last_delay; } + /* save for next time */ + this->frame_last_delay = delay; + this->frame_last_pts = vp->pts; - - void video_refresh_timer(void *userdata) + /* update delay to sync to audio if not master source */ + if(this->av_sync_type != AV_SYNC_VIDEO_MASTER) { - VideoState *is = (VideoState *)userdata; - VideoPicture *vp; - double actual_delay, delay, sync_threshold, ref_clock, diff; + ref_clock = get_master_clock(this); + diff = vp->pts - ref_clock; - if(!is->video_st) - { - schedule_refresh(is, 100); - return; - } - if(is->pictq_size == 0) + /* Skip or repeat the frame. Take delay into account + FFPlay still doesn't "know if this is the best guess." */ + sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; + if(fabs(diff) < AV_NOSYNC_THRESHOLD) { - is->refresh = true; - return; + if(diff <= -sync_threshold) + delay = 0; + else if(diff >= sync_threshold) + delay = 2 * delay; } + } + this->frame_timer += delay; - vp = &is->pictq[is->pictq_rindex]; + /* compute the REAL delay */ + actual_delay = this->frame_timer - (av_gettime() / 1000000.0); + if(actual_delay < 0.010) + { + /* Really it should skip the picture instead */ + actual_delay = 0.010; + } + this->schedule_refresh((int)(actual_delay * 1000 + 0.5)); - is->video_current_pts = vp->pts; - is->video_current_pts_time = av_gettime(); + /* show the picture! */ + this->video_display(); - delay = vp->pts - is->frame_last_pts; /* the pts from last time */ - if(delay <= 0 || delay >= 1.0) { - /* if incorrect delay, use previous one */ - delay = is->frame_last_delay; - } - /* save for next time */ - is->frame_last_delay = delay; - is->frame_last_pts = vp->pts; + /* update queue for next picture! */ + if(++this->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) + this->pictq_rindex = 0; + this->pictq_mutex.lock(); + this->pictq_size--; + this->pictq_cond.notify_one(); + this->pictq_mutex.unlock(); +} - /* update delay to sync to audio if not master source */ - if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) - { - ref_clock = get_master_clock(is); - diff = vp->pts - ref_clock; - /* Skip or repeat the frame. Take delay into account - FFPlay still doesn't "know if this is the best guess." */ - sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; - if(fabs(diff) < AV_NOSYNC_THRESHOLD) - { - if(diff <= -sync_threshold) - delay = 0; - else if(diff >= sync_threshold) - delay = 2 * delay; - } - } +int VideoState::queue_picture(AVFrame *pFrame, double pts) +{ + VideoPicture *vp; - is->frame_timer += delay; - /* computer the REAL delay */ - actual_delay = is->frame_timer - (av_gettime() / 1000000.0); - if(actual_delay < 0.010) - { - /* Really it should skip the picture instead */ - actual_delay = 0.010; - } - schedule_refresh(is, (int)(actual_delay * 1000 + 0.5)); + /* wait until we have a new pic */ + { + boost::unique_lock lock(this->pictq_mutex); + while(this->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !this->quit) + this->pictq_cond.timed_wait(lock, boost::posix_time::milliseconds(1)); + } + if(this->quit) + return -1; - /* show the picture! */ - video_display(is); + // windex is set to 0 initially + vp = &this->pictq[this->pictq_windex]; - /* update queue for next picture! */ - if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) - is->pictq_rindex = 0; - is->pictq_mutex.lock(); - is->pictq_size--; - is->pictq_cond.notify_one (); - is->pictq_mutex.unlock (); + // Convert the image into RGBA format for Ogre + if(this->sws_context == NULL) + { + int w = this->video_st->codec->width; + int h = this->video_st->codec->height; + this->sws_context = sws_getContext(w, h, this->video_st->codec->pix_fmt, + w, h, PIX_FMT_RGBA, SWS_BICUBIC, + NULL, NULL, NULL); + if(this->sws_context == NULL) + throw std::runtime_error("Cannot initialize the conversion context!\n"); } - int queue_picture(VideoState *is, AVFrame *pFrame, double pts) - { - VideoPicture *vp; + vp->data = (uint8_t*)malloc(this->video_st->codec->width * this->video_st->codec->height * 4); - /* wait until we have a new pic */ - { - boost::unique_lock lock(is->pictq_mutex); - while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is->quit) - is->pictq_cond.timed_wait(lock, boost::posix_time::milliseconds(1)); - } + sws_scale(this->sws_context, pFrame->data, pFrame->linesize, + 0, this->video_st->codec->height, &vp->data, this->rgbaFrame->linesize); - if(is->quit) - return -1; + vp->pts = pts; - // windex is set to 0 initially - vp = &is->pictq[is->pictq_windex]; + // now we inform our display thread that we have a pic ready + this->pictq_windex = (this->pictq_windex+1) % VIDEO_PICTURE_QUEUE_SIZE; + this->pictq_mutex.lock(); + this->pictq_size++; + this->pictq_mutex.unlock(); - // Convert the image into YUV format that SDL uses - if(is->sws_context == NULL) - { - int w = is->video_st->codec->width; - int h = is->video_st->codec->height; - is->sws_context = sws_getContext(w, h, is->video_st->codec->pix_fmt, - w, h, PIX_FMT_RGBA, SWS_BICUBIC, - NULL, NULL, NULL); - if(is->sws_context == NULL) - throw std::runtime_error("Cannot initialize the conversion context!\n"); - } + return 0; +} - vp->data =(uint8_t*) malloc(is->video_st->codec->width * is->video_st->codec->height * 4); +double VideoState::synchronize_video(AVFrame *src_frame, double pts) +{ + double frame_delay; - sws_scale(is->sws_context, pFrame->data, pFrame->linesize, - 0, is->video_st->codec->height, &vp->data, is->rgbaFrame->linesize); + /* if we have pts, set video clock to it */ + if(pts != 0) + this->video_clock = pts; + else + pts = this->video_clock; - vp->pts = pts; + /* update the video clock */ + frame_delay = av_q2d(this->video_st->codec->time_base); - // now we inform our display thread that we have a pic ready - if(++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) - is->pictq_windex = 0; - is->pictq_mutex.lock(); - is->pictq_size++; - is->pictq_mutex.unlock(); + /* if we are repeating a frame, adjust clock accordingly */ + frame_delay += src_frame->repeat_pict * (frame_delay * 0.5); + this->video_clock += frame_delay; - return 0; - } + return pts; +} - double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) - { - double frame_delay; - if(pts != 0) - { - /* if we have pts, set video clock to it */ - is->video_clock = pts; - } - else - { - /* if we aren't given a pts, set it to the clock */ - pts = is->video_clock; - } - /* update the video clock */ - frame_delay = av_q2d(is->video_st->codec->time_base); - /* if we are repeating a frame, adjust clock accordingly */ - frame_delay += src_frame->repeat_pict * (frame_delay * 0.5); - is->video_clock += frame_delay; - return pts; - } +/* These are called whenever we allocate a frame + * buffer. We use this to store the global_pts in + * a frame at the time it is allocated. + */ +static uint64_t global_video_pkt_pts = AV_NOPTS_VALUE; +static int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) +{ + int ret = avcodec_default_get_buffer(c, pic); + uint64_t *pts = (uint64_t*)av_malloc(sizeof(uint64_t)); + *pts = global_video_pkt_pts; + pic->opaque = pts; + return ret; +} +static void our_release_buffer(struct AVCodecContext *c, AVFrame *pic) +{ + if(pic) av_freep(&pic->opaque); + avcodec_default_release_buffer(c, pic); +} - uint64_t global_video_pkt_pts = AV_NOPTS_VALUE; - /* These are called whenever we allocate a frame - * buffer. We use this to store the global_pts in - * a frame at the time it is allocated. - */ - int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) - { - int ret = avcodec_default_get_buffer(c, pic); - uint64_t *pts = (uint64_t*)av_malloc(sizeof(uint64_t)); - *pts = global_video_pkt_pts; - pic->opaque = pts; - return ret; - } - void our_release_buffer(struct AVCodecContext *c, AVFrame *pic) - { - if(pic) av_freep(&pic->opaque); - avcodec_default_release_buffer(c, pic); - } +void VideoState::video_thread_loop(VideoState *self) +{ + AVPacket pkt1, *packet = &pkt1; + int frameFinished; + AVFrame *pFrame; + double pts; - int video_thread(void *arg) - { - VideoState *is = (VideoState *)arg; - AVPacket pkt1, *packet = &pkt1; - int frameFinished; - AVFrame *pFrame; - double pts; + pFrame = avcodec_alloc_frame(); - pFrame = avcodec_alloc_frame(); + self->rgbaFrame = avcodec_alloc_frame(); + avpicture_alloc((AVPicture*)self->rgbaFrame, PIX_FMT_RGBA, self->video_st->codec->width, self->video_st->codec->height); - is->rgbaFrame = avcodec_alloc_frame(); - avpicture_alloc((AVPicture*)is->rgbaFrame, PIX_FMT_RGBA, is->video_st->codec->width, is->video_st->codec->height); + while(self->videoq.get(packet, self, 1) >= 0) + { + // Save global pts to be stored in pFrame + global_video_pkt_pts = packet->pts; + // Decode video frame + if(avcodec_decode_video2(self->video_st->codec, pFrame, &frameFinished, packet) < 0) + throw std::runtime_error("Error decoding video frame"); - for(;;) + pts = 0; + if((uint64_t)packet->dts != AV_NOPTS_VALUE) + pts = packet->dts; + else if(pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) + pts = *(uint64_t*)pFrame->opaque; + pts *= av_q2d(self->video_st->time_base); + + // Did we get a video frame? + if(frameFinished) { - if(is->videoq.get(packet, is, 1) < 0) - { - // means we quit getting packets + pts = self->synchronize_video(pFrame, pts); + if(self->queue_picture(pFrame, pts) < 0) break; - } - pts = 0; - - // Save global pts to be stored in pFrame - global_video_pkt_pts = packet->pts; - // Decode video frame - if (avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, packet) < 0) - throw std::runtime_error("Error decoding video frame"); - - if((uint64_t)packet->dts == AV_NOPTS_VALUE && - pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) - pts = *(uint64_t *)pFrame->opaque; - else if((uint64_t)packet->dts != AV_NOPTS_VALUE) - pts = packet->dts; - else - pts = 0; - pts *= av_q2d(is->video_st->time_base); - - // Did we get a video frame? - if(frameFinished) - { - pts = synchronize_video(is, pFrame, pts); - if(queue_picture(is, pFrame, pts) < 0) - break; - } - av_free_packet(packet); } + av_free_packet(packet); + } - av_free(pFrame); + av_free(pFrame); - avpicture_free((AVPicture *)is->rgbaFrame); - av_free(is->rgbaFrame); + avpicture_free((AVPicture*)self->rgbaFrame); + av_free(self->rgbaFrame); +} - return 0; - } +void VideoState::decode_thread_loop(VideoState *self) +{ + AVFormatContext *pFormatCtx = self->format_ctx; + AVPacket pkt1, *packet = &pkt1; - int stream_component_open(VideoState *is, int stream_index, AVFormatContext *pFormatCtx) + try { - MWSound::DecoderPtr decoder; - AVCodecContext *codecCtx; - AVCodec *codec; - - if(stream_index < 0 || stream_index >= static_cast(pFormatCtx->nb_streams)) - return -1; - - // Get a pointer to the codec context for the video stream - codecCtx = pFormatCtx->streams[stream_index]->codec; - codec = avcodec_find_decoder(codecCtx->codec_id); - if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) - { - fprintf(stderr, "Unsupported codec!\n"); - return -1; - } + if(self->videoStream < 0 && self->audioStream < 0) + throw std::runtime_error("No streams to decode"); - switch(codecCtx->codec_type) + // main decode loop + for(;;) { - case AVMEDIA_TYPE_AUDIO: - is->audioStream = stream_index; - is->audio_st = pFormatCtx->streams[stream_index]; - - /* averaging filter for audio sync */ - is->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); - is->audio_diff_avg_count = 0; - /* Correct audio only if larger error than this */ - is->audio_diff_threshold = 2.0 * 0.1/* 100 ms */; - - memset(&is->audio_pkt, 0, sizeof(is->audio_pkt)); + if(self->quit) + break; - decoder.reset(new MovieAudioDecoder(is)); - is->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); - if(!is->AudioTrack) + if((self->audioStream >= 0 && self->audioq.size > MAX_AUDIOQ_SIZE) || + (self->videoStream >= 0 && self->videoq.size > MAX_VIDEOQ_SIZE)) { - is->audioStream = -1; - avcodec_close(is->audio_st->codec); - is->audio_st = NULL; - return -1; + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + continue; } - break; - case AVMEDIA_TYPE_VIDEO: - is->videoStream = stream_index; - is->video_st = pFormatCtx->streams[stream_index]; + if(av_read_frame(pFormatCtx, packet) < 0) + break; - is->frame_timer = (double)av_gettime() / 1000000.0; - is->frame_last_delay = 40e-3; - is->video_current_pts_time = av_gettime(); + // Is this a packet from the video stream? + if(packet->stream_index == self->videoStream) + self->videoq.put(packet); + else if(packet->stream_index == self->audioStream) + self->audioq.put(packet); + else + av_free_packet(packet); + } + /* all done - wait for it */ + while(!self->quit) + { + // EOF reached, all packets processed, we can exit now + if(self->audioq.nb_packets == 0 && self->videoq.nb_packets == 0) + break; + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } + } + catch(std::runtime_error& e) { + std::cerr << "An error occured playing the video: " << e.what () << std::endl; + } + catch(Ogre::Exception& e) { + std::cerr << "An error occured playing the video: " << e.getFullDescription () << std::endl; + } - codecCtx->get_buffer = our_get_buffer; - codecCtx->release_buffer = our_release_buffer; - is->video_thread = boost::thread(video_thread, is); - break; + self->quit = 1; +} - default: - break; - } - return 0; - } +int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) +{ + MWSound::DecoderPtr decoder; + AVCodecContext *codecCtx; + AVCodec *codec; - int decode_thread(void *arg) - { - VideoState *is = (VideoState *)arg; - try - { - AVFormatContext *pFormatCtx = is->format_ctx; - AVPacket pkt1, *packet = &pkt1; + if(stream_index < 0 || stream_index >= static_cast(pFormatCtx->nb_streams)) + return -1; - if(is->videoStream >= 0 /*|| is->audioStream < 0*/) - { - // main decode loop - for(;;) - { - if(is->quit) - break; + // Get a pointer to the codec context for the video stream + codecCtx = pFormatCtx->streams[stream_index]->codec; + codec = avcodec_find_decoder(codecCtx->codec_id); + if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) + { + fprintf(stderr, "Unsupported codec!\n"); + return -1; + } - if((is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || - is->videoq.size > MAX_VIDEOQ_SIZE) - { - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - continue; - } + switch(codecCtx->codec_type) + { + case AVMEDIA_TYPE_AUDIO: + this->audioStream = stream_index; + this->audio_st = pFormatCtx->streams[stream_index]; - if(av_read_frame(pFormatCtx, packet) < 0) - break; + /* averaging filter for audio sync */ + this->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); + this->audio_diff_avg_count = 0; + /* Correct audio only if larger error than this */ + this->audio_diff_threshold = 2.0 * 0.1/* 100 ms */; - // Is this a packet from the video stream? - if(packet->stream_index == is->videoStream) - is->videoq.put(packet); - else if(packet->stream_index == is->audioStream) - is->audioq.put(packet); - else - av_free_packet(packet); - } - /* all done - wait for it */ - while(!is->quit) - { - // EOF reached, all packets processed, we can exit now - if(is->audioq.nb_packets == 0 && is->videoq.nb_packets == 0) - break; - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - } - } + memset(&this->audio_pkt, 0, sizeof(this->audio_pkt)); - is->quit = 1; - } - catch (std::runtime_error& e) + decoder.reset(new MovieAudioDecoder(this)); + this->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); + if(!this->AudioTrack) { - std::cerr << "An error occured playing the video: " << e.what () << std::endl; - is->quit = 1; - } - catch (Ogre::Exception& e) - { - std::cerr << "An error occured playing the video: " << e.getFullDescription () << std::endl; - is->quit = 1; + this->audioStream = -1; + avcodec_close(this->audio_st->codec); + this->audio_st = NULL; + return -1; } + break; - return 0; - } + case AVMEDIA_TYPE_VIDEO: + this->videoStream = stream_index; + this->video_st = pFormatCtx->streams[stream_index]; - void VideoState::init(const std::string& resourceName) - { - try - { - int video_index = -1; - int audio_index = -1; - unsigned int i; - - this->av_sync_type = DEFAULT_AV_SYNC_TYPE; - this->videoStream = -1; - this->audioStream = -1; - this->refresh = false; - this->quit = 0; + this->frame_timer = (double)av_gettime() / 1000000.0; + this->frame_last_delay = 40e-3; + this->video_current_pts_time = av_gettime(); - this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); - if(this->stream.isNull()) - throw std::runtime_error("Failed to open video resource"); + codecCtx->get_buffer = our_get_buffer; + codecCtx->release_buffer = our_release_buffer; + this->video_thread = boost::thread(video_thread_loop, this); + break; - this->format_ctx = avformat_alloc_context(); - this->format_ctx->pb = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if(!this->format_ctx->pb) - { - avformat_free_context(this->format_ctx); - throw std::runtime_error("Failed to allocate ioContext "); - } + default: + break; + } - // Open video file - /// \todo leak here, ffmpeg or valgrind bug ? - if (avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) - { - // "Note that a user-supplied AVFormatContext will be freed on failure." - this->format_ctx = NULL; - throw std::runtime_error("Failed to open video input"); - } + return 0; +} - // Retrieve stream information - if(avformat_find_stream_info(this->format_ctx, NULL) < 0) - throw std::runtime_error("Failed to retrieve stream information"); +void VideoState::init(const std::string& resourceName) +{ + try + { + int video_index = -1; + int audio_index = -1; + unsigned int i; - // Dump information about file onto standard error - av_dump_format(this->format_ctx, 0, resourceName.c_str(), 0); + this->av_sync_type = DEFAULT_AV_SYNC_TYPE; + this->videoStream = -1; + this->audioStream = -1; + this->refresh = false; + this->quit = 0; - for(i = 0;i < this->format_ctx->nb_streams;i++) - { - if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) - video_index = i; - if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) - audio_index = i; - } + this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); + if(this->stream.isNull()) + throw std::runtime_error("Failed to open video resource"); - if(audio_index >= 0) - stream_component_open(this, audio_index, this->format_ctx); - if(video_index >= 0) - stream_component_open(this, video_index, this->format_ctx); + this->format_ctx = avformat_alloc_context(); + this->format_ctx->pb = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if(!this->format_ctx->pb) + { + avformat_free_context(this->format_ctx); + throw std::runtime_error("Failed to allocate ioContext "); } - catch(std::runtime_error& e) + + // Open video file + /// \todo leak here, ffmpeg or valgrind bug ? + if (avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) { - this->quit = 1; - throw; + // "Note that a user-supplied AVFormatContext will be freed on failure." + this->format_ctx = NULL; + throw std::runtime_error("Failed to open video input"); } - catch(Ogre::Exception& e) + + // Retrieve stream information + if(avformat_find_stream_info(this->format_ctx, NULL) < 0) + throw std::runtime_error("Failed to retrieve stream information"); + + // Dump information about file onto standard error + av_dump_format(this->format_ctx, 0, resourceName.c_str(), 0); + + for(i = 0;i < this->format_ctx->nb_streams;i++) { - this->quit = 1; - throw; + if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) + video_index = i; + if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) + audio_index = i; } - } - void VideoState::deinit() + if(audio_index >= 0) + this->stream_open(audio_index, this->format_ctx); + if(video_index >= 0) + this->stream_open(video_index, this->format_ctx); + + this->schedule_refresh(40); + this->parse_thread = boost::thread(decode_thread_loop, this); + } + catch(std::runtime_error& e) { - this->audioq.cond.notify_one(); - this->videoq.cond.notify_one(); + this->quit = 1; + throw; + } + catch(Ogre::Exception& e) + { + this->quit = 1; + throw; + } +} - this->parse_thread.join(); - this->video_thread.join(); +void VideoState::deinit() +{ + this->audioq.cond.notify_one(); + this->videoq.cond.notify_one(); - if(this->audioStream >= 0) - avcodec_close(this->audio_st->codec); - if(this->videoStream >= 0) - avcodec_close(this->video_st->codec); + this->parse_thread.join(); + this->video_thread.join(); - if(this->sws_context) - sws_freeContext(this->sws_context); + if(this->audioStream >= 0) + avcodec_close(this->audio_st->codec); + if(this->videoStream >= 0) + avcodec_close(this->video_st->codec); - if(this->format_ctx) - { - AVIOContext *ioContext = this->format_ctx->pb; - avformat_close_input(&this->format_ctx); - av_free(ioContext); - } + if(this->sws_context) + sws_freeContext(this->sws_context); + + if(this->format_ctx) + { + AVIOContext *ioContext = this->format_ctx->pb; + avformat_close_input(&this->format_ctx); + av_free(ioContext); } +} VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) @@ -942,9 +923,6 @@ int64_t VideoState::OgreResource_Seek(void *user_data, int64_t offset, int whenc mState = new VideoState; mState->init(resourceName); - - schedule_refresh(mState, 40); - mState->parse_thread = boost::thread(decode_thread, mState); } void VideoPlayer::update () @@ -956,7 +934,7 @@ int64_t VideoState::OgreResource_Seek(void *user_data, int64_t offset, int whenc else if(mState->refresh) { mState->refresh = false; - video_refresh_timer(mState); + mState->video_refresh_timer(); } } diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 82a7083ae..4bbd81a6e 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -83,6 +83,20 @@ namespace MWRender void init(const std::string& resourceName); void deinit(); + int stream_open(int stream_index, AVFormatContext *pFormatCtx); + + static void video_thread_loop(VideoState *is); + static void decode_thread_loop(VideoState *is); + + void video_display(); + void video_refresh_timer(); + + int queue_picture(AVFrame *pFrame, double pts); + double synchronize_video(AVFrame *src_frame, double pts); + + static void timer_callback(VideoState* is, boost::system_time t); + void schedule_refresh(int delay); + static int OgreResource_Read(void *user_data, uint8_t *buf, int buf_size); static int OgreResource_Write(void *user_data, uint8_t *buf, int buf_size); static int64_t OgreResource_Seek(void *user_data, int64_t offset, int whence); From 157cb10f561112fe174346faef925ec6b32219c8 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 02:39:46 -0800 Subject: [PATCH 154/378] Fix a 16-bit audio assumption --- apps/openmw/mwrender/videoplayer.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 4334bc634..6271b2f18 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -145,14 +145,8 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) return samples_size; - double diff, avg_diff, ref_clock; - int wanted_size, min_size, max_size, n; - // int nb_samples; - - n = 2 * is->audio_st->codec->channels; - - ref_clock = get_master_clock(is); - diff = get_audio_clock(is) - ref_clock; + double ref_clock = get_master_clock(is); + double diff = get_audio_clock(is) - ref_clock; if(diff < AV_NOSYNC_THRESHOLD) { // accumulate the diffs @@ -162,12 +156,14 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder is->audio_diff_avg_count++; else { - avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); + double avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); if(fabs(avg_diff) >= is->audio_diff_threshold) { - wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); - min_size = samples_size/n * (100-SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; - max_size = samples_size/n * (100+SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; + int n = av_samples_get_buffer_size(NULL, is->audio_st->codec->channels, 1, + is->audio_st->codec->sample_fmt, 1); + int wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); + int min_size = samples_size/n * (100-SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; + int max_size = samples_size/n * (100+SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; if(wanted_size < min_size) wanted_size = min_size; From 8db5d10f1013ba5735819a479a627587d03b2c9a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 03:11:59 -0800 Subject: [PATCH 155/378] Avoid showing a video picture if we're late Ideally we should skip decoding, or at least YUV->RGB conversion, too. --- apps/openmw/mwrender/videoplayer.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 6271b2f18..432cfae3e 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -447,8 +447,6 @@ void VideoState::video_display() buffer->blitFromMemory(pb); this->display_ready = 1; } - - free(vp->data); } void VideoState::video_refresh_timer() @@ -504,17 +502,21 @@ void VideoState::video_refresh_timer() actual_delay = this->frame_timer - (av_gettime() / 1000000.0); if(actual_delay < 0.010) { - /* Really it should skip the picture instead */ - actual_delay = 0.010; + /* Skip this picture */ + this->refresh = true; + } + else + { + this->schedule_refresh((int)(actual_delay * 1000 + 0.5)); + /* show the picture! */ + this->video_display(); } - this->schedule_refresh((int)(actual_delay * 1000 + 0.5)); - /* show the picture! */ - this->video_display(); + free(vp->data); + vp->data = NULL; /* update queue for next picture! */ - if(++this->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) - this->pictq_rindex = 0; + this->pictq_rindex = (this->pictq_rindex+1) % VIDEO_PICTURE_QUEUE_SIZE; this->pictq_mutex.lock(); this->pictq_size--; this->pictq_cond.notify_one(); @@ -550,13 +552,12 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) throw std::runtime_error("Cannot initialize the conversion context!\n"); } + vp->pts = pts; vp->data = (uint8_t*)malloc(this->video_st->codec->width * this->video_st->codec->height * 4); sws_scale(this->sws_context, pFrame->data, pFrame->linesize, 0, this->video_st->codec->height, &vp->data, this->rgbaFrame->linesize); - vp->pts = pts; - // now we inform our display thread that we have a pic ready this->pictq_windex = (this->pictq_windex+1) % VIDEO_PICTURE_QUEUE_SIZE; this->pictq_mutex.lock(); From 4d6c05f6ccd4855f6538cb4f7f5e4961a9316cd3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 03:19:32 -0800 Subject: [PATCH 156/378] Tighten audio skew allowance --- apps/openmw/mwrender/videoplayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 432cfae3e..b901c097d 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -733,7 +733,7 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) this->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); this->audio_diff_avg_count = 0; /* Correct audio only if larger error than this */ - this->audio_diff_threshold = 2.0 * 0.1/* 100 ms */; + this->audio_diff_threshold = 2.0 * 0.025/* 25 ms */; memset(&this->audio_pkt, 0, sizeof(this->audio_pkt)); From a6e627001a128948708028170e846243a787f987 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 03:23:04 -0800 Subject: [PATCH 157/378] Avoid a for(;;) construct --- apps/openmw/mwrender/videoplayer.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index b901c097d..b9ff81de0 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -662,11 +662,8 @@ void VideoState::decode_thread_loop(VideoState *self) throw std::runtime_error("No streams to decode"); // main decode loop - for(;;) + while(!self->quit) { - if(self->quit) - break; - if((self->audioStream >= 0 && self->audioq.size > MAX_AUDIOQ_SIZE) || (self->videoStream >= 0 && self->videoq.size > MAX_VIDEOQ_SIZE)) { From 2f37d31108f5190ee5312d724431b20b8fabe879 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 04:12:34 -0800 Subject: [PATCH 158/378] Move some definitions into the source file they're used in --- apps/openmw/mwrender/videoplayer.cpp | 18 +++++++++++++++++- apps/openmw/mwrender/videoplayer.hpp | 13 ------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index b9ff81de0..9cbac1688 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -8,9 +8,25 @@ #include "../mwsound/sound.hpp" +#define MAX_AUDIOQ_SIZE (5 * 16 * 1024) +#define MAX_VIDEOQ_SIZE (5 * 256 * 1024) +#define AV_SYNC_THRESHOLD 0.01 +#define AV_NOSYNC_THRESHOLD 10.0 +#define SAMPLE_CORRECTION_PERCENT_MAX 10 +#define AUDIO_DIFF_AVG_NB 20 + + namespace MWRender { +enum { + AV_SYNC_AUDIO_MASTER, + AV_SYNC_VIDEO_MASTER, + AV_SYNC_EXTERNAL_MASTER, + + AV_SYNC_DEFAULT = AV_SYNC_EXTERNAL_MASTER +}; + void PacketQueue::put(AVPacket *pkt) { AVPacketList *pkt1; @@ -773,7 +789,7 @@ void VideoState::init(const std::string& resourceName) int audio_index = -1; unsigned int i; - this->av_sync_type = DEFAULT_AV_SYNC_TYPE; + this->av_sync_type = AV_SYNC_DEFAULT; this->videoStream = -1; this->audioStream = -1; this->refresh = false; diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 4bbd81a6e..428907c4a 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -21,15 +21,7 @@ extern "C" #include "../mwbase/soundmanager.hpp" -#define MAX_AUDIOQ_SIZE (5 * 16 * 1024) -#define MAX_VIDEOQ_SIZE (5 * 256 * 1024) -#define AV_SYNC_THRESHOLD 0.01 -#define AV_NOSYNC_THRESHOLD 10.0 -#define SAMPLE_CORRECTION_PERCENT_MAX 10 -#define AUDIO_DIFF_AVG_NB 20 #define VIDEO_PICTURE_QUEUE_SIZE 1 -#define DEFAULT_AV_SYNC_TYPE AV_SYNC_EXTERNAL_MASTER - namespace MWRender { @@ -147,11 +139,6 @@ namespace MWRender int display_ready; }; - enum { - AV_SYNC_AUDIO_MASTER, - AV_SYNC_VIDEO_MASTER, - AV_SYNC_EXTERNAL_MASTER - }; class VideoPlayer From 05c6483257f2947f1ea24b3e102dc8b8e03e60af Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 04:35:57 -0800 Subject: [PATCH 159/378] Fix external clock --- apps/openmw/mwrender/videoplayer.cpp | 5 +++-- apps/openmw/mwrender/videoplayer.hpp | 17 ++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 9cbac1688..56f0bc691 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -128,7 +128,7 @@ static double get_video_clock(VideoState *is) static double get_external_clock(VideoState *is) { - return av_gettime() / 1000000.0; + return ((uint64_t)av_gettime()-is->external_clock_base) / 1000000.0; } static double get_master_clock(VideoState *is) @@ -746,7 +746,7 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) this->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); this->audio_diff_avg_count = 0; /* Correct audio only if larger error than this */ - this->audio_diff_threshold = 2.0 * 0.025/* 25 ms */; + this->audio_diff_threshold = 2.0 * 0.050/* 50 ms */; memset(&this->audio_pkt, 0, sizeof(this->audio_pkt)); @@ -831,6 +831,7 @@ void VideoState::init(const std::string& resourceName) audio_index = i; } + this->external_clock_base = av_gettime(); if(audio_index >= 0) this->stream_open(audio_index, this->format_ctx); if(video_index >= 0) diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 428907c4a..798611d90 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -55,12 +55,12 @@ namespace MWRender struct VideoState { VideoState () - : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock(0), - external_clock_time(0), audio_clock(0), audio_st(NULL), audio_diff_cum(0), - audio_diff_avg_coef(0), audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), - frame_last_pts(0), frame_last_delay(0), video_clock(0), video_current_pts(0), - video_current_pts_time(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), - pictq_windex(0), quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) + : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock_base(0), + audio_clock(0), audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), + audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), + frame_last_delay(0), video_clock(0), video_current_pts(0), video_current_pts_time(0), + video_st(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), + quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) {} ~VideoState() @@ -95,9 +95,8 @@ namespace MWRender int videoStream, audioStream; - int av_sync_type; - double external_clock; /* external clock base */ - int64_t external_clock_time; + int av_sync_type; + uint64_t external_clock_base; double audio_clock; AVStream *audio_st; From 7332ffb0f865e61e2359d816b9156bf1f732652c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 06:02:34 -0800 Subject: [PATCH 160/378] Let the wanted sample size go down to 0 --- apps/openmw/mwrender/videoplayer.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 56f0bc691..4e9dd0ca2 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -178,13 +178,10 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder int n = av_samples_get_buffer_size(NULL, is->audio_st->codec->channels, 1, is->audio_st->codec->sample_fmt, 1); int wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); - int min_size = samples_size/n * (100-SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; int max_size = samples_size/n * (100+SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; - if(wanted_size < min_size) - wanted_size = min_size; - else if (wanted_size > max_size) - wanted_size = max_size; + wanted_size = std::max(0, wanted_size); + wanted_size = std::min(wanted_size, max_size); if(wanted_size < samples_size) { From d66d8a3118926c5577501980ead59ad24ab03026 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 06:43:51 -0800 Subject: [PATCH 161/378] Don't assume we can write beyond the end of the sample buffer --- apps/openmw/mwrender/videoplayer.cpp | 89 ++++++++++++++++------------ 1 file changed, 50 insertions(+), 39 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 4e9dd0ca2..93255dd45 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -151,8 +151,8 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder VideoState *is; AVFrame *mFrame; - size_t mFramePos; - size_t mFrameSize; + ssize_t mFramePos; + ssize_t mFrameSize; /* Add or subtract samples to get a better sync, return new * audio buffer size */ @@ -175,38 +175,14 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder double avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); if(fabs(avg_diff) >= is->audio_diff_threshold) { - int n = av_samples_get_buffer_size(NULL, is->audio_st->codec->channels, 1, - is->audio_st->codec->sample_fmt, 1); + int n = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * + is->audio_st->codec->channels; int wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); - int max_size = samples_size/n * (100+SAMPLE_CORRECTION_PERCENT_MAX) / 100 * n; wanted_size = std::max(0, wanted_size); - wanted_size = std::min(wanted_size, max_size); - - if(wanted_size < samples_size) - { - /* remove samples */ - samples_size = wanted_size; - } - else if(wanted_size > samples_size) - { - uint8_t *samples_end, *q; - int nb; - - /* add samples by copying final sample*/ - nb = (samples_size - wanted_size); - samples_end = samples + samples_size - n; - q = samples_end + n; - - while(nb > 0) - { - memcpy(q, samples_end, n); - q += n; - nb -= n; - } - - samples_size = wanted_size; - } + wanted_size = std::min(wanted_size, samples_size*2); + + samples_size = wanted_size; } } } @@ -249,8 +225,8 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder if(!got_frame) continue; - int smp_size = av_samples_get_buffer_size(NULL, is->audio_st->codec->channels, 1, - is->audio_st->codec->sample_fmt, 1); + int smp_size = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * + is->audio_st->codec->channels; data_size = frame->nb_samples * smp_size; if(data_size <= 0) { @@ -360,19 +336,54 @@ public: double pts; /* We have already sent all our data; get more */ - audio_size = audio_decode_frame(mFrame, &pts); - if(audio_size < 0) + mFrameSize = audio_decode_frame(mFrame, &pts); + if(mFrameSize < 0) { /* If error, we're done */ break; } - mFrameSize = synchronize_audio(mFrame->data[0], audio_size, pts); - mFramePos = 0; + audio_size = synchronize_audio(mFrame->data[0], mFrameSize, pts); + mFramePos = mFrameSize - audio_size; + } + + size_t len1 = len - total; + if(mFramePos >= 0) + { + len1 = std::min(len1, mFrameSize-mFramePos); + memcpy(stream, mFrame->data[0]+mFramePos, len1); } + else + { + len1 = std::min(len1, -mFramePos); - size_t len1 = std::min(len - total, mFrameSize-mFramePos); - memcpy(stream, mFrame->data[0]+mFramePos, len1); + int n = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * + is->audio_st->codec->channels; + + /* add samples by copying the first sample*/ + if(n == 1) + memset(stream, *mFrame->data[0], len1); + else if(n == 2) + { + for(size_t nb = 0;nb < len1;nb += n) + *((int16_t*)(stream+nb)) = *((int16_t*)mFrame->data[0]); + } + else if(n == 4) + { + for(size_t nb = 0;nb < len1;nb += n) + *((int32_t*)(stream+nb)) = *((int32_t*)mFrame->data[0]); + } + else if(n == 8) + { + for(size_t nb = 0;nb < len1;nb += n) + *((int64_t*)(stream+nb)) = *((int64_t*)mFrame->data[0]); + } + else + { + for(size_t nb = 0;nb < len1;nb += n) + memcpy(stream+nb, mFrame->data[0], n); + } + } total += len1; stream += len1; From f97eaec7ab447ab8d0f2818b38118f7cb561ad83 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 07:25:28 -0800 Subject: [PATCH 162/378] Consolidate some code --- apps/openmw/mwrender/videoplayer.cpp | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 93255dd45..396b91c33 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -199,20 +199,15 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder int audio_decode_frame(AVFrame *frame, double *pts_ptr) { AVPacket *pkt = &is->audio_pkt; - int len1, data_size; for(;;) { while(pkt->size > 0) { - int got_frame; + int len1, got_frame; len1 = avcodec_decode_audio4(is->audio_st->codec, frame, &got_frame, pkt); - if(len1 < 0 || len1 > pkt->size) - { - /* if error, skip packet */ - break; - } + if(len1 < 0) break; if(len1 <= pkt->size) { @@ -222,24 +217,18 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder memset(&pkt->data[remaining], 0, pkt->size - remaining); pkt->size -= len1; } - if(!got_frame) - continue; - int smp_size = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * - is->audio_st->codec->channels; - data_size = frame->nb_samples * smp_size; - if(data_size <= 0) - { - /* No data yet, get more frames */ + /* No data yet? Look for more frames */ + if(!got_frame || frame->nb_samples <= 0) continue; - } *pts_ptr = is->audio_clock; - is->audio_clock += (double)(data_size/smp_size) / + is->audio_clock += (double)frame->nb_samples / (double)is->audio_st->codec->sample_rate; /* We have data, return it and come back for more later */ - return data_size; + return frame->nb_samples * av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * + is->audio_st->codec->channels; } if(pkt->data) av_free_packet(pkt); From 582efcdb9bc13bac35abcd9d65a4a7a285a3eeb5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 07:40:19 -0800 Subject: [PATCH 163/378] Always try to resync if the clock difference is large --- apps/openmw/mwrender/videoplayer.cpp | 56 +++++++++++----------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 396b91c33..bdfffd0cb 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -11,7 +11,6 @@ #define MAX_AUDIOQ_SIZE (5 * 16 * 1024) #define MAX_VIDEOQ_SIZE (5 * 256 * 1024) #define AV_SYNC_THRESHOLD 0.01 -#define AV_NOSYNC_THRESHOLD 10.0 #define SAMPLE_CORRECTION_PERCENT_MAX 10 #define AUDIO_DIFF_AVG_NB 20 @@ -161,37 +160,27 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) return samples_size; - double ref_clock = get_master_clock(is); - double diff = get_audio_clock(is) - ref_clock; - if(diff < AV_NOSYNC_THRESHOLD) + // accumulate the clock difference + double diff = get_audio_clock(is) - get_master_clock(is); + is->audio_diff_cum = diff + is->audio_diff_avg_coef * + is->audio_diff_cum; + if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) + is->audio_diff_avg_count++; + else { - // accumulate the diffs - is->audio_diff_cum = diff + is->audio_diff_avg_coef * - is->audio_diff_cum; - if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) - is->audio_diff_avg_count++; - else + double avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); + if(fabs(avg_diff) >= is->audio_diff_threshold) { - double avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); - if(fabs(avg_diff) >= is->audio_diff_threshold) - { - int n = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * - is->audio_st->codec->channels; - int wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); + int n = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * + is->audio_st->codec->channels; + int wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); - wanted_size = std::max(0, wanted_size); - wanted_size = std::min(wanted_size, samples_size*2); + wanted_size = std::max(0, wanted_size); + wanted_size = std::min(wanted_size, samples_size*2); - samples_size = wanted_size; - } + samples_size = wanted_size; } } - else - { - /* difference is TOO big; reset diff stuff */ - is->audio_diff_avg_count = 0; - is->audio_diff_cum = 0; - } return samples_size; } @@ -499,15 +488,12 @@ void VideoState::video_refresh_timer() diff = vp->pts - ref_clock; /* Skip or repeat the frame. Take delay into account - FFPlay still doesn't "know if this is the best guess." */ - sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; - if(fabs(diff) < AV_NOSYNC_THRESHOLD) - { - if(diff <= -sync_threshold) - delay = 0; - else if(diff >= sync_threshold) - delay = 2 * delay; - } + * FFPlay still doesn't "know if this is the best guess." */ + sync_threshold = std::max(delay, AV_SYNC_THRESHOLD); + if(diff <= -sync_threshold) + delay = 0; + else if(diff >= sync_threshold) + delay = 2 * delay; } this->frame_timer += delay; From 71ff90aaee5bcd8208996a66de53ec3ceee2088a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 08:42:37 -0800 Subject: [PATCH 164/378] Don't use sub-frame timing for the video clock --- apps/openmw/mwrender/videoplayer.cpp | 11 +++-------- apps/openmw/mwrender/videoplayer.hpp | 11 +++++------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index bdfffd0cb..fe228d25e 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -119,10 +119,7 @@ static double get_audio_clock(VideoState *is) static double get_video_clock(VideoState *is) { - double delta; - - delta = (av_gettime() - is->video_current_pts_time) / 1000000.0; - return is->video_current_pts + delta; + return is->video_current_pts; } static double get_external_clock(VideoState *is) @@ -469,9 +466,6 @@ void VideoState::video_refresh_timer() vp = &this->pictq[this->pictq_rindex]; - this->video_current_pts = vp->pts; - this->video_current_pts_time = av_gettime(); - delay = vp->pts - this->frame_last_pts; /* the pts from last time */ if(delay <= 0 || delay >= 1.0) { /* if incorrect delay, use previous one */ @@ -481,6 +475,8 @@ void VideoState::video_refresh_timer() this->frame_last_delay = delay; this->frame_last_pts = vp->pts; + this->video_current_pts = vp->pts; + /* update delay to sync to audio if not master source */ if(this->av_sync_type != AV_SYNC_VIDEO_MASTER) { @@ -750,7 +746,6 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) this->frame_timer = (double)av_gettime() / 1000000.0; this->frame_last_delay = 40e-3; - this->video_current_pts_time = av_gettime(); codecCtx->get_buffer = our_get_buffer; codecCtx->release_buffer = our_release_buffer; diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 798611d90..1213ca36c 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -58,9 +58,9 @@ namespace MWRender : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock_base(0), audio_clock(0), audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), - frame_last_delay(0), video_clock(0), video_current_pts(0), video_current_pts_time(0), - video_st(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), - quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) + frame_last_delay(0), video_clock(0), video_current_pts(0), video_st(NULL), + rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), quit(false), + refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) {} ~VideoState() @@ -68,8 +68,8 @@ namespace MWRender audioq.flush(); videoq.flush(); - if(pictq_size >= 1) - free(pictq[0].data); + for(int i = 0;i < VIDEO_PICTURE_QUEUE_SIZE;i++) + free(pictq[i].data); } void init(const std::string& resourceName); @@ -112,7 +112,6 @@ namespace MWRender double frame_last_delay; double video_clock; /// Date: Fri, 14 Dec 2012 09:07:59 -0800 Subject: [PATCH 165/378] Move more stuff to where it should be, and improve cleanup --- apps/openmw/mwrender/videoplayer.cpp | 174 +++++++++++++++++++++------ apps/openmw/mwrender/videoplayer.hpp | 115 ------------------ 2 files changed, 138 insertions(+), 151 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index fe228d25e..2a545f772 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -13,6 +13,7 @@ #define AV_SYNC_THRESHOLD 0.01 #define SAMPLE_CORRECTION_PERCENT_MAX 10 #define AUDIO_DIFF_AVG_NB 20 +#define VIDEO_PICTURE_QUEUE_SIZE 1 namespace MWRender @@ -26,6 +27,135 @@ enum { AV_SYNC_DEFAULT = AV_SYNC_EXTERNAL_MASTER }; +struct PacketQueue { + PacketQueue() + : first_pkt(NULL), last_pkt(NULL), nb_packets(0), size(0) + { } + ~PacketQueue() + { flush(); } + + AVPacketList *first_pkt, *last_pkt; + int nb_packets; + int size; + + boost::mutex mutex; + boost::condition_variable cond; + + void put(AVPacket *pkt); + int get(AVPacket *pkt, VideoState *is, int block); + + void flush(); +}; + +struct VideoPicture { + VideoPicture() : pts(0.0) + { } + + std::vector data; + double pts; +}; + +struct VideoState { + VideoState() + : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock_base(0), + audio_clock(0), audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), + audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), + frame_last_delay(0), video_clock(0), video_current_pts(0), video_st(NULL), + rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), quit(false), + refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) + { } + + ~VideoState() + { } + + void init(const std::string& resourceName); + void deinit(); + + int stream_open(int stream_index, AVFormatContext *pFormatCtx); + + static void video_thread_loop(VideoState *is); + static void decode_thread_loop(VideoState *is); + + void video_display(); + void video_refresh_timer(); + + int queue_picture(AVFrame *pFrame, double pts); + double synchronize_video(AVFrame *src_frame, double pts); + + static void timer_callback(VideoState* is, boost::system_time t); + void schedule_refresh(int delay); + + + double get_audio_clock() + { return this->AudioTrack->getTimeOffset(); } + + double get_video_clock() + { return this->video_current_pts; } + + double get_external_clock() + { return ((uint64_t)av_gettime()-this->external_clock_base) / 1000000.0; } + + double get_master_clock() + { + if(this->av_sync_type == AV_SYNC_VIDEO_MASTER) + return this->get_video_clock(); + if(this->av_sync_type == AV_SYNC_AUDIO_MASTER) + return this->get_audio_clock(); + return this->get_external_clock(); + } + + + static int OgreResource_Read(void *user_data, uint8_t *buf, int buf_size); + static int OgreResource_Write(void *user_data, uint8_t *buf, int buf_size); + static int64_t OgreResource_Seek(void *user_data, int64_t offset, int whence); + + + int videoStream, audioStream; + + int av_sync_type; + uint64_t external_clock_base; + + double audio_clock; + AVStream *audio_st; + PacketQueue audioq; + AVPacket audio_pkt; + double audio_diff_cum; /* used for AV difference average computation */ + double audio_diff_avg_coef; + double audio_diff_threshold; + int audio_diff_avg_count; + + double frame_timer; + double frame_last_pts; + double frame_last_delay; + double video_clock; ///AudioTrack->getTimeOffset(); -} - -static double get_video_clock(VideoState *is) -{ - return is->video_current_pts; -} - -static double get_external_clock(VideoState *is) -{ - return ((uint64_t)av_gettime()-is->external_clock_base) / 1000000.0; -} - -static double get_master_clock(VideoState *is) -{ - if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) - return get_video_clock(is); - if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) - return get_audio_clock(is); - return get_external_clock(is); -} - - class MovieAudioDecoder : public MWSound::Sound_Decoder { static void fail(const std::string &str) @@ -158,7 +263,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder return samples_size; // accumulate the clock difference - double diff = get_audio_clock(is) - get_master_clock(is); + double diff = is->get_audio_clock() - is->get_master_clock(); is->audio_diff_cum = diff + is->audio_diff_avg_coef * is->audio_diff_cum; if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) @@ -441,7 +546,7 @@ void VideoState::video_display() Ogre::PF_BYTE_RGBA, Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); } - Ogre::PixelBox pb(this->video_st->codec->width, this->video_st->codec->height, 1, Ogre::PF_BYTE_RGBA, vp->data); + Ogre::PixelBox pb(this->video_st->codec->width, this->video_st->codec->height, 1, Ogre::PF_BYTE_RGBA, &vp->data[0]); Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer(); buffer->blitFromMemory(pb); this->display_ready = 1; @@ -451,7 +556,7 @@ void VideoState::video_display() void VideoState::video_refresh_timer() { VideoPicture *vp; - double actual_delay, delay, sync_threshold, ref_clock, diff; + double actual_delay, delay; if(!this->video_st) { @@ -480,12 +585,11 @@ void VideoState::video_refresh_timer() /* update delay to sync to audio if not master source */ if(this->av_sync_type != AV_SYNC_VIDEO_MASTER) { - ref_clock = get_master_clock(this); - diff = vp->pts - ref_clock; + double diff = this->get_video_clock() - this->get_master_clock(); /* Skip or repeat the frame. Take delay into account * FFPlay still doesn't "know if this is the best guess." */ - sync_threshold = std::max(delay, AV_SYNC_THRESHOLD); + double sync_threshold = std::max(delay, AV_SYNC_THRESHOLD); if(diff <= -sync_threshold) delay = 0; else if(diff >= sync_threshold) @@ -507,9 +611,6 @@ void VideoState::video_refresh_timer() this->video_display(); } - free(vp->data); - vp->data = NULL; - /* update queue for next picture! */ this->pictq_rindex = (this->pictq_rindex+1) % VIDEO_PICTURE_QUEUE_SIZE; this->pictq_mutex.lock(); @@ -548,10 +649,11 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) } vp->pts = pts; - vp->data = (uint8_t*)malloc(this->video_st->codec->width * this->video_st->codec->height * 4); + vp->data.resize(this->video_st->codec->width * this->video_st->codec->height * 4); + uint8_t *dst = &vp->data[0]; sws_scale(this->sws_context, pFrame->data, pFrame->linesize, - 0, this->video_st->codec->height, &vp->data, this->rgbaFrame->linesize); + 0, this->video_st->codec->height, &dst, this->rgbaFrame->linesize); // now we inform our display thread that we have a pic ready this->pictq_windex = (this->pictq_windex+1) % VIDEO_PICTURE_QUEUE_SIZE; diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 1213ca36c..7d3e4ac02 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -21,127 +21,13 @@ extern "C" #include "../mwbase/soundmanager.hpp" -#define VIDEO_PICTURE_QUEUE_SIZE 1 namespace MWRender { struct VideoState; - struct PacketQueue { - PacketQueue() - : first_pkt(NULL), last_pkt(NULL), nb_packets(0), size(0) - { } - - AVPacketList *first_pkt, *last_pkt; - int nb_packets; - int size; - - boost::mutex mutex; - boost::condition_variable cond; - - void put(AVPacket *pkt); - int get(AVPacket *pkt, VideoState *is, int block); - - void flush(); - }; - - struct VideoPicture { - VideoPicture () : data(NULL), pts(0) - { } - - uint8_t *data; - double pts; - }; - - struct VideoState { - VideoState () - : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock_base(0), - audio_clock(0), audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), - audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), - frame_last_delay(0), video_clock(0), video_current_pts(0), video_st(NULL), - rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), quit(false), - refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) - {} - - ~VideoState() - { - audioq.flush(); - videoq.flush(); - - for(int i = 0;i < VIDEO_PICTURE_QUEUE_SIZE;i++) - free(pictq[i].data); - } - - void init(const std::string& resourceName); - void deinit(); - - int stream_open(int stream_index, AVFormatContext *pFormatCtx); - - static void video_thread_loop(VideoState *is); - static void decode_thread_loop(VideoState *is); - - void video_display(); - void video_refresh_timer(); - - int queue_picture(AVFrame *pFrame, double pts); - double synchronize_video(AVFrame *src_frame, double pts); - - static void timer_callback(VideoState* is, boost::system_time t); - void schedule_refresh(int delay); - - static int OgreResource_Read(void *user_data, uint8_t *buf, int buf_size); - static int OgreResource_Write(void *user_data, uint8_t *buf, int buf_size); - static int64_t OgreResource_Seek(void *user_data, int64_t offset, int whence); - - int videoStream, audioStream; - - int av_sync_type; - uint64_t external_clock_base; - - double audio_clock; - AVStream *audio_st; - PacketQueue audioq; - AVPacket audio_pkt; - double audio_diff_cum; /* used for AV difference average computation */ - double audio_diff_avg_coef; - double audio_diff_threshold; - int audio_diff_avg_count; - - double frame_timer; - double frame_last_pts; - double frame_last_delay; - double video_clock; /// Date: Fri, 14 Dec 2012 23:42:49 -0800 Subject: [PATCH 166/378] Update the queued sample count immediately --- apps/openmw/mwsound/openal_output.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 672e52b56..bfead7843 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -339,8 +339,6 @@ bool OpenAL_SoundStream::process() { try { bool finished = mIsFinished; - ALint samples_unqueued = 0; - ALint samples_queued = 0; ALint processed, state; alGetSourcei(mSource, AL_SOURCE_STATE, &state); @@ -355,7 +353,7 @@ bool OpenAL_SoundStream::process() size_t got; alSourceUnqueueBuffers(mSource, 1, &bufid); - samples_unqueued += getBufferSampleCount(bufid); + mSamplesQueued -= getBufferSampleCount(bufid); processed--; if(finished) @@ -367,7 +365,7 @@ bool OpenAL_SoundStream::process() { alBufferData(bufid, mFormat, &data[0], got, mSampleRate); alSourceQueueBuffers(mSource, 1, &bufid); - samples_queued += getBufferSampleCount(bufid); + mSamplesQueued += getBufferSampleCount(bufid); } } while(processed > 0); throwALerror(); @@ -383,8 +381,6 @@ bool OpenAL_SoundStream::process() throwALerror(); } - mSamplesQueued -= samples_unqueued; - mSamplesQueued += samples_queued; mIsFinished = finished; } catch(std::exception &e) { From 62a995d492ecf02cf0ac02bcec92cd0360736e95 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 00:18:42 -0800 Subject: [PATCH 167/378] Calculate audio sync once per read --- apps/openmw/mwrender/videoplayer.cpp | 36 ++++++++++++---------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 2a545f772..7281e1c3a 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -255,15 +255,17 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder ssize_t mFramePos; ssize_t mFrameSize; - /* Add or subtract samples to get a better sync, return new - * audio buffer size */ - int synchronize_audio(uint8_t *samples, int samples_size, double pts) + /* Add or subtract samples to get a better sync, return number of bytes to + * skip (negative means to duplicate). */ + int synchronize_audio() { if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) - return samples_size; + return 0; + + int sample_skip = 0; // accumulate the clock difference - double diff = is->get_audio_clock() - is->get_master_clock(); + double diff = is->get_master_clock() - is->get_audio_clock(); is->audio_diff_cum = diff + is->audio_diff_avg_coef * is->audio_diff_cum; if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) @@ -275,19 +277,14 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder { int n = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * is->audio_st->codec->channels; - int wanted_size = samples_size + ((int)(diff * is->audio_st->codec->sample_rate) * n); - - wanted_size = std::max(0, wanted_size); - wanted_size = std::min(wanted_size, samples_size*2); - - samples_size = wanted_size; + sample_skip = ((int)(diff * is->audio_st->codec->sample_rate) * n); } } - return samples_size; + return sample_skip; } - int audio_decode_frame(AVFrame *frame, double *pts_ptr) + int audio_decode_frame(AVFrame *frame) { AVPacket *pkt = &is->audio_pkt; @@ -313,7 +310,6 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder if(!got_frame || frame->nb_samples <= 0) continue; - *pts_ptr = is->audio_clock; is->audio_clock += (double)frame->nb_samples / (double)is->audio_st->codec->sample_rate; @@ -406,25 +402,23 @@ public: size_t read(char *stream, size_t len) { + int sample_skip = synchronize_audio(); size_t total = 0; while(total < len) { - if(mFramePos >= mFrameSize) + while(mFramePos >= mFrameSize) { - int audio_size; - double pts; - /* We have already sent all our data; get more */ - mFrameSize = audio_decode_frame(mFrame, &pts); + mFrameSize = audio_decode_frame(mFrame); if(mFrameSize < 0) { /* If error, we're done */ break; } - audio_size = synchronize_audio(mFrame->data[0], mFrameSize, pts); - mFramePos = mFrameSize - audio_size; + mFramePos = std::min(mFrameSize, sample_skip); + sample_skip -= mFramePos; } size_t len1 = len - total; From b41a77648ec9444b3136e9303fc00b59306c6a1d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 02:33:59 -0800 Subject: [PATCH 168/378] Avoid re-reading the source sample to duplicate, to avoid pointer aliasing --- apps/openmw/mwrender/videoplayer.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 7281e1c3a..755378c28 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -439,18 +439,21 @@ public: memset(stream, *mFrame->data[0], len1); else if(n == 2) { + const int16_t val = *((int16_t*)mFrame->data[0]); for(size_t nb = 0;nb < len1;nb += n) - *((int16_t*)(stream+nb)) = *((int16_t*)mFrame->data[0]); + *((int16_t*)(stream+nb)) = val; } else if(n == 4) { + const int32_t val = *((int32_t*)mFrame->data[0]); for(size_t nb = 0;nb < len1;nb += n) - *((int32_t*)(stream+nb)) = *((int32_t*)mFrame->data[0]); + *((int32_t*)(stream+nb)) = val; } else if(n == 8) { + const int64_t val = *((int64_t*)mFrame->data[0]); for(size_t nb = 0;nb < len1;nb += n) - *((int64_t*)(stream+nb)) = *((int64_t*)mFrame->data[0]); + *((int64_t*)(stream+nb)) = val; } else { From eb0e8d9e37043e8d9e312ed7a22fcf9ca96d0f2c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 02:50:59 -0800 Subject: [PATCH 169/378] Simplify PacketQueue::get --- apps/openmw/mwrender/videoplayer.cpp | 32 +++++++--------------------- 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 755378c28..e54b4c5d9 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -42,7 +42,7 @@ struct PacketQueue { boost::condition_variable cond; void put(AVPacket *pkt); - int get(AVPacket *pkt, VideoState *is, int block); + int get(AVPacket *pkt, VideoState *is); void flush(); }; @@ -181,21 +181,12 @@ void PacketQueue::put(AVPacket *pkt) this->mutex.unlock(); } -int PacketQueue::get(AVPacket *pkt, VideoState *is, int block) +int PacketQueue::get(AVPacket *pkt, VideoState *is) { - AVPacketList *pkt1; - int ret; - boost::unique_lock lock(this->mutex); - for(;;) + while(!is->quit) { - if(is->quit) - { - ret = -1; - break; - } - - pkt1 = this->first_pkt; + AVPacketList *pkt1 = this->first_pkt; if(pkt1) { this->first_pkt = pkt1->next; @@ -207,20 +198,13 @@ int PacketQueue::get(AVPacket *pkt, VideoState *is, int block) *pkt = pkt1->pkt; av_free(pkt1); - ret = 1; - break; - } - - if (!block) - { - ret = 0; - break; + return 1; } this->cond.wait(lock); } - return ret; + return -1; } void PacketQueue::flush() @@ -324,7 +308,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder return -1; /* next packet */ - if(is->audioq.get(pkt, is, 1) < 0) + if(is->audioq.get(pkt, is) < 0) return -1; /* if update, update the audio clock w/pts */ @@ -714,7 +698,7 @@ void VideoState::video_thread_loop(VideoState *self) self->rgbaFrame = avcodec_alloc_frame(); avpicture_alloc((AVPicture*)self->rgbaFrame, PIX_FMT_RGBA, self->video_st->codec->width, self->video_st->codec->height); - while(self->videoq.get(packet, self, 1) >= 0) + while(self->videoq.get(packet, self) >= 0) { // Save global pts to be stored in pFrame global_video_pkt_pts = packet->pts; From d50698d7d1598e79fa6fb6bbfbfc47f5bc386b9f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 04:01:52 -0800 Subject: [PATCH 170/378] Clean up the rectangle and scene node used for displaying the video --- apps/openmw/mwrender/videoplayer.cpp | 171 ++++++++++++++------------- apps/openmw/mwrender/videoplayer.hpp | 3 +- 2 files changed, 94 insertions(+), 80 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index e54b4c5d9..5921a4e6f 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -938,103 +938,116 @@ void VideoState::deinit() } - VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) - : mState(NULL) - , mSceneMgr(sceneMgr) - { - mVideoMaterial = Ogre::MaterialManager::getSingleton ().create("VideoMaterial", "General"); - mVideoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(); - - mRectangle = new Ogre::Rectangle2D(true); - mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); - mRectangle->setMaterial("VideoMaterial"); - mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+1); - // Use infinite AAB to always stay visible - Ogre::AxisAlignedBox aabInf; - aabInf.setInfinite(); - mRectangle->setBoundingBox(aabInf); - // Attach background to the scene - Ogre::SceneNode* node = sceneMgr->getRootSceneNode()->createChildSceneNode(); - node->attachObject(mRectangle); - mRectangle->setVisible(false); - mRectangle->setVisibilityFlags (0x1); - } +VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) + : mState(NULL) + , mSceneMgr(sceneMgr) + , mVideoMaterial(NULL) + , mRectangle(NULL) + , mNode(NULL) +{ + mVideoMaterial = Ogre::MaterialManager::getSingleton().create("VideoMaterial", "General"); + mVideoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); + mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); + mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState()->setTextureName("black.png"); + + mRectangle = new Ogre::Rectangle2D(true); + mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); + mRectangle->setMaterial("VideoMaterial"); + mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+1); + + // Use infinite AAB to always stay visible + Ogre::AxisAlignedBox aabInf; + aabInf.setInfinite(); + mRectangle->setBoundingBox(aabInf); + + // Attach background to the scene + mNode = sceneMgr->getRootSceneNode()->createChildSceneNode(); + mNode->attachObject(mRectangle); + + mRectangle->setVisible(false); + mRectangle->setVisibilityFlags(0x1); +} - VideoPlayer::~VideoPlayer () - { - if (mState) - close(); - } +VideoPlayer::~VideoPlayer() +{ + if(mState) + close(); - void VideoPlayer::playVideo (const std::string &resourceName) - { - // Register all formats and codecs - av_register_all(); + if(mNode) + mSceneMgr->destroySceneNode(mNode); + mNode = NULL; - if (mState) - close(); + if(mRectangle) + delete mRectangle; + mRectangle = NULL; +} - mRectangle->setVisible(true); +void VideoPlayer::playVideo(const std::string &resourceName) +{ + // Register all formats and codecs + av_register_all(); - MWBase::Environment::get().getWindowManager ()->pushGuiMode (MWGui::GM_Video); + if(mState) + close(); - // Turn off rendering except the GUI - mSceneMgr->clearSpecialCaseRenderQueues(); - // SCRQM_INCLUDE with RENDER_QUEUE_OVERLAY does not work. - for (int i = 0; i < Ogre::RENDER_QUEUE_MAX; ++i) - { - if (i > 0 && i < 96) - mSceneMgr->addSpecialCaseRenderQueue(i); - } - mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); + mRectangle->setVisible(true); - MWBase::Environment::get().getSoundManager()->pauseAllSounds(); + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Video); - mState = new VideoState; - mState->init(resourceName); + // Turn off rendering except the GUI + mSceneMgr->clearSpecialCaseRenderQueues(); + // SCRQM_INCLUDE with RENDER_QUEUE_OVERLAY does not work. + for(int i = 0;i < Ogre::RENDER_QUEUE_MAX;++i) + { + if(i > 0 && i < 96) + mSceneMgr->addSpecialCaseRenderQueue(i); } + mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); + + MWBase::Environment::get().getSoundManager()->pauseAllSounds(); + + mState = new VideoState; + mState->init(resourceName); +} - void VideoPlayer::update () +void VideoPlayer::update () +{ + if(mState) { - if(mState) + if(mState->quit) + close(); + else if(mState->refresh) { - if(mState->quit) - close(); - else if(mState->refresh) - { - mState->refresh = false; - mState->video_refresh_timer(); - } + mState->refresh = false; + mState->video_refresh_timer(); + // Would be nice not to do this all the time... + if(mState->display_ready) + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("VideoTexture"); } - - if (mState && mState->display_ready && !Ogre::TextureManager::getSingleton ().getByName ("VideoTexture").isNull ()) - mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("VideoTexture"); - else - mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState (0)->setTextureName ("black.png"); } +} - void VideoPlayer::close() - { - mState->quit = 1; - mState->deinit(); +void VideoPlayer::close() +{ + mState->quit = 1; + mState->deinit(); - delete mState; - mState = NULL; + delete mState; + mState = NULL; - MWBase::Environment::get().getSoundManager()->resumeAllSounds(); + MWBase::Environment::get().getSoundManager()->resumeAllSounds(); - mRectangle->setVisible (false); - MWBase::Environment::get().getWindowManager ()->removeGuiMode (MWGui::GM_Video); + mRectangle->setVisible(false); + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Video); - mSceneMgr->clearSpecialCaseRenderQueues(); - mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); - } + mSceneMgr->clearSpecialCaseRenderQueues(); + mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); +} + +bool VideoPlayer::isPlaying () +{ + return mState != NULL; +} - bool VideoPlayer::isPlaying () - { - return mState != NULL; - } } diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 7d3e4ac02..9060241bd 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -45,8 +45,9 @@ namespace MWRender VideoState* mState; Ogre::SceneManager* mSceneMgr; - Ogre::Rectangle2D* mRectangle; Ogre::MaterialPtr mVideoMaterial; + Ogre::Rectangle2D* mRectangle; + Ogre::SceneNode* mNode; }; } From da44141b95dc712d3872c5398cf4b21adb5f3ed7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 04:17:28 -0800 Subject: [PATCH 171/378] Avoid creating extra texture unit states on the video material --- apps/openmw/mwrender/videoplayer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 5921a4e6f..4f42c3fb6 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -949,7 +949,10 @@ VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) mVideoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState()->setTextureName("black.png"); + if(mVideoMaterial->getTechnique(0)->getPass(0)->getNumTextureUnitStates() == 0) + mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState()->setTextureName("black.png"); + else + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); mRectangle = new Ogre::Rectangle2D(true); mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); From 6008cf0d1511f3c2a05c6696bbd457bcfd181236 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 05:02:01 -0800 Subject: [PATCH 172/378] Remove unneeded video_current_pts field --- apps/openmw/mwrender/videoplayer.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 4f42c3fb6..e7bdfab91 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -60,9 +60,9 @@ struct VideoState { : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock_base(0), audio_clock(0), audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), - frame_last_delay(0), video_clock(0), video_current_pts(0), video_st(NULL), - rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), quit(false), - refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) + frame_last_delay(0), video_clock(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), + pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), format_ctx(0), + sws_context(NULL), display_ready(0) { } ~VideoState() @@ -90,7 +90,7 @@ struct VideoState { { return this->AudioTrack->getTimeOffset(); } double get_video_clock() - { return this->video_current_pts; } + { return this->frame_last_pts; } double get_external_clock() { return ((uint64_t)av_gettime()-this->external_clock_base) / 1000000.0; } @@ -128,7 +128,6 @@ struct VideoState { double frame_last_pts; double frame_last_delay; double video_clock; ///frame_last_delay = delay; this->frame_last_pts = vp->pts; - this->video_current_pts = vp->pts; - /* update delay to sync to audio if not master source */ if(this->av_sync_type != AV_SYNC_VIDEO_MASTER) { From db23c8152e5f0030fea540364ac69d5dc9cff7ab Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 07:33:27 -0800 Subject: [PATCH 173/378] Only duplicate AVPackets as needed Packets that don't have a destruct method are using static memory, which will only be valid until the next av_read_frame call. Otherwise, it's already dynamically allocated and will remain valid. --- apps/openmw/mwrender/videoplayer.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index e7bdfab91..bfcb92745 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -158,14 +158,21 @@ struct VideoState { void PacketQueue::put(AVPacket *pkt) { AVPacketList *pkt1; - if(av_dup_packet(pkt) < 0) - throw std::runtime_error("Failed to duplicate packet"); - pkt1 = (AVPacketList*)av_malloc(sizeof(AVPacketList)); if(!pkt1) throw std::bad_alloc(); pkt1->pkt = *pkt; pkt1->next = NULL; + if(pkt1->pkt.destruct == NULL) + { + if(av_dup_packet(&pkt1->pkt) < 0) + { + av_free(pkt1); + throw std::runtime_error("Failed to duplicate packet"); + } + av_free_packet(pkt); + } + this->mutex.lock (); if(!last_pkt) From 74773454814a0106821574e28b8d2d13594aeefa Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Dec 2012 17:10:21 +0100 Subject: [PATCH 174/378] fixed video material --- apps/openmw/mwrender/videoplayer.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index bfcb92745..407f1c212 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -407,7 +407,7 @@ public: break; } - mFramePos = std::min(mFrameSize, sample_skip); + mFramePos = std::min(static_cast(mFrameSize), sample_skip); sample_skip -= mFramePos; } @@ -949,14 +949,16 @@ VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) , mRectangle(NULL) , mNode(NULL) { - mVideoMaterial = Ogre::MaterialManager::getSingleton().create("VideoMaterial", "General"); - mVideoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); - if(mVideoMaterial->getTechnique(0)->getPass(0)->getNumTextureUnitStates() == 0) + mVideoMaterial = Ogre::MaterialManager::getSingleton().getByName("VideoMaterial", "General"); + if (mVideoMaterial.isNull ()) + { + mVideoMaterial = Ogre::MaterialManager::getSingleton().create("VideoMaterial", "General"); + mVideoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); + mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState()->setTextureName("black.png"); - else - mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); + } + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); mRectangle = new Ogre::Rectangle2D(true); mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); @@ -1016,6 +1018,8 @@ void VideoPlayer::playVideo(const std::string &resourceName) mState = new VideoState; mState->init(resourceName); + + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); } void VideoPlayer::update () From fa1ad381da72c76ea8f5f85c19959630aab7e3ea Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 08:42:54 -0800 Subject: [PATCH 175/378] Make sure packets are cleaned up properly --- apps/openmw/mwrender/videoplayer.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index bfcb92745..b0bf37154 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -118,7 +118,6 @@ struct VideoState { double audio_clock; AVStream *audio_st; PacketQueue audioq; - AVPacket audio_pkt; double audio_diff_cum; /* used for AV difference average computation */ double audio_diff_avg_coef; double audio_diff_threshold; @@ -239,8 +238,19 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder throw std::runtime_error(str); } + struct AutoAVPacket : public AVPacket { + AutoAVPacket(int size=0) + { + if(av_new_packet(this, size) < 0) + throw std::bad_alloc(); + } + ~AutoAVPacket() + { av_free_packet(this); } + }; + VideoState *is; + AutoAVPacket mPacket; AVFrame *mFrame; ssize_t mFramePos; ssize_t mFrameSize; @@ -276,7 +286,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder int audio_decode_frame(AVFrame *frame) { - AVPacket *pkt = &is->audio_pkt; + AVPacket *pkt = &mPacket; for(;;) { @@ -292,8 +302,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder /* Move the unread data to the front and clear the end bits */ int remaining = pkt->size - len1; memmove(pkt->data, &pkt->data[len1], remaining); - memset(&pkt->data[remaining], 0, pkt->size - remaining); - pkt->size -= len1; + av_shrink_packet(pkt, remaining); } /* No data yet? Look for more frames */ @@ -307,8 +316,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder return frame->nb_samples * av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * is->audio_st->codec->channels; } - if(pkt->data) - av_free_packet(pkt); + av_free_packet(pkt); if(is->quit) return -1; @@ -717,6 +725,8 @@ void VideoState::video_thread_loop(VideoState *self) pts = *(uint64_t*)pFrame->opaque; pts *= av_q2d(self->video_st->time_base); + av_free_packet(packet); + // Did we get a video frame? if(frameFinished) { @@ -724,7 +734,6 @@ void VideoState::video_thread_loop(VideoState *self) if(self->queue_picture(pFrame, pts) < 0) break; } - av_free_packet(packet); } av_free(pFrame); @@ -814,8 +823,6 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) /* Correct audio only if larger error than this */ this->audio_diff_threshold = 2.0 * 0.050/* 50 ms */; - memset(&this->audio_pkt, 0, sizeof(this->audio_pkt)); - decoder.reset(new MovieAudioDecoder(this)); this->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); if(!this->AudioTrack) From 9b3cf5c1593c72dc2e2fe85f02074b57d69e1e93 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 09:14:58 -0800 Subject: [PATCH 176/378] Use a looping thread to trigger refreshes --- apps/openmw/mwrender/videoplayer.cpp | 63 ++++++++++------------------ 1 file changed, 23 insertions(+), 40 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index b0bf37154..f6edf9ad9 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -59,7 +59,7 @@ struct VideoState { VideoState() : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock_base(0), audio_clock(0), audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), - audio_diff_threshold(0), audio_diff_avg_count(0), frame_timer(0), frame_last_pts(0), + audio_diff_threshold(0), audio_diff_avg_count(0), frame_last_pts(0), frame_last_delay(0), video_clock(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) @@ -82,8 +82,7 @@ struct VideoState { int queue_picture(AVFrame *pFrame, double pts); double synchronize_video(AVFrame *src_frame, double pts); - static void timer_callback(VideoState* is, boost::system_time t); - void schedule_refresh(int delay); + static void video_refresh(VideoState *is); double get_audio_clock() @@ -123,7 +122,6 @@ struct VideoState { double audio_diff_threshold; int audio_diff_avg_count; - double frame_timer; double frame_last_pts; double frame_last_delay; double video_clock; ///refresh = true; -} - -/* schedule a video refresh in 'delay' ms */ -void VideoState::schedule_refresh(int delay) +void VideoState::video_refresh(VideoState* is) { - boost::system_time t = boost::get_system_time() + boost::posix_time::milliseconds(delay); - boost::thread(boost::bind(&timer_callback, this, t)).detach(); + boost::system_time t = boost::get_system_time(); + while(!is->quit) + { + t += boost::posix_time::milliseconds(is->refresh_rate_ms); + boost::this_thread::sleep(t); + is->refresh = true; + } } @@ -551,18 +550,10 @@ void VideoState::video_display() void VideoState::video_refresh_timer() { VideoPicture *vp; - double actual_delay, delay; + double delay; - if(!this->video_st) - { - this->schedule_refresh(100); - return; - } if(this->pictq_size == 0) - { - this->refresh = true; return; - } vp = &this->pictq[this->pictq_rindex]; @@ -575,6 +566,8 @@ void VideoState::video_refresh_timer() this->frame_last_delay = delay; this->frame_last_pts = vp->pts; + /* FIXME: Syncing should be done in the decoding stage, where frames can be + * skipped or duplicated as needed. */ /* update delay to sync to audio if not master source */ if(this->av_sync_type != AV_SYNC_VIDEO_MASTER) { @@ -588,21 +581,10 @@ void VideoState::video_refresh_timer() else if(diff >= sync_threshold) delay = 2 * delay; } - this->frame_timer += delay; - /* compute the REAL delay */ - actual_delay = this->frame_timer - (av_gettime() / 1000000.0); - if(actual_delay < 0.010) - { - /* Skip this picture */ - this->refresh = true; - } - else - { - this->schedule_refresh((int)(actual_delay * 1000 + 0.5)); - /* show the picture! */ - this->video_display(); - } + this->refresh_rate_ms = std::max(1, (int)(delay*1000.0)); + /* show the picture! */ + this->video_display(); /* update queue for next picture! */ this->pictq_rindex = (this->pictq_rindex+1) % VIDEO_PICTURE_QUEUE_SIZE; @@ -838,12 +820,12 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) this->videoStream = stream_index; this->video_st = pFormatCtx->streams[stream_index]; - this->frame_timer = (double)av_gettime() / 1000000.0; this->frame_last_delay = 40e-3; codecCtx->get_buffer = our_get_buffer; codecCtx->release_buffer = our_release_buffer; this->video_thread = boost::thread(video_thread_loop, this); + this->refresh_thread = boost::thread(video_refresh, this); break; default: @@ -864,6 +846,7 @@ void VideoState::init(const std::string& resourceName) this->av_sync_type = AV_SYNC_DEFAULT; this->videoStream = -1; this->audioStream = -1; + this->refresh_rate_ms = 10; this->refresh = false; this->quit = 0; @@ -909,7 +892,6 @@ void VideoState::init(const std::string& resourceName) if(video_index >= 0) this->stream_open(video_index, this->format_ctx); - this->schedule_refresh(40); this->parse_thread = boost::thread(decode_thread_loop, this); } catch(std::runtime_error& e) @@ -931,6 +913,7 @@ void VideoState::deinit() this->parse_thread.join(); this->video_thread.join(); + this->refresh_thread.join(); if(this->audioStream >= 0) avcodec_close(this->audio_st->codec); From 5ed04ae53ef6465b3cc167f720c58267872a8167 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Dec 2012 19:23:03 +0100 Subject: [PATCH 177/378] added black bars --- apps/openmw/mwrender/renderingmanager.cpp | 3 ++ apps/openmw/mwrender/videoplayer.cpp | 44 +++++++++++++++++++---- apps/openmw/mwrender/videoplayer.hpp | 7 ++++ 3 files changed, 47 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 811d000a9..3abf88cca 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -162,6 +162,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); mVideoPlayer = new VideoPlayer(mRendering.getScene ()); + mVideoPlayer->setResolution (Settings::Manager::getInt ("resolution x", "Video"), Settings::Manager::getInt ("resolution y", "Video")); mSun = 0; @@ -843,6 +844,8 @@ void RenderingManager::windowResized(Ogre::RenderWindow* rw) mCompositors->recreate(); mWater->assignTextures(); + mVideoPlayer->setResolution (rw->getWidth(), rw->getHeight()); + const Settings::CategorySettingVector& changed = Settings::Manager::apply(); MWBase::Environment::get().getInputManager()->processChangedSettings(changed); //FIXME MWBase::Environment::get().getWindowManager()->processChangedSettings(changed); // FIXME diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 4f9963de5..11b6d2b8f 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -950,22 +950,41 @@ VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) } mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); + Ogre::MaterialPtr blackMaterial = Ogre::MaterialManager::getSingleton().getByName("BlackBarsMaterial", "General"); + if (blackMaterial.isNull ()) + { + blackMaterial = Ogre::MaterialManager::getSingleton().create("BlackBarsMaterial", "General"); + blackMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); + blackMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); + blackMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); + blackMaterial->getTechnique(0)->getPass(0)->createTextureUnitState()->setTextureName("black.png"); + } + mRectangle = new Ogre::Rectangle2D(true); mRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); mRectangle->setMaterial("VideoMaterial"); - mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+1); + mRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+2); + mBackgroundRectangle = new Ogre::Rectangle2D(true); + mBackgroundRectangle->setCorners(-1.0, 1.0, 1.0, -1.0); + mBackgroundRectangle->setMaterial("BlackBarsMaterial"); + mBackgroundRectangle->setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY+1); // Use infinite AAB to always stay visible Ogre::AxisAlignedBox aabInf; aabInf.setInfinite(); mRectangle->setBoundingBox(aabInf); + mBackgroundRectangle->setBoundingBox(aabInf); // Attach background to the scene mNode = sceneMgr->getRootSceneNode()->createChildSceneNode(); mNode->attachObject(mRectangle); + mBackgroundNode = sceneMgr->getRootSceneNode()->createChildSceneNode(); + mBackgroundNode->attachObject(mBackgroundRectangle); mRectangle->setVisible(false); mRectangle->setVisibilityFlags(0x1); + mBackgroundRectangle->setVisible(false); + mBackgroundRectangle->setVisibilityFlags(0x1); } VideoPlayer::~VideoPlayer() @@ -973,13 +992,11 @@ VideoPlayer::~VideoPlayer() if(mState) close(); - if(mNode) - mSceneMgr->destroySceneNode(mNode); - mNode = NULL; + mSceneMgr->destroySceneNode(mNode); + mSceneMgr->destroySceneNode(mBackgroundNode); - if(mRectangle) - delete mRectangle; - mRectangle = NULL; + delete mRectangle; + delete mBackgroundRectangle; } void VideoPlayer::playVideo(const std::string &resourceName) @@ -991,6 +1008,7 @@ void VideoPlayer::playVideo(const std::string &resourceName) close(); mRectangle->setVisible(true); + mBackgroundRectangle->setVisible(true); MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Video); @@ -1025,6 +1043,17 @@ void VideoPlayer::update () // Would be nice not to do this all the time... if(mState->display_ready) mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("VideoTexture"); + + // Correct aspect ratio by adding black bars + int width = (mState->video_st->codec->width); + int height = (mState->video_st->codec->height); + + float screenaspect = static_cast(mWidth) / mHeight; + float videoaspect = static_cast(width) / height; + float aspect_correction = videoaspect / screenaspect; + + mRectangle->setCorners (std::max(-1.f, -1.f * aspect_correction), std::min(1.f, 1.f / aspect_correction), + std::min(1.f, 1.f * aspect_correction), std::max(-1.f, -1.f / aspect_correction)); } } } @@ -1040,6 +1069,7 @@ void VideoPlayer::close() MWBase::Environment::get().getSoundManager()->resumeAllSounds(); mRectangle->setVisible(false); + mBackgroundRectangle->setVisible(false); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Video); mSceneMgr->clearSpecialCaseRenderQueues(); diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 9060241bd..c82a16e15 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -40,6 +40,8 @@ namespace MWRender bool isPlaying(); + void setResolution (int w, int h) { mWidth = w; mHeight = h; } + private: VideoState* mState; @@ -47,7 +49,12 @@ namespace MWRender Ogre::SceneManager* mSceneMgr; Ogre::MaterialPtr mVideoMaterial; Ogre::Rectangle2D* mRectangle; + Ogre::Rectangle2D* mBackgroundRectangle; Ogre::SceneNode* mNode; + Ogre::SceneNode* mBackgroundNode; + + int mWidth; + int mHeight; }; } From 63e86555b64e33ceb2fdfcdcf4c3fba9ed3292dc Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Dec 2012 19:40:59 +0100 Subject: [PATCH 178/378] use sample_aspect_ratio if available --- apps/openmw/mwrender/videoplayer.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 11b6d2b8f..740da7a12 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -1045,15 +1045,16 @@ void VideoPlayer::update () mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("VideoTexture"); // Correct aspect ratio by adding black bars - int width = (mState->video_st->codec->width); - int height = (mState->video_st->codec->height); + double videoaspect = static_cast(mState->video_st->codec->width) / mState->video_st->codec->height; - float screenaspect = static_cast(mWidth) / mHeight; - float videoaspect = static_cast(width) / height; - float aspect_correction = videoaspect / screenaspect; + if (av_q2d(mState->video_st->codec->sample_aspect_ratio) != 0) + videoaspect *= av_q2d(mState->video_st->codec->sample_aspect_ratio); - mRectangle->setCorners (std::max(-1.f, -1.f * aspect_correction), std::min(1.f, 1.f / aspect_correction), - std::min(1.f, 1.f * aspect_correction), std::max(-1.f, -1.f / aspect_correction)); + double screenaspect = static_cast(mWidth) / mHeight; + double aspect_correction = videoaspect / screenaspect; + + mRectangle->setCorners (std::max(-1.0, -1.0 * aspect_correction), std::min(1.0, 1.0 / aspect_correction), + std::min(1.0, 1.0 * aspect_correction), std::max(-1.0, -1.0 / aspect_correction)); } } } From c869444dcf34920d08bc7ef22d6cfe8bfae88140 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 11:31:50 -0800 Subject: [PATCH 179/378] Don't leak the IO context if avformat_open_input fails --- apps/openmw/mwrender/videoplayer.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 740da7a12..0165d9855 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -854,20 +854,20 @@ void VideoState::init(const std::string& resourceName) if(this->stream.isNull()) throw std::runtime_error("Failed to open video resource"); + AVIOContext *ioCtx = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if(!ioCtx) throw std::runtime_error("Failed to allocate AVIOContext"); + this->format_ctx = avformat_alloc_context(); - this->format_ctx->pb = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if(!this->format_ctx->pb) - { - avformat_free_context(this->format_ctx); - throw std::runtime_error("Failed to allocate ioContext "); - } + if(this->format_ctx) + this->format_ctx->pb = ioCtx; // Open video file /// \todo leak here, ffmpeg or valgrind bug ? - if (avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) + if(!this->format_ctx || avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) { // "Note that a user-supplied AVFormatContext will be freed on failure." this->format_ctx = NULL; + av_free(ioCtx); throw std::runtime_error("Failed to open video input"); } From a6c3e06e543bef88f25935d1aeb66ff357550f27 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Dec 2012 20:36:17 +0100 Subject: [PATCH 180/378] fixed fog in some cells --- files/materials/objects.shader | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/files/materials/objects.shader b/files/materials/objects.shader index 5ea076342..25624351c 100644 --- a/files/materials/objects.shader +++ b/files/materials/objects.shader @@ -227,7 +227,7 @@ float4 worldNormal = shMatrixMult(worldMatrix, float4(normal.xyz, 0)); waterEyePos = intercept(worldPos, cameraPos.xyz - worldPos, float3(0,1,0), waterLevel); caustics = getCaustics(causticMap, worldPos, waterEyePos.xyz, worldNormal.xyz, lightDirectionWS0.xyz, waterLevel, waterTimer, windDir_windSpeed); - if (worldPos.y >= waterLevel || waterEnabled != 1) + if (worldPos.y >= waterLevel || waterEnabled != 1.f) caustics = float3(1,1,1); #endif @@ -269,7 +269,7 @@ #if UNDERWATER // regular fog only if fragment is above water - if (worldPos.y > waterLevel) + if (worldPos.y > waterLevel || waterEnabled != 1.f) #endif shOutputColour(0).xyz = shLerp (shOutputColour(0).xyz, gammaCorrectRead(fogColour), fogValue); #endif From edf18a7d6ec12d9519d00e14f5a532c940a463d9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 15 Dec 2012 22:01:45 +0100 Subject: [PATCH 181/378] change destruction order to fix crash on exit when a video is playing --- apps/openmw/mwbase/environment.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 9aaa5af85..5a13a50ec 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -128,12 +128,6 @@ float MWBase::Environment::getFrameDuration() const void MWBase::Environment::cleanup() { - delete mInputManager; - mInputManager = 0; - - delete mSoundManager; - mSoundManager = 0; - delete mMechanicsManager; mMechanicsManager = 0; @@ -146,11 +140,17 @@ void MWBase::Environment::cleanup() delete mScriptManager; mScriptManager = 0; + delete mWorld; + mWorld = 0; + + delete mSoundManager; + mSoundManager = 0; + delete mWindowManager; mWindowManager = 0; - delete mWorld; - mWorld = 0; + delete mInputManager; + mInputManager = 0; } const MWBase::Environment& MWBase::Environment::get() From a3bd3a40ca6494494daf69aaff68284c7792f062 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Dec 2012 00:47:29 +0100 Subject: [PATCH 182/378] fix 2 unrelated leaks --- apps/openmw/mwgui/windowmanagerimp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 258a40e1c..373546aa8 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -225,6 +225,8 @@ WindowManager::~WindowManager() delete mSpellCreationDialog; delete mEnchantingDialog; delete mTrainingWindow; + delete mCountDialog; + delete mQuickKeysMenu; cleanupGarbage(); From 9d86890d9d71da789fc92dd9ea83b00dcd65e76d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 22:13:19 -0800 Subject: [PATCH 183/378] Only use one stream for the ffmpeg decoder --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 165 ++++++++++++------------- apps/openmw/mwsound/ffmpeg_decoder.hpp | 7 +- 2 files changed, 80 insertions(+), 92 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 6e60a7b9e..205dddd35 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -72,39 +72,26 @@ int64_t FFmpeg_Decoder::seek(void *user_data, int64_t offset, int whence) /* Used by getAV*Data to search for more compressed data, and buffer it in the * correct stream. It won't buffer data for streams that the app doesn't have a * handle for. */ -bool FFmpeg_Decoder::getNextPacket(int streamidx) +bool FFmpeg_Decoder::getNextPacket() { - PacketList *packet; + if(!mStream.get()) + return false; - packet = (PacketList*)av_malloc(sizeof(*packet)); + PacketList *packet = (PacketList*)av_malloc(sizeof(*packet)); packet->next = NULL; - -next_packet: while(av_read_frame(mFormatCtx, &packet->pkt) >= 0) { - std::vector::iterator iter = mStreams.begin(); - - /* Check each stream the user has a handle for, looking for the one - * this packet belongs to */ - while(iter != mStreams.end()) + /* Check if the packet belongs to this stream */ + if(mStream->mStreamIdx == packet->pkt.stream_index) { - if((*iter)->mStreamIdx == packet->pkt.stream_index) - { - PacketList **last; - - last = &(*iter)->mPackets; - while(*last != NULL) - last = &(*last)->next; + PacketList **last; - *last = packet; - if((*iter)->mStreamIdx == streamidx) - return true; + last = &mStream->mPackets; + while(*last != NULL) + last = &(*last)->next; - packet = (PacketList*)av_malloc(sizeof(*packet)); - packet->next = NULL; - goto next_packet; - } - iter++; + *last = packet; + return true; } /* Free the packet and look for another */ av_free_packet(&packet->pkt); @@ -138,7 +125,7 @@ void *FFmpeg_Decoder::MyStream::getAVAudioData(size_t *length) mDecodedDataSize = 0; next_packet: - if(!mPackets && !mParent->getNextPacket(mStreamIdx)) + if(!mPackets && !mParent->getNextPacket()) return NULL; /* Decode some data, and check for errors */ @@ -173,8 +160,7 @@ next_packet: /* Move the unread data to the front and clear the end bits */ int remaining = mPackets->pkt.size - len; memmove(mPackets->pkt.data, &mPackets->pkt.data[len], remaining); - memset(&mPackets->pkt.data[remaining], 0, mPackets->pkt.size - remaining); - mPackets->pkt.size -= len; + av_shrink_packet(&mPackets->pkt, remaining); } else { @@ -261,35 +247,38 @@ void FFmpeg_Decoder::open(const std::string &fname) if(avformat_find_stream_info(mFormatCtx, NULL) < 0) fail("Failed to find stream info in "+fname); + int audio_idx = -1; for(size_t j = 0;j < mFormatCtx->nb_streams;j++) { if(mFormatCtx->streams[j]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { - std::auto_ptr stream(new MyStream); - stream->mCodecCtx = mFormatCtx->streams[j]->codec; - stream->mStreamIdx = j; - stream->mPackets = NULL; - - AVCodec *codec = avcodec_find_decoder(stream->mCodecCtx->codec_id); - if(!codec) - { - std::stringstream ss("No codec found for id "); - ss << stream->mCodecCtx->codec_id; - fail(ss.str()); - } - if(avcodec_open2(stream->mCodecCtx, codec, NULL) < 0) - fail("Failed to open audio codec " + std::string(codec->long_name)); - - stream->mDecodedData = (char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); - stream->mDecodedDataSize = 0; - - stream->mParent = this; - mStreams.push_back(stream.release()); + audio_idx = j; break; } } - if(mStreams.empty()) + if(audio_idx == -1) fail("No audio streams in "+fname); + + std::auto_ptr stream(new MyStream); + stream->mCodecCtx = mFormatCtx->streams[audio_idx]->codec; + stream->mStreamIdx = audio_idx; + stream->mPackets = NULL; + + AVCodec *codec = avcodec_find_decoder(stream->mCodecCtx->codec_id); + if(!codec) + { + std::stringstream ss("No codec found for id "); + ss << stream->mCodecCtx->codec_id; + fail(ss.str()); + } + if(avcodec_open2(stream->mCodecCtx, codec, NULL) < 0) + fail("Failed to open audio codec " + std::string(codec->long_name)); + + stream->mDecodedData = (char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); + stream->mDecodedDataSize = 0; + + stream->mParent = this; + mStream = stream; } catch(std::exception &e) { @@ -301,23 +290,19 @@ void FFmpeg_Decoder::open(const std::string &fname) void FFmpeg_Decoder::close() { - while(!mStreams.empty()) + if(mStream.get()) { - MyStream *stream = mStreams.front(); - - stream->clearPackets(); - avcodec_close(stream->mCodecCtx); - av_free(stream->mDecodedData); - delete stream; - - mStreams.erase(mStreams.begin()); + mStream->clearPackets(); + avcodec_close(mStream->mCodecCtx); + av_free(mStream->mDecodedData); } + mStream.reset(); + if(mFormatCtx) { AVIOContext* context = mFormatCtx->pb; - av_free(context); - mFormatCtx->pb = NULL; avformat_close_input(&mFormatCtx); + av_free(context); } mFormatCtx = NULL; @@ -331,81 +316,83 @@ std::string FFmpeg_Decoder::getName() void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) { - if(mStreams.empty()) + if(!mStream.get()) fail("No audio stream info"); - MyStream *stream = mStreams[0]; - if(stream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8) + if(mStream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8) *type = SampleType_UInt8; - else if(stream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_S16) + else if(mStream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_S16) *type = SampleType_Int16; else fail(std::string("Unsupported sample format: ")+ - av_get_sample_fmt_name(stream->mCodecCtx->sample_fmt)); + av_get_sample_fmt_name(mStream->mCodecCtx->sample_fmt)); - if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO) + if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO) *chans = ChannelConfig_Mono; - else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_STEREO) + else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_STEREO) *chans = ChannelConfig_Stereo; - else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_QUAD) + else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_QUAD) *chans = ChannelConfig_Quad; - else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1) + else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1) *chans = ChannelConfig_5point1; - else if(stream->mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1) + else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1) *chans = ChannelConfig_7point1; - else if(stream->mCodecCtx->channel_layout == 0) + else if(mStream->mCodecCtx->channel_layout == 0) { /* Unknown channel layout. Try to guess. */ - if(stream->mCodecCtx->channels == 1) + if(mStream->mCodecCtx->channels == 1) *chans = ChannelConfig_Mono; - else if(stream->mCodecCtx->channels == 2) + else if(mStream->mCodecCtx->channels == 2) *chans = ChannelConfig_Stereo; else { std::stringstream sstr("Unsupported raw channel count: "); - sstr << stream->mCodecCtx->channels; + sstr << mStream->mCodecCtx->channels; fail(sstr.str()); } } else { char str[1024]; - av_get_channel_layout_string(str, sizeof(str), stream->mCodecCtx->channels, - stream->mCodecCtx->channel_layout); + av_get_channel_layout_string(str, sizeof(str), mStream->mCodecCtx->channels, + mStream->mCodecCtx->channel_layout); fail(std::string("Unsupported channel layout: ")+str); } - *samplerate = stream->mCodecCtx->sample_rate; + *samplerate = mStream->mCodecCtx->sample_rate; } size_t FFmpeg_Decoder::read(char *buffer, size_t bytes) { - if(mStreams.empty()) - fail("No audio streams"); + if(!mStream.get()) + fail("No audio stream"); - MyStream *stream = mStreams.front(); - size_t got = stream->readAVAudioData(buffer, bytes); - mSamplesRead += got / av_samples_get_buffer_size(NULL, stream->mCodecCtx->channels, 1, - stream->mCodecCtx->sample_fmt, 1); + size_t got = mStream->readAVAudioData(buffer, bytes); + mSamplesRead += got / mStream->mCodecCtx->channels / + av_get_bytes_per_sample(mStream->mCodecCtx->sample_fmt); return got; } void FFmpeg_Decoder::readAll(std::vector &output) { - if(mStreams.empty()) - fail("No audio streams"); - MyStream *stream = mStreams.front(); + if(!mStream.get()) + fail("No audio stream"); + char *inbuf; size_t got; - - while((inbuf=(char*)stream->getAVAudioData(&got)) != NULL && got > 0) + while((inbuf=(char*)mStream->getAVAudioData(&got)) != NULL && got > 0) + { output.insert(output.end(), inbuf, inbuf+got); + mSamplesRead += got / mStream->mCodecCtx->channels / + av_get_bytes_per_sample(mStream->mCodecCtx->sample_fmt); + } } void FFmpeg_Decoder::rewind() { av_seek_frame(mFormatCtx, -1, 0, 0); - std::for_each(mStreams.begin(), mStreams.end(), std::mem_fun(&MyStream::clearPackets)); + if(mStream.get()) + mStream->clearPackets(); mSamplesRead = 0; } diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index ff63edf07..b7d2e1bb2 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -21,13 +21,14 @@ namespace MWSound { class FFmpeg_Decoder : public Sound_Decoder { + struct MyStream; + AVFormatContext *mFormatCtx; - struct MyStream; - std::vector mStreams; + std::auto_ptr mStream; size_t mSamplesRead; - bool getNextPacket(int streamidx); + bool getNextPacket(); Ogre::DataStreamPtr mDataStream; static int readPacket(void *user_data, uint8_t *buf, int buf_size); From 5f4c33f8960f2d9ba18fe135f01d103b307e1375 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 22:54:54 -0800 Subject: [PATCH 184/378] Only store one packet at a time --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 111 ++++++------------------- 1 file changed, 24 insertions(+), 87 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 205dddd35..70f7682ef 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -15,23 +15,17 @@ static void fail(const std::string &msg) { throw std::runtime_error("FFmpeg exception: "+msg); } -struct PacketList { - AVPacket pkt; - PacketList *next; -}; - struct FFmpeg_Decoder::MyStream { AVCodecContext *mCodecCtx; int mStreamIdx; - PacketList *mPackets; + AVPacket mPacket; char *mDecodedData; size_t mDecodedDataSize; FFmpeg_Decoder *mParent; - void clearPackets(); void *getAVAudioData(size_t *length); size_t readAVAudioData(void *data, size_t length); }; @@ -77,104 +71,47 @@ bool FFmpeg_Decoder::getNextPacket() if(!mStream.get()) return false; - PacketList *packet = (PacketList*)av_malloc(sizeof(*packet)); - packet->next = NULL; - while(av_read_frame(mFormatCtx, &packet->pkt) >= 0) + while(av_read_frame(mFormatCtx, &mStream->mPacket) >= 0) { /* Check if the packet belongs to this stream */ - if(mStream->mStreamIdx == packet->pkt.stream_index) - { - PacketList **last; - - last = &mStream->mPackets; - while(*last != NULL) - last = &(*last)->next; - - *last = packet; + if(mStream->mStreamIdx == mStream->mPacket.stream_index) return true; - } + /* Free the packet and look for another */ - av_free_packet(&packet->pkt); + av_free_packet(&mStream->mPacket); } - av_free(packet); return false; } -void FFmpeg_Decoder::MyStream::clearPackets() -{ - while(mPackets) - { - PacketList *self = mPackets; - mPackets = self->next; - - av_free_packet(&self->pkt); - av_free(self); - } -} - void *FFmpeg_Decoder::MyStream::getAVAudioData(size_t *length) { - int size; - int len; + int size, len; if(length) *length = 0; if(mCodecCtx->codec_type != AVMEDIA_TYPE_AUDIO) return NULL; mDecodedDataSize = 0; + do { + if(mPacket.size == 0 && !mParent->getNextPacket()) + return NULL; -next_packet: - if(!mPackets && !mParent->getNextPacket()) - return NULL; - - /* Decode some data, and check for errors */ - size = AVCODEC_MAX_AUDIO_FRAME_SIZE; - while((len=avcodec_decode_audio3(mCodecCtx, (int16_t*)mDecodedData, &size, - &mPackets->pkt)) == 0) - { - PacketList *self; - - if(size > 0) - break; - - /* Packet went unread and no data was given? Drop it and try the next, - * I guess... */ - self = mPackets; - mPackets = self->next; - - av_free_packet(&self->pkt); - av_free(self); - - if(!mPackets) - goto next_packet; - + /* Decode some data, and check for errors */ size = AVCODEC_MAX_AUDIO_FRAME_SIZE; - } + if((len=avcodec_decode_audio3(mCodecCtx, (int16_t*)mDecodedData, &size, &mPacket)) < 0) + return NULL; - if(len < 0) - return NULL; - - if(len < mPackets->pkt.size) - { /* Move the unread data to the front and clear the end bits */ - int remaining = mPackets->pkt.size - len; - memmove(mPackets->pkt.data, &mPackets->pkt.data[len], remaining); - av_shrink_packet(&mPackets->pkt, remaining); - } - else - { - PacketList *self; - - self = mPackets; - mPackets = self->next; - - av_free_packet(&self->pkt); - av_free(self); - } - - if(size == 0) - goto next_packet; + int remaining = mPacket.size - len; + if(remaining <= 0) + av_free_packet(&mPacket); + else + { + memmove(mPacket.data, &mPacket.data[len], remaining); + av_shrink_packet(&mPacket, remaining); + } + } while(size == 0); /* Set the output buffer size */ mDecodedDataSize = size; @@ -262,7 +199,7 @@ void FFmpeg_Decoder::open(const std::string &fname) std::auto_ptr stream(new MyStream); stream->mCodecCtx = mFormatCtx->streams[audio_idx]->codec; stream->mStreamIdx = audio_idx; - stream->mPackets = NULL; + memset(&stream->mPacket, 0, sizeof(stream->mPacket)); AVCodec *codec = avcodec_find_decoder(stream->mCodecCtx->codec_id); if(!codec) @@ -292,7 +229,7 @@ void FFmpeg_Decoder::close() { if(mStream.get()) { - mStream->clearPackets(); + av_free_packet(&mStream->mPacket); avcodec_close(mStream->mCodecCtx); av_free(mStream->mDecodedData); } @@ -392,7 +329,7 @@ void FFmpeg_Decoder::rewind() { av_seek_frame(mFormatCtx, -1, 0, 0); if(mStream.get()) - mStream->clearPackets(); + av_free_packet(&mStream->mPacket); mSamplesRead = 0; } From 5fff1c4e47b387aa433d436342909b4ace43626d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 23:22:37 -0800 Subject: [PATCH 185/378] Update the ffmpeg decoder to use avcodec_decode_audio4 --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 82 ++++++++++---------------- 1 file changed, 32 insertions(+), 50 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 70f7682ef..7b6144dd0 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -20,13 +20,14 @@ struct FFmpeg_Decoder::MyStream { int mStreamIdx; AVPacket mPacket; + AVFrame *mFrame; - char *mDecodedData; - size_t mDecodedDataSize; + int mFrameSize; + int mFramePos; FFmpeg_Decoder *mParent; - void *getAVAudioData(size_t *length); + bool getAVAudioData(); size_t readAVAudioData(void *data, size_t length); }; @@ -84,23 +85,20 @@ bool FFmpeg_Decoder::getNextPacket() return false; } -void *FFmpeg_Decoder::MyStream::getAVAudioData(size_t *length) +bool FFmpeg_Decoder::MyStream::getAVAudioData() { - int size, len; + int got_frame, len; - if(length) *length = 0; if(mCodecCtx->codec_type != AVMEDIA_TYPE_AUDIO) - return NULL; + return false; - mDecodedDataSize = 0; do { if(mPacket.size == 0 && !mParent->getNextPacket()) - return NULL; + return false; /* Decode some data, and check for errors */ - size = AVCODEC_MAX_AUDIO_FRAME_SIZE; - if((len=avcodec_decode_audio3(mCodecCtx, (int16_t*)mDecodedData, &size, &mPacket)) < 0) - return NULL; + if((len=avcodec_decode_audio4(mCodecCtx, mFrame, &got_frame, &mPacket)) < 0) + return false; /* Move the unread data to the front and clear the end bits */ int remaining = mPacket.size - len; @@ -111,13 +109,9 @@ void *FFmpeg_Decoder::MyStream::getAVAudioData(size_t *length) memmove(mPacket.data, &mPacket.data[len], remaining); av_shrink_packet(&mPacket, remaining); } - } while(size == 0); - - /* Set the output buffer size */ - mDecodedDataSize = size; - if(length) *length = mDecodedDataSize; + } while(got_frame == 0 || mFrame->nb_samples == 0); - return mDecodedData; + return true; } size_t FFmpeg_Decoder::MyStream::readAVAudioData(void *data, size_t length) @@ -127,34 +121,24 @@ size_t FFmpeg_Decoder::MyStream::readAVAudioData(void *data, size_t length) while(dec < length) { /* If there's no decoded data, find some */ - if(mDecodedDataSize == 0) + if(mFramePos >= mFrameSize) { - if(getAVAudioData(NULL) == NULL) + if(!getAVAudioData()) break; + mFramePos = 0; + mFrameSize = mFrame->nb_samples * mCodecCtx->channels * + av_get_bytes_per_sample(mCodecCtx->sample_fmt); } - if(mDecodedDataSize > 0) - { - /* Get the amount of bytes remaining to be written, and clamp to - * the amount of decoded data we have */ - size_t rem = length-dec; - if(rem > mDecodedDataSize) - rem = mDecodedDataSize; - - /* Copy the data to the app's buffer and increment */ - if(data != NULL) - { - memcpy(data, mDecodedData, rem); - data = (char*)data + rem; - } - dec += rem; + /* Get the amount of bytes remaining to be written, and clamp to + * the amount of decoded data we have */ + size_t rem = std::min(length-dec, mFrameSize-mFramePos); - /* If there's any decoded data left, move it to the front of the - * buffer for next time */ - if(rem < mDecodedDataSize) - memmove(mDecodedData, &mDecodedData[rem], mDecodedDataSize - rem); - mDecodedDataSize -= rem; - } + /* Copy the data to the app's buffer and increment */ + memcpy(data, mFrame->data[0]+mFramePos, rem); + data = (char*)data + rem; + dec += rem; + mFramePos += rem; } /* Return the number of bytes we were able to get */ @@ -162,7 +146,6 @@ size_t FFmpeg_Decoder::MyStream::readAVAudioData(void *data, size_t length) } - void FFmpeg_Decoder::open(const std::string &fname) { close(); @@ -211,8 +194,7 @@ void FFmpeg_Decoder::open(const std::string &fname) if(avcodec_open2(stream->mCodecCtx, codec, NULL) < 0) fail("Failed to open audio codec " + std::string(codec->long_name)); - stream->mDecodedData = (char*)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); - stream->mDecodedDataSize = 0; + stream->mFrame = avcodec_alloc_frame(); stream->mParent = this; mStream = stream; @@ -231,7 +213,7 @@ void FFmpeg_Decoder::close() { av_free_packet(&mStream->mPacket); avcodec_close(mStream->mCodecCtx); - av_free(mStream->mDecodedData); + av_free(mStream->mFrame); } mStream.reset(); @@ -315,13 +297,13 @@ void FFmpeg_Decoder::readAll(std::vector &output) if(!mStream.get()) fail("No audio stream"); - char *inbuf; - size_t got; - while((inbuf=(char*)mStream->getAVAudioData(&got)) != NULL && got > 0) + while(mStream->getAVAudioData()) { + size_t got = mStream->mFrame->nb_samples * mStream->mCodecCtx->channels * + av_get_bytes_per_sample(mStream->mCodecCtx->sample_fmt); + const char *inbuf = reinterpret_cast(mStream->mFrame->data[0]); output.insert(output.end(), inbuf, inbuf+got); - mSamplesRead += got / mStream->mCodecCtx->channels / - av_get_bytes_per_sample(mStream->mCodecCtx->sample_fmt); + mSamplesRead += mStream->mFrame->nb_samples; } } From 1a771ae67150a2ed4539cdb44db40f0e4fe8097c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 15 Dec 2012 23:46:32 -0800 Subject: [PATCH 186/378] Merge the stream struct into the parent decoder --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 142 +++++++++++-------------- apps/openmw/mwsound/ffmpeg_decoder.hpp | 13 ++- 2 files changed, 71 insertions(+), 84 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index 7b6144dd0..e0f071568 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -15,23 +15,6 @@ static void fail(const std::string &msg) { throw std::runtime_error("FFmpeg exception: "+msg); } -struct FFmpeg_Decoder::MyStream { - AVCodecContext *mCodecCtx; - int mStreamIdx; - - AVPacket mPacket; - AVFrame *mFrame; - - int mFrameSize; - int mFramePos; - - FFmpeg_Decoder *mParent; - - bool getAVAudioData(); - size_t readAVAudioData(void *data, size_t length); -}; - - int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size) { Ogre::DataStreamPtr stream = static_cast(user_data)->mDataStream; @@ -69,35 +52,36 @@ int64_t FFmpeg_Decoder::seek(void *user_data, int64_t offset, int whence) * handle for. */ bool FFmpeg_Decoder::getNextPacket() { - if(!mStream.get()) + if(!mStream) return false; - while(av_read_frame(mFormatCtx, &mStream->mPacket) >= 0) + int stream_idx = mStream - mFormatCtx->streams; + while(av_read_frame(mFormatCtx, &mPacket) >= 0) { /* Check if the packet belongs to this stream */ - if(mStream->mStreamIdx == mStream->mPacket.stream_index) + if(stream_idx == mPacket.stream_index) return true; /* Free the packet and look for another */ - av_free_packet(&mStream->mPacket); + av_free_packet(&mPacket); } return false; } -bool FFmpeg_Decoder::MyStream::getAVAudioData() +bool FFmpeg_Decoder::getAVAudioData() { int got_frame, len; - if(mCodecCtx->codec_type != AVMEDIA_TYPE_AUDIO) + if((*mStream)->codec->codec_type != AVMEDIA_TYPE_AUDIO) return false; do { - if(mPacket.size == 0 && !mParent->getNextPacket()) + if(mPacket.size == 0 && !getNextPacket()) return false; /* Decode some data, and check for errors */ - if((len=avcodec_decode_audio4(mCodecCtx, mFrame, &got_frame, &mPacket)) < 0) + if((len=avcodec_decode_audio4((*mStream)->codec, mFrame, &got_frame, &mPacket)) < 0) return false; /* Move the unread data to the front and clear the end bits */ @@ -114,7 +98,7 @@ bool FFmpeg_Decoder::MyStream::getAVAudioData() return true; } -size_t FFmpeg_Decoder::MyStream::readAVAudioData(void *data, size_t length) +size_t FFmpeg_Decoder::readAVAudioData(void *data, size_t length) { size_t dec = 0; @@ -126,8 +110,8 @@ size_t FFmpeg_Decoder::MyStream::readAVAudioData(void *data, size_t length) if(!getAVAudioData()) break; mFramePos = 0; - mFrameSize = mFrame->nb_samples * mCodecCtx->channels * - av_get_bytes_per_sample(mCodecCtx->sample_fmt); + mFrameSize = mFrame->nb_samples * (*mStream)->codec->channels * + av_get_bytes_per_sample((*mStream)->codec->sample_fmt); } /* Get the amount of bytes remaining to be written, and clamp to @@ -167,55 +151,46 @@ void FFmpeg_Decoder::open(const std::string &fname) if(avformat_find_stream_info(mFormatCtx, NULL) < 0) fail("Failed to find stream info in "+fname); - int audio_idx = -1; for(size_t j = 0;j < mFormatCtx->nb_streams;j++) { if(mFormatCtx->streams[j]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { - audio_idx = j; + mStream = &mFormatCtx->streams[j]; break; } } - if(audio_idx == -1) + if(!mStream) fail("No audio streams in "+fname); - std::auto_ptr stream(new MyStream); - stream->mCodecCtx = mFormatCtx->streams[audio_idx]->codec; - stream->mStreamIdx = audio_idx; - memset(&stream->mPacket, 0, sizeof(stream->mPacket)); + memset(&mPacket, 0, sizeof(mPacket)); - AVCodec *codec = avcodec_find_decoder(stream->mCodecCtx->codec_id); + AVCodec *codec = avcodec_find_decoder((*mStream)->codec->codec_id); if(!codec) { std::stringstream ss("No codec found for id "); - ss << stream->mCodecCtx->codec_id; + ss << (*mStream)->codec->codec_id; fail(ss.str()); } - if(avcodec_open2(stream->mCodecCtx, codec, NULL) < 0) + if(avcodec_open2((*mStream)->codec, codec, NULL) < 0) fail("Failed to open audio codec " + std::string(codec->long_name)); - stream->mFrame = avcodec_alloc_frame(); - - stream->mParent = this; - mStream = stream; + mFrame = avcodec_alloc_frame(); } catch(std::exception &e) { avformat_close_input(&mFormatCtx); - mFormatCtx = NULL; throw; } } void FFmpeg_Decoder::close() { - if(mStream.get()) - { - av_free_packet(&mStream->mPacket); - avcodec_close(mStream->mCodecCtx); - av_free(mStream->mFrame); - } - mStream.reset(); + if(mStream) + avcodec_close((*mStream)->codec); + mStream = NULL; + + av_free_packet(&mPacket); + av_freep(&mFrame); if(mFormatCtx) { @@ -223,7 +198,6 @@ void FFmpeg_Decoder::close() avformat_close_input(&mFormatCtx); av_free(context); } - mFormatCtx = NULL; mDataStream.setNull(); } @@ -235,83 +209,82 @@ std::string FFmpeg_Decoder::getName() void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) { - if(!mStream.get()) + if(!mStream) fail("No audio stream info"); - if(mStream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_U8) + if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_U8) *type = SampleType_UInt8; - else if(mStream->mCodecCtx->sample_fmt == AV_SAMPLE_FMT_S16) + else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16) *type = SampleType_Int16; else fail(std::string("Unsupported sample format: ")+ - av_get_sample_fmt_name(mStream->mCodecCtx->sample_fmt)); + av_get_sample_fmt_name((*mStream)->codec->sample_fmt)); - if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_MONO) + if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_MONO) *chans = ChannelConfig_Mono; - else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_STEREO) + else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_STEREO) *chans = ChannelConfig_Stereo; - else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_QUAD) + else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_QUAD) *chans = ChannelConfig_Quad; - else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_5POINT1) + else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_5POINT1) *chans = ChannelConfig_5point1; - else if(mStream->mCodecCtx->channel_layout == AV_CH_LAYOUT_7POINT1) + else if((*mStream)->codec->channel_layout == AV_CH_LAYOUT_7POINT1) *chans = ChannelConfig_7point1; - else if(mStream->mCodecCtx->channel_layout == 0) + else if((*mStream)->codec->channel_layout == 0) { /* Unknown channel layout. Try to guess. */ - if(mStream->mCodecCtx->channels == 1) + if((*mStream)->codec->channels == 1) *chans = ChannelConfig_Mono; - else if(mStream->mCodecCtx->channels == 2) + else if((*mStream)->codec->channels == 2) *chans = ChannelConfig_Stereo; else { std::stringstream sstr("Unsupported raw channel count: "); - sstr << mStream->mCodecCtx->channels; + sstr << (*mStream)->codec->channels; fail(sstr.str()); } } else { char str[1024]; - av_get_channel_layout_string(str, sizeof(str), mStream->mCodecCtx->channels, - mStream->mCodecCtx->channel_layout); + av_get_channel_layout_string(str, sizeof(str), (*mStream)->codec->channels, + (*mStream)->codec->channel_layout); fail(std::string("Unsupported channel layout: ")+str); } - *samplerate = mStream->mCodecCtx->sample_rate; + *samplerate = (*mStream)->codec->sample_rate; } size_t FFmpeg_Decoder::read(char *buffer, size_t bytes) { - if(!mStream.get()) + if(!mStream) fail("No audio stream"); - size_t got = mStream->readAVAudioData(buffer, bytes); - mSamplesRead += got / mStream->mCodecCtx->channels / - av_get_bytes_per_sample(mStream->mCodecCtx->sample_fmt); + size_t got = readAVAudioData(buffer, bytes); + mSamplesRead += got / (*mStream)->codec->channels / + av_get_bytes_per_sample((*mStream)->codec->sample_fmt); return got; } void FFmpeg_Decoder::readAll(std::vector &output) { - if(!mStream.get()) + if(!mStream) fail("No audio stream"); - while(mStream->getAVAudioData()) + while(getAVAudioData()) { - size_t got = mStream->mFrame->nb_samples * mStream->mCodecCtx->channels * - av_get_bytes_per_sample(mStream->mCodecCtx->sample_fmt); - const char *inbuf = reinterpret_cast(mStream->mFrame->data[0]); + size_t got = mFrame->nb_samples * (*mStream)->codec->channels * + av_get_bytes_per_sample((*mStream)->codec->sample_fmt); + const char *inbuf = reinterpret_cast(mFrame->data[0]); output.insert(output.end(), inbuf, inbuf+got); - mSamplesRead += mStream->mFrame->nb_samples; + mSamplesRead += mFrame->nb_samples; } } void FFmpeg_Decoder::rewind() { av_seek_frame(mFormatCtx, -1, 0, 0); - if(mStream.get()) - av_free_packet(&mStream->mPacket); + av_free_packet(&mPacket); mSamplesRead = 0; } @@ -320,12 +293,19 @@ size_t FFmpeg_Decoder::getSampleOffset() return mSamplesRead; } -FFmpeg_Decoder::FFmpeg_Decoder() : mFormatCtx(NULL), mSamplesRead(0) +FFmpeg_Decoder::FFmpeg_Decoder() + : mFormatCtx(NULL) + , mStream(NULL) + , mFrame(NULL) + , mFrameSize(0) + , mFramePos(0) + , mSamplesRead(0) { - static bool done_init = false; + memset(&mPacket, 0, sizeof(mPacket)); /* We need to make sure ffmpeg is initialized. Optionally silence warning * output from the lib */ + static bool done_init = false; if(!done_init) { av_register_all(); diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index b7d2e1bb2..dbd4f5adc 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -21,11 +21,15 @@ namespace MWSound { class FFmpeg_Decoder : public Sound_Decoder { - struct MyStream; - AVFormatContext *mFormatCtx; + AVStream **mStream; + + AVPacket mPacket; + AVFrame *mFrame; + + int mFrameSize; + int mFramePos; - std::auto_ptr mStream; size_t mSamplesRead; bool getNextPacket(); @@ -35,6 +39,9 @@ namespace MWSound static int writePacket(void *user_data, uint8_t *buf, int buf_size); static int64_t seek(void *user_data, int64_t offset, int whence); + bool getAVAudioData(); + size_t readAVAudioData(void *data, size_t length); + virtual void open(const std::string &fname); virtual void close(); From 4561c22e2b5f55253ea179907f7dfe62aa475faf Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 00:07:56 -0800 Subject: [PATCH 187/378] More fixes for the audio clock The audio_clock for the decoder represents the end of the current packet, so it needs to be adjusted back to match the position that's actually going to be read next. --- apps/openmw/mwrender/videoplayer.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 0165d9855..d57295f65 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -416,7 +416,7 @@ public: break; } - mFramePos = std::min(static_cast(mFrameSize), sample_skip); + mFramePos = std::min(mFrameSize, sample_skip); sample_skip -= mFramePos; } @@ -471,7 +471,9 @@ public: size_t getSampleOffset() { - return (size_t)(is->audio_clock*is->audio_st->codec->sample_rate); + ssize_t clock_delay = (mFrameSize-mFramePos) / is->audio_st->codec->channels / + av_get_bytes_per_sample(is->audio_st->codec->sample_fmt); + return (size_t)(is->audio_clock*is->audio_st->codec->sample_rate) - clock_delay; } }; From 0edc87825d1ea553a9ec8f7d990945608c6e8a09 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 01:17:58 -0800 Subject: [PATCH 188/378] Move audio_clock to the decoder where it's used --- apps/openmw/mwrender/videoplayer.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index d57295f65..cac25713a 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -58,11 +58,10 @@ struct VideoPicture { struct VideoState { VideoState() : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock_base(0), - audio_clock(0), audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), - audio_diff_threshold(0), audio_diff_avg_count(0), frame_last_pts(0), - frame_last_delay(0), video_clock(0), video_st(NULL), rgbaFrame(NULL), pictq_size(0), - pictq_rindex(0), pictq_windex(0), quit(false), refresh(0), format_ctx(0), - sws_context(NULL), display_ready(0) + audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), audio_diff_threshold(0), + audio_diff_avg_count(0), frame_last_pts(0), frame_last_delay(0), video_clock(0), + video_st(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), + quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) { } ~VideoState() @@ -114,7 +113,6 @@ struct VideoState { int av_sync_type; uint64_t external_clock_base; - double audio_clock; AVStream *audio_st; PacketQueue audioq; double audio_diff_cum; /* used for AV difference average computation */ @@ -256,6 +254,8 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder ssize_t mFramePos; ssize_t mFrameSize; + double audio_clock; + /* Add or subtract samples to get a better sync, return number of bytes to * skip (negative means to duplicate). */ int synchronize_audio() @@ -310,8 +310,8 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder if(!got_frame || frame->nb_samples <= 0) continue; - is->audio_clock += (double)frame->nb_samples / - (double)is->audio_st->codec->sample_rate; + this->audio_clock += (double)frame->nb_samples / + (double)is->audio_st->codec->sample_rate; /* We have data, return it and come back for more later */ return frame->nb_samples * av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * @@ -328,7 +328,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder /* if update, update the audio clock w/pts */ if((uint64_t)pkt->pts != AV_NOPTS_VALUE) - is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; + this->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; } } @@ -348,6 +348,7 @@ public: , mFrame(avcodec_alloc_frame()) , mFramePos(0) , mFrameSize(0) + , audio_clock(0.0) { } virtual ~MovieAudioDecoder() { @@ -473,7 +474,7 @@ public: { ssize_t clock_delay = (mFrameSize-mFramePos) / is->audio_st->codec->channels / av_get_bytes_per_sample(is->audio_st->codec->sample_fmt); - return (size_t)(is->audio_clock*is->audio_st->codec->sample_rate) - clock_delay; + return (size_t)(this->audio_clock*is->audio_st->codec->sample_rate) - clock_delay; } }; From e9d833be036879b650911bf65a9ae1a582eed5bb Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 01:56:52 -0800 Subject: [PATCH 189/378] Use the packet pts to calculate the decoder sample offset --- apps/openmw/mwsound/ffmpeg_decoder.cpp | 27 ++++++++++++++------------ apps/openmw/mwsound/ffmpeg_decoder.hpp | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwsound/ffmpeg_decoder.cpp b/apps/openmw/mwsound/ffmpeg_decoder.cpp index e0f071568..261a86ca6 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.cpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.cpp @@ -60,7 +60,11 @@ bool FFmpeg_Decoder::getNextPacket() { /* Check if the packet belongs to this stream */ if(stream_idx == mPacket.stream_index) + { + if((uint64_t)mPacket.pts != AV_NOPTS_VALUE) + mNextPts = av_q2d((*mStream)->time_base)*mPacket.pts; return true; + } /* Free the packet and look for another */ av_free_packet(&mPacket); @@ -94,6 +98,7 @@ bool FFmpeg_Decoder::getAVAudioData() av_shrink_packet(&mPacket, remaining); } } while(got_frame == 0 || mFrame->nb_samples == 0); + mNextPts += (double)mFrame->nb_samples / (double)(*mStream)->codec->sample_rate; return true; } @@ -162,8 +167,6 @@ void FFmpeg_Decoder::open(const std::string &fname) if(!mStream) fail("No audio streams in "+fname); - memset(&mPacket, 0, sizeof(mPacket)); - AVCodec *codec = avcodec_find_decoder((*mStream)->codec->codec_id); if(!codec) { @@ -259,11 +262,7 @@ size_t FFmpeg_Decoder::read(char *buffer, size_t bytes) { if(!mStream) fail("No audio stream"); - - size_t got = readAVAudioData(buffer, bytes); - mSamplesRead += got / (*mStream)->codec->channels / - av_get_bytes_per_sample((*mStream)->codec->sample_fmt); - return got; + return readAVAudioData(buffer, bytes); } void FFmpeg_Decoder::readAll(std::vector &output) @@ -277,20 +276,24 @@ void FFmpeg_Decoder::readAll(std::vector &output) av_get_bytes_per_sample((*mStream)->codec->sample_fmt); const char *inbuf = reinterpret_cast(mFrame->data[0]); output.insert(output.end(), inbuf, inbuf+got); - mSamplesRead += mFrame->nb_samples; } } void FFmpeg_Decoder::rewind() { - av_seek_frame(mFormatCtx, -1, 0, 0); + int stream_idx = mStream - mFormatCtx->streams; + if(av_seek_frame(mFormatCtx, stream_idx, 0, 0) < 0) + fail("Failed to seek in audio stream"); av_free_packet(&mPacket); - mSamplesRead = 0; + mFrameSize = mFramePos = 0; + mNextPts = 0.0; } size_t FFmpeg_Decoder::getSampleOffset() { - return mSamplesRead; + int delay = (mFrameSize-mFramePos) / (*mStream)->codec->channels / + av_get_bytes_per_sample((*mStream)->codec->sample_fmt); + return (int)(mNextPts*(*mStream)->codec->sample_rate) - delay; } FFmpeg_Decoder::FFmpeg_Decoder() @@ -299,7 +302,7 @@ FFmpeg_Decoder::FFmpeg_Decoder() , mFrame(NULL) , mFrameSize(0) , mFramePos(0) - , mSamplesRead(0) + , mNextPts(0.0) { memset(&mPacket, 0, sizeof(mPacket)); diff --git a/apps/openmw/mwsound/ffmpeg_decoder.hpp b/apps/openmw/mwsound/ffmpeg_decoder.hpp index dbd4f5adc..32b2797ed 100644 --- a/apps/openmw/mwsound/ffmpeg_decoder.hpp +++ b/apps/openmw/mwsound/ffmpeg_decoder.hpp @@ -30,7 +30,7 @@ namespace MWSound int mFrameSize; int mFramePos; - size_t mSamplesRead; + double mNextPts; bool getNextPacket(); From 3f6d36c7127bd5cb6144a56497ae5fe3daeaf663 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 02:19:19 -0800 Subject: [PATCH 190/378] Avoid double-setting the material texture --- apps/openmw/mwrender/videoplayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index cac25713a..c5976222f 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -949,7 +949,7 @@ VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) mVideoMaterial->getTechnique(0)->getPass(0)->setDepthWriteEnabled(false); mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); - mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState()->setTextureName("black.png"); + mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(); } mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); From dd20db5dc2ff26eda2b5dad4274eac3228b7310a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 03:05:44 -0800 Subject: [PATCH 191/378] Remove the stream indices from the VideoState --- apps/openmw/mwrender/videoplayer.cpp | 180 +++++++++++++-------------- 1 file changed, 87 insertions(+), 93 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index c5976222f..81eba2417 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -57,11 +57,14 @@ struct VideoPicture { struct VideoState { VideoState() - : videoStream(-1), audioStream(-1), av_sync_type(0), external_clock_base(0), - audio_st(NULL), audio_diff_cum(0), audio_diff_avg_coef(0), audio_diff_threshold(0), - audio_diff_avg_count(0), frame_last_pts(0), frame_last_delay(0), video_clock(0), - video_st(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0), - quit(false), refresh(0), format_ctx(0), sws_context(NULL), display_ready(0) + : format_ctx(NULL), av_sync_type(AV_SYNC_DEFAULT) + , external_clock_base(0.0) + , audio_st(NULL), audio_diff_cum(0.0), audio_diff_avg_coef(0.0), + audio_diff_threshold(0.0), audio_diff_avg_count(0) + , video_st(NULL), frame_last_pts(0.0), frame_last_delay(0.0), + video_clock(0.0), sws_context(NULL), rgbaFrame(NULL), pictq_size(0), + pictq_rindex(0), pictq_windex(0) + , refresh_rate_ms(10), refresh(false), quit(false), display_ready(false) { } ~VideoState() @@ -108,38 +111,33 @@ struct VideoState { static int64_t OgreResource_Seek(void *user_data, int64_t offset, int whence); - int videoStream, audioStream; + Ogre::DataStreamPtr stream; + AVFormatContext* format_ctx; int av_sync_type; uint64_t external_clock_base; - AVStream *audio_st; + AVStream** audio_st; PacketQueue audioq; double audio_diff_cum; /* used for AV difference average computation */ double audio_diff_avg_coef; double audio_diff_threshold; int audio_diff_avg_count; + MWBase::SoundPtr AudioTrack; + AVStream** video_st; double frame_last_pts; double frame_last_delay; double video_clock; ///audio_diff_cum * (1.0 - is->audio_diff_avg_coef); if(fabs(avg_diff) >= is->audio_diff_threshold) { - int n = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * - is->audio_st->codec->channels; - sample_skip = ((int)(diff * is->audio_st->codec->sample_rate) * n); + int n = av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt) * + (*is->audio_st)->codec->channels; + sample_skip = ((int)(diff * (*is->audio_st)->codec->sample_rate) * n); } } @@ -295,7 +292,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder { int len1, got_frame; - len1 = avcodec_decode_audio4(is->audio_st->codec, frame, &got_frame, pkt); + len1 = avcodec_decode_audio4((*is->audio_st)->codec, frame, &got_frame, pkt); if(len1 < 0) break; if(len1 <= pkt->size) @@ -311,11 +308,11 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder continue; this->audio_clock += (double)frame->nb_samples / - (double)is->audio_st->codec->sample_rate; + (double)(*is->audio_st)->codec->sample_rate; /* We have data, return it and come back for more later */ - return frame->nb_samples * av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * - is->audio_st->codec->channels; + return frame->nb_samples * av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt) * + (*is->audio_st)->codec->channels; } av_free_packet(pkt); @@ -328,7 +325,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder /* if update, update the audio clock w/pts */ if((uint64_t)pkt->pts != AV_NOPTS_VALUE) - this->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts; + this->audio_clock = av_q2d((*is->audio_st)->time_base)*pkt->pts; } } @@ -357,47 +354,47 @@ public: void getInfo(int *samplerate, MWSound::ChannelConfig *chans, MWSound::SampleType * type) { - if(is->audio_st->codec->sample_fmt == AV_SAMPLE_FMT_U8) + if((*is->audio_st)->codec->sample_fmt == AV_SAMPLE_FMT_U8) *type = MWSound::SampleType_UInt8; - else if(is->audio_st->codec->sample_fmt == AV_SAMPLE_FMT_S16) + else if((*is->audio_st)->codec->sample_fmt == AV_SAMPLE_FMT_S16) *type = MWSound::SampleType_Int16; else fail(std::string("Unsupported sample format: ")+ - av_get_sample_fmt_name(is->audio_st->codec->sample_fmt)); + av_get_sample_fmt_name((*is->audio_st)->codec->sample_fmt)); - if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_MONO) + if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_MONO) *chans = MWSound::ChannelConfig_Mono; - else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_STEREO) + else if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_STEREO) *chans = MWSound::ChannelConfig_Stereo; - else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_QUAD) + else if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_QUAD) *chans = MWSound::ChannelConfig_Quad; - else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_5POINT1) + else if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_5POINT1) *chans = MWSound::ChannelConfig_5point1; - else if(is->audio_st->codec->channel_layout == AV_CH_LAYOUT_7POINT1) + else if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_7POINT1) *chans = MWSound::ChannelConfig_7point1; - else if(is->audio_st->codec->channel_layout == 0) + else if((*is->audio_st)->codec->channel_layout == 0) { /* Unknown channel layout. Try to guess. */ - if(is->audio_st->codec->channels == 1) + if((*is->audio_st)->codec->channels == 1) *chans = MWSound::ChannelConfig_Mono; - else if(is->audio_st->codec->channels == 2) + else if((*is->audio_st)->codec->channels == 2) *chans = MWSound::ChannelConfig_Stereo; else { std::stringstream sstr("Unsupported raw channel count: "); - sstr << is->audio_st->codec->channels; + sstr << (*is->audio_st)->codec->channels; fail(sstr.str()); } } else { char str[1024]; - av_get_channel_layout_string(str, sizeof(str), is->audio_st->codec->channels, - is->audio_st->codec->channel_layout); + av_get_channel_layout_string(str, sizeof(str), (*is->audio_st)->codec->channels, + (*is->audio_st)->codec->channel_layout); fail(std::string("Unsupported channel layout: ")+str); } - *samplerate = is->audio_st->codec->sample_rate; + *samplerate = (*is->audio_st)->codec->sample_rate; } size_t read(char *stream, size_t len) @@ -431,8 +428,8 @@ public: { len1 = std::min(len1, -mFramePos); - int n = av_get_bytes_per_sample(is->audio_st->codec->sample_fmt) * - is->audio_st->codec->channels; + int n = av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt) * + (*is->audio_st)->codec->channels; /* add samples by copying the first sample*/ if(n == 1) @@ -472,9 +469,9 @@ public: size_t getSampleOffset() { - ssize_t clock_delay = (mFrameSize-mFramePos) / is->audio_st->codec->channels / - av_get_bytes_per_sample(is->audio_st->codec->sample_fmt); - return (size_t)(this->audio_clock*is->audio_st->codec->sample_rate) - clock_delay; + ssize_t clock_delay = (mFrameSize-mFramePos) / (*is->audio_st)->codec->channels / + av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt); + return (size_t)(this->audio_clock*(*is->audio_st)->codec->sample_rate) - clock_delay; } }; @@ -527,26 +524,26 @@ void VideoState::video_display() { VideoPicture *vp = &this->pictq[this->pictq_rindex]; - if(this->video_st->codec->width != 0 && this->video_st->codec->height != 0) + if((*this->video_st)->codec->width != 0 && (*this->video_st)->codec->height != 0) { Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().getByName("VideoTexture"); - if(texture.isNull () || static_cast(texture->getWidth()) != this->video_st->codec->width - || static_cast(texture->getHeight()) != this->video_st->codec->height) + if(texture.isNull() || static_cast(texture->getWidth()) != (*this->video_st)->codec->width + || static_cast(texture->getHeight()) != (*this->video_st)->codec->height) { Ogre::TextureManager::getSingleton ().remove ("VideoTexture"); texture = Ogre::TextureManager::getSingleton().createManual( "VideoTexture", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, - this->video_st->codec->width, this->video_st->codec->height, + (*this->video_st)->codec->width, (*this->video_st)->codec->height, 0, Ogre::PF_BYTE_RGBA, Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); } - Ogre::PixelBox pb(this->video_st->codec->width, this->video_st->codec->height, 1, Ogre::PF_BYTE_RGBA, &vp->data[0]); + Ogre::PixelBox pb((*this->video_st)->codec->width, (*this->video_st)->codec->height, 1, Ogre::PF_BYTE_RGBA, &vp->data[0]); Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer(); buffer->blitFromMemory(pb); - this->display_ready = 1; + this->display_ready = true; } } @@ -617,9 +614,9 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) // Convert the image into RGBA format for Ogre if(this->sws_context == NULL) { - int w = this->video_st->codec->width; - int h = this->video_st->codec->height; - this->sws_context = sws_getContext(w, h, this->video_st->codec->pix_fmt, + int w = (*this->video_st)->codec->width; + int h = (*this->video_st)->codec->height; + this->sws_context = sws_getContext(w, h, (*this->video_st)->codec->pix_fmt, w, h, PIX_FMT_RGBA, SWS_BICUBIC, NULL, NULL, NULL); if(this->sws_context == NULL) @@ -627,11 +624,11 @@ int VideoState::queue_picture(AVFrame *pFrame, double pts) } vp->pts = pts; - vp->data.resize(this->video_st->codec->width * this->video_st->codec->height * 4); + vp->data.resize((*this->video_st)->codec->width * (*this->video_st)->codec->height * 4); uint8_t *dst = &vp->data[0]; sws_scale(this->sws_context, pFrame->data, pFrame->linesize, - 0, this->video_st->codec->height, &dst, this->rgbaFrame->linesize); + 0, (*this->video_st)->codec->height, &dst, this->rgbaFrame->linesize); // now we inform our display thread that we have a pic ready this->pictq_windex = (this->pictq_windex+1) % VIDEO_PICTURE_QUEUE_SIZE; @@ -653,7 +650,7 @@ double VideoState::synchronize_video(AVFrame *src_frame, double pts) pts = this->video_clock; /* update the video clock */ - frame_delay = av_q2d(this->video_st->codec->time_base); + frame_delay = av_q2d((*this->video_st)->codec->time_base); /* if we are repeating a frame, adjust clock accordingly */ frame_delay += src_frame->repeat_pict * (frame_delay * 0.5); @@ -693,14 +690,14 @@ void VideoState::video_thread_loop(VideoState *self) pFrame = avcodec_alloc_frame(); self->rgbaFrame = avcodec_alloc_frame(); - avpicture_alloc((AVPicture*)self->rgbaFrame, PIX_FMT_RGBA, self->video_st->codec->width, self->video_st->codec->height); + avpicture_alloc((AVPicture*)self->rgbaFrame, PIX_FMT_RGBA, (*self->video_st)->codec->width, (*self->video_st)->codec->height); while(self->videoq.get(packet, self) >= 0) { // Save global pts to be stored in pFrame global_video_pkt_pts = packet->pts; // Decode video frame - if(avcodec_decode_video2(self->video_st->codec, pFrame, &frameFinished, packet) < 0) + if(avcodec_decode_video2((*self->video_st)->codec, pFrame, &frameFinished, packet) < 0) throw std::runtime_error("Error decoding video frame"); pts = 0; @@ -708,7 +705,7 @@ void VideoState::video_thread_loop(VideoState *self) pts = packet->dts; else if(pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) pts = *(uint64_t*)pFrame->opaque; - pts *= av_q2d(self->video_st->time_base); + pts *= av_q2d((*self->video_st)->time_base); av_free_packet(packet); @@ -734,14 +731,14 @@ void VideoState::decode_thread_loop(VideoState *self) try { - if(self->videoStream < 0 && self->audioStream < 0) + if(!self->video_st && !self->audio_st < 0) throw std::runtime_error("No streams to decode"); // main decode loop while(!self->quit) { - if((self->audioStream >= 0 && self->audioq.size > MAX_AUDIOQ_SIZE) || - (self->videoStream >= 0 && self->videoq.size > MAX_VIDEOQ_SIZE)) + if((self->audio_st >= 0 && self->audioq.size > MAX_AUDIOQ_SIZE) || + (self->video_st >= 0 && self->videoq.size > MAX_VIDEOQ_SIZE)) { boost::this_thread::sleep(boost::posix_time::milliseconds(10)); continue; @@ -751,9 +748,9 @@ void VideoState::decode_thread_loop(VideoState *self) break; // Is this a packet from the video stream? - if(packet->stream_index == self->videoStream) + if(self->video_st && packet->stream_index == self->video_st-pFormatCtx->streams) self->videoq.put(packet); - else if(packet->stream_index == self->audioStream) + else if(self->audio_st && packet->stream_index == self->audio_st-pFormatCtx->streams) self->audioq.put(packet); else av_free_packet(packet); @@ -774,7 +771,7 @@ void VideoState::decode_thread_loop(VideoState *self) std::cerr << "An error occured playing the video: " << e.getFullDescription () << std::endl; } - self->quit = 1; + self->quit = true; } @@ -799,8 +796,7 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) switch(codecCtx->codec_type) { case AVMEDIA_TYPE_AUDIO: - this->audioStream = stream_index; - this->audio_st = pFormatCtx->streams[stream_index]; + this->audio_st = pFormatCtx->streams + stream_index; /* averaging filter for audio sync */ this->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); @@ -812,16 +808,14 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) this->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); if(!this->AudioTrack) { - this->audioStream = -1; - avcodec_close(this->audio_st->codec); + avcodec_close((*this->audio_st)->codec); this->audio_st = NULL; return -1; } break; case AVMEDIA_TYPE_VIDEO: - this->videoStream = stream_index; - this->video_st = pFormatCtx->streams[stream_index]; + this->video_st = pFormatCtx->streams + stream_index; this->frame_last_delay = 40e-3; @@ -847,11 +841,9 @@ void VideoState::init(const std::string& resourceName) unsigned int i; this->av_sync_type = AV_SYNC_DEFAULT; - this->videoStream = -1; - this->audioStream = -1; this->refresh_rate_ms = 10; this->refresh = false; - this->quit = 0; + this->quit = false; this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); if(this->stream.isNull()) @@ -899,12 +891,12 @@ void VideoState::init(const std::string& resourceName) } catch(std::runtime_error& e) { - this->quit = 1; + this->quit = true; throw; } catch(Ogre::Exception& e) { - this->quit = 1; + this->quit = true; throw; } } @@ -918,13 +910,16 @@ void VideoState::deinit() this->video_thread.join(); this->refresh_thread.join(); - if(this->audioStream >= 0) - avcodec_close(this->audio_st->codec); - if(this->videoStream >= 0) - avcodec_close(this->video_st->codec); + if(this->audio_st) + avcodec_close((*this->audio_st)->codec); + this->audio_st = NULL; + if(this->video_st) + avcodec_close((*this->video_st)->codec); + this->video_st = NULL; if(this->sws_context) sws_freeContext(this->sws_context); + this->sws_context = NULL; if(this->format_ctx) { @@ -1012,6 +1007,7 @@ void VideoPlayer::playVideo(const std::string &resourceName) mRectangle->setVisible(true); mBackgroundRectangle->setVisible(true); + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Video); @@ -1029,8 +1025,6 @@ void VideoPlayer::playVideo(const std::string &resourceName) mState = new VideoState; mState->init(resourceName); - - mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); } void VideoPlayer::update () @@ -1048,23 +1042,23 @@ void VideoPlayer::update () mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("VideoTexture"); // Correct aspect ratio by adding black bars - double videoaspect = static_cast(mState->video_st->codec->width) / mState->video_st->codec->height; - - if (av_q2d(mState->video_st->codec->sample_aspect_ratio) != 0) - videoaspect *= av_q2d(mState->video_st->codec->sample_aspect_ratio); + double videoaspect = av_q2d((*mState->video_st)->codec->sample_aspect_ratio); + if(videoaspect == 0.0) + videoaspect = 1.0; + videoaspect *= static_cast((*mState->video_st)->codec->width) / (*mState->video_st)->codec->height; double screenaspect = static_cast(mWidth) / mHeight; double aspect_correction = videoaspect / screenaspect; - mRectangle->setCorners (std::max(-1.0, -1.0 * aspect_correction), std::min(1.0, 1.0 / aspect_correction), - std::min(1.0, 1.0 * aspect_correction), std::max(-1.0, -1.0 / aspect_correction)); + mRectangle->setCorners(std::max(-1.0, -1.0 * aspect_correction), std::min( 1.0, 1.0 / aspect_correction), + std::min( 1.0, 1.0 * aspect_correction), std::max(-1.0, -1.0 / aspect_correction)); } } } void VideoPlayer::close() { - mState->quit = 1; + mState->quit = true; mState->deinit(); delete mState; From 254a623319126d233d65321c03bce9f6cd094c1a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 03:09:24 -0800 Subject: [PATCH 192/378] Remove a redundant check --- apps/openmw/mwrender/videoplayer.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 81eba2417..817ec0a3f 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -316,9 +316,6 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder } av_free_packet(pkt); - if(is->quit) - return -1; - /* next packet */ if(is->audioq.get(pkt, is) < 0) return -1; From 8cde6db665056dd35aab98ceb036e405c0b34d02 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 03:17:17 -0800 Subject: [PATCH 193/378] We no longer need SDL --- CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 084363da7..f320c5c3c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,9 +163,6 @@ if (USE_MPG123) set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123) endif (USE_MPG123) -find_package (SDL REQUIRED) -set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${SDL_INCLUDE_DIR}) -set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${SDL_LIBRARY}) # Platform specific if (WIN32) From 77852439ce561091596fe749f9b79b33570c3ec7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 16 Dec 2012 12:52:23 +0100 Subject: [PATCH 194/378] moved ColumnBase and Column struct into a separate translation unit --- apps/opencs/model/world/columnbase.cpp | 13 ++++++ apps/opencs/model/world/columnbase.hpp | 57 ++++++++++++++++++++++++ apps/opencs/model/world/idcollection.cpp | 11 ----- apps/opencs/model/world/idcollection.hpp | 45 +------------------ 4 files changed, 71 insertions(+), 55 deletions(-) create mode 100644 apps/opencs/model/world/columnbase.cpp create mode 100644 apps/opencs/model/world/columnbase.hpp diff --git a/apps/opencs/model/world/columnbase.cpp b/apps/opencs/model/world/columnbase.cpp new file mode 100644 index 000000000..7adc7e6c3 --- /dev/null +++ b/apps/opencs/model/world/columnbase.cpp @@ -0,0 +1,13 @@ + +#include "columnbase.hpp" + +CSMWorld::ColumnBase::ColumnBase (const std::string& title, int flags) +: mTitle (title), mFlags (flags) +{} + +CSMWorld::ColumnBase::~ColumnBase() {} + +bool CSMWorld::ColumnBase::isUserEditable() const +{ + return isEditable(); +} \ No newline at end of file diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp new file mode 100644 index 000000000..dc077eff6 --- /dev/null +++ b/apps/opencs/model/world/columnbase.hpp @@ -0,0 +1,57 @@ +#ifndef CSM_WOLRD_COLUMNBASE_H +#define CSM_WOLRD_COLUMNBASE_H + +#include + +#include +#include + +#include "record.hpp" + +namespace CSMWorld +{ + struct ColumnBase + { + enum Roles + { + Role_Flags = Qt::UserRole + }; + + enum Flags + { + Flag_Table = 1, // column should be displayed in table view + Flag_Dialogue = 2 // column should be displayed in dialogue view + }; + + std::string mTitle; + int mFlags; + + ColumnBase (const std::string& title, int flag); + + virtual ~ColumnBase(); + + virtual bool isEditable() const = 0; + + virtual bool isUserEditable() const; + ///< Can this column be edited directly by the user? + }; + + template + struct Column : public ColumnBase + { + std::string mTitle; + int mFlags; + + Column (const std::string& title, int flags = Flag_Table | Flag_Dialogue) + : ColumnBase (title, flags) {} + + virtual QVariant get (const Record& record) const = 0; + + virtual void set (Record& record, const QVariant& data) + { + throw std::logic_error ("Column " + mTitle + " is not editable"); + } + }; +} + +#endif diff --git a/apps/opencs/model/world/idcollection.cpp b/apps/opencs/model/world/idcollection.cpp index c6469ea98..fc4bb1ef6 100644 --- a/apps/opencs/model/world/idcollection.cpp +++ b/apps/opencs/model/world/idcollection.cpp @@ -1,17 +1,6 @@ #include "idcollection.hpp" -CSMWorld::ColumnBase::ColumnBase (const std::string& title, int flags) -: mTitle (title), mFlags (flags) -{} - -CSMWorld::ColumnBase::~ColumnBase() {} - -bool CSMWorld::ColumnBase::isUserEditable() const -{ - return isEditable(); -} - CSMWorld::IdCollectionBase::IdCollectionBase() {} CSMWorld::IdCollectionBase::~IdCollectionBase() {} \ No newline at end of file diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 5d6f52aa0..1b2d1e349 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -11,53 +11,10 @@ #include -#include "record.hpp" +#include "columnbase.hpp" namespace CSMWorld { - struct ColumnBase - { - enum Roles - { - Role_Flags = Qt::UserRole - }; - - enum Flags - { - Flag_Table = 1, // column should be displayed in table view - Flag_Dialogue = 2 // column should be displayed in dialogue view - }; - - std::string mTitle; - int mFlags; - - ColumnBase (const std::string& title, int flag); - - virtual ~ColumnBase(); - - virtual bool isEditable() const = 0; - - virtual bool isUserEditable() const; - ///< Can this column be edited directly by the user? - }; - - template - struct Column : public ColumnBase - { - std::string mTitle; - int mFlags; - - Column (const std::string& title, int flags = Flag_Table | Flag_Dialogue) - : ColumnBase (title, flags) {} - - virtual QVariant get (const Record& record) const = 0; - - virtual void set (Record& record, const QVariant& data) - { - throw std::logic_error ("Column " + mTitle + " is not editable"); - } - }; - class IdCollectionBase { // not implemented From 3829bbfeca4aaa0b9889e6bffbe9137c751257ba Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 04:01:27 -0800 Subject: [PATCH 195/378] Look for all available sound input libs as needed, and warn if not found --- CMakeLists.txt | 58 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f320c5c3c..182cc268a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,8 +36,8 @@ option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF) option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest ang GMock frameworks" OFF) # Sound source selection -option(USE_FFMPEG "use ffmpeg for sound" OFF) -option(USE_AUDIERE "use audiere for sound" OFF) +option(USE_FFMPEG "use ffmpeg for sound" ON) +option(USE_AUDIERE "use audiere for sound" ON) option(USE_MPG123 "use mpg123 + libsndfile for sound" ON) # OS X deployment @@ -137,31 +137,53 @@ set(OPENMW_LIBS ${OENGINE_ALL}) set(OPENMW_LIBS_HEADER) # Sound setup +set(GOT_SOUND_INPUT 0) set(SOUND_INPUT_INCLUDES "") set(SOUND_INPUT_LIBRARY "") set(SOUND_DEFINE "") if (USE_FFMPEG) set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE) - find_package(FFmpeg REQUIRED) - set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIRS}) - set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES}) - set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG) + find_package(FFmpeg) + if (FFMPEG_FOUND) + set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIRS}) + set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES}) + set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG) + set(GOT_SOUND_INPUT 1) + endif (FFMPEG_FOUND) endif (USE_FFMPEG) -if (USE_AUDIERE) - find_package(Audiere REQUIRED) - set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${AUDIERE_INCLUDE_DIR}) - set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${AUDIERE_LIBRARY}) - set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_AUDIERE) -endif (USE_AUDIERE) - -if (USE_MPG123) +if (USE_AUDIERE AND NOT GOT_SOUND_INPUT) + find_package(Audiere) + if (AUDIERE_FOUND) + set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${AUDIERE_INCLUDE_DIR}) + set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${AUDIERE_LIBRARY}) + set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_AUDIERE) + set(GOT_SOUND_INPUT 1) + endif (AUDIERE_FOUND) +endif (USE_AUDIERE AND NOT GOT_SOUND_INPUT) + +if (USE_MPG123 AND NOT GOT_SOUND_INPUT) find_package(MPG123 REQUIRED) find_package(SNDFILE REQUIRED) - set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR}) - set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${MPG123_LIBRARY} ${SNDFILE_LIBRARY}) - set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123) -endif (USE_MPG123) + if (MPG123_FOUND AND SNDFILE_FOUND) + set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR}) + set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${MPG123_LIBRARY} ${SNDFILE_LIBRARY}) + set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123) + set(GOT_SOUND_INPUT 1) + endif (MPG123_FOUND AND SNDFILE_FOUND) +endif (USE_MPG123 AND NOT GOT_SOUND_INPUT) + +if (NOT GOT_SOUND_INPUT) + message(WARNING "--------------------") + message(WARNING "Failed to find any sound input packages") + message(WARNING "--------------------") +endif (NOT GOT_SOUND_INPUT) + +if (NOT FFMPEG_FOUND) + message(WARNING "--------------------") + message(WARNING "FFmpeg not found, video playback will be disabled") + message(WARNING "--------------------") +endif (NOT FFMPEG_FOUND) # Platform specific From 6bc526b74d7c939d6ebc1e471b12b719c388f772 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 05:30:38 -0800 Subject: [PATCH 196/378] Avoid another loop for decoding audio --- apps/openmw/mwrender/videoplayer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 817ec0a3f..1c22b7b3d 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -401,7 +401,7 @@ public: while(total < len) { - while(mFramePos >= mFrameSize) + if(mFramePos >= mFrameSize) { /* We have already sent all our data; get more */ mFrameSize = audio_decode_frame(mFrame); @@ -413,6 +413,7 @@ public: mFramePos = std::min(mFrameSize, sample_skip); sample_skip -= mFramePos; + continue; } size_t len1 = len - total; From c92cde2be92f092cbb00249457f39e355146fde3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 05:50:20 -0800 Subject: [PATCH 197/378] Properly flush packet queues when at EOF Note: the previous flush method was renamed to clear. Flushing a queue allows consumers to retrieve queued packets, but not expect any more to come in. --- apps/openmw/mwrender/videoplayer.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 1c22b7b3d..d2e317b71 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -29,12 +29,13 @@ enum { struct PacketQueue { PacketQueue() - : first_pkt(NULL), last_pkt(NULL), nb_packets(0), size(0) + : first_pkt(NULL), last_pkt(NULL), flushing(false), nb_packets(0), size(0) { } ~PacketQueue() - { flush(); } + { clear(); } AVPacketList *first_pkt, *last_pkt; + volatile bool flushing; int nb_packets; int size; @@ -45,6 +46,7 @@ struct PacketQueue { int get(AVPacket *pkt, VideoState *is); void flush(); + void clear(); }; struct VideoPicture { @@ -202,6 +204,8 @@ int PacketQueue::get(AVPacket *pkt, VideoState *is) return 1; } + if(this->flushing) + break; this->cond.wait(lock); } @@ -209,6 +213,12 @@ int PacketQueue::get(AVPacket *pkt, VideoState *is) } void PacketQueue::flush() +{ + this->flushing = true; + this->cond.notify_one(); +} + +void PacketQueue::clear() { AVPacketList *pkt, *pkt1; @@ -753,7 +763,10 @@ void VideoState::decode_thread_loop(VideoState *self) else av_free_packet(packet); } + /* all done - wait for it */ + self->videoq.flush(); + self->audioq.flush(); while(!self->quit) { // EOF reached, all packets processed, we can exit now From c5dd0e19688b388b6e4e978d5ecf07ec14f5085d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Dec 2012 16:05:31 +0100 Subject: [PATCH 198/378] New Game button --- apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwgui/mainmenu.cpp | 25 ++++++++++++++++++++----- apps/openmw/mwgui/mainmenu.hpp | 8 +++++++- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 15 ++++++++++++++- apps/openmw/mwworld/worldimp.hpp | 3 +++ 6 files changed, 47 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 198a20d45..3c707d196 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -82,6 +82,8 @@ namespace MWBase virtual OEngine::Render::Fader* getFader() = 0; ///< \ŧodo remove this function. Rendering details should not be exposed. + virtual void newGame() = 0; + virtual MWWorld::CellStore *getExterior (int x, int y) = 0; virtual MWWorld::CellStore *getInterior (const std::string& name) = 0; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index e98b75e9b..b19f3de6d 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -6,12 +6,15 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "confirmationdialog.hpp" + namespace MWGui { - MainMenu::MainMenu(int w, int h) + MainMenu::MainMenu(MWBase::WindowManager& parWindowManager, int w, int h) : OEngine::GUI::Layout("openmw_mainmenu.layout") , mButtonBox(0) + , mDialog(parWindowManager) { onResChange(w,h); } @@ -20,7 +23,7 @@ namespace MWGui { setCoord(0,0,w,h); - int height = 64 * 3; + int height = 64 * 4; if (mButtonBox) MyGUI::Gui::getInstance ().destroyWidget(mButtonBox); @@ -33,12 +36,11 @@ namespace MWGui mReturn->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::returnToGame); curH += 64; - - /* mNewGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); + mNewGame->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::newGame); mNewGame->setImageResource ("Menu_NewGame"); curH += 64; - +/* mLoadGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); mLoadGame->setImageResource ("Menu_LoadGame"); curH += 64; @@ -81,4 +83,17 @@ namespace MWGui Ogre::Root::getSingleton ().queueEndRendering (); } + void MainMenu::newGame(MyGUI::Widget* sender) + { + mDialog.open ("#{sNotifyMessage54}"); + mDialog.eventOkClicked.clear(); + mDialog.eventCancelClicked.clear(); + mDialog.eventOkClicked += MyGUI::newDelegate(this, &MainMenu::newGameConfirmed); + } + + void MainMenu::newGameConfirmed() + { + MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); + MWBase::Environment::get().getWorld ()->newGame(); + } } diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index fd583d187..ab48f29d9 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -1,12 +1,14 @@ #include +#include "confirmationdialog.hpp" + namespace MWGui { class MainMenu : public OEngine::GUI::Layout { public: - MainMenu(int w, int h); + MainMenu(MWBase::WindowManager& parWindowManager, int w, int h); void onResChange(int w, int h); @@ -24,6 +26,10 @@ namespace MWGui void returnToGame(MyGUI::Widget* sender); void showOptions(MyGUI::Widget* sender); void exitGame(MyGUI::Widget* sender); + void newGame(MyGUI::Widget* sender); + void newGameConfirmed(); + + ConfirmationDialog mDialog; }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 373546aa8..627f8f339 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -139,7 +139,7 @@ WindowManager::WindowManager( mDragAndDrop->mDraggedWidget = 0; mDragAndDrop->mDragAndDropWidget = dragAndDropWidget; - mMenu = new MainMenu(w,h); + mMenu = new MainMenu(*this, w,h); mMap = new MapWindow(*this, cacheDir); mStatsWindow = new StatsWindow(*this); mConsole = new Console(w,h, consoleOnlyScripts); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8eb121d37..f1fb67632 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -170,7 +170,7 @@ namespace MWWorld const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, const std::string& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), - mSky (true), mCells (mStore, mEsm), + mSky (true), mCells (mStore, mEsm), mNewGameStarted(false), mNumFacing(0) { mPhysics = new PhysicsSystem(renderer); @@ -1015,6 +1015,12 @@ namespace MWWorld } } } + + if (mNewGameStarted) + { + playVideo ("mw_intro.bik"); + mNewGameStarted = false; + } } bool World::isCellExterior() const @@ -1296,4 +1302,11 @@ namespace MWWorld { mRendering->stopVideo(); } + + void World::newGame () + { + // set new game mark + mGlobalVariables->setInt ("chargenstate", 1); + mNewGameStarted = true; // in order to play the intro video at the end of the next frame + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1c1352913..a58e70d12 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -60,6 +60,7 @@ namespace MWWorld MWWorld::Globals *mGlobalVariables; MWWorld::PhysicsSystem *mPhysics; bool mSky; + bool mNewGameStarted; Cells mCells; @@ -102,6 +103,8 @@ namespace MWWorld virtual OEngine::Render::Fader* getFader(); ///< \ŧodo remove this function. Rendering details should not be exposed. + virtual void newGame(); + virtual CellStore *getExterior (int x, int y); virtual CellStore *getInterior (const std::string& name); From 86671096ec89960405726594a50e5a7fa787b075 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Dec 2012 16:14:49 +0100 Subject: [PATCH 199/378] remove commandline switch for new game --- apps/openmw/engine.cpp | 10 ++-------- apps/openmw/engine.hpp | 4 ---- apps/openmw/main.cpp | 4 ---- apps/openmw/mwbase/windowmanager.hpp | 2 ++ apps/openmw/mwgui/mainmenu.cpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 12 +++++++++--- apps/openmw/mwgui/windowmanagerimp.hpp | 4 +++- apps/openmw/mwworld/worldimp.cpp | 8 +------- apps/openmw/mwworld/worldimp.hpp | 2 +- 9 files changed, 19 insertions(+), 28 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2299053cd..08e24b5d6 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -125,7 +125,6 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mFpsLevel(0) , mDebug (false) , mVerboseScripts (false) - , mNewGame (false) , mUseSound (true) , mCompileAll (false) , mScriptContext (0) @@ -237,11 +236,6 @@ void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity) mVerboseScripts = scriptsVerbosity; } -void OMW::Engine::setNewGame(bool newGame) -{ - mNewGame = newGame; -} - // Initialise and enter main loop. void OMW::Engine::go() @@ -332,13 +326,13 @@ void OMW::Engine::go() // Create the world mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster, - mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoding, mFallbackMap)); + mResDir, mCfgMgr.getCachePath(), mEncoding, mFallbackMap)); // Create window manager - this manages all the MW-specific GUI windows MWScript::registerExtensions (mExtensions); mEnvironment.setWindowManager (new MWGui::WindowManager( - mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), + mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), mCfgMgr.getCachePath ().string(), mScriptConsoleMode)); // Create sound system diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 57402c91e..b13ec4368 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -68,7 +68,6 @@ namespace OMW int mFpsLevel; bool mDebug; bool mVerboseScripts; - bool mNewGame; bool mUseSound; bool mCompileAll; std::string mFocusName; @@ -138,9 +137,6 @@ namespace OMW /// Disable or enable all sounds void setSoundUsage(bool soundUsage); - /// Start as a new game. - void setNewGame(bool newGame); - /// Initialise and enter main loop. void go(); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 0563fdbbb..6b31f3ade 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -133,9 +133,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("script-run", bpo::value()->default_value(""), "select a file containing a list of console commands that is executed on startup") - ("new-game", bpo::value()->implicit_value(true) - ->default_value(false), "activate char gen/new game mechanics") - ("fs-strict", bpo::value()->implicit_value(true) ->default_value(false), "strict file system handling (no case folding)") @@ -238,7 +235,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat // startup-settings engine.setCell(variables["start"].as()); - engine.setNewGame(variables["new-game"].as()); // other settings engine.setDebugMode(variables["debug"].as()); diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index c177912d4..9df75870d 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -79,6 +79,8 @@ namespace MWBase */ virtual void update() = 0; + virtual void newGame() = 0; + virtual void pushGuiMode (MWGui::GuiMode mode) = 0; virtual void popGuiMode() = 0; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index b19f3de6d..0f74f6e14 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -94,6 +94,7 @@ namespace MWGui void MainMenu::newGameConfirmed() { MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); + MWBase::Environment::get().getWindowManager ()->newGame (); MWBase::Environment::get().getWorld ()->newGame(); } } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 627f8f339..1f5966f72 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -54,7 +54,7 @@ using namespace MWGui; WindowManager::WindowManager( - const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, + const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts) : mGuiManager(NULL) , mHud(NULL) @@ -95,8 +95,8 @@ WindowManager::WindowManager( , mGui(NULL) , mGarbageDialogs() , mShown(GW_ALL) - , mAllowed(newGame ? GW_None : GW_ALL) - , mRestAllowed(newGame ? false : true) + , mAllowed(GW_ALL) + , mRestAllowed(true) , mShowFPSLevel(fpsLevel) , mFPS(0.0f) , mTriangleCount(0) @@ -1041,3 +1041,9 @@ void WindowManager::startTraining(MWWorld::Ptr actor) { mTrainingWindow->startTraining(actor); } + +void WindowManager::newGame () +{ + mAllowed = GW_None; + mRestAllowed = false; +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 2e684b5da..db5c22348 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -74,7 +74,7 @@ namespace MWGui typedef std::pair Faction; typedef std::vector FactionList; - WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, + WindowManager(const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts); virtual ~WindowManager(); @@ -86,6 +86,8 @@ namespace MWGui */ virtual void update(); + virtual void newGame(); + virtual void pushGuiMode(GuiMode mode); virtual void popGuiMode(); virtual void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f1fb67632..afcdb8d39 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -167,7 +167,7 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, + const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, const std::string& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mCells (mStore, mEsm), mNewGameStarted(false), @@ -197,12 +197,6 @@ namespace MWWorld // global variables mGlobalVariables = new Globals (mStore); - if (newGame) - { - // set new game mark - mGlobalVariables->setInt ("chargenstate", 1); - } - mGlobalVariables->setInt ("pcrace", 3); mWorldScene = new Scene(*mRendering, mPhysics); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index a58e70d12..8f89ecff3 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -95,7 +95,7 @@ namespace MWWorld World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, + const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, const std::string& encoding, std::map fallbackMap); virtual ~World(); From f0f521a4e0550ae454100065fc2a0a1b05fed5ac Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 16 Dec 2012 16:55:59 +0100 Subject: [PATCH 200/378] enableRestMenu -> enableRest --- apps/openmw/mwscript/guiextensions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 72c2db164..7d437f3c0 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -181,7 +181,7 @@ opcodeEnableStatsReviewMenu); extensions.registerInstruction ("enablemapmenu", "", opcodeEnableMapMenu); extensions.registerInstruction ("enablestatsmenu", "", opcodeEnableStatsMenu); - extensions.registerInstruction ("enablerestmenu", "", opcodeEnableRest); + extensions.registerInstruction ("enablerest", "", opcodeEnableRest); extensions.registerInstruction ("enablelevelupmenu", "", opcodeEnableRest); extensions.registerInstruction ("showrestmenu", "", opcodeShowRestMenu); From 06fd66e99dfd6c8aea5b29c8e881baa852e2315a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 16 Dec 2012 11:49:46 -0800 Subject: [PATCH 201/378] Move some fields to the class they're used in --- apps/openmw/mwrender/videoplayer.cpp | 47 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index d2e317b71..3e1701095 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -61,8 +61,7 @@ struct VideoState { VideoState() : format_ctx(NULL), av_sync_type(AV_SYNC_DEFAULT) , external_clock_base(0.0) - , audio_st(NULL), audio_diff_cum(0.0), audio_diff_avg_coef(0.0), - audio_diff_threshold(0.0), audio_diff_avg_count(0) + , audio_st(NULL) , video_st(NULL), frame_last_pts(0.0), frame_last_delay(0.0), video_clock(0.0), sws_context(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0) @@ -121,10 +120,6 @@ struct VideoState { AVStream** audio_st; PacketQueue audioq; - double audio_diff_cum; /* used for AV difference average computation */ - double audio_diff_avg_coef; - double audio_diff_threshold; - int audio_diff_avg_count; MWBase::SoundPtr AudioTrack; AVStream** video_st; @@ -261,7 +256,13 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder ssize_t mFramePos; ssize_t mFrameSize; - double audio_clock; + double mAudioClock; + + /* averaging filter for audio sync */ + double mAudioDiffAccum; + double mAudioDiffAvgCoef; + double mAudioDiffThreshold; + int mAudioDiffAvgCount; /* Add or subtract samples to get a better sync, return number of bytes to * skip (negative means to duplicate). */ @@ -274,14 +275,13 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder // accumulate the clock difference double diff = is->get_master_clock() - is->get_audio_clock(); - is->audio_diff_cum = diff + is->audio_diff_avg_coef * - is->audio_diff_cum; - if(is->audio_diff_avg_count < AUDIO_DIFF_AVG_NB) - is->audio_diff_avg_count++; + mAudioDiffAccum = diff + mAudioDiffAvgCoef * mAudioDiffAccum; + if(mAudioDiffAvgCount < AUDIO_DIFF_AVG_NB) + mAudioDiffAvgCount++; else { - double avg_diff = is->audio_diff_cum * (1.0 - is->audio_diff_avg_coef); - if(fabs(avg_diff) >= is->audio_diff_threshold) + double avg_diff = mAudioDiffAccum * (1.0 - mAudioDiffAvgCoef); + if(fabs(avg_diff) >= mAudioDiffThreshold) { int n = av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt) * (*is->audio_st)->codec->channels; @@ -317,8 +317,8 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder if(!got_frame || frame->nb_samples <= 0) continue; - this->audio_clock += (double)frame->nb_samples / - (double)(*is->audio_st)->codec->sample_rate; + mAudioClock += (double)frame->nb_samples / + (double)(*is->audio_st)->codec->sample_rate; /* We have data, return it and come back for more later */ return frame->nb_samples * av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt) * @@ -332,7 +332,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder /* if update, update the audio clock w/pts */ if((uint64_t)pkt->pts != AV_NOPTS_VALUE) - this->audio_clock = av_q2d((*is->audio_st)->time_base)*pkt->pts; + mAudioClock = av_q2d((*is->audio_st)->time_base)*pkt->pts; } } @@ -352,7 +352,12 @@ public: , mFrame(avcodec_alloc_frame()) , mFramePos(0) , mFrameSize(0) - , audio_clock(0.0) + , mAudioClock(0.0) + , mAudioDiffAccum(0.0) + , mAudioDiffAvgCoef(exp(log(0.01 / AUDIO_DIFF_AVG_NB))) + /* Correct audio only if larger error than this */ + , mAudioDiffThreshold(2.0 * 0.050/* 50 ms */) + , mAudioDiffAvgCount(0) { } virtual ~MovieAudioDecoder() { @@ -479,7 +484,7 @@ public: { ssize_t clock_delay = (mFrameSize-mFramePos) / (*is->audio_st)->codec->channels / av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt); - return (size_t)(this->audio_clock*(*is->audio_st)->codec->sample_rate) - clock_delay; + return (size_t)(mAudioClock*(*is->audio_st)->codec->sample_rate) - clock_delay; } }; @@ -809,12 +814,6 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) case AVMEDIA_TYPE_AUDIO: this->audio_st = pFormatCtx->streams + stream_index; - /* averaging filter for audio sync */ - this->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); - this->audio_diff_avg_count = 0; - /* Correct audio only if larger error than this */ - this->audio_diff_threshold = 2.0 * 0.050/* 50 ms */; - decoder.reset(new MovieAudioDecoder(this)); this->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); if(!this->AudioTrack) From 26660110e5f900c5ca93993b212fec66c390b0bd Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 17 Dec 2012 00:20:56 -0800 Subject: [PATCH 202/378] Allow building the video player without ffmpeg (playVideo will always throw an exception) --- apps/openmw/mwrender/videoplayer.cpp | 122 ++++++++++++++++++--------- apps/openmw/mwrender/videoplayer.hpp | 22 ++--- 2 files changed, 89 insertions(+), 55 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 3e1701095..9c4546d15 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -1,5 +1,15 @@ #include "videoplayer.hpp" +#define __STDC_CONSTANT_MACROS +#include + +#include +#include + +#include +#include + +#include #include "../mwbase/windowmanager.hpp" #include "../mwbase/environment.hpp" @@ -8,17 +18,24 @@ #include "../mwsound/sound.hpp" +namespace MWRender +{ + +#ifdef OPENMW_USE_FFMPEG + +extern "C" +{ +#include +#include +#include +} + #define MAX_AUDIOQ_SIZE (5 * 16 * 1024) #define MAX_VIDEOQ_SIZE (5 * 256 * 1024) #define AV_SYNC_THRESHOLD 0.01 -#define SAMPLE_CORRECTION_PERCENT_MAX 10 #define AUDIO_DIFF_AVG_NB 20 #define VIDEO_PICTURE_QUEUE_SIZE 1 - -namespace MWRender -{ - enum { AV_SYNC_AUDIO_MASTER, AV_SYNC_VIDEO_MASTER, @@ -27,6 +44,7 @@ enum { AV_SYNC_DEFAULT = AV_SYNC_EXTERNAL_MASTER }; + struct PacketQueue { PacketQueue() : first_pkt(NULL), last_pkt(NULL), flushing(false), nb_packets(0), size(0) @@ -66,16 +84,21 @@ struct VideoState { video_clock(0.0), sws_context(NULL), rgbaFrame(NULL), pictq_size(0), pictq_rindex(0), pictq_windex(0) , refresh_rate_ms(10), refresh(false), quit(false), display_ready(false) - { } + { + // Register all formats and codecs + av_register_all(); + } ~VideoState() - { } + { deinit(); } void init(const std::string& resourceName); void deinit(); int stream_open(int stream_index, AVFormatContext *pFormatCtx); + bool update(Ogre::MaterialPtr &mat, Ogre::Rectangle2D *rect, int screen_width, int screen_height); + static void video_thread_loop(VideoState *is); static void decode_thread_loop(VideoState *is); @@ -791,6 +814,35 @@ void VideoState::decode_thread_loop(VideoState *self) } +bool VideoState::update(Ogre::MaterialPtr &mat, Ogre::Rectangle2D *rect, int screen_width, int screen_height) +{ + if(this->quit) + return false; + + if(this->refresh) + { + this->refresh = false; + this->video_refresh_timer(); + // Would be nice not to do this all the time... + if(this->display_ready) + mat->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("VideoTexture"); + + // Correct aspect ratio by adding black bars + double videoaspect = av_q2d((*this->video_st)->codec->sample_aspect_ratio); + if(videoaspect == 0.0) + videoaspect = 1.0; + videoaspect *= static_cast((*this->video_st)->codec->width) / (*this->video_st)->codec->height; + + double screenaspect = static_cast(screen_width) / screen_height; + double aspect_correction = videoaspect / screenaspect; + + rect->setCorners(std::max(-1.0, -1.0 * aspect_correction), std::min( 1.0, 1.0 / aspect_correction), + std::min( 1.0, 1.0 * aspect_correction), std::max(-1.0, -1.0 / aspect_correction)); + } + return true; +} + + int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) { MWSound::DecoderPtr decoder; @@ -899,12 +951,7 @@ void VideoState::init(const std::string& resourceName) this->parse_thread = boost::thread(decode_thread_loop, this); } - catch(std::runtime_error& e) - { - this->quit = true; - throw; - } - catch(Ogre::Exception& e) + catch(...) { this->quit = true; throw; @@ -913,6 +960,8 @@ void VideoState::init(const std::string& resourceName) void VideoState::deinit() { + this->quit = true; + this->audioq.cond.notify_one(); this->videoq.cond.notify_one(); @@ -939,6 +988,27 @@ void VideoState::deinit() } } +#else // defined OPENMW_USE_FFMPEG + +class VideoState +{ +public: + VideoState() { } + + void init(const std::string& resourceName) + { + throw std::runtime_error("FFmpeg not supported, cannot play video \""+resourceName+"\""); + } + void deinit() { } + + void close() { } + + bool update(Ogre::MaterialPtr &mat, Ogre::Rectangle2D *rect, int screen_width, int screen_height) + { return false; } +}; + +#endif // defined OPENMW_USE_FFMPEG + VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) : mState(NULL) @@ -1009,9 +1079,6 @@ VideoPlayer::~VideoPlayer() void VideoPlayer::playVideo(const std::string &resourceName) { - // Register all formats and codecs - av_register_all(); - if(mState) close(); @@ -1041,34 +1108,13 @@ void VideoPlayer::update () { if(mState) { - if(mState->quit) + if(!mState->update(mVideoMaterial, mRectangle, mWidth, mHeight)) close(); - else if(mState->refresh) - { - mState->refresh = false; - mState->video_refresh_timer(); - // Would be nice not to do this all the time... - if(mState->display_ready) - mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("VideoTexture"); - - // Correct aspect ratio by adding black bars - double videoaspect = av_q2d((*mState->video_st)->codec->sample_aspect_ratio); - if(videoaspect == 0.0) - videoaspect = 1.0; - videoaspect *= static_cast((*mState->video_st)->codec->width) / (*mState->video_st)->codec->height; - - double screenaspect = static_cast(mWidth) / mHeight; - double aspect_correction = videoaspect / screenaspect; - - mRectangle->setCorners(std::max(-1.0, -1.0 * aspect_correction), std::min( 1.0, 1.0 / aspect_correction), - std::min( 1.0, 1.0 * aspect_correction), std::max(-1.0, -1.0 / aspect_correction)); - } } } void VideoPlayer::close() { - mState->quit = true; mState->deinit(); delete mState; diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index c82a16e15..d8c1902aa 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -1,27 +1,15 @@ #ifndef VIDEOPLAYER_H #define VIDEOPLAYER_H -#include -#include +#include -#include - - -#define __STDC_CONSTANT_MACROS -#include -extern "C" +namespace Ogre { -#include -#include -#include + class SceneManager; + class SceneNode; + class Rectangle2D; } -#include -#include - -#include "../mwbase/soundmanager.hpp" - - namespace MWRender { struct VideoState; From 9e842a0bbb96b5a282307342d89b8a29a9718dc3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 17 Dec 2012 00:41:04 -0800 Subject: [PATCH 203/378] Fix for trying to play videos when not supported --- apps/openmw/mwrender/videoplayer.cpp | 113 ++++++++++++++------------- 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 9c4546d15..9fdd051b6 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -896,66 +896,58 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) void VideoState::init(const std::string& resourceName) { - try - { - int video_index = -1; - int audio_index = -1; - unsigned int i; - - this->av_sync_type = AV_SYNC_DEFAULT; - this->refresh_rate_ms = 10; - this->refresh = false; - this->quit = false; + int video_index = -1; + int audio_index = -1; + unsigned int i; - this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); - if(this->stream.isNull()) - throw std::runtime_error("Failed to open video resource"); + this->av_sync_type = AV_SYNC_DEFAULT; + this->refresh_rate_ms = 10; + this->refresh = false; + this->quit = false; - AVIOContext *ioCtx = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if(!ioCtx) throw std::runtime_error("Failed to allocate AVIOContext"); + this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); + if(this->stream.isNull()) + throw std::runtime_error("Failed to open video resource"); - this->format_ctx = avformat_alloc_context(); - if(this->format_ctx) - this->format_ctx->pb = ioCtx; + AVIOContext *ioCtx = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if(!ioCtx) throw std::runtime_error("Failed to allocate AVIOContext"); - // Open video file - /// \todo leak here, ffmpeg or valgrind bug ? - if(!this->format_ctx || avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) - { - // "Note that a user-supplied AVFormatContext will be freed on failure." - this->format_ctx = NULL; - av_free(ioCtx); - throw std::runtime_error("Failed to open video input"); - } + this->format_ctx = avformat_alloc_context(); + if(this->format_ctx) + this->format_ctx->pb = ioCtx; - // Retrieve stream information - if(avformat_find_stream_info(this->format_ctx, NULL) < 0) - throw std::runtime_error("Failed to retrieve stream information"); + // Open video file + /// \todo leak here, ffmpeg or valgrind bug ? + if(!this->format_ctx || avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) + { + // "Note that a user-supplied AVFormatContext will be freed on failure." + this->format_ctx = NULL; + av_free(ioCtx); + throw std::runtime_error("Failed to open video input"); + } - // Dump information about file onto standard error - av_dump_format(this->format_ctx, 0, resourceName.c_str(), 0); + // Retrieve stream information + if(avformat_find_stream_info(this->format_ctx, NULL) < 0) + throw std::runtime_error("Failed to retrieve stream information"); - for(i = 0;i < this->format_ctx->nb_streams;i++) - { - if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) - video_index = i; - if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) - audio_index = i; - } + // Dump information about file onto standard error + av_dump_format(this->format_ctx, 0, resourceName.c_str(), 0); - this->external_clock_base = av_gettime(); - if(audio_index >= 0) - this->stream_open(audio_index, this->format_ctx); - if(video_index >= 0) - this->stream_open(video_index, this->format_ctx); - - this->parse_thread = boost::thread(decode_thread_loop, this); - } - catch(...) + for(i = 0;i < this->format_ctx->nb_streams;i++) { - this->quit = true; - throw; + if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) + video_index = i; + if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) + audio_index = i; } + + this->external_clock_base = av_gettime(); + if(audio_index >= 0) + this->stream_open(audio_index, this->format_ctx); + if(video_index >= 0) + this->stream_open(video_index, this->format_ctx); + + this->parse_thread = boost::thread(decode_thread_loop, this); } void VideoState::deinit() @@ -997,7 +989,7 @@ public: void init(const std::string& resourceName) { - throw std::runtime_error("FFmpeg not supported, cannot play video \""+resourceName+"\""); + throw std::runtime_error("FFmpeg not supported, cannot play \""+resourceName+"\""); } void deinit() { } @@ -1100,8 +1092,14 @@ void VideoPlayer::playVideo(const std::string &resourceName) MWBase::Environment::get().getSoundManager()->pauseAllSounds(); - mState = new VideoState; - mState->init(resourceName); + try { + mState = new VideoState; + mState->init(resourceName); + } + catch(std::exception& e) { + std::cerr<< "Failed to play video: "<deinit(); + if(mState) + { + mState->deinit(); - delete mState; - mState = NULL; + delete mState; + mState = NULL; + } MWBase::Environment::get().getSoundManager()->resumeAllSounds(); From 58ab3407b7ccbe5b395db4ed66e2587a68c091f4 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 17 Dec 2012 04:17:06 -0800 Subject: [PATCH 204/378] Constify a couple fields --- apps/openmw/mwrender/videoplayer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 9fdd051b6..38f7a9d16 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -283,9 +283,9 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder /* averaging filter for audio sync */ double mAudioDiffAccum; - double mAudioDiffAvgCoef; - double mAudioDiffThreshold; - int mAudioDiffAvgCount; + const double mAudioDiffAvgCoef; + const double mAudioDiffThreshold; + int mAudioDiffAvgCount; /* Add or subtract samples to get a better sync, return number of bytes to * skip (negative means to duplicate). */ From 4373218746eea967d4275c9363ae65f80b4202b4 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 17 Dec 2012 06:56:30 -0800 Subject: [PATCH 205/378] Fix audio stream check --- apps/openmw/mwrender/videoplayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 38f7a9d16..56312aa26 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -767,7 +767,7 @@ void VideoState::decode_thread_loop(VideoState *self) try { - if(!self->video_st && !self->audio_st < 0) + if(!self->video_st && !self->audio_st) throw std::runtime_error("No streams to decode"); // main decode loop From 67485d3454ea3dca7812b951ed76f3b96c2b807e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 17 Dec 2012 07:15:53 -0800 Subject: [PATCH 206/378] Store the AVStream in the decoder for easy referencing --- apps/openmw/mwrender/videoplayer.cpp | 72 ++++++++++++++-------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 56312aa26..9c72e9919 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -272,7 +272,8 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder { av_free_packet(this); } }; - VideoState *is; + VideoState *mVideoState; + AVStream *mAVStream; AutoAVPacket mPacket; AVFrame *mFrame; @@ -291,13 +292,13 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder * skip (negative means to duplicate). */ int synchronize_audio() { - if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) + if(mVideoState->av_sync_type == AV_SYNC_AUDIO_MASTER) return 0; int sample_skip = 0; // accumulate the clock difference - double diff = is->get_master_clock() - is->get_audio_clock(); + double diff = mVideoState->get_master_clock() - mVideoState->get_audio_clock(); mAudioDiffAccum = diff + mAudioDiffAvgCoef * mAudioDiffAccum; if(mAudioDiffAvgCount < AUDIO_DIFF_AVG_NB) mAudioDiffAvgCount++; @@ -306,9 +307,9 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder double avg_diff = mAudioDiffAccum * (1.0 - mAudioDiffAvgCoef); if(fabs(avg_diff) >= mAudioDiffThreshold) { - int n = av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt) * - (*is->audio_st)->codec->channels; - sample_skip = ((int)(diff * (*is->audio_st)->codec->sample_rate) * n); + int n = av_get_bytes_per_sample(mAVStream->codec->sample_fmt) * + mAVStream->codec->channels; + sample_skip = ((int)(diff * mAVStream->codec->sample_rate) * n); } } @@ -325,7 +326,7 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder { int len1, got_frame; - len1 = avcodec_decode_audio4((*is->audio_st)->codec, frame, &got_frame, pkt); + len1 = avcodec_decode_audio4(mAVStream->codec, frame, &got_frame, pkt); if(len1 < 0) break; if(len1 <= pkt->size) @@ -341,21 +342,21 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder continue; mAudioClock += (double)frame->nb_samples / - (double)(*is->audio_st)->codec->sample_rate; + (double)mAVStream->codec->sample_rate; /* We have data, return it and come back for more later */ - return frame->nb_samples * av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt) * - (*is->audio_st)->codec->channels; + return frame->nb_samples * mAVStream->codec->channels * + av_get_bytes_per_sample(mAVStream->codec->sample_fmt); } av_free_packet(pkt); /* next packet */ - if(is->audioq.get(pkt, is) < 0) + if(mVideoState->audioq.get(pkt, mVideoState) < 0) return -1; /* if update, update the audio clock w/pts */ if((uint64_t)pkt->pts != AV_NOPTS_VALUE) - mAudioClock = av_q2d((*is->audio_st)->time_base)*pkt->pts; + mAudioClock = av_q2d(mAVStream->time_base)*pkt->pts; } } @@ -365,13 +366,14 @@ class MovieAudioDecoder : public MWSound::Sound_Decoder void close() { } std::string getName() - { return is->stream->getName(); } + { return mVideoState->stream->getName(); } void rewind() { } public: - MovieAudioDecoder(VideoState *_is) - : is(_is) + MovieAudioDecoder(VideoState *is) + : mVideoState(is) + , mAVStream(*is->audio_st) , mFrame(avcodec_alloc_frame()) , mFramePos(0) , mFrameSize(0) @@ -389,47 +391,47 @@ public: void getInfo(int *samplerate, MWSound::ChannelConfig *chans, MWSound::SampleType * type) { - if((*is->audio_st)->codec->sample_fmt == AV_SAMPLE_FMT_U8) + if(mAVStream->codec->sample_fmt == AV_SAMPLE_FMT_U8) *type = MWSound::SampleType_UInt8; - else if((*is->audio_st)->codec->sample_fmt == AV_SAMPLE_FMT_S16) + else if(mAVStream->codec->sample_fmt == AV_SAMPLE_FMT_S16) *type = MWSound::SampleType_Int16; else fail(std::string("Unsupported sample format: ")+ - av_get_sample_fmt_name((*is->audio_st)->codec->sample_fmt)); + av_get_sample_fmt_name(mAVStream->codec->sample_fmt)); - if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_MONO) + if(mAVStream->codec->channel_layout == AV_CH_LAYOUT_MONO) *chans = MWSound::ChannelConfig_Mono; - else if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_STEREO) + else if(mAVStream->codec->channel_layout == AV_CH_LAYOUT_STEREO) *chans = MWSound::ChannelConfig_Stereo; - else if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_QUAD) + else if(mAVStream->codec->channel_layout == AV_CH_LAYOUT_QUAD) *chans = MWSound::ChannelConfig_Quad; - else if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_5POINT1) + else if(mAVStream->codec->channel_layout == AV_CH_LAYOUT_5POINT1) *chans = MWSound::ChannelConfig_5point1; - else if((*is->audio_st)->codec->channel_layout == AV_CH_LAYOUT_7POINT1) + else if(mAVStream->codec->channel_layout == AV_CH_LAYOUT_7POINT1) *chans = MWSound::ChannelConfig_7point1; - else if((*is->audio_st)->codec->channel_layout == 0) + else if(mAVStream->codec->channel_layout == 0) { /* Unknown channel layout. Try to guess. */ - if((*is->audio_st)->codec->channels == 1) + if(mAVStream->codec->channels == 1) *chans = MWSound::ChannelConfig_Mono; - else if((*is->audio_st)->codec->channels == 2) + else if(mAVStream->codec->channels == 2) *chans = MWSound::ChannelConfig_Stereo; else { std::stringstream sstr("Unsupported raw channel count: "); - sstr << (*is->audio_st)->codec->channels; + sstr << mAVStream->codec->channels; fail(sstr.str()); } } else { char str[1024]; - av_get_channel_layout_string(str, sizeof(str), (*is->audio_st)->codec->channels, - (*is->audio_st)->codec->channel_layout); + av_get_channel_layout_string(str, sizeof(str), mAVStream->codec->channels, + mAVStream->codec->channel_layout); fail(std::string("Unsupported channel layout: ")+str); } - *samplerate = (*is->audio_st)->codec->sample_rate; + *samplerate = mAVStream->codec->sample_rate; } size_t read(char *stream, size_t len) @@ -464,8 +466,8 @@ public: { len1 = std::min(len1, -mFramePos); - int n = av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt) * - (*is->audio_st)->codec->channels; + int n = av_get_bytes_per_sample(mAVStream->codec->sample_fmt) * + mAVStream->codec->channels; /* add samples by copying the first sample*/ if(n == 1) @@ -505,9 +507,9 @@ public: size_t getSampleOffset() { - ssize_t clock_delay = (mFrameSize-mFramePos) / (*is->audio_st)->codec->channels / - av_get_bytes_per_sample((*is->audio_st)->codec->sample_fmt); - return (size_t)(mAudioClock*(*is->audio_st)->codec->sample_rate) - clock_delay; + ssize_t clock_delay = (mFrameSize-mFramePos) / mAVStream->codec->channels / + av_get_bytes_per_sample(mAVStream->codec->sample_fmt); + return (size_t)(mAudioClock*mAVStream->codec->sample_rate) - clock_delay; } }; From 32f051d61dfe6be90bfacc4413d560c63a31ed64 Mon Sep 17 00:00:00 2001 From: Thoronador Date: Mon, 17 Dec 2012 23:13:33 +0100 Subject: [PATCH 207/378] Remove 'GMST fixing' for dirty GMST records The 'fixing' for so-called dirty GMSTs does not work properly in its current state anyway, so it should be removed. Fixing the 'GMST fixing' might not be possible, because whether or not a GMST is 'dirty' depends on the language version of MW. Therefore different 'fixing' algorithms would be required for the different MW localisations, and I do not see a good reason why GMST values should be hard-coded in the GMST load procedure. In my opinion, it only clutters the code. Last but not least, I believe that it is not the task of the engine to clean ESM files from dirty entries. That is a job for the modders, who should only release clean ESM/ESP files in the first place. The engine should not need to worry about whether a file is 'dirty' or not. That is why I believe a feature for cleaning ESM/ESP files shall not be part of the engine. --- components/esm/loadgmst.cpp | 139 +----------------------------------- components/esm/loadgmst.hpp | 60 ---------------- 2 files changed, 1 insertion(+), 198 deletions(-) diff --git a/components/esm/loadgmst.cpp b/components/esm/loadgmst.cpp index 14e29f4ae..6ce20f935 100644 --- a/components/esm/loadgmst.cpp +++ b/components/esm/loadgmst.cpp @@ -10,138 +10,10 @@ namespace ESM { -/// \todo Review GMST "fixing". Probably remove completely or at least make it optional. Its definitely not -/// working properly in its current state and I doubt it can be fixed without breaking other stuff. - -// Some handy macros -#define cI(s,x) { label = (s); boost::algorithm::to_lower(label); if (mId == label) return (mI == (x)); } -#define cF(s,x) { label = (s); boost::algorithm::to_lower(label); if (mId == label) return (mF == (x)); } -#define cS(s,x) { label = (s); boost::algorithm::to_lower(label); if (mId == label) return (mStr == (x)); } - -bool GameSetting::isDirtyTribunal() -{ - /* - Here, mId contains the game setting name, and we check the - setting for certain values. If it matches, this is a "mDirty" - entry. The correct entry (as defined in Tribunal and Bloodmoon - esms) are given in the comments. Many of the values are correct, - and are marked as 'same'. We still ignore them though, as they - are still in the wrong file and might override custom values - from other mods. - */ - - std::string label; - // Strings - cS("sProfitValue", "Profit Value"); // 'Profit:' - cS("sEditNote", "Edit Note"); // same - cS("sDeleteNote", "Delete Note?"); // same - cS("sMaxSale", "Max Sale"); // 'Seller Max' - cS("sMagicFabricantID", "Fabricant"); // 'Fabricant_summon' - cS("sTeleportDisabled", - "Teleportation magic does not work here.");// same - cS("sLevitateDisabled", - "Levitation magic does not work here."); // same - cS("sCompanionShare", "Companion Share"); // 'Share' - cS("sCompanionWarningButtonOne", - "Let the mercenary quit."); // same - cS("sCompanionWarningButtonTwo", - "Return to Companion Share display."); // same - cS("sCompanionWarningMessage", - "Your mercenary is poorer now than when he contracted with you. Your mercenary will quit if you do not give him gold or goods to bring his Profit Value to a positive value."); - // 'Your mercenary is poorer now than when he contracted with - // you. Your mercenary will quit if you do not give him gold - // or goods to bring his Profit to a positive value.' - // [The difference here is "Profit Value" -> "Profit"] - - // Strings that matches the mId - cS("sEffectSummonFabricant", "sEffectSummonFabricant");// 'Summon Fabricant' - return false; -} - -// Bloodmoon variant -bool GameSetting::isDirtyBloodmoon() -{ - std::string label; - // Strings - cS("sWerewolfPopup", "Werewolf"); // same - cS("sWerewolfRestMessage", - "You cannot rest in werewolf form."); // same - cS("sWerewolfRefusal", - "You cannot do this as a werewolf."); // same - cS("sWerewolfAlarmMessage", - "You have been detected changing from a werewolf state."); - // 'You have been detected as a known werewolf.' - - // Strings that matches the mId - cS("sMagicCreature01ID", "sMagicCreature01ID"); // 'BM_wolf_grey_summon' - cS("sMagicCreature02ID", "sMagicCreature02ID"); // 'BM_bear_black_summon' - cS("sMagicCreature03ID", "sMagicCreature03ID"); // 'BM_wolf_bone_summon' - cS("sMagicCreature04ID", "sMagicCreature04ID"); // same - cS("sMagicCreature05ID", "sMagicCreature05ID"); // same - cS("sEffectSummonCreature01", "sEffectSummonCreature01"); // 'Calf Wolf' - cS("sEffectSummonCreature02", "sEffectSummonCreature02"); // 'Calf Bear' - cS("sEffectSummonCreature03", "sEffectSummonCreature03"); // 'Summon Bonewolf' - cS("sEffectSummonCreature04", "sEffectSummonCreature04"); // same - cS("sEffectSummonCreature05", "sEffectSummonCreature05"); // same - - // Integers - cI("iWereWolfBounty", 10000); // 1000 - cI("iWereWolfFightMod", 100); // same - cI("iWereWolfFleeMod", 100); // same - cI("iWereWolfLevelToAttack", 20); // same - - // Floats - cF("fFleeDistance", 3000); // same - cF("fCombatDistanceWerewolfMod", 0.3); // same - cF("fWereWolfFatigue", 400); // same - cF("fWereWolfEnchant", 1); // 0 - cF("fWereWolfArmorer", 1); // 0 - cF("fWereWolfBlock", 1); // 0 - cF("fWereWolfSneak", 1); // 95 - cF("fWereWolfDestruction", 1); // 0 - cF("fWereWolfEndurance", 150); // same - cF("fWereWolfConjuration", 1); // 0 - cF("fWereWolfRestoration", 1); // 0 - cF("fWereWolfAthletics", 150); // 50 - cF("fWereWolfLuck", 1); // 25 - cF("fWereWolfSilverWeaponDamageMult", 1.5); // 2 - cF("fWereWolfMediumArmor", 1); // 0 - cF("fWereWolfShortBlade", 1); // 0 - cF("fWereWolfAcrobatics", 150); // 80 - cF("fWereWolfSpeechcraft", 1); // 0 - cF("fWereWolfAlteration", 1); // 0 - cF("fWereWolfIllusion", 1); // 0 - cF("fWereWolfLongBlade", 1); // 0 - cF("fWereWolfMarksman", 1); // 0 - cF("fWereWolfHandtoHand", 100); // same - cF("fWereWolfIntellegence", 1); // 0 - cF("fWereWolfAlchemy", 1); // 0 - cF("fWereWolfUnarmored", 100); // same - cF("fWereWolfAxe", 1); // 0 - cF("fWereWolfRunMult", 1.5); // 1.3 - cF("fWereWolfMagicka", 100); // same - cF("fWereWolfAgility", 150); // same - cF("fWereWolfBluntWeapon", 1); // 0 - cF("fWereWolfSecurity", 1); // 0 - cF("fWereWolfPersonality", 1); // 0 - cF("fWereWolfMerchantile", 1); // 0 - cF("fWereWolfHeavyArmor", 1); // 0 - cF("fWereWolfSpear", 1); // 0 - cF("fWereWolfStrength", 150); // same - cF("fWereWolfHealth", 2); // same - cF("fWereWolfMysticism", 1); // 0 - cF("fWereWolfLightArmor", 1); // 0 - cF("fWereWolfWillPower", 1); // 0 - cF("fWereWolfSpeed", 150); // 90 - return false; -} - void GameSetting::load(ESMReader &esm) { assert(mId != ""); - mDirty = false; - // We are apparently allowed to be empty if (!esm.hasMoreSubs()) { @@ -169,17 +41,8 @@ void GameSetting::load(ESMReader &esm) } else esm.fail("Unwanted subrecord type"); - - int spf = esm.getSpecial(); - - // Check if this is one of the mDirty values mentioned above. If it - // is, we set the mDirty flag. This will ONLY work if you've set - // the 'id' string correctly before calling load(). - - if ((spf != SF_Tribunal && isDirtyTribunal()) || (spf != SF_Bloodmoon - && isDirtyBloodmoon())) - mDirty = true; } + void GameSetting::save(ESMWriter &esm) { switch(mType) diff --git a/components/esm/loadgmst.hpp b/components/esm/loadgmst.hpp index a3471598c..ab9a9551e 100644 --- a/components/esm/loadgmst.hpp +++ b/components/esm/loadgmst.hpp @@ -25,66 +25,6 @@ struct GameSetting float mF; VarType mType; - // Set to true if this is a 'dirty' entry which should be ignored - bool mDirty; - - /* - These functions check if this game setting is one of the "dirty" - GMST records found in many mods. These are due to a serious bug in - the official TES3 editor. It only occurs in the newer editor - versions that came with Tribunal and Bloodmoon, and only if a - modder tries to make a mod without loading the corresponding - expansion master file. For example, if you have Tribunal installed - and try to make a mod without loading Tribunal.esm, the editor - will insert these GMST records as a replacement for the entries it - cannot find in the ESMs. - - The values of these "dirty" records differ in general from their - values as defined in Tribunal.esm and Bloodmoon.esm, and are - always set to the same "default" values. Most of these values are - nonsensical, ie. changing the "Seller Max" string to "Max Sale", - or change the stats of werewolves to useless values like 1. Some - of them break certain spell effects. - - It is most likely that these values are just leftover values from - an early stage of development that are inserted as default values - by the editor code. They are supposed to be overridden when the - correct esm file is loaded. When it isn't loaded however, you get - stuck with the initial value, and this gets written to every mod - by the editor, for some reason. - - Bethesda themselves have fallen for this bug. If you install both - Tribunal and Bloodmoon, the updated Tribunal.esm will contain the - dirty GMST settings from Bloodmoon, and Bloodmoon.esm will contain - some of the dirty settings from Tribunal. In other words, this bug - affects the game EVEN IF YOU DO NOT USE ANY MODS! - - The guys at Bethesda are well aware of this bug (and many others), - as the mod community and fan base complained about them for a long - time. But unfortunately it was never fixed. - - There are several tools available to help modders remove these - records from their files, but not all modders use them, and they - really shouldn't have to. In this file we choose instead to reject - all the corrupt values at load time. - - These functions checks if the current game setting is one of the - "dirty" ones as described above. TODO: I have not checked this - against other sources yet, do that later. Currently recognizes 22 - values for tribunal and 50 for bloodmoon. Legitimate GMSTs in mods - (setting values other than the default "dirty" ones) are not - affected and will work correctly. - */ - - /* - Checks for dirty tribunal values. These will be ignored if found - in any file except when they are found in "Tribunal.esm". - */ - bool isDirtyTribunal(); - - // Bloodmoon variant - bool isDirtyBloodmoon(); - void load(ESMReader &esm); int getInt() const; From 5a7a8629b6477fb103709f21df394a936b595458 Mon Sep 17 00:00:00 2001 From: Thoronador Date: Mon, 17 Dec 2012 23:20:43 +0100 Subject: [PATCH 208/378] remove unnecessary include directive --- components/esm/loadgmst.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/esm/loadgmst.cpp b/components/esm/loadgmst.cpp index 6ce20f935..a73095a66 100644 --- a/components/esm/loadgmst.cpp +++ b/components/esm/loadgmst.cpp @@ -2,8 +2,6 @@ #include -#include - #include "esmreader.hpp" #include "esmwriter.hpp" From d348435a1d550fbfd27b4e1fe78a4a39ce56cefe Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 17 Dec 2012 21:09:57 -0800 Subject: [PATCH 209/378] Improve audio open error message --- apps/openmw/mwsound/soundmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index efdb6f7ea..4ec3dfbb3 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -86,7 +86,7 @@ namespace MWSound { if(devname.empty()) throw; - std::cout <<"Failed to open device \""<init(); Settings::Manager::setString("device", "Sound", ""); } From 20321c45528851c7ab0ea26ab46e3aa33b338025 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 17 Dec 2012 21:50:01 -0800 Subject: [PATCH 210/378] Keep track of the actual active sounds --- apps/openmw/mwsound/openal_output.cpp | 52 ++++++++++++++++++++++----- apps/openmw/mwsound/openal_output.hpp | 3 ++ 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index bfead7843..94499c8fb 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -131,6 +131,8 @@ class OpenAL_SoundStream : public Sound OpenAL_SoundStream(const OpenAL_SoundStream &rhs); OpenAL_SoundStream& operator=(const OpenAL_SoundStream &rhs); + friend class OpenAL_Output; + public: OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder); virtual ~OpenAL_SoundStream(); @@ -232,6 +234,8 @@ OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, Decode mBufferSize = static_cast(sBufferLength*srate); mBufferSize = framesToBytes(mBufferSize, chans, type); + + mOutput.mActiveSounds.push_back(this); } catch(std::exception &e) { @@ -252,6 +256,9 @@ OpenAL_SoundStream::~OpenAL_SoundStream() alGetError(); mDecoder->close(); + + mOutput.mActiveSounds.erase(std::find(mOutput.mActiveSounds.begin(), + mOutput.mActiveSounds.end(), this)); } void OpenAL_SoundStream::play() @@ -402,6 +409,8 @@ protected: ALuint mSource; ALuint mBuffer; + friend class OpenAL_Output; + private: OpenAL_Sound(const OpenAL_Sound &rhs); OpenAL_Sound& operator=(const OpenAL_Sound &rhs); @@ -435,6 +444,7 @@ public: OpenAL_Sound::OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf) : mOutput(output), mSource(src), mBuffer(buf) { + mOutput.mActiveSounds.push_back(this); } OpenAL_Sound::~OpenAL_Sound() { @@ -443,6 +453,9 @@ OpenAL_Sound::~OpenAL_Sound() mOutput.mFreeSources.push_back(mSource); mOutput.bufferFinished(mBuffer); + + mOutput.mActiveSounds.erase(std::find(mOutput.mActiveSounds.begin(), + mOutput.mActiveSounds.end(), this)); } void OpenAL_Sound::stop() @@ -597,6 +610,7 @@ void OpenAL_Output::deinit() { mStreamThread->removeAll(); + std::cerr<< "There are "< 0) alDeleteSources(mSources.size(), &mSources[0]); @@ -889,11 +903,22 @@ void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 void OpenAL_Output::pauseAllSounds() { - IDVec sources = mSources; - IDDq::const_iterator iter = mFreeSources.begin(); - while(iter != mFreeSources.end()) + IDVec sources; + SoundVec::const_iterator iter = mActiveSounds.begin(); + while(iter != mActiveSounds.end()) { - sources.erase(std::find(sources.begin(), sources.end(), *iter)); + const OpenAL_SoundStream *stream = dynamic_cast(*iter); + if(stream) + { + if(stream->mSource) + sources.push_back(stream->mSource); + } + else + { + const OpenAL_Sound *sound = dynamic_cast(*iter); + if(sound && sound->mSource) + sources.push_back(sound->mSource); + } iter++; } if(sources.size() > 0) @@ -902,11 +927,22 @@ void OpenAL_Output::pauseAllSounds() void OpenAL_Output::resumeAllSounds() { - IDVec sources = mSources; - IDDq::const_iterator iter = mFreeSources.begin(); - while(iter != mFreeSources.end()) + IDVec sources; + SoundVec::const_iterator iter = mActiveSounds.begin(); + while(iter != mActiveSounds.end()) { - sources.erase(std::find(sources.begin(), sources.end(), *iter)); + const OpenAL_SoundStream *stream = dynamic_cast(*iter); + if(stream) + { + if(stream->mSource) + sources.push_back(stream->mSource); + } + else + { + const OpenAL_Sound *sound = dynamic_cast(*iter); + if(sound && sound->mSource) + sources.push_back(sound->mSource); + } iter++; } if(sources.size() > 0) diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index ce126035f..41ce56965 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -36,6 +36,9 @@ namespace MWSound uint64_t mBufferCacheMemSize; + typedef std::vector SoundVec; + SoundVec mActiveSounds; + ALuint getBuffer(const std::string &fname); void bufferFinished(ALuint buffer); From dd3e568a003d19ce4717a61cb03b8b194a3b8601 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 17 Dec 2012 23:35:20 -0800 Subject: [PATCH 211/378] Set the sound properties at initialization --- apps/openmw/mwsound/openal_output.cpp | 134 +++++++++++------------- apps/openmw/mwsound/openal_output.hpp | 4 +- apps/openmw/mwsound/sound.hpp | 15 +-- apps/openmw/mwsound/sound_output.hpp | 4 +- apps/openmw/mwsound/soundmanagerimp.cpp | 36 ++----- 5 files changed, 80 insertions(+), 113 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 94499c8fb..824a72299 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -128,13 +128,15 @@ class OpenAL_SoundStream : public Sound volatile bool mIsFinished; + void updateAll(bool local); + OpenAL_SoundStream(const OpenAL_SoundStream &rhs); OpenAL_SoundStream& operator=(const OpenAL_SoundStream &rhs); friend class OpenAL_Output; public: - OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder); + OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder, float basevol, float pitch, int flags); virtual ~OpenAL_SoundStream(); virtual void stop(); @@ -215,8 +217,9 @@ private: }; -OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder) - : mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true) +OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder, float basevol, float pitch, int flags) + : Sound(Ogre::Vector3(0.0f), 1.0f, basevol, pitch, 1.0f, 1000.0f, flags) + , mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true) { throwALerror(); @@ -324,6 +327,25 @@ double OpenAL_SoundStream::getTimeOffset() return t; } +void OpenAL_SoundStream::updateAll(bool local) +{ + alSourcef(mSource, AL_REFERENCE_DISTANCE, mMinDistance); + alSourcef(mSource, AL_MAX_DISTANCE, mMaxDistance); + if(local) + { + alSourcef(mSource, AL_ROLLOFF_FACTOR, 0.0f); + alSourcei(mSource, AL_SOURCE_RELATIVE, AL_TRUE); + } + else + { + alSourcef(mSource, AL_ROLLOFF_FACTOR, 1.0f); + alSourcei(mSource, AL_SOURCE_RELATIVE, AL_FALSE); + } + alSourcei(mSource, AL_LOOPING, AL_FALSE); + + update(); +} + void OpenAL_SoundStream::update() { ALfloat gain = mVolume*mBaseVolume; @@ -411,12 +433,14 @@ protected: friend class OpenAL_Output; + void updateAll(bool local); + private: OpenAL_Sound(const OpenAL_Sound &rhs); OpenAL_Sound& operator=(const OpenAL_Sound &rhs); public: - OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf); + OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf, const Ogre::Vector3& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags); virtual ~OpenAL_Sound(); virtual void stop(); @@ -434,15 +458,16 @@ class OpenAL_Sound3D : public OpenAL_Sound OpenAL_Sound3D& operator=(const OpenAL_Sound &rhs); public: - OpenAL_Sound3D(OpenAL_Output &output, ALuint src, ALuint buf) - : OpenAL_Sound(output, src, buf) + OpenAL_Sound3D(OpenAL_Output &output, ALuint src, ALuint buf, const Ogre::Vector3& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) + : OpenAL_Sound(output, src, buf, pos, vol, basevol, pitch, mindist, maxdist, flags) { } virtual void update(); }; -OpenAL_Sound::OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf) - : mOutput(output), mSource(src), mBuffer(buf) +OpenAL_Sound::OpenAL_Sound(OpenAL_Output &output, ALuint src, ALuint buf, const Ogre::Vector3& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) + : Sound(pos, vol, basevol, pitch, mindist, maxdist, flags) + , mOutput(output), mSource(src), mBuffer(buf) { mOutput.mActiveSounds.push_back(this); } @@ -484,10 +509,30 @@ double OpenAL_Sound::getTimeOffset() return t; } +void OpenAL_Sound::updateAll(bool local) +{ + alSourcef(mSource, AL_REFERENCE_DISTANCE, mMinDistance); + alSourcef(mSource, AL_MAX_DISTANCE, mMaxDistance); + if(local) + { + alSourcef(mSource, AL_ROLLOFF_FACTOR, 0.0f); + alSourcei(mSource, AL_SOURCE_RELATIVE, AL_TRUE); + } + else + { + alSourcef(mSource, AL_ROLLOFF_FACTOR, 1.0f); + alSourcei(mSource, AL_SOURCE_RELATIVE, AL_FALSE); + } + alSourcei(mSource, AL_LOOPING, (mFlags&MWBase::SoundManager::Play_Loop) ? AL_TRUE : AL_FALSE); + + update(); +} + void OpenAL_Sound::update() { ALfloat gain = mVolume*mBaseVolume; ALfloat pitch = mPitch; + if(!(mFlags&MWBase::SoundManager::Play_NoEnv) && mOutput.mLastEnvironment == Env_Underwater) { gain *= 0.9f; @@ -731,7 +776,7 @@ void OpenAL_Output::bufferFinished(ALuint buf) } -MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume, float pitch, int flags) +MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float vol, float basevol, float pitch, int flags) { boost::shared_ptr sound; ALuint src=0, buf=0; @@ -744,7 +789,7 @@ MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume try { buf = getBuffer(fname); - sound.reset(new OpenAL_Sound(*this, src, buf)); + sound.reset(new OpenAL_Sound(*this, src, buf, Ogre::Vector3(0.0f), vol, basevol, pitch, 1.0f, 1000.0f, flags)); } catch(std::exception &e) { @@ -755,25 +800,7 @@ MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume throw; } - alSource3f(src, AL_POSITION, 0.0f, 0.0f, 0.0f); - alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f); - alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f); - - alSourcef(src, AL_REFERENCE_DISTANCE, 1.0f); - alSourcef(src, AL_MAX_DISTANCE, 1000.0f); - alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f); - - if(!(flags&MWBase::SoundManager::Play_NoEnv) && mLastEnvironment == Env_Underwater) - { - volume *= 0.9f; - pitch *= 0.7f; - } - alSourcef(src, AL_GAIN, volume); - alSourcef(src, AL_PITCH, pitch); - - alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE); - alSourcei(src, AL_LOOPING, (flags&MWBase::SoundManager::Play_Loop) ? AL_TRUE : AL_FALSE); - throwALerror(); + sound->updateAll(true); alSourcei(src, AL_BUFFER, buf); alSourcePlay(src); @@ -782,8 +809,8 @@ MWBase::SoundPtr OpenAL_Output::playSound(const std::string &fname, float volume return sound; } -MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector3 &pos, float volume, float pitch, - float min, float max, int flags) +MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre::Vector3 &pos, float vol, float basevol, float pitch, + float min, float max, int flags) { boost::shared_ptr sound; ALuint src=0, buf=0; @@ -796,7 +823,7 @@ MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre try { buf = getBuffer(fname); - sound.reset(new OpenAL_Sound3D(*this, src, buf)); + sound.reset(new OpenAL_Sound3D(*this, src, buf, pos, vol, basevol, pitch, min, max, flags)); } catch(std::exception &e) { @@ -807,26 +834,7 @@ MWBase::SoundPtr OpenAL_Output::playSound3D(const std::string &fname, const Ogre throw; } - alSource3f(src, AL_POSITION, pos.x, pos.z, -pos.y); - alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f); - alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f); - - alSourcef(src, AL_REFERENCE_DISTANCE, min); - alSourcef(src, AL_MAX_DISTANCE, max); - alSourcef(src, AL_ROLLOFF_FACTOR, 1.0f); - - if(!(flags&MWBase::SoundManager::Play_NoEnv) && mLastEnvironment == Env_Underwater) - { - volume *= 0.9f; - pitch *= 0.7f; - } - alSourcef(src, AL_GAIN, (pos.squaredDistance(mPos) > max*max) ? - 0.0f : volume); - alSourcef(src, AL_PITCH, pitch); - - alSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE); - alSourcei(src, AL_LOOPING, (flags&MWBase::SoundManager::Play_Loop) ? AL_TRUE : AL_FALSE); - throwALerror(); + sound->updateAll(false); alSourcei(src, AL_BUFFER, buf); alSourcePlay(src); @@ -850,7 +858,7 @@ MWBase::SoundPtr OpenAL_Output::streamSound(DecoderPtr decoder, float volume, fl std::cout <<"Warning: cannot loop stream \""<getName()<<"\""<< std::endl; try { - sound.reset(new OpenAL_SoundStream(*this, src, decoder)); + sound.reset(new OpenAL_SoundStream(*this, src, decoder, volume, pitch, flags)); } catch(std::exception &e) { @@ -858,25 +866,7 @@ MWBase::SoundPtr OpenAL_Output::streamSound(DecoderPtr decoder, float volume, fl throw; } - alSource3f(src, AL_POSITION, 0.0f, 0.0f, 0.0f); - alSource3f(src, AL_DIRECTION, 0.0f, 0.0f, 0.0f); - alSource3f(src, AL_VELOCITY, 0.0f, 0.0f, 0.0f); - - alSourcef(src, AL_REFERENCE_DISTANCE, 1.0f); - alSourcef(src, AL_MAX_DISTANCE, 1000.0f); - alSourcef(src, AL_ROLLOFF_FACTOR, 0.0f); - - if(!(flags&MWBase::SoundManager::Play_NoEnv) && mLastEnvironment == Env_Underwater) - { - volume *= 0.9f; - pitch *= 0.7f; - } - alSourcef(src, AL_GAIN, volume); - alSourcef(src, AL_PITCH, pitch); - - alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE); - alSourcei(src, AL_LOOPING, AL_FALSE); - throwALerror(); + sound->updateAll(true); sound->play(); return sound; diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index 41ce56965..29d4102c4 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -48,9 +48,9 @@ namespace MWSound virtual void init(const std::string &devname=""); virtual void deinit(); - virtual MWBase::SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags); + virtual MWBase::SoundPtr playSound(const std::string &fname, float vol, float basevol, float pitch, int flags); virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, - float volume, float pitch, float min, float max, int flags); + float vol, float basevol, float pitch, float min, float max, int flags); virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags); virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env); diff --git a/apps/openmw/mwsound/sound.hpp b/apps/openmw/mwsound/sound.hpp index 1b6f50ff4..e76912d67 100644 --- a/apps/openmw/mwsound/sound.hpp +++ b/apps/openmw/mwsound/sound.hpp @@ -30,13 +30,14 @@ namespace MWSound void setPosition(const Ogre::Vector3 &pos) { mPos = pos; } void setVolume(float volume) { mVolume = volume; } - Sound() : mPos(0.0f, 0.0f, 0.0f) - , mVolume(1.0f) - , mBaseVolume(1.0f) - , mPitch(1.0f) - , mMinDistance(20.0f) /* 1 * min_range_scale */ - , mMaxDistance(12750.0f) /* 255 * max_range_scale */ - , mFlags(MWBase::SoundManager::Play_Normal) + Sound(const Ogre::Vector3& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) + : mPos(pos) + , mVolume(vol) + , mBaseVolume(basevol) + , mPitch(pitch) + , mMinDistance(mindist) + , mMaxDistance(maxdist) + , mFlags(flags) { } virtual ~Sound() { } diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index 1229f87d0..baed39da0 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -24,9 +24,9 @@ namespace MWSound virtual void init(const std::string &devname="") = 0; virtual void deinit() = 0; - virtual MWBase::SoundPtr playSound(const std::string &fname, float volume, float pitch, int flags) = 0; + virtual MWBase::SoundPtr playSound(const std::string &fname, float vol, float basevol, float pitch, int flags) = 0; virtual MWBase::SoundPtr playSound3D(const std::string &fname, const Ogre::Vector3 &pos, - float volume, float pitch, float min, float max, int flags) = 0; + float vol, float basevol, float pitch, float min, float max, int flags) = 0; virtual MWBase::SoundPtr streamSound(DecoderPtr decoder, float volume, float pitch, int flags) = 0; virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env) = 0; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 4ec3dfbb3..ff10e567a 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -221,11 +221,8 @@ namespace MWSound const ESM::Position &pos = ptr.getRefData().getPosition(); const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); - MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, basevol, 1.0f, - 20.0f, 12750.0f, Play_Normal); - sound->mPos = objpos; - sound->mBaseVolume = basevol; - + MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f, + 20.0f, 12750.0f, Play_Normal); mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound")); } catch(std::exception &e) @@ -243,9 +240,7 @@ namespace MWSound float basevol = mMasterVolume * mVoiceVolume; std::string filePath = "Sound/"+filename; - MWBase::SoundPtr sound = mOutput->playSound(filePath, basevol, 1.0f, Play_Normal); - sound->mBaseVolume = basevol; - + MWBase::SoundPtr sound = mOutput->playSound(filePath, 1.0f, basevol, 1.0f, Play_Normal); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), std::string("_say_sound")); } catch(std::exception &e) @@ -282,11 +277,7 @@ namespace MWSound return track; try { - float basevol = mMasterVolume; - - track = mOutput->streamSound(decoder, basevol, 1.0f, Play_NoEnv); - track->mBaseVolume = basevol; - track->mFlags = Play_NoEnv; + track = mOutput->streamSound(decoder, mMasterVolume, 1.0f, Play_NoEnv); } catch(std::exception &e) { @@ -307,14 +298,7 @@ namespace MWSound float min, max; std::string file = lookup(soundId, basevol, min, max); - sound = mOutput->playSound(file, volume*basevol, pitch, mode); - sound->mVolume = volume; - sound->mBaseVolume = basevol; - sound->mPitch = pitch; - sound->mMinDistance = min; - sound->mMaxDistance = max; - sound->mFlags = mode; - + sound = mOutput->playSound(file, volume, basevol, pitch, mode); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); } catch(std::exception &e) @@ -339,15 +323,7 @@ namespace MWSound const ESM::Position &pos = ptr.getRefData().getPosition();; const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); - sound = mOutput->playSound3D(file, objpos, volume*basevol, pitch, min, max, mode); - sound->mPos = objpos; - sound->mVolume = volume; - sound->mBaseVolume = basevol; - sound->mPitch = pitch; - sound->mMinDistance = min; - sound->mMaxDistance = max; - sound->mFlags = mode; - + sound = mOutput->playSound3D(file, objpos, volume, basevol, pitch, min, max, mode); if((mode&Play_NoTrack)) mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); else From 72ffceb2066ef343108676aa404499bb07944f50 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 18 Dec 2012 00:56:29 -0800 Subject: [PATCH 212/378] Add type flags to the sound play mode --- apps/openmw/mwbase/soundmanager.hpp | 8 ++++++- apps/openmw/mwsound/soundmanagerimp.cpp | 30 +++++++++---------------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 8d204bad4..de17f8dc8 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -39,9 +39,15 @@ namespace MWBase Play_Normal = 0, /* tracked, non-looping, multi-instance, environment */ Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */ Play_NoEnv = 1<<1, /* Do not apply environment effects (eg, underwater filters) */ - Play_NoTrack = 1<<2 /* (3D only) Play the sound at the given object's position + Play_NoTrack = 1<<2, /* (3D only) Play the sound at the given object's position * but do not keep it updated (the sound will not move with * the object and will not stop when the object is deleted. */ + + Play_TypeSfx = 0, /* Normal SFX sound */ + Play_TypeVoice = 1<<3, /* Voice sound */ + Play_TypeMusic = 1<<4, /* Music track */ + Play_TypeMovie = 1<<5, /* Movie audio track */ + Play_TypeMask = Play_TypeSfx|Play_TypeVoice|Play_TypeMusic|Play_TypeMovie }; private: diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index ff10e567a..64c3d9903 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -171,9 +171,7 @@ namespace MWSound DecoderPtr decoder = getDecoder(); decoder->open(filename); - mMusic = mOutput->streamSound(decoder, basevol, 1.0f, Play_NoEnv); - mMusic->mBaseVolume = basevol; - mMusic->mFlags = Play_NoEnv; + mMusic = mOutput->streamSound(decoder, basevol, 1.0f, Play_NoEnv|Play_TypeMusic); } catch(std::exception &e) { @@ -222,7 +220,7 @@ namespace MWSound const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f, - 20.0f, 12750.0f, Play_Normal); + 20.0f, 12750.0f, Play_Normal|Play_TypeVoice); mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound")); } catch(std::exception &e) @@ -240,7 +238,7 @@ namespace MWSound float basevol = mMasterVolume * mVoiceVolume; std::string filePath = "Sound/"+filename; - MWBase::SoundPtr sound = mOutput->playSound(filePath, 1.0f, basevol, 1.0f, Play_Normal); + MWBase::SoundPtr sound = mOutput->playSound(filePath, 1.0f, basevol, 1.0f, Play_Normal|Play_TypeVoice); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), std::string("_say_sound")); } catch(std::exception &e) @@ -277,7 +275,7 @@ namespace MWSound return track; try { - track = mOutput->streamSound(decoder, mMasterVolume, 1.0f, Play_NoEnv); + track = mOutput->streamSound(decoder, mMasterVolume, 1.0f, Play_NoEnv|Play_TypeMovie); } catch(std::exception &e) { @@ -296,7 +294,7 @@ namespace MWSound { float basevol = mMasterVolume * mSFXVolume; float min, max; - std::string file = lookup(soundId, basevol, min, max); + std::string file = lookup(soundId, volume, min, max); sound = mOutput->playSound(file, volume, basevol, pitch, mode); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); @@ -309,7 +307,7 @@ namespace MWSound } MWBase::SoundPtr SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId, - float volume, float pitch, int mode) + float volume, float pitch, int mode) { MWBase::SoundPtr sound; if(!mOutput->isInitialized()) @@ -319,7 +317,7 @@ namespace MWSound // Look up the sound in the ESM data float basevol = mMasterVolume * mSFXVolume; float min, max; - std::string file = lookup(soundId, basevol, min, max); + std::string file = lookup(soundId, volume, min, max); const ESM::Position &pos = ptr.getRefData().getPosition();; const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); @@ -538,18 +536,10 @@ namespace MWSound SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) { - if(snditer->second.second != "_say_sound") - { - float basevol = mMasterVolume * mSFXVolume; - float min, max; - lookup(snditer->second.second, basevol, min, max); - snditer->first->mBaseVolume = basevol; - } + if((snditer->first->mFlags&MWBase::SoundManager::Play_TypeVoice)) + snditer->first->mBaseVolume = mMasterVolume * mVoiceVolume; else - { - float basevol = mMasterVolume * mVoiceVolume; - snditer->first->mBaseVolume = basevol; - } + snditer->first->mBaseVolume = mMasterVolume * mSFXVolume; snditer->first->update(); snditer++; } From a5356e194efe2af54ea838ca42693eed99f497a5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 18 Dec 2012 01:35:20 -0800 Subject: [PATCH 213/378] Allow specifying a type for the playTrack method --- apps/openmw/mwbase/soundmanager.hpp | 9 +++++---- apps/openmw/mwrender/videoplayer.cpp | 2 +- apps/openmw/mwsound/soundmanagerimp.cpp | 4 ++-- apps/openmw/mwsound/soundmanagerimp.hpp | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index de17f8dc8..a47b9a6ca 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -34,15 +34,16 @@ namespace MWBase class SoundManager { public: - + /* These must all fit together */ enum PlayMode { Play_Normal = 0, /* tracked, non-looping, multi-instance, environment */ Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */ Play_NoEnv = 1<<1, /* Do not apply environment effects (eg, underwater filters) */ - Play_NoTrack = 1<<2, /* (3D only) Play the sound at the given object's position + Play_NoTrack = 1<<2 /* (3D only) Play the sound at the given object's position * but do not keep it updated (the sound will not move with * the object and will not stop when the object is deleted. */ - + }; + enum PlayType { Play_TypeSfx = 0, /* Normal SFX sound */ Play_TypeVoice = 1<<3, /* Voice sound */ Play_TypeMusic = 1<<4, /* Music track */ @@ -97,7 +98,7 @@ namespace MWBase virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()) = 0; ///< Stop an actor speaking - virtual SoundPtr playTrack(const MWSound::DecoderPtr& decoder) = 0; + virtual SoundPtr playTrack(const MWSound::DecoderPtr& decoder, PlayType type) = 0; ///< Play a 2D audio track, using a custom decoder virtual SoundPtr playSound(const std::string& soundId, float volume, float pitch, diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 9c72e9919..e5dee76af 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -869,7 +869,7 @@ int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) this->audio_st = pFormatCtx->streams + stream_index; decoder.reset(new MovieAudioDecoder(this)); - this->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); + this->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder, MWBase::SoundManager::Play_TypeMovie); if(!this->AudioTrack) { avcodec_close((*this->audio_st)->codec); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 64c3d9903..2c4e8eff0 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -268,14 +268,14 @@ namespace MWSound } - MWBase::SoundPtr SoundManager::playTrack(const DecoderPtr& decoder) + MWBase::SoundPtr SoundManager::playTrack(const DecoderPtr& decoder, PlayType type) { MWBase::SoundPtr track; if(!mOutput->isInitialized()) return track; try { - track = mOutput->streamSound(decoder, mMasterVolume, 1.0f, Play_NoEnv|Play_TypeMovie); + track = mOutput->streamSound(decoder, mMasterVolume, 1.0f, Play_NoEnv|type); } catch(std::exception &e) { diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 26ff309fb..879110b9f 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -103,7 +103,7 @@ namespace MWSound virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()); ///< Stop an actor speaking - virtual MWBase::SoundPtr playTrack(const DecoderPtr& decoder); + virtual MWBase::SoundPtr playTrack(const DecoderPtr& decoder, PlayType type); ///< Play a 2D audio track, using a custom decoder virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal); From b4e36d4f3104f8eb873c0bc63372cf6a45d9d70a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 18 Dec 2012 02:01:04 -0800 Subject: [PATCH 214/378] Add a method to get the volume from the sound type --- apps/openmw/mwsound/sound.hpp | 4 +++ apps/openmw/mwsound/soundmanagerimp.cpp | 42 ++++++++++++++++++------- apps/openmw/mwsound/soundmanagerimp.hpp | 2 ++ 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwsound/sound.hpp b/apps/openmw/mwsound/sound.hpp index e76912d67..8deaf26a6 100644 --- a/apps/openmw/mwsound/sound.hpp +++ b/apps/openmw/mwsound/sound.hpp @@ -30,6 +30,10 @@ namespace MWSound void setPosition(const Ogre::Vector3 &pos) { mPos = pos; } void setVolume(float volume) { mVolume = volume; } + MWBase::SoundManager::PlayType getPlayType() const + { return (MWBase::SoundManager::PlayType)(mFlags&MWBase::SoundManager::Play_TypeMask); } + + Sound(const Ogre::Vector3& pos, float vol, float basevol, float pitch, float mindist, float maxdist, int flags) : mPos(pos) , mVolume(vol) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 2c4e8eff0..34524ba9a 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -137,6 +137,27 @@ namespace MWSound return "Sound/"+snd->mSound; } + // Gets the combined volume settings for the given sound type + float SoundManager::volumeFromType(PlayType type) const + { + float volume = mMasterVolume; + switch(type) + { + case Play_TypeSfx: + volume *= mSFXVolume; + break; + case Play_TypeVoice: + volume *= mVoiceVolume; + break; + case Play_TypeMusic: + case Play_TypeMovie: + volume *= mMusicVolume; + break; + case Play_TypeMask: + break; + } + return volume; + } bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const { @@ -165,13 +186,13 @@ namespace MWSound std::cout <<"Playing "<open(filename); - mMusic = mOutput->streamSound(decoder, basevol, 1.0f, Play_NoEnv|Play_TypeMusic); + mMusic = mOutput->streamSound(decoder, volumeFromType(Play_TypeMusic), + 1.0f, Play_NoEnv|Play_TypeMusic); } catch(std::exception &e) { @@ -214,7 +235,7 @@ namespace MWSound try { // The range values are not tested - float basevol = mMasterVolume * mVoiceVolume; + float basevol = volumeFromType(Play_TypeVoice); std::string filePath = "Sound/"+filename; const ESM::Position &pos = ptr.getRefData().getPosition(); const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); @@ -235,7 +256,7 @@ namespace MWSound return; try { - float basevol = mMasterVolume * mVoiceVolume; + float basevol = volumeFromType(Play_TypeVoice); std::string filePath = "Sound/"+filename; MWBase::SoundPtr sound = mOutput->playSound(filePath, 1.0f, basevol, 1.0f, Play_Normal|Play_TypeVoice); @@ -275,7 +296,7 @@ namespace MWSound return track; try { - track = mOutput->streamSound(decoder, mMasterVolume, 1.0f, Play_NoEnv|type); + track = mOutput->streamSound(decoder, volumeFromType(type), 1.0f, Play_NoEnv|type); } catch(std::exception &e) { @@ -292,7 +313,7 @@ namespace MWSound return sound; try { - float basevol = mMasterVolume * mSFXVolume; + float basevol = volumeFromType((PlayType)(mode&Play_TypeMask)); float min, max; std::string file = lookup(soundId, volume, min, max); @@ -315,7 +336,7 @@ namespace MWSound try { // Look up the sound in the ESM data - float basevol = mMasterVolume * mSFXVolume; + float basevol = volumeFromType((PlayType)(mode&Play_TypeMask)); float min, max; std::string file = lookup(soundId, volume, min, max); const ESM::Position &pos = ptr.getRefData().getPosition();; @@ -536,16 +557,13 @@ namespace MWSound SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) { - if((snditer->first->mFlags&MWBase::SoundManager::Play_TypeVoice)) - snditer->first->mBaseVolume = mMasterVolume * mVoiceVolume; - else - snditer->first->mBaseVolume = mMasterVolume * mSFXVolume; + snditer->first->mBaseVolume = volumeFromType(snditer->first->getPlayType()); snditer->first->update(); snditer++; } if(mMusic) { - mMusic->mBaseVolume = mMasterVolume * mMusicVolume; + mMusic->mBaseVolume = volumeFromType(mMusic->getPlayType()); mMusic->update(); } } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 879110b9f..f9ceeded6 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -59,6 +59,8 @@ namespace MWSound void updateSounds(float duration); void updateRegionSound(float duration); + float volumeFromType(PlayType type) const; + SoundManager(const SoundManager &rhs); SoundManager& operator=(const SoundManager &rhs); From 1cf019a0070c718976005000af0b50a9ba229af1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 18 Dec 2012 12:36:26 +0100 Subject: [PATCH 215/378] post merge fix --- apps/esmtool/record.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index b5f97e979..a732f1938 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -738,7 +738,6 @@ void Record::print() default: std::cout << "unknown type"; } - std::cout << "\n Dirty: " << mData.mDirty << std::endl; } template<> From 2f8daec379644128e0bec352376029b1f23f26b3 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 18 Dec 2012 04:19:35 -0800 Subject: [PATCH 216/378] Allow pausing only certain types of sounds --- apps/openmw/mwbase/soundmanager.hpp | 17 +++++++++++------ apps/openmw/mwrender/videoplayer.cpp | 4 ++-- apps/openmw/mwsound/openal_output.cpp | 12 ++++++------ apps/openmw/mwsound/openal_output.hpp | 4 ++-- apps/openmw/mwsound/sound_output.hpp | 4 ++-- apps/openmw/mwsound/soundmanagerimp.cpp | 16 ++++++++-------- apps/openmw/mwsound/soundmanagerimp.hpp | 4 ++-- 7 files changed, 33 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index a47b9a6ca..d804830fa 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -44,10 +44,10 @@ namespace MWBase * the object and will not stop when the object is deleted. */ }; enum PlayType { - Play_TypeSfx = 0, /* Normal SFX sound */ - Play_TypeVoice = 1<<3, /* Voice sound */ - Play_TypeMusic = 1<<4, /* Music track */ - Play_TypeMovie = 1<<5, /* Movie audio track */ + Play_TypeSfx = 1<<3, /* Normal SFX sound */ + Play_TypeVoice = 1<<4, /* Voice sound */ + Play_TypeMusic = 1<<5, /* Music track */ + Play_TypeMovie = 1<<6, /* Movie audio track */ Play_TypeMask = Play_TypeSfx|Play_TypeVoice|Play_TypeMusic|Play_TypeMovie }; @@ -124,10 +124,10 @@ namespace MWBase virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const = 0; ///< Is the given sound currently playing on the given object? - virtual void pauseAllSounds() = 0; + virtual void pauseSounds(int types=Play_TypeMask) = 0; ///< Pauses all currently playing sounds, including music. - virtual void resumeAllSounds() = 0; + virtual void resumeSounds(int types=Play_TypeMask) = 0; ///< Resumes all previously paused sounds. virtual void update(float duration) = 0; @@ -139,6 +139,11 @@ namespace MWBase { return static_cast (a) | static_cast (b); } inline int operator&(SoundManager::PlayMode a, SoundManager::PlayMode b) { return static_cast (a) & static_cast (b); } + + inline int operator|(SoundManager::PlayType a, SoundManager::PlayType b) + { return static_cast (a) | static_cast (b); } + inline int operator&(SoundManager::PlayType a, SoundManager::PlayType b) + { return static_cast (a) & static_cast (b); } } #endif diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index e5dee76af..8e05a4f82 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -1092,7 +1092,7 @@ void VideoPlayer::playVideo(const std::string &resourceName) } mSceneMgr->setSpecialCaseRenderQueueMode(Ogre::SceneManager::SCRQM_EXCLUDE); - MWBase::Environment::get().getSoundManager()->pauseAllSounds(); + MWBase::Environment::get().getSoundManager()->pauseSounds(); try { mState = new VideoState; @@ -1123,7 +1123,7 @@ void VideoPlayer::close() mState = NULL; } - MWBase::Environment::get().getSoundManager()->resumeAllSounds(); + MWBase::Environment::get().getSoundManager()->resumeSounds(); mRectangle->setVisible(false); mBackgroundRectangle->setVisible(false); diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 824a72299..406c1bcca 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -891,7 +891,7 @@ void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 } -void OpenAL_Output::pauseAllSounds() +void OpenAL_Output::pauseSounds(int types) { IDVec sources; SoundVec::const_iterator iter = mActiveSounds.begin(); @@ -900,13 +900,13 @@ void OpenAL_Output::pauseAllSounds() const OpenAL_SoundStream *stream = dynamic_cast(*iter); if(stream) { - if(stream->mSource) + if(stream->mSource && (stream->getPlayType()&types)) sources.push_back(stream->mSource); } else { const OpenAL_Sound *sound = dynamic_cast(*iter); - if(sound && sound->mSource) + if(sound && sound->mSource && (sound->getPlayType()&types)) sources.push_back(sound->mSource); } iter++; @@ -915,7 +915,7 @@ void OpenAL_Output::pauseAllSounds() alSourcePausev(sources.size(), &sources[0]); } -void OpenAL_Output::resumeAllSounds() +void OpenAL_Output::resumeSounds(int types) { IDVec sources; SoundVec::const_iterator iter = mActiveSounds.begin(); @@ -924,13 +924,13 @@ void OpenAL_Output::resumeAllSounds() const OpenAL_SoundStream *stream = dynamic_cast(*iter); if(stream) { - if(stream->mSource) + if(stream->mSource && (stream->getPlayType()&types)) sources.push_back(stream->mSource); } else { const OpenAL_Sound *sound = dynamic_cast(*iter); - if(sound && sound->mSource) + if(sound && sound->mSource && (sound->getPlayType()&types)) sources.push_back(sound->mSource); } iter++; diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index 29d4102c4..ce35f4e68 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -55,8 +55,8 @@ namespace MWSound virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env); - virtual void pauseAllSounds(); - virtual void resumeAllSounds(); + virtual void pauseSounds(int types); + virtual void resumeSounds(int types); OpenAL_Output& operator=(const OpenAL_Output &rhs); OpenAL_Output(const OpenAL_Output &rhs); diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index baed39da0..b5ccc946a 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -31,8 +31,8 @@ namespace MWSound virtual void updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 &atdir, const Ogre::Vector3 &updir, Environment env) = 0; - virtual void pauseAllSounds() = 0; - virtual void resumeAllSounds() = 0; + virtual void pauseSounds(int types) = 0; + virtual void resumeSounds(int types) = 0; Sound_Output& operator=(const Sound_Output &rhs); Sound_Output(const Sound_Output &rhs); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 34524ba9a..165d501a2 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -313,11 +313,11 @@ namespace MWSound return sound; try { - float basevol = volumeFromType((PlayType)(mode&Play_TypeMask)); + float basevol = volumeFromType(Play_TypeSfx); float min, max; std::string file = lookup(soundId, volume, min, max); - sound = mOutput->playSound(file, volume, basevol, pitch, mode); + sound = mOutput->playSound(file, volume, basevol, pitch, mode|Play_TypeSfx); mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); } catch(std::exception &e) @@ -336,13 +336,13 @@ namespace MWSound try { // Look up the sound in the ESM data - float basevol = volumeFromType((PlayType)(mode&Play_TypeMask)); + float basevol = volumeFromType(Play_TypeSfx); float min, max; std::string file = lookup(soundId, volume, min, max); const ESM::Position &pos = ptr.getRefData().getPosition();; const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]); - sound = mOutput->playSound3D(file, objpos, volume, basevol, pitch, min, max, mode); + sound = mOutput->playSound3D(file, objpos, volume, basevol, pitch, min, max, mode|Play_TypeSfx); if((mode&Play_NoTrack)) mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId); else @@ -423,16 +423,16 @@ namespace MWSound } - void SoundManager::pauseAllSounds() + void SoundManager::pauseSounds(int types) { if(mOutput->isInitialized()) - mOutput->pauseAllSounds(); + mOutput->pauseSounds(types); } - void SoundManager::resumeAllSounds() + void SoundManager::resumeSounds(int types) { if(mOutput->isInitialized()) - mOutput->resumeAllSounds(); + mOutput->resumeSounds(types); } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index f9ceeded6..14b8e5cef 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -130,10 +130,10 @@ namespace MWSound virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? - virtual void pauseAllSounds(); + virtual void pauseSounds(int types=Play_TypeMask); ///< Pauses all currently playing sounds, including music. - virtual void resumeAllSounds(); + virtual void resumeSounds(int types=Play_TypeMask); ///< Resumes all previously paused sounds. virtual void update(float duration); From fe36cc1de76724ed540ce16654ad7904c7c43961 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 18 Dec 2012 04:35:24 -0800 Subject: [PATCH 217/378] Don't try to resume sound types that aren't paused --- apps/openmw/mwsound/soundmanagerimp.cpp | 9 +++++++++ apps/openmw/mwsound/soundmanagerimp.hpp | 2 ++ 2 files changed, 11 insertions(+) diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 165d501a2..d801e6618 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -52,6 +52,7 @@ namespace MWSound , mMusicVolume(1.0f) , mFootstepsVolume(1.0f) , mVoiceVolume(1.0f) + , mPausedSoundTypes(0) { if(!useSound) return; @@ -426,13 +427,21 @@ namespace MWSound void SoundManager::pauseSounds(int types) { if(mOutput->isInitialized()) + { + types &= Play_TypeMask; mOutput->pauseSounds(types); + mPausedSoundTypes |= types; + } } void SoundManager::resumeSounds(int types) { if(mOutput->isInitialized()) + { + types &= types&Play_TypeMask&mPausedSoundTypes; mOutput->resumeSounds(types); + mPausedSoundTypes &= ~types; + } } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 14b8e5cef..d26f51ef0 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -52,6 +52,8 @@ namespace MWSound Ogre::Vector3 mListenerDir; Ogre::Vector3 mListenerUp; + int mPausedSoundTypes; + std::string lookup(const std::string &soundId, float &volume, float &min, float &max); void streamMusicFull(const std::string& filename); From 3b7edae7c3858d83b548c0c140b847d596bb45f5 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 18 Dec 2012 05:19:32 -0800 Subject: [PATCH 218/378] Don't hold a list of all sound sources --- apps/openmw/mwsound/openal_output.cpp | 19 +++++++++++-------- apps/openmw/mwsound/openal_output.hpp | 3 --- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 406c1bcca..67008e2bc 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -637,9 +637,8 @@ void OpenAL_Output::init(const std::string &devname) ALuint src = 0; alGenSources(1, &src); throwALerror(); - mSources.push_back(src); + mFreeSources.push_back(src); } - mFreeSources.insert(mFreeSources.begin(), mSources.begin(), mSources.end()); } catch(std::exception &e) { @@ -655,11 +654,9 @@ void OpenAL_Output::deinit() { mStreamThread->removeAll(); - std::cerr<< "There are "< 0) - alDeleteSources(mSources.size(), &mSources[0]); - mSources.clear(); mBufferRefs.clear(); mUnusedBuffers.clear(); @@ -893,7 +890,7 @@ void OpenAL_Output::updateListener(const Ogre::Vector3 &pos, const Ogre::Vector3 void OpenAL_Output::pauseSounds(int types) { - IDVec sources; + std::vector sources; SoundVec::const_iterator iter = mActiveSounds.begin(); while(iter != mActiveSounds.end()) { @@ -912,12 +909,15 @@ void OpenAL_Output::pauseSounds(int types) iter++; } if(sources.size() > 0) + { alSourcePausev(sources.size(), &sources[0]); + throwALerror(); + } } void OpenAL_Output::resumeSounds(int types) { - IDVec sources; + std::vector sources; SoundVec::const_iterator iter = mActiveSounds.begin(); while(iter != mActiveSounds.end()) { @@ -936,7 +936,10 @@ void OpenAL_Output::resumeSounds(int types) iter++; } if(sources.size() > 0) + { alSourcePlayv(sources.size(), &sources[0]); + throwALerror(); + } } diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index ce35f4e68..e240a8b01 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -21,9 +21,6 @@ namespace MWSound ALCdevice *mDevice; ALCcontext *mContext; - typedef std::vector IDVec; - IDVec mSources; - typedef std::deque IDDq; IDDq mFreeSources; IDDq mUnusedBuffers; From 7b2c3e6cd37f597777734a9ddaca20040079d47d Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 18 Dec 2012 06:01:21 -0800 Subject: [PATCH 219/378] Pass a proper PlayMode enum to playSound and playSound3D --- apps/openmw/mwbase/soundmanager.hpp | 14 ++------------ apps/openmw/mwscript/soundextensions.cpp | 6 ++++-- apps/openmw/mwsound/soundmanagerimp.cpp | 4 ++-- apps/openmw/mwsound/soundmanagerimp.hpp | 4 ++-- apps/openmw/mwworld/weather.cpp | 4 ++-- 5 files changed, 12 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index d804830fa..0c706ab49 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -102,11 +102,11 @@ namespace MWBase ///< Play a 2D audio track, using a custom decoder virtual SoundPtr playSound(const std::string& soundId, float volume, float pitch, - int mode=Play_Normal) = 0; + PlayMode mode=Play_Normal) = 0; ///< Play a sound, independently of 3D-position virtual SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId, - float volume, float pitch, int mode=Play_Normal) = 0; + float volume, float pitch, PlayMode mode=Play_Normal) = 0; ///< Play a sound from an object virtual void stopSound3D(MWWorld::Ptr reference, const std::string& soundId) = 0; @@ -134,16 +134,6 @@ namespace MWBase virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) = 0; }; - - inline int operator|(SoundManager::PlayMode a, SoundManager::PlayMode b) - { return static_cast (a) | static_cast (b); } - inline int operator&(SoundManager::PlayMode a, SoundManager::PlayMode b) - { return static_cast (a) & static_cast (b); } - - inline int operator|(SoundManager::PlayType a, SoundManager::PlayType b) - { return static_cast (a) | static_cast (b); } - inline int operator&(SoundManager::PlayType a, SoundManager::PlayType b) - { return static_cast (a) & static_cast (b); } } #endif diff --git a/apps/openmw/mwscript/soundextensions.cpp b/apps/openmw/mwscript/soundextensions.cpp index 35e66241e..408a6f29d 100644 --- a/apps/openmw/mwscript/soundextensions.cpp +++ b/apps/openmw/mwscript/soundextensions.cpp @@ -118,7 +118,8 @@ namespace MWScript std::string sound = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, 1.0, 1.0, mLoop ? MWBase::SoundManager::Play_Loop : 0); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, 1.0, 1.0, mLoop ? MWBase::SoundManager::Play_Loop : + MWBase::SoundManager::Play_Normal); } }; @@ -144,7 +145,8 @@ namespace MWScript Interpreter::Type_Float pitch = runtime[0].mFloat; runtime.pop(); - MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, volume, pitch, mLoop ? MWBase::SoundManager::Play_Loop : 0); + MWBase::Environment::get().getSoundManager()->playSound3D (ptr, sound, volume, pitch, mLoop ? MWBase::SoundManager::Play_Loop : + MWBase::SoundManager::Play_Normal); } }; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index d801e6618..8a69fc96b 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -307,7 +307,7 @@ namespace MWSound } - MWBase::SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, int mode) + MWBase::SoundPtr SoundManager::playSound(const std::string& soundId, float volume, float pitch, PlayMode mode) { MWBase::SoundPtr sound; if(!mOutput->isInitialized()) @@ -329,7 +329,7 @@ namespace MWSound } MWBase::SoundPtr SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId, - float volume, float pitch, int mode) + float volume, float pitch, PlayMode mode) { MWBase::SoundPtr sound; if(!mOutput->isInitialized()) diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index d26f51ef0..b475449d9 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -110,11 +110,11 @@ namespace MWSound virtual MWBase::SoundPtr playTrack(const DecoderPtr& decoder, PlayType type); ///< Play a 2D audio track, using a custom decoder - virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, int mode=Play_Normal); + virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, PlayMode mode=Play_Normal); ///< Play a sound, independently of 3D-position virtual MWBase::SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId, - float volume, float pitch, int mode=Play_Normal); + float volume, float pitch, PlayMode mode=Play_Normal); ///< Play a sound from an object virtual void stopSound3D(MWWorld::Ptr reference, const std::string& soundId); diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 009b325c0..19bc881f7 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -738,7 +738,7 @@ void WeatherManager::update(float duration) if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), ambientSnd) == mSoundsPlaying.end()) { mSoundsPlaying.push_back(ambientSnd); - MWBase::Environment::get().getSoundManager()->playSound(ambientSnd, 1.0, 1.0, true); + MWBase::Environment::get().getSoundManager()->playSound(ambientSnd, 1.0, 1.0, MWBase::SoundManager::Play_Loop); } } @@ -749,7 +749,7 @@ void WeatherManager::update(float duration) if (std::find(mSoundsPlaying.begin(), mSoundsPlaying.end(), rainSnd) == mSoundsPlaying.end()) { mSoundsPlaying.push_back(rainSnd); - MWBase::Environment::get().getSoundManager()->playSound(rainSnd, 1.0, 1.0, true); + MWBase::Environment::get().getSoundManager()->playSound(rainSnd, 1.0, 1.0, MWBase::SoundManager::Play_Loop); } } From 1c73a3f2fb967d1edb324668c029746efe8c3a30 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 18 Dec 2012 19:09:08 +0100 Subject: [PATCH 220/378] Revert "remove commandline switch for new game" This reverts commit 86671096ec89960405726594a50e5a7fa787b075. --- apps/openmw/engine.cpp | 10 ++++++++-- apps/openmw/engine.hpp | 4 ++++ apps/openmw/main.cpp | 4 ++++ apps/openmw/mwbase/windowmanager.hpp | 2 -- apps/openmw/mwgui/mainmenu.cpp | 1 - apps/openmw/mwgui/windowmanagerimp.cpp | 12 +++--------- apps/openmw/mwgui/windowmanagerimp.hpp | 4 +--- apps/openmw/mwworld/worldimp.cpp | 8 +++++++- apps/openmw/mwworld/worldimp.hpp | 2 +- 9 files changed, 28 insertions(+), 19 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 08e24b5d6..2299053cd 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -125,6 +125,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mFpsLevel(0) , mDebug (false) , mVerboseScripts (false) + , mNewGame (false) , mUseSound (true) , mCompileAll (false) , mScriptContext (0) @@ -236,6 +237,11 @@ void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity) mVerboseScripts = scriptsVerbosity; } +void OMW::Engine::setNewGame(bool newGame) +{ + mNewGame = newGame; +} + // Initialise and enter main loop. void OMW::Engine::go() @@ -326,13 +332,13 @@ void OMW::Engine::go() // Create the world mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster, - mResDir, mCfgMgr.getCachePath(), mEncoding, mFallbackMap)); + mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoding, mFallbackMap)); // Create window manager - this manages all the MW-specific GUI windows MWScript::registerExtensions (mExtensions); mEnvironment.setWindowManager (new MWGui::WindowManager( - mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), + mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), mCfgMgr.getCachePath ().string(), mScriptConsoleMode)); // Create sound system diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index b13ec4368..57402c91e 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -68,6 +68,7 @@ namespace OMW int mFpsLevel; bool mDebug; bool mVerboseScripts; + bool mNewGame; bool mUseSound; bool mCompileAll; std::string mFocusName; @@ -137,6 +138,9 @@ namespace OMW /// Disable or enable all sounds void setSoundUsage(bool soundUsage); + /// Start as a new game. + void setNewGame(bool newGame); + /// Initialise and enter main loop. void go(); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 6b31f3ade..0563fdbbb 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -133,6 +133,9 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("script-run", bpo::value()->default_value(""), "select a file containing a list of console commands that is executed on startup") + ("new-game", bpo::value()->implicit_value(true) + ->default_value(false), "activate char gen/new game mechanics") + ("fs-strict", bpo::value()->implicit_value(true) ->default_value(false), "strict file system handling (no case folding)") @@ -235,6 +238,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat // startup-settings engine.setCell(variables["start"].as()); + engine.setNewGame(variables["new-game"].as()); // other settings engine.setDebugMode(variables["debug"].as()); diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 9df75870d..c177912d4 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -79,8 +79,6 @@ namespace MWBase */ virtual void update() = 0; - virtual void newGame() = 0; - virtual void pushGuiMode (MWGui::GuiMode mode) = 0; virtual void popGuiMode() = 0; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 0f74f6e14..b19f3de6d 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -94,7 +94,6 @@ namespace MWGui void MainMenu::newGameConfirmed() { MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); - MWBase::Environment::get().getWindowManager ()->newGame (); MWBase::Environment::get().getWorld ()->newGame(); } } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 1f5966f72..627f8f339 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -54,7 +54,7 @@ using namespace MWGui; WindowManager::WindowManager( - const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *mOgre, + const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts) : mGuiManager(NULL) , mHud(NULL) @@ -95,8 +95,8 @@ WindowManager::WindowManager( , mGui(NULL) , mGarbageDialogs() , mShown(GW_ALL) - , mAllowed(GW_ALL) - , mRestAllowed(true) + , mAllowed(newGame ? GW_None : GW_ALL) + , mRestAllowed(newGame ? false : true) , mShowFPSLevel(fpsLevel) , mFPS(0.0f) , mTriangleCount(0) @@ -1041,9 +1041,3 @@ void WindowManager::startTraining(MWWorld::Ptr actor) { mTrainingWindow->startTraining(actor); } - -void WindowManager::newGame () -{ - mAllowed = GW_None; - mRestAllowed = false; -} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index db5c22348..2e684b5da 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -74,7 +74,7 @@ namespace MWGui typedef std::pair Faction; typedef std::vector FactionList; - WindowManager(const Compiler::Extensions& extensions, int fpsLevel, + WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts); virtual ~WindowManager(); @@ -86,8 +86,6 @@ namespace MWGui */ virtual void update(); - virtual void newGame(); - virtual void pushGuiMode(GuiMode mode); virtual void popGuiMode(); virtual void removeGuiMode(GuiMode mode); ///< can be anywhere in the stack diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index afcdb8d39..f1fb67632 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -167,7 +167,7 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, + const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, const std::string& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mCells (mStore, mEsm), mNewGameStarted(false), @@ -197,6 +197,12 @@ namespace MWWorld // global variables mGlobalVariables = new Globals (mStore); + if (newGame) + { + // set new game mark + mGlobalVariables->setInt ("chargenstate", 1); + } + mGlobalVariables->setInt ("pcrace", 3); mWorldScene = new Scene(*mRendering, mPhysics); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 8f89ecff3..a58e70d12 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -95,7 +95,7 @@ namespace MWWorld World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, + const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, const std::string& encoding, std::map fallbackMap); virtual ~World(); From 64210e6efaf5e536e353d5357900252fab437a34 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 18 Dec 2012 19:09:27 +0100 Subject: [PATCH 221/378] Revert "New Game button" This reverts commit c5dd0e19688b388b6e4e978d5ecf07ec14f5085d. --- apps/openmw/mwbase/world.hpp | 2 -- apps/openmw/mwgui/mainmenu.cpp | 25 +++++-------------------- apps/openmw/mwgui/mainmenu.hpp | 8 +------- apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 15 +-------------- apps/openmw/mwworld/worldimp.hpp | 3 --- 6 files changed, 8 insertions(+), 47 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3c707d196..198a20d45 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -82,8 +82,6 @@ namespace MWBase virtual OEngine::Render::Fader* getFader() = 0; ///< \ŧodo remove this function. Rendering details should not be exposed. - virtual void newGame() = 0; - virtual MWWorld::CellStore *getExterior (int x, int y) = 0; virtual MWWorld::CellStore *getInterior (const std::string& name) = 0; diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index b19f3de6d..e98b75e9b 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -6,15 +6,12 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" -#include "confirmationdialog.hpp" - namespace MWGui { - MainMenu::MainMenu(MWBase::WindowManager& parWindowManager, int w, int h) + MainMenu::MainMenu(int w, int h) : OEngine::GUI::Layout("openmw_mainmenu.layout") , mButtonBox(0) - , mDialog(parWindowManager) { onResChange(w,h); } @@ -23,7 +20,7 @@ namespace MWGui { setCoord(0,0,w,h); - int height = 64 * 4; + int height = 64 * 3; if (mButtonBox) MyGUI::Gui::getInstance ().destroyWidget(mButtonBox); @@ -36,11 +33,12 @@ namespace MWGui mReturn->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::returnToGame); curH += 64; + + /* mNewGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); - mNewGame->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::newGame); mNewGame->setImageResource ("Menu_NewGame"); curH += 64; -/* + mLoadGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); mLoadGame->setImageResource ("Menu_LoadGame"); curH += 64; @@ -83,17 +81,4 @@ namespace MWGui Ogre::Root::getSingleton ().queueEndRendering (); } - void MainMenu::newGame(MyGUI::Widget* sender) - { - mDialog.open ("#{sNotifyMessage54}"); - mDialog.eventOkClicked.clear(); - mDialog.eventCancelClicked.clear(); - mDialog.eventOkClicked += MyGUI::newDelegate(this, &MainMenu::newGameConfirmed); - } - - void MainMenu::newGameConfirmed() - { - MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); - MWBase::Environment::get().getWorld ()->newGame(); - } } diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index ab48f29d9..fd583d187 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -1,14 +1,12 @@ #include -#include "confirmationdialog.hpp" - namespace MWGui { class MainMenu : public OEngine::GUI::Layout { public: - MainMenu(MWBase::WindowManager& parWindowManager, int w, int h); + MainMenu(int w, int h); void onResChange(int w, int h); @@ -26,10 +24,6 @@ namespace MWGui void returnToGame(MyGUI::Widget* sender); void showOptions(MyGUI::Widget* sender); void exitGame(MyGUI::Widget* sender); - void newGame(MyGUI::Widget* sender); - void newGameConfirmed(); - - ConfirmationDialog mDialog; }; } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 627f8f339..373546aa8 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -139,7 +139,7 @@ WindowManager::WindowManager( mDragAndDrop->mDraggedWidget = 0; mDragAndDrop->mDragAndDropWidget = dragAndDropWidget; - mMenu = new MainMenu(*this, w,h); + mMenu = new MainMenu(w,h); mMap = new MapWindow(*this, cacheDir); mStatsWindow = new StatsWindow(*this); mConsole = new Console(w,h, consoleOnlyScripts); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f1fb67632..8eb121d37 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -170,7 +170,7 @@ namespace MWWorld const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, const std::string& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), - mSky (true), mCells (mStore, mEsm), mNewGameStarted(false), + mSky (true), mCells (mStore, mEsm), mNumFacing(0) { mPhysics = new PhysicsSystem(renderer); @@ -1015,12 +1015,6 @@ namespace MWWorld } } } - - if (mNewGameStarted) - { - playVideo ("mw_intro.bik"); - mNewGameStarted = false; - } } bool World::isCellExterior() const @@ -1302,11 +1296,4 @@ namespace MWWorld { mRendering->stopVideo(); } - - void World::newGame () - { - // set new game mark - mGlobalVariables->setInt ("chargenstate", 1); - mNewGameStarted = true; // in order to play the intro video at the end of the next frame - } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index a58e70d12..1c1352913 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -60,7 +60,6 @@ namespace MWWorld MWWorld::Globals *mGlobalVariables; MWWorld::PhysicsSystem *mPhysics; bool mSky; - bool mNewGameStarted; Cells mCells; @@ -103,8 +102,6 @@ namespace MWWorld virtual OEngine::Render::Fader* getFader(); ///< \ŧodo remove this function. Rendering details should not be exposed. - virtual void newGame(); - virtual CellStore *getExterior (int x, int y); virtual CellStore *getInterior (const std::string& name); From 85850c7440d54d612c9ab8fd9026a1ae5c06f095 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 18 Dec 2012 10:22:40 -0800 Subject: [PATCH 222/378] Fix DEFAULT_OUTPUT declaration --- apps/openmw/mwsound/openal_output.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index e240a8b01..02706b50c 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -70,7 +70,7 @@ namespace MWSound friend class SoundManager; }; #ifndef DEFAULT_OUTPUT -#define DEFAULT_OUTPUT (::MWSound::OpenAL_Output) +#define DEFAULT_OUTPUT(x) ::MWSound::OpenAL_Output((x)) #endif } From f2c6907244a25949094dfe67529ec53992e44a91 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Thu, 20 Dec 2012 23:16:34 +0000 Subject: [PATCH 223/378] Added in text escape sequences for dialogue, messageboxes and books. builtins are placeholders, global variables work --- apps/openmw/mwbase/world.hpp | 2 + apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 12 +- apps/openmw/mwgui/formatting.cpp | 12 ++ apps/openmw/mwscript/interpretercontext.cpp | 14 ++ apps/openmw/mwscript/interpretercontext.hpp | 4 + apps/openmw/mwworld/globals.cpp | 11 + apps/openmw/mwworld/globals.hpp | 3 + apps/openmw/mwworld/worldimp.cpp | 5 + apps/openmw/mwworld/worldimp.hpp | 2 + components/CMakeLists.txt | 2 +- components/interpreter/context.hpp | 4 + components/interpreter/defines.cpp | 200 ++++++++++++++++++ components/interpreter/defines.hpp | 12 ++ components/interpreter/miscopcodes.hpp | 4 +- 14 files changed, 282 insertions(+), 5 deletions(-) create mode 100644 components/interpreter/defines.cpp create mode 100644 components/interpreter/defines.hpp diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 40ebde246..0be732790 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -133,6 +133,8 @@ namespace MWBase virtual char getGlobalVariableType (const std::string& name) const = 0; ///< Return ' ', if there is no global variable with this name. + + virtual std::vector getGlobals () const = 0; virtual MWWorld::Ptr getPtr (const std::string& name, bool activeOnly) = 0; ///< Return a pointer to a liveCellRef with the given name. diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 80316c0f5..26d5af202 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -16,6 +16,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -155,7 +156,9 @@ namespace MWDialogue } parseText (info->mResponse); - win->addText (info->mResponse); + + MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); + win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); executeScript (info->mResultScript); mLastTopic = it->mId; mLastDialogue = *info; @@ -267,7 +270,8 @@ namespace MWDialogue else win->addTitle (topic); - win->addText (info->mResponse); + MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); + win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); executeScript (info->mResultScript); @@ -411,7 +415,9 @@ namespace MWDialogue mIsInChoice = false; std::string text = info->mResponse; parseText (text); - MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (text); + + MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); + MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (Interpreter::fixDefinesDialog(text, interpreterContext)); executeScript (info->mResultScript); mLastTopic = mLastTopic; mLastDialogue = *info; diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 53c23c25d..273034edd 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -1,5 +1,10 @@ #include "formatting.hpp" +#include + +#include "../mwscript/interpretercontext.hpp" +#include "../mwworld/ptr.hpp" + #include #include @@ -68,6 +73,9 @@ std::vector BookTextParser::split(std::string text, const int width { std::vector result; + MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor + text = Interpreter::fixDefinesDialog(text, interpreterContext); + boost::algorithm::replace_all(text, "
", "\n"); boost::algorithm::replace_all(text, "

", "\n\n"); @@ -167,6 +175,10 @@ std::vector BookTextParser::split(std::string text, const int width MyGUI::IntSize BookTextParser::parse(std::string text, MyGUI::Widget* parent, const int width) { + MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor + text = Interpreter::fixDefinesDialog(text, interpreterContext); + + mParent = parent; mWidth = width; mHeight = 0; diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 577ad008f..4ea984f9c 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -174,6 +174,20 @@ namespace MWScript MWBase::Environment::get().getWorld()->getGlobalVariable (name).mFloat = value; } + std::vector InterpreterContext::getGlobals () const + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + return world->getGlobals(); + + } + + char InterpreterContext::getGlobalType (const std::string& name) const + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + return world->getGlobalVariableType(name); + } + + bool InterpreterContext::isScriptRunning (const std::string& name) const { return MWBase::Environment::get().getScriptManager()->getGlobalScripts().isRunning (name); diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index 6d97f7949..15578aa24 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -75,6 +75,10 @@ namespace MWScript virtual void setGlobalLong (const std::string& name, int value); virtual void setGlobalFloat (const std::string& name, float value); + + virtual std::vector getGlobals () const; + + virtual char getGlobalType (const std::string& name) const; virtual bool isScriptRunning (const std::string& name) const; diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index 76dede5a3..f010661b9 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -7,6 +7,17 @@ namespace MWWorld { + std::vector Globals::getGlobals () const + { + std::vector retval; + Collection::const_iterator it; + for(it = mVariables.begin(); it != mVariables.end(); ++it){ + retval.push_back(it->first); + } + + return retval; + } + Globals::Collection::const_iterator Globals::find (const std::string& name) const { Collection::const_iterator iter = mVariables.find (name); diff --git a/apps/openmw/mwworld/globals.hpp b/apps/openmw/mwworld/globals.hpp index c7aee5f93..681bd560e 100644 --- a/apps/openmw/mwworld/globals.hpp +++ b/apps/openmw/mwworld/globals.hpp @@ -1,6 +1,7 @@ #ifndef GAME_MWWORLD_GLOBALS_H #define GAME_MWWORLD_GLOBALS_H +#include #include #include @@ -53,6 +54,8 @@ namespace MWWorld char getType (const std::string& name) const; ///< If there is no global variable with this name, ' ' is returned. + + std::vector getGlobals () const; }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f0b2efcec..88ad917c3 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -296,6 +296,11 @@ namespace MWWorld return mGlobalVariables->getType (name); } + std::vector World::getGlobals () const + { + return mGlobalVariables->getGlobals(); + } + Ptr World::getPtr (const std::string& name, bool activeOnly) { // the player is always in an active cell. diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 0962c292c..15b13256a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -153,6 +153,8 @@ namespace MWWorld virtual char getGlobalVariableType (const std::string& name) const; ///< Return ' ', if there is no global variable with this name. + + virtual std::vector getGlobals () const; virtual Ptr getPtr (const std::string& name, bool activeOnly); ///< Return a pointer to a liveCellRef with the given name. diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 29d6f46cd..bff99a69c 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -59,7 +59,7 @@ add_component_dir (compiler add_component_dir (interpreter context controlopcodes genericopcodes installopcodes interpreter localopcodes mathopcodes - miscopcodes opcodes runtime scriptopcodes spatialopcodes types + miscopcodes opcodes runtime scriptopcodes spatialopcodes types defines ) include_directories(${BULLET_INCLUDE_DIRS}) diff --git a/components/interpreter/context.hpp b/components/interpreter/context.hpp index 4221da36e..2cfc1c03d 100644 --- a/components/interpreter/context.hpp +++ b/components/interpreter/context.hpp @@ -49,6 +49,10 @@ namespace Interpreter virtual void setGlobalFloat (const std::string& name, float value) = 0; + virtual std::vector getGlobals () const = 0; + + virtual char getGlobalType (const std::string& name) const = 0; + virtual bool isScriptRunning (const std::string& name) const = 0; virtual void startScript (const std::string& name) = 0; diff --git a/components/interpreter/defines.cpp b/components/interpreter/defines.cpp new file mode 100644 index 000000000..d5e3e3d0d --- /dev/null +++ b/components/interpreter/defines.cpp @@ -0,0 +1,200 @@ +#include "defines.hpp" + +#include +#include +#include +#include + +namespace Interpreter{ + + bool Check(const std::string str, const std::string escword, unsigned int* i, unsigned int* start){ + bool retval = str.find(escword) == 0; + if(retval){ + (*i) += escword.length(); + (*start) = (*i) + 1; + } + return retval; + } + + std::vector globals; + + bool longerStr(const std::string a, const std::string b){ + return a.length() > b.length(); + } + + std::string fixDefinesReal(std::string text, char eschar, Context& context){ + + unsigned int start = 0; + std::string retval = ""; + for(unsigned int i = 0; i < text.length(); i++){ + if(text[i] == eschar){ + retval += text.substr(start, i - start); + std::string temp = text.substr(i+1, 100); + transform(temp.begin(), temp.end(), temp.begin(), ::tolower); + + bool found; + + if( (found = Check(temp, "actionslideright", &i, &start))){ + retval += "PLACEHOLDER_ACTION_SLIDE_RIGHT"; + } + else if((found = Check(temp, "actionreadymagic", &i, &start))){ + retval += "PLACEHOLDER_ACTION_READY_MAGIC"; + } + else if((found = Check(temp, "actionprevweapon", &i, &start))){ + retval += "PLACEHOLDER_ACTION_PREV_WEAPON"; + } + else if((found = Check(temp, "actionnextweapon", &i, &start))){ + retval += "PLACEHOLDER_ACTION_PREV_WEAPON"; + } + else if((found = Check(temp, "actiontogglerun", &i, &start))){ + retval += "PLACEHOLDER_ACTION_TOGGLE_RUN"; + } + else if((found = Check(temp, "actionslideleft", &i, &start))){ + retval += "PLACEHOLDER_ACTION_TOGGLE_RUN"; + } + else if((found = Check(temp, "actionreadyitem", &i, &start))){ + retval += "PLACEHOLDER_ACTION_READY_ITEM"; + } + else if((found = Check(temp, "actionprevspell", &i, &start))){ + retval += "PLACEHOLDER_ACTION_PREV_SPELL"; + } + else if((found = Check(temp, "actionnextspell", &i, &start))){ + retval += "PLACEHOLDER_ACTION_NEXT_SPELL"; + } + else if((found = Check(temp, "actionrestmenu", &i, &start))){ + retval += "PLACEHOLDER_ACTION_REST_MENU"; + } + else if((found = Check(temp, "actionmenumode", &i, &start))){ + retval += "PLACEHOLDER_ACTION_MENU_MODE"; + } + else if((found = Check(temp, "actionactivate", &i, &start))){ + retval += "PLACEHOLDER_ACTION_ACTIVATE"; + } + else if((found = Check(temp, "actionjournal", &i, &start))){ + retval += "PLACEHOLDER_ACTION_JOURNAL"; + } + else if((found = Check(temp, "actionforward", &i, &start))){ + retval += "PLACEHOLDER_ACTION_FORWARD"; + } + else if((found = Check(temp, "pccrimelevel", &i, &start))){ + retval += "PLACEHOLDER_PC_CRIME_LEVEL"; + } + else if((found = Check(temp, "actioncrouch", &i, &start))){ + retval += "PLACEHOLDER_ACTION_CROUCH"; + } + else if((found = Check(temp, "actionjump", &i, &start))){ + retval += "PLACEHOLDER_ACTION_JUMP"; + } + else if((found = Check(temp, "actionback", &i, &start))){ + retval += "PLACEHOLDER_ACTION_BACK"; + } + else if((found = Check(temp, "actionuse", &i, &start))){ + retval += "PLACEHOLDER_ACTION_USE"; + } + else if((found = Check(temp, "actionrun", &i, &start))){ + retval += "PLACEHOLDER_ACTION_RUN"; + } + else if((found = Check(temp, "pcclass", &i, &start))){ + retval += "PLACEHOLDER_PC_CLASS"; + } + else if((found = Check(temp, "pcrace", &i, &start))){ + retval += "PLACEHOLDER_PC_RACE"; + } + else if((found = Check(temp, "pcname", &i, &start))){ + retval += "PLACEHOLDER_PC_NAME"; + } + else if((found = Check(temp, "cell", &i, &start))){ + retval += "PLACEHOLDER_CELL"; + } + + else if(eschar == '%'){ // In Dialogue, not messagebox + if( (found = Check(temp, "faction", &i, &start))){ + retval += "PLACEHOLDER_FACTION"; + } + else if((found = Check(temp, "nextpcrank", &i, &start))){ + retval += "PLACEHOLDER_NEXT_PC_RANK"; + } + else if((found = Check(temp, "pcnextrank", &i, &start))){ + retval += "PLACEHOLDER_PC_NEXT_RANK"; + } + else if((found = Check(temp, "pcrank", &i, &start))){ + retval += "PLACEHOLDER_PC_RANK"; + } + else if((found = Check(temp, "rank", &i, &start))){ + retval += "PLACEHOLDER_RANK"; + } + + else if((found = Check(temp, "class", &i, &start))){ + retval += "PLACEHOLDER_CLASS"; + } + else if((found = Check(temp, "race", &i, &start))){ + retval += "PLACEHOLDER_RACE"; + } + else if((found = Check(temp, "name", &i, &start))){ + retval += "PLACEHOLDER_NAME"; + } + } + else if(eschar == '^') { // In messagebox, not dialogue + + /* empty in messageboxes */ + if( (found = Check(temp, "faction", &i, &start))); + else if((found = Check(temp, "nextpcrank", &i, &start))); + else if((found = Check(temp, "pcnextrank", &i, &start))); + else if((found = Check(temp, "pcrank", &i, &start))); + else if((found = Check(temp, "rank", &i, &start))); + + /* uses pc in messageboxes */ + else if((found = Check(temp, "class", &i, &start))){ + retval += "PLACEHOLDER_CLASS"; + } + else if((found = Check(temp, "race", &i, &start))){ + retval += "PLACEHOLDER_RACE"; + } + else if((found = Check(temp, "name", &i, &start))){ + retval += "PLACEHOLDER_NAME"; + } + } + + /* Not a builtin, try global variables */ + if(!found){ + /* if list of globals is empty, grab it and sort it by descending string length */ + if(globals.empty()){ + globals = context.getGlobals(); + sort(globals.begin(), globals.end(), longerStr); + } + + for(unsigned int j = 0; j < globals.size(); j++){ + if((found = Check(temp, globals[j], &i, &start))){ + char type = context.getGlobalType(globals[j]); + + switch(type){ + case 's': retval += std::to_string(context.getGlobalShort(globals[j])); break; + case 'l': retval += std::to_string(context.getGlobalLong(globals[j])); break; + case 'f': retval += std::to_string(context.getGlobalFloat(globals[j])); break; + } + break; + } + } + } + + /* Not found */ + if(!found){ + /* leave unmodified */ + i += 1; + start = i; + retval += eschar; + } + } + } + retval += text.substr(start, text.length() - start); + return retval; + } + + std::string fixDefinesDialog(std::string text, Context& context){ + return fixDefinesReal(text, '%', context); + } + + std::string fixDefinesMsgBox(std::string text, Context& context){ + return fixDefinesReal(text, '^', context); + } +} diff --git a/components/interpreter/defines.hpp b/components/interpreter/defines.hpp new file mode 100644 index 000000000..731284661 --- /dev/null +++ b/components/interpreter/defines.hpp @@ -0,0 +1,12 @@ +#ifndef GAME_MWMECHANICS_DEFINES_H +#define GAME_MWMECHANICS_DEFINES_H + +#include +#include "context.hpp" + +namespace Interpreter{ + std::string fixDefinesDialog(std::string text, Context& context); + std::string fixDefinesMsgBox(std::string text, Context& context); +} + +#endif diff --git a/components/interpreter/miscopcodes.hpp b/components/interpreter/miscopcodes.hpp index 37c38fc30..1b4c823a0 100644 --- a/components/interpreter/miscopcodes.hpp +++ b/components/interpreter/miscopcodes.hpp @@ -10,6 +10,7 @@ #include "opcodes.hpp" #include "runtime.hpp" +#include "defines.hpp" namespace Interpreter { @@ -69,7 +70,8 @@ namespace Interpreter } } } - + + formattedMessage = fixDefinesMsgBox(formattedMessage, runtime.getContext()); return formattedMessage; } From 8ac8fdff47b0fdf2523f8dadc2b370994b394f4d Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Fri, 21 Dec 2012 18:09:31 +0000 Subject: [PATCH 224/378] implemented all text defines except a few for keybindings that don't exist yet --- apps/openmw/mwbase/world.hpp | 2 + apps/openmw/mwscript/interpretercontext.cpp | 129 ++++++++++++++++++++ apps/openmw/mwscript/interpretercontext.hpp | 26 ++++ apps/openmw/mwworld/worldimp.cpp | 38 ++++++ apps/openmw/mwworld/worldimp.hpp | 2 + components/interpreter/context.hpp | 26 ++++ components/interpreter/defines.cpp | 58 ++++----- components/interpreter/defines.hpp | 4 +- 8 files changed, 254 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 0be732790..cc625b306 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -136,6 +136,8 @@ namespace MWBase virtual std::vector getGlobals () const = 0; + virtual std::string getCurrentCellName() const = 0; + virtual MWWorld::Ptr getPtr (const std::string& name, bool activeOnly) = 0; ///< Return a pointer to a liveCellRef with the given name. /// \param activeOnly do non search inactive cells. diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 4ea984f9c..cb0e4a7f9 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -11,10 +11,13 @@ #include "../mwbase/world.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/inputmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "../mwmechanics/npcstats.hpp" + #include "locals.hpp" #include "globalscripts.hpp" @@ -186,7 +189,133 @@ namespace MWScript MWBase::World *world = MWBase::Environment::get().getWorld(); return world->getGlobalVariableType(name); } + + std::string InterpreterContext::getActionBinding(const std::string& action) const + { + std::vector actions = MWBase::Environment::get().getInputManager()->getActionSorting (); + for (std::vector::const_iterator it = actions.begin(); it != actions.end(); ++it) + { + std::string desc = MWBase::Environment::get().getInputManager()->getActionDescription (*it); + if(desc == "") + continue; + + if(desc == action) + return MWBase::Environment::get().getInputManager()->getActionBindingName (*it); + } + + return "None"; + } + + std::string InterpreterContext::getNPCName() const + { + ESM::NPC npc = *mReference.get()->mBase; + return npc.mName; + } + + std::string InterpreterContext::getNPCRace() const + { + ESM::NPC npc = *mReference.get()->mBase; + return npc.mRace; + } + + std::string InterpreterContext::getNPCClass() const + { + ESM::NPC npc = *mReference.get()->mBase; + return npc.mClass; + } + + std::string InterpreterContext::getNPCFaction() const + { + ESM::NPC npc = *mReference.get()->mBase; + return npc.mFaction; + } + + std::string InterpreterContext::getNPCRank() const + { + std::map ranks = MWWorld::Class::get (mReference).getNpcStats (mReference).getFactionRanks(); + std::map::const_iterator it = ranks.begin(); + + MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::ESMStore &store = world->getStore(); + const ESM::Faction *faction = store.get().find(it->first); + + return faction->mRanks[it->second]; + } + std::string InterpreterContext::getPCName() const + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + ESM::NPC player = *world->getPlayer().getPlayer().get()->mBase; + return player.mName; + } + + std::string InterpreterContext::getPCRace() const + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + ESM::NPC player = *world->getPlayer().getPlayer().get()->mBase; + return player.mRace; + } + + std::string InterpreterContext::getPCClass() const + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + ESM::NPC player = *world->getPlayer().getPlayer().get()->mBase; + return player.mClass; + } + + std::string InterpreterContext::getPCRank() const + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + MWWorld::Ptr player = world->getPlayer().getPlayer(); + + std::string factionId = MWWorld::Class::get (mReference).getNpcStats (mReference).getFactionRanks().begin()->first; + + std::map ranks = MWWorld::Class::get (player).getNpcStats (player).getFactionRanks(); + std::map::const_iterator it = ranks.begin(); + + const MWWorld::ESMStore &store = world->getStore(); + const ESM::Faction *faction = store.get().find(factionId); + + if(it->second < 0 || it->second > 9) // there are only 10 ranks + return ""; + + return faction->mRanks[it->second]; + } + + std::string InterpreterContext::getPCNextRank() const + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + MWWorld::Ptr player = world->getPlayer().getPlayer(); + + std::string factionId = MWWorld::Class::get (mReference).getNpcStats (mReference).getFactionRanks().begin()->first; + + std::map ranks = MWWorld::Class::get (player).getNpcStats (player).getFactionRanks(); + std::map::const_iterator it = ranks.begin(); + + const MWWorld::ESMStore &store = world->getStore(); + const ESM::Faction *faction = store.get().find(factionId); + + if(it->second < 0 || it->second > 9) + return ""; + + if(it->second <= 8) // If player is at max rank, there is no next rank + return faction->mRanks[it->second + 1]; + else + return faction->mRanks[it->second]; + } + + int InterpreterContext::getPCBounty() const + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + MWWorld::Ptr player = world->getPlayer().getPlayer(); + return MWWorld::Class::get (player).getNpcStats (player).getBounty(); + } + + std::string InterpreterContext::getCurrentCellName() const + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + return world->getCurrentCellName(); + } bool InterpreterContext::isScriptRunning (const std::string& name) const { diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index 15578aa24..f0b2758d9 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -79,6 +79,32 @@ namespace MWScript virtual std::vector getGlobals () const; virtual char getGlobalType (const std::string& name) const; + + virtual std::string getActionBinding(const std::string& action) const; + + virtual std::string getNPCName() const; + + virtual std::string getNPCRace() const; + + virtual std::string getNPCClass() const; + + virtual std::string getNPCFaction() const; + + virtual std::string getNPCRank() const; + + virtual std::string getPCName() const; + + virtual std::string getPCRace() const; + + virtual std::string getPCClass() const; + + virtual std::string getPCRank() const; + + virtual std::string getPCNextRank() const; + + virtual int getPCBounty() const; + + virtual std::string getCurrentCellName() const; virtual bool isScriptRunning (const std::string& name) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 88ad917c3..343b5dcfc 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -300,6 +300,44 @@ namespace MWWorld { return mGlobalVariables->getGlobals(); } + + std::string World::getCurrentCellName () const + { + std::string name; + + Ptr::CellStore *cell = mWorldScene->getCurrentCell(); + if (cell->mCell->isExterior()) + { + if (cell->mCell->mName != "") + { + name = cell->mCell->mName; + } + else + { + const ESM::Region* region = + MWBase::Environment::get().getWorld()->getStore().get().search(cell->mCell->mRegion); + if (region) + name = region->mName; + else + { + const ESM::GameSetting *setting = + MWBase::Environment::get().getWorld()->getStore().get().search("sDefaultCellname"); + + if (setting && setting->mType == ESM::VT_String) + name = setting->getString(); + else + name = "Wilderness"; + } + + } + } + else + { + name = cell->mCell->mName; + } + + return name; + } Ptr World::getPtr (const std::string& name, bool activeOnly) { diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 15b13256a..ba90dd403 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -155,6 +155,8 @@ namespace MWWorld ///< Return ' ', if there is no global variable with this name. virtual std::vector getGlobals () const; + + virtual std::string getCurrentCellName () const; virtual Ptr getPtr (const std::string& name, bool activeOnly); ///< Return a pointer to a liveCellRef with the given name. diff --git a/components/interpreter/context.hpp b/components/interpreter/context.hpp index 2cfc1c03d..bdba7b6af 100644 --- a/components/interpreter/context.hpp +++ b/components/interpreter/context.hpp @@ -53,6 +53,32 @@ namespace Interpreter virtual char getGlobalType (const std::string& name) const = 0; + virtual std::string getActionBinding(const std::string& action) const = 0; + + virtual std::string getNPCName() const = 0; + + virtual std::string getNPCRace() const = 0; + + virtual std::string getNPCClass() const = 0; + + virtual std::string getNPCFaction() const = 0; + + virtual std::string getNPCRank() const = 0; + + virtual std::string getPCName() const = 0; + + virtual std::string getPCRace() const = 0; + + virtual std::string getPCClass() const = 0; + + virtual std::string getPCRank() const = 0; + + virtual std::string getPCNextRank() const = 0; + + virtual int getPCBounty() const = 0; + + virtual std::string getCurrentCellName() const = 0; + virtual bool isScriptRunning (const std::string& name) const = 0; virtual void startScript (const std::string& name) = 0; diff --git a/components/interpreter/defines.cpp b/components/interpreter/defines.cpp index d5e3e3d0d..bd355fd7c 100644 --- a/components/interpreter/defines.cpp +++ b/components/interpreter/defines.cpp @@ -35,10 +35,10 @@ namespace Interpreter{ bool found; if( (found = Check(temp, "actionslideright", &i, &start))){ - retval += "PLACEHOLDER_ACTION_SLIDE_RIGHT"; + retval += context.getActionBinding("#{sRight}"); } else if((found = Check(temp, "actionreadymagic", &i, &start))){ - retval += "PLACEHOLDER_ACTION_READY_MAGIC"; + retval += context.getActionBinding("#{sReady_Magic}"); } else if((found = Check(temp, "actionprevweapon", &i, &start))){ retval += "PLACEHOLDER_ACTION_PREV_WEAPON"; @@ -47,13 +47,13 @@ namespace Interpreter{ retval += "PLACEHOLDER_ACTION_PREV_WEAPON"; } else if((found = Check(temp, "actiontogglerun", &i, &start))){ - retval += "PLACEHOLDER_ACTION_TOGGLE_RUN"; + retval += context.getActionBinding("#{sAuto_Run}"); } else if((found = Check(temp, "actionslideleft", &i, &start))){ - retval += "PLACEHOLDER_ACTION_TOGGLE_RUN"; + retval += context.getActionBinding("#{sLeft}"); } else if((found = Check(temp, "actionreadyitem", &i, &start))){ - retval += "PLACEHOLDER_ACTION_READY_ITEM"; + retval += context.getActionBinding("#{sReady_Weapon}"); } else if((found = Check(temp, "actionprevspell", &i, &start))){ retval += "PLACEHOLDER_ACTION_PREV_SPELL"; @@ -62,31 +62,31 @@ namespace Interpreter{ retval += "PLACEHOLDER_ACTION_NEXT_SPELL"; } else if((found = Check(temp, "actionrestmenu", &i, &start))){ - retval += "PLACEHOLDER_ACTION_REST_MENU"; + retval += context.getActionBinding("#{sRestKey}"); } else if((found = Check(temp, "actionmenumode", &i, &start))){ - retval += "PLACEHOLDER_ACTION_MENU_MODE"; + retval += context.getActionBinding("#{sJournal}"); } else if((found = Check(temp, "actionactivate", &i, &start))){ - retval += "PLACEHOLDER_ACTION_ACTIVATE"; + retval += context.getActionBinding("#{sActivate}"); } else if((found = Check(temp, "actionjournal", &i, &start))){ - retval += "PLACEHOLDER_ACTION_JOURNAL"; + retval += context.getActionBinding("#{sJournal}"); } else if((found = Check(temp, "actionforward", &i, &start))){ - retval += "PLACEHOLDER_ACTION_FORWARD"; + retval += context.getActionBinding("#{sForward}"); } else if((found = Check(temp, "pccrimelevel", &i, &start))){ - retval += "PLACEHOLDER_PC_CRIME_LEVEL"; + retval += std::to_string(context.getPCBounty()); } else if((found = Check(temp, "actioncrouch", &i, &start))){ - retval += "PLACEHOLDER_ACTION_CROUCH"; + retval += context.getActionBinding("#{sCrouch_Sneak}"); } else if((found = Check(temp, "actionjump", &i, &start))){ - retval += "PLACEHOLDER_ACTION_JUMP"; + retval += context.getActionBinding("#{sJump}"); } else if((found = Check(temp, "actionback", &i, &start))){ - retval += "PLACEHOLDER_ACTION_BACK"; + retval += context.getActionBinding("#{sBack}"); } else if((found = Check(temp, "actionuse", &i, &start))){ retval += "PLACEHOLDER_ACTION_USE"; @@ -95,43 +95,43 @@ namespace Interpreter{ retval += "PLACEHOLDER_ACTION_RUN"; } else if((found = Check(temp, "pcclass", &i, &start))){ - retval += "PLACEHOLDER_PC_CLASS"; + retval += context.getPCClass(); } else if((found = Check(temp, "pcrace", &i, &start))){ - retval += "PLACEHOLDER_PC_RACE"; + retval += context.getPCRace(); } else if((found = Check(temp, "pcname", &i, &start))){ - retval += "PLACEHOLDER_PC_NAME"; + retval += context.getPCName(); } else if((found = Check(temp, "cell", &i, &start))){ - retval += "PLACEHOLDER_CELL"; + retval += context.getCurrentCellName(); } else if(eschar == '%'){ // In Dialogue, not messagebox if( (found = Check(temp, "faction", &i, &start))){ - retval += "PLACEHOLDER_FACTION"; + retval += context.getNPCFaction(); } else if((found = Check(temp, "nextpcrank", &i, &start))){ - retval += "PLACEHOLDER_NEXT_PC_RANK"; + retval += context.getPCNextRank(); } else if((found = Check(temp, "pcnextrank", &i, &start))){ - retval += "PLACEHOLDER_PC_NEXT_RANK"; + retval += context.getPCNextRank(); } else if((found = Check(temp, "pcrank", &i, &start))){ - retval += "PLACEHOLDER_PC_RANK"; + retval += context.getPCRank(); } else if((found = Check(temp, "rank", &i, &start))){ - retval += "PLACEHOLDER_RANK"; + retval += context.getNPCRank(); } else if((found = Check(temp, "class", &i, &start))){ - retval += "PLACEHOLDER_CLASS"; + retval += context.getNPCClass(); } else if((found = Check(temp, "race", &i, &start))){ - retval += "PLACEHOLDER_RACE"; + retval += context.getNPCRace(); } else if((found = Check(temp, "name", &i, &start))){ - retval += "PLACEHOLDER_NAME"; + retval += context.getNPCName(); } } else if(eschar == '^') { // In messagebox, not dialogue @@ -145,13 +145,13 @@ namespace Interpreter{ /* uses pc in messageboxes */ else if((found = Check(temp, "class", &i, &start))){ - retval += "PLACEHOLDER_CLASS"; + retval += context.getPCClass(); } else if((found = Check(temp, "race", &i, &start))){ - retval += "PLACEHOLDER_RACE"; + retval += context.getPCRace(); } else if((found = Check(temp, "name", &i, &start))){ - retval += "PLACEHOLDER_NAME"; + retval += context.getPCName(); } } diff --git a/components/interpreter/defines.hpp b/components/interpreter/defines.hpp index 731284661..4cfba21ea 100644 --- a/components/interpreter/defines.hpp +++ b/components/interpreter/defines.hpp @@ -1,5 +1,5 @@ -#ifndef GAME_MWMECHANICS_DEFINES_H -#define GAME_MWMECHANICS_DEFINES_H +#ifndef INTERPRETER_DEFINES_H_INCLUDED +#define INTERPRETER_DEFINES_H_INCLUDED #include #include "context.hpp" From 74ae47978009d6a0b187284615bc924e4c9ad971 Mon Sep 17 00:00:00 2001 From: lazydev Date: Sun, 23 Dec 2012 23:23:24 +0400 Subject: [PATCH 225/378] Cell names localization fix --- apps/esmtool/esmtool.cpp | 22 ++-- apps/launcher/model/datafilesmodel.cpp | 4 +- apps/openmw/engine.cpp | 9 +- apps/openmw/engine.hpp | 4 +- apps/openmw/main.cpp | 17 +-- apps/openmw/mwclass/door.cpp | 56 ++++++---- apps/openmw/mwclass/door.hpp | 3 + apps/openmw/mwgui/hud.cpp | 2 +- apps/openmw/mwgui/map_window.cpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 28 +++-- apps/openmw/mwgui/windowmanagerimp.hpp | 5 +- apps/openmw/mwworld/esmstore.hpp | 8 +- apps/openmw/mwworld/recordcmp.hpp | 56 +--------- apps/openmw/mwworld/store.hpp | 26 ++--- apps/openmw/mwworld/worldimp.cpp | 39 ++----- apps/openmw/mwworld/worldimp.hpp | 6 +- components/CMakeLists.txt | 4 + components/esm/esmreader.cpp | 16 +-- components/esm/esmreader.hpp | 2 +- components/misc/stringops.hpp | 50 +++++++++ components/to_utf8/to_utf8.cpp | 20 ++++ components/to_utf8/to_utf8.hpp | 3 + .../translation_data/translation_data.cpp | 104 ++++++++++++++++++ .../translation_data/translation_data.hpp | 36 ++++++ 24 files changed, 339 insertions(+), 183 deletions(-) create mode 100644 components/translation_data/translation_data.cpp create mode 100644 components/translation_data/translation_data.hpp diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 4f6d9dbfc..963656af5 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -59,7 +59,7 @@ struct Arguments std::string outname; std::vector types; - + ESMData data; ESM::ESMReader reader; ESM::ESMWriter writer; @@ -74,7 +74,7 @@ bool parseOptions (int argc, char** argv, Arguments &info) ("version,v", "print version information and quit.") ("raw,r", "Show an unformatted list of all records and subrecords.") // The intention is that this option would interact better - // with other modes including clone, dump, and raw. + // with other modes including clone, dump, and raw. ("type,t", bpo::value< std::vector >(), "Show only records of this type (four character record code). May " "be specified multiple times. Only affects dump mode.") @@ -262,7 +262,7 @@ void printRaw(ESM::ESMReader &esm) int load(Arguments& info) { ESM::ESMReader& esm = info.reader; - esm.setEncoding(info.encoding); + esm.setEncoding(ToUTF8::CalculateEncoding(info.encoding)); std::string filename = info.filename; std::cout << "Loading file: " << filename << std::endl; @@ -321,7 +321,7 @@ int load(Arguments& info) if (info.types.size() > 0) { std::vector::iterator match; - match = std::find(info.types.begin(), info.types.end(), + match = std::find(info.types.begin(), info.types.end(), n.toString()); if (match == info.types.end()) interested = false; } @@ -425,7 +425,7 @@ int clone(Arguments& info) if (++i % 3 == 0) std::cout << std::endl; } - + if (i % 3 != 0) std::cout << std::endl; @@ -450,7 +450,7 @@ int clone(Arguments& info) for (Records::iterator it = records.begin(); it != records.end() && i > 0; ++it) { EsmTool::RecordBase *record = *it; - + name.val = record->getType().val; esm.startRecord(name.toString(), record->getFlags()); @@ -485,7 +485,7 @@ int clone(Arguments& info) std::cerr << "\r" << perc << "%"; } } - + std::cout << "\rDone!" << std::endl; esm.close(); @@ -513,7 +513,7 @@ int comp(Arguments& info) fileOne.encoding = info.encoding; fileTwo.encoding = info.encoding; - + fileOne.filename = info.filename; fileTwo.filename = info.outname; @@ -534,9 +534,9 @@ int comp(Arguments& info) std::cout << "Not equal, different amount of records." << std::endl; return 1; } - - - + + + return 0; } diff --git a/apps/launcher/model/datafilesmodel.cpp b/apps/launcher/model/datafilesmodel.cpp index d85a15e73..6d70c6031 100644 --- a/apps/launcher/model/datafilesmodel.cpp +++ b/apps/launcher/model/datafilesmodel.cpp @@ -272,7 +272,7 @@ void DataFilesModel::addMasters(const QString &path) foreach (const QString &path, dir.entryList()) { try { ESM::ESMReader fileReader; - fileReader.setEncoding(mEncoding.toStdString()); + fileReader.setEncoding(ToUTF8::CalculateEncoding(mEncoding.toStdString())); fileReader.open(dir.absoluteFilePath(path).toStdString()); ESM::ESMReader::MasterList mlist = fileReader.getMasters(); @@ -335,7 +335,7 @@ void DataFilesModel::addPlugins(const QString &path) try { ESM::ESMReader fileReader; - fileReader.setEncoding(mEncoding.toStdString()); + fileReader.setEncoding(ToUTF8::CalculateEncoding(mEncoding.toStdString())); fileReader.open(dir.absoluteFilePath(path).toStdString()); ESM::ESMReader::MasterList mlist = fileReader.getMasters(); diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 2299053cd..9c31124b4 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -334,12 +335,16 @@ void OMW::Engine::go() mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster, mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoding, mFallbackMap)); + //Load translation data + std::unique_ptr translationDataStorage(new TranslationData::Storage(mEncoding)); + translationDataStorage->loadTranslationData(mFileCollections, mMaster); + // Create window manager - this manages all the MW-specific GUI windows MWScript::registerExtensions (mExtensions); mEnvironment.setWindowManager (new MWGui::WindowManager( mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), - mCfgMgr.getCachePath ().string(), mScriptConsoleMode)); + mCfgMgr.getCachePath ().string(), mScriptConsoleMode, translationDataStorage.release())); // Create sound system mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound)); @@ -487,7 +492,7 @@ void OMW::Engine::showFPS(int level) mFpsLevel = level; } -void OMW::Engine::setEncoding(const std::string& encoding) +void OMW::Engine::setEncoding(const ToUTF8::FromType& encoding) { mEncoding = encoding; } diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 57402c91e..2d2d58234 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -59,7 +59,7 @@ namespace OMW class Engine : private Ogre::FrameListener { MWBase::Environment mEnvironment; - std::string mEncoding; + ToUTF8::FromType mEncoding; Files::PathContainer mDataDirs; boost::filesystem::path mResDir; OEngine::Render::OgreRenderer *mOgre; @@ -154,7 +154,7 @@ namespace OMW void setCompileAll (bool all); /// Font encoding - void setEncoding(const std::string& encoding); + void setEncoding(const ToUTF8::FromType& encoding); void setAnimationVerbose(bool animverbose); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 0563fdbbb..8b5c5c6d6 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -181,21 +181,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat // Font encoding settings std::string encoding(variables["encoding"].as()); - if (encoding == "win1250") - { - std::cout << "Using Central and Eastern European font encoding." << std::endl; - engine.setEncoding(encoding); - } - else if (encoding == "win1251") - { - std::cout << "Using Cyrillic font encoding." << std::endl; - engine.setEncoding(encoding); - } - else - { - std::cout << "Using default (English) font encoding." << std::endl; - engine.setEncoding("win1252"); - } + std::cout << ToUTF8::EncodingUsingMessage(encoding) << std::endl; + engine.setEncoding(ToUTF8::CalculateEncoding(encoding)); // directory settings engine.enableFSStrict(variables["fs-strict"].as()); diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 843d1af4c..09a15a2cb 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -204,33 +204,10 @@ namespace MWClass std::string text; - const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - if (ref->mRef.mTeleport) { - std::string dest; - if (ref->mRef.mDestCell != "") - { - // door leads to an interior, use interior name as tooltip - dest = ref->mRef.mDestCell; - } - else - { - // door leads to exterior, use cell name (if any), otherwise translated region name - int x,y; - MWBase::Environment::get().getWorld()->positionToIndex (ref->mRef.mDoorDest.pos[0], ref->mRef.mDoorDest.pos[1], x, y); - const ESM::Cell* cell = store.get().find(x,y); - if (cell->mName != "") - dest = cell->mName; - else - { - const ESM::Region* region = - store.get().find(cell->mRegion); - dest = region->mName; - } - } text += "\n#{sTo}"; - text += "\n"+dest; + text += "\n" + getDestination(*ref); } if (ref->mRef.mLockLevel > 0) @@ -246,6 +223,37 @@ namespace MWClass return info; } + std::string Door::getDestination (const MWWorld::LiveCellRef& door) + { + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + + std::string dest; + if (door.mRef.mDestCell != "") + { + // door leads to an interior, use interior name as tooltip + dest = door.mRef.mDestCell; + } + else + { + // door leads to exterior, use cell name (if any), otherwise translated region name + int x,y; + MWBase::Environment::get().getWorld()->positionToIndex (door.mRef.mDoorDest.pos[0], door.mRef.mDoorDest.pos[1], x, y); + const ESM::Cell* cell = store.get().find(x,y); + if (cell->mName != "") + dest = cell->mName; + else + { + const ESM::Region* region = + store.get().find(cell->mRegion); + + //name as is, not a token + return region->mName; + } + } + + return "#{sCell=" + dest + "}"; + } + MWWorld::Ptr Door::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index b0f86f12d..05ba0248b 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -31,6 +31,9 @@ namespace MWClass virtual MWGui::ToolTipInfo getToolTipInfo (const MWWorld::Ptr& ptr) const; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. + static std::string getDestination (const MWWorld::LiveCellRef& door); + ///< @return destination cell name or token + virtual void lock (const MWWorld::Ptr& ptr, int lockLevel) const; ///< Lock object diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 9b4075f57..689baf488 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -319,7 +319,7 @@ void HUD::setCellName(const std::string& cellName) mCellNameTimer = 5.0f; mCellName = cellName; - mCellNameBox->setCaption(mCellName); + mCellNameBox->setCaptionWithReplacing("#{sCell=" + mCellName + "}"); mCellNameBox->setVisible(mMapVisible); } } diff --git a/apps/openmw/mwgui/map_window.cpp b/apps/openmw/mwgui/map_window.cpp index 0a26ebb8f..4e2ee517e 100644 --- a/apps/openmw/mwgui/map_window.cpp +++ b/apps/openmw/mwgui/map_window.cpp @@ -299,7 +299,7 @@ MapWindow::~MapWindow() void MapWindow::setCellName(const std::string& cellName) { - setTitle(cellName); + setTitle("#{sCell=" + cellName + "}"); } void MapWindow::addVisitedLocation(const std::string& name, int x, int y) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 376eca88d..fbe1250e3 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -55,7 +55,8 @@ using namespace MWGui; WindowManager::WindowManager( const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, - const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts) + const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, + TranslationData::Storage* pTranslationDataStorage) : mGuiManager(NULL) , mHud(NULL) , mMap(NULL) @@ -104,6 +105,7 @@ WindowManager::WindowManager( , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) , mHudEnabled(true) + , mTranslationDataStorage(pTranslationDataStorage) { // Set up the GUI system @@ -612,7 +614,7 @@ void WindowManager::changeCell(MWWorld::Ptr::CellStore* cell) if (cell->mCell->mName != "") { name = cell->mCell->mName; - mMap->addVisitedLocation (name, cell->mCell->getGridX (), cell->mCell->getGridY ()); + mMap->addVisitedLocation ("#{sCell=" + name + "}", cell->mCell->getGridX (), cell->mCell->getGridY ()); } else { @@ -722,13 +724,25 @@ void WindowManager::setDragDrop(bool dragDrop) void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result) { - const ESM::GameSetting *setting = - MWBase::Environment::get().getWorld()->getStore().get().find(_tag); + std::string tag(_tag); - if (setting && setting->mType == ESM::VT_String) - _result = setting->getString(); + std::string tokenToFind = "sCell="; + size_t tokenLength = tokenToFind.length(); + + if (tag.substr(0, tokenLength) == tokenToFind) + { + _result = mTranslationDataStorage->translateCellName(tag.substr(tokenLength)); + } else - _result = _tag; + { + const ESM::GameSetting *setting = + MWBase::Environment::get().getWorld()->getStore().get().find(tag); + + if (setting && setting->mType == ESM::VT_String) + _result = setting->getString(); + else + _result = tag; + } } void WindowManager::processChangedSettings(const Settings::CategorySettingVector& changed) diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 2e684b5da..40bff0eb0 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -14,6 +14,7 @@ #include #include +#include #include "../mwbase/windowmanager.hpp" @@ -76,7 +77,8 @@ namespace MWGui WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, - const std::string& cacheDir, bool consoleOnlyScripts); + const std::string& cacheDir, bool consoleOnlyScripts, + TranslationData::Storage* pTranslationDataStorage); virtual ~WindowManager(); /** @@ -250,6 +252,7 @@ namespace MWGui SpellCreationDialog* mSpellCreationDialog; EnchantingDialog* mEnchantingDialog; TrainingWindow* mTrainingWindow; + std::unique_ptr mTranslationDataStorage; CharacterCreation* mCharGen; diff --git a/apps/openmw/mwworld/esmstore.hpp b/apps/openmw/mwworld/esmstore.hpp index 9917254ee..e14219aae 100644 --- a/apps/openmw/mwworld/esmstore.hpp +++ b/apps/openmw/mwworld/esmstore.hpp @@ -6,7 +6,7 @@ #include #include "store.hpp" -namespace MWWorld +namespace MWWorld { class ESMStore { @@ -158,7 +158,7 @@ namespace MWWorld std::ostringstream id; id << "$dynamic" << mDynamicCount++; record.mId = id.str(); - + T *ptr = store.insert(record); for (iterator it = mStores.begin(); it != mStores.end(); ++it) { if (it->second == &store) { @@ -179,7 +179,7 @@ namespace MWWorld template <> inline const ESM::NPC *ESMStore::insert(const ESM::NPC &npc) { - if (StringUtils::ciEqual(npc.mId, "player")) { + if (Misc::StringUtils::ciEqual(npc.mId, "player")) { return mNpcs.insert(npc); } else if (mNpcs.search(npc.mId) != 0) { std::ostringstream msg; @@ -191,7 +191,7 @@ namespace MWWorld std::ostringstream id; id << "$dynamic" << mDynamicCount++; record.mId = id.str(); - + ESM::NPC *ptr = mNpcs.insert(record); mIds[ptr->mId] = ESM::REC_NPC_; return ptr; diff --git a/apps/openmw/mwworld/recordcmp.hpp b/apps/openmw/mwworld/recordcmp.hpp index 0b1655100..7de4f5565 100644 --- a/apps/openmw/mwworld/recordcmp.hpp +++ b/apps/openmw/mwworld/recordcmp.hpp @@ -1,62 +1,12 @@ #ifndef OPENMW_MWWORLD_RECORDCMP_H #define OPENMW_MWWORLD_RECORDCMP_H -#include #include -#include #include namespace MWWorld { - /// \todo move this to another location - class StringUtils - { - struct ci - { - bool operator()(int x, int y) const { - return std::tolower(x) < std::tolower(y); - } - }; - - public: - static bool ciLess(const std::string &x, const std::string &y) { - return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end(), ci()); - } - - static bool ciEqual(const std::string &x, const std::string &y) { - if (x.size() != y.size()) { - return false; - } - std::string::const_iterator xit = x.begin(); - std::string::const_iterator yit = y.begin(); - for (; xit != x.end(); ++xit, ++yit) { - if (std::tolower(*xit) != std::tolower(*yit)) { - return false; - } - } - return true; - } - - /// Transforms input string to lower case w/o copy - static std::string &toLower(std::string &inout) { - std::transform( - inout.begin(), - inout.end(), - inout.begin(), - (int (*)(int)) std::tolower - ); - return inout; - } - - /// Returns lower case copy of input string - static std::string lowerCase(const std::string &in) - { - std::string out = in; - return toLower(out); - } - }; - struct RecordCmp { template @@ -67,17 +17,17 @@ namespace MWWorld template <> inline bool RecordCmp::operator()(const ESM::Dialogue &x, const ESM::Dialogue &y) const { - return StringUtils::ciLess(x.mId, y.mId); + return Misc::StringUtils::ciLess(x.mId, y.mId); } template <> inline bool RecordCmp::operator()(const ESM::Cell &x, const ESM::Cell &y) const { - return StringUtils::ciLess(x.mName, y.mName); + return Misc::StringUtils::ciLess(x.mName, y.mName); } template <> inline bool RecordCmp::operator()(const ESM::Pathgrid &x, const ESM::Pathgrid &y) const { - return StringUtils::ciLess(x.mCell, y.mCell); + return Misc::StringUtils::ciLess(x.mCell, y.mCell); } } // end namespace diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 046de8c63..431cd3cf9 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -105,12 +105,12 @@ namespace MWWorld const T *search(const std::string &id) const { T item; - item.mId = StringUtils::lowerCase(id); + item.mId = Misc::StringUtils::lowerCase(id); typename std::vector::const_iterator it = std::lower_bound(mStatic.begin(), mStatic.end(), item, RecordCmp()); - if (it != mStatic.end() && StringUtils::ciEqual(it->mId, id)) { + if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->mId, id)) { return &(*it); } @@ -134,7 +134,7 @@ namespace MWWorld void load(ESM::ESMReader &esm, const std::string &id) { mStatic.push_back(T()); - mStatic.back().mId = StringUtils::lowerCase(id); + mStatic.back().mId = Misc::StringUtils::lowerCase(id); mStatic.back().load(esm); } @@ -169,7 +169,7 @@ namespace MWWorld } T *insert(const T &item) { - std::string id = StringUtils::lowerCase(item.mId); + std::string id = Misc::StringUtils::lowerCase(item.mId); std::pair result = mDynamic.insert(std::pair(id, item)); T *ptr = &result.first->second; @@ -182,7 +182,7 @@ namespace MWWorld } bool erase(const std::string &id) { - std::string key = StringUtils::lowerCase(id); + std::string key = Misc::StringUtils::lowerCase(id); typename Dynamic::iterator it = mDynamic.find(key); if (it == mDynamic.end()) { return false; @@ -213,7 +213,7 @@ namespace MWWorld inline void Store::load(ESM::ESMReader &esm, const std::string &id) { mStatic.push_back(ESM::Script()); mStatic.back().load(esm); - StringUtils::toLower(mStatic.back().mId); + Misc::StringUtils::toLower(mStatic.back().mId); } template <> @@ -385,12 +385,12 @@ namespace MWWorld const ESM::Cell *search(const std::string &id) const { ESM::Cell cell; - cell.mName = StringUtils::lowerCase(id); + cell.mName = Misc::StringUtils::lowerCase(id); std::vector::const_iterator it = std::lower_bound(mInt.begin(), mInt.end(), cell, RecordCmp()); - if (it != mInt.end() && StringUtils::ciEqual(it->mName, id)) { + if (it != mInt.end() && Misc::StringUtils::ciEqual(it->mName, id)) { return &(*it); } @@ -490,7 +490,7 @@ namespace MWWorld const ESM::Cell *searchExtByName(const std::string &id) const { std::vector::const_iterator it = mSharedExt.begin(); for (; it != mSharedExt.end(); ++it) { - if (StringUtils::ciEqual((*it)->mName, id)) { + if (Misc::StringUtils::ciEqual((*it)->mName, id)) { return *it; } } @@ -501,7 +501,7 @@ namespace MWWorld const ESM::Cell *searchExtByRegion(const std::string &id) const { std::vector::const_iterator it = mSharedExt.begin(); for (; it != mSharedExt.end(); ++it) { - if (StringUtils::ciEqual((*it)->mRegion, id)) { + if (Misc::StringUtils::ciEqual((*it)->mRegion, id)) { return *it; } } @@ -541,7 +541,7 @@ namespace MWWorld ptr = &result.first->second; mSharedExt.push_back(ptr); } else { - std::string key = StringUtils::lowerCase(cell.mName); + std::string key = Misc::StringUtils::lowerCase(cell.mName); // duplicate insertions are avoided by search(ESM::Cell &) std::pair result = @@ -561,7 +561,7 @@ namespace MWWorld } bool erase(const std::string &id) { - std::string key = StringUtils::lowerCase(id); + std::string key = Misc::StringUtils::lowerCase(id); DynamicInt::iterator it = mDynamicInt.find(key); if (it == mDynamicInt.end()) { @@ -691,7 +691,7 @@ namespace MWWorld pg.mCell = name; iterator it = std::lower_bound(mIntBegin, mIntEnd, pg, RecordCmp()); - if (it != mIntEnd && StringUtils::ciEqual(it->mCell, name)) { + if (it != mIntEnd && Misc::StringUtils::ciEqual(it->mCell, name)) { return &(*it); } return 0; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f0b2efcec..97f9d0f11 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -11,6 +11,8 @@ #include "../mwrender/sky.hpp" #include "../mwrender/player.hpp" +#include "../mwclass/door.hpp" + #include "player.hpp" #include "manualref.hpp" #include "cellfunctors.hpp" @@ -168,7 +170,7 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, - const std::string& encoding, std::map fallbackMap) + const ToUTF8::FromType& encoding, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mCells (mStore, mEsm), mNumFacing(0) @@ -237,7 +239,7 @@ namespace MWWorld MWWorld::Store::iterator it = regions.begin(); for (; it != regions.end(); ++it) { - if (MWWorld::StringUtils::ciEqual(cellName, it->mName)) + if (Misc::StringUtils::ciEqual(cellName, it->mName)) { return mStore.get().searchExtByRegion(it->mId); } @@ -805,7 +807,7 @@ namespace MWWorld { bool update = false; - if (StringUtils::ciEqual(record.mId, "player")) + if (Misc::StringUtils::ciEqual(record.mId, "player")) { static const char *sRaces[] = { @@ -816,7 +818,7 @@ namespace MWWorld int i=0; for (; sRaces[i]; ++i) - if (StringUtils::ciEqual (sRaces[i], record.mRace)) + if (Misc::StringUtils::ciEqual (sRaces[i], record.mRace)) break; mGlobalVariables->setInt ("pcrace", sRaces[i] ? i+1 : 0); @@ -825,9 +827,9 @@ namespace MWWorld mPlayer->getPlayer().get()->mBase; update = record.isMale() != player->isMale() || - !StringUtils::ciEqual(record.mRace, player->mRace) || - !StringUtils::ciEqual(record.mHead, player->mHead) || - !StringUtils::ciEqual(record.mHair, player->mHair); + !Misc::StringUtils::ciEqual(record.mRace, player->mRace) || + !Misc::StringUtils::ciEqual(record.mHead, player->mHead) || + !Misc::StringUtils::ciEqual(record.mHair, player->mHair); } const ESM::NPC *ret = mStore.insert(record); if (update) { @@ -1080,28 +1082,7 @@ namespace MWWorld if (ref.mRef.mTeleport) { World::DoorMarker newMarker; - - std::string dest; - if (ref.mRef.mDestCell != "") - { - // door leads to an interior, use interior name - dest = ref.mRef.mDestCell; - } - else - { - // door leads to exterior, use cell name (if any), otherwise translated region name - int x,y; - positionToIndex (ref.mRef.mDoorDest.pos[0], ref.mRef.mDoorDest.pos[1], x, y); - const ESM::Cell* cell = mStore.get().find(x,y); - if (cell->mName != "") - dest = cell->mName; - else - { - dest = mStore.get().find(cell->mRegion)->mName; - } - } - - newMarker.name = dest; + newMarker.name = MWClass::Door::getDestination(ref); ESM::Position pos = ref.mData.getPosition (); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 0962c292c..a7634a57c 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -95,7 +95,7 @@ namespace MWWorld World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, - const std::string& encoding, std::map fallbackMap); + const ToUTF8::FromType& encoding, std::map fallbackMap); virtual ~World(); @@ -228,7 +228,7 @@ namespace MWWorld virtual void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false); virtual void safePlaceObject(const MWWorld::Ptr& ptr,MWWorld::CellStore &Cell,ESM::Position pos); - ///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr. + ///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr. virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) const; @@ -323,7 +323,7 @@ namespace MWWorld } virtual void renderPlayer(); - + virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); virtual int canRest(); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 29d6f46cd..2d2e21633 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -62,6 +62,10 @@ add_component_dir (interpreter miscopcodes opcodes runtime scriptopcodes spatialopcodes types ) +add_component_dir (translation_data + translation_data + ) + include_directories(${BULLET_INCLUDE_DIRS}) add_library(components STATIC ${COMPONENT_FILES}) diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 2915a1ce7..580e006df 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -347,21 +347,9 @@ void ESMReader::fail(const std::string &msg) throw std::runtime_error(ss.str()); } -void ESMReader::setEncoding(const std::string& encoding) +void ESMReader::setEncoding(const ToUTF8::FromType& encoding) { - if (encoding == "win1250") - { - mEncoding = ToUTF8::WINDOWS_1250; - } - else if (encoding == "win1251") - { - mEncoding = ToUTF8::WINDOWS_1251; - } - else - { - // Default Latin encoding - mEncoding = ToUTF8::WINDOWS_1252; - } + mEncoding = encoding; } } diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 6a74c53e8..1d0f6f580 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -234,7 +234,7 @@ public: void fail(const std::string &msg); /// Sets font encoding for ESM strings - void setEncoding(const std::string& encoding); + void setEncoding(const ToUTF8::FromType& encoding); private: Ogre::DataStreamPtr mEsm; diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index a32104bf3..112d66bb8 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -1,8 +1,58 @@ #ifndef MISC_STRINGOPS_H #define MISC_STRINGOPS_H +#include +#include + namespace Misc { +class StringUtils +{ + struct ci + { + bool operator()(int x, int y) const { + return std::tolower(x) < std::tolower(y); + } + }; + +public: + static bool ciLess(const std::string &x, const std::string &y) { + return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end(), ci()); + } + + static bool ciEqual(const std::string &x, const std::string &y) { + if (x.size() != y.size()) { + return false; + } + std::string::const_iterator xit = x.begin(); + std::string::const_iterator yit = y.begin(); + for (; xit != x.end(); ++xit, ++yit) { + if (std::tolower(*xit) != std::tolower(*yit)) { + return false; + } + } + return true; + } + + /// Transforms input string to lower case w/o copy + static std::string &toLower(std::string &inout) { + std::transform( + inout.begin(), + inout.end(), + inout.begin(), + (int (*)(int)) std::tolower + ); + return inout; + } + + /// Returns lower case copy of input string + static std::string lowerCase(const std::string &in) + { + std::string out = in; + return toLower(out); + } +}; + /// Returns true if str1 begins with substring str2 bool begins(const char* str1, const char* str2); diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index 6f0ed8bfd..ee1e56229 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -357,3 +357,23 @@ std::string ToUTF8::getLegacyEnc(ToUTF8::FromType to) // Return a string return std::string(&output[0], outlen); } + +ToUTF8::FromType ToUTF8::CalculateEncoding(const std::string& encodingName) +{ + if (encodingName == "win1250") + return ToUTF8::WINDOWS_1250; + else if (encodingName == "win1251") + return ToUTF8::WINDOWS_1251; + else + return ToUTF8::WINDOWS_1252; +} + +std::string ToUTF8::EncodingUsingMessage(const std::string& encodingName) +{ + if (encodingName == "win1250") + return "Using Central and Eastern European font encoding."; + else if (encodingName == "win1251") + return "Using Cyrillic font encoding."; + else + return "Using default (English) font encoding."; +} diff --git a/components/to_utf8/to_utf8.hpp b/components/to_utf8/to_utf8.hpp index 69e9fc92c..9982213c9 100644 --- a/components/to_utf8/to_utf8.hpp +++ b/components/to_utf8/to_utf8.hpp @@ -22,6 +22,9 @@ namespace ToUTF8 // page. std::string getUtf8(FromType from); std::string getLegacyEnc(FromType to); + + FromType CalculateEncoding(const std::string& encodingName); + std::string EncodingUsingMessage(const std::string& encodingName); } #endif diff --git a/components/translation_data/translation_data.cpp b/components/translation_data/translation_data.cpp new file mode 100644 index 000000000..cd124bfbc --- /dev/null +++ b/components/translation_data/translation_data.cpp @@ -0,0 +1,104 @@ +#include "translation_data.hpp" +#include + +#include + +namespace TranslationData +{ + void Storage::loadTranslationData(const Files::Collections& dataFileCollections, + const std::string& esmFileName) + { + std::string esmNameNoExtension(Misc::StringUtils::lowerCase(esmFileName)); + //changing the extension + size_t dotPos = esmNameNoExtension.rfind('.'); + if (dotPos != std::string::npos) + esmNameNoExtension.resize(dotPos); + + loadData(mCellNamesTranslations, esmNameNoExtension, ".cel", dataFileCollections); + loadData(mPhraseForms, esmNameNoExtension, ".top", dataFileCollections); + loadData(mTopicIDs, esmNameNoExtension, ".mrk", dataFileCollections); + } + + void Storage::loadData(ContainerType& container, + const std::string& fileNameNoExtension, + const std::string& extension, + const Files::Collections& dataFileCollections) + { + std::string path; + try + { + path = dataFileCollections.getCollection(extension).getPath(fileNameNoExtension + extension).string(); + } + catch(...) + { + //no file + return; + } + + std::ifstream stream(path); + if (stream.is_open()) + { + loadDataFromStream(container, stream); + stream.close(); + } + } + + void Storage::loadDataFromStream(ContainerType& container, std::istream& stream) + { + std::string line; + while (!stream.eof()) + { + std::getline( stream, line ); + if (!line.empty() && *line.rbegin() == '\r') + line.resize(line.size() - 1); + + if (!line.empty()) + { + char* buffer = ToUTF8::getBuffer(line.size() + 1); + //buffer has at least line.size() + 1 bytes, so it must be safe + strcpy(buffer, line.c_str()); + line = ToUTF8::getUtf8(mEncoding); + + size_t tab_pos = line.find('\t'); + if (tab_pos != std::string::npos && tab_pos > 0 && tab_pos < line.size() - 1) + { + std::string key = line.substr(0, tab_pos); + std::string value = line.substr(tab_pos + 1); + + if (!key.empty() && !value.empty()) + container.insert(std::make_pair(key, value)); + } + } + } + } + + std::string Storage::translateCellName(const std::string& cellName) const + { + auto entry = mCellNamesTranslations.find(cellName); + + if (entry == mCellNamesTranslations.end()) + return cellName; + + return entry->second; + } + + std::string Storage::topicID(const std::string& phrase) const + { + std::string result; + + //seeking for the standard phrase form + auto phraseFormsIterator = mPhraseForms.find(phrase); + if (phraseFormsIterator != mPhraseForms.end()) + result = phraseFormsIterator->second; + else + result = phrase; + + + //seeking for the topic ID + auto topicIDIterator = mTopicIDs.find(result); + if (topicIDIterator != mTopicIDs.end()) + result = topicIDIterator->second; + + return result; + } +} diff --git a/components/translation_data/translation_data.hpp b/components/translation_data/translation_data.hpp new file mode 100644 index 000000000..7f4162c09 --- /dev/null +++ b/components/translation_data/translation_data.hpp @@ -0,0 +1,36 @@ +#ifndef COMPONENTS_TRANSLATION_DATA_H +#define COMPONENTS_TRANSLATION_DATA_H + +#include +#include + +namespace TranslationData +{ + class Storage + { + public: + Storage(const ToUTF8::FromType& encoding) : mEncoding(encoding) {} + + void loadTranslationData(const Files::Collections& dataFileCollections, + const std::string& esmFileName); + + std::string translateCellName(const std::string& cellName) const; + std::string topicID(const std::string& phrase) const; + + private: + typedef std::map ContainerType; + + void loadData(ContainerType& container, + const std::string& fileNameNoExtension, + const std::string& extension, + const Files::Collections& dataFileCollections); + + void loadDataFromStream(ContainerType& container, std::istream& stream); + + + ToUTF8::FromType mEncoding; + std::map mCellNamesTranslations, mTopicIDs, mPhraseForms; + }; +} + +#endif From 1f71395660d75ef5eedfb8c2eeac028345aaafb8 Mon Sep 17 00:00:00 2001 From: lazydev Date: Tue, 25 Dec 2012 23:20:39 +0400 Subject: [PATCH 226/378] renaming of translation component; removing of C++11 features --- apps/openmw/engine.cpp | 4 ++-- apps/openmw/mwgui/windowmanagerimp.hpp | 4 ++-- components/CMakeLists.txt | 4 ++-- .../translation.cpp} | 13 +++++++++---- .../translation.hpp} | 0 5 files changed, 15 insertions(+), 10 deletions(-) rename components/{translation_data/translation_data.cpp => translation/translation.cpp} (89%) rename components/{translation_data/translation_data.hpp => translation/translation.hpp} (100%) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 9c31124b4..3a7b6f6cf 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include @@ -336,7 +336,7 @@ void OMW::Engine::go() mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoding, mFallbackMap)); //Load translation data - std::unique_ptr translationDataStorage(new TranslationData::Storage(mEncoding)); + std::auto_ptr translationDataStorage(new TranslationData::Storage(mEncoding)); translationDataStorage->loadTranslationData(mFileCollections, mMaster); // Create window manager - this manages all the MW-specific GUI windows diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 40bff0eb0..ad3c9b736 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include "../mwbase/windowmanager.hpp" @@ -252,7 +252,7 @@ namespace MWGui SpellCreationDialog* mSpellCreationDialog; EnchantingDialog* mEnchantingDialog; TrainingWindow* mTrainingWindow; - std::unique_ptr mTranslationDataStorage; + std::auto_ptr mTranslationDataStorage; CharacterCreation* mCharGen; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 2d2e21633..140bef771 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -62,8 +62,8 @@ add_component_dir (interpreter miscopcodes opcodes runtime scriptopcodes spatialopcodes types ) -add_component_dir (translation_data - translation_data +add_component_dir (translation + translation ) include_directories(${BULLET_INCLUDE_DIRS}) diff --git a/components/translation_data/translation_data.cpp b/components/translation/translation.cpp similarity index 89% rename from components/translation_data/translation_data.cpp rename to components/translation/translation.cpp index cd124bfbc..a178a053c 100644 --- a/components/translation_data/translation_data.cpp +++ b/components/translation/translation.cpp @@ -1,4 +1,4 @@ -#include "translation_data.hpp" +#include "translation.hpp" #include #include @@ -74,7 +74,8 @@ namespace TranslationData std::string Storage::translateCellName(const std::string& cellName) const { - auto entry = mCellNamesTranslations.find(cellName); + std::map::const_iterator entry = + mCellNamesTranslations.find(cellName); if (entry == mCellNamesTranslations.end()) return cellName; @@ -87,7 +88,9 @@ namespace TranslationData std::string result; //seeking for the standard phrase form - auto phraseFormsIterator = mPhraseForms.find(phrase); + std::map::const_iterator phraseFormsIterator = + mPhraseForms.find(phrase); + if (phraseFormsIterator != mPhraseForms.end()) result = phraseFormsIterator->second; else @@ -95,7 +98,9 @@ namespace TranslationData //seeking for the topic ID - auto topicIDIterator = mTopicIDs.find(result); + std::map::const_iterator topicIDIterator = + mTopicIDs.find(result); + if (topicIDIterator != mTopicIDs.end()) result = topicIDIterator->second; diff --git a/components/translation_data/translation_data.hpp b/components/translation/translation.hpp similarity index 100% rename from components/translation_data/translation_data.hpp rename to components/translation/translation.hpp From 2d468fec028d0ea3d324cdd2a5acab6da997edf6 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 26 Dec 2012 16:19:59 +0100 Subject: [PATCH 227/378] made previous commits naming standard compliant --- apps/esmtool/esmtool.cpp | 2 +- apps/launcher/model/datafilesmodel.cpp | 4 ++-- apps/openmw/main.cpp | 4 ++-- components/to_utf8/to_utf8.cpp | 4 ++-- components/to_utf8/to_utf8.hpp | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 963656af5..0cd6e3905 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -262,7 +262,7 @@ void printRaw(ESM::ESMReader &esm) int load(Arguments& info) { ESM::ESMReader& esm = info.reader; - esm.setEncoding(ToUTF8::CalculateEncoding(info.encoding)); + esm.setEncoding(ToUTF8::calculateEncoding(info.encoding)); std::string filename = info.filename; std::cout << "Loading file: " << filename << std::endl; diff --git a/apps/launcher/model/datafilesmodel.cpp b/apps/launcher/model/datafilesmodel.cpp index 6d70c6031..716c9e902 100644 --- a/apps/launcher/model/datafilesmodel.cpp +++ b/apps/launcher/model/datafilesmodel.cpp @@ -272,7 +272,7 @@ void DataFilesModel::addMasters(const QString &path) foreach (const QString &path, dir.entryList()) { try { ESM::ESMReader fileReader; - fileReader.setEncoding(ToUTF8::CalculateEncoding(mEncoding.toStdString())); + fileReader.setEncoding(ToUTF8::calculateEncoding(mEncoding.toStdString())); fileReader.open(dir.absoluteFilePath(path).toStdString()); ESM::ESMReader::MasterList mlist = fileReader.getMasters(); @@ -335,7 +335,7 @@ void DataFilesModel::addPlugins(const QString &path) try { ESM::ESMReader fileReader; - fileReader.setEncoding(ToUTF8::CalculateEncoding(mEncoding.toStdString())); + fileReader.setEncoding(ToUTF8::calculateEncoding(mEncoding.toStdString())); fileReader.open(dir.absoluteFilePath(path).toStdString()); ESM::ESMReader::MasterList mlist = fileReader.getMasters(); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 8b5c5c6d6..96dbf8987 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -181,8 +181,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat // Font encoding settings std::string encoding(variables["encoding"].as()); - std::cout << ToUTF8::EncodingUsingMessage(encoding) << std::endl; - engine.setEncoding(ToUTF8::CalculateEncoding(encoding)); + std::cout << ToUTF8::encodingUsingMessage(encoding) << std::endl; + engine.setEncoding(ToUTF8::calculateEncoding(encoding)); // directory settings engine.enableFSStrict(variables["fs-strict"].as()); diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index ee1e56229..7db611247 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -358,7 +358,7 @@ std::string ToUTF8::getLegacyEnc(ToUTF8::FromType to) return std::string(&output[0], outlen); } -ToUTF8::FromType ToUTF8::CalculateEncoding(const std::string& encodingName) +ToUTF8::FromType ToUTF8::calculateEncoding(const std::string& encodingName) { if (encodingName == "win1250") return ToUTF8::WINDOWS_1250; @@ -368,7 +368,7 @@ ToUTF8::FromType ToUTF8::CalculateEncoding(const std::string& encodingName) return ToUTF8::WINDOWS_1252; } -std::string ToUTF8::EncodingUsingMessage(const std::string& encodingName) +std::string ToUTF8::encodingUsingMessage(const std::string& encodingName) { if (encodingName == "win1250") return "Using Central and Eastern European font encoding."; diff --git a/components/to_utf8/to_utf8.hpp b/components/to_utf8/to_utf8.hpp index 9982213c9..f52ae73bd 100644 --- a/components/to_utf8/to_utf8.hpp +++ b/components/to_utf8/to_utf8.hpp @@ -23,8 +23,8 @@ namespace ToUTF8 std::string getUtf8(FromType from); std::string getLegacyEnc(FromType to); - FromType CalculateEncoding(const std::string& encodingName); - std::string EncodingUsingMessage(const std::string& encodingName); + FromType calculateEncoding(const std::string& encodingName); + std::string encodingUsingMessage(const std::string& encodingName); } #endif From 206c613b52e599b9366cbfa92278bf961e07bc8c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 26 Dec 2012 17:03:37 +0100 Subject: [PATCH 228/378] moved translation storage from GUI manager to engine --- apps/openmw/engine.cpp | 6 +++--- apps/openmw/engine.hpp | 3 ++- apps/openmw/mwgui/windowmanagerimp.cpp | 6 +++--- apps/openmw/mwgui/windowmanagerimp.hpp | 4 ++-- components/translation/translation.cpp | 5 +++++ components/translation/translation.hpp | 3 ++- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 3a7b6f6cf..b995300d5 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -336,15 +336,14 @@ void OMW::Engine::go() mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoding, mFallbackMap)); //Load translation data - std::auto_ptr translationDataStorage(new TranslationData::Storage(mEncoding)); - translationDataStorage->loadTranslationData(mFileCollections, mMaster); + mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster); // Create window manager - this manages all the MW-specific GUI windows MWScript::registerExtensions (mExtensions); mEnvironment.setWindowManager (new MWGui::WindowManager( mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), - mCfgMgr.getCachePath ().string(), mScriptConsoleMode, translationDataStorage.release())); + mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage)); // Create sound system mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound)); @@ -495,6 +494,7 @@ void OMW::Engine::showFPS(int level) void OMW::Engine::setEncoding(const ToUTF8::FromType& encoding) { mEncoding = encoding; + mTranslationDataStorage.setEncoding (encoding); } void OMW::Engine::setFallbackValues(std::map fallbackMap) diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 2d2d58234..cbc627b83 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -5,6 +5,7 @@ #include #include +#include #include "mwbase/environment.hpp" @@ -79,9 +80,9 @@ namespace OMW Compiler::Extensions mExtensions; Compiler::Context *mScriptContext; - Files::Collections mFileCollections; bool mFSStrict; + TranslationData::Storage mTranslationDataStorage; // not implemented Engine (const Engine&); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index fbe1250e3..cdcb615dc 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -56,7 +56,7 @@ using namespace MWGui; WindowManager::WindowManager( const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, - TranslationData::Storage* pTranslationDataStorage) + TranslationData::Storage& translationDataStorage) : mGuiManager(NULL) , mHud(NULL) , mMap(NULL) @@ -105,7 +105,7 @@ WindowManager::WindowManager( , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) , mHudEnabled(true) - , mTranslationDataStorage(pTranslationDataStorage) + , mTranslationDataStorage (translationDataStorage) { // Set up the GUI system @@ -731,7 +731,7 @@ void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _r if (tag.substr(0, tokenLength) == tokenToFind) { - _result = mTranslationDataStorage->translateCellName(tag.substr(tokenLength)); + _result = mTranslationDataStorage.translateCellName(tag.substr(tokenLength)); } else { diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index ad3c9b736..b252254a0 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -78,7 +78,7 @@ namespace MWGui WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, - TranslationData::Storage* pTranslationDataStorage); + TranslationData::Storage& translationDataStorage); virtual ~WindowManager(); /** @@ -252,7 +252,7 @@ namespace MWGui SpellCreationDialog* mSpellCreationDialog; EnchantingDialog* mEnchantingDialog; TrainingWindow* mTrainingWindow; - std::auto_ptr mTranslationDataStorage; + TranslationData::Storage& mTranslationDataStorage; CharacterCreation* mCharGen; diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index a178a053c..e6c6a0233 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -106,4 +106,9 @@ namespace TranslationData return result; } + + void Storage::setEncoding (const ToUTF8::FromType& encoding) + { + mEncoding = encoding; + } } diff --git a/components/translation/translation.hpp b/components/translation/translation.hpp index 7f4162c09..0160bc48a 100644 --- a/components/translation/translation.hpp +++ b/components/translation/translation.hpp @@ -9,7 +9,6 @@ namespace TranslationData class Storage { public: - Storage(const ToUTF8::FromType& encoding) : mEncoding(encoding) {} void loadTranslationData(const Files::Collections& dataFileCollections, const std::string& esmFileName); @@ -17,6 +16,8 @@ namespace TranslationData std::string translateCellName(const std::string& cellName) const; std::string topicID(const std::string& phrase) const; + void setEncoding (const ToUTF8::FromType& encoding); + private: typedef std::map ContainerType; From afc2e840aeff2dc43a4020a2c333a22c4ef6d6fd Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 26 Dec 2012 17:06:33 +0100 Subject: [PATCH 229/378] renamed namespace TranslationData to Translation --- apps/openmw/engine.hpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 3 ++- apps/openmw/mwgui/windowmanagerimp.hpp | 10 +++++++--- components/translation/translation.cpp | 2 +- components/translation/translation.hpp | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index cbc627b83..00889197e 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -82,7 +82,7 @@ namespace OMW Files::Collections mFileCollections; bool mFSStrict; - TranslationData::Storage mTranslationDataStorage; + Translation::Storage mTranslationDataStorage; // not implemented Engine (const Engine&); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index cdcb615dc..caf6a4ab0 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -9,6 +9,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -56,7 +57,7 @@ using namespace MWGui; WindowManager::WindowManager( const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, - TranslationData::Storage& translationDataStorage) + Translation::Storage& translationDataStorage) : mGuiManager(NULL) , mHud(NULL) , mMap(NULL) diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index b252254a0..aaa856aef 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -14,7 +14,6 @@ #include #include -#include #include "../mwbase/windowmanager.hpp" @@ -30,6 +29,11 @@ namespace Compiler class Extensions; } +namespace Translation +{ + class Storage; +} + namespace OEngine { namespace GUI @@ -78,7 +82,7 @@ namespace MWGui WindowManager(const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, - TranslationData::Storage& translationDataStorage); + Translation::Storage& translationDataStorage); virtual ~WindowManager(); /** @@ -252,7 +256,7 @@ namespace MWGui SpellCreationDialog* mSpellCreationDialog; EnchantingDialog* mEnchantingDialog; TrainingWindow* mTrainingWindow; - TranslationData::Storage& mTranslationDataStorage; + Translation::Storage& mTranslationDataStorage; CharacterCreation* mCharGen; diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index e6c6a0233..feda76f6f 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -3,7 +3,7 @@ #include -namespace TranslationData +namespace Translation { void Storage::loadTranslationData(const Files::Collections& dataFileCollections, const std::string& esmFileName) diff --git a/components/translation/translation.hpp b/components/translation/translation.hpp index 0160bc48a..668d4c067 100644 --- a/components/translation/translation.hpp +++ b/components/translation/translation.hpp @@ -4,7 +4,7 @@ #include #include -namespace TranslationData +namespace Translation { class Storage { From e9ba7339f32fecbb974cb80ffd258a6bb8731620 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 26 Dec 2012 17:15:53 +0100 Subject: [PATCH 230/378] improved error handling --- components/files/multidircollection.cpp | 5 +++++ components/files/multidircollection.hpp | 3 +++ components/translation/translation.cpp | 22 +++++++++------------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/components/files/multidircollection.cpp b/components/files/multidircollection.cpp index b44c42986..347de96a6 100644 --- a/components/files/multidircollection.cpp +++ b/components/files/multidircollection.cpp @@ -95,6 +95,11 @@ namespace Files return iter->second; } + bool MultiDirCollection::doesExist (const std::string& file) const + { + return mFiles.find (file)!=mFiles.end(); + } + MultiDirCollection::TIter MultiDirCollection::begin() const { return mFiles.begin(); diff --git a/components/files/multidircollection.hpp b/components/files/multidircollection.hpp index e8abeb45d..3b420d677 100644 --- a/components/files/multidircollection.hpp +++ b/components/files/multidircollection.hpp @@ -73,6 +73,9 @@ namespace Files /// If the file does not exist, an exception is thrown. \a file must include /// the extension. + bool doesExist (const std::string& file) const; + ///< \return Does a file with the given name exist? + TIter begin() const; ///< Return iterator pointing to the first file. diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index feda76f6f..b559c6c1c 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -24,22 +24,18 @@ namespace Translation const std::string& extension, const Files::Collections& dataFileCollections) { - std::string path; - try - { - path = dataFileCollections.getCollection(extension).getPath(fileNameNoExtension + extension).string(); - } - catch(...) - { - //no file - return; - } + std::string fileName = fileNameNoExtension + extension; - std::ifstream stream(path); - if (stream.is_open()) + if (dataFileCollections.getCollection (extension).doesExist (fileName)) { + std::string path = dataFileCollections.getCollection (extension).getPath (fileName).string(); + + std::ifstream stream (path); + + if (!stream.is_open()) + throw std::runtime_error ("failed to open translation file: " + fileName); + loadDataFromStream(container, stream); - stream.close(); } } From a14b7e4a0fe80f07949b5fc7e019f4c5827b1baa Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Wed, 26 Dec 2012 18:07:56 +0000 Subject: [PATCH 231/378] small fixes on text defines --- apps/openmw/mwgui/formatting.cpp | 4 ++-- components/interpreter/defines.cpp | 22 +++++++++++++++------- components/interpreter/defines.hpp | 1 + 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwgui/formatting.cpp b/apps/openmw/mwgui/formatting.cpp index 273034edd..4090b592d 100644 --- a/apps/openmw/mwgui/formatting.cpp +++ b/apps/openmw/mwgui/formatting.cpp @@ -74,7 +74,7 @@ std::vector BookTextParser::split(std::string text, const int width std::vector result; MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor - text = Interpreter::fixDefinesDialog(text, interpreterContext); + text = Interpreter::fixDefinesBook(text, interpreterContext); boost::algorithm::replace_all(text, "
", "\n"); boost::algorithm::replace_all(text, "

", "\n\n"); @@ -176,7 +176,7 @@ std::vector BookTextParser::split(std::string text, const int width MyGUI::IntSize BookTextParser::parse(std::string text, MyGUI::Widget* parent, const int width) { MWScript::InterpreterContext interpreterContext(NULL, MWWorld::Ptr()); // empty arguments, because there is no locals or actor - text = Interpreter::fixDefinesDialog(text, interpreterContext); + text = Interpreter::fixDefinesBook(text, interpreterContext); mParent = parent; diff --git a/components/interpreter/defines.cpp b/components/interpreter/defines.cpp index bd355fd7c..29c78200d 100644 --- a/components/interpreter/defines.cpp +++ b/components/interpreter/defines.cpp @@ -1,6 +1,5 @@ #include "defines.hpp" -#include #include #include #include @@ -22,7 +21,7 @@ namespace Interpreter{ return a.length() > b.length(); } - std::string fixDefinesReal(std::string text, char eschar, Context& context){ + std::string fixDefinesReal(std::string text, char eschar, bool isBook, Context& context){ unsigned int start = 0; std::string retval = ""; @@ -107,7 +106,7 @@ namespace Interpreter{ retval += context.getCurrentCellName(); } - else if(eschar == '%'){ // In Dialogue, not messagebox + else if(eschar == '%' && !isBook) { // In Dialogue, not messagebox if( (found = Check(temp, "faction", &i, &start))){ retval += context.getNPCFaction(); } @@ -134,9 +133,9 @@ namespace Interpreter{ retval += context.getNPCName(); } } - else if(eschar == '^') { // In messagebox, not dialogue + else { // In messagebox or book, not dialogue - /* empty in messageboxes */ + /* empty outside dialogue */ if( (found = Check(temp, "faction", &i, &start))); else if((found = Check(temp, "nextpcrank", &i, &start))); else if((found = Check(temp, "pcnextrank", &i, &start))); @@ -164,6 +163,11 @@ namespace Interpreter{ } for(unsigned int j = 0; j < globals.size(); j++){ + if(globals[j].length() > temp.length()){ // Just in case there's a global with a huuuge name + std::string temp = text.substr(i+1, globals[j].length()); + transform(temp.begin(), temp.end(), temp.begin(), ::tolower); + } + if((found = Check(temp, globals[j], &i, &start))){ char type = context.getGlobalType(globals[j]); @@ -191,10 +195,14 @@ namespace Interpreter{ } std::string fixDefinesDialog(std::string text, Context& context){ - return fixDefinesReal(text, '%', context); + return fixDefinesReal(text, '%', false, context); } std::string fixDefinesMsgBox(std::string text, Context& context){ - return fixDefinesReal(text, '^', context); + return fixDefinesReal(text, '^', false, context); + } + + std::string fixDefinesBook(std::string text, Context& context){ + return fixDefinesReal(text, '%', true, context); } } diff --git a/components/interpreter/defines.hpp b/components/interpreter/defines.hpp index 4cfba21ea..00c4386b8 100644 --- a/components/interpreter/defines.hpp +++ b/components/interpreter/defines.hpp @@ -7,6 +7,7 @@ namespace Interpreter{ std::string fixDefinesDialog(std::string text, Context& context); std::string fixDefinesMsgBox(std::string text, Context& context); + std::string fixDefinesBook(std::string text, Context& context); } #endif From 716fbbbd74d3474fb0a36f8eff00fdd7da8152b5 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Wed, 26 Dec 2012 22:57:53 +0000 Subject: [PATCH 232/378] messageboxes during dialogue show up in dialogue window, and messageboxes are generated when an item is removed from inventory --- apps/openmw/mwgui/dialogue.cpp | 5 +++ apps/openmw/mwgui/dialogue.hpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 22 +++++++++----- apps/openmw/mwscript/containerextensions.cpp | 32 ++++++++++++++++++++ 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 258e9174c..e2b9f461a 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -370,6 +370,11 @@ void DialogueWindow::addText(std::string text) mHistory->addDialogText("#B29154"+parseText(text)+"#B29154"); } +void DialogueWindow::addMessageBox(std::string text) +{ + mHistory->addDialogText("\n#FFFFFF"+text+"#B29154"); +} + void DialogueWindow::addTitle(std::string text) { // This is called from the dialogue manager, so text is diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 082d92524..c4b86e6c8 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -65,6 +65,7 @@ namespace MWGui void setKeywords(std::list keyWord); void removeKeyword(std::string keyWord); void addText(std::string text); + void addMessageBox(std::string text); void addTitle(std::string text); void askQuestion(std::string question); void goodbye(); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index caf6a4ab0..ebef71409 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -537,14 +537,20 @@ void WindowManager::removeDialog(OEngine::GUI::Layout*dialog) void WindowManager::messageBox (const std::string& message, const std::vector& buttons) { - if (buttons.empty()) - { - mMessageBoxManager->createMessageBox(message); - } - else - { - mMessageBoxManager->createInteractiveMessageBox(message, buttons); - pushGuiMode(GM_InterMessageBox); + /* If there are no buttons, and there is a dialogue window open, messagebox goes to the dialogue window */ + if(buttons.empty() && std::find(mGuiModes.begin(), mGuiModes.end(), GM_Dialogue) != mGuiModes.end()) + mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message)); + + else{ + if (buttons.empty()) + { + mMessageBoxManager->createMessageBox(message); + } + else + { + mMessageBoxManager->createInteractiveMessageBox(message, buttons); + pushGuiMode(GM_InterMessageBox); + } } } diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 7f4913b8a..07cf3b9e3 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -10,6 +10,7 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/class.hpp" @@ -106,12 +107,41 @@ namespace MWScript throw std::runtime_error ("second argument for RemoveItem must be non-negative"); MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); + + std::string itemName = ""; for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end() && count; ++iter) { if (toLower(iter->getCellRef().mRefID) == toLower(item)) { + switch(iter.getType()){ + case MWWorld::ContainerStore::Type_Potion: + itemName = iter->get()->mBase->mName; break; + case MWWorld::ContainerStore::Type_Apparatus: + itemName = iter->get()->mBase->mName; break; + case MWWorld::ContainerStore::Type_Armor: + itemName = iter->get()->mBase->mName; break; + case MWWorld::ContainerStore::Type_Book: + itemName = iter->get()->mBase->mName; break; + case MWWorld::ContainerStore::Type_Clothing: + itemName = iter->get()->mBase->mName; break; + case MWWorld::ContainerStore::Type_Ingredient: + itemName = iter->get()->mBase->mName; break; + case MWWorld::ContainerStore::Type_Light: + itemName = iter->get()->mBase->mName; break; + case MWWorld::ContainerStore::Type_Lockpick: + itemName = iter->get()->mBase->mName; break; + case MWWorld::ContainerStore::Type_Miscellaneous: + itemName = iter->get()->mBase->mName; break; + case MWWorld::ContainerStore::Type_Probe: + itemName = iter->get()->mBase->mName; break; + case MWWorld::ContainerStore::Type_Repair: + itemName = iter->get()->mBase->mName; break; + case MWWorld::ContainerStore::Type_Weapon: + itemName = iter->get()->mBase->mName; break; + } + if (iter->getRefData().getCount()<=count) { count -= iter->getRefData().getCount(); @@ -124,6 +154,8 @@ namespace MWScript } } } + + MWBase::Environment::get().getWindowManager()->messageBox(itemName + " has been removed from your inventory.", std::vector()); // To be fully compatible with original Morrowind, we would need to check if // count is >= 0 here and throw an exception. But let's be tollerant instead. From 299a1f32ed59855c3be4305d1336799efaca3ec2 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Wed, 26 Dec 2012 23:52:16 +0000 Subject: [PATCH 233/378] cleanup --- apps/openmw/mwgui/windowmanagerimp.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index ebef71409..fe3cb7b11 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -537,20 +537,19 @@ void WindowManager::removeDialog(OEngine::GUI::Layout*dialog) void WindowManager::messageBox (const std::string& message, const std::vector& buttons) { - /* If there are no buttons, and there is a dialogue window open, messagebox goes to the dialogue window */ - if(buttons.empty() && std::find(mGuiModes.begin(), mGuiModes.end(), GM_Dialogue) != mGuiModes.end()) - mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message)); + if(buttons.empty()){ + /* If there are no buttons, and there is a dialogue window open, messagebox goes to the dialogue window */ + if(std::find(mGuiModes.begin(), mGuiModes.end(), GM_Dialogue) != mGuiModes.end()) + mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message)); - else{ - if (buttons.empty()) - { - mMessageBoxManager->createMessageBox(message); - } else - { - mMessageBoxManager->createInteractiveMessageBox(message, buttons); - pushGuiMode(GM_InterMessageBox); - } + mMessageBoxManager->createMessageBox(message); + } + + else + { + mMessageBoxManager->createInteractiveMessageBox(message, buttons); + pushGuiMode(GM_InterMessageBox); } } From ba2301a1566e0655cbcb0d57d658ca7d3c145d39 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Thu, 27 Dec 2012 00:49:39 +0000 Subject: [PATCH 234/378] removed redundant code thanks to scrawl --- apps/openmw/mwscript/containerextensions.cpp | 27 +------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 07cf3b9e3..813ac555f 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -115,32 +115,7 @@ namespace MWScript { if (toLower(iter->getCellRef().mRefID) == toLower(item)) { - switch(iter.getType()){ - case MWWorld::ContainerStore::Type_Potion: - itemName = iter->get()->mBase->mName; break; - case MWWorld::ContainerStore::Type_Apparatus: - itemName = iter->get()->mBase->mName; break; - case MWWorld::ContainerStore::Type_Armor: - itemName = iter->get()->mBase->mName; break; - case MWWorld::ContainerStore::Type_Book: - itemName = iter->get()->mBase->mName; break; - case MWWorld::ContainerStore::Type_Clothing: - itemName = iter->get()->mBase->mName; break; - case MWWorld::ContainerStore::Type_Ingredient: - itemName = iter->get()->mBase->mName; break; - case MWWorld::ContainerStore::Type_Light: - itemName = iter->get()->mBase->mName; break; - case MWWorld::ContainerStore::Type_Lockpick: - itemName = iter->get()->mBase->mName; break; - case MWWorld::ContainerStore::Type_Miscellaneous: - itemName = iter->get()->mBase->mName; break; - case MWWorld::ContainerStore::Type_Probe: - itemName = iter->get()->mBase->mName; break; - case MWWorld::ContainerStore::Type_Repair: - itemName = iter->get()->mBase->mName; break; - case MWWorld::ContainerStore::Type_Weapon: - itemName = iter->get()->mBase->mName; break; - } + itemName = MWWorld::Class::get(*iter).getName(*iter); if (iter->getRefData().getCount()<=count) { From 2736e84e496220eb0f4e3381d190c1edba740e7f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 27 Dec 2012 11:04:53 +0100 Subject: [PATCH 235/378] updated credits file --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index 063e9940a..84a596a6b 100644 --- a/credits.txt +++ b/credits.txt @@ -26,6 +26,7 @@ Jacob Essex (Yacoby) Jannik Heller (scrawl) Jason Hooks (jhooks) Karl-Felix Glatzer (k1ll) +lazydev Leon Saunders (emoose) Lukasz Gromanowski (lgro) Marcin Hulist (Gohan) From ad9b86058b503cc1f3b4ee5821e2bc02f0bde9b0 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Thu, 27 Dec 2012 15:28:13 +0000 Subject: [PATCH 236/378] replaced explicit text with GMST entries, thanks to zinnschlag --- apps/openmw/mwgui/dialogue.cpp | 2 +- apps/openmw/mwgui/dialogue.hpp | 2 +- apps/openmw/mwscript/containerextensions.cpp | 27 ++++++++++++++++++-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index e2b9f461a..2809e842a 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -370,7 +370,7 @@ void DialogueWindow::addText(std::string text) mHistory->addDialogText("#B29154"+parseText(text)+"#B29154"); } -void DialogueWindow::addMessageBox(std::string text) +void DialogueWindow::addMessageBox(const std::string& text) { mHistory->addDialogText("\n#FFFFFF"+text+"#B29154"); } diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index c4b86e6c8..72bb6d19a 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -65,7 +65,7 @@ namespace MWGui void setKeywords(std::list keyWord); void removeKeyword(std::string keyWord); void addText(std::string text); - void addMessageBox(std::string text); + void addMessageBox(const std::string& text); void addTitle(std::string text); void askQuestion(std::string question); void goodbye(); diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 813ac555f..2ac18a554 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -3,6 +3,10 @@ #include +#include + +#include + #include #include @@ -102,13 +106,14 @@ namespace MWScript Interpreter::Type_Integer count = runtime[0].mInteger; runtime.pop(); - + if (count<0) throw std::runtime_error ("second argument for RemoveItem must be non-negative"); MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); std::string itemName = ""; + Interpreter::Type_Integer originalCount = count; for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end() && count; ++iter) @@ -129,8 +134,26 @@ namespace MWScript } } } + + /* The two GMST entries below expand to strings informing the player of what, and how many of it has been removed from their inventory */ + std::string msgBox; + if(originalCount - count > 1) + { + msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage63}"); + std::stringstream temp; + temp << boost::format(msgBox) % (originalCount - count) % itemName; + msgBox = temp.str(); + } + else + { + msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage62}"); + std::stringstream temp; + temp << boost::format(msgBox) % itemName; + msgBox = temp.str(); + } - MWBase::Environment::get().getWindowManager()->messageBox(itemName + " has been removed from your inventory.", std::vector()); + if(originalCount - count > 0) + MWBase::Environment::get().getWindowManager()->messageBox(msgBox, std::vector()); // To be fully compatible with original Morrowind, we would need to check if // count is >= 0 here and throw an exception. But let's be tollerant instead. From 8545667bbde8579e596ff1d302ab673504b4175b Mon Sep 17 00:00:00 2001 From: eduard Date: Fri, 28 Dec 2012 17:54:56 +0100 Subject: [PATCH 237/378] string compare and tolower --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 78 +++++++------------ apps/openmw/mwgui/inventorywindow.cpp | 15 +--- apps/openmw/mwscript/containerextensions.cpp | 17 +--- apps/openmw/mwworld/containerstore.cpp | 18 ++--- apps/openmw/mwworld/worldimp.cpp | 16 +--- components/esm_store/reclists.hpp | 41 ++++------ components/misc/stringops.cpp | 37 +++++++++ components/misc/stringops.hpp | 9 +++ 8 files changed, 101 insertions(+), 130 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 62f7df679..2c30ebf06 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -41,30 +41,6 @@ namespace { - std::string toLower (const std::string& name) - { - std::string lowerCase; - - std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); - - return lowerCase; - } - - bool stringCompareNoCase (std::string first, std::string second) - { - unsigned int i=0; - while ( (itolower(second[i])) return false; - ++i; - } - if (first.length() bool selectCompare (char comp, T1 value1, T2 value2) @@ -72,7 +48,7 @@ namespace switch (comp) { case '0': return value1==value2; - case '1': return value1!=value2; +// case '1': return value1!=value2; case '2': return value1>value2; case '3': return value1>=value2; case '4': return value1first; - if(PCstats.getFactionRanks().find(toLower(NPCFaction)) != PCstats.getFactionRanks().end()) sameFaction = 1; + if(PCstats.getFactionRanks().find(Misc::toLower(NPCFaction)) != PCstats.getFactionRanks().end()) sameFaction = 1; } if(!selectCompare(comp,sameFaction,select.mI)) return false; } @@ -307,12 +283,12 @@ namespace MWDialogue if (select.mType==ESM::VT_Short || select.mType==ESM::VT_Int || select.mType==ESM::VT_Long) { - if (!checkGlobal (comp, toLower (name), select.mI)) + if (!checkGlobal (comp, Misc::toLower (name), select.mI)) return false; } else if (select.mType==ESM::VT_Float) { - if (!checkGlobal (comp, toLower (name), select.mF)) + if (!checkGlobal (comp, Misc::toLower (name), select.mF)) return false; } else @@ -326,13 +302,13 @@ namespace MWDialogue if (select.mType==ESM::VT_Short || select.mType==ESM::VT_Int || select.mType==ESM::VT_Long) { - if (!checkLocal (comp, toLower (name), select.mI, actor, + if (!checkLocal (comp, Misc::toLower (name), select.mI, actor, MWBase::Environment::get().getWorld()->getStore())) return false; } else if (select.mType==ESM::VT_Float) { - if (!checkLocal (comp, toLower (name), select.mF, actor, + if (!checkLocal (comp, Misc::toLower (name), select.mF, actor, MWBase::Environment::get().getWorld()->getStore())) return false; } @@ -345,7 +321,7 @@ namespace MWDialogue case '4'://journal if(select.mType==ESM::VT_Int) { - if(!selectCompare(comp,MWBase::Environment::get().getJournal()->getJournalIndex(toLower(name)),select.mI)) return false; + if(!selectCompare(comp,MWBase::Environment::get().getJournal()->getJournalIndex(Misc::toLower(name)),select.mI)) return false; } else throw std::runtime_error ( @@ -361,7 +337,7 @@ namespace MWDialogue int sum = 0; for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) - if (toLower(iter->getCellRef().mRefID) == toLower(name)) + if (Misc::toLower(iter->getCellRef().mRefID) == Misc::toLower(name)) sum += iter->getRefData().getCount(); if(!selectCompare(comp,sum,select.mI)) return false; } @@ -375,7 +351,7 @@ namespace MWDialogue case '7':// not ID if(select.mType==ESM::VT_String ||select.mType==ESM::VT_Int)//bug in morrowind here? it's not a short, it's a string { - int isID = int(toLower(name)==toLower(MWWorld::Class::get (actor).getId (actor))); + int isID = int(Misc::toLower(name)==Misc::toLower(MWWorld::Class::get (actor).getId (actor))); if (selectCompare(comp,!isID,select.mI)) return false; } else @@ -391,7 +367,7 @@ namespace MWDialogue if(select.mType==ESM::VT_Int) { MWWorld::LiveCellRef* npc = actor.get(); - int isFaction = int(toLower(npc->base->mFaction) == toLower(name)); + int isFaction = int(Misc::toLower(npc->base->mFaction) == Misc::toLower(name)); if(selectCompare(comp,!isFaction,select.mI)) return false; } @@ -408,7 +384,7 @@ namespace MWDialogue if(select.mType==ESM::VT_Int) { MWWorld::LiveCellRef* npc = actor.get(); - int isClass = int(toLower(npc->base->mClass) == toLower(name)); + int isClass = int(Misc::toLower(npc->base->mClass) == Misc::toLower(name)); if(selectCompare(comp,!isClass,select.mI)) return false; } @@ -425,7 +401,7 @@ namespace MWDialogue if(select.mType==ESM::VT_Int) { MWWorld::LiveCellRef* npc = actor.get(); - int isRace = int(toLower(npc->base->mRace) == toLower(name)); + int isRace = int(Misc::toLower(npc->base->mRace) == Misc::toLower(name)); if(selectCompare(comp,!isRace,select.mI)) return false; } @@ -438,7 +414,7 @@ namespace MWDialogue case 'B'://not Cell if(select.mType==ESM::VT_Int) { - int isCell = int(toLower(actor.getCell()->cell->mName) == toLower(name)); + int isCell = int(Misc::toLower(actor.getCell()->cell->mName) == Misc::toLower(name)); if(selectCompare(comp,!isCell,select.mI)) return false; } @@ -451,13 +427,13 @@ namespace MWDialogue if (select.mType==ESM::VT_Short || select.mType==ESM::VT_Int || select.mType==ESM::VT_Long) { - if (checkLocal (comp, toLower (name), select.mI, actor, + if (checkLocal (comp, Misc::toLower (name), select.mI, actor, MWBase::Environment::get().getWorld()->getStore())) return false; } else if (select.mType==ESM::VT_Float) { - if (checkLocal (comp, toLower (name), select.mF, actor, + if (checkLocal (comp, Misc::toLower (name), select.mF, actor, MWBase::Environment::get().getWorld()->getStore())) return false; } @@ -482,7 +458,7 @@ namespace MWDialogue // actor id if (!info.mActor.empty()) - if (toLower (info.mActor)!=MWWorld::Class::get (actor).getId (actor)) + if (Misc::toLower (info.mActor)!=MWWorld::Class::get (actor).getId (actor)) return false; //NPC race @@ -496,7 +472,7 @@ namespace MWDialogue if (!cellRef) return false; - if (toLower (info.mRace)!=toLower (cellRef->base->mRace)) + if (Misc::toLower (info.mRace)!=Misc::toLower (cellRef->base->mRace)) return false; } @@ -511,7 +487,7 @@ namespace MWDialogue if (!cellRef) return false; - if (toLower (info.mClass)!=toLower (cellRef->base->mClass)) + if (Misc::toLower (info.mClass)!=Misc::toLower (cellRef->base->mClass)) return false; } @@ -523,7 +499,7 @@ namespace MWDialogue //MWWorld::Class npcClass = MWWorld::Class::get(actor); MWMechanics::NpcStats stats = MWWorld::Class::get(actor).getNpcStats(actor); - std::map::iterator it = stats.getFactionRanks().find(toLower(info.mNpcFaction)); + std::map::iterator it = stats.getFactionRanks().find(Misc::toLower(info.mNpcFaction)); if(it!=stats.getFactionRanks().end()) { //check rank @@ -540,7 +516,7 @@ namespace MWDialogue if(!info.mPcFaction.empty()) { MWMechanics::NpcStats stats = MWWorld::Class::get(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()).getNpcStats(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - std::map::iterator it = stats.getFactionRanks().find(toLower(info.mPcFaction)); + std::map::iterator it = stats.getFactionRanks().find(Misc::toLower(info.mPcFaction)); if(it!=stats.getFactionRanks().end()) { //check rank @@ -593,13 +569,13 @@ namespace MWDialogue ESMS::RecListCaseT::MapType dialogueList = MWBase::Environment::get().getWorld()->getStore().dialogs.list; for(ESMS::RecListCaseT::MapType::iterator it = dialogueList.begin(); it!=dialogueList.end();it++) { - mDialogueMap[toLower(it->first)] = it->second; + mDialogueMap[Misc::toLower(it->first)] = it->second; } } void DialogueManager::addTopic (const std::string& topic) { - mKnownTopics[toLower(topic)] = true; + mKnownTopics[Misc::toLower(topic)] = true; } void DialogueManager::parseText (std::string text) @@ -753,9 +729,9 @@ namespace MWDialogue { if (isMatching (mActor, *iter) && functionFilter(mActor,*iter,true)) { - mActorKnownTopics.push_back(toLower(it->first)); + mActorKnownTopics.push_back(Misc::toLower(it->first)); //does the player know the topic? - if(mKnownTopics.find(toLower(it->first)) != mKnownTopics.end()) + if(mKnownTopics.find(Misc::toLower(it->first)) != mKnownTopics.end()) { keywordList.push_back(it->first); break; @@ -813,7 +789,7 @@ namespace MWDialogue win->setServices (windowServices); // sort again, because the previous sort was case-sensitive - keywordList.sort(stringCompareNoCase); + keywordList.sort(Misc::stringCompareNoCase); win->setKeywords(keywordList); mChoice = choice; @@ -907,7 +883,7 @@ namespace MWDialogue { MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); win->askQuestion(question); - mChoiceMap[toLower(question)] = choice; + mChoiceMap[Misc::toLower(question)] = choice; mIsInChoice = true; } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index bb3dc67e6..1ac5b2fc6 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -24,19 +24,6 @@ #include "scrollwindow.hpp" #include "spellwindow.hpp" -namespace -{ - std::string toLower (const std::string& name) - { - std::string lowerCase; - - std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); - - return lowerCase; - } -} - namespace MWGui { @@ -284,7 +271,7 @@ namespace MWGui for (MWWorld::ContainerStoreIterator it = invStore.begin(); it != invStore.end(); ++it) { - if (toLower(it->getCellRef().mRefID) == "gold_001") + if (Misc::toLower(it->getCellRef().mRefID) == "gold_001") return it->getRefData().getCount(); } return 0; diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 5e9746b2f..725885e41 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -18,19 +18,6 @@ #include "interpretercontext.hpp" #include "ref.hpp" -namespace -{ - std::string toLower (const std::string& name) - { - std::string lowerCase; - - std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); - - return lowerCase; - } -} - namespace MWScript { namespace Container @@ -78,7 +65,7 @@ namespace MWScript Interpreter::Type_Integer sum = 0; for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) - if (toLower(iter->getCellRef().mRefID) == toLower(item)) + if (Misc::toLower(iter->getCellRef().mRefID) == Misc::toLower(item)) sum += iter->getRefData().getCount(); runtime.push (sum); @@ -108,7 +95,7 @@ namespace MWScript for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end() && count; ++iter) { - if (toLower(iter->getCellRef().mRefID) == toLower(item)) + if (Misc::toLower(iter->getCellRef().mRefID) == Misc::toLower(item)) { if (iter->getRefData().getCount()<=count) { diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 5c4dd03a4..f736a5253 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -34,12 +34,6 @@ namespace return sum; } - - bool compare_string_ci(std::string str1, std::string str2) - { - boost::algorithm::to_lower(str1); - return str1 == str2; - } } MWWorld::ContainerStore::ContainerStore() : mStateId (0), mCachedWeight (0), mWeightUpToDate (false) {} @@ -81,11 +75,11 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr) MWWorld::LiveCellRef *gold = ptr.get(); - if (compare_string_ci(gold->ref.mRefID, "gold_001") - || compare_string_ci(gold->ref.mRefID, "gold_005") - || compare_string_ci(gold->ref.mRefID, "gold_010") - || compare_string_ci(gold->ref.mRefID, "gold_025") - || compare_string_ci(gold->ref.mRefID, "gold_100")) + if (Misc::compare_string_ci(gold->ref.mRefID, "gold_001") + || Misc::compare_string_ci(gold->ref.mRefID, "gold_005") + || Misc::compare_string_ci(gold->ref.mRefID, "gold_010") + || Misc::compare_string_ci(gold->ref.mRefID, "gold_025") + || Misc::compare_string_ci(gold->ref.mRefID, "gold_100")) { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), "Gold_001"); @@ -93,7 +87,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr) ref.getPtr().getRefData().setCount(count); for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) { - if (compare_string_ci((*iter).get()->ref.mRefID, "gold_001")) + if (Misc::compare_string_ci((*iter).get()->ref.mRefID, "gold_001")) { (*iter).getRefData().setCount( (*iter).getRefData().getCount() + count); flagAsModified(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c7f0de245..fe8aeb90b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -228,12 +228,12 @@ namespace MWWorld return cell; // didn't work -> now check for regions - std::string cellName2 = ESMS::RecListT::toLower (cellName); + std::string cellName2 = Misc::toLower (cellName); for (ESMS::RecListT::MapType::const_iterator iter (mStore.regions.list.begin()); iter!=mStore.regions.list.end(); ++iter) { - if (ESMS::RecListT::toLower (iter->second.mName)==cellName2) + if (Misc::toLower (iter->second.mName)==cellName2) { if (const ESM::Cell *cell = mStore.cells.searchExtByRegion (iter->first)) return cell; @@ -563,16 +563,6 @@ namespace MWWorld } } - std::string toLower (const std::string& name) - { - std::string lowerCase; - - std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); - - return lowerCase; - } - void World::moveObject(const Ptr &ptr, CellStore &newCell, float x, float y, float z) { ESM::Position &pos = ptr.getRefData().getPosition(); @@ -585,7 +575,7 @@ namespace MWWorld if (*currCell != newCell) { if (isPlayer) { if (!newCell.isExterior()) { - changeToInteriorCell(toLower(newCell.cell->mName), pos); + changeToInteriorCell(Misc::toLower(newCell.cell->mName), pos); } else { int cellX = newCell.cell->mData.mX; int cellY = newCell.cell->mData.mY; diff --git a/components/esm_store/reclists.hpp b/components/esm_store/reclists.hpp index 0b265a566..f9f74244b 100644 --- a/components/esm_store/reclists.hpp +++ b/components/esm_store/reclists.hpp @@ -28,15 +28,6 @@ namespace ESMS virtual int getSize() = 0; virtual void listIdentifier (std::vector& identifier) const = 0; - static std::string toLower (const std::string& name) - { - std::string lowerCase; - - std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); - - return lowerCase; - } }; typedef std::map RecListList; @@ -53,14 +44,14 @@ namespace ESMS // Load one object of this type void load(ESMReader &esm, const std::string &id) { - std::string id2 = toLower (id); + std::string id2 = Misc::toLower (id); list[id2].load(esm); } // Find the given object ID, or return NULL if not found. const X* search(const std::string &id) const { - std::string id2 = toLower (id); + std::string id2 = Misc::toLower (id); typename MapType::const_iterator iter = list.find (id2); @@ -104,7 +95,7 @@ namespace ESMS // Load one object of this type void load(ESMReader &esm, const std::string &id) { - //std::string id2 = toLower (id); + //std::string id2 = Misc::toLower (id); list[id].load(esm); } @@ -112,12 +103,12 @@ namespace ESMS // Find the given object ID, or return NULL if not found. const X* search(const std::string &id) const { - std::string id2 = toLower (id); + std::string id2 = Misc::toLower (id); for (typename MapType::const_iterator iter = list.begin(); iter != list.end(); ++iter) { - if (toLower(iter->first) == id2) + if (Misc::toLower(iter->first) == id2) return &iter->second; } @@ -127,12 +118,12 @@ namespace ESMS // non-const version X* search(const std::string &id) { - std::string id2 = toLower (id); + std::string id2 = Misc::toLower (id); for (typename MapType::iterator iter = list.begin(); iter != list.end(); ++iter) { - if (toLower(iter->first) == id2) + if (Misc::toLower(iter->first) == id2) return &iter->second; } @@ -172,7 +163,7 @@ namespace ESMS // Load one object of this type void load(ESMReader &esm, const std::string &id) { - std::string id2 = toLower (id); + std::string id2 = Misc::toLower (id); list[id2].mId = id2; list[id2].load(esm); } @@ -180,7 +171,7 @@ namespace ESMS // Find the given object ID, or return NULL if not found. const X* search(const std::string &id) const { - std::string id2 = toLower (id); + std::string id2 = Misc::toLower (id); typename MapType::const_iterator iter = list.find (id2); @@ -223,7 +214,7 @@ namespace ESMS void load(ESMReader &esm, const std::string &id) { - std::string id2 = toLower (id); + std::string id2 = Misc::toLower (id); X& ref = list[id2]; ref.mId = id; @@ -233,7 +224,7 @@ namespace ESMS // Find the given object ID, or return NULL if not found. const X* search(const std::string &id) const { - std::string id2 = toLower (id); + std::string id2 = Misc::toLower (id); typename MapType::const_iterator iter = list.find (id2); @@ -440,7 +431,7 @@ namespace ESMS { for (ExtCells::const_iterator iter = extCells.begin(); iter!=extCells.end(); ++iter) { - if (toLower (iter->second->mName) == toLower (id)) + if (Misc::toLower (iter->second->mName) == Misc::toLower (id)) return iter->second; } @@ -449,10 +440,10 @@ namespace ESMS const ESM::Cell *searchExtByRegion (const std::string& id) const { - std::string id2 = toLower (id); + std::string id2 = Misc::toLower (id); for (ExtCells::const_iterator iter = extCells.begin(); iter!=extCells.end(); ++iter) - if (toLower (iter->second->mRegion)==id) + if (Misc::toLower (iter->second->mRegion)==id) return iter->second; return 0; @@ -586,7 +577,7 @@ namespace ESMS X ref; ref.load (esm); - std::string realId = toLower (ref.mData.mName.toString()); + std::string realId = Misc::toLower (ref.mData.mName.toString()); std::swap (list[realId], ref); } @@ -594,7 +585,7 @@ namespace ESMS // Find the given object ID, or return NULL if not found. const X* search(const std::string &id) const { - std::string id2 = toLower (id); + std::string id2 = Misc::toLower (id); typename MapType::const_iterator iter = list.find (id2); diff --git a/components/misc/stringops.cpp b/components/misc/stringops.cpp index 53eed1fdc..f209bf46b 100644 --- a/components/misc/stringops.cpp +++ b/components/misc/stringops.cpp @@ -1,7 +1,14 @@ #include "stringops.hpp" +#include +#include +#include + #include #include +#include + + namespace Misc { @@ -61,4 +68,34 @@ bool iends(const char* str1, const char* str2) return strcasecmp(str2, str1+len1-len2) == 0; } +std::string toLower (const std::string& name) +{ + std::string lowerCase; + + std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), + (int(*)(int)) std::tolower); + + return lowerCase; +} + +bool stringCompareNoCase (std::string first, std::string second) +{ + unsigned int i=0; + while ( (itolower(second[i])) return false; + ++i; + } + if (first.length() + namespace Misc { @@ -16,6 +18,13 @@ bool ibegins(const char* str1, const char* str2); /// Case insensitive, returns true if str1 ends with substring str2 bool iends(const char* str1, const char* str2); + +std::string toLower (const std::string& name); + +bool stringCompareNoCase (std::string first, std::string second); + +bool compare_string_ci (std::string first, std::string second); + } #endif From c75a5ae212bc01d1b3d37ffcf0f3cf879f7775a4 Mon Sep 17 00:00:00 2001 From: eduard Date: Fri, 28 Dec 2012 18:05:52 +0100 Subject: [PATCH 238/378] string compare and tolower --- apps/openmw/mwworld/containerstore.cpp | 18 ++++++++++++------ components/misc/stringops.cpp | 7 +------ components/misc/stringops.hpp | 5 ++--- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index f736a5253..5c4dd03a4 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -34,6 +34,12 @@ namespace return sum; } + + bool compare_string_ci(std::string str1, std::string str2) + { + boost::algorithm::to_lower(str1); + return str1 == str2; + } } MWWorld::ContainerStore::ContainerStore() : mStateId (0), mCachedWeight (0), mWeightUpToDate (false) {} @@ -75,11 +81,11 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr) MWWorld::LiveCellRef *gold = ptr.get(); - if (Misc::compare_string_ci(gold->ref.mRefID, "gold_001") - || Misc::compare_string_ci(gold->ref.mRefID, "gold_005") - || Misc::compare_string_ci(gold->ref.mRefID, "gold_010") - || Misc::compare_string_ci(gold->ref.mRefID, "gold_025") - || Misc::compare_string_ci(gold->ref.mRefID, "gold_100")) + if (compare_string_ci(gold->ref.mRefID, "gold_001") + || compare_string_ci(gold->ref.mRefID, "gold_005") + || compare_string_ci(gold->ref.mRefID, "gold_010") + || compare_string_ci(gold->ref.mRefID, "gold_025") + || compare_string_ci(gold->ref.mRefID, "gold_100")) { MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), "Gold_001"); @@ -87,7 +93,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr) ref.getPtr().getRefData().setCount(count); for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) { - if (Misc::compare_string_ci((*iter).get()->ref.mRefID, "gold_001")) + if (compare_string_ci((*iter).get()->ref.mRefID, "gold_001")) { (*iter).getRefData().setCount( (*iter).getRefData().getCount() + count); flagAsModified(); diff --git a/components/misc/stringops.cpp b/components/misc/stringops.cpp index f209bf46b..51cf2105a 100644 --- a/components/misc/stringops.cpp +++ b/components/misc/stringops.cpp @@ -6,7 +6,6 @@ #include #include -#include @@ -92,10 +91,6 @@ bool stringCompareNoCase (std::string first, std::string second) else return false; } -bool compare_string_ci(std::string str1, std::string str2) -{ - boost::algorithm::to_lower(str1); - return str1 == str2; -} + } diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index 6403a7128..4dbfd40d4 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -18,13 +18,12 @@ bool ibegins(const char* str1, const char* str2); /// Case insensitive, returns true if str1 ends with substring str2 bool iends(const char* str1, const char* str2); - +/// std::string toLower (const std::string& name); +/// Case fold compare bool stringCompareNoCase (std::string first, std::string second); -bool compare_string_ci (std::string first, std::string second); - } #endif From ade4ec04532382fec14d6963324be55731911672 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 28 Dec 2012 19:01:47 +0100 Subject: [PATCH 239/378] fix texture edge bleeding due to wrong addressing mode --- apps/openmw/mwrender/videoplayer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 8e05a4f82..2d24edc51 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -1019,7 +1019,8 @@ VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) mVideoMaterial->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); mVideoMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false); mVideoMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(); - } + mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP); + } mVideoMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTextureName("black.png"); Ogre::MaterialPtr blackMaterial = Ogre::MaterialManager::getSingleton().getByName("BlackBarsMaterial", "General"); From 1dd9276cebdd6035f6c0071d5dea1a28b873d4d6 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 28 Dec 2012 11:26:41 -0800 Subject: [PATCH 240/378] Add missing decoder method declarations --- apps/openmw/mwsound/audiere_decoder.hpp | 1 + apps/openmw/mwsound/mpgsnd_decoder.hpp | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/openmw/mwsound/audiere_decoder.hpp b/apps/openmw/mwsound/audiere_decoder.hpp index 8623a3f2c..91c07ccac 100644 --- a/apps/openmw/mwsound/audiere_decoder.hpp +++ b/apps/openmw/mwsound/audiere_decoder.hpp @@ -21,6 +21,7 @@ namespace MWSound virtual void open(const std::string &fname); virtual void close(); + virtual std::string getName(); virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); virtual size_t read(char *buffer, size_t bytes); diff --git a/apps/openmw/mwsound/mpgsnd_decoder.hpp b/apps/openmw/mwsound/mpgsnd_decoder.hpp index 52c37bb87..be52f6f49 100644 --- a/apps/openmw/mwsound/mpgsnd_decoder.hpp +++ b/apps/openmw/mwsound/mpgsnd_decoder.hpp @@ -34,6 +34,7 @@ namespace MWSound virtual void open(const std::string &fname); virtual void close(); + virtual std::string getName(); virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); virtual size_t read(char *buffer, size_t bytes); From bed8fb69e65048166cef300ffe2bbe2354def3d3 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Fri, 28 Dec 2012 22:26:21 +0000 Subject: [PATCH 241/378] added bounty related scripting functions --- apps/openmw/mwscript/docs/vmformat.txt | 5 ++- apps/openmw/mwscript/statsextensions.cpp | 52 ++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 835a3e3ff..df1a52329 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -291,8 +291,11 @@ op 0x20001e8: RaiseRank op 0x20001e9: RaiseRank, explicit op 0x20001ea: LowerRank op 0x20001eb: LowerRank, explicit +op 0x20001ec: GetPCCrimeLevel +op 0x20001ed: SetPCCrimeLevel +op 0x20001ee: SetPCCrimeLevel -opcodes 0x20001ec-0x3ffffff unused +opcodes 0x20001ef-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index c67782168..9e630aedf 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -392,6 +392,46 @@ namespace MWScript } }; + class OpGetPCCrimeLevel : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + MWWorld::Ptr player = world->getPlayer().getPlayer(); + runtime.push (static_cast (MWWorld::Class::get (player).getNpcStats (player).getBounty())); + } + }; + + class OpSetPCCrimeLevel : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + MWWorld::Ptr player = world->getPlayer().getPlayer(); + + MWWorld::Class::get (player).getNpcStats (player).setBounty(runtime[0].mFloat); + runtime.pop(); + } + }; + + class OpModPCCrimeLevel : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + MWWorld::Ptr player = world->getPlayer().getPlayer(); + + MWWorld::Class::get (player).getNpcStats (player).setBounty(runtime[0].mFloat + MWWorld::Class::get (player).getNpcStats (player).getBounty()); + runtime.pop(); + } + }; + template class OpAddSpell : public Interpreter::Opcode0 { @@ -1016,6 +1056,10 @@ namespace MWScript const int opcodeModSkill = 0x20000fa; const int opcodeModSkillExplicit = 0x2000115; + const int opcodeGetPCCrimeLevel = 0x20001ec; + const int opcodeSetPCCrimeLevel = 0x20001ed; + const int opcodeModPCCrimeLevel = 0x20001ee; + const int opcodeAddSpell = 0x2000147; const int opcodeAddSpellExplicit = 0x2000148; const int opcodeRemoveSpell = 0x2000149; @@ -1141,6 +1185,10 @@ namespace MWScript opcodeModSkill+i, opcodeModSkillExplicit+i); } + extensions.registerFunction ("getpccrimelevel", 'f', "", opcodeGetPCCrimeLevel); + extensions.registerInstruction ("setpccrimelevel", "f", opcodeSetPCCrimeLevel); + extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel); + extensions.registerInstruction ("addspell", "c", opcodeAddSpell, opcodeAddSpellExplicit); extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, opcodeRemoveSpellExplicit); @@ -1235,6 +1283,10 @@ namespace MWScript interpreter.installSegment5 (opcodeModSkillExplicit+i, new OpModSkill (i)); } + interpreter.installSegment5 (opcodeGetPCCrimeLevel, new OpGetPCCrimeLevel); + interpreter.installSegment5 (opcodeSetPCCrimeLevel, new OpSetPCCrimeLevel); + interpreter.installSegment5 (opcodeModPCCrimeLevel, new OpModPCCrimeLevel); + interpreter.installSegment5 (opcodeAddSpell, new OpAddSpell); interpreter.installSegment5 (opcodeAddSpellExplicit, new OpAddSpell); interpreter.installSegment5 (opcodeRemoveSpell, new OpRemoveSpell); From 8d4afe3bf5abfe2703a782eb5c7338a2d4efb0ce Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 28 Dec 2012 23:45:13 +0100 Subject: [PATCH 242/378] post merge fix --- apps/opencs/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 1cfb5d9a9..abbc953ca 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -6,6 +6,7 @@ set (OPENCS_SRC model/world/universalid.cpp model/world/idcollection.cpp model/world/data.cpp model/world/idtable.cpp model/world/commands.cpp model/world/idtableproxymodel.cpp model/world/record.cpp + model/world/columnbase.cpp model/tools/tools.cpp model/tools/operation.cpp model/tools/stage.cpp model/tools/verifier.cpp model/tools/mandatoryid.cpp model/tools/reportmodel.cpp @@ -26,7 +27,7 @@ set (OPENCS_HDR model/world/universalid.hpp model/world/record.hpp model/world/idcollection.hpp model/world/data.hpp model/world/idtable.hpp model/world/columns.hpp model/world/idtableproxymodel.hpp - model/world/commands.hpp + model/world/commands.hpp model/world/columnbase.hpp model/tools/tools.hpp model/tools/operation.hpp model/tools/stage.hpp model/tools/verifier.hpp model/tools/mandatoryid.hpp model/tools/reportmodel.hpp From 92623921add0d6e16a34973dcf6f2ee1f52dbbe7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 29 Dec 2012 09:23:06 +0100 Subject: [PATCH 243/378] updated credits file --- credits.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/credits.txt b/credits.txt index 84a596a6b..72451c3d0 100644 --- a/credits.txt +++ b/credits.txt @@ -37,7 +37,7 @@ Pieter van der Kloet (pvdk) Roman Melnik (Kromgart) Sebastian Wick (swick) Sylvain T. (Garvek) - +Tom Mason (wheybags) Packagers: Alexander Olofsson (Ace) - Windows From b2203d22fce8483ed2cbce48a55cb65d175445c9 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sat, 29 Dec 2012 15:22:29 +0100 Subject: [PATCH 244/378] mwiniimporter: added fallback values Ugly comments included. I will merge it on another branch to clean this up later. --- apps/mwiniimporter/importer.cpp | 564 ++++++++++++++++++++++++++++++++ 1 file changed, 564 insertions(+) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 19b69794f..1236dcaf0 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -18,8 +18,572 @@ MwIniImporter::MwIniImporter() { 0, 0 } }; const char *fallback[] = { + + /* + "Fonts:Font 0", + "Fonts:Font 1", + "Fonts:Font 2", + */ + + /* + "FontColor:color_normal", + "FontColor:color_normal_over", + "FontColor:color_normal_pressed", + "FontColor:color_active", + "FontColor:color_active_over", + "FontColor:color_active_pressed", + "FontColor:color_disabled", + "FontColor:color_disabled_over", + "FontColor:color_disabled_pressed", + "FontColor:color_link", + "FontColor:color_link_over", + "FontColor:color_link_pressed", + "FontColor:color_journal_link", + "FontColor:color_journal_link_over", + "FontColor:color_journal_link_pressed", + "FontColor:color_journal_topic", + "FontColor:color_journal_topic_over", + "FontColor:color_journal_topic_pressed", + "FontColor:color_answer", + "FontColor:color_answer_over", + "FontColor:color_answer_pressed", + "FontColor:color_header", + "FontColor:color_notify", + "FontColor:color_big_normal", + "FontColor:color_big_normal_over", + "FontColor:color_big_normal_pressed", + "FontColor:color_big_link", + "FontColor:color_big_link_over", + "FontColor:color_big_link_pressed", + "FontColor:color_big_answer", + "FontColor:color_big_answer_over", + "FontColor:color_big_answer_pressed", + "FontColor:color_big_header", + "FontColor:color_big_notify", + "FontColor:color_background", + "FontColor:color_focus", + "FontColor:color_health", + "FontColor:color_magic", + "FontColor:color_fatigue", + "FontColor:color_misc", + "FontColor:color_weapon_fill", + "FontColor:color_magic_fill", + "FontColor:color_positive", + "FontColor:color_negative", + "FontColor:color_count", + */ + + /* + "Cursors:Cursor 0", + "Cursors:Cursor 1", + "Cursors:Cursor 2", + "Cursors:Cursor 3", + "Cursors:Cursor 4", + */ + + // level up messages + // not used for now + "Level Up:Level2", + "Level Up:Level3", + "Level Up:Level4", + "Level Up:Level5", + "Level Up:Level6", + "Level Up:Level7", + "Level Up:Level8", + "Level Up:Level9", + "Level Up:Level10", + "Level Up:Level11", + "Level Up:Level12", + "Level Up:Level13", + "Level Up:Level14", + "Level Up:Level15", + "Level Up:Level16", + "Level Up:Level17", + "Level Up:Level18", + "Level Up:Level19", + "Level Up:Level20", + "Level Up:Default", + + // character creation multiple choice test + // hardcoded in mwgui/charactercreation.cpp + "Question 1:Question", + "Question 1:AnswerOne", + "Question 1:AnswerTwo", + "Question 1:AnswerThree", + "Question 1:Sound", + "Question 2:Question", + "Question 2:AnswerOne", + "Question 2:AnswerTwo", + "Question 2:AnswerThree", + "Question 2:Sound", + "Question 3:Question", + "Question 3:AnswerOne", + "Question 3:AnswerTwo", + "Question 3:AnswerThree", + "Question 3:Sound", + "Question 4:Question", + "Question 4:AnswerOne", + "Question 4:AnswerTwo", + "Question 4:AnswerThree", + "Question 4:Sound", + "Question 5:Question", + "Question 5:AnswerOne", + "Question 5:AnswerTwo", + "Question 5:AnswerThree", + "Question 5:Sound", + "Question 6:Question", + "Question 6:AnswerOne", + "Question 6:AnswerTwo", + "Question 6:AnswerThree", + "Question 6:Sound", + "Question 7:Question", + "Question 7:AnswerOne", + "Question 7:AnswerTwo", + "Question 7:AnswerThree", + "Question 7:Sound", + "Question 8:Question", + "Question 8:AnswerOne", + "Question 8:AnswerTwo", + "Question 8:AnswerThree", + "Question 8:Sound", + "Question 9:Question", + "Question 9:AnswerOne", + "Question 9:AnswerTwo", + "Question 9:AnswerThree", + "Question 9:Sound", + "Question 10:Question", + "Question 10:AnswerOne", + "Question 10:AnswerTwo", + "Question 10:AnswerThree", + "Question 10:Sound", + + // blood textures and models + // not used for now + "Blood:Model 0", + "Blood:Model 1", + "Blood:Model 2", + "Blood:Texture 0", + "Blood:Texture 1", + "Blood:Texture 2", + "Blood:Texture Name 0", + "Blood:Texture Name 1", + "Blood:Texture Name 2", + + // movies + // not used for now + "Movies:Company Logo", + "Movies:Morrowind Logo", + "Movies:New Game", + "Movies:Loading", + "Movies:Options Menu", + + // weather related values + // hardcoded in mwworld/wheather.cpp + // Some are easy to replace, others are used as magic number + + // globals + "Weather Thunderstorm:Thunder Sound ID 0", + "Weather Thunderstorm:Thunder Sound ID 1", + "Weather Thunderstorm:Thunder Sound ID 2", + "Weather Thunderstorm:Thunder Sound ID 3", "Weather:Sunrise Time", "Weather:Sunset Time", + "Weather:Sunrise Duration", + "Weather:Sunset Duration", + "Weather:Hours Between Weather Changes", // AKA weather update time + "Weather Thunderstorm:Thunder Frequency", + "Weather Thunderstorm:Thunder Threshold", + // thunder sound delay? + + "Weather Clear:Cloud Texture", + "Weather Clear:Clouds Maximum Percent", + "Weather Clear:Transition Delta", + "Weather Clear:Sky Sunrise Color", + "Weather Clear:Sky Day Color", + "Weather Clear:Sky Sunset Color", + "Weather Clear:Sky Night Color", + "Weather Clear:Fog Sunrise Color", + "Weather Clear:Fog Day Color", + "Weather Clear:Fog Sunset Color", + "Weather Clear:Fog Night Color", + "Weather Clear:Ambient Sunrise Color", + "Weather Clear:Ambient Day Color", + "Weather Clear:Ambient Sunset Color", + "Weather Clear:Ambient Night Color", + "Weather Clear:Sun Sunrise Color", + "Weather Clear:Sun Day Color", + "Weather Clear:Sun Sunset Color", + "Weather Clear:Sun Night Color", + "Weather Clear:Sun Disc Sunset Color", + "Weather Clear:Land Fog Day Depth", + "Weather Clear:Land Fog Night Depth", + "Weather Clear:Wind Speed", + "Weather Clear:Cloud Speed", + "Weather Clear:Glare View", + + "Weather Cloudy:Cloud Texture", + "Weather Cloudy:Clouds Maximum Percent", + "Weather Cloudy:Transition Delta", + "Weather Cloudy:Sky Sunrise Color", + "Weather Cloudy:Sky Day Color", + "Weather Cloudy:Sky Sunset Color", + "Weather Cloudy:Sky Night Color", + "Weather Cloudy:Fog Sunrise Color", + "Weather Cloudy:Fog Day Color", + "Weather Cloudy:Fog Sunset Color", + "Weather Cloudy:Fog Night Color", + "Weather Cloudy:Ambient Sunrise Color", + "Weather Cloudy:Ambient Day Color", + "Weather Cloudy:Ambient Sunset Color", + "Weather Cloudy:Ambient Night Color", + "Weather Cloudy:Sun Sunrise Color", + "Weather Cloudy:Sun Day Color", + "Weather Cloudy:Sun Sunset Color", + "Weather Cloudy:Sun Night Color", + "Weather Cloudy:Sun Disc Sunset Color", + "Weather Cloudy:Land Fog Day Depth", + "Weather Cloudy:Land Fog Night Depth", + "Weather Cloudy:Wind Speed", + "Weather Cloudy:Cloud Speed", + "Weather Cloudy:Glare View", + + "Weather Foggy:Cloud Texture", + "Weather Foggy:Clouds Maximum Percent", + "Weather Foggy:Transition Delta", + "Weather Foggy:Sky Sunrise Color", + "Weather Foggy:Sky Day Color", + "Weather Foggy:Sky Sunset Color", + "Weather Foggy:Sky Night Color", + "Weather Foggy:Fog Sunrise Color", + "Weather Foggy:Fog Day Color", + "Weather Foggy:Fog Sunset Color", + "Weather Foggy:Fog Night Color", + "Weather Foggy:Ambient Sunrise Color", + "Weather Foggy:Ambient Day Color", + "Weather Foggy:Ambient Sunset Color", + "Weather Foggy:Ambient Night Color", + "Weather Foggy:Sun Sunrise Color", + "Weather Foggy:Sun Day Color", + "Weather Foggy:Sun Sunset Color", + "Weather Foggy:Sun Night Color", + "Weather Foggy:Sun Disc Sunset Color", + "Weather Foggy:Land Fog Day Depth", + "Weather Foggy:Land Fog Night Depth", + "Weather Foggy:Wind Speed", + "Weather Foggy:Cloud Speed", + "Weather Foggy:Glare View", + + "Weather Thunderstorm:Cloud Texture", + "Weather Thunderstorm:Clouds Maximum Percent", + "Weather Thunderstorm:Transition Delta", + "Weather Thunderstorm:Sky Sunrise Color", + "Weather Thunderstorm:Sky Day Color", + "Weather Thunderstorm:Sky Sunset Color", + "Weather Thunderstorm:Sky Night Color", + "Weather Thunderstorm:Fog Sunrise Color", + "Weather Thunderstorm:Fog Day Color", + "Weather Thunderstorm:Fog Sunset Color", + "Weather Thunderstorm:Fog Night Color", + "Weather Thunderstorm:Ambient Sunrise Color", + "Weather Thunderstorm:Ambient Day Color", + "Weather Thunderstorm:Ambient Sunset Color", + "Weather Thunderstorm:Ambient Night Color", + "Weather Thunderstorm:Sun Sunrise Color", + "Weather Thunderstorm:Sun Day Color", + "Weather Thunderstorm:Sun Sunset Color", + "Weather Thunderstorm:Sun Night Color", + "Weather Thunderstorm:Sun Disc Sunset Color", + "Weather Thunderstorm:Land Fog Day Depth", + "Weather Thunderstorm:Land Fog Night Depth", + "Weather Thunderstorm:Wind Speed", + "Weather Thunderstorm:Cloud Speed", + "Weather Thunderstorm:Glare View", + "Weather Thunderstorm:Rain Loop Sound ID", + + "Weather Rain:Cloud Texture", + "Weather Rain:Clouds Maximum Percent", + "Weather Rain:Transition Delta", + "Weather Rain:Sky Sunrise Color", + "Weather Rain:Sky Day Color", + "Weather Rain:Sky Sunset Color", + "Weather Rain:Sky Night Color", + "Weather Rain:Fog Sunrise Color", + "Weather Rain:Fog Day Color", + "Weather Rain:Fog Sunset Color", + "Weather Rain:Fog Night Color", + "Weather Rain:Ambient Sunrise Color", + "Weather Rain:Ambient Day Color", + "Weather Rain:Ambient Sunset Color", + "Weather Rain:Ambient Night Color", + "Weather Rain:Sun Sunrise Color", + "Weather Rain:Sun Day Color", + "Weather Rain:Sun Sunset Color", + "Weather Rain:Sun Night Color", + "Weather Rain:Sun Disc Sunset Color", + "Weather Rain:Land Fog Day Depth", + "Weather Rain:Land Fog Night Depth", + "Weather Rain:Wind Speed", + "Weather Rain:Cloud Speed", + "Weather Rain:Glare View", + "Weather Rain:Rain Loop Sound ID", + + "Weather Overcast:Cloud Texture", + "Weather Overcast:Clouds Maximum Percent", + "Weather Overcast:Transition Delta", + "Weather Overcast:Sky Sunrise Color", + "Weather Overcast:Sky Day Color", + "Weather Overcast:Sky Sunset Color", + "Weather Overcast:Sky Night Color", + "Weather Overcast:Fog Sunrise Color", + "Weather Overcast:Fog Day Color", + "Weather Overcast:Fog Sunset Color", + "Weather Overcast:Fog Night Color", + "Weather Overcast:Ambient Sunrise Color", + "Weather Overcast:Ambient Day Color", + "Weather Overcast:Ambient Sunset Color", + "Weather Overcast:Ambient Night Color", + "Weather Overcast:Sun Sunrise Color", + "Weather Overcast:Sun Day Color", + "Weather Overcast:Sun Sunset Color", + "Weather Overcast:Sun Night Color", + "Weather Overcast:Sun Disc Sunset Color", + "Weather Overcast:Land Fog Day Depth", + "Weather Overcast:Land Fog Night Depth", + "Weather Overcast:Wind Speed", + "Weather Overcast:Cloud Speed", + "Weather Overcast:Glare View", + + "Weather Ashstorm:Cloud Texture", + "Weather Ashstorm:Clouds Maximum Percent", + "Weather Ashstorm:Transition Delta", + "Weather Ashstorm:Sky Sunrise Color", + "Weather Ashstorm:Sky Day Color", + "Weather Ashstorm:Sky Sunset Color", + "Weather Ashstorm:Sky Night Color", + "Weather Ashstorm:Fog Sunrise Color", + "Weather Ashstorm:Fog Day Color", + "Weather Ashstorm:Fog Sunset Color", + "Weather Ashstorm:Fog Night Color", + "Weather Ashstorm:Ambient Sunrise Color", + "Weather Ashstorm:Ambient Day Color", + "Weather Ashstorm:Ambient Sunset Color", + "Weather Ashstorm:Ambient Night Color", + "Weather Ashstorm:Sun Sunrise Color", + "Weather Ashstorm:Sun Day Color", + "Weather Ashstorm:Sun Sunset Color", + "Weather Ashstorm:Sun Night Color", + "Weather Ashstorm:Sun Disc Sunset Color", + "Weather Ashstorm:Land Fog Day Depth", + "Weather Ashstorm:Land Fog Night Depth", + "Weather Ashstorm:Wind Speed", + "Weather Ashstorm:Cloud Speed", + "Weather Ashstorm:Glare View", + "Weather Ashstorm:Ambient Loop Sound ID", + + "Weather Blight:Cloud Texture", + "Weather Blight:Clouds Maximum Percent", + "Weather Blight:Transition Delta", + "Weather Blight:Sky Sunrise Color", + "Weather Blight:Sky Day Color", + "Weather Blight:Sky Sunset Color", + "Weather Blight:Sky Night Color", + "Weather Blight:Fog Sunrise Color", + "Weather Blight:Fog Day Color", + "Weather Blight:Fog Sunset Color", + "Weather Blight:Fog Night Color", + "Weather Blight:Ambient Sunrise Color", + "Weather Blight:Ambient Day Color", + "Weather Blight:Ambient Sunset Color", + "Weather Blight:Ambient Night Color", + "Weather Blight:Sun Sunrise Color", + "Weather Blight:Sun Day Color", + "Weather Blight:Sun Sunset Color", + "Weather Blight:Sun Night Color", + "Weather Blight:Sun Disc Sunset Color", + "Weather Blight:Land Fog Day Depth", + "Weather Blight:Land Fog Night Depth", + "Weather Blight:Wind Speed", + "Weather Blight:Cloud Speed", + "Weather Blight:Glare View", + "Weather Blight:Ambient Loop Sound ID", + + // not used for now (todo) + "Weather Blight:Disease Chance", + + // for Bloodmoon + "Weather Snow:Cloud Texture", + "Weather Snow:Clouds Maximum Percent", + "Weather Snow:Transition Delta", + "Weather Snow:Sky Sunrise Color", + "Weather Snow:Sky Day Color", + "Weather Snow:Sky Sunset Color", + "Weather Snow:Sky Night Color", + "Weather Snow:Fog Sunrise Color", + "Weather Snow:Fog Day Color", + "Weather Snow:Fog Sunset Color", + "Weather Snow:Fog Night Color", + "Weather Snow:Ambient Sunrise Color", + "Weather Snow:Ambient Day Color", + "Weather Snow:Ambient Sunset Color", + "Weather Snow:Ambient Night Color", + "Weather Snow:Sun Sunrise Color", + "Weather Snow:Sun Day Color", + "Weather Snow:Sun Sunset Color", + "Weather Snow:Sun Night Color", + "Weather Snow:Sun Disc Sunset Color", + "Weather Snow:Land Fog Day Depth", + "Weather Snow:Land Fog Night Depth", + "Weather Snow:Wind Speed", + "Weather Snow:Cloud Speed", + "Weather Snow:Glare View", + + // for Bloodmoon + "Weather Blizzard:Cloud Texture", + "Weather Blizzard:Clouds Maximum Percent", + "Weather Blizzard:Transition Delta", + "Weather Blizzard:Sky Sunrise Color", + "Weather Blizzard:Sky Day Color", + "Weather Blizzard:Sky Sunset Color", + "Weather Blizzard:Sky Night Color", + "Weather Blizzard:Fog Sunrise Color", + "Weather Blizzard:Fog Day Color", + "Weather Blizzard:Fog Sunset Color", + "Weather Blizzard:Fog Night Color", + "Weather Blizzard:Ambient Sunrise Color", + "Weather Blizzard:Ambient Day Color", + "Weather Blizzard:Ambient Sunset Color", + "Weather Blizzard:Ambient Night Color", + "Weather Blizzard:Sun Sunrise Color", + "Weather Blizzard:Sun Day Color", + "Weather Blizzard:Sun Sunset Color", + "Weather Blizzard:Sun Night Color", + "Weather Blizzard:Sun Disc Sunset Color", + "Weather Blizzard:Land Fog Day Depth", + "Weather Blizzard:Land Fog Night Depth", + "Weather Blizzard:Wind Speed", + "Weather Blizzard:Cloud Speed", + "Weather Blizzard:Glare View", + "Weather Blizzard:Ambient Loop Sound ID", + + // not used + "Weather Clear:Ambient Loop Sound ID", + "Weather Cloudy:Ambient Loop Sound ID", + "Weather Foggy:Ambient Loop Sound ID", + "Weather Overcast:Ambient Loop Sound ID", + "Weather Snow:Ambient Loop Sound ID", + // + "Weather Ashstorm:Storm Threshold", + "Weather Blight:Storm Threshold", + "Weather Snow:Snow Threshold", + "Weather Blizzard:Storm Threshold", + // + "Weather Snow:Snow Diameter", + "Weather Snow:Snow Height Min", + "Weather Snow:Snow Height Max", + "Weather Snow:Snow Entrance Speed", + "Weather Snow:Max Snowflakes", + // + "Weather:EnvReduceColor", + "Weather:LerpCloseColor", + "Weather:BumpFadeColor", + "Weather:AlphaReduce", + "Weather:Minimum Time Between Environmental Sounds", + "Weather:Maximum Time Between Environmental Sounds", + "Weather:Sun Glare Fader Max", + "Weather:Sun Glare Fader Angle Max", + "Weather:Sun Glare Fader Color", + "Weather:Timescale Clouds", + "Weather:Precip Gravity", + "Weather:Rain Ripples", + "Weather:Rain Ripple Radius", + "Weather:Rain Ripples Per Drop", + "Weather:Rain Ripple Scale", + "Weather:Rain Ripple Speed", + "Weather:Fog Depth Change Speed", + "Weather:Sky Pre-Sunrise Time", + "Weather:Sky Post-Sunrise Time", + "Weather:Sky Pre-Sunset Time", + "Weather:Sky Post-Sunset Time", + "Weather:Ambient Pre-Sunrise Time", + "Weather:Ambient Post-Sunrise Time", + "Weather:Ambient Pre-Sunset Time", + "Weather:Ambient Post-Sunset Time", + "Weather:Fog Pre-Sunrise Time", + "Weather:Fog Post-Sunrise Time", + "Weather:Fog Pre-Sunset Time", + "Weather:Fog Post-Sunset Time", + "Weather:Sun Pre-Sunrise Time", + "Weather:Sun Post-Sunrise Time", + "Weather:Sun Pre-Sunset Time", + "Weather:Sun Post-Sunset Time", + "Weather:Stars Post-Sunset Start", + "Weather:Stars Pre-Sunrise Finish", + "Weather:Stars Fading Duration", + "Weather:Snow Ripples", + "Weather:Snow Ripple Radius", + "Weather:Snow Ripples Per Flake", + "Weather:Snow Ripple Scale", + "Weather:Snow Ripple Speed", + "Weather:Snow Gravity Scale", + "Weather:Snow High Kill", + "Weather:Snow Low Kill", + // + "Weather Rain:Using Precip", + "Weather Rain:Rain Diameter", + "Weather Rain:Rain Height Min", + "Weather Rain:Rain Height Max", + "Weather Rain:Rain Threshold", + "Weather Rain:Rain Entrance Speed", + "Weather Rain:Ambient Loop Sound ID", + "Weather Rain:Max Raindrops", + // + "Weather Thunderstorm:Using Precip", + "Weather Thunderstorm:Rain Diameter", + "Weather Thunderstorm:Rain Height Min", + "Weather Thunderstorm:Rain Height Max", + "Weather Thunderstorm:Rain Threshold", + "Weather Thunderstorm:Max Raindrops", + "Weather Thunderstorm:Rain Entrance Speed", + "Weather Thunderstorm:Ambient Loop Sound ID", + "Weather Thunderstorm:Flash Decrement", + + // moons + + // these are hardcoded in mwworld/weather.cpp + // l.642..665 (masser_angle_fade) + "Moons:Secunda Fade Start Angle", // = 50 + "Moons:Secunda Fade End Angle", // = 40 + "Moons:Masser Fade Start Angle", // = 50 + "Moons:Masser Fade End Angle", // = 30 + + // hardcoded in weather.cpp l.635 to 641 (hour_fade) + "Moons:Secunda Fade In Start", // = 14 + "Moons:Secunda Fade In Finish", // = 15 + "Moons:Secunda Fade Out Start", // = 7 + "Moons:Secunda Fade Out Finish", // = 10 + + // same values as Secunda + "Moons:Masser Fade In Start", + "Moons:Masser Fade In Finish", + "Moons:Masser Fade Out Start", + "Moons:Masser Fade Out Finish", + + // color code hardcoded in mwrender/sky.cpp (l.417, mMoonRed) + // as float values (divided by 255) + "Moons:Script Color", + + // not used + "Moons:Secunda Size", + "Moons:Secunda Axis Offset", + "Moons:Secunda Speed", + "Moons:Secunda Daily Increment", + "Moons:Secunda Moon Shadow Early Fade Angle", + "Moons:Masser Size", + "Moons:Masser Axis Offset", + "Moons:Masser Speed", + "Moons:Masser Daily Increment", + "Moons:Masser Moon Shadow Early Fade Angle", + 0 }; From 9d043d0193b3a35c2f7bb0946c39f20469f771c8 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sat, 29 Dec 2012 17:15:53 +0100 Subject: [PATCH 245/378] mwiniimporter: fix string cutting --- apps/mwiniimporter/importer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 1236dcaf0..0bf0ae4ec 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -618,7 +618,7 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) { if(line[0] == '[') { if(line.length() > 2) { - section = line.substr(1, line.length()-3); + section = line.substr(1, line.length()-2); } continue; } @@ -711,7 +711,7 @@ void MwIniImporter::mergeFallback(multistrmap &cfg, multistrmap &ini) { std::string value(*it); std::replace( value.begin(), value.end(), ' ', '_' ); std::replace( value.begin(), value.end(), ':', '_' ); - value.append(",").append(vc->substr(0,vc->length()-1)); + value.append(",").append(vc->substr(0,vc->length())); insertMultistrmap(cfg, "fallback", value); } } From fb25f407fbcd2e392e63a8c1f1b7393ecfe65e01 Mon Sep 17 00:00:00 2001 From: eduard Date: Sun, 30 Dec 2012 13:37:19 +0100 Subject: [PATCH 246/378] dialoguemanager conflict --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 533 +----------------- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 1 + 2 files changed, 4 insertions(+), 530 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 743db9a43..8e74894b9 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -43,89 +43,6 @@ namespace { -<<<<<<< HEAD - template - bool selectCompare (char comp, T1 value1, T2 value2) - { - switch (comp) - { - case '0': return value1==value2; -// case '1': return value1!=value2; - case '2': return value1>value2; - case '3': return value1>=value2; - case '4': return value1 - bool checkLocal (char comp, const std::string& name, T value, const MWWorld::Ptr& actor, - const ESMS::ESMStore& store) - { - std::string scriptName = MWWorld::Class::get (actor).getScript (actor); - - if (scriptName.empty()) - return false; // no script - - const ESM::Script *script = store.scripts.find (scriptName); - - int i = 0; - - for (; i (script->mVarNames.size()); ++i) - if (script->mVarNames[i]==name) - break; - - if (i>=static_cast (script->mVarNames.size())) - return false; // script does not have a variable of this name - - const MWScript::Locals& locals = actor.getRefData().getLocals(); - - if (imData.mNumShorts) - return selectCompare (comp, locals.mShorts[i], value); - else - i -= script->mData.mNumShorts; - - if (imData.mNumLongs) - return selectCompare (comp, locals.mLongs[i], value); - else - i -= script->mData.mNumShorts; - - return selectCompare (comp, locals.mFloats.at (i), value); - } - - template - bool checkGlobal (char comp, const std::string& name, T value) - { - switch (MWBase::Environment::get().getWorld()->getGlobalVariableType (name)) - { - case 's': - return selectCompare (comp, MWBase::Environment::get().getWorld()->getGlobalVariable (name).mShort, value); - - case 'l': - - return selectCompare (comp, MWBase::Environment::get().getWorld()->getGlobalVariable (name).mLong, value); - - case 'f': - - return selectCompare (comp, MWBase::Environment::get().getWorld()->getGlobalVariable (name).mFloat, value); - - case ' ': - - MWBase::Environment::get().getWorld()->getGlobalVariable (name); // trigger exception - break; - - default: - - throw std::runtime_error ("unsupported gobal variable type"); - } - - return false; - } - -======= ->>>>>>> 92623921add0d6e16a34973dcf6f2ee1f52dbbe7 //helper function std::string::size_type find_str_ci(const std::string& str, const std::string& substr,size_t pos) { @@ -135,437 +52,7 @@ namespace namespace MWDialogue { -<<<<<<< HEAD - - - bool DialogueManager::functionFilter(const MWWorld::Ptr& actor, const ESM::DialInfo& info,bool choice) - { - bool isCreature = (actor.getTypeName() != typeid(ESM::NPC).name()); - - for (std::vector::const_iterator iter (info.mSelects.begin()); - iter != info.mSelects.end(); ++iter) - { - ESM::DialInfo::SelectStruct select = *iter; - char type = select.mSelectRule[1]; - if(type == '1') - { - char comp = select.mSelectRule[4]; - std::string name = select.mSelectRule.substr (5); - std::string function = select.mSelectRule.substr(2,2); - - int ifunction; - std::istringstream iss(function); - iss >> ifunction; - switch(ifunction) - { - case 39://PC Expelled - if(!selectCompare(comp,0,select.mI)) return false; - break; - - case 40://PC Common Disease - if(!selectCompare(comp,0,select.mI)) return false; - break; - - case 41://PC Blight Disease - if(!selectCompare(comp,0,select.mI)) return false; - break; - - case 43://PC Crime level - if(!selectCompare(comp,0,select.mI)) return false; - break; - - case 46://Same faction - { - if (isCreature) - return false; - - MWMechanics::NpcStats PCstats = MWWorld::Class::get(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()).getNpcStats(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - MWMechanics::NpcStats NPCstats = MWWorld::Class::get(actor).getNpcStats(actor); - int sameFaction = 0; - if(!NPCstats.getFactionRanks().empty()) - { - std::string NPCFaction = NPCstats.getFactionRanks().begin()->first; - if(PCstats.getFactionRanks().find(Misc::toLower(NPCFaction)) != PCstats.getFactionRanks().end()) sameFaction = 1; - } - if(!selectCompare(comp,sameFaction,select.mI)) return false; - } - break; - - case 48://Detected - if(!selectCompare(comp,1,select.mI)) return false; - break; - - case 49://Alarmed - if(!selectCompare(comp,0,select.mI)) return false; - break; - - case 50://choice - if(choice) - { - if(!selectCompare(comp,mChoice,select.mI)) return false; - } - break; - - case 60://PC Vampire - if(!selectCompare(comp,0,select.mI)) return false; - break; - - case 61://Level - if(!selectCompare(comp,1,select.mI)) return false; - break; - - case 62://Attacked - if(!selectCompare(comp,0,select.mI)) return false; - break; - - case 63://Talked to PC - if(!selectCompare(comp,0,select.mI)) return false; - break; - - case 64://PC Health - if(!selectCompare(comp,50,select.mI)) return false; - break; - - case 65://Creature target - if(!selectCompare(comp,0,select.mI)) return false; - break; - - case 66://Friend hit - if(!selectCompare(comp,0,select.mI)) return false; - break; - - case 67://Fight - if(!selectCompare(comp,0,select.mI)) return false; - break; - - case 68://Hello???? - if(!selectCompare(comp,0,select.mI)) return false; - break; - - case 69://Alarm - if(!selectCompare(comp,0,select.mI)) return false; - break; - - case 70://Flee - if(!selectCompare(comp,0,select.mI)) return false; - break; - - case 71://Should Attack - if(!selectCompare(comp,0,select.mI)) return false; - break; - - default: - break; - - } - } - } - - return true; - } - - bool DialogueManager::isMatching (const MWWorld::Ptr& actor, - const ESM::DialInfo::SelectStruct& select) const - { - bool isCreature = (actor.getTypeName() != typeid(ESM::NPC).name()); - - char type = select.mSelectRule[1]; - - if (type!='0') - { - char comp = select.mSelectRule[4]; - std::string name = select.mSelectRule.substr (5); - std::string function = select.mSelectRule.substr(1,2); - - switch (type) - { - case '1': // function - - return true; // Done elsewhere. - - case '2': // global - - if (select.mType==ESM::VT_Short || select.mType==ESM::VT_Int || - select.mType==ESM::VT_Long) - { - if (!checkGlobal (comp, Misc::toLower (name), select.mI)) - return false; - } - else if (select.mType==ESM::VT_Float) - { - if (!checkGlobal (comp, Misc::toLower (name), select.mF)) - return false; - } - else - throw std::runtime_error ( - "unsupported variable type in dialogue info select"); - - return true; - - case '3': // local - - if (select.mType==ESM::VT_Short || select.mType==ESM::VT_Int || - select.mType==ESM::VT_Long) - { - if (!checkLocal (comp, Misc::toLower (name), select.mI, actor, - MWBase::Environment::get().getWorld()->getStore())) - return false; - } - else if (select.mType==ESM::VT_Float) - { - if (!checkLocal (comp, Misc::toLower (name), select.mF, actor, - MWBase::Environment::get().getWorld()->getStore())) - return false; - } - else - throw std::runtime_error ( - "unsupported variable type in dialogue info select"); - - return true; - - case '4'://journal - if(select.mType==ESM::VT_Int) - { - if(!selectCompare(comp,MWBase::Environment::get().getJournal()->getJournalIndex(Misc::toLower(name)),select.mI)) return false; - } - else - throw std::runtime_error ( - "unsupported variable type in dialogue info select"); - - return true; - - case '5'://item - { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWWorld::ContainerStore& store = MWWorld::Class::get (player).getContainerStore (player); - - int sum = 0; - - for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) - if (Misc::toLower(iter->getCellRef().mRefID) == Misc::toLower(name)) - sum += iter->getRefData().getCount(); - if(!selectCompare(comp,sum,select.mI)) return false; - } - - return true; - - - case '6'://dead - if(!selectCompare(comp,0,select.mI)) return false; - - case '7':// not ID - if(select.mType==ESM::VT_String ||select.mType==ESM::VT_Int)//bug in morrowind here? it's not a short, it's a string - { - int isID = int(Misc::toLower(name)==Misc::toLower(MWWorld::Class::get (actor).getId (actor))); - if (selectCompare(comp,!isID,select.mI)) return false; - } - else - throw std::runtime_error ( - "unsupported variable type in dialogue info select"); - - return true; - - case '8':// not faction - if (isCreature) - return false; - - if(select.mType==ESM::VT_Int) - { - MWWorld::LiveCellRef* npc = actor.get(); - int isFaction = int(Misc::toLower(npc->base->mFaction) == Misc::toLower(name)); - if(selectCompare(comp,!isFaction,select.mI)) - return false; - } - else - throw std::runtime_error ( - "unsupported variable type in dialogue info select"); - - return true; - - case '9':// not class - if (isCreature) - return false; - - if(select.mType==ESM::VT_Int) - { - MWWorld::LiveCellRef* npc = actor.get(); - int isClass = int(Misc::toLower(npc->base->mClass) == Misc::toLower(name)); - if(selectCompare(comp,!isClass,select.mI)) - return false; - } - else - throw std::runtime_error ( - "unsupported variable type in dialogue info select"); - - return true; - - case 'A'://not Race - if (isCreature) - return false; - - if(select.mType==ESM::VT_Int) - { - MWWorld::LiveCellRef* npc = actor.get(); - int isRace = int(Misc::toLower(npc->base->mRace) == Misc::toLower(name)); - if(selectCompare(comp,!isRace,select.mI)) - return false; - } - else - throw std::runtime_error ( - "unsupported variable type in dialogue info select"); - - return true; - - case 'B'://not Cell - if(select.mType==ESM::VT_Int) - { - int isCell = int(Misc::toLower(actor.getCell()->cell->mName) == Misc::toLower(name)); - if(selectCompare(comp,!isCell,select.mI)) - return false; - } - else - throw std::runtime_error ( - "unsupported variable type in dialogue info select"); - return true; - - case 'C'://not local - if (select.mType==ESM::VT_Short || select.mType==ESM::VT_Int || - select.mType==ESM::VT_Long) - { - if (checkLocal (comp, Misc::toLower (name), select.mI, actor, - MWBase::Environment::get().getWorld()->getStore())) - return false; - } - else if (select.mType==ESM::VT_Float) - { - if (checkLocal (comp, Misc::toLower (name), select.mF, actor, - MWBase::Environment::get().getWorld()->getStore())) - return false; - } - else - throw std::runtime_error ( - "unsupported variable type in dialogue info select"); - return true; - - - default: - - std::cout << "unchecked select: " << type << " " << comp << " " << name << std::endl; - } - } - - return true; - } - - bool DialogueManager::isMatching (const MWWorld::Ptr& actor, const ESM::DialInfo& info) const - { - bool isCreature = (actor.getTypeName() != typeid(ESM::NPC).name()); - - // actor id - if (!info.mActor.empty()) - if (Misc::toLower (info.mActor)!=MWWorld::Class::get (actor).getId (actor)) - return false; - - //NPC race - if (!info.mRace.empty()) - { - if (isCreature) - return false; - - MWWorld::LiveCellRef *cellRef = actor.get(); - - if (!cellRef) - return false; - - if (Misc::toLower (info.mRace)!=Misc::toLower (cellRef->base->mRace)) - return false; - } - - //NPC class - if (!info.mClass.empty()) - { - if (isCreature) - return false; - - MWWorld::LiveCellRef *cellRef = actor.get(); - - if (!cellRef) - return false; - - if (Misc::toLower (info.mClass)!=Misc::toLower (cellRef->base->mClass)) - return false; - } - - //NPC faction - if (!info.mNpcFaction.empty()) - { - if (isCreature) - return false; - - //MWWorld::Class npcClass = MWWorld::Class::get(actor); - MWMechanics::NpcStats stats = MWWorld::Class::get(actor).getNpcStats(actor); - std::map::iterator it = stats.getFactionRanks().find(Misc::toLower(info.mNpcFaction)); - if(it!=stats.getFactionRanks().end()) - { - //check rank - if(it->second < (int)info.mData.mRank) return false; - } - else - { - //not in the faction - return false; - } - } - - // TODO check player faction - if(!info.mPcFaction.empty()) - { - MWMechanics::NpcStats stats = MWWorld::Class::get(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()).getNpcStats(MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); - std::map::iterator it = stats.getFactionRanks().find(Misc::toLower(info.mPcFaction)); - if(it!=stats.getFactionRanks().end()) - { - //check rank - if(it->second < (int)info.mData.mPCrank) return false; - } - else - { - //not in the faction - return false; - } - } - - //check gender - if (!isCreature) - { - MWWorld::LiveCellRef* npc = actor.get(); - if(npc->base->mFlags & npc->base->Female) - { - if(static_cast (info.mData.mGender)==0) return false; - } - else - { - if(static_cast (info.mData.mGender)==1) return false; - } - } - - // check cell - if (!info.mCell.empty()) - if (MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->cell->mName != info.mCell) - return false; - - // TODO check DATAstruct - for (std::vector::const_iterator iter (info.mSelects.begin()); - iter != info.mSelects.end(); ++iter) - if (!isMatching (actor, *iter)) - return false; - - return true; - } - - DialogueManager::DialogueManager (const Compiler::Extensions& extensions) : -======= DialogueManager::DialogueManager (const Compiler::Extensions& extensions, bool scriptVerbose) : ->>>>>>> 92623921add0d6e16a34973dcf6f2ee1f52dbbe7 mCompilerContext (MWScript::CompilerContext::Type_Dialgoue), mErrorStream(std::cout.rdbuf()),mErrorHandler(mErrorStream) , mTemporaryDispositionChange(0.f) @@ -583,11 +70,7 @@ namespace MWDialogue MWWorld::Store::iterator it = dialogs.begin(); for (; it != dialogs.end(); ++it) { -<<<<<<< HEAD - mDialogueMap[Misc::toLower(it->first)] = it->second; -======= - mDialogueMap[toLower(it->mId)] = *it; ->>>>>>> 92623921add0d6e16a34973dcf6f2ee1f52dbbe7 + mDialogueMap[Misc::toLower(it->mId)] = *it; } } @@ -791,22 +274,12 @@ namespace MWDialogue { if (filter.search (*iter)) { - mActorKnownTopics.push_back (toLower (iter->mId)); + mActorKnownTopics.push_back (Misc::toLower (iter->mId)); //does the player know the topic? - if (mKnownTopics.find (toLower (iter->mId)) != mKnownTopics.end()) + if (mKnownTopics.find (Misc::toLower (iter->mId)) != mKnownTopics.end()) { -<<<<<<< HEAD - mActorKnownTopics.push_back(Misc::toLower(it->first)); - //does the player know the topic? - if(mKnownTopics.find(Misc::toLower(it->first)) != mKnownTopics.end()) - { - keywordList.push_back(it->first); - break; - } -======= keywordList.push_back (iter->mId); ->>>>>>> 92623921add0d6e16a34973dcf6f2ee1f52dbbe7 } } } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 98b27f774..b0bef5ea0 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -71,6 +71,7 @@ namespace MWDialogue virtual void persuade (int type); virtual int getTemporaryDispositionChange () const; virtual void applyTemporaryDispositionChange (int delta); + void toLower(std::string question); }; } From 98103e15eb55a7c63bf7796edf83e2c67d6acd0b Mon Sep 17 00:00:00 2001 From: eduard Date: Sun, 30 Dec 2012 13:46:07 +0100 Subject: [PATCH 247/378] conflict resolution --- apps/openmw/mwworld/worldimp.cpp | 21 +-------------------- components/misc/stringops.hpp | 3 --- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5e7a19caa..83c6b20e5 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -235,20 +235,11 @@ namespace MWWorld } // didn't work -> now check for regions -<<<<<<< HEAD - std::string cellName2 = Misc::toLower (cellName); - - for (ESMS::RecListT::MapType::const_iterator iter (mStore.regions.list.begin()); - iter!=mStore.regions.list.end(); ++iter) - { - if (Misc::toLower (iter->second.mName)==cellName2) -======= const MWWorld::Store ®ions = mStore.get(); MWWorld::Store::iterator it = regions.begin(); for (; it != regions.end(); ++it) { if (Misc::StringUtils::ciEqual(cellName, it->mName)) ->>>>>>> 92623921add0d6e16a34973dcf6f2ee1f52dbbe7 { return mStore.get().searchExtByRegion(it->mId); } @@ -625,26 +616,16 @@ namespace MWWorld CellStore *currCell = ptr.getCell(); bool isPlayer = ptr == mPlayer->getPlayer(); bool haveToMove = mWorldScene->isCellActive(*currCell) || isPlayer; -<<<<<<< HEAD - if (*currCell != newCell) { - if (isPlayer) { - if (!newCell.isExterior()) { - changeToInteriorCell(Misc::toLower(newCell.cell->mName), pos); - } else { - int cellX = newCell.cell->mData.mX; - int cellY = newCell.cell->mData.mY; -======= if (*currCell != newCell) { if (isPlayer) if (!newCell.isExterior()) - changeToInteriorCell(toLower(newCell.mCell->mName), pos); + changeToInteriorCell(Misc::toLower(newCell.mCell->mName), pos); else { int cellX = newCell.mCell->getGridX(); int cellY = newCell.mCell->getGridY(); ->>>>>>> 92623921add0d6e16a34973dcf6f2ee1f52dbbe7 mWorldScene->changeCell(cellX, cellY, pos, false); } else { diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index cfe7ba028..71c96b652 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -2,10 +2,7 @@ #define MISC_STRINGOPS_H #include -<<<<<<< HEAD -======= #include ->>>>>>> 92623921add0d6e16a34973dcf6f2ee1f52dbbe7 namespace Misc { From 714b1924a5758c6c7ef5c87cd25ba895e23cbcaf Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 30 Dec 2012 14:01:52 +0100 Subject: [PATCH 248/378] partial dialogue subview implementation --- apps/opencs/model/world/columns.hpp | 2 +- apps/opencs/model/world/data.cpp | 23 ++++++++++++------- apps/opencs/model/world/data.hpp | 16 +++++++++---- apps/opencs/view/world/dialoguesubview.cpp | 26 ++++++++++++++++++++++ 4 files changed, 54 insertions(+), 13 deletions(-) diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index f58d77c90..5abf4ea8b 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -1,7 +1,7 @@ #ifndef CSM_WOLRD_COLUMNS_H #define CSM_WOLRD_COLUMNS_H -#include "idcollection.hpp" +#include "columnbase.hpp" namespace CSMWorld { diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 0da0cba83..a3522503e 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -10,6 +10,16 @@ #include "idtable.hpp" #include "columns.hpp" +void CSMWorld::Data::addModel (QAbstractTableModel *model, UniversalId::Type type1, + UniversalId::Type type2) +{ + mModels.push_back (model); + mModelIndex.insert (std::make_pair (type1, model)); + + if (type2!=UniversalId::Type_None) + mModelIndex.insert (std::make_pair (type2, model)); +} + CSMWorld::Data::Data() { mGlobals.addColumn (new StringIdColumn); @@ -17,16 +27,13 @@ CSMWorld::Data::Data() mGlobals.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Global)); mGlobals.addColumn (new FloatValueColumn); - mModels.insert (std::make_pair ( - UniversalId (UniversalId::Type_Globals), - new IdTable (&mGlobals) - )); + addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global); } CSMWorld::Data::~Data() { - for (std::map::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter) - delete iter->second; + for (std::vector::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter) + delete *iter; } const CSMWorld::IdCollection& CSMWorld::Data::getGlobals() const @@ -41,9 +48,9 @@ CSMWorld::IdCollection& CSMWorld::Data::getGlobals() QAbstractTableModel *CSMWorld::Data::getTableModel (const UniversalId& id) { - std::map::iterator iter = mModels.find (id); + std::map::iterator iter = mModelIndex.find (id.getType()); - if (iter==mModels.end()) + if (iter==mModelIndex.end()) throw std::logic_error ("No table model available for " + id.toString()); return iter->second; diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index a8a21e205..f7748cb5d 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -1,7 +1,8 @@ -#ifndef CSM_WOLRD_IDLIST_H -#define CSM_WOLRD_IDLIST_H +#ifndef CSM_WOLRD_DATA_H +#define CSM_WOLRD_DATA_H #include +#include #include @@ -15,12 +16,16 @@ namespace CSMWorld class Data { IdCollection mGlobals; - std::map mModels; + std::vector mModels; + std::map mModelIndex; // not implemented Data (const Data&); Data& operator= (const Data&); + void addModel (QAbstractTableModel *model, UniversalId::Type type1, + UniversalId::Type type2 = UniversalId::Type_None); + public: Data(); @@ -32,7 +37,10 @@ namespace CSMWorld IdCollection& getGlobals(); QAbstractTableModel *getTableModel (const UniversalId& id); - ///< If no table model is available for \ id, an exception is thrown. + ///< If no table model is available for \a id, an exception is thrown. + /// + /// \note The returned table may either be the model for the ID itself or the model that + /// contains the record specified by the ID. void merge(); ///< Merge modified into base. diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index e2d16f1e3..354223757 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -1,11 +1,37 @@ #include "dialoguesubview.hpp" +#include +#include +#include + +#include "../../model/world/columnbase.hpp" + CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete) : SubView (id) { + QWidget *widget = new QWidget (this); + + setWidget (widget); + + QGridLayout *layout = new QGridLayout; + + widget->setLayout (layout); + + QAbstractTableModel *model = document.getData().getTableModel (id); + + int columns = model->columnCount(); + + for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); + if (flags & CSMWorld::ColumnBase::Flag_Dialogue) + { + layout->addWidget (new QLabel (model->headerData (i, Qt::Horizontal).toString()), i, 0); + } + } } void CSVWorld::DialogueSubView::setEditLock (bool locked) From 38e7dbb970b523ea68be73aa3732f73c82e237b5 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sun, 30 Dec 2012 16:41:25 +0100 Subject: [PATCH 249/378] mwininimporter: add more fallback values --- apps/mwiniimporter/importer.cpp | 75 +++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 0bf0ae4ec..335b7aecf 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -19,6 +19,81 @@ MwIniImporter::MwIniImporter() }; const char *fallback[] = { + // light + "LightAttenuation:UseConstant", + "LightAttenuation:ConstantValue", + "LightAttenuation:UseLinear", + "LightAttenuation:LinearMethod", + "LightAttenuation:LinearValue", + "LightAttenuation:LinearRadiusMult", + "LightAttenuation:UseQuadratic", + "LightAttenuation:QuadraticMethod", + "LightAttenuation:QuadraticValue", + "LightAttenuation:QuadraticRadiusMult", + "LightAttenuation:OutQuadInLin", + + // inventory + "Inventory:DirectionalDiffuseR", + "Inventory:DirectionalDiffuseG", + "Inventory:DirectionalDiffuseB", + "Inventory:DirectionalAmbientR", + "Inventory:DirectionalAmbientG", + "Inventory:DirectionalAmbientB", + "Inventory:DirectionalRotationX", + "Inventory:DirectionalRotationY", + "Inventory:UniformScaling", + + // map + "Map:Travel Siltstrider Red", + "Map:Travel Siltstrider Green", + "Map:Travel Siltstrider Blue", + "Map:Travel Boat Red", + "Map:Travel Boat Green", + "Map:Travel Boat Blue", + "Map:Travel Magic Red", + "Map:Travel Magic Green", + "Map:Travel Magic Blue", + "Map:Show Travel Lines", + + // water + "Water:Map Alpha", + "Water:World Alpha", + "Water:SurfaceTextureSize", + "Water:SurfaceTileCount", + "Water:SurfaceFPS", + "Water:SurfaceTexture", + "Water:SurfaceFrameCount", + "Water:TileTextureDivisor", + "Water:RippleTexture", + "Water:RippleFrameCount", + "Water:RippleLifetime", + "Water:MaxNumberRipples", + "Water:RippleScale", + "Water:RippleRotSpeed", + "Water:RippleAlphas", + "Water:PSWaterReflectTerrain", + "Water:PSWaterReflectUpdate", + "Water:NearWaterRadius", + "Water:NearWaterPoints", + "Water:NearWaterUnderwaterFreq", + "Water:NearWaterUnderwaterVolume", + "Water:NearWaterIndoorTolerance", + "Water:NearWaterOutdoorTolerance", + "Water:NearWaterIndoorID", + "Water:NearWaterOutdoorID", + "Water:UnderwaterSunriseFog", + "Water:UnderwaterDayFog", + "Water:UnderwaterSunsetFog", + "Water:UnderwaterNightFog", + "Water:UnderwaterIndoorFog", + "Water:UnderwaterColor", + "Water:UnderwaterColorWeight", + + // pixelwater + "PixelWater:SurfaceFPS", + "PixelWater:TileCount", + "PixelWater:Resolution", + /* "Fonts:Font 0", "Fonts:Font 1", From cb71efc4279146467e53098748d87e7f9a649325 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sun, 30 Dec 2012 16:44:52 +0100 Subject: [PATCH 250/378] mwiniimporter: clean up --- apps/mwiniimporter/importer.cpp | 213 ++++++++++++++------------------ 1 file changed, 91 insertions(+), 122 deletions(-) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 335b7aecf..dda8fd8ba 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -94,13 +94,12 @@ MwIniImporter::MwIniImporter() "PixelWater:TileCount", "PixelWater:Resolution", - /* + // fonts "Fonts:Font 0", "Fonts:Font 1", "Fonts:Font 2", - */ - /* + // UI colors "FontColor:color_normal", "FontColor:color_normal_over", "FontColor:color_normal_pressed", @@ -146,18 +145,15 @@ MwIniImporter::MwIniImporter() "FontColor:color_positive", "FontColor:color_negative", "FontColor:color_count", - */ - /* + // cursors "Cursors:Cursor 0", "Cursors:Cursor 1", "Cursors:Cursor 2", "Cursors:Cursor 3", "Cursors:Cursor 4", - */ // level up messages - // not used for now "Level Up:Level2", "Level Up:Level3", "Level Up:Level4", @@ -180,7 +176,6 @@ MwIniImporter::MwIniImporter() "Level Up:Default", // character creation multiple choice test - // hardcoded in mwgui/charactercreation.cpp "Question 1:Question", "Question 1:AnswerOne", "Question 1:AnswerTwo", @@ -233,7 +228,6 @@ MwIniImporter::MwIniImporter() "Question 10:Sound", // blood textures and models - // not used for now "Blood:Model 0", "Blood:Model 1", "Blood:Model 2", @@ -245,7 +239,6 @@ MwIniImporter::MwIniImporter() "Blood:Texture Name 2", // movies - // not used for now "Movies:Company Logo", "Movies:Morrowind Logo", "Movies:New Game", @@ -253,10 +246,7 @@ MwIniImporter::MwIniImporter() "Movies:Options Menu", // weather related values - // hardcoded in mwworld/wheather.cpp - // Some are easy to replace, others are used as magic number - // globals "Weather Thunderstorm:Thunder Sound ID 0", "Weather Thunderstorm:Thunder Sound ID 1", "Weather Thunderstorm:Thunder Sound ID 2", @@ -268,7 +258,51 @@ MwIniImporter::MwIniImporter() "Weather:Hours Between Weather Changes", // AKA weather update time "Weather Thunderstorm:Thunder Frequency", "Weather Thunderstorm:Thunder Threshold", - // thunder sound delay? + + "Weather:EnvReduceColor", + "Weather:LerpCloseColor", + "Weather:BumpFadeColor", + "Weather:AlphaReduce", + "Weather:Minimum Time Between Environmental Sounds", + "Weather:Maximum Time Between Environmental Sounds", + "Weather:Sun Glare Fader Max", + "Weather:Sun Glare Fader Angle Max", + "Weather:Sun Glare Fader Color", + "Weather:Timescale Clouds", + "Weather:Precip Gravity", + "Weather:Rain Ripples", + "Weather:Rain Ripple Radius", + "Weather:Rain Ripples Per Drop", + "Weather:Rain Ripple Scale", + "Weather:Rain Ripple Speed", + "Weather:Fog Depth Change Speed", + "Weather:Sky Pre-Sunrise Time", + "Weather:Sky Post-Sunrise Time", + "Weather:Sky Pre-Sunset Time", + "Weather:Sky Post-Sunset Time", + "Weather:Ambient Pre-Sunrise Time", + "Weather:Ambient Post-Sunrise Time", + "Weather:Ambient Pre-Sunset Time", + "Weather:Ambient Post-Sunset Time", + "Weather:Fog Pre-Sunrise Time", + "Weather:Fog Post-Sunrise Time", + "Weather:Fog Pre-Sunset Time", + "Weather:Fog Post-Sunset Time", + "Weather:Sun Pre-Sunrise Time", + "Weather:Sun Post-Sunrise Time", + "Weather:Sun Pre-Sunset Time", + "Weather:Sun Post-Sunset Time", + "Weather:Stars Post-Sunset Start", + "Weather:Stars Pre-Sunrise Finish", + "Weather:Stars Fading Duration", + "Weather:Snow Ripples", + "Weather:Snow Ripple Radius", + "Weather:Snow Ripples Per Flake", + "Weather:Snow Ripple Scale", + "Weather:Snow Ripple Speed", + "Weather:Snow Gravity Scale", + "Weather:Snow High Kill", + "Weather:Snow Low Kill", "Weather Clear:Cloud Texture", "Weather Clear:Clouds Maximum Percent", @@ -295,6 +329,7 @@ MwIniImporter::MwIniImporter() "Weather Clear:Wind Speed", "Weather Clear:Cloud Speed", "Weather Clear:Glare View", + "Weather Clear:Ambient Loop Sound ID", "Weather Cloudy:Cloud Texture", "Weather Cloudy:Clouds Maximum Percent", @@ -321,6 +356,7 @@ MwIniImporter::MwIniImporter() "Weather Cloudy:Wind Speed", "Weather Cloudy:Cloud Speed", "Weather Cloudy:Glare View", + "Weather Cloudy:Ambient Loop Sound ID", "Weather Foggy:Cloud Texture", "Weather Foggy:Clouds Maximum Percent", @@ -347,6 +383,7 @@ MwIniImporter::MwIniImporter() "Weather Foggy:Wind Speed", "Weather Foggy:Cloud Speed", "Weather Foggy:Glare View", + "Weather Foggy:Ambient Loop Sound ID", "Weather Thunderstorm:Cloud Texture", "Weather Thunderstorm:Clouds Maximum Percent", @@ -374,6 +411,15 @@ MwIniImporter::MwIniImporter() "Weather Thunderstorm:Cloud Speed", "Weather Thunderstorm:Glare View", "Weather Thunderstorm:Rain Loop Sound ID", + "Weather Thunderstorm:Using Precip", + "Weather Thunderstorm:Rain Diameter", + "Weather Thunderstorm:Rain Height Min", + "Weather Thunderstorm:Rain Height Max", + "Weather Thunderstorm:Rain Threshold", + "Weather Thunderstorm:Max Raindrops", + "Weather Thunderstorm:Rain Entrance Speed", + "Weather Thunderstorm:Ambient Loop Sound ID", + "Weather Thunderstorm:Flash Decrement", "Weather Rain:Cloud Texture", "Weather Rain:Clouds Maximum Percent", @@ -401,6 +447,14 @@ MwIniImporter::MwIniImporter() "Weather Rain:Cloud Speed", "Weather Rain:Glare View", "Weather Rain:Rain Loop Sound ID", + "Weather Rain:Using Precip", + "Weather Rain:Rain Diameter", + "Weather Rain:Rain Height Min", + "Weather Rain:Rain Height Max", + "Weather Rain:Rain Threshold", + "Weather Rain:Rain Entrance Speed", + "Weather Rain:Ambient Loop Sound ID", + "Weather Rain:Max Raindrops", "Weather Overcast:Cloud Texture", "Weather Overcast:Clouds Maximum Percent", @@ -427,6 +481,7 @@ MwIniImporter::MwIniImporter() "Weather Overcast:Wind Speed", "Weather Overcast:Cloud Speed", "Weather Overcast:Glare View", + "Weather Overcast:Ambient Loop Sound ID", "Weather Ashstorm:Cloud Texture", "Weather Ashstorm:Clouds Maximum Percent", @@ -454,6 +509,7 @@ MwIniImporter::MwIniImporter() "Weather Ashstorm:Cloud Speed", "Weather Ashstorm:Glare View", "Weather Ashstorm:Ambient Loop Sound ID", + "Weather Ashstorm:Storm Threshold", "Weather Blight:Cloud Texture", "Weather Blight:Clouds Maximum Percent", @@ -481,8 +537,7 @@ MwIniImporter::MwIniImporter() "Weather Blight:Cloud Speed", "Weather Blight:Glare View", "Weather Blight:Ambient Loop Sound ID", - - // not used for now (todo) + "Weather Blight:Storm Threshold", "Weather Blight:Disease Chance", // for Bloodmoon @@ -511,6 +566,13 @@ MwIniImporter::MwIniImporter() "Weather Snow:Wind Speed", "Weather Snow:Cloud Speed", "Weather Snow:Glare View", + "Weather Snow:Snow Diameter", + "Weather Snow:Snow Height Min", + "Weather Snow:Snow Height Max", + "Weather Snow:Snow Entrance Speed", + "Weather Snow:Max Snowflakes", + "Weather Snow:Ambient Loop Sound ID", + "Weather Snow:Snow Threshold", // for Bloodmoon "Weather Blizzard:Cloud Texture", @@ -539,125 +601,32 @@ MwIniImporter::MwIniImporter() "Weather Blizzard:Cloud Speed", "Weather Blizzard:Glare View", "Weather Blizzard:Ambient Loop Sound ID", - - // not used - "Weather Clear:Ambient Loop Sound ID", - "Weather Cloudy:Ambient Loop Sound ID", - "Weather Foggy:Ambient Loop Sound ID", - "Weather Overcast:Ambient Loop Sound ID", - "Weather Snow:Ambient Loop Sound ID", - // - "Weather Ashstorm:Storm Threshold", - "Weather Blight:Storm Threshold", - "Weather Snow:Snow Threshold", "Weather Blizzard:Storm Threshold", - // - "Weather Snow:Snow Diameter", - "Weather Snow:Snow Height Min", - "Weather Snow:Snow Height Max", - "Weather Snow:Snow Entrance Speed", - "Weather Snow:Max Snowflakes", - // - "Weather:EnvReduceColor", - "Weather:LerpCloseColor", - "Weather:BumpFadeColor", - "Weather:AlphaReduce", - "Weather:Minimum Time Between Environmental Sounds", - "Weather:Maximum Time Between Environmental Sounds", - "Weather:Sun Glare Fader Max", - "Weather:Sun Glare Fader Angle Max", - "Weather:Sun Glare Fader Color", - "Weather:Timescale Clouds", - "Weather:Precip Gravity", - "Weather:Rain Ripples", - "Weather:Rain Ripple Radius", - "Weather:Rain Ripples Per Drop", - "Weather:Rain Ripple Scale", - "Weather:Rain Ripple Speed", - "Weather:Fog Depth Change Speed", - "Weather:Sky Pre-Sunrise Time", - "Weather:Sky Post-Sunrise Time", - "Weather:Sky Pre-Sunset Time", - "Weather:Sky Post-Sunset Time", - "Weather:Ambient Pre-Sunrise Time", - "Weather:Ambient Post-Sunrise Time", - "Weather:Ambient Pre-Sunset Time", - "Weather:Ambient Post-Sunset Time", - "Weather:Fog Pre-Sunrise Time", - "Weather:Fog Post-Sunrise Time", - "Weather:Fog Pre-Sunset Time", - "Weather:Fog Post-Sunset Time", - "Weather:Sun Pre-Sunrise Time", - "Weather:Sun Post-Sunrise Time", - "Weather:Sun Pre-Sunset Time", - "Weather:Sun Post-Sunset Time", - "Weather:Stars Post-Sunset Start", - "Weather:Stars Pre-Sunrise Finish", - "Weather:Stars Fading Duration", - "Weather:Snow Ripples", - "Weather:Snow Ripple Radius", - "Weather:Snow Ripples Per Flake", - "Weather:Snow Ripple Scale", - "Weather:Snow Ripple Speed", - "Weather:Snow Gravity Scale", - "Weather:Snow High Kill", - "Weather:Snow Low Kill", - // - "Weather Rain:Using Precip", - "Weather Rain:Rain Diameter", - "Weather Rain:Rain Height Min", - "Weather Rain:Rain Height Max", - "Weather Rain:Rain Threshold", - "Weather Rain:Rain Entrance Speed", - "Weather Rain:Ambient Loop Sound ID", - "Weather Rain:Max Raindrops", - // - "Weather Thunderstorm:Using Precip", - "Weather Thunderstorm:Rain Diameter", - "Weather Thunderstorm:Rain Height Min", - "Weather Thunderstorm:Rain Height Max", - "Weather Thunderstorm:Rain Threshold", - "Weather Thunderstorm:Max Raindrops", - "Weather Thunderstorm:Rain Entrance Speed", - "Weather Thunderstorm:Ambient Loop Sound ID", - "Weather Thunderstorm:Flash Decrement", // moons - - // these are hardcoded in mwworld/weather.cpp - // l.642..665 (masser_angle_fade) - "Moons:Secunda Fade Start Angle", // = 50 - "Moons:Secunda Fade End Angle", // = 40 - "Moons:Masser Fade Start Angle", // = 50 - "Moons:Masser Fade End Angle", // = 30 - - // hardcoded in weather.cpp l.635 to 641 (hour_fade) - "Moons:Secunda Fade In Start", // = 14 - "Moons:Secunda Fade In Finish", // = 15 - "Moons:Secunda Fade Out Start", // = 7 - "Moons:Secunda Fade Out Finish", // = 10 - - // same values as Secunda - "Moons:Masser Fade In Start", - "Moons:Masser Fade In Finish", - "Moons:Masser Fade Out Start", - "Moons:Masser Fade Out Finish", - - // color code hardcoded in mwrender/sky.cpp (l.417, mMoonRed) - // as float values (divided by 255) - "Moons:Script Color", - - // not used "Moons:Secunda Size", "Moons:Secunda Axis Offset", "Moons:Secunda Speed", "Moons:Secunda Daily Increment", "Moons:Secunda Moon Shadow Early Fade Angle", + "Moons:Secunda Fade Start Angle", + "Moons:Secunda Fade End Angle", + "Moons:Secunda Fade In Start", + "Moons:Secunda Fade In Finish", + "Moons:Secunda Fade Out Start", + "Moons:Secunda Fade Out Finish", "Moons:Masser Size", "Moons:Masser Axis Offset", "Moons:Masser Speed", "Moons:Masser Daily Increment", "Moons:Masser Moon Shadow Early Fade Angle", + "Moons:Masser Fade Start Angle", + "Moons:Masser Fade End Angle", + "Moons:Masser Fade In Start", + "Moons:Masser Fade In Finish", + "Moons:Masser Fade Out Start", + "Moons:Masser Fade Out Finish", + "Moons:Script Color", 0 }; From f56182cac28631740e139b963ebcf3545d157529 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 30 Dec 2012 17:06:17 +0100 Subject: [PATCH 251/378] fix journal window not rendering any text --- apps/openmw/mwgui/journalwindow.cpp | 13 ++++--------- apps/openmw/mwgui/journalwindow.hpp | 4 +--- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index 9d5d236be..bbd3d4d28 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -84,9 +84,9 @@ book formatText(std::string text,book mBook,int maxLine, int lineSize) MWGui::JournalWindow::JournalWindow (MWBase::WindowManager& parWindowManager) : WindowBase("openmw_journal.layout", parWindowManager) , mLastPos(0) - , mVisible(false) , mPageNumber(0) { + mMainWidget->setVisible(false); //setCoord(0,0,498, 342); center(); @@ -115,20 +115,15 @@ MWGui::JournalWindow::JournalWindow (MWBase::WindowManager& parWindowManager) //displayLeftText(list.front()); } -void MWGui::JournalWindow::setVisible(bool visible) +void MWGui::JournalWindow::close() { - if (mVisible && !visible) - MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); - mVisible = visible; - - mMainWidget->setVisible(visible); + MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); } void MWGui::JournalWindow::open() { mPageNumber = 0; - std::string journalOpenSound = "book open"; - MWBase::Environment::get().getSoundManager()->playSound (journalOpenSound, 1.0, 1.0); + MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); if(MWBase::Environment::get().getJournal()->begin()!=MWBase::Environment::get().getJournal()->end()) { book journal; diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index a62e48803..3317880cd 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -15,8 +15,7 @@ namespace MWGui public: JournalWindow(MWBase::WindowManager& parWindowManager); virtual void open(); - - virtual void setVisible(bool visible); // only used to play close sound + virtual void close(); private: void displayLeftText(std::string text); @@ -41,7 +40,6 @@ namespace MWGui std::vector mLeftPages; std::vector mRightPages; int mPageNumber; //store the number of the current left page - bool mVisible; }; } From 69d9d22579c4ddf2b4b5fffb7355bd0b5413f0cb Mon Sep 17 00:00:00 2001 From: eduard Date: Sun, 30 Dec 2012 19:56:38 +0100 Subject: [PATCH 252/378] comparestring --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 16 +- apps/openmw/mwgui/inventorywindow.cpp | 2 +- apps/openmw/mwscript/containerextensions.cpp | 15 +- apps/openmw/mwworld/worldimp.cpp | 2 +- components/esm_store/reclists.hpp | 674 ------------------ components/misc/stringops.cpp | 26 - components/misc/stringops.hpp | 6 - 7 files changed, 18 insertions(+), 723 deletions(-) delete mode 100644 components/esm_store/reclists.hpp diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 8e74894b9..80999c5ff 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -45,8 +45,8 @@ namespace //helper function std::string::size_type find_str_ci(const std::string& str, const std::string& substr,size_t pos) - { - return Misc::toLower(str).find(Misc::toLower(substr),pos); + { + return Misc::StringUtils::toLower(const_cast(str)).find(Misc::StringUtils::toLower(const_cast(substr)).c_str(),pos); } } @@ -70,13 +70,13 @@ namespace MWDialogue MWWorld::Store::iterator it = dialogs.begin(); for (; it != dialogs.end(); ++it) { - mDialogueMap[Misc::toLower(it->mId)] = *it; + mDialogueMap[Misc::StringUtils::toLower(const_cast(it->mId))] = *it; } } void DialogueManager::addTopic (const std::string& topic) { - mKnownTopics[Misc::toLower(topic)] = true; + mKnownTopics[Misc::StringUtils::toLower(const_cast(topic))] = true; } void DialogueManager::parseText (const std::string& text) @@ -274,10 +274,10 @@ namespace MWDialogue { if (filter.search (*iter)) { - mActorKnownTopics.push_back (Misc::toLower (iter->mId)); + mActorKnownTopics.push_back ( Misc::StringUtils::toLower(const_cast(iter->mId))); //does the player know the topic? - if (mKnownTopics.find (Misc::toLower (iter->mId)) != mKnownTopics.end()) + if (mKnownTopics.find ( Misc::StringUtils::toLower(const_cast(iter->mId))) != mKnownTopics.end()) { keywordList.push_back (iter->mId); } @@ -335,7 +335,7 @@ namespace MWDialogue win->setServices (windowServices); // sort again, because the previous sort was case-sensitive - keywordList.sort(Misc::stringCompareNoCase); + keywordList.sort(Misc::StringUtils::ciEqual); win->setKeywords(keywordList); mChoice = choice; @@ -415,7 +415,7 @@ namespace MWDialogue { MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); win->askQuestion(question); - mChoiceMap[Misc::toLower(question)] = choice; + mChoiceMap[Misc::StringUtils::toLower(const_cast(question))] = choice; mIsInChoice = true; } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 1ac5b2fc6..dd75b8216 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -271,7 +271,7 @@ namespace MWGui for (MWWorld::ContainerStoreIterator it = invStore.begin(); it != invStore.end(); ++it) { - if (Misc::toLower(it->getCellRef().mRefID) == "gold_001") + if (Misc::StringUtils::toLower(it->getCellRef().mRefID) == "gold_001") return it->getRefData().getCount(); } return 0; diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 114704388..2cdad2a90 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -72,7 +72,7 @@ namespace MWScript Interpreter::Type_Integer sum = 0; for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) - if (Misc::toLower(iter->getCellRef().mRefID) == Misc::toLower(item)) + if (Misc::StringUtils::toLower(iter->getCellRef().mRefID) == Misc::StringUtils::toLower(item)) sum += iter->getRefData().getCount(); runtime.push (sum); @@ -105,7 +105,7 @@ namespace MWScript for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end() && count; ++iter) { - if (Misc::toLower(iter->getCellRef().mRefID) == Misc::toLower(item)) + if (Misc::StringUtils::toLower(iter->getCellRef().mRefID) == Misc::StringUtils::toLower(item)) { itemName = MWWorld::Class::get(*iter).getName(*iter); @@ -163,7 +163,7 @@ namespace MWScript MWWorld::ContainerStoreIterator it = invStore.begin(); for (; it != invStore.end(); ++it) { - if (toLower(it->getCellRef().mRefID) == toLower(item)) + if (Misc::StringUtils::toLower(it->getCellRef().mRefID) == Misc::StringUtils::toLower(item)) break; } if (it == invStore.end()) @@ -263,7 +263,7 @@ namespace MWScript for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) { MWWorld::ContainerStoreIterator it = invStore.getSlot (slot); - if (it != invStore.end() && toLower(it->getCellRef().mRefID) == toLower(item)) + if (it != invStore.end() && Misc::StringUtils::toLower(it->getCellRef().mRefID) == Misc::StringUtils::toLower(item)) { runtime.push(1); return; @@ -281,8 +281,9 @@ namespace MWScript virtual void execute(Interpreter::Runtime &runtime) { MWWorld::Ptr ptr = R()(runtime); - - std::string creatureName = toLower (runtime.getStringLiteral (runtime[0].mInteger)); + + const std::string &name = runtime.getStringLiteral (runtime[0].mInteger); + std::string creatureName = Misc::StringUtils::toLower (const_cast(name)); runtime.pop(); MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore (ptr); @@ -290,7 +291,7 @@ namespace MWScript it != invStore.end(); ++it) { - if (toLower(it->getCellRef().mSoul) == toLower(creatureName)) + if (Misc::StringUtils::toLower(it->getCellRef().mSoul) == Misc::StringUtils::toLower(creatureName)) { runtime.push(1); return; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 83c6b20e5..e38fcf717 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -621,7 +621,7 @@ namespace MWWorld { if (isPlayer) if (!newCell.isExterior()) - changeToInteriorCell(Misc::toLower(newCell.mCell->mName), pos); + changeToInteriorCell(Misc::StringUtils::toLower(const_cast (newCell.mCell->mName)), pos); else { int cellX = newCell.mCell->getGridX(); diff --git a/components/esm_store/reclists.hpp b/components/esm_store/reclists.hpp deleted file mode 100644 index f9f74244b..000000000 --- a/components/esm_store/reclists.hpp +++ /dev/null @@ -1,674 +0,0 @@ -#ifndef _GAME_ESM_RECLISTS_H -#define _GAME_ESM_RECLISTS_H - -#include "components/esm/records.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -using namespace boost::algorithm; - -namespace ESMS -{ - using namespace ESM; - - struct RecList - { - virtual ~RecList() {} - - virtual void load(ESMReader &esm, const std::string &id) = 0; - virtual int getSize() = 0; - virtual void listIdentifier (std::vector& identifier) const = 0; - - }; - - typedef std::map RecListList; - - template - struct RecListT : RecList - { - virtual ~RecListT() {} - - typedef std::map MapType; - - MapType list; - - // Load one object of this type - void load(ESMReader &esm, const std::string &id) - { - std::string id2 = Misc::toLower (id); - list[id2].load(esm); - } - - // Find the given object ID, or return NULL if not found. - const X* search(const std::string &id) const - { - std::string id2 = Misc::toLower (id); - - typename MapType::const_iterator iter = list.find (id2); - - if (iter == list.end()) - return NULL; - - return &iter->second; - } - - // Find the given object ID (throws an exception if not found) - const X* find(const std::string &id) const - { - const X *object = search (id); - - if (!object) - throw std::runtime_error ("object " + id + " not found"); - - return object; - } - - int getSize() { return list.size(); } - - virtual void listIdentifier (std::vector& identifier) const - { - for (typename MapType::const_iterator iter (list.begin()); iter!=list.end(); ++iter) - identifier.push_back (iter->first); - } - }; - - // Same as RecListT, but does not case-smash the IDs - // Note that lookups (search or find) are still case insensitive - template - struct RecListCaseT : RecList - { - virtual ~RecListCaseT() {} - - typedef std::map MapType; - - MapType list; - - // Load one object of this type - void load(ESMReader &esm, const std::string &id) - { - //std::string id2 = Misc::toLower (id); - - list[id].load(esm); - } - - // Find the given object ID, or return NULL if not found. - const X* search(const std::string &id) const - { - std::string id2 = Misc::toLower (id); - - for (typename MapType::const_iterator iter = list.begin(); - iter != list.end(); ++iter) - { - if (Misc::toLower(iter->first) == id2) - return &iter->second; - } - - return NULL; - } - - // non-const version - X* search(const std::string &id) - { - std::string id2 = Misc::toLower (id); - - for (typename MapType::iterator iter = list.begin(); - iter != list.end(); ++iter) - { - if (Misc::toLower(iter->first) == id2) - return &iter->second; - } - - return NULL; - } - - // Find the given object ID (throws an exception if not found) - const X* find(const std::string &id) const - { - const X *object = search (id); - - if (!object) - throw std::runtime_error ("object " + id + " not found"); - - return object; - } - - int getSize() { return list.size(); } - - virtual void listIdentifier (std::vector& identifier) const - { - for (typename MapType::const_iterator iter (list.begin()); iter!=list.end(); ++iter) - identifier.push_back (iter->first); - } - }; - - /// Modified version of RecListT for records, that need to store their own ID - template - struct RecListWithIDT : RecList - { - virtual ~RecListWithIDT() {} - - typedef std::map MapType; - - MapType list; - - // Load one object of this type - void load(ESMReader &esm, const std::string &id) - { - std::string id2 = Misc::toLower (id); - list[id2].mId = id2; - list[id2].load(esm); - } - - // Find the given object ID, or return NULL if not found. - const X* search(const std::string &id) const - { - std::string id2 = Misc::toLower (id); - - typename MapType::const_iterator iter = list.find (id2); - - if (iter == list.end()) - return NULL; - return &iter->second; - } - - // Find the given object ID (throws an exception if not found) - const X* find(const std::string &id) const - { - const X *object = search (id); - - if (!object) - throw std::runtime_error ("object " + id + " not found"); - - return object; - } - - int getSize() { return list.size(); } - - virtual void listIdentifier (std::vector& identifier) const - { - for (typename MapType::const_iterator iter (list.begin()); iter!=list.end(); ++iter) - identifier.push_back (iter->first); - } - }; - - // The only difference to the above is a slight change to the load() - // function. We might merge these together later, and store the id - // in all the structs. - template - struct RecIDListT : RecList - { - virtual ~RecIDListT() {} - - typedef std::map MapType; - - MapType list; - - void load(ESMReader &esm, const std::string &id) - { - std::string id2 = Misc::toLower (id); - X& ref = list[id2]; - - ref.mId = id; - ref.load(esm); - } - - // Find the given object ID, or return NULL if not found. - const X* search(const std::string &id) const - { - std::string id2 = Misc::toLower (id); - - typename MapType::const_iterator iter = list.find (id2); - - if (iter == list.end()) - return NULL; - - return &iter->second; - } - - // Find the given object ID (throws an exception if not found) - const X* find(const std::string &id) const - { - const X *object = search (id); - - if (!object) - throw std::runtime_error ("object " + id + " not found"); - - return object; - } - - int getSize() { return list.size(); } - - virtual void listIdentifier (std::vector& identifier) const - { - for (typename MapType::const_iterator iter (list.begin()); iter!=list.end(); ++iter) - identifier.push_back (iter->first); - } - }; - - /* Land textures are indexed by an integer number - */ - struct LTexList : RecList - { - virtual ~LTexList() {} - - // TODO: For multiple ESM/ESP files we need one list per file. - std::vector ltex; - - LTexList() - { - // More than enough to hold Morrowind.esm. - ltex.reserve(128); - } - - const LandTexture* search(size_t index) const - { - assert(index < ltex.size()); - return <ex.at(index); - } - - int getSize() { return ltex.size(); } - int getSize() const { return ltex.size(); } - - virtual void listIdentifier (std::vector& identifier) const {} - - void load(ESMReader &esm, const std::string &id) - { - LandTexture lt; - lt.load(esm); - lt.mId = id; - - // Make sure we have room for the structure - if(lt.mIndex + 1 > (int)ltex.size()) - ltex.resize(lt.mIndex+1); - - // Store it - ltex[lt.mIndex] = lt; - } - }; - - /* Landscapes are indexed by the X,Y coordinates of the exterior - cell they belong to. - */ - struct LandList : RecList - { - virtual ~LandList() - { - for ( LandMap::iterator itr = lands.begin(); itr != lands.end(); ++itr ) - { - delete itr->second; - } - } - - // Map containing all landscapes - typedef std::pair LandCoord; - typedef std::map LandMap; - LandMap lands; - - int count; - LandList() : count(0) {} - int getSize() { return count; } - - virtual void listIdentifier (std::vector& identifier) const {} - - // Find land for the given coordinates. Return null if no mData. - Land *search(int x, int y) const - { - LandMap::const_iterator itr = lands.find(std::make_pair (x, y)); - if ( itr == lands.end() ) - { - return NULL; - } - - return itr->second; - } - - void load(ESMReader &esm, const std::string &id) - { - count++; - - // Create the structure and load it. This actually skips the - // landscape data and remembers the file position for later. - Land *land = new Land(); - land->load(esm); - - // Store the structure - lands[std::make_pair (land->mX, land->mY)] = land; - } - }; - - struct ciLessBoost : std::binary_function -{ - bool operator() (const std::string & s1, const std::string & s2) const { - //case insensitive version of is_less - return lexicographical_compare(s1, s2, is_iless()); - } -}; - - - // Cells aren't simply indexed by name. Exterior cells are treated - // separately. - // TODO: case handling (cell names are case-insensitive, but they are also showen to the - // player, so we can't simply smash case. - struct CellList : RecList - { - // Total cell count. Used for statistics. - int count; - CellList() : count(0) {} - int getSize() { return count; } - - // List of interior cells. Indexed by cell name. - typedef std::map IntCells; - IntCells intCells; - - // List of exterior cells. Indexed as extCells[mX][mY]. - typedef std::map, ESM::Cell*> ExtCells; - ExtCells extCells; - - virtual void listIdentifier (std::vector& identifier) const - { - for (IntCells::const_iterator iter (intCells.begin()); iter!=intCells.end(); ++iter) - identifier.push_back (iter->first); - } - - virtual ~CellList() - { - for (IntCells::iterator it = intCells.begin(); it!=intCells.end(); ++it) - delete it->second; - - for (ExtCells::iterator it = extCells.begin(); it!=extCells.end(); ++it) - delete it->second; - } - - const ESM::Cell* searchInt(const std::string &id) const - { - IntCells::const_iterator iter = intCells.find(id); - - if (iter!=intCells.end()) - return iter->second; - - return 0; - } - - const ESM::Cell* findInt(const std::string &id) const - { - const ESM::Cell *cell = searchInt (id); - - if (!cell) - throw std::runtime_error ("Interior cell not found - " + id); - - return cell; - } - - const ESM::Cell *searchExt (int x, int y) const - { - ExtCells::const_iterator it = extCells.find (std::make_pair (x, y)); - - if (it==extCells.end()) - return 0; - - return it->second; - } - - const ESM::Cell *findExt (int x, int y) const - { - const ESM::Cell *cell = searchExt (x, y); - - if (!cell) - throw std::runtime_error ("Exterior cell not found"); - - return cell; - } - const ESM::Cell *searchExtByName (const std::string& id) const - { - for (ExtCells::const_iterator iter = extCells.begin(); iter!=extCells.end(); ++iter) - { - if (Misc::toLower (iter->second->mName) == Misc::toLower (id)) - return iter->second; - } - - return 0; - } - - const ESM::Cell *searchExtByRegion (const std::string& id) const - { - std::string id2 = Misc::toLower (id); - - for (ExtCells::const_iterator iter = extCells.begin(); iter!=extCells.end(); ++iter) - if (Misc::toLower (iter->second->mRegion)==id) - return iter->second; - - return 0; - } - - void load(ESMReader &esm, const std::string &id) - { - count++; - - // All cells have a name record, even nameless exterior cells. - ESM::Cell *cell = new ESM::Cell; - cell->mName = id; - - // The cell itself takes care of all the hairy details - cell->load(esm); - - if(cell->mData.mFlags & ESM::Cell::Interior) - { - // Store interior cell by name - intCells[id] = cell; - } - else - { - // Store exterior cells by grid position - extCells[std::make_pair (cell->mData.mX, cell->mData.mY)] = cell; - } - } - }; - - struct PathgridList : RecList - { - int count; - - // List of grids for interior cells. Indexed by cell name. - typedef std::map IntGrids; - IntGrids intGrids; - - // List of grids for exterior cells. Indexed as extCells[mX][mY]. - typedef std::map, ESM::Pathgrid*> ExtGrids; - ExtGrids extGrids; - - PathgridList() : count(0) {} - - virtual ~PathgridList() - { - for (IntGrids::iterator it = intGrids.begin(); it!=intGrids.end(); ++it) - delete it->second; - - for (ExtGrids::iterator it = extGrids.begin(); it!=extGrids.end(); ++it) - delete it->second; - } - - int getSize() { return count; } - - virtual void listIdentifier (std::vector& identifier) const - { - // do nothing - } - - void load(ESMReader &esm, const std::string &id) - { - count++; - ESM::Pathgrid *grid = new ESM::Pathgrid; - grid->load(esm); - if (grid->mData.mX == 0 && grid->mData.mY == 0) - { - intGrids[grid->mCell] = grid; - } - else - { - extGrids[std::make_pair(grid->mData.mX, grid->mData.mY)] = grid; - } - } - - Pathgrid *find(int cellX, int cellY, const std::string &cellName) const - { - Pathgrid *result = search(cellX, cellY, cellName); - if (!result) - { - throw std::runtime_error("no pathgrid found for cell " + cellName); - } - return result; - } - - Pathgrid *search(int cellX, int cellY, const std::string &cellName) const - { - Pathgrid *result = NULL; - if (cellX == 0 && cellY == 0) // possibly interior - { - IntGrids::const_iterator it = intGrids.find(cellName); - if (it != intGrids.end()) - result = it->second; - } - else - { - ExtGrids::const_iterator it = extGrids.find(std::make_pair(cellX, cellY)); - if (it != extGrids.end()) - result = it->second; - } - return result; - } - - Pathgrid *search(const ESM::Cell &cell) const - { - int cellX, cellY; - if (cell.mData.mFlags & ESM::Cell::Interior) - { - cellX = cellY = 0; - } - else - { - cellX = cell.mData.mX; - cellY = cell.mData.mY; - } - return search(cellX, cellY, cell.mName); - } - }; - - template - struct ScriptListT : RecList - { - virtual ~ScriptListT() {} - - typedef std::map MapType; - - MapType list; - - // Load one object of this type - void load(ESMReader &esm, const std::string &id) - { - X ref; - ref.load (esm); - - std::string realId = Misc::toLower (ref.mData.mName.toString()); - - std::swap (list[realId], ref); - } - - // Find the given object ID, or return NULL if not found. - const X* search(const std::string &id) const - { - std::string id2 = Misc::toLower (id); - - typename MapType::const_iterator iter = list.find (id2); - - if (iter == list.end()) - return NULL; - - return &iter->second; - } - - // Find the given object ID (throws an exception if not found) - const X* find(const std::string &id) const - { - const X *object = search (id); - - if (!object) - throw std::runtime_error ("object " + id + " not found"); - - return object; - } - - int getSize() { return list.size(); } - - virtual void listIdentifier (std::vector& identifier) const - { - for (typename MapType::const_iterator iter (list.begin()); iter!=list.end(); ++iter) - identifier.push_back (iter->first); - } - }; - - template - struct IndexListT - { - virtual ~IndexListT() {} - - typedef std::map MapType; - - MapType list; - - void load(ESMReader &esm) - { - X ref; - ref.load (esm); - int index = ref.mIndex; - list[index] = ref; - } - - int getSize() - { - return list.size(); - } - - virtual void listIdentifier (std::vector& identifier) const {} - - // Find the given object ID, or return NULL if not found. - const X* search (int id) const - { - typename MapType::const_iterator iter = list.find (id); - - if (iter == list.end()) - return NULL; - - return &iter->second; - } - - // Find the given object ID (throws an exception if not found) - const X* find (int id) const - { - const X *object = search (id); - - if (!object) - { - std::ostringstream error; - error << "object " << id << " not found"; - throw std::runtime_error (error.str()); - } - - return object; - } - }; - - /* We need special lists for: - - Path grids - */ -} -#endif diff --git a/components/misc/stringops.cpp b/components/misc/stringops.cpp index 51cf2105a..0bc8e290a 100644 --- a/components/misc/stringops.cpp +++ b/components/misc/stringops.cpp @@ -67,30 +67,4 @@ bool iends(const char* str1, const char* str2) return strcasecmp(str2, str1+len1-len2) == 0; } -std::string toLower (const std::string& name) -{ - std::string lowerCase; - - std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); - - return lowerCase; -} - -bool stringCompareNoCase (std::string first, std::string second) -{ - unsigned int i=0; - while ( (itolower(second[i])) return false; - ++i; - } - if (first.length() Date: Mon, 31 Dec 2012 04:15:47 +0400 Subject: [PATCH 253/378] 1. Dialogs in Russian version now works. 2. Destination names it travel window are now translated --- apps/openmw/engine.cpp | 2 +- apps/openmw/mwbase/windowmanager.hpp | 4 + apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 103 ++++++++++++++++-- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 19 +++- apps/openmw/mwgui/dialogue.cpp | 83 ++++++++++++-- apps/openmw/mwgui/dialogue.hpp | 12 +- apps/openmw/mwgui/travelwindow.cpp | 2 +- apps/openmw/mwgui/windowmanagerimp.cpp | 5 + apps/openmw/mwgui/windowmanagerimp.hpp | 2 + components/translation/translation.cpp | 30 +++-- components/translation/translation.hpp | 7 +- 11 files changed, 236 insertions(+), 33 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index b995300d5..e2d28a808 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -360,7 +360,7 @@ void OMW::Engine::go() // Create dialog system mEnvironment.setJournal (new MWDialogue::Journal); - mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mVerboseScripts)); + mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mVerboseScripts, mTranslationDataStorage)); // Sets up the input system mEnvironment.setInputManager (new MWInput::InputManager (*mOgre, diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index c177912d4..30bfced06 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -7,6 +7,8 @@ #include +#include + #include "../mwmechanics/stat.hpp" #include "../mwgui/mode.hpp" @@ -233,6 +235,8 @@ namespace MWBase virtual void startSpellMaking(MWWorld::Ptr actor) = 0; virtual void startEnchanting(MWWorld::Ptr actor) = 0; virtual void startTraining(MWWorld::Ptr actor) = 0; + + virtual const Translation::Storage& getTranslationDataStorage() const = 0; }; } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 26d5af202..68837be3c 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -52,7 +52,7 @@ namespace return lowerCase; } - bool stringCompareNoCase (std::string first, std::string second) + bool stringCompareNoCase (const std::string& first, const std::string& second) { unsigned int i=0; while ( (i::iterator it; - for(it = mActorKnownTopics.begin();it != mActorKnownTopics.end();++it) + std::vector hypertext = ParseHyperText(text); + + //calculation of standard form fir all hyperlinks + for (size_t i = 0; i < hypertext.size(); ++i) { - size_t pos = find_str_ci(text,*it,0); - if(pos !=std::string::npos) + if (hypertext[i].mLink) { - mKnownTopics[*it] = true; + size_t asterisk_count = MWDialogue::RemovePseudoAsterisks(hypertext[i].mText); + for(; asterisk_count > 0; --asterisk_count) + hypertext[i].mText.append("*"); + + hypertext[i].mText = mTranslationDataStorage.topicStandardForm(hypertext[i].mText); } } + + for (size_t i = 0; i < hypertext.size(); ++i) + { + std::list::iterator it; + for(it = mActorKnownTopics.begin(); it != mActorKnownTopics.end(); ++it) + { + if (hypertext[i].mLink) + { + if( hypertext[i].mText == *it ) + { + mKnownTopics[hypertext[i].mText] = true; + } + } + else if( !mTranslationDataStorage.hasTranslation() ) + { + size_t pos = find_str_ci(hypertext[i].mText, *it, 0); + if(pos !=std::string::npos) + { + mKnownTopics[*it] = true; + } + } + } + } + updateTopics(); } @@ -156,7 +186,7 @@ namespace MWDialogue } parseText (info->mResponse); - + MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); executeScript (info->mResultScript); @@ -298,7 +328,7 @@ namespace MWDialogue { if (filter.search (*iter)) { - mActorKnownTopics.push_back (toLower (iter->mId)); + mActorKnownTopics.push_back(toLower (iter->mId)); //does the player know the topic? if (mKnownTopics.find (toLower (iter->mId)) != mKnownTopics.end()) @@ -415,7 +445,7 @@ namespace MWDialogue mIsInChoice = false; std::string text = info->mResponse; parseText (text); - + MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (Interpreter::fixDefinesDialog(text, interpreterContext)); executeScript (info->mResultScript); @@ -499,4 +529,57 @@ namespace MWDialogue { mTemporaryDispositionChange += delta; } + + std::vector ParseHyperText(const std::string& text) + { + std::vector result; + + MyGUI::UString utext(text); + + size_t pos_begin, pos_end, iteration_pos = 0; + for(;;) + { + pos_begin = utext.find('@', iteration_pos); + if (pos_begin != std::string::npos) + pos_end = utext.find('#', pos_begin); + + if (pos_begin != std::string::npos && pos_end != std::string::npos) + { + result.push_back( HyperTextToken(utext.substr(iteration_pos, pos_begin - iteration_pos), false) ); + + std::string link = utext.substr(pos_begin + 1, pos_end - pos_begin - 1); + result.push_back( HyperTextToken(link, true) ); + + iteration_pos = pos_end + 1; + } + else + { + result.push_back( HyperTextToken(utext.substr(iteration_pos), false) ); + break; + } + } + + return result; + } + + size_t RemovePseudoAsterisks(std::string& phrase) + { + size_t pseudoAsterisksCount = 0; + const char specialPseudoAsteriskCharacter = 127; + + if( !phrase.empty() ) + { + std::string::reverse_iterator rit = phrase.rbegin(); + + while( rit != phrase.rend() && *rit == specialPseudoAsteriskCharacter ) + { + pseudoAsterisksCount++; + ++rit; + } + } + + phrase = phrase.substr(0, phrase.length() - pseudoAsterisksCount); + + return pseudoAsterisksCount; + } } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 98b27f774..1ca2ae5eb 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -7,6 +7,7 @@ #include #include +#include #include "../mwworld/ptr.hpp" @@ -20,6 +21,7 @@ namespace MWDialogue std::map mKnownTopics;// Those are the topics the player knows. std::list mActorKnownTopics; + Translation::Storage& mTranslationDataStorage; MWScript::CompilerContext mCompilerContext; std::ostream mErrorStream; Compiler::StreamErrorHandler mErrorHandler; @@ -50,7 +52,7 @@ namespace MWDialogue public: - DialogueManager (const Compiler::Extensions& extensions, bool scriptVerbose); + DialogueManager (const Compiler::Extensions& extensions, bool scriptVerbose, Translation::Storage& translationDataStorage); virtual void startDialogue (const MWWorld::Ptr& actor); @@ -72,6 +74,21 @@ namespace MWDialogue virtual int getTemporaryDispositionChange () const; virtual void applyTemporaryDispositionChange (int delta); }; + + + struct HyperTextToken + { + HyperTextToken(const std::string& text, bool link) : mText(text), mLink(link) {} + + std::string mText; + bool mLink; + }; + + // In translations (at least Russian) the links are marked with @#, so + // it should be a function to parse it + std::vector ParseHyperText(const std::string& text); + + size_t RemovePseudoAsterisks(std::string& phrase); } #endif diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 258e9174c..0dbe37941 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -16,6 +16,8 @@ #include "../mwmechanics/npcstats.hpp" +#include "../mwdialogue/dialoguemanagerimp.hpp" + #include "dialogue_history.hpp" #include "widgets.hpp" #include "list.hpp" @@ -52,7 +54,6 @@ bool sortByLength (const std::string& left, const std::string& right) { return left.size() > right.size(); } - } @@ -131,6 +132,7 @@ DialogueWindow::DialogueWindow(MWBase::WindowManager& parWindowManager) , mPersuasionDialog(parWindowManager) , mEnabled(false) , mServices(0) + , mWindowManager(parWindowManager) { // Centre dialog center(); @@ -180,9 +182,30 @@ void DialogueWindow::onHistoryClicked(MyGUI::Widget* _sender) if(color != "#B29154") { MyGUI::UString key = mHistory->getColorTextAt(cursorPosition); - if(color == "#686EBA") MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(key)); - if(color == "#572D21") MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key)); + if(color == "#686EBA") + { + std::map::iterator i = mHyperLinks.upper_bound(cursorPosition); + if( !mHyperLinks.empty() ) + { + --i; + + if( i->first + i->second.mLength > cursorPosition) + { + MWBase::Environment::get().getDialogueManager()->keywordSelected(i->second.mTrueValue); + } + } + else + { + // the link was colored, but it is not in mHyperLinks. + // It means that those liunks are not marked with @# and found + // by topic name search + MWBase::Environment::get().getDialogueManager()->keywordSelected(lower_string(key)); + } + } + + if(color == "#572D21") + MWBase::Environment::get().getDialogueManager()->questionAnswered(lower_string(key)); } } @@ -258,6 +281,7 @@ void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName) setTitle(npcName); mTopicsList->clear(); + mHyperLinks.clear(); mHistory->setCaption(""); updateOptions(); } @@ -340,7 +364,7 @@ void addColorInString(std::string& str, const std::string& keyword,std::string c } } -std::string DialogueWindow::parseText(std::string text) +std::string DialogueWindow::parseText(const std::string& text) { bool separatorReached = false; // only parse topics that are below the separator (this prevents actions like "Barter" that are not topics from getting blue-colored) @@ -358,11 +382,55 @@ std::string DialogueWindow::parseText(std::string text) // sort by length to make sure longer topics are replaced first std::sort(topics.begin(), topics.end(), sortByLength); - for(std::vector::const_iterator it = topics.begin(); it != topics.end(); ++it) + std::vector hypertext = MWDialogue::ParseHyperText(text); + + size_t historySize = 0; + if(mHistory->getClient()->getSubWidgetText() != nullptr) + { + historySize = mHistory->getOnlyText().size(); + } + + std::string result; + size_t hypertextPos = 0; + for (size_t i = 0; i < hypertext.size(); ++i) { - addColorInString(text,*it,"#686EBA","#B29154"); + if (hypertext[i].mLink) + { + size_t asterisk_count = MWDialogue::RemovePseudoAsterisks(hypertext[i].mText); + std::string standardForm = hypertext[i].mText; + for(; asterisk_count > 0; --asterisk_count) + standardForm.append("*"); + + standardForm = + mWindowManager.getTranslationDataStorage().topicStandardForm(standardForm); + + if( std::find(topics.begin(), topics.end(), std::string(standardForm) ) != topics.end() ) + { + result.append("#686EBA").append(hypertext[i].mText).append("#B29154"); + + mHyperLinks[historySize+hypertextPos].mLength = MyGUI::UString(hypertext[i].mText).length(); + mHyperLinks[historySize+hypertextPos].mTrueValue = lower_string(standardForm); + } + else + result += hypertext[i].mText; + } + else + { + if( !mWindowManager.getTranslationDataStorage().hasTranslation() ) + { + for(std::vector::const_iterator it = topics.begin(); it != topics.end(); ++it) + { + addColorInString(hypertext[i].mText, *it, "#686EBA", "#B29154"); + } + } + + result += hypertext[i].mText; + } + + hypertextPos += MyGUI::UString(hypertext[i].mText).length(); } - return text; + + return result; } void DialogueWindow::addText(std::string text) @@ -394,6 +462,7 @@ void DialogueWindow::updateOptions() { //Clear the list of topics mTopicsList->clear(); + mHyperLinks.clear(); mHistory->eraseText(0, mHistory->getTextLength()); if (mPtr.getTypeName() == typeid(ESM::NPC).name()) diff --git a/apps/openmw/mwgui/dialogue.hpp b/apps/openmw/mwgui/dialogue.hpp index 082d92524..3eeea01cd 100644 --- a/apps/openmw/mwgui/dialogue.hpp +++ b/apps/openmw/mwgui/dialogue.hpp @@ -92,12 +92,18 @@ namespace MWGui virtual void onReferenceUnavailable(); + struct HyperLink + { + size_t mLength; + std::string mTrueValue; + }; + private: void updateOptions(); /** *Helper function that add topic keyword in blue in a text. */ - std::string parseText(std::string text); + std::string parseText(const std::string& text); int mServices; @@ -109,6 +115,10 @@ namespace MWGui MyGUI::EditPtr mDispositionText; PersuasionDialog mPersuasionDialog; + + MWBase::WindowManager& mWindowManager; + + std::map mHyperLinks; }; } #endif diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index abbc6172f..9615e95f7 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -82,7 +82,7 @@ namespace MWGui oss << price; toAdd->setUserString("price",oss.str()); - toAdd->setCaptionWithReplacing(travelId+" - "+boost::lexical_cast(price)+"#{sgp}"); + toAdd->setCaptionWithReplacing("#{sCell=" + travelId + "} - " + boost::lexical_cast(price)+"#{sgp}"); toAdd->setSize(toAdd->getTextSize().width,sLineHeight); toAdd->eventMouseWheel += MyGUI::newDelegate(this, &TravelWindow::onMouseWheel); toAdd->setUserString("Destination", travelId); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index caf6a4ab0..12e72eaa9 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1050,3 +1050,8 @@ void WindowManager::startTraining(MWWorld::Ptr actor) { mTrainingWindow->startTraining(actor); } + +const Translation::Storage& WindowManager::getTranslationDataStorage() const +{ + return mTranslationDataStorage; +} diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index aaa856aef..8bcb88e22 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -225,6 +225,8 @@ namespace MWGui virtual void startEnchanting(MWWorld::Ptr actor); virtual void startTraining(MWWorld::Ptr actor); + virtual const Translation::Storage& getTranslationDataStorage() const; + private: OEngine::GUI::MyGUIManager *mGuiManager; HUD *mHud; diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index b559c6c1c..fb5b03861 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -81,17 +81,7 @@ namespace Translation std::string Storage::topicID(const std::string& phrase) const { - std::string result; - - //seeking for the standard phrase form - std::map::const_iterator phraseFormsIterator = - mPhraseForms.find(phrase); - - if (phraseFormsIterator != mPhraseForms.end()) - result = phraseFormsIterator->second; - else - result = phrase; - + std::string result = topicStandardForm(phrase); //seeking for the topic ID std::map::const_iterator topicIDIterator = @@ -103,8 +93,26 @@ namespace Translation return result; } + std::string Storage::topicStandardForm(const std::string& phrase) const + { + std::map::const_iterator phraseFormsIterator = + mPhraseForms.find(phrase); + + if (phraseFormsIterator != mPhraseForms.end()) + return phraseFormsIterator->second; + else + return phrase; + } + void Storage::setEncoding (const ToUTF8::FromType& encoding) { mEncoding = encoding; } + + bool Storage::hasTranslation() const + { + return !mCellNamesTranslations.empty() || + !mTopicIDs.empty() || + !mPhraseForms.empty(); + } } diff --git a/components/translation/translation.hpp b/components/translation/translation.hpp index 668d4c067..80d44d871 100644 --- a/components/translation/translation.hpp +++ b/components/translation/translation.hpp @@ -16,8 +16,13 @@ namespace Translation std::string translateCellName(const std::string& cellName) const; std::string topicID(const std::string& phrase) const; + // Standard form usually means nominative case + std::string topicStandardForm(const std::string& phrase) const; + void setEncoding (const ToUTF8::FromType& encoding); + bool hasTranslation() const; + private: typedef std::map ContainerType; @@ -30,7 +35,7 @@ namespace Translation ToUTF8::FromType mEncoding; - std::map mCellNamesTranslations, mTopicIDs, mPhraseForms; + ContainerType mCellNamesTranslations, mTopicIDs, mPhraseForms; }; } From 7e0713f6c4d414a0df087f7d4954c36709b02ce0 Mon Sep 17 00:00:00 2001 From: greye Date: Mon, 31 Dec 2012 11:22:40 +0400 Subject: [PATCH 254/378] fix string case update --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 23 ++++++------------- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 1 - apps/openmw/mwgui/inventorywindow.cpp | 2 +- apps/openmw/mwscript/containerextensions.cpp | 13 +++++------ apps/openmw/mwworld/worldimp.cpp | 2 +- 5 files changed, 15 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 80999c5ff..d81421f81 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -40,16 +40,6 @@ #include "filter.hpp" -namespace -{ - - //helper function - std::string::size_type find_str_ci(const std::string& str, const std::string& substr,size_t pos) - { - return Misc::StringUtils::toLower(const_cast(str)).find(Misc::StringUtils::toLower(const_cast(substr)).c_str(),pos); - } -} - namespace MWDialogue { DialogueManager::DialogueManager (const Compiler::Extensions& extensions, bool scriptVerbose) : @@ -70,13 +60,13 @@ namespace MWDialogue MWWorld::Store::iterator it = dialogs.begin(); for (; it != dialogs.end(); ++it) { - mDialogueMap[Misc::StringUtils::toLower(const_cast(it->mId))] = *it; + mDialogueMap[Misc::StringUtils::lowerCase(it->mId)] = *it; } } void DialogueManager::addTopic (const std::string& topic) { - mKnownTopics[Misc::StringUtils::toLower(const_cast(topic))] = true; + mKnownTopics[Misc::StringUtils::lowerCase(topic)] = true; } void DialogueManager::parseText (const std::string& text) @@ -84,7 +74,7 @@ namespace MWDialogue std::list::iterator it; for(it = mActorKnownTopics.begin();it != mActorKnownTopics.end();++it) { - size_t pos = find_str_ci(text,*it,0); + size_t pos = Misc::StringUtils::lowerCase(text).find(*it, 0); if(pos !=std::string::npos) { mKnownTopics[*it] = true; @@ -274,10 +264,11 @@ namespace MWDialogue { if (filter.search (*iter)) { - mActorKnownTopics.push_back ( Misc::StringUtils::toLower(const_cast(iter->mId))); + std::string lower = Misc::StringUtils::lowerCase(iter->mId); + mActorKnownTopics.push_back (lower); //does the player know the topic? - if (mKnownTopics.find ( Misc::StringUtils::toLower(const_cast(iter->mId))) != mKnownTopics.end()) + if (mKnownTopics.find (lower) != mKnownTopics.end()) { keywordList.push_back (iter->mId); } @@ -415,7 +406,7 @@ namespace MWDialogue { MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); win->askQuestion(question); - mChoiceMap[Misc::StringUtils::toLower(const_cast(question))] = choice; + mChoiceMap[Misc::StringUtils::lowerCase(question)] = choice; mIsInChoice = true; } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index b0bef5ea0..98b27f774 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -71,7 +71,6 @@ namespace MWDialogue virtual void persuade (int type); virtual int getTemporaryDispositionChange () const; virtual void applyTemporaryDispositionChange (int delta); - void toLower(std::string question); }; } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index dd75b8216..ffb6c5282 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -271,7 +271,7 @@ namespace MWGui for (MWWorld::ContainerStoreIterator it = invStore.begin(); it != invStore.end(); ++it) { - if (Misc::StringUtils::toLower(it->getCellRef().mRefID) == "gold_001") + if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, "gold_001")) return it->getRefData().getCount(); } return 0; diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 2cdad2a90..1fa69d1fd 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -72,7 +72,7 @@ namespace MWScript Interpreter::Type_Integer sum = 0; for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) - if (Misc::StringUtils::toLower(iter->getCellRef().mRefID) == Misc::StringUtils::toLower(item)) + if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item)) sum += iter->getRefData().getCount(); runtime.push (sum); @@ -105,7 +105,7 @@ namespace MWScript for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end() && count; ++iter) { - if (Misc::StringUtils::toLower(iter->getCellRef().mRefID) == Misc::StringUtils::toLower(item)) + if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item)) { itemName = MWWorld::Class::get(*iter).getName(*iter); @@ -163,7 +163,7 @@ namespace MWScript MWWorld::ContainerStoreIterator it = invStore.begin(); for (; it != invStore.end(); ++it) { - if (Misc::StringUtils::toLower(it->getCellRef().mRefID) == Misc::StringUtils::toLower(item)) + if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, item)) break; } if (it == invStore.end()) @@ -263,7 +263,7 @@ namespace MWScript for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) { MWWorld::ContainerStoreIterator it = invStore.getSlot (slot); - if (it != invStore.end() && Misc::StringUtils::toLower(it->getCellRef().mRefID) == Misc::StringUtils::toLower(item)) + if (it != invStore.end() && Misc::StringUtils::ciEqual(it->getCellRef().mRefID, item)) { runtime.push(1); return; @@ -282,8 +282,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - const std::string &name = runtime.getStringLiteral (runtime[0].mInteger); - std::string creatureName = Misc::StringUtils::toLower (const_cast(name)); + const std::string &name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore (ptr); @@ -291,7 +290,7 @@ namespace MWScript it != invStore.end(); ++it) { - if (Misc::StringUtils::toLower(it->getCellRef().mSoul) == Misc::StringUtils::toLower(creatureName)) + if (Misc::StringUtils::ciEqual(it->getCellRef().mSoul, name)) { runtime.push(1); return; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e38fcf717..3995123b0 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -621,7 +621,7 @@ namespace MWWorld { if (isPlayer) if (!newCell.isExterior()) - changeToInteriorCell(Misc::StringUtils::toLower(const_cast (newCell.mCell->mName)), pos); + changeToInteriorCell(Misc::StringUtils::lowerCase(newCell.mCell->mName), pos); else { int cellX = newCell.mCell->getGridX(); From e2b348de9620c23695064f585d7ea4600b2abe88 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Mon, 31 Dec 2012 16:44:22 +0100 Subject: [PATCH 255/378] mwiniimporter: fix line ending problem --- apps/mwiniimporter/importer.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index dda8fd8ba..30822516e 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -660,10 +660,19 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) { std::string line; while (std::getline(file, line)) { + // unify Unix-style and Windows file ending + if (!(line.empty()) && (line[line.length()-1]) == '\r') { + line = line.substr(0, line.length()-1); + } + if(line[0] == '[') { - if(line.length() > 2) { - section = line.substr(1, line.length()-2); + int pos = line.find(']'); + if(pos < 2) { + std::cout << "Warning: ini file wrongly formatted (" << line << "). Line ignored." << std::endl; + continue; } + + section = line.substr(1, line.find(']')-1); continue; } From e9a464c9f7463c175f38e4966600ef3f839f68d1 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Mon, 31 Dec 2012 16:45:24 +0100 Subject: [PATCH 256/378] mwiniimporter: remove fallback values for Cursors --- apps/mwiniimporter/importer.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 30822516e..c15818ae3 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -146,13 +146,6 @@ MwIniImporter::MwIniImporter() "FontColor:color_negative", "FontColor:color_count", - // cursors - "Cursors:Cursor 0", - "Cursors:Cursor 1", - "Cursors:Cursor 2", - "Cursors:Cursor 3", - "Cursors:Cursor 4", - // level up messages "Level Up:Level2", "Level Up:Level3", From 008d6d6589e9709efdede92a94f1bd706df9fe06 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sun, 30 Dec 2012 14:59:09 -0800 Subject: [PATCH 257/378] added additional library search paths to find bullet This allowed CMake to correctly build Visual Studio 2010 project files against a windows compiled version of bullet (2.81 rev 2613) --- cmake/FindBullet.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/FindBullet.cmake b/cmake/FindBullet.cmake index 8d5ea2f1e..97feddffe 100644 --- a/cmake/FindBullet.cmake +++ b/cmake/FindBullet.cmake @@ -27,6 +27,8 @@ macro(_FIND_BULLET_LIBRARY _var) ${ARGN} PATHS ${BULLET_ROOT} + ${BULLET_ROOT}/lib/Debug + ${BULLET_ROOT}/lib/Release ${BULLET_ROOT}/out/release8/libs ${BULLET_ROOT}/out/debug8/libs PATH_SUFFIXES lib From 7228f5d696fa641a0bced370b86bb875330f2534 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sun, 30 Dec 2012 21:16:54 -0800 Subject: [PATCH 258/378] added missing reference to added missing reference to that was causing Visual Studio 2010 to fail to compile stringops.hpp --- components/misc/stringops.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index 112d66bb8..029b617e1 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -1,6 +1,7 @@ #ifndef MISC_STRINGOPS_H #define MISC_STRINGOPS_H +#include #include #include From a842fc2c11e30d189a41be930cf9db55eaddddfa Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sun, 30 Dec 2012 22:02:55 -0800 Subject: [PATCH 259/378] added hack to fix an alignment issue in MSVC 2010 The default allocator in Visual Studio 2010 does not respect the alignment requirements of classes it is allocating memory for. This results in a potential crash when using OEngine::Physics::PhysicActor that has been allocated on the heap if it is built against a version of Bullet that has SSE intrinsics enabled. --- libs/openengine/bullet/physic.hpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index f320d009d..76bdb491d 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -122,8 +122,13 @@ namespace Physic */ void runPmove(); - - +//HACK: in Visual Studio 2010 and presumably above, this structures alignment +// must be 16, but the built in operator new & delete don't properly +// perform this alignment. +#if _MSC_VER >= 1600 + void * operator new (size_t Size) { return _aligned_malloc (Size, 16); } + void operator delete (void * Data) { _aligned_free (Data); } +#endif private: From 08fa9dcd3ebe6a92eb38be671dfe63800647c4fc Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Tue, 1 Jan 2013 11:59:05 -0800 Subject: [PATCH 260/378] replaced std::string concatenation with std::ostringstream Changed a block of code that was performing a series of string concatenations to use an ostringstream instead. This allowed the removal of calls to std::to_string (not C++03 compliant) and fixes an compile error in MSVC 2010 (ambigous overload when calling std::to_string). --- components/interpreter/defines.cpp | 87 +++++++++++++++--------------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/components/interpreter/defines.cpp b/components/interpreter/defines.cpp index 29c78200d..18e5f81c1 100644 --- a/components/interpreter/defines.cpp +++ b/components/interpreter/defines.cpp @@ -1,6 +1,7 @@ #include "defines.hpp" #include +#include #include #include @@ -24,113 +25,113 @@ namespace Interpreter{ std::string fixDefinesReal(std::string text, char eschar, bool isBook, Context& context){ unsigned int start = 0; - std::string retval = ""; + std::ostringstream retval; for(unsigned int i = 0; i < text.length(); i++){ if(text[i] == eschar){ - retval += text.substr(start, i - start); + retval << text.substr(start, i - start); std::string temp = text.substr(i+1, 100); transform(temp.begin(), temp.end(), temp.begin(), ::tolower); bool found; if( (found = Check(temp, "actionslideright", &i, &start))){ - retval += context.getActionBinding("#{sRight}"); + retval << context.getActionBinding("#{sRight}"); } else if((found = Check(temp, "actionreadymagic", &i, &start))){ - retval += context.getActionBinding("#{sReady_Magic}"); + retval << context.getActionBinding("#{sReady_Magic}"); } else if((found = Check(temp, "actionprevweapon", &i, &start))){ - retval += "PLACEHOLDER_ACTION_PREV_WEAPON"; + retval << "PLACEHOLDER_ACTION_PREV_WEAPON"; } else if((found = Check(temp, "actionnextweapon", &i, &start))){ - retval += "PLACEHOLDER_ACTION_PREV_WEAPON"; + retval << "PLACEHOLDER_ACTION_PREV_WEAPON"; } else if((found = Check(temp, "actiontogglerun", &i, &start))){ - retval += context.getActionBinding("#{sAuto_Run}"); + retval << context.getActionBinding("#{sAuto_Run}"); } else if((found = Check(temp, "actionslideleft", &i, &start))){ - retval += context.getActionBinding("#{sLeft}"); + retval << context.getActionBinding("#{sLeft}"); } else if((found = Check(temp, "actionreadyitem", &i, &start))){ - retval += context.getActionBinding("#{sReady_Weapon}"); + retval << context.getActionBinding("#{sReady_Weapon}"); } else if((found = Check(temp, "actionprevspell", &i, &start))){ - retval += "PLACEHOLDER_ACTION_PREV_SPELL"; + retval << "PLACEHOLDER_ACTION_PREV_SPELL"; } else if((found = Check(temp, "actionnextspell", &i, &start))){ - retval += "PLACEHOLDER_ACTION_NEXT_SPELL"; + retval << "PLACEHOLDER_ACTION_NEXT_SPELL"; } else if((found = Check(temp, "actionrestmenu", &i, &start))){ - retval += context.getActionBinding("#{sRestKey}"); + retval << context.getActionBinding("#{sRestKey}"); } else if((found = Check(temp, "actionmenumode", &i, &start))){ - retval += context.getActionBinding("#{sJournal}"); + retval << context.getActionBinding("#{sJournal}"); } else if((found = Check(temp, "actionactivate", &i, &start))){ - retval += context.getActionBinding("#{sActivate}"); + retval << context.getActionBinding("#{sActivate}"); } else if((found = Check(temp, "actionjournal", &i, &start))){ - retval += context.getActionBinding("#{sJournal}"); + retval << context.getActionBinding("#{sJournal}"); } else if((found = Check(temp, "actionforward", &i, &start))){ - retval += context.getActionBinding("#{sForward}"); + retval << context.getActionBinding("#{sForward}"); } else if((found = Check(temp, "pccrimelevel", &i, &start))){ - retval += std::to_string(context.getPCBounty()); + retval << context.getPCBounty(); } else if((found = Check(temp, "actioncrouch", &i, &start))){ - retval += context.getActionBinding("#{sCrouch_Sneak}"); + retval << context.getActionBinding("#{sCrouch_Sneak}"); } else if((found = Check(temp, "actionjump", &i, &start))){ - retval += context.getActionBinding("#{sJump}"); + retval << context.getActionBinding("#{sJump}"); } else if((found = Check(temp, "actionback", &i, &start))){ - retval += context.getActionBinding("#{sBack}"); + retval << context.getActionBinding("#{sBack}"); } else if((found = Check(temp, "actionuse", &i, &start))){ - retval += "PLACEHOLDER_ACTION_USE"; + retval << "PLACEHOLDER_ACTION_USE"; } else if((found = Check(temp, "actionrun", &i, &start))){ - retval += "PLACEHOLDER_ACTION_RUN"; + retval << "PLACEHOLDER_ACTION_RUN"; } else if((found = Check(temp, "pcclass", &i, &start))){ - retval += context.getPCClass(); + retval << context.getPCClass(); } else if((found = Check(temp, "pcrace", &i, &start))){ - retval += context.getPCRace(); + retval << context.getPCRace(); } else if((found = Check(temp, "pcname", &i, &start))){ - retval += context.getPCName(); + retval << context.getPCName(); } else if((found = Check(temp, "cell", &i, &start))){ - retval += context.getCurrentCellName(); + retval << context.getCurrentCellName(); } else if(eschar == '%' && !isBook) { // In Dialogue, not messagebox if( (found = Check(temp, "faction", &i, &start))){ - retval += context.getNPCFaction(); + retval << context.getNPCFaction(); } else if((found = Check(temp, "nextpcrank", &i, &start))){ - retval += context.getPCNextRank(); + retval << context.getPCNextRank(); } else if((found = Check(temp, "pcnextrank", &i, &start))){ - retval += context.getPCNextRank(); + retval << context.getPCNextRank(); } else if((found = Check(temp, "pcrank", &i, &start))){ - retval += context.getPCRank(); + retval << context.getPCRank(); } else if((found = Check(temp, "rank", &i, &start))){ - retval += context.getNPCRank(); + retval << context.getNPCRank(); } else if((found = Check(temp, "class", &i, &start))){ - retval += context.getNPCClass(); + retval << context.getNPCClass(); } else if((found = Check(temp, "race", &i, &start))){ - retval += context.getNPCRace(); + retval << context.getNPCRace(); } else if((found = Check(temp, "name", &i, &start))){ - retval += context.getNPCName(); + retval << context.getNPCName(); } } else { // In messagebox or book, not dialogue @@ -144,13 +145,13 @@ namespace Interpreter{ /* uses pc in messageboxes */ else if((found = Check(temp, "class", &i, &start))){ - retval += context.getPCClass(); + retval << context.getPCClass(); } else if((found = Check(temp, "race", &i, &start))){ - retval += context.getPCRace(); + retval << context.getPCRace(); } else if((found = Check(temp, "name", &i, &start))){ - retval += context.getPCName(); + retval << context.getPCName(); } } @@ -172,9 +173,9 @@ namespace Interpreter{ char type = context.getGlobalType(globals[j]); switch(type){ - case 's': retval += std::to_string(context.getGlobalShort(globals[j])); break; - case 'l': retval += std::to_string(context.getGlobalLong(globals[j])); break; - case 'f': retval += std::to_string(context.getGlobalFloat(globals[j])); break; + case 's': retval << context.getGlobalShort(globals[j]); break; + case 'l': retval << context.getGlobalLong(globals[j]); break; + case 'f': retval << context.getGlobalFloat(globals[j]); break; } break; } @@ -186,12 +187,12 @@ namespace Interpreter{ /* leave unmodified */ i += 1; start = i; - retval += eschar; + retval << eschar; } } } - retval += text.substr(start, text.length() - start); - return retval; + retval << text.substr(start, text.length() - start); + return retval.str (); } std::string fixDefinesDialog(std::string text, Context& context){ From 2a7336c310eb668015e39265c5e4f4f2d1216edb Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 1 Jan 2013 21:59:30 +0100 Subject: [PATCH 261/378] mwiniimporter: handle ini file encodings Use components/to_utf8 to handle encodings. Add an --encoding option for choosing between win1250, win1251 and win1252 encodings for the imported ini file. --- apps/mwiniimporter/importer.cpp | 15 +++++++++++++++ apps/mwiniimporter/importer.hpp | 5 +++++ apps/mwiniimporter/main.cpp | 9 +++++++++ 3 files changed, 29 insertions(+) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index c15818ae3..def70615b 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -653,6 +653,8 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) { std::string line; while (std::getline(file, line)) { + line = toUTF8(line); + // unify Unix-style and Windows file ending if (!(line.empty()) && (line[line.length()-1]) == '\r') { line = line.substr(0, line.length()-1); @@ -826,3 +828,16 @@ void MwIniImporter::writeToFile(boost::iostreams::stream #include +#include "../../components/to_utf8/to_utf8.hpp" + class MwIniImporter { public: typedef std::map strmap; typedef std::map > multistrmap; MwIniImporter(); + void setInputEncoding(const ToUTF8::FromType& encoding); void setVerbose(bool verbose); multistrmap loadIniFile(std::string filename); multistrmap loadCfgFile(std::string filename); @@ -25,9 +28,11 @@ class MwIniImporter { private: void insertMultistrmap(multistrmap &cfg, std::string key, std::string value); std::string numberToString(int n); + std::string toUTF8(const std::string &str); bool mVerbose; strmap mMergeMap; std::vector mMergeFallback; + ToUTF8::FromType mEncoding; }; diff --git a/apps/mwiniimporter/main.cpp b/apps/mwiniimporter/main.cpp index 234d7d57d..e90f26dd2 100644 --- a/apps/mwiniimporter/main.cpp +++ b/apps/mwiniimporter/main.cpp @@ -18,6 +18,11 @@ int main(int argc, char *argv[]) { ("cfg,c", bpo::value(), "openmw.cfg file") ("output,o", bpo::value()->default_value(""), "openmw.cfg file") ("game-files,g", "import esm and esp files") + ("encoding,e", bpo::value()-> default_value("win1252"), + "Character encoding used in OpenMW game messages:\n" + "\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n" + "\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n" + "\n\twin1252 - Western European (Latin) alphabet, used by default") ; p_desc.add("ini", 1).add("cfg", 1); @@ -57,6 +62,10 @@ int main(int argc, char *argv[]) { MwIniImporter importer; importer.setVerbose(vm.count("verbose")); + // Font encoding settings + std::string encoding(vm["encoding"].as()); + importer.setInputEncoding(ToUTF8::calculateEncoding(encoding)); + MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile); MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile); From 86f30992d7f1979f3ac187add917e793a1671c1b Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 1 Jan 2013 22:00:14 +0100 Subject: [PATCH 262/378] fix message boxes not appearing properly when e.g. trading --- apps/openmw/mwgui/windowmanagerimp.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 6164f9037..7f5d34939 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -539,9 +539,8 @@ void WindowManager::messageBox (const std::string& message, const std::vectoraddMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(message)); - else mMessageBoxManager->createMessageBox(message); } From c613f07b84f1ebabd758c1e4a993e883b22f7c1b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 2 Jan 2013 10:10:10 +0100 Subject: [PATCH 263/378] updated credits file --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index 72451c3d0..713fbe9ac 100644 --- a/credits.txt +++ b/credits.txt @@ -32,6 +32,7 @@ Lukasz Gromanowski (lgro) Marcin Hulist (Gohan) Michael Mc Donnell Michael Papageorgiou (werdanith) +Nathan Jeffords (blunted2night) Nikolay Kasyanov (corristo) Pieter van der Kloet (pvdk) Roman Melnik (Kromgart) From 04cca2a1ce85e73e9fe0d4a6e3b948b7ebf5d603 Mon Sep 17 00:00:00 2001 From: lazydev Date: Wed, 2 Jan 2013 18:58:52 +0400 Subject: [PATCH 264/378] fix for http://bugs.openmw.org/issues/517 --- apps/openmw/mwgui/container.cpp | 8 ++++---- apps/openmw/mwgui/tradewindow.cpp | 16 ++++++++++------ apps/openmw/mwgui/tradewindow.hpp | 4 ++-- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 20bc95445..479a82efa 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -195,13 +195,13 @@ void ContainerBase::sellAlreadyBoughtItem(MyGUI::Widget* _sender, int count) if (isInventory()) { MWBase::Environment::get().getWindowManager()->getTradeWindow()->addItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->sellToNpc(object, count, true); MWBase::Environment::get().getWindowManager()->getTradeWindow()->drawItems(); } else { MWBase::Environment::get().getWindowManager()->getInventoryWindow()->addItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->sellToNpc(object, count); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count, true); MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); } @@ -218,13 +218,13 @@ void ContainerBase::sellItem(MyGUI::Widget* _sender, int count) if (isInventory()) { MWBase::Environment::get().getWindowManager()->getTradeWindow()->addBarteredItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->sellToNpc(object, count, false); MWBase::Environment::get().getWindowManager()->getTradeWindow()->drawItems(); } else { MWBase::Environment::get().getWindowManager()->getInventoryWindow()->addBarteredItem(object, count); - MWBase::Environment::get().getWindowManager()->getTradeWindow()->sellToNpc(object, count); + MWBase::Environment::get().getWindowManager()->getTradeWindow()->buyFromNpc(object, count, false); MWBase::Environment::get().getWindowManager()->getInventoryWindow()->drawItems(); } diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index a005c618f..51eb3d87e 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -401,19 +401,23 @@ namespace MWGui return items; } - void TradeWindow::sellToNpc(MWWorld::Ptr item, int count) + void TradeWindow::sellToNpc(MWWorld::Ptr item, int count, bool boughtItem) { + int diff = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, MWWorld::Class::get(item).getValue(item) * count, boughtItem); + + mCurrentBalance += diff; + mCurrentMerchantOffer += diff; - mCurrentBalance -= MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, MWWorld::Class::get(item).getValue(item) * count,true); - mCurrentMerchantOffer -= MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, MWWorld::Class::get(item).getValue(item) * count,true); updateLabels(); } - void TradeWindow::buyFromNpc(MWWorld::Ptr item, int count) + void TradeWindow::buyFromNpc(MWWorld::Ptr item, int count, bool soldItem) { + int diff = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, MWWorld::Class::get(item).getValue(item) * count, !soldItem); + + mCurrentBalance -= diff; + mCurrentMerchantOffer -= diff; - mCurrentBalance += MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, MWWorld::Class::get(item).getValue(item) * count,false); - mCurrentMerchantOffer += MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, MWWorld::Class::get(item).getValue(item) * count,false); updateLabels(); } diff --git a/apps/openmw/mwgui/tradewindow.hpp b/apps/openmw/mwgui/tradewindow.hpp index db386d8b6..219e44036 100644 --- a/apps/openmw/mwgui/tradewindow.hpp +++ b/apps/openmw/mwgui/tradewindow.hpp @@ -27,8 +27,8 @@ namespace MWGui void startTrade(MWWorld::Ptr actor); - void sellToNpc(MWWorld::Ptr item, int count); ///< only used for adjusting the gold balance - void buyFromNpc(MWWorld::Ptr item, int count); ///< only used for adjusting the gold balance + void sellToNpc(MWWorld::Ptr item, int count, bool boughtItem); ///< only used for adjusting the gold balance + void buyFromNpc(MWWorld::Ptr item, int count, bool soldItem); ///< only used for adjusting the gold balance bool npcAcceptsItem(MWWorld::Ptr item); From ddbe13e5694230b889a6d5606cc36e3a3098b1c1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 3 Jan 2013 01:07:17 +0100 Subject: [PATCH 265/378] Workaround for http://bugs.openmw.org/issues/475 --- apps/openmw/mwinput/inputmanagerimp.cpp | 9 ++++++++- apps/openmw/mwinput/inputmanagerimp.hpp | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 6a1c3aa9b..995da8f34 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -40,6 +40,7 @@ namespace MWInput , mMouseLookEnabled(true) , mMouseX(ogre.getWindow()->getWidth ()/2.f) , mMouseY(ogre.getWindow()->getHeight ()/2.f) + , mMouseWheel(0) , mUserFile(userFile) , mDragDrop(false) , mGuiCursorEnabled(false) @@ -242,6 +243,11 @@ namespace MWInput mKeyboard->capture(); mMouse->capture(); + // inject some fake mouse movement to force updating MyGUI's widget states + // this shouldn't do any harm since we're moving back to the original position afterwards + MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX+1), int(mMouseY+1), mMouseWheel); + MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), mMouseWheel); + // update values of channels (as a result of pressed keys) if (!loading) mInputCtrl->update(dt); @@ -486,8 +492,9 @@ namespace MWInput mMouseY += float(arg.state.Y.rel) * mUISensitivity * mUIYMultiplier; mMouseX = std::max(0.f, std::min(mMouseX, float(viewSize.width))); mMouseY = std::max(0.f, std::min(mMouseY, float(viewSize.height))); + mMouseWheel = arg.state.Z.abs; - MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), arg.state.Z.abs); + MyGUI::InputManager::getInstance().injectMouseMove( int(mMouseX), int(mMouseY), mMouseWheel); } if (mMouseLookEnabled) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 718d6b76f..9deed1f28 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -147,6 +147,7 @@ namespace MWInput float mMouseX; float mMouseY; + int mMouseWheel; std::map mControlSwitch; From 1718d735b566786809de51803f308176d3e594a4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 3 Jan 2013 01:09:03 +0100 Subject: [PATCH 266/378] Fix menu/journal/book/scroll buttons when using localised MW-installations This patch implements a custom widget that can switch its texture on-the-fly, making it obsolete having to use an atlas to get a hover animation. This also removes the predefined size restriction and should now work with all button texture sizes. --- CMakeLists.txt | 1 - apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwgui/bookwindow.cpp | 8 +- apps/openmw/mwgui/bookwindow.hpp | 18 ++-- apps/openmw/mwgui/cursorreplace.cpp | 4 - apps/openmw/mwgui/imagebutton.cpp | 63 ++++++++++++++ apps/openmw/mwgui/imagebutton.hpp | 33 ++++++++ apps/openmw/mwgui/journalwindow.cpp | 5 +- apps/openmw/mwgui/journalwindow.hpp | 14 ++- apps/openmw/mwgui/mainmenu.cpp | 92 +++++++++----------- apps/openmw/mwgui/mainmenu.hpp | 16 ++-- apps/openmw/mwgui/scrollwindow.hpp | 5 +- apps/openmw/mwgui/windowmanagerimp.cpp | 2 + files/mygui/CMakeLists.txt | 2 - files/mygui/atlas1.cfg | 51 ----------- files/mygui/mainmenu.cfg | 95 --------------------- files/mygui/openmw_book.layout | 24 ++++-- files/mygui/openmw_journal.layout | 12 ++- files/mygui/openmw_mainmenu_skin.xml | 30 ------- files/mygui/openmw_scroll.layout | 12 ++- libs/openengine/ogre/atlas.cpp | 113 ------------------------- libs/openengine/ogre/atlas.hpp | 27 ------ 22 files changed, 202 insertions(+), 427 deletions(-) create mode 100644 apps/openmw/mwgui/imagebutton.cpp create mode 100644 apps/openmw/mwgui/imagebutton.hpp delete mode 100644 files/mygui/atlas1.cfg delete mode 100644 files/mygui/mainmenu.cfg delete mode 100644 libs/openengine/ogre/atlas.cpp delete mode 100644 libs/openengine/ogre/atlas.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d7115583f..36e45d78a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,7 +104,6 @@ set(OENGINE_OGRE ${LIBDIR}/openengine/ogre/renderer.cpp ${LIBDIR}/openengine/ogre/fader.cpp ${LIBDIR}/openengine/ogre/imagerotate.cpp - ${LIBDIR}/openengine/ogre/atlas.cpp ${LIBDIR}/openengine/ogre/selectionbuffer.cpp ) set(OENGINE_GUI diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index e2a2e7f14..56147b500 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -30,7 +30,7 @@ add_openmw_dir (mwgui formatting inventorywindow container hud countdialog tradewindow settingswindow confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu itemselection spellbuyingwindow loadingscreen levelupdialog waitdialog spellcreationdialog - enchantingdialog trainingwindow travelwindow + enchantingdialog trainingwindow travelwindow imagebutton ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwgui/bookwindow.cpp b/apps/openmw/mwgui/bookwindow.cpp index bc3cd7b40..659795e18 100644 --- a/apps/openmw/mwgui/bookwindow.cpp +++ b/apps/openmw/mwgui/bookwindow.cpp @@ -88,7 +88,7 @@ void BookWindow::setTakeButtonShow(bool show) mTakeButton->setVisible(show); } -void BookWindow::onCloseButtonClicked (MyGUI::Widget* _sender) +void BookWindow::onCloseButtonClicked (MyGUI::Widget* sender) { // no 3d sounds because the object could be in a container. MWBase::Environment::get().getSoundManager()->playSound ("book close", 1.0, 1.0); @@ -96,7 +96,7 @@ void BookWindow::onCloseButtonClicked (MyGUI::Widget* _sender) mWindowManager.removeGuiMode(GM_Book); } -void BookWindow::onTakeButtonClicked (MyGUI::Widget* _sender) +void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender) { MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack); @@ -106,7 +106,7 @@ void BookWindow::onTakeButtonClicked (MyGUI::Widget* _sender) mWindowManager.removeGuiMode(GM_Book); } -void BookWindow::onNextPageButtonClicked (MyGUI::Widget* _sender) +void BookWindow::onNextPageButtonClicked (MyGUI::Widget* sender) { if ((mCurrentPage+1)*2 < mPages.size()) { @@ -118,7 +118,7 @@ void BookWindow::onNextPageButtonClicked (MyGUI::Widget* _sender) } } -void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* _sender) +void BookWindow::onPrevPageButtonClicked (MyGUI::Widget* sender) { if (mCurrentPage > 0) { diff --git a/apps/openmw/mwgui/bookwindow.hpp b/apps/openmw/mwgui/bookwindow.hpp index fedb783b2..5887975ea 100644 --- a/apps/openmw/mwgui/bookwindow.hpp +++ b/apps/openmw/mwgui/bookwindow.hpp @@ -5,6 +5,8 @@ #include "../mwworld/ptr.hpp" +#include "imagebutton.hpp" + namespace MWGui { class BookWindow : public WindowBase @@ -16,19 +18,19 @@ namespace MWGui void setTakeButtonShow(bool show); protected: - void onNextPageButtonClicked (MyGUI::Widget* _sender); - void onPrevPageButtonClicked (MyGUI::Widget* _sender); - void onCloseButtonClicked (MyGUI::Widget* _sender); - void onTakeButtonClicked (MyGUI::Widget* _sender); + void onNextPageButtonClicked (MyGUI::Widget* sender); + void onPrevPageButtonClicked (MyGUI::Widget* sender); + void onCloseButtonClicked (MyGUI::Widget* sender); + void onTakeButtonClicked (MyGUI::Widget* sender); void updatePages(); void clearPages(); private: - MyGUI::Button* mCloseButton; - MyGUI::Button* mTakeButton; - MyGUI::Button* mNextPageButton; - MyGUI::Button* mPrevPageButton; + MWGui::ImageButton* mCloseButton; + MWGui::ImageButton* mTakeButton; + MWGui::ImageButton* mNextPageButton; + MWGui::ImageButton* mPrevPageButton; MyGUI::TextBox* mLeftPageNumber; MyGUI::TextBox* mRightPageNumber; MyGUI::Widget* mLeftPage; diff --git a/apps/openmw/mwgui/cursorreplace.cpp b/apps/openmw/mwgui/cursorreplace.cpp index a4b6a100b..2079538fc 100644 --- a/apps/openmw/mwgui/cursorreplace.cpp +++ b/apps/openmw/mwgui/cursorreplace.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -14,7 +13,4 @@ CursorReplace::CursorReplace() OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_vresize.png", 90); OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize1.png", -45); OEngine::Render::ImageRotate::rotate("textures\\tx_cursormove.dds", "mwpointer_dresize2.png", 45); - - OEngine::Render::Atlas::createFromFile("atlas1.cfg", "mwgui1", "textures\\"); - OEngine::Render::Atlas::createFromFile("mainmenu.cfg", "mwgui2", "textures\\"); } diff --git a/apps/openmw/mwgui/imagebutton.cpp b/apps/openmw/mwgui/imagebutton.cpp new file mode 100644 index 000000000..98f05373b --- /dev/null +++ b/apps/openmw/mwgui/imagebutton.cpp @@ -0,0 +1,63 @@ +#include "imagebutton.hpp" + +#include + +namespace MWGui +{ + + void ImageButton::setPropertyOverride(const std::string &_key, const std::string &_value) + { + if (_key == "ImageHighlighted") + mImageHighlighted = _value; + else if (_key == "ImagePushed") + mImagePushed = _value; + else if (_key == "ImageNormal") + { + if (mImageNormal == "") + { + setImageTexture(_value); + } + mImageNormal = _value; + } + else + ImageBox::setPropertyOverride(_key, _value); + } + void ImageButton::onMouseSetFocus(Widget* _old) + { + setImageTexture(mImageHighlighted); + ImageBox::onMouseSetFocus(_old); + } + + void ImageButton::onMouseLostFocus(Widget* _new) + { + setImageTexture(mImageNormal); + ImageBox::onMouseLostFocus(_new); + } + + void ImageButton::onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id) + { + if (_id == MyGUI::MouseButton::Left) + setImageTexture(mImagePushed); + + ImageBox::onMouseButtonPressed(_left, _top, _id); + } + + MyGUI::IntSize ImageButton::getRequestedSize() + { + Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().getByName(mImageNormal); + if (texture.isNull()) + { + std::cerr << "ImageButton: can't find " << mImageNormal << std::endl; + return MyGUI::IntSize(0,0); + } + return MyGUI::IntSize (texture->getWidth(), texture->getHeight()); + } + + void ImageButton::onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id) + { + if (_id == MyGUI::MouseButton::Left) + setImageTexture(mImageHighlighted); + + ImageBox::onMouseButtonReleased(_left, _top, _id); + } +} diff --git a/apps/openmw/mwgui/imagebutton.hpp b/apps/openmw/mwgui/imagebutton.hpp new file mode 100644 index 000000000..9fce12da1 --- /dev/null +++ b/apps/openmw/mwgui/imagebutton.hpp @@ -0,0 +1,33 @@ +#ifndef MWGUI_IMAGEBUTTON_H +#define MWGUI_IMAGEBUTTON_H + +#include "MyGUI_ImageBox.h" + +namespace MWGui +{ + + /** + * @brief allows using different image textures depending on the button state + */ + class ImageButton : public MyGUI::ImageBox + { + MYGUI_RTTI_DERIVED(ImageButton) + + public: + MyGUI::IntSize getRequestedSize(); + + protected: + virtual void setPropertyOverride(const std::string& _key, const std::string& _value); + virtual void onMouseLostFocus(MyGUI::Widget* _new); + virtual void onMouseSetFocus(MyGUI::Widget* _old); + virtual void onMouseButtonPressed(int _left, int _top, MyGUI::MouseButton _id); + virtual void onMouseButtonReleased(int _left, int _top, MyGUI::MouseButton _id); + + std::string mImageHighlighted; + std::string mImageNormal; + std::string mImagePushed; + }; + +} + +#endif diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index bbd3d4d28..ba39b0101 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -83,7 +83,6 @@ book formatText(std::string text,book mBook,int maxLine, int lineSize) MWGui::JournalWindow::JournalWindow (MWBase::WindowManager& parWindowManager) : WindowBase("openmw_journal.layout", parWindowManager) - , mLastPos(0) , mPageNumber(0) { mMainWidget->setVisible(false); @@ -177,7 +176,7 @@ void MWGui::JournalWindow::displayRightText(std::string text) } -void MWGui::JournalWindow::notifyNextPage(MyGUI::WidgetPtr _sender) +void MWGui::JournalWindow::notifyNextPage(MyGUI::Widget* _sender) { if(mPageNumber < int(mLeftPages.size())-1) { @@ -189,7 +188,7 @@ void MWGui::JournalWindow::notifyNextPage(MyGUI::WidgetPtr _sender) } } -void MWGui::JournalWindow::notifyPrevPage(MyGUI::WidgetPtr _sender) +void MWGui::JournalWindow::notifyPrevPage(MyGUI::Widget* _sender) { if(mPageNumber > 0) { diff --git a/apps/openmw/mwgui/journalwindow.hpp b/apps/openmw/mwgui/journalwindow.hpp index 3317880cd..044a2b2a4 100644 --- a/apps/openmw/mwgui/journalwindow.hpp +++ b/apps/openmw/mwgui/journalwindow.hpp @@ -7,6 +7,7 @@ #include #include "window_base.hpp" +#include "imagebutton.hpp" namespace MWGui { @@ -25,18 +26,13 @@ namespace MWGui /** *Called when next/prev button is used. */ - void notifyNextPage(MyGUI::WidgetPtr _sender); - void notifyPrevPage(MyGUI::WidgetPtr _sender); + void notifyNextPage(MyGUI::Widget* _sender); + void notifyPrevPage(MyGUI::Widget* _sender); - static const int sLineHeight; - - MyGUI::WidgetPtr mSkillAreaWidget, mSkillClientWidget; - MyGUI::ScrollBar* mSkillScrollerWidget; - int mLastPos, mClientHeight; MyGUI::EditPtr mLeftTextWidget; MyGUI::EditPtr mRightTextWidget; - MyGUI::ButtonPtr mPrevBtn; - MyGUI::ButtonPtr mNextBtn; + MWGui::ImageButton* mPrevBtn; + MWGui::ImageButton* mNextBtn; std::vector mLeftPages; std::vector mRightPages; int mPageNumber; //store the number of the current left page diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index e98b75e9b..14309abc5 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -20,65 +20,57 @@ namespace MWGui { setCoord(0,0,w,h); - int height = 64 * 3; if (mButtonBox) MyGUI::Gui::getInstance ().destroyWidget(mButtonBox); - mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(w/2 - 64, h/2 - height/2, 128, height), MyGUI::Align::Default); + mButtonBox = mMainWidget->createWidget("", MyGUI::IntCoord(0, 0, 0, 0), MyGUI::Align::Default); int curH = 0; - mReturn = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); - mReturn->setImageResource ("Menu_Return"); - mReturn->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::returnToGame); - curH += 64; - - - /* - mNewGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); - mNewGame->setImageResource ("Menu_NewGame"); - curH += 64; - - mLoadGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); - mLoadGame->setImageResource ("Menu_LoadGame"); - curH += 64; - - - mSaveGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); - mSaveGame->setImageResource ("Menu_SaveGame"); - curH += 64; - */ - - mOptions = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); - mOptions->setImageResource ("Menu_Options"); - mOptions->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::showOptions); - curH += 64; - - /* - mCredits = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); - mCredits->setImageResource ("Menu_Credits"); - curH += 64; - */ - - mExitGame = mButtonBox->createWidget ("ButtonImage", MyGUI::IntCoord(0, curH, 128, 64), MyGUI::Align::Default); - mExitGame->setImageResource ("Menu_ExitGame"); - mExitGame->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::exitGame); - curH += 64; - } - - void MainMenu::returnToGame(MyGUI::Widget* sender) - { - MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); - } - - void MainMenu::showOptions(MyGUI::Widget* sender) - { - MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); + std::vector buttons; + buttons.push_back("return"); + //buttons.push_back("newgame"); + //buttons.push_back("loadgame"); + //buttons.push_back("savegame"); + buttons.push_back("options"); + //buttons.push_back("credits"); + buttons.push_back("exitgame"); + + int maxwidth = 0; + + mButtons.clear(); + for (std::vector::iterator it = buttons.begin(); it != buttons.end(); ++it) + { + MWGui::ImageButton* button = mButtonBox->createWidget + ("ImageBox", MyGUI::IntCoord(0, curH, 0, 0), MyGUI::Align::Default); + button->setProperty("ImageHighlighted", "textures\\menu_" + *it + "_over.dds"); + button->setProperty("ImageNormal", "textures\\menu_" + *it + ".dds"); + button->setProperty("ImagePushed", "textures\\menu_" + *it + "_pressed.dds"); + MyGUI::IntSize requested = button->getRequestedSize(); + button->eventMouseButtonClick += MyGUI::newDelegate(this, &MainMenu::onButtonClicked); + mButtons[*it] = button; + curH += requested.height; + + if (requested.width > maxwidth) + maxwidth = requested.width; + } + for (std::map::iterator it = mButtons.begin(); it != mButtons.end(); ++it) + { + MyGUI::IntSize requested = it->second->getRequestedSize(); + it->second->setCoord((maxwidth-requested.width) / 2, it->second->getTop(), requested.width, requested.height); + } + + mButtonBox->setCoord (w/2 - maxwidth/2, h/2 - curH/2, maxwidth, curH); } - void MainMenu::exitGame(MyGUI::Widget* sender) + void MainMenu::onButtonClicked(MyGUI::Widget *sender) { - Ogre::Root::getSingleton ().queueEndRendering (); + if (sender == mButtons["return"]) + MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); + else if (sender == mButtons["options"]) + MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); + else if (sender == mButtons["exitgame"]) + Ogre::Root::getSingleton ().queueEndRendering (); } } diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index fd583d187..4e76a64df 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -1,5 +1,7 @@ #include +#include "imagebutton.hpp" + namespace MWGui { @@ -11,19 +13,11 @@ namespace MWGui void onResChange(int w, int h); private: - MyGUI::Button* mReturn; - MyGUI::Button* mNewGame; - MyGUI::Button* mLoadGame; - MyGUI::Button* mSaveGame; - MyGUI::Button* mOptions; - MyGUI::Button* mCredits; - MyGUI::Button* mExitGame; - MyGUI::Widget* mButtonBox; - void returnToGame(MyGUI::Widget* sender); - void showOptions(MyGUI::Widget* sender); - void exitGame(MyGUI::Widget* sender); + std::map mButtons; + + void onButtonClicked (MyGUI::Widget* sender); }; } diff --git a/apps/openmw/mwgui/scrollwindow.hpp b/apps/openmw/mwgui/scrollwindow.hpp index b8f52fb65..7932a215b 100644 --- a/apps/openmw/mwgui/scrollwindow.hpp +++ b/apps/openmw/mwgui/scrollwindow.hpp @@ -2,6 +2,7 @@ #define MWGUI_SCROLLWINDOW_H #include "window_base.hpp" +#include "imagebutton.hpp" #include "../mwworld/ptr.hpp" @@ -20,8 +21,8 @@ namespace MWGui void onTakeButtonClicked (MyGUI::Widget* _sender); private: - MyGUI::Button* mCloseButton; - MyGUI::Button* mTakeButton; + MWGui::ImageButton* mCloseButton; + MWGui::ImageButton* mTakeButton; MyGUI::ScrollView* mTextView; MWWorld::Ptr mScroll; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 7f5d34939..099dafa6b 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -51,6 +51,7 @@ #include "spellcreationdialog.hpp" #include "enchantingdialog.hpp" #include "trainingwindow.hpp" +#include "imagebutton.hpp" using namespace MWGui; @@ -126,6 +127,7 @@ WindowManager::WindowManager( MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); + MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 562668a90..871be93d1 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -4,8 +4,6 @@ set(SDIR ${CMAKE_CURRENT_SOURCE_DIR}) set(DDIR ${OpenMW_BINARY_DIR}/resources/mygui) set(MYGUI_FILES - atlas1.cfg - mainmenu.cfg bigbars.png black.png core.skin diff --git a/files/mygui/atlas1.cfg b/files/mygui/atlas1.cfg deleted file mode 100644 index d1e05e041..000000000 --- a/files/mygui/atlas1.cfg +++ /dev/null @@ -1,51 +0,0 @@ -[settings] - size_x = 512 - size_y = 512 - -[tx_menubook_close_idle.dds] - x = 0 - y = 0 - -[tx_menubook_close_over.dds] - x = 128 - y = 0 - -[tx_menubook_close_pressed.dds] - x = 256 - y = 0 - -[tx_menubook_take_idle.dds] - x = 384 - y = 0 - -[tx_menubook_take_over.dds] - x = 0 - y = 32 - -[tx_menubook_take_pressed.dds] - x = 128 - y = 32 - -[tx_menubook_next_idle.dds] - x = 256 - y = 32 - -[tx_menubook_next_over.dds] - x = 384 - y = 32 - -[tx_menubook_next_pressed.dds] - x = 0 - y = 64 - -[tx_menubook_prev_idle.dds] - x = 128 - y = 64 - -[tx_menubook_prev_over.dds] - x = 256 - y = 64 - -[tx_menubook_prev_pressed.dds] - x = 384 - y = 64 diff --git a/files/mygui/mainmenu.cfg b/files/mygui/mainmenu.cfg deleted file mode 100644 index 7aaf8c1c7..000000000 --- a/files/mygui/mainmenu.cfg +++ /dev/null @@ -1,95 +0,0 @@ -[settings] - size_x = 512 - size_y = 512 - - -[menu_newgame.dds] - x = 0 - y = 0 - -[menu_newgame_pressed.dds] - x = 128 - y = 0 - -[menu_newgame_over.dds] - x = 256 - y = 0 - - -[menu_loadgame.dds] - x = 384 - y = 0 - -[menu_loadgame_pressed.dds] - x = 0 - y = 64 - -[menu_loadgame_over.dds] - x = 128 - y = 64 - - -[menu_options.dds] - x = 256 - y = 64 - -[menu_options_pressed.dds] - x = 384 - y = 64 - -[menu_options_over.dds] - x = 0 - y = 128 - - -[menu_credits.dds] - x = 128 - y = 128 - -[menu_credits_pressed.dds] - x = 256 - y = 128 - -[menu_credits_over.dds] - x = 384 - y = 128 - - -[menu_exitgame.dds] - x = 0 - y = 192 - -[menu_exitgame_pressed.dds] - x = 128 - y = 192 - -[menu_exitgame_over.dds] - x = 256 - y = 192 - - -[menu_savegame.dds] - x = 384 - y = 192 - -[menu_savegame_pressed.dds] - x = 0 - y = 256 - -[menu_savegame_over.dds] - x = 128 - y = 256 - - -[menu_return.dds] - x = 256 - y = 256 - -[menu_return_pressed.dds] - x = 384 - y = 256 - -[menu_return_over.dds] - x = 0 - y = 320 - diff --git a/files/mygui/openmw_book.layout b/files/mygui/openmw_book.layout index 6c708cdd3..894c1dbeb 100644 --- a/files/mygui/openmw_book.layout +++ b/files/mygui/openmw_book.layout @@ -7,17 +7,25 @@ - - + + + + - - + + + + - - + + + + - - + + + + diff --git a/files/mygui/openmw_journal.layout b/files/mygui/openmw_journal.layout index e4c3c7e47..fdf82e4de 100644 --- a/files/mygui/openmw_journal.layout +++ b/files/mygui/openmw_journal.layout @@ -7,11 +7,15 @@ - - + + + + - - + + + + diff --git a/files/mygui/openmw_mainmenu_skin.xml b/files/mygui/openmw_mainmenu_skin.xml index 4100a2eb7..c7f2fbce3 100644 --- a/files/mygui/openmw_mainmenu_skin.xml +++ b/files/mygui/openmw_mainmenu_skin.xml @@ -1,34 +1,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/files/mygui/openmw_scroll.layout b/files/mygui/openmw_scroll.layout index 0f4a0be3e..6315c0241 100644 --- a/files/mygui/openmw_scroll.layout +++ b/files/mygui/openmw_scroll.layout @@ -7,12 +7,16 @@ - - + + + + - - + + + + diff --git a/libs/openengine/ogre/atlas.cpp b/libs/openengine/ogre/atlas.cpp deleted file mode 100644 index 01b84afab..000000000 --- a/libs/openengine/ogre/atlas.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include "atlas.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace Ogre; -using namespace OEngine::Render; - -void Atlas::createFromFile (const std::string& filename, const std::string& textureName, const std::string& texturePrefix) -{ - ConfigFile file; - file.load(filename, ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME, "\t:=", true); - - Root* root = Ogre::Root::getSingletonPtr(); - - SceneManager* sceneMgr = root->createSceneManager(ST_GENERIC); - Camera* camera = sceneMgr->createCamera("AtlasCamera"); - - int width = StringConverter::parseInt(file.getSetting("size_x", "settings")); - int height = StringConverter::parseInt(file.getSetting("size_y", "settings")); - - std::vector rectangles; - int i = 0; - - ConfigFile::SectionIterator seci = file.getSectionIterator(); - while (seci.hasMoreElements()) - { - Ogre::String sectionName = seci.peekNextKey(); - seci.getNext(); - - if (sectionName == "settings" || sectionName == "") - continue; - - MaterialPtr material = MaterialManager::getSingleton().create("AtlasMaterial" + StringConverter::toString(i), ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - material->getTechnique(0)->getPass(0)->setLightingEnabled(false); - material->getTechnique(0)->getPass(0)->setDepthCheckEnabled(false); - TextureUnitState* tus = material->getTechnique(0)->getPass(0)->createTextureUnitState(texturePrefix + sectionName); - tus->setTextureBorderColour(ColourValue(0, 0, 0, 0)); - - Rectangle2D* rect = new Rectangle2D(true); - rect->setMaterial("AtlasMaterial" + StringConverter::toString(i)); - rect->setRenderQueueGroup(RENDER_QUEUE_BACKGROUND); - - int x = StringConverter::parseInt(file.getSetting("x", sectionName)); - int y = StringConverter::parseInt(file.getSetting("y", sectionName)); - - TexturePtr texture = TextureManager::getSingleton().getByName(texturePrefix + sectionName); - if (texture.isNull()) - { - std::cerr << "OEngine::Render::Atlas: Can't find texture " << texturePrefix + sectionName << ", skipping..." << std::endl; - continue; - } - int textureWidth = texture->getWidth(); - int textureHeight = texture->getHeight(); - - float left = x/float(width) * 2 - 1; - float top = (1-(y/float(height))) * 2 - 1; - float right = ((x+textureWidth))/float(width) * 2 - 1; - float bottom = (1-((y+textureHeight)/float(height))) * 2 - 1; - rect->setCorners(left, top, right, bottom); - - // Use infinite AAB to always stay visible - AxisAlignedBox aabInf; - aabInf.setInfinite(); - rect->setBoundingBox(aabInf); - - // Attach background to the scene - SceneNode* node = sceneMgr->getRootSceneNode()->createChildSceneNode(); - node->attachObject(rect); - - rectangles.push_back(rect); - ++i; - } - - TexturePtr destTexture = TextureManager::getSingleton().createManual( - textureName, - ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - TEX_TYPE_2D, - width, height, - 0, - PF_FLOAT16_RGBA, - TU_RENDERTARGET); - - RenderTarget* rtt = destTexture->getBuffer()->getRenderTarget(); - rtt->setAutoUpdated(false); - Viewport* vp = rtt->addViewport(camera); - vp->setOverlaysEnabled(false); - vp->setShadowsEnabled(false); - vp->setBackgroundColour(ColourValue(0,0,0,0)); - - rtt->update(); - - // remove all the junk we've created - for (std::vector::iterator it=rectangles.begin(); - it!=rectangles.end(); ++it) - { - delete (*it); - } - while (i > 0) - { - MaterialManager::getSingleton().remove("AtlasMaterial" + StringConverter::toString(i-1)); - --i; - } - root->destroySceneManager(sceneMgr); -} diff --git a/libs/openengine/ogre/atlas.hpp b/libs/openengine/ogre/atlas.hpp deleted file mode 100644 index 5dcd409ca..000000000 --- a/libs/openengine/ogre/atlas.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef OENGINE_OGRE_ATLAS_HPP -#define OENGINE_OGRE_ATLAS_HPP - -#include - -namespace OEngine -{ -namespace Render -{ - - /// \brief Creates a texture atlas at runtime - class Atlas - { - public: - /** - * @param absolute path to file that specifies layout of the texture (positions of the textures it contains) - * @param name of the destination texture to save to (in memory) - * @param texture directory prefix - */ - static void createFromFile (const std::string& filename, const std::string& textureName, const std::string& texturePrefix="textures\\"); - }; - -} -} - -#endif - From ba7086cadf88ecc9aaf15908e2cbc83e7ab8080f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 3 Jan 2013 02:05:05 +0100 Subject: [PATCH 267/378] Use race/class names (instead of ID) in replaced escape sequences --- apps/openmw/mwscript/interpretercontext.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index cb0e4a7f9..a8d615d89 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -252,15 +252,17 @@ namespace MWScript std::string InterpreterContext::getPCRace() const { MWBase::World *world = MWBase::Environment::get().getWorld(); - ESM::NPC player = *world->getPlayer().getPlayer().get()->mBase; - return player.mRace; + std::string race = world->getPlayer().getPlayer().get()->mBase->mRace; + const ESM::Race* _race = world->getStore().get().find(race); + return _race->mName; } std::string InterpreterContext::getPCClass() const { MWBase::World *world = MWBase::Environment::get().getWorld(); - ESM::NPC player = *world->getPlayer().getPlayer().get()->mBase; - return player.mClass; + std::string _class = world->getPlayer().getPlayer().get()->mBase->mClass; + const ESM::Class* __class = world->getStore().get().find(_class); + return __class->mName; } std::string InterpreterContext::getPCRank() const From 42e0501c671b2034f5d6f8a7c90dc85f34d1dd51 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 3 Jan 2013 02:40:21 +0100 Subject: [PATCH 268/378] fix typo --- apps/openmw/mwscript/docs/vmformat.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 8a0913517..89cacf65c 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -4,7 +4,7 @@ Segment 0: (not implemented yet) opcodes 0x20-0x3f unused -Segment 1:< +Segment 1: (not implemented yet) opcodes 0x20-0x3f unused From 25d9918765e32fd6b4d0aa4f0bd4108d7e4a9e80 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 3 Jan 2013 09:55:48 +0100 Subject: [PATCH 269/378] post merge fix: bringing code more in line with our naming standards and fixing an invalid name (names starting with double underscore are reserved in C++) --- apps/openmw/mwscript/interpretercontext.cpp | 48 ++++++++++----------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index a8d615d89..c74e3f163 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -179,17 +179,17 @@ namespace MWScript std::vector InterpreterContext::getGlobals () const { - MWBase::World *world = MWBase::Environment::get().getWorld(); + MWBase::World *world = MWBase::Environment::get().getWorld(); return world->getGlobals(); } - + char InterpreterContext::getGlobalType (const std::string& name) const { - MWBase::World *world = MWBase::Environment::get().getWorld(); + MWBase::World *world = MWBase::Environment::get().getWorld(); return world->getGlobalVariableType(name); } - + std::string InterpreterContext::getActionBinding(const std::string& action) const { std::vector actions = MWBase::Environment::get().getInputManager()->getActionSorting (); @@ -205,19 +205,19 @@ namespace MWScript return "None"; } - + std::string InterpreterContext::getNPCName() const { ESM::NPC npc = *mReference.get()->mBase; return npc.mName; } - + std::string InterpreterContext::getNPCRace() const { ESM::NPC npc = *mReference.get()->mBase; return npc.mRace; } - + std::string InterpreterContext::getNPCClass() const { ESM::NPC npc = *mReference.get()->mBase; @@ -238,7 +238,7 @@ namespace MWScript MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::ESMStore &store = world->getStore(); const ESM::Faction *faction = store.get().find(it->first); - + return faction->mRanks[it->second]; } @@ -253,66 +253,64 @@ namespace MWScript { MWBase::World *world = MWBase::Environment::get().getWorld(); std::string race = world->getPlayer().getPlayer().get()->mBase->mRace; - const ESM::Race* _race = world->getStore().get().find(race); - return _race->mName; + return world->getStore().get().find(race)->mName; } std::string InterpreterContext::getPCClass() const { MWBase::World *world = MWBase::Environment::get().getWorld(); - std::string _class = world->getPlayer().getPlayer().get()->mBase->mClass; - const ESM::Class* __class = world->getStore().get().find(_class); - return __class->mName; + std::string class_ = world->getPlayer().getPlayer().get()->mBase->mClass; + return world->getStore().get().find(class_)->mName; } - + std::string InterpreterContext::getPCRank() const { MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayer().getPlayer(); - + std::string factionId = MWWorld::Class::get (mReference).getNpcStats (mReference).getFactionRanks().begin()->first; - + std::map ranks = MWWorld::Class::get (player).getNpcStats (player).getFactionRanks(); std::map::const_iterator it = ranks.begin(); const MWWorld::ESMStore &store = world->getStore(); const ESM::Faction *faction = store.get().find(factionId); - + if(it->second < 0 || it->second > 9) // there are only 10 ranks return ""; - + return faction->mRanks[it->second]; - } + } std::string InterpreterContext::getPCNextRank() const { MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayer().getPlayer(); - + std::string factionId = MWWorld::Class::get (mReference).getNpcStats (mReference).getFactionRanks().begin()->first; - + std::map ranks = MWWorld::Class::get (player).getNpcStats (player).getFactionRanks(); std::map::const_iterator it = ranks.begin(); const MWWorld::ESMStore &store = world->getStore(); const ESM::Faction *faction = store.get().find(factionId); - + if(it->second < 0 || it->second > 9) return ""; - + if(it->second <= 8) // If player is at max rank, there is no next rank return faction->mRanks[it->second + 1]; else return faction->mRanks[it->second]; } - + int InterpreterContext::getPCBounty() const { MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr player = world->getPlayer().getPlayer(); return MWWorld::Class::get (player).getNpcStats (player).getBounty(); } - + std::string InterpreterContext::getCurrentCellName() const { MWBase::World *world = MWBase::Environment::get().getWorld(); From 5c007cd52714779c071ef8bf5b82c808090edc8d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 3 Jan 2013 10:05:37 +0100 Subject: [PATCH 270/378] updated credits file --- credits.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/credits.txt b/credits.txt index 713fbe9ac..e1905c88b 100644 --- a/credits.txt +++ b/credits.txt @@ -21,6 +21,7 @@ Cris Mihalache (Mirceam) Douglas Diniz (Dgdiniz) Eduard Cot (trombonecot) Eli2 +Emanuel "potatoesmaster" Guével gugus / gus Jacob Essex (Yacoby) Jannik Heller (scrawl) From b7b51f24d63696436157086ed79356704889d084 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 3 Jan 2013 11:20:25 +0100 Subject: [PATCH 271/378] added new Role for table headers (Role_Display) --- apps/opencs/model/world/columnbase.cpp | 4 ++-- apps/opencs/model/world/columnbase.hpp | 18 ++++++++++++++---- apps/opencs/model/world/columns.hpp | 9 +++++---- apps/opencs/model/world/idtable.cpp | 3 +++ 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/apps/opencs/model/world/columnbase.cpp b/apps/opencs/model/world/columnbase.cpp index 7adc7e6c3..134c582c4 100644 --- a/apps/opencs/model/world/columnbase.cpp +++ b/apps/opencs/model/world/columnbase.cpp @@ -1,8 +1,8 @@ #include "columnbase.hpp" -CSMWorld::ColumnBase::ColumnBase (const std::string& title, int flags) -: mTitle (title), mFlags (flags) +CSMWorld::ColumnBase::ColumnBase (const std::string& title, Display displayType, int flags) +: mTitle (title), mDisplayType (displayType), mFlags (flags) {} CSMWorld::ColumnBase::~ColumnBase() {} diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index dc077eff6..38b73ee3f 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -14,7 +14,8 @@ namespace CSMWorld { enum Roles { - Role_Flags = Qt::UserRole + Role_Flags = Qt::UserRole, + Role_Display = Qt::UserRole+1 }; enum Flags @@ -23,10 +24,18 @@ namespace CSMWorld Flag_Dialogue = 2 // column should be displayed in dialogue view }; + enum Display + { + Display_String, + Display_Integer, + Display_Float + }; + std::string mTitle; int mFlags; + Display mDisplayType; - ColumnBase (const std::string& title, int flag); + ColumnBase (const std::string& title, Display displayType, int flag); virtual ~ColumnBase(); @@ -34,6 +43,7 @@ namespace CSMWorld virtual bool isUserEditable() const; ///< Can this column be edited directly by the user? + }; template @@ -42,8 +52,8 @@ namespace CSMWorld std::string mTitle; int mFlags; - Column (const std::string& title, int flags = Flag_Table | Flag_Dialogue) - : ColumnBase (title, flags) {} + Column (const std::string& title, Display displayType, int flags = Flag_Table | Flag_Dialogue) + : ColumnBase (title, displayType, flags) {} virtual QVariant get (const Record& record) const = 0; diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 5abf4ea8b..1e2de9265 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -8,7 +8,7 @@ namespace CSMWorld template struct FloatValueColumn : public Column { - FloatValueColumn() : Column ("Value") {} + FloatValueColumn() : Column ("Value", ColumnBase::Display_Float) {} virtual QVariant get (const Record& record) const { @@ -31,7 +31,7 @@ namespace CSMWorld template struct StringIdColumn : public Column { - StringIdColumn() : Column ("ID") {} + StringIdColumn() : Column ("ID", ColumnBase::Display_String) {} virtual QVariant get (const Record& record) const { @@ -47,7 +47,7 @@ namespace CSMWorld template struct RecordStateColumn : public Column { - RecordStateColumn() : Column ("*") {} + RecordStateColumn() : Column ("*", ColumnBase::Display_Integer) {} virtual QVariant get (const Record& record) const { @@ -78,7 +78,8 @@ namespace CSMWorld { int mType; - FixedRecordTypeColumn (int type) : Column ("Type", 0), mType (type) {} + FixedRecordTypeColumn (int type) + : Column ("Type", ColumnBase::Display_Integer, 0), mType (type) {} virtual QVariant get (const Record& record) const { diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 2815e318c..afed6b6ed 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -51,6 +51,9 @@ QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation if (role==ColumnBase::Role_Flags) return mIdCollection->getColumn (section).mFlags; + if (role==ColumnBase::Role_Display) + return mIdCollection->getColumn (section).mDisplayType; + return QVariant(); } From 9afe4467d8e5ea3f6fc9960e338bc16bcc1d7bbc Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Wed, 2 Jan 2013 13:50:44 -0800 Subject: [PATCH 272/378] cache results of query for spash screen names ResourceGroupManager::listResourceNames returns a list of all resource accessable which is expensive, this change caches the result of the processed query so additional splash screen changes are quicker. --- apps/openmw/mwgui/loadingscreen.cpp | 28 ++++++++++++++++------------ apps/openmw/mwgui/loadingscreen.hpp | 1 + 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index d721e209a..a508bcd34 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -213,22 +213,26 @@ namespace MWGui void LoadingScreen::changeWallpaper () { - std::vector splash; - - Ogre::StringVectorPtr resources = Ogre::ResourceGroupManager::getSingleton ().listResourceNames ("General", false); - for (Ogre::StringVector::const_iterator it = resources->begin(); it != resources->end(); ++it) + if (mResources.isNull ()) { - if (it->size() < 6) - continue; - std::string start = it->substr(0, 6); - boost::to_lower(start); + mResources = Ogre::StringVectorPtr (new Ogre::StringVector); + + Ogre::StringVectorPtr resources = Ogre::ResourceGroupManager::getSingleton ().listResourceNames ("General", false); + for (Ogre::StringVector::const_iterator it = resources->begin(); it != resources->end(); ++it) + { + if (it->size() < 6) + continue; + std::string start = it->substr(0, 6); + boost::to_lower(start); - if (start == "splash") - splash.push_back (*it); + if (start == "splash") + mResources->push_back (*it); + } } - if (splash.size()) + + if (mResources->size()) { - std::string randomSplash = splash[rand() % splash.size()]; + std::string randomSplash = mResources->at (rand() % mResources->size()); Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, "General"); mBackgroundImage->setImageTexture (randomSplash); diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index a012793ca..c14087a3b 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -42,6 +42,7 @@ namespace MWGui Ogre::Rectangle2D* mRectangle; Ogre::MaterialPtr mBackgroundMaterial; + Ogre::StringVectorPtr mResources; bool mLoadingOn; From 740e2b5769d85bec22b8478846bd3800d2552049 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Wed, 2 Jan 2013 23:02:13 +0100 Subject: [PATCH 273/378] components/to_utf8: add class Utf8Encoder --- components/to_utf8/to_utf8.cpp | 795 ++++++++++++++++++++++----------- components/to_utf8/to_utf8.hpp | 67 ++- 2 files changed, 589 insertions(+), 273 deletions(-) diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index 7db611247..8ac582b81 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include /* This file contains the code to translate from WINDOWS-1252 (native charset used in English version of Morrowind) to UTF-8. The library @@ -46,334 +48,611 @@ static std::vector buf (50*1024); static std::vector output (50*1024); static int size; -// Make sure the given vector is large enough for 'size' bytes, +using namespace ToUTF8; + +Utf8Encoder::Utf8Encoder(void): + mOutput(50*1024) +{ +} + +void Utf8Encoder::setEncoding(const FromType sourceEncoding) +{ + mEncoding = sourceEncoding; + + switch (mEncoding) + { + case ToUTF8::WINDOWS_1252: + { + translationArray = ToUTF8::windows_1252; + break; + } + case ToUTF8::WINDOWS_1250: + { + translationArray = ToUTF8::windows_1250; + break; + } + case ToUTF8::WINDOWS_1251: + { + translationArray = ToUTF8::windows_1251; + break; + } + default: + { + assert(0); + } + } +} + +std::string Utf8Encoder::getUtf8(const char* input, int size) +{ + // Double check that the input string stops at some point (it might + // contain zero terminators before this, inside its own data, which + // is also ok.) + assert(input[size] == 0); + + // TODO: The rest of this function is designed for single-character + // input encodings only. It also assumes that the input the input + // encoding shares its first 128 values (0-127) with ASCII. These + // conditions must be checked again if you add more input encodings + // later. + + // Compute output length, and check for pure ascii input at the same + // time. + bool ascii; + size_t outlen = getLength(input, ascii); + + // If we're pure ascii, then don't bother converting anything. + if(ascii) + return std::string(input, outlen); + + // Make sure the output is large enough + resize(outlen); + char *out = &mOutput[0]; + + // Translate + while (*input) + copyFromArray(*(input++), out); + + // Make sure that we wrote the correct number of bytes + assert((out-&mOutput[0]) == (int)outlen); + + // And make extra sure the output is null terminated + assert(mOutput.size() > outlen); + assert(mOutput[outlen] == 0); + + // Return a string + return std::string(&mOutput[0], outlen); +} + +std::string Utf8Encoder::getLegacyEnc(const char *input, int size) +{ + // Double check that the input string stops at some point (it might + // contain zero terminators before this, inside its own data, which + // is also ok.) + assert(input[size] == 0); + + // TODO: The rest of this function is designed for single-character + // input encodings only. It also assumes that the input the input + // encoding shares its first 128 values (0-127) with ASCII. These + // conditions must be checked again if you add more input encodings + // later. + + // Compute output length, and check for pure ascii input at the same + // time. + bool ascii; + size_t outlen = getLength2(input, ascii); + + // If we're pure ascii, then don't bother converting anything. + if(ascii) + return std::string(input, outlen); + + // Make sure the output is large enough + resize(outlen); + char *out = &mOutput[0]; + + // Translate + while(*input) + copyFromArray2(input, out); + + // Make sure that we wrote the correct number of bytes + assert((out-&mOutput[0]) == (int)outlen); + + // And make extra sure the output is null terminated + assert(mOutput.size() > outlen); + assert(mOutput[outlen] == 0); + + // Return a string + return std::string(&mOutput[0], outlen); +} + +// Make sure the output vector is large enough for 'size' bytes, // including a terminating zero after it. +void Utf8Encoder::resize(size_t size) +{ + if (mOutput.size() <= size) + // Add some extra padding to reduce the chance of having to resize + // again later. + mOutput.resize(3*size); + + // And make sure the string is zero terminated + mOutput[size] = 0; +} + +/** Get the total length length needed to decode the given string with + the given translation array. The arrays are encoded with 6 bytes + per character, with the first giving the length and the next 5 the + actual data. + + The function serves a dual purpose for optimization reasons: it + checks if the input is pure ascii (all values are <= 127). If this + is the case, then the ascii parameter is set to true, and the + caller can optimize for this case. + */ +size_t Utf8Encoder::getLength(const char* input, bool &ascii) +{ + ascii = true; + size_t len = 0; + const char* ptr = input; + unsigned char inp = *ptr; + + // Do away with the ascii part of the string first (this is almost + // always the entire string.) + while (inp && inp < 128) + inp = *(++ptr); + len += (ptr-input); + + // If we're not at the null terminator at this point, then there + // were some non-ascii characters to deal with. Go to slow-mode for + // the rest of the string. + if (inp) + { + ascii = false; + while (inp) + { + // Find the translated length of this character in the + // lookup table. + len += translationArray[inp*6]; + inp = *(++ptr); + } + } + return len; +} + +// Translate one character 'ch' using the translation array 'arr', and +// advance the output pointer accordingly. +void Utf8Encoder::copyFromArray(unsigned char ch, char* &out) +{ + // Optimize for ASCII values + if (ch < 128) + { + *(out++) = ch; + return; + } + + const char *in = translationArray + ch*6; + int len = *(in++); + for (int i=0; i &buf, size_t size) { - if(buf.size() <= size) - // Add some extra padding to reduce the chance of having to resize - // again later. - buf.resize(3*size); + if(buf.size() <= size) + // Add some extra padding to reduce the chance of having to resize + // again later. + buf.resize(3*size); - // And make sure the string is zero terminated - buf[size] = 0; + // And make sure the string is zero terminated + buf[size] = 0; } // This is just used to spew out a reusable input buffer for the // conversion process. char *ToUTF8::getBuffer(int s) { - // Remember the requested size - size = s; - resize(buf, size); - return &buf[0]; + // Remember the requested size + size = s; + resize(buf, size); + return &buf[0]; } /** Get the total length length needed to decode the given string with - the given translation array. The arrays are encoded with 6 bytes - per character, with the first giving the length and the next 5 the - actual data. - - The function serves a dual purpose for optimization reasons: it - checks if the input is pure ascii (all values are <= 127). If this - is the case, then the ascii parameter is set to true, and the - caller can optimize for this case. + the given translation array. The arrays are encoded with 6 bytes + per character, with the first giving the length and the next 5 the + actual data. + + The function serves a dual purpose for optimization reasons: it + checks if the input is pure ascii (all values are <= 127). If this + is the case, then the ascii parameter is set to true, and the + caller can optimize for this case. */ static size_t getLength(const char *arr, const char* input, bool &ascii) { - ascii = true; - size_t len = 0; - const char* ptr = input; - unsigned char inp = *ptr; - - // Do away with the ascii part of the string first (this is almost - // always the entire string.) - while(inp && inp < 128) - inp = *(++ptr); - len += (ptr-input); - - // If we're not at the null terminator at this point, then there - // were some non-ascii characters to deal with. Go to slow-mode for - // the rest of the string. - if(inp) + ascii = true; + size_t len = 0; + const char* ptr = input; + unsigned char inp = *ptr; + + // Do away with the ascii part of the string first (this is almost + // always the entire string.) + while(inp && inp < 128) + inp = *(++ptr); + len += (ptr-input); + + // If we're not at the null terminator at this point, then there + // were some non-ascii characters to deal with. Go to slow-mode for + // the rest of the string. + if(inp) { - ascii = false; - while(inp) + ascii = false; + while(inp) { - // Find the translated length of this character in the - // lookup table. - len += arr[inp*6]; - inp = *(++ptr); + // Find the translated length of this character in the + // lookup table. + len += arr[inp*6]; + inp = *(++ptr); } } - return len; + return len; } // Translate one character 'ch' using the translation array 'arr', and // advance the output pointer accordingly. static void copyFromArray(const char *arr, unsigned char ch, char* &out) { - // Optimize for ASCII values - if(ch < 128) + // Optimize for ASCII values + if(ch < 128) { - *(out++) = ch; - return; + *(out++) = ch; + return; } - const char *in = arr + ch*6; - int len = *(in++); - for(int i=0; i outlen); - assert(output[outlen] == 0); - - // Return a string - return std::string(&output[0], outlen); + + // Double check that the input string stops at some point (it might + // contain zero terminators before this, inside its own data, which + // is also ok.) + const char* input = &buf[0]; + assert(input[size] == 0); + + // TODO: The rest of this function is designed for single-character + // input encodings only. It also assumes that the input the input + // encoding shares its first 128 values (0-127) with ASCII. These + // conditions must be checked again if you add more input encodings + // later. + + // Compute output length, and check for pure ascii input at the same + // time. + bool ascii; + size_t outlen = getLength(arr, input, ascii); + + // If we're pure ascii, then don't bother converting anything. + if(ascii) + return std::string(input, outlen); + + // Make sure the output is large enough + resize(output, outlen); + char *out = &output[0]; + + // Translate + while(*input) + copyFromArray(arr, *(input++), out); + + // Make sure that we wrote the correct number of bytes + assert((out-&output[0]) == (int)outlen); + + // And make extra sure the output is null terminated + assert(output.size() > outlen); + assert(output[outlen] == 0); + + // Return a string + return std::string(&output[0], outlen); } static size_t getLength2(const char *arr, const char* input, bool &ascii) { - ascii = true; - size_t len = 0; - const char* ptr = input; - unsigned char inp = *ptr; - - // Do away with the ascii part of the string first (this is almost - // always the entire string.) - while(inp && inp < 128) - inp = *(++ptr); - len += (ptr-input); - - // If we're not at the null terminator at this point, then there - // were some non-ascii characters to deal with. Go to slow-mode for - // the rest of the string. - if(inp) + ascii = true; + size_t len = 0; + const char* ptr = input; + unsigned char inp = *ptr; + + // Do away with the ascii part of the string first (this is almost + // always the entire string.) + while(inp && inp < 128) + inp = *(++ptr); + len += (ptr-input); + + // If we're not at the null terminator at this point, then there + // were some non-ascii characters to deal with. Go to slow-mode for + // the rest of the string. + if(inp) { - ascii = false; - while(inp) + ascii = false; + while(inp) { len += 1; - // Find the translated length of this character in the - // lookup table. + // Find the translated length of this character in the + // lookup table. switch(inp) { - case 0xe2: len -= 2; break; - case 0xc2: - case 0xcb: - case 0xc4: - case 0xc6: - case 0xc3: - case 0xd0: - case 0xd1: - case 0xd2: - case 0xc5: len -= 1; break; + case 0xe2: len -= 2; break; + case 0xc2: + case 0xcb: + case 0xc4: + case 0xc6: + case 0xc3: + case 0xd0: + case 0xd1: + case 0xd2: + case 0xc5: len -= 1; break; } - inp = *(++ptr); + inp = *(++ptr); } } - return len; + return len; } -#include -#include - static void copyFromArray2(const char *arr, char*& chp, char* &out) { unsigned char ch = *(chp++); - // Optimize for ASCII values - if(ch < 128) + // Optimize for ASCII values + if(ch < 128) { - *(out++) = ch; - return; + *(out++) = ch; + return; } - int len = 1; - switch (ch) - { - case 0xe2: len = 3; break; - case 0xc2: - case 0xcb: - case 0xc4: - case 0xc6: - case 0xc3: - case 0xd0: - case 0xd1: - case 0xd2: - case 0xc5: len = 2; break; - } - - if (len == 1) // There is no 1 length utf-8 glyph that is not 0x20 (empty space) - { - *(out++) = ch; - return; - } - - unsigned char ch2 = *(chp++); - unsigned char ch3 = '\0'; - if (len == 3) - ch3 = *(chp++); - - for (int i = 128; i < 256; i++) - { - unsigned char b1 = arr[i*6 + 1], b2 = arr[i*6 + 2], b3 = arr[i*6 + 3]; - if (b1 == ch && b2 == ch2 && (len != 3 || b3 == ch3)) - { - *(out++) = (char)i; - return; - } - } - - std::cout << "Could not find glyph " << std::hex << (int)ch << " " << (int)ch2 << " " << (int)ch3 << std::endl; - - *(out++) = ch; // Could not find glyph, just put whatever -} - -std::string ToUTF8::getLegacyEnc(ToUTF8::FromType to) -{ - // Pick translation array - const char *arr; - switch (to) - { - case ToUTF8::WINDOWS_1252: + int len = 1; + switch (ch) { - arr = ToUTF8::windows_1252; - break; + case 0xe2: len = 3; break; + case 0xc2: + case 0xcb: + case 0xc4: + case 0xc6: + case 0xc3: + case 0xd0: + case 0xd1: + case 0xd2: + case 0xc5: len = 2; break; } - case ToUTF8::WINDOWS_1250: + + if (len == 1) // There is no 1 length utf-8 glyph that is not 0x20 (empty space) { - arr = ToUTF8::windows_1250; - break; + *(out++) = ch; + return; } - case ToUTF8::WINDOWS_1251: + + unsigned char ch2 = *(chp++); + unsigned char ch3 = '\0'; + if (len == 3) + ch3 = *(chp++); + + for (int i = 128; i < 256; i++) { - arr = ToUTF8::windows_1251; - break; + unsigned char b1 = arr[i*6 + 1], b2 = arr[i*6 + 2], b3 = arr[i*6 + 3]; + if (b1 == ch && b2 == ch2 && (len != 3 || b3 == ch3)) + { + *(out++) = (char)i; + return; + } } - default: + + std::cout << "Could not find glyph " << std::hex << (int)ch << " " << (int)ch2 << " " << (int)ch3 << std::endl; + + *(out++) = ch; // Could not find glyph, just put whatever +} + +std::string ToUTF8::getLegacyEnc(ToUTF8::FromType to) +{ + // Pick translation array + const char *arr; + switch (to) { - assert(0); + case ToUTF8::WINDOWS_1252: + { + arr = ToUTF8::windows_1252; + break; + } + case ToUTF8::WINDOWS_1250: + { + arr = ToUTF8::windows_1250; + break; + } + case ToUTF8::WINDOWS_1251: + { + arr = ToUTF8::windows_1251; + break; + } + default: + { + assert(0); + } } - } - - // Double check that the input string stops at some point (it might - // contain zero terminators before this, inside its own data, which - // is also ok.) - char* input = &buf[0]; - assert(input[size] == 0); - - // TODO: The rest of this function is designed for single-character - // input encodings only. It also assumes that the input the input - // encoding shares its first 128 values (0-127) with ASCII. These - // conditions must be checked again if you add more input encodings - // later. - - // Compute output length, and check for pure ascii input at the same - // time. - bool ascii; - size_t outlen = getLength2(arr, input, ascii); - - // If we're pure ascii, then don't bother converting anything. - if(ascii) - return std::string(input, outlen); - - // Make sure the output is large enough - resize(output, outlen); - char *out = &output[0]; - - // Translate - while(*input) - copyFromArray2(arr, input, out); - - // Make sure that we wrote the correct number of bytes - assert((out-&output[0]) == (int)outlen); - - // And make extra sure the output is null terminated - assert(output.size() > outlen); - assert(output[outlen] == 0); - - // Return a string - return std::string(&output[0], outlen); + + // Double check that the input string stops at some point (it might + // contain zero terminators before this, inside its own data, which + // is also ok.) + char* input = &buf[0]; + assert(input[size] == 0); + + // TODO: The rest of this function is designed for single-character + // input encodings only. It also assumes that the input the input + // encoding shares its first 128 values (0-127) with ASCII. These + // conditions must be checked again if you add more input encodings + // later. + + // Compute output length, and check for pure ascii input at the same + // time. + bool ascii; + size_t outlen = getLength2(arr, input, ascii); + + // If we're pure ascii, then don't bother converting anything. + if(ascii) + return std::string(input, outlen); + + // Make sure the output is large enough + resize(output, outlen); + char *out = &output[0]; + + // Translate + while(*input) + copyFromArray2(arr, input, out); + + // Make sure that we wrote the correct number of bytes + assert((out-&output[0]) == (int)outlen); + + // And make extra sure the output is null terminated + assert(output.size() > outlen); + assert(output[outlen] == 0); + + // Return a string + return std::string(&output[0], outlen); } ToUTF8::FromType ToUTF8::calculateEncoding(const std::string& encodingName) { - if (encodingName == "win1250") - return ToUTF8::WINDOWS_1250; - else if (encodingName == "win1251") - return ToUTF8::WINDOWS_1251; - else - return ToUTF8::WINDOWS_1252; + if (encodingName == "win1250") + return ToUTF8::WINDOWS_1250; + else if (encodingName == "win1251") + return ToUTF8::WINDOWS_1251; + else + return ToUTF8::WINDOWS_1252; } std::string ToUTF8::encodingUsingMessage(const std::string& encodingName) { - if (encodingName == "win1250") - return "Using Central and Eastern European font encoding."; - else if (encodingName == "win1251") - return "Using Cyrillic font encoding."; - else - return "Using default (English) font encoding."; + if (encodingName == "win1250") + return "Using Central and Eastern European font encoding."; + else if (encodingName == "win1251") + return "Using Cyrillic font encoding."; + else + return "Using default (English) font encoding."; } diff --git a/components/to_utf8/to_utf8.hpp b/components/to_utf8/to_utf8.hpp index f52ae73bd..6877e2dc1 100644 --- a/components/to_utf8/to_utf8.hpp +++ b/components/to_utf8/to_utf8.hpp @@ -2,29 +2,66 @@ #define COMPONENTS_TOUTF8_H #include +#include +#include namespace ToUTF8 { - // These are all the currently supported code pages - enum FromType + // These are all the currently supported code pages + enum FromType { - WINDOWS_1250, // Central ane Eastern European languages - WINDOWS_1251, // Cyrillic languages - WINDOWS_1252 // Used by English version of Morrowind (and - // probably others) + WINDOWS_1250, // Central ane Eastern European languages + WINDOWS_1251, // Cyrillic languages + WINDOWS_1252 // Used by English version of Morrowind (and + // probably others) }; - // Return a writable buffer of at least 'size' bytes. The buffer - // does not have to be freed. - char* getBuffer(int size); + // Return a writable buffer of at least 'size' bytes. The buffer + // does not have to be freed. + char* getBuffer(int size); - // Convert the previously written buffer to UTF8 from the given code - // page. - std::string getUtf8(FromType from); - std::string getLegacyEnc(FromType to); + // Convert the previously written buffer to UTF8 from the given code + // page. + std::string getUtf8(FromType from); + std::string getLegacyEnc(FromType to); - FromType calculateEncoding(const std::string& encodingName); - std::string encodingUsingMessage(const std::string& encodingName); + FromType calculateEncoding(const std::string& encodingName); + std::string encodingUsingMessage(const std::string& encodingName); + + // class + + class Utf8Encoder + { + public: + Utf8Encoder(void); + + void setEncoding(const FromType sourceEncoding); + + // Convert to UTF8 from the previously given code page. + std::string getUtf8(const char *input, int size); + inline std::string getUtf8(const std::string &str) + { + return getUtf8(str.c_str(), str.size()); + } + + std::string getLegacyEnc(const char *input, int size); + inline std::string getLegacyEnc(const std::string &str) + { + return getLegacyEnc(str.c_str(), str.size()); + } + + private: + void resize(size_t size); + size_t getLength(const char* input, bool &ascii); + void copyFromArray(unsigned char chp, char* &out); + size_t getLength2(const char* input, bool &ascii); + void copyFromArray2(const char*& chp, char* &out); + + FromType mEncoding; + std::vector mOutput; + int mSize; + char* translationArray; + }; } #endif From 67273fc1777b45d9860fe114689cbdc9836c1f48 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Wed, 2 Jan 2013 23:39:21 +0100 Subject: [PATCH 274/378] mwiniimporter: use Utf8Encoder --- apps/mwiniimporter/importer.cpp | 12 +++--------- apps/mwiniimporter/importer.hpp | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index def70615b..5c3dedd04 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -649,11 +649,13 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) { std::string section(""); MwIniImporter::multistrmap map; boost::iostreams::streamfile(filename.c_str()); + ToUTF8::Utf8Encoder encoder; + encoder.setEncoding(mEncoding); std::string line; while (std::getline(file, line)) { - line = toUTF8(line); + line = encoder.getUtf8(line); // unify Unix-style and Windows file ending if (!(line.empty()) && (line[line.length()-1]) == '\r') { @@ -829,14 +831,6 @@ void MwIniImporter::writeToFile(boost::iostreams::stream #include -#include "../../components/to_utf8/to_utf8.hpp" +#include class MwIniImporter { public: From 9906c3051dddaaf435d55841f5e9d0d392d575c5 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Thu, 3 Jan 2013 15:01:08 +0100 Subject: [PATCH 275/378] components/translation: use Utf8Encoder --- components/translation/translation.cpp | 6 ++---- components/translation/translation.hpp | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index fb5b03861..002446e4f 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -50,10 +50,7 @@ namespace Translation if (!line.empty()) { - char* buffer = ToUTF8::getBuffer(line.size() + 1); - //buffer has at least line.size() + 1 bytes, so it must be safe - strcpy(buffer, line.c_str()); - line = ToUTF8::getUtf8(mEncoding); + line = mEncoder.getUtf8(line); size_t tab_pos = line.find('\t'); if (tab_pos != std::string::npos && tab_pos > 0 && tab_pos < line.size() - 1) @@ -107,6 +104,7 @@ namespace Translation void Storage::setEncoding (const ToUTF8::FromType& encoding) { mEncoding = encoding; + mEncoder.setEncoding(encoding); } bool Storage::hasTranslation() const diff --git a/components/translation/translation.hpp b/components/translation/translation.hpp index 80d44d871..6c3e4df86 100644 --- a/components/translation/translation.hpp +++ b/components/translation/translation.hpp @@ -35,6 +35,7 @@ namespace Translation ToUTF8::FromType mEncoding; + ToUTF8::Utf8Encoder mEncoder; ContainerType mCellNamesTranslations, mTopicIDs, mPhraseForms; }; } From 02bf02f288c407ad639ad15c8bc3a72ae56597b4 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Thu, 3 Jan 2013 21:15:18 +0100 Subject: [PATCH 276/378] ESMReader, ESMWriter: use Utf8Encoder --- components/esm/esmreader.cpp | 22 +++++++++++++++++++--- components/esm/esmreader.hpp | 6 ++++++ components/esm/esmwriter.cpp | 8 +++----- components/esm/esmwriter.hpp | 1 + 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 580e006df..5cd99a64a 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -13,6 +13,11 @@ ESM_Context ESMReader::getContext() return mCtx; } +ESMReader::ESMReader(void): + mBuffer(50*1024) +{ +} + void ESMReader::restoreContext(const ESM_Context &rc) { // Reopen the file if necessary @@ -325,11 +330,21 @@ void ESMReader::getExact(void*x, int size) std::string ESMReader::getString(int size) { - char *ptr = ToUTF8::getBuffer(size); - mEsm->read(ptr, size); + size_t s = size; + if (mBuffer.size() <= s) + // Add some extra padding to reduce the chance of having to resize + // again later. + mBuffer.resize(3*s); + + // And make sure the string is zero terminated + mBuffer[s] = 0; + + // read ESM data + char *ptr = &mBuffer[0]; + getExact(ptr, size); // Convert to UTF8 and return - return ToUTF8::getUtf8(mEncoding); + return mEncoder.getUtf8(ptr, size); } void ESMReader::fail(const std::string &msg) @@ -350,6 +365,7 @@ void ESMReader::fail(const std::string &msg) void ESMReader::setEncoding(const ToUTF8::FromType& encoding) { mEncoding = encoding; + mEncoder.setEncoding(encoding); } } diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 1d0f6f580..57503aea7 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -20,6 +20,8 @@ class ESMReader { public: + ESMReader(void); + /************************************************************************* * * Public type definitions @@ -244,9 +246,13 @@ private: // Special file signifier (see SpecialFile enum above) int mSpf; + // Buffer for ESM strings + std::vector mBuffer; + SaveData mSaveData; MasterList mMasters; ToUTF8::FromType mEncoding; + ToUTF8::Utf8Encoder mEncoder; }; } #endif diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index c1ae06490..a00c7971d 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -157,12 +157,8 @@ void ESMWriter::writeHString(const std::string& data) write("\0", 1); else { - char *ptr = ToUTF8::getBuffer(data.size()+1); - strncpy(ptr, &data[0], data.size()); - ptr[data.size()] = '\0'; - // Convert to UTF8 and return - std::string ascii = ToUTF8::getLegacyEnc(m_encoding); + std::string ascii = m_encoder.getLegacyEnc(data); write(ascii.c_str(), ascii.size()); } @@ -207,6 +203,8 @@ void ESMWriter::setEncoding(const std::string& encoding) // Default Latin encoding m_encoding = ToUTF8::WINDOWS_1252; } + + m_encoder.setEncoding(m_encoding); } } diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index d3777be81..20bc5da12 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -95,6 +95,7 @@ private: std::ostream* m_stream; std::streampos m_headerPos; ToUTF8::FromType m_encoding; + ToUTF8::Utf8Encoder m_encoder; int m_recordCount; HEDRstruct m_header; From 0bdf52a0719a756f3be97a988ff33784d02f7fcc Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Thu, 3 Jan 2013 23:21:14 +0100 Subject: [PATCH 277/378] components/to_utf8: keep only Utf8Encoder --- components/to_utf8/to_utf8.cpp | 314 --------------------------------- components/to_utf8/to_utf8.hpp | 9 - 2 files changed, 323 deletions(-) diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index 8ac582b81..5efec36a4 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -41,13 +41,6 @@ // Generated tables #include "tables_gen.hpp" -// Shared global buffers, we love you. These initial sizes are large -// enough to hold the largest books in Morrowind.esm, but we will -// resize automaticall if necessary. -static std::vector buf (50*1024); -static std::vector output (50*1024); -static int size; - using namespace ToUTF8; Utf8Encoder::Utf8Encoder(void): @@ -330,313 +323,6 @@ void Utf8Encoder::copyFromArray2(const char*& chp, char* &out) *(out++) = ch; // Could not find glyph, just put whatever } -static void resize(std::vector &buf, size_t size) -{ - if(buf.size() <= size) - // Add some extra padding to reduce the chance of having to resize - // again later. - buf.resize(3*size); - - // And make sure the string is zero terminated - buf[size] = 0; -} - -// This is just used to spew out a reusable input buffer for the -// conversion process. -char *ToUTF8::getBuffer(int s) -{ - // Remember the requested size - size = s; - resize(buf, size); - return &buf[0]; -} - -/** Get the total length length needed to decode the given string with - the given translation array. The arrays are encoded with 6 bytes - per character, with the first giving the length and the next 5 the - actual data. - - The function serves a dual purpose for optimization reasons: it - checks if the input is pure ascii (all values are <= 127). If this - is the case, then the ascii parameter is set to true, and the - caller can optimize for this case. - */ -static size_t getLength(const char *arr, const char* input, bool &ascii) -{ - ascii = true; - size_t len = 0; - const char* ptr = input; - unsigned char inp = *ptr; - - // Do away with the ascii part of the string first (this is almost - // always the entire string.) - while(inp && inp < 128) - inp = *(++ptr); - len += (ptr-input); - - // If we're not at the null terminator at this point, then there - // were some non-ascii characters to deal with. Go to slow-mode for - // the rest of the string. - if(inp) - { - ascii = false; - while(inp) - { - // Find the translated length of this character in the - // lookup table. - len += arr[inp*6]; - inp = *(++ptr); - } - } - return len; -} - -// Translate one character 'ch' using the translation array 'arr', and -// advance the output pointer accordingly. -static void copyFromArray(const char *arr, unsigned char ch, char* &out) -{ - // Optimize for ASCII values - if(ch < 128) - { - *(out++) = ch; - return; - } - - const char *in = arr + ch*6; - int len = *(in++); - for(int i=0; i outlen); - assert(output[outlen] == 0); - - // Return a string - return std::string(&output[0], outlen); -} - -static size_t getLength2(const char *arr, const char* input, bool &ascii) -{ - ascii = true; - size_t len = 0; - const char* ptr = input; - unsigned char inp = *ptr; - - // Do away with the ascii part of the string first (this is almost - // always the entire string.) - while(inp && inp < 128) - inp = *(++ptr); - len += (ptr-input); - - // If we're not at the null terminator at this point, then there - // were some non-ascii characters to deal with. Go to slow-mode for - // the rest of the string. - if(inp) - { - ascii = false; - while(inp) - { - len += 1; - // Find the translated length of this character in the - // lookup table. - switch(inp) - { - case 0xe2: len -= 2; break; - case 0xc2: - case 0xcb: - case 0xc4: - case 0xc6: - case 0xc3: - case 0xd0: - case 0xd1: - case 0xd2: - case 0xc5: len -= 1; break; - } - - inp = *(++ptr); - } - } - return len; -} - -static void copyFromArray2(const char *arr, char*& chp, char* &out) -{ - unsigned char ch = *(chp++); - // Optimize for ASCII values - if(ch < 128) - { - *(out++) = ch; - return; - } - - int len = 1; - switch (ch) - { - case 0xe2: len = 3; break; - case 0xc2: - case 0xcb: - case 0xc4: - case 0xc6: - case 0xc3: - case 0xd0: - case 0xd1: - case 0xd2: - case 0xc5: len = 2; break; - } - - if (len == 1) // There is no 1 length utf-8 glyph that is not 0x20 (empty space) - { - *(out++) = ch; - return; - } - - unsigned char ch2 = *(chp++); - unsigned char ch3 = '\0'; - if (len == 3) - ch3 = *(chp++); - - for (int i = 128; i < 256; i++) - { - unsigned char b1 = arr[i*6 + 1], b2 = arr[i*6 + 2], b3 = arr[i*6 + 3]; - if (b1 == ch && b2 == ch2 && (len != 3 || b3 == ch3)) - { - *(out++) = (char)i; - return; - } - } - - std::cout << "Could not find glyph " << std::hex << (int)ch << " " << (int)ch2 << " " << (int)ch3 << std::endl; - - *(out++) = ch; // Could not find glyph, just put whatever -} - -std::string ToUTF8::getLegacyEnc(ToUTF8::FromType to) -{ - // Pick translation array - const char *arr; - switch (to) - { - case ToUTF8::WINDOWS_1252: - { - arr = ToUTF8::windows_1252; - break; - } - case ToUTF8::WINDOWS_1250: - { - arr = ToUTF8::windows_1250; - break; - } - case ToUTF8::WINDOWS_1251: - { - arr = ToUTF8::windows_1251; - break; - } - default: - { - assert(0); - } - } - - // Double check that the input string stops at some point (it might - // contain zero terminators before this, inside its own data, which - // is also ok.) - char* input = &buf[0]; - assert(input[size] == 0); - - // TODO: The rest of this function is designed for single-character - // input encodings only. It also assumes that the input the input - // encoding shares its first 128 values (0-127) with ASCII. These - // conditions must be checked again if you add more input encodings - // later. - - // Compute output length, and check for pure ascii input at the same - // time. - bool ascii; - size_t outlen = getLength2(arr, input, ascii); - - // If we're pure ascii, then don't bother converting anything. - if(ascii) - return std::string(input, outlen); - - // Make sure the output is large enough - resize(output, outlen); - char *out = &output[0]; - - // Translate - while(*input) - copyFromArray2(arr, input, out); - - // Make sure that we wrote the correct number of bytes - assert((out-&output[0]) == (int)outlen); - - // And make extra sure the output is null terminated - assert(output.size() > outlen); - assert(output[outlen] == 0); - - // Return a string - return std::string(&output[0], outlen); -} - ToUTF8::FromType ToUTF8::calculateEncoding(const std::string& encodingName) { if (encodingName == "win1250") diff --git a/components/to_utf8/to_utf8.hpp b/components/to_utf8/to_utf8.hpp index 6877e2dc1..bfba8a1ac 100644 --- a/components/to_utf8/to_utf8.hpp +++ b/components/to_utf8/to_utf8.hpp @@ -16,15 +16,6 @@ namespace ToUTF8 // probably others) }; - // Return a writable buffer of at least 'size' bytes. The buffer - // does not have to be freed. - char* getBuffer(int size); - - // Convert the previously written buffer to UTF8 from the given code - // page. - std::string getUtf8(FromType from); - std::string getLegacyEnc(FromType to); - FromType calculateEncoding(const std::string& encodingName); std::string encodingUsingMessage(const std::string& encodingName); From 019893b5c677c1fd605e106cb22295805f2e019a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 3 Jan 2013 23:44:58 -0800 Subject: [PATCH 278/378] Get rid of some unnecessary case-insensitive compares --- components/nifogre/ogre_nif_loader.cpp | 21 ++++++++------------- components/nifogre/ogre_nif_loader.hpp | 17 ----------------- 2 files changed, 8 insertions(+), 30 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index ad51d50b9..b3ceb089e 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -43,6 +43,7 @@ #include +#include #include #include @@ -219,7 +220,7 @@ struct KeyTimeSort }; -typedef std::map LoaderMap; +typedef std::map LoaderMap; static LoaderMap sLoaders; public: @@ -918,7 +919,7 @@ class NIFMeshLoader : Ogre::ManualResourceLoader } - typedef std::map LoaderMap; + typedef std::map LoaderMap; static LoaderMap sLoaders; public: @@ -1101,13 +1102,6 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textke return entitylist; } -struct checklow { - bool operator()(const char &a, const char &b) const - { - return ::tolower(a) == ::tolower(b); - } -}; - EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bonename, Ogre::SceneNode *parentNode, const std::string &name, @@ -1120,18 +1114,19 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo return entitylist; Ogre::SceneManager *sceneMgr = parentNode->getCreator(); - std::string filter = "Tri "+bonename; + std::string filter = "tri "+bonename; + std::transform(filter.begin()+4, filter.end(), filter.begin()+4, ::tolower); for(size_t i = 0;i < meshes.size();i++) { Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first->getName()); if(ent->hasSkeleton()) { + std::transform(meshes[i].second.begin(), meshes[i].second.end(), meshes[i].second.begin(), ::tolower); + if(meshes[i].second.length() < filter.length() || - std::mismatch(filter.begin(), filter.end(), meshes[i].second.begin(), checklow()).first != filter.end()) + meshes[i].second.compare(0, filter.length(), filter) != 0) { sceneMgr->destroyEntity(ent); - meshes.erase(meshes.begin()+i); - i--; continue; } if(!entitylist.mSkelBase) diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index a203112b5..08a233245 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -30,23 +30,6 @@ #include #include -#include -#include - -#include "../nif/node.hpp" - -#include - -class BoundsFinder; - -struct ciLessBoost : std::binary_function -{ - bool operator() (const std::string & s1, const std::string & s2) const - { - //case insensitive version of is_less - return boost::algorithm::lexicographical_compare(s1, s2, boost::algorithm::is_iless()); - } -}; namespace Nif { From 61ad8bb3ddcc1ec2a599a34a9a63d6707faaaae8 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 4 Jan 2013 00:14:41 -0800 Subject: [PATCH 279/378] Use a list of mesh names instead of mesh objects --- components/nifogre/ogre_nif_loader.cpp | 6 +++--- components/nifogre/ogre_nif_loader.hpp | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index b3ceb089e..9e17736ef 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -1005,7 +1005,7 @@ public: mesh->setAutoBuildEdgeLists(false); } - meshes.push_back(std::make_pair(mesh, shape->name)); + meshes.push_back(std::make_pair(mesh->getName(), shape->name)); } else if(node->recType != Nif::RC_NiNode && node->recType != Nif::RC_RootCollisionNode && node->recType != Nif::RC_NiRotatingParticles) @@ -1072,7 +1072,7 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textke Ogre::SceneManager *sceneMgr = parent->getCreator(); for(size_t i = 0;i < meshes.size();i++) { - entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].first->getName())); + entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].first)); Ogre::Entity *entity = entitylist.mEntities.back(); if(!entitylist.mSkelBase && entity->hasSkeleton()) entitylist.mSkelBase = entity; @@ -1118,7 +1118,7 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo std::transform(filter.begin()+4, filter.end(), filter.begin()+4, ::tolower); for(size_t i = 0;i < meshes.size();i++) { - Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first->getName()); + Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first); if(ent->hasSkeleton()) { std::transform(meshes[i].second.begin(), meshes[i].second.end(), meshes[i].second.begin(), ::tolower); diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 08a233245..74b6f44df 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -52,9 +52,8 @@ struct EntityList { }; -/** This holds a list of meshes along with the names of their parent nodes - */ -typedef std::vector< std::pair > MeshPairList; +/** This holds a list of mesh names along with the names of their parent nodes */ +typedef std::vector< std::pair > MeshPairList; /** Manual resource loader for NIF meshes. This is the main class responsible for translating the internal NIF mesh structure into From c947d87ab9e9510e322d8cb030f42b64f6f07dc4 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Fri, 4 Jan 2013 15:10:30 +0100 Subject: [PATCH 280/378] Add a test for to_utf8 component --- components/to_utf8/tests/.gitignore | 1 + .../to_utf8/tests/output/to_utf8_test.out | 4 ++ components/to_utf8/tests/test.sh | 18 ++++++ components/to_utf8/tests/to_utf8_test.cpp | 61 +++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 components/to_utf8/tests/.gitignore create mode 100644 components/to_utf8/tests/output/to_utf8_test.out create mode 100755 components/to_utf8/tests/test.sh create mode 100644 components/to_utf8/tests/to_utf8_test.cpp diff --git a/components/to_utf8/tests/.gitignore b/components/to_utf8/tests/.gitignore new file mode 100644 index 000000000..814490404 --- /dev/null +++ b/components/to_utf8/tests/.gitignore @@ -0,0 +1 @@ +*_test diff --git a/components/to_utf8/tests/output/to_utf8_test.out b/components/to_utf8/tests/output/to_utf8_test.out new file mode 100644 index 000000000..dcb32359a --- /dev/null +++ b/components/to_utf8/tests/output/to_utf8_test.out @@ -0,0 +1,4 @@ +original: Без вопросов отдаете ему рулет, зная, что позже вы сможете привести с собой своих друзей и тогда он получит по заслугам? +converted: Без вопросов отдаете ему рулет, зная, что позже вы сможете привести с собой своих друзей и тогда он получит по заслугам? +original: Vous lui donnez le gâteau sans protester avant d’aller chercher tous vos amis et de revenir vous venger. +converted: Vous lui donnez le gâteau sans protester avant d’aller chercher tous vos amis et de revenir vous venger. diff --git a/components/to_utf8/tests/test.sh b/components/to_utf8/tests/test.sh new file mode 100755 index 000000000..2d07708ad --- /dev/null +++ b/components/to_utf8/tests/test.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +make || exit + +mkdir -p output + +PROGS=*_test + +for a in $PROGS; do + if [ -f "output/$a.out" ]; then + echo "Running $a:" + ./$a | diff output/$a.out - + else + echo "Creating $a.out" + ./$a > "output/$a.out" + git add "output/$a.out" + fi +done diff --git a/components/to_utf8/tests/to_utf8_test.cpp b/components/to_utf8/tests/to_utf8_test.cpp new file mode 100644 index 000000000..8c25c483e --- /dev/null +++ b/components/to_utf8/tests/to_utf8_test.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include + +#include "../to_utf8.hpp" + +std::string getFirstLine(const std::string &filename); +void testEncoder(ToUTF8::FromType encoding, const std::string &legacyEncFile, + const std::string &utf8File); + +/// Test character encoding conversion to and from UTF-8 +void testEncoder(ToUTF8::FromType encoding, const std::string &legacyEncFile, + const std::string &utf8File) +{ + // get some test data + std::string legacyEncLine = getFirstLine(legacyEncFile); + std::string utf8Line = getFirstLine(utf8File); + + // create an encoder for specified character encoding + ToUTF8::Utf8Encoder encoder; + encoder.setEncoding(encoding); + + // convert text to UTF-8 + std::string convertedUtf8Line = encoder.getUtf8(legacyEncLine); + + std::cout << "original: " << utf8Line << std::endl; + std::cout << "converted: " << convertedUtf8Line << std::endl; + + // check correctness + assert(convertedUtf8Line == utf8Line); + + // convert UTF-8 text to legacy encoding + std::string convertedLegacyEncLine = encoder.getLegacyEnc(utf8Line); + // check correctness + assert(convertedLegacyEncLine == legacyEncLine); +} + +std::string getFirstLine(const std::string &filename) +{ + std::string line; + std::ifstream text (filename.c_str()); + + if (!text.is_open()) + { + throw std::runtime_error("Unable to open file " + filename); + } + + std::getline(text, line); + text.close(); + + return line; +} + +int main() +{ + testEncoder(ToUTF8::WINDOWS_1251, "data/russian-win1251.txt", "data/russian-utf8.txt"); + testEncoder(ToUTF8::WINDOWS_1252, "data/french-win1252.txt", "data/french-utf8.txt"); + return 0; +} From cc792da85895c85db8b76a5c9692bc260f35f649 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Fri, 4 Jan 2013 15:24:07 +0100 Subject: [PATCH 281/378] Fix to_utf8 test: add test data directory and remove unused include --- components/to_utf8/tests/test_data/french-utf8.txt | 1 + components/to_utf8/tests/test_data/french-win1252.txt | 1 + components/to_utf8/tests/test_data/russian-utf8.txt | 1 + components/to_utf8/tests/test_data/russian-win1251.txt | 1 + components/to_utf8/tests/to_utf8_test.cpp | 5 ++--- 5 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 components/to_utf8/tests/test_data/french-utf8.txt create mode 100644 components/to_utf8/tests/test_data/french-win1252.txt create mode 100644 components/to_utf8/tests/test_data/russian-utf8.txt create mode 100644 components/to_utf8/tests/test_data/russian-win1251.txt diff --git a/components/to_utf8/tests/test_data/french-utf8.txt b/components/to_utf8/tests/test_data/french-utf8.txt new file mode 100644 index 000000000..aaaccac73 --- /dev/null +++ b/components/to_utf8/tests/test_data/french-utf8.txt @@ -0,0 +1 @@ +Vous lui donnez le gâteau sans protester avant d’aller chercher tous vos amis et de revenir vous venger. \ No newline at end of file diff --git a/components/to_utf8/tests/test_data/french-win1252.txt b/components/to_utf8/tests/test_data/french-win1252.txt new file mode 100644 index 000000000..1de4593e9 --- /dev/null +++ b/components/to_utf8/tests/test_data/french-win1252.txt @@ -0,0 +1 @@ +Vous lui donnez le gteau sans protester avant daller chercher tous vos amis et de revenir vous venger. \ No newline at end of file diff --git a/components/to_utf8/tests/test_data/russian-utf8.txt b/components/to_utf8/tests/test_data/russian-utf8.txt new file mode 100644 index 000000000..eb20b32dd --- /dev/null +++ b/components/to_utf8/tests/test_data/russian-utf8.txt @@ -0,0 +1 @@ +Без вопросов отдаете ему рулет, зная, что позже вы сможете привести с собой своих друзей и тогда он получит по заслугам? \ No newline at end of file diff --git a/components/to_utf8/tests/test_data/russian-win1251.txt b/components/to_utf8/tests/test_data/russian-win1251.txt new file mode 100644 index 000000000..086e57edd --- /dev/null +++ b/components/to_utf8/tests/test_data/russian-win1251.txt @@ -0,0 +1 @@ + , , ? \ No newline at end of file diff --git a/components/to_utf8/tests/to_utf8_test.cpp b/components/to_utf8/tests/to_utf8_test.cpp index 8c25c483e..4bba0cf90 100644 --- a/components/to_utf8/tests/to_utf8_test.cpp +++ b/components/to_utf8/tests/to_utf8_test.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include "../to_utf8.hpp" @@ -55,7 +54,7 @@ std::string getFirstLine(const std::string &filename) int main() { - testEncoder(ToUTF8::WINDOWS_1251, "data/russian-win1251.txt", "data/russian-utf8.txt"); - testEncoder(ToUTF8::WINDOWS_1252, "data/french-win1252.txt", "data/french-utf8.txt"); + testEncoder(ToUTF8::WINDOWS_1251, "test_data/russian-win1251.txt", "test_data/russian-utf8.txt"); + testEncoder(ToUTF8::WINDOWS_1252, "test_data/french-win1252.txt", "test_data/french-utf8.txt"); return 0; } From 3c91f7793b29d2c9479b3ca15e4b327cd87e08f2 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Fri, 4 Jan 2013 08:40:33 -0800 Subject: [PATCH 282/378] overrode MyGUI::DataManager::isDataExist Created a override of MyGUI::DataManager::isDataExist to fix a performance issue with MyGUI startup. This required moving the functionality of MyGUI::OgrePlatform into OEngine::GUI::MyGUIManager so that a new version of the MyGUI::DataManager could be created. --- libs/openengine/gui/manager.cpp | 45 ++++++++++++++++++++++++++++----- libs/openengine/gui/manager.hpp | 11 +++++--- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/libs/openengine/gui/manager.cpp b/libs/openengine/gui/manager.cpp index acb4ed9df..925891e1b 100644 --- a/libs/openengine/gui/manager.cpp +++ b/libs/openengine/gui/manager.cpp @@ -6,6 +6,19 @@ using namespace OEngine::GUI; +/* + * As of MyGUI 3.2.0, MyGUI::OgreDataManager::isDataExist is unnecessarily complex + * this override fixes the resulting performance issue. + */ +class FixedOgreDataManager : public MyGUI::OgreDataManager +{ +public: + bool isDataExist(const std::string& _name) + { + return Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup (_name); + } +}; + void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging, const std::string& logDir) { assert(wnd); @@ -25,11 +38,18 @@ void MyGUIManager::setup(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool if(!logDir.empty()) theLogFile.insert(0, logDir); - // Set up OGRE platform. We might make this more generic later. - mPlatform = new OgrePlatform(); + // Set up OGRE platform (bypassing OgrePlatform). We might make this more generic later. + mLogManager = new LogManager(); + mRenderManager = new OgreRenderManager(); + mDataManager = new FixedOgreDataManager(); + LogManager::getInstance().setSTDOutputEnabled(logging); - mPlatform->initialise(wnd, mgr, "General", theLogFile); + if (!theLogFile.empty()) + LogManager::getInstance().createDefaultSource(theLogFile); + + mRenderManager->initialise(wnd, mgr); + mDataManager->initialise("General"); // Create GUI mGui = new Gui(); @@ -40,11 +60,22 @@ void MyGUIManager::shutdown() { mGui->shutdown (); delete mGui; - if(mPlatform) + if(mRenderManager) + { + mRenderManager->shutdown(); + delete mRenderManager; + mRenderManager = NULL; + } + if(mDataManager) + { + mDataManager->shutdown(); + delete mDataManager; + mDataManager = NULL; + } + if (mLogManager) { - mPlatform->shutdown(); - delete mPlatform; + delete mLogManager; + mLogManager = NULL; } mGui = NULL; - mPlatform = NULL; } diff --git a/libs/openengine/gui/manager.hpp b/libs/openengine/gui/manager.hpp index 1ec2e2fcf..c0f98da88 100644 --- a/libs/openengine/gui/manager.hpp +++ b/libs/openengine/gui/manager.hpp @@ -3,8 +3,10 @@ namespace MyGUI { - class OgrePlatform; class Gui; + class LogManager; + class OgreDataManager; + class OgreRenderManager; } namespace Ogre @@ -18,12 +20,15 @@ namespace GUI { class MyGUIManager { - MyGUI::OgrePlatform *mPlatform; MyGUI::Gui *mGui; + MyGUI::LogManager* mLogManager; + MyGUI::OgreDataManager* mDataManager; + MyGUI::OgreRenderManager* mRenderManager; Ogre::SceneManager* mSceneMgr; + public: - MyGUIManager() : mPlatform(NULL), mGui(NULL) {} + MyGUIManager() : mLogManager(NULL), mDataManager(NULL), mRenderManager(NULL), mGui(NULL) {} MyGUIManager(Ogre::RenderWindow *wnd, Ogre::SceneManager *mgr, bool logging=false, const std::string& logDir = std::string("")) { setup(wnd,mgr,logging, logDir); From 218139351830d00b3b564b13ab7d16276b99c6d3 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Fri, 4 Jan 2013 11:36:22 -0800 Subject: [PATCH 283/378] change texture renaming logic to increase performance ResourceGroupManager::resourceExistsInAnyGroup is slow (at least on windows) if the tested path does not exist, but is fast if it does (due to finding it in the index). This change tries the '.dds' version of the name first, and reverts to the original if the '.dds' version was not found. --- components/nifogre/ogre_nif_loader.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index ad51d50b9..dca459119 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -536,11 +536,23 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam * textures from tga to dds for increased load speed, but all * texture file name references were kept as .tga. */ - texName = "textures\\" + st->filename; - if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texName)) + + static const char * path = "textures\\"; + + texName = path + st->filename; + + Ogre::String::size_type pos = texName.rfind('.'); + + if (texName.compare (pos, texName.size () - pos, ".dds") != 0) { - Ogre::String::size_type pos = texName.rfind('.'); + // since we know all (GOTY edition or less) textures end + // in .dds, we change the extension texName.replace(pos, texName.length(), ".dds"); + + // if it turns out that the above wasn't true in all cases (not for vanilla, but maybe mods) + // verify, and revert if false (this call succeeds quickly, but fails slowly) + if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texName)) + texName = path + st->filename; } } else warn("Found internal texture, ignoring."); From dcfa902525ef76cfab2426c03965a6a0291b7c53 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Fri, 4 Jan 2013 13:08:09 -0800 Subject: [PATCH 284/378] fixed error in cmake when disabling esmtool --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 36e45d78a..766167672 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -534,7 +534,9 @@ if (WIN32) set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS}) endif (BUILD_LAUNCHER) set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS}) - set_target_properties(esmtool PROPERTIES COMPILE_FLAGS ${WARNINGS}) + if (BUILD_ESMTOOL) + set_target_properties(esmtool PROPERTIES COMPILE_FLAGS ${WARNINGS}) + endif (BUILD_ESMTOOL) endif(MSVC) # Same for MinGW From 5c7f1bd497d620e1d219b485eebb6fb9f8c598ab Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Tue, 1 Jan 2013 12:55:39 -0800 Subject: [PATCH 285/378] moved ConstrainedDataStream into its own file moved the ConstrainedDataStream into its own source file and changed BSAFile to use it though the exposed factory function. This is in preperation foreimplementing it based on feedback from profiling that (at least on windows) the C++ iostreams library is quite slow. --- components/CMakeLists.txt | 2 +- components/bsa/bsa_file.cpp | 89 +------------- .../files/constrainedfiledatastream.cpp | 112 ++++++++++++++++++ .../files/constrainedfiledatastream.hpp | 8 ++ 4 files changed, 126 insertions(+), 85 deletions(-) create mode 100644 components/files/constrainedfiledatastream.cpp create mode 100644 components/files/constrainedfiledatastream.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index f3d5f4aee..f54efab22 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -48,7 +48,7 @@ add_component_dir (misc add_component_dir (files linuxpath windowspath macospath fixedpath multidircollection collections fileops configurationmanager - filelibrary ogreplugin + filelibrary ogreplugin constrainedfiledatastream ) add_component_dir (compiler diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index 1700e1aab..5e529e18e 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -23,94 +23,15 @@ #include "bsa_file.hpp" -#include -#include -#include +//#include +//#include +//#include -#include +#include "../files/constrainedfiledatastream.hpp" using namespace std; using namespace Bsa; -class ConstrainedDataStream : public Ogre::DataStream { - std::ifstream mStream; - const size_t mStart; - size_t mPos; - bool mIsEOF; - -public: - ConstrainedDataStream(const Ogre::String &fname, size_t start, size_t length) - : mStream(fname.c_str(), std::ios_base::binary), mStart(start), mPos(0), mIsEOF(false) - { - mSize = length; - if(!mStream.seekg(mStart, std::ios_base::beg)) - throw std::runtime_error("Error seeking to start of BSA entry"); - } - - ConstrainedDataStream(const Ogre::String &name, const Ogre::String &fname, - size_t start, size_t length) - : Ogre::DataStream(name), mStream(fname.c_str(), std::ios_base::binary), - mStart(start), mPos(0), mIsEOF(false) - { - mSize = length; - if(!mStream.seekg(mStart, std::ios_base::beg)) - throw std::runtime_error("Error seeking to start of BSA entry"); - } - - - virtual size_t read(void *buf, size_t count) - { - mStream.clear(); - - if(count > mSize-mPos) - { - count = mSize-mPos; - mIsEOF = true; - } - mStream.read(reinterpret_cast(buf), count); - - count = mStream.gcount(); - mPos += count; - return count; - } - - virtual void skip(long count) - { - if((count >= 0 && (size_t)count <= mSize-mPos) || - (count < 0 && (size_t)-count <= mPos)) - { - mStream.clear(); - if(mStream.seekg(count, std::ios_base::cur)) - { - mPos += count; - mIsEOF = false; - } - } - } - - virtual void seek(size_t pos) - { - if(pos < mSize) - { - mStream.clear(); - if(mStream.seekg(pos+mStart, std::ios_base::beg)) - { - mPos = pos; - mIsEOF = false; - } - } - } - - virtual size_t tell() const - { return mPos; } - - virtual bool eof() const - { return mIsEOF; } - - virtual void close() - { mStream.close(); } -}; - /// Error handling void BSAFile::fail(const string &msg) @@ -253,5 +174,5 @@ Ogre::DataStreamPtr BSAFile::getFile(const char *file) fail("File not found: " + string(file)); const FileStruct &fs = files[i]; - return Ogre::DataStreamPtr(new ConstrainedDataStream(filename, fs.offset, fs.fileSize)); + return openConstrainedFileDataStream (filename.c_str (), fs.offset, fs.fileSize); } diff --git a/components/files/constrainedfiledatastream.cpp b/components/files/constrainedfiledatastream.cpp new file mode 100644 index 000000000..037ffe6f0 --- /dev/null +++ b/components/files/constrainedfiledatastream.cpp @@ -0,0 +1,112 @@ +/* + OpenMW - The completely unofficial reimplementation of Morrowind + Copyright (C) 2008-2010 Nicolay Korslund + Email: < korslund@gmail.com > + WWW: http://openmw.sourceforge.net/ + + This file (bsa_file.cpp) is part of the OpenMW package. + + OpenMW is distributed as free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License + version 3, as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + version 3 along with this program. If not, see + http://www.gnu.org/licenses/ . + + */ + +#include "constrainedfiledatastream.hpp" + +#include + +class ConstrainedDataStream : public Ogre::DataStream { + std::ifstream mStream; + const size_t mStart; + size_t mPos; + bool mIsEOF; + +public: + ConstrainedDataStream(const Ogre::String &fname, size_t start, size_t length) + : mStream(fname.c_str(), std::ios_base::binary), mStart(start), mPos(0), mIsEOF(false) + { + mSize = length; + if(!mStream.seekg(mStart, std::ios_base::beg)) + throw std::runtime_error("Error seeking to start of BSA entry"); + } + + ConstrainedDataStream(const Ogre::String &name, const Ogre::String &fname, + size_t start, size_t length) + : Ogre::DataStream(name), mStream(fname.c_str(), std::ios_base::binary), + mStart(start), mPos(0), mIsEOF(false) + { + mSize = length; + if(!mStream.seekg(mStart, std::ios_base::beg)) + throw std::runtime_error("Error seeking to start of BSA entry"); + } + + + virtual size_t read(void *buf, size_t count) + { + mStream.clear(); + + if(count > mSize-mPos) + { + count = mSize-mPos; + mIsEOF = true; + } + mStream.read(reinterpret_cast(buf), count); + + count = mStream.gcount(); + mPos += count; + return count; + } + + virtual void skip(long count) + { + if((count >= 0 && (size_t)count <= mSize-mPos) || + (count < 0 && (size_t)-count <= mPos)) + { + mStream.clear(); + if(mStream.seekg(count, std::ios_base::cur)) + { + mPos += count; + mIsEOF = false; + } + } + } + + virtual void seek(size_t pos) + { + if(pos < mSize) + { + mStream.clear(); + if(mStream.seekg(pos+mStart, std::ios_base::beg)) + { + mPos = pos; + mIsEOF = false; + } + } + } + + virtual size_t tell() const + { return mPos; } + + virtual bool eof() const + { return mIsEOF; } + + virtual void close() + { mStream.close(); } +}; + +Ogre::DataStreamPtr openConstrainedFileDataStream (char const * filename, size_t offset, size_t length) +{ + assert (length != 0xFFFFFFFF); // reserved for future use... + + return Ogre::DataStreamPtr(new ConstrainedDataStream(filename, offset, length)); +} diff --git a/components/files/constrainedfiledatastream.hpp b/components/files/constrainedfiledatastream.hpp new file mode 100644 index 000000000..367defcbc --- /dev/null +++ b/components/files/constrainedfiledatastream.hpp @@ -0,0 +1,8 @@ +#ifndef COMPONENTS_FILES_CONSTRAINEDFILEDATASTREAM_HPP +#define COMPONENTS_FILES_CONSTRAINEDFILEDATASTREAM_HPP + +#include + +Ogre::DataStreamPtr openConstrainedFileDataStream (char const * filename, size_t offset = 0, size_t length = 0xFFFFFFFF); + +#endif // COMPONENTS_FILES_CONSTRAINEDFILEDATASTREAM_HPP From 278337116b93e62e7458e03463e83c2055eb15e7 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Tue, 1 Jan 2013 15:07:33 -0800 Subject: [PATCH 286/378] increased performance of ConstrainedDataStream Reimplemented ConstrainedDataStream to use low-level IO calls and a custom buffering scheme to avoid using C++ iostreams. --- components/CMakeLists.txt | 2 +- .../files/constrainedfiledatastream.cpp | 218 ++++++++----- components/files/lowlevelfile.cpp | 299 ++++++++++++++++++ components/files/lowlevelfile.hpp | 54 ++++ 4 files changed, 492 insertions(+), 81 deletions(-) create mode 100644 components/files/lowlevelfile.cpp create mode 100644 components/files/lowlevelfile.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index f54efab22..3da09ecb8 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -48,7 +48,7 @@ add_component_dir (misc add_component_dir (files linuxpath windowspath macospath fixedpath multidircollection collections fileops configurationmanager - filelibrary ogreplugin constrainedfiledatastream + filelibrary ogreplugin constrainedfiledatastream lowlevelfile ) add_component_dir (compiler diff --git a/components/files/constrainedfiledatastream.cpp b/components/files/constrainedfiledatastream.cpp index 037ffe6f0..2f75ce41b 100644 --- a/components/files/constrainedfiledatastream.cpp +++ b/components/files/constrainedfiledatastream.cpp @@ -1,112 +1,170 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008-2010 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.sourceforge.net/ +#include "constrainedfiledatastream.hpp" +#include "lowlevelfile.hpp" - This file (bsa_file.cpp) is part of the OpenMW package. +#include +#include - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. +namespace { - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. +class ConstrainedDataStream : public Ogre::DataStream { +public: - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . + static const size_t sBufferSize = 4096; // somewhat arbitrary though 64KB buffers didn't seem to improve performance any + static const size_t sBufferThreshold = 1024; // reads larger than this bypass buffering as cost of memcpy outweighs cost of system call - */ + ConstrainedDataStream(const Ogre::String &fname, size_t start, size_t length) + { + mFile.open (fname.c_str ()); + mSize = length != 0xFFFFFFFF ? length : mFile.size () - start; -#include "constrainedfiledatastream.hpp" + mPos = 0; + mOrigin = start; + mExtent = start + mSize; -#include + mBufferOrigin = 0; + mBufferExtent = 0; + } + -class ConstrainedDataStream : public Ogre::DataStream { - std::ifstream mStream; - const size_t mStart; - size_t mPos; - bool mIsEOF; + size_t read(void* buf, size_t count) + { + assert (mPos <= mSize); -public: - ConstrainedDataStream(const Ogre::String &fname, size_t start, size_t length) - : mStream(fname.c_str(), std::ios_base::binary), mStart(start), mPos(0), mIsEOF(false) - { - mSize = length; - if(!mStream.seekg(mStart, std::ios_base::beg)) - throw std::runtime_error("Error seeking to start of BSA entry"); - } + uint8_t * out = reinterpret_cast (buf); - ConstrainedDataStream(const Ogre::String &name, const Ogre::String &fname, - size_t start, size_t length) - : Ogre::DataStream(name), mStream(fname.c_str(), std::ios_base::binary), - mStart(start), mPos(0), mIsEOF(false) - { - mSize = length; - if(!mStream.seekg(mStart, std::ios_base::beg)) - throw std::runtime_error("Error seeking to start of BSA entry"); - } + size_t posBeg = mOrigin + mPos; + size_t posEnd = posBeg + count; + if (posEnd > mExtent) + posEnd = mExtent; - virtual size_t read(void *buf, size_t count) - { - mStream.clear(); - - if(count > mSize-mPos) - { - count = mSize-mPos; - mIsEOF = true; - } - mStream.read(reinterpret_cast(buf), count); - - count = mStream.gcount(); - mPos += count; - return count; - } + size_t posCur = posBeg; + + while (posCur != posEnd) + { + size_t readLeft = posEnd - posCur; + + if (posCur < mBufferOrigin || posCur >= mBufferExtent) + { + if (readLeft >= sBufferThreshold || (posCur == mOrigin && posEnd == mExtent)) + { + assert (mFile.tell () == mBufferExtent); + + if (posCur != mBufferExtent) + mFile.seek (posCur); + + posCur += mFile.read (out, readLeft); + + mBufferOrigin = mBufferExtent = posCur; + + mPos = posCur - mOrigin; + + return posCur - posBeg; + } + else + { + size_t newBufferOrigin; + + if ((posCur < mBufferOrigin) && (mBufferOrigin - posCur < sBufferSize)) + newBufferOrigin = std::max (mOrigin, mBufferOrigin > sBufferSize ? mBufferOrigin - sBufferSize : 0); + else + newBufferOrigin = posCur; + + fill (newBufferOrigin); + } + } + + size_t xfer = std::min (readLeft, mBufferExtent - posCur); - virtual void skip(long count) + memcpy (out, mBuffer + (posCur - mBufferOrigin), xfer); + + posCur += xfer; + out += xfer; + } + + count = posEnd - posBeg; + mPos += count; + return count; + } + + void skip(long count) { + assert (mPos <= mSize); + if((count >= 0 && (size_t)count <= mSize-mPos) || (count < 0 && (size_t)-count <= mPos)) - { - mStream.clear(); - if(mStream.seekg(count, std::ios_base::cur)) - { - mPos += count; - mIsEOF = false; - } - } + mPos += count; } - virtual void seek(size_t pos) + void seek(size_t pos) { - if(pos < mSize) - { - mStream.clear(); - if(mStream.seekg(pos+mStart, std::ios_base::beg)) - { - mPos = pos; - mIsEOF = false; - } - } + assert (mPos <= mSize); + + if (pos < mSize) + mPos = pos; } virtual size_t tell() const - { return mPos; } + { + assert (mPos <= mSize); + + return mPos; + } virtual bool eof() const - { return mIsEOF; } + { + assert (mPos <= mSize); + + return mPos == mSize; + } virtual void close() - { mStream.close(); } + { + mFile.close(); + } + +private: + + void fill (size_t newOrigin) + { + assert (mFile.tell () == mBufferExtent); + + size_t newExtent = newOrigin + sBufferSize; + + if (newExtent > mExtent) + newExtent = mExtent; + + size_t oldExtent = mBufferExtent; + + if (newOrigin != oldExtent) + mFile.seek (newOrigin); + + mBufferOrigin = mBufferExtent = newOrigin; + + size_t amountRequested = newExtent - newOrigin; + + size_t amountRead = mFile.read (mBuffer, amountRequested); + + if (amountRead != amountRequested) + throw std::runtime_error ("An unexpected condition occurred while reading from a file."); + + mBufferExtent = newExtent; + } + + LowLevelFile mFile; + + size_t mOrigin; + size_t mExtent; + size_t mPos; + + uint8_t mBuffer [sBufferSize]; + size_t mBufferOrigin; + size_t mBufferExtent; }; +} // end of unnamed namespace + Ogre::DataStreamPtr openConstrainedFileDataStream (char const * filename, size_t offset, size_t length) { - assert (length != 0xFFFFFFFF); // reserved for future use... - return Ogre::DataStreamPtr(new ConstrainedDataStream(filename, offset, length)); } diff --git a/components/files/lowlevelfile.cpp b/components/files/lowlevelfile.cpp new file mode 100644 index 000000000..71fd1b523 --- /dev/null +++ b/components/files/lowlevelfile.cpp @@ -0,0 +1,299 @@ +#include "lowlevelfile.hpp" + +#include +#include +#include + +#if FILE_API == FILE_API_POSIX +#include +#include +#include +#endif + +#if FILE_API == FILE_API_STDIO +/* + * + * Implementation of LowLevelFile methods using c stdio + * + */ + +LowLevelFile::LowLevelFile () +{ + mHandle = NULL; +} + +LowLevelFile::~LowLevelFile () +{ + if (mHandle != NULL) + fclose (mHandle); +} + +void LowLevelFile::open (char const * filename) +{ + assert (mHandle == NULL); + + mHandle = fopen (filename, "rb"); + + if (mHandle == NULL) + { + std::ostringstream os; + os << "Failed to open '" << filename << "' for reading."; + throw std::runtime_error (os.str ()); + } +} + +void LowLevelFile::close () +{ + assert (mHandle != NULL); + + fclose (mHandle); + + mHandle = NULL; +} + +size_t LowLevelFile::size () +{ + assert (mHandle != NULL); + + long oldPosition = ftell (mHandle); + + if (oldPosition == -1) + throw std::runtime_error ("A query operation on a file failed."); + + if (fseek (mHandle, 0, SEEK_END) != 0) + throw std::runtime_error ("A query operation on a file failed."); + + long Size = ftell (mHandle); + + if (Size == -1) + throw std::runtime_error ("A query operation on a file failed."); + + if (fseek (mHandle, oldPosition, SEEK_SET) != 0) + throw std::runtime_error ("A query operation on a file failed."); + + return size_t (Size); +} + +void LowLevelFile::seek (size_t Position) +{ + assert (mHandle != NULL); + + if (fseek (mHandle, Position, SEEK_SET) != 0) + throw std::runtime_error ("A seek operation on a file failed."); +} + +size_t LowLevelFile::tell () +{ + assert (mHandle != NULL); + + long Position = ftell (mHandle); + + if (Position == -1) + throw std::runtime_error ("A query operation on a file failed."); + + return size_t (Position); +} + +size_t LowLevelFile::read (void * data, size_t size) +{ + assert (mHandle != NULL); + + int amount = fread (data, 1, size, mHandle); + + if (amount == 0 && ferror (mHandle)) + throw std::runtime_error ("A read operation on a file failed."); + + return amount; +} + +#elif FILE_API == FILE_API_POSIX +/* + * + * Implementation of LowLevelFile methods using posix IO calls + * + */ + +LowLevelFile::LowLevelFile () +{ + mHandle = -1; +} + +LowLevelFile::~LowLevelFile () +{ + if (mHandle != -1) + ::close (mHandle); +} + +void LowLevelFile::open (char const * filename) +{ + assert (mHandle == -1); + +#ifdef O_BINARY + static const int openFlags = O_RDONLY | O_BINARY; +#else + static const int openFlags = O_RDONLY; +#endif + + mHandle = ::open (filename, openFlags, 0); + + if (mHandle == -1) + { + std::ostringstream os; + os << "Failed to open '" << filename << "' for reading."; + throw std::runtime_error (os.str ()); + } +} + +void LowLevelFile::close () +{ + assert (mHandle != -1); + + ::close (mHandle); + + mHandle = -1; +} + +size_t LowLevelFile::size () +{ + assert (mHandle != -1); + + size_t oldPosition = ::lseek (mHandle, 0, SEEK_CUR); + + if (oldPosition == size_t (-1)) + throw std::runtime_error ("A query operation on a file failed."); + + size_t Size = ::lseek (mHandle, 0, SEEK_END); + + if (Size == size_t (-1)) + throw std::runtime_error ("A query operation on a file failed."); + + if (lseek (mHandle, oldPosition, SEEK_SET) == -1) + throw std::runtime_error ("A query operation on a file failed."); + + return Size; +} + +void LowLevelFile::seek (size_t Position) +{ + assert (mHandle != -1); + + if (::lseek (mHandle, Position, SEEK_SET) == -1) + throw std::runtime_error ("A seek operation on a file failed."); +} + +size_t LowLevelFile::tell () +{ + assert (mHandle != -1); + + size_t Position = ::lseek (mHandle, 0, SEEK_CUR); + + if (Position == size_t (-1)) + throw std::runtime_error ("A query operation on a file failed."); + + return Position; +} + +size_t LowLevelFile::read (void * data, size_t size) +{ + assert (mHandle != -1); + + int amount = ::read (mHandle, data, size); + + if (amount == -1) + throw std::runtime_error ("A read operation on a file failed."); + + return amount; +} + +#elif FILE_API == FILE_API_WIN32 +/* + * + * Implementation of LowLevelFile methods using Win32 API calls + * + */ + +LowLevelFile::LowLevelFile () +{ + mHandle = INVALID_HANDLE_VALUE; +} + +LowLevelFile::~LowLevelFile () +{ + if (mHandle == INVALID_HANDLE_VALUE) + CloseHandle (mHandle); +} + +void LowLevelFile::open (char const * filename) +{ + assert (mHandle == INVALID_HANDLE_VALUE); + + HANDLE handle = CreateFileA (filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + + if (handle == NULL) + { + std::ostringstream os; + os << "Failed to open '" << filename << "' for reading."; + throw std::runtime_error (os.str ()); + } + + mHandle = handle; +} + +void LowLevelFile::close () +{ + assert (mHandle != INVALID_HANDLE_VALUE); + + CloseHandle (mHandle); + + mHandle = INVALID_HANDLE_VALUE; +} + +size_t LowLevelFile::size () +{ + assert (mHandle != INVALID_HANDLE_VALUE); + + BY_HANDLE_FILE_INFORMATION info; + + if (!GetFileInformationByHandle (mHandle, &info)) + throw std::runtime_error ("A query operation on a file failed."); + + if (info.nFileSizeHigh != 0) + throw std::runtime_error ("Files greater that 4GB are not supported."); + + return info.nFileSizeLow; +} + +void LowLevelFile::seek (size_t Position) +{ + assert (mHandle != INVALID_HANDLE_VALUE); + + if (SetFilePointer (mHandle, Position, NULL, SEEK_SET) == INVALID_SET_FILE_POINTER) + if (GetLastError () != NO_ERROR) + throw std::runtime_error ("A seek operation on a file failed."); +} + +size_t LowLevelFile::tell () +{ + assert (mHandle != INVALID_HANDLE_VALUE); + + DWORD value = SetFilePointer (mHandle, 0, NULL, SEEK_CUR); + + if (value == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) + throw std::runtime_error ("A query operation on a file failed."); + + return value; +} + +size_t LowLevelFile::read (void * data, size_t size) +{ + assert (mHandle != INVALID_HANDLE_VALUE); + + DWORD read; + + if (!ReadFile (mHandle, data, size, &read, NULL)) + throw std::runtime_error ("A read operation on a file failed."); + + return read; +} + +#endif diff --git a/components/files/lowlevelfile.hpp b/components/files/lowlevelfile.hpp new file mode 100644 index 000000000..a6de6df76 --- /dev/null +++ b/components/files/lowlevelfile.hpp @@ -0,0 +1,54 @@ +#ifndef COMPONENTS_FILES_LOWLEVELFILE_HPP +#define COMPONENTS_FILES_LOWLEVELFILE_HPP + +#include + +#define FILE_API_STDIO 0 +#define FILE_API_POSIX 1 +#define FILE_API_WIN32 2 + +#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX +#define FILE_API FILE_API_POSIX +#elif OGRE_PLATFORM == OGRE_PLATFORM_WIN32 +#define FILE_API FILE_API_WIN32 +#else +#define FILE_API FILE_API_STDIO +#endif + +#if FILE_API == FILE_API_STDIO +#include +#elif FILE_API == FILE_API_POSIX +#elif FILE_API == FILE_API_WIN32 +#include +#else +#error Unsupported File API +#endif + +class LowLevelFile +{ +public: + + LowLevelFile (); + ~LowLevelFile (); + + void open (char const * filename); + void close (); + + size_t size (); + + void seek (size_t Position); + size_t tell (); + + size_t read (void * data, size_t size); + +private: +#if FILE_API == FILE_API_STDIO + FILE* mHandle; +#elif FILE_API == FILE_API_POSIX + int mHandle; +#elif FILE_API == FILE_API_WIN32 + HANDLE mHandle; +#endif +}; + +#endif From fec9a5923767747e9d845ac84cbadc60342e7da5 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Tue, 1 Jan 2013 15:13:36 -0800 Subject: [PATCH 287/378] changed EMSLoader to use ConstrainedDataStream Changed the EMSLoader class to use the ConstrainedDataStream so that future changes may benifit from its increased performance. --- components/esm/esmreader.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 580e006df..62078977e 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -1,6 +1,8 @@ #include "esmreader.hpp" #include +#include "../files/constrainedfiledatastream.hpp" + namespace ESM { @@ -108,16 +110,12 @@ void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name) void ESMReader::open(const std::string &file) { - std::ifstream *stream = OGRE_NEW_T(std::ifstream, Ogre::MEMCATEGORY_GENERAL)(file.c_str(), std::ios_base::binary); - // Ogre will delete the stream for us - open(Ogre::DataStreamPtr(new Ogre::FileStreamDataStream(stream)), file); + open (openConstrainedFileDataStream (file.c_str ()), file); } void ESMReader::openRaw(const std::string &file) { - std::ifstream *stream = OGRE_NEW_T(std::ifstream, Ogre::MEMCATEGORY_GENERAL)(file.c_str(), std::ios_base::binary); - // Ogre will delete the stream for us - openRaw(Ogre::DataStreamPtr(new Ogre::FileStreamDataStream(stream)), file); + openRaw (openConstrainedFileDataStream (file.c_str ()), file); } int64_t ESMReader::getHNLong(const char *name) From b4d63814cc13bcd0ef9d46682b73b0f36dad2d0f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 4 Jan 2013 23:12:56 +0100 Subject: [PATCH 288/378] post merge fix --- components/files/lowlevelfile.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/files/lowlevelfile.hpp b/components/files/lowlevelfile.hpp index a6de6df76..f49c466a5 100644 --- a/components/files/lowlevelfile.hpp +++ b/components/files/lowlevelfile.hpp @@ -3,6 +3,8 @@ #include +#include + #define FILE_API_STDIO 0 #define FILE_API_POSIX 1 #define FILE_API_WIN32 2 From 1d4d67f81153fc9a299158daa20b161bb9add69a Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 4 Jan 2013 15:03:57 -0800 Subject: [PATCH 289/378] Avoid underflows if the texture name doesn't include a '.' --- components/nifogre/ogre_nif_loader.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index ae2a0ae15..93f00b178 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -537,14 +537,11 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam * textures from tga to dds for increased load speed, but all * texture file name references were kept as .tga. */ - - static const char * path = "textures\\"; + static const char path[] = "textures\\"; texName = path + st->filename; - Ogre::String::size_type pos = texName.rfind('.'); - - if (texName.compare (pos, texName.size () - pos, ".dds") != 0) + if(pos != Ogre::String::npos && texName.compare(pos, texName.length() - pos, ".dds") != 0) { // since we know all (GOTY edition or less) textures end // in .dds, we change the extension From b52904a6ea4f9acd5f76a89a1eecabd27fc5562c Mon Sep 17 00:00:00 2001 From: Jordan Milne Date: Fri, 4 Jan 2013 19:47:24 -0400 Subject: [PATCH 290/378] Include headers necessary for compilation under VS2010 --- components/files/constrainedfiledatastream.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/files/constrainedfiledatastream.cpp b/components/files/constrainedfiledatastream.cpp index 2f75ce41b..9e36f9075 100644 --- a/components/files/constrainedfiledatastream.cpp +++ b/components/files/constrainedfiledatastream.cpp @@ -3,6 +3,7 @@ #include #include +#include namespace { From a3d33db4153c26f17dfc632551e09c2b62226f29 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 4 Jan 2013 15:55:47 -0800 Subject: [PATCH 291/378] Store a NiNode's NiTextKeyExtraData in a user object binding on Ogre::Bone --- components/nifogre/ogre_nif_loader.cpp | 78 +++++++++++++++++--------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 93f00b178..badadfe01 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -49,10 +49,21 @@ typedef unsigned char ubyte; -using namespace std; -using namespace Nif; -using namespace NifOgre; +namespace Nif +{ + +// These operators allow the Nif extra data types to be stored in an Ogre::Any +// object, which can then be stored in user object bindings on the nodes + +// TODO: Do something useful +std::ostream& operator<<(std::ostream &o, const Nif::NiTextKeyExtraData&) +{ return o; } +} // namespace Nif + + +namespace NifOgre +{ // Helper class that computes the bounding box and of a mesh class BoundsFinder @@ -63,7 +74,7 @@ class BoundsFinder MaxMinFinder() { - min = numeric_limits::infinity(); + min = std::numeric_limits::infinity(); max = -min; } @@ -198,6 +209,17 @@ void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vectornext; } + Nif::ExtraPtr e = node->extra; + while(!e.empty()) + { + if(e->recType == Nif::RC_NiTextKeyExtraData) + { + const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); + bone->getUserObjectBindings().setUserAny(tk->recName, Ogre::Any(*tk)); + } + e = e->extra; + } + const Nif::NiNode *ninode = dynamic_cast(node); if(ninode) { @@ -270,16 +292,16 @@ void loadResource(Ogre::Resource *resource) Nif::NiKeyframeData *kf = kfc->data.getPtr(); /* Get the keyframes and make sure they're sorted first to last */ - QuaternionKeyList quatkeys = kf->mRotations; - Vector3KeyList trankeys = kf->mTranslations; - FloatKeyList scalekeys = kf->mScales; + Nif::QuaternionKeyList quatkeys = kf->mRotations; + Nif::Vector3KeyList trankeys = kf->mTranslations; + Nif::FloatKeyList scalekeys = kf->mScales; std::sort(quatkeys.mKeys.begin(), quatkeys.mKeys.end(), KeyTimeSort()); std::sort(trankeys.mKeys.begin(), trankeys.mKeys.end(), KeyTimeSort()); std::sort(scalekeys.mKeys.begin(), scalekeys.mKeys.end(), KeyTimeSort()); - QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin(); - Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin(); - FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin(); + Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin(); + Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin(); + Nif::FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin(); Ogre::Bone *bone = skel->getBone(targets[i]); const Ogre::Quaternion startquat = bone->getInitialOrientation(); @@ -347,7 +369,7 @@ void loadResource(Ogre::Resource *resource) kframe->setRotation(curquat); else { - QuaternionKeyList::VecType::const_iterator last = quatiter-1; + Nif::QuaternionKeyList::VecType::const_iterator last = quatiter-1; float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime); kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat)); } @@ -355,7 +377,7 @@ void loadResource(Ogre::Resource *resource) kframe->setTranslate(curtrans); else { - Vector3KeyList::VecType::const_iterator last = traniter-1; + Nif::Vector3KeyList::VecType::const_iterator last = traniter-1; float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime); kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff)); } @@ -363,7 +385,7 @@ void loadResource(Ogre::Resource *resource) kframe->setScale(curscale); else { - FloatKeyList::VecType::const_iterator last = scaleiter-1; + Nif::FloatKeyList::VecType::const_iterator last = scaleiter-1; float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime); kframe->setScale(lastscale + ((curscale-lastscale)*diff)); } @@ -485,7 +507,7 @@ static void fail(const std::string &msg) public: -static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &name, const Ogre::String &group) +static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String &name, const Ogre::String &group) { Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton(); Ogre::MaterialPtr material = matMgr.getByName(name); @@ -505,24 +527,24 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam bool vertexColour = (shape->data->colors.size() != 0); // These are set below if present - const NiTexturingProperty *t = NULL; - const NiMaterialProperty *m = NULL; - const NiAlphaProperty *a = NULL; + const Nif::NiTexturingProperty *t = NULL; + const Nif::NiMaterialProperty *m = NULL; + const Nif::NiAlphaProperty *a = NULL; // Scan the property list for material information - const PropertyList &list = shape->props; + const Nif::PropertyList &list = shape->props; for (size_t i = 0;i < list.length();i++) { // Entries may be empty if (list[i].empty()) continue; - const Property *pr = list[i].getPtr(); - if (pr->recType == RC_NiTexturingProperty) - t = static_cast(pr); - else if (pr->recType == RC_NiMaterialProperty) - m = static_cast(pr); - else if (pr->recType == RC_NiAlphaProperty) - a = static_cast(pr); + const Nif::Property *pr = list[i].getPtr(); + if (pr->recType == Nif::RC_NiTexturingProperty) + t = static_cast(pr); + else if (pr->recType == Nif::RC_NiMaterialProperty) + m = static_cast(pr); + else if (pr->recType == Nif::RC_NiAlphaProperty) + a = static_cast(pr); else warn("Skipped property type: "+pr->recName); } @@ -530,7 +552,7 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam // Texture if (t && t->textures[0].inUse) { - NiSourceTexture *st = t->textures[0].texture.getPtr(); + Nif::NiSourceTexture *st = t->textures[0].texture.getPtr(); if (st->external) { /* Bethesda at some at some point converted all their BSA @@ -991,7 +1013,7 @@ public: if(node->recType == Nif::RC_NiTriShape) { - const NiTriShape *shape = dynamic_cast(node); + const Nif::NiTriShape *shape = dynamic_cast(node); Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); std::string fullname = mName+"@shape="+shape->name; @@ -1220,3 +1242,5 @@ extern "C" void ogre_insertTexture(char* name, uint32_t width, uint32_t height, */ + +} // nsmaepace NifOgre From 683ced54a09eefd8dc38f8d64341a30d289f45a2 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 4 Jan 2013 19:41:37 -0800 Subject: [PATCH 292/378] Store and retrieve the node text keys in the bones' user object bindings --- components/nifogre/ogre_nif_loader.cpp | 67 +++++++++++++------------- components/nifogre/ogre_nif_loader.hpp | 2 +- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index badadfe01..6ee02438a 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -49,22 +49,20 @@ typedef unsigned char ubyte; -namespace Nif +namespace std { -// These operators allow the Nif extra data types to be stored in an Ogre::Any +// These operators allow extra data types to be stored in an Ogre::Any // object, which can then be stored in user object bindings on the nodes // TODO: Do something useful -std::ostream& operator<<(std::ostream &o, const Nif::NiTextKeyExtraData&) +ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&) { return o; } -} // namespace Nif - +} namespace NifOgre { - // Helper class that computes the bounding box and of a mesh class BoundsFinder { @@ -164,8 +162,9 @@ static void fail(const std::string &msg) } -static void insertTextKeys(const Nif::NiTextKeyExtraData *tk, TextKeyMap *textkeys) +static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk) { + TextKeyMap textkeys; for(size_t i = 0;i < tk->list.size();i++) { const std::string &str = tk->list[i].text; @@ -178,11 +177,12 @@ static void insertTextKeys(const Nif::NiTextKeyExtraData *tk, TextKeyMap *textke break; std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos)); - textkeys->insert(std::make_pair(tk->list[i].time, str.substr(pos, nextpos-pos))); + textkeys.insert(std::make_pair(tk->list[i].time, str.substr(pos, nextpos-pos))); pos = nextpos; } } + return textkeys; } @@ -215,7 +215,7 @@ void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vectorrecType == Nif::RC_NiTextKeyExtraData) { const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); - bone->getUserObjectBindings().setUserAny(tk->recName, Ogre::Any(*tk)); + bone->getUserObjectBindings().setUserAny("TextKeyExtraData", Ogre::Any(extractTextKeys(tk))); } e = e->extra; } @@ -394,35 +394,18 @@ void loadResource(Ogre::Resource *resource) anim->optimise(); } -bool createSkeleton(const std::string &name, const std::string &group, TextKeyMap *textkeys, const Nif::Node *node) +bool createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node) { - if(textkeys) - { - Nif::ExtraPtr e = node->extra; - while(!e.empty()) - { - if(e->recType == Nif::RC_NiTextKeyExtraData) - { - const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); - insertTextKeys(tk, textkeys); - } - e = e->extra; - } - } - if(node->boneTrafo != NULL) { Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); - Ogre::SkeletonPtr skel = skelMgr.getByName(name); if(skel.isNull()) { NIFSkeletonLoader *loader = &sLoaders[name]; skel = skelMgr.create(name, group, true, loader); } - - if(!textkeys || textkeys->size() > 0) - return true; + return true; } const Nif::NiNode *ninode = dynamic_cast(node); @@ -433,7 +416,7 @@ bool createSkeleton(const std::string &name, const std::string &group, TextKeyMa { if(!children[i].empty()) { - if(createSkeleton(name, group, textkeys, children[i].getPtr())) + if(createSkeleton(name, group, children[i].getPtr())) return true; } } @@ -1057,7 +1040,7 @@ public: NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; -MeshPairList NIFLoader::load(std::string name, std::string skelName, TextKeyMap *textkeys, const std::string &group) +MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::string &group) { MeshPairList meshes; @@ -1084,7 +1067,7 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, TextKeyMap } NIFSkeletonLoader skelldr; - bool hasSkel = skelldr.createSkeleton(skelName, group, textkeys, node); + bool hasSkel = skelldr.createSkeleton(skelName, group, node); NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string())); meshldr.createMeshes(node, meshes); @@ -1096,7 +1079,7 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textke { EntityList entitylist; - MeshPairList meshes = load(name, name, textkeys, group); + MeshPairList meshes = load(name, name, group); if(meshes.size() == 0) return entitylist; @@ -1109,6 +1092,24 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textke entitylist.mSkelBase = entity; } + if(entitylist.mSkelBase) + { + // Would be nice if Ogre::SkeletonInstance allowed access to the 'master' Ogre::SkeletonPtr. + Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); + Ogre::SkeletonPtr skel = skelMgr.getByName(entitylist.mSkelBase->getSkeleton()->getName()); + Ogre::Skeleton::BoneIterator iter = skel->getBoneIterator(); + while(iter.hasMoreElements()) + { + Ogre::Bone *bone = iter.getNext(); + const Ogre::Any &data = bone->getUserObjectBindings().getUserAny("TextKeyExtraData"); + if(!data.isEmpty()) + { + *textkeys = Ogre::any_cast(data); + break; + } + } + } + if(entitylist.mSkelBase) { parent->attachObject(entitylist.mSkelBase); @@ -1140,7 +1141,7 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo { EntityList entitylist; - MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), NULL, group); + MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), group); if(meshes.size() == 0) return entitylist; diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index 74b6f44df..3e05c5873 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -69,7 +69,7 @@ typedef std::vector< std::pair > MeshPairList; */ class NIFLoader { - static MeshPairList load(std::string name, std::string skelName, TextKeyMap *textkeys, const std::string &group); + static MeshPairList load(std::string name, std::string skelName, const std::string &group); public: static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename, From 917bbc4e110672e8c0ef7bc8ea8d6282213fdeed Mon Sep 17 00:00:00 2001 From: Jordan Milne Date: Sat, 5 Jan 2013 00:26:33 -0400 Subject: [PATCH 293/378] Create static and non-discardable textures with the right parameters (should fix Issue 443) --- libs/openengine/ogre/imagerotate.cpp | 21 ++++++++++++++++++--- libs/openengine/ogre/renderer.cpp | 2 +- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/libs/openengine/ogre/imagerotate.cpp b/libs/openengine/ogre/imagerotate.cpp index 11fd5eea6..3dd584078 100644 --- a/libs/openengine/ogre/imagerotate.cpp +++ b/libs/openengine/ogre/imagerotate.cpp @@ -16,6 +16,8 @@ void ImageRotate::rotate(const std::string& sourceImage, const std::string& dest { Root* root = Ogre::Root::getSingletonPtr(); + std::string destImageRot = std::string(destImage) + std::string("_rot"); + SceneManager* sceneMgr = root->createSceneManager(ST_GENERIC); Camera* camera = sceneMgr->createCamera("ImageRotateCamera"); @@ -48,8 +50,8 @@ void ImageRotate::rotate(const std::string& sourceImage, const std::string& dest unsigned int width = sourceTexture->getWidth(); unsigned int height = sourceTexture->getHeight(); - TexturePtr destTexture = TextureManager::getSingleton().createManual( - destImage, + TexturePtr destTextureRot = TextureManager::getSingleton().createManual( + destImageRot, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, width, height, @@ -57,7 +59,7 @@ void ImageRotate::rotate(const std::string& sourceImage, const std::string& dest PF_FLOAT16_RGBA, TU_RENDERTARGET); - RenderTarget* rtt = destTexture->getBuffer()->getRenderTarget(); + RenderTarget* rtt = destTextureRot->getBuffer()->getRenderTarget(); rtt->setAutoUpdated(false); Viewport* vp = rtt->addViewport(camera); vp->setOverlaysEnabled(false); @@ -66,7 +68,20 @@ void ImageRotate::rotate(const std::string& sourceImage, const std::string& dest rtt->update(); + //copy the rotated image to a static texture + TexturePtr destTexture = TextureManager::getSingleton().createManual( + destImage, + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + TEX_TYPE_2D, + width, height, + 0, + PF_FLOAT16_RGBA, + Ogre::TU_STATIC); + + destTexture->getBuffer()->blit(destTextureRot->getBuffer()); + // remove all the junk we've created + TextureManager::getSingleton().remove(destImageRot); MaterialManager::getSingleton().remove("ImageRotateMaterial"); root->destroySceneManager(sceneMgr); delete rect; diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 87ebe1139..3cdb00518 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -226,7 +226,7 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& 1, 1, 0, Ogre::PF_A8R8G8B8, - Ogre::TU_DYNAMIC_WRITE_ONLY); + Ogre::TU_WRITE_ONLY); } void OgreRenderer::createScene(const std::string& camName, float fov, float nearClip) From 3131e8dae6de127c4d3c8ed95c773b56442da92f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 4 Jan 2013 20:33:26 -0800 Subject: [PATCH 294/378] Don't get the text keys if they're not being requested --- components/nifogre/ogre_nif_loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 6ee02438a..27e4e7440 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -1092,7 +1092,7 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textke entitylist.mSkelBase = entity; } - if(entitylist.mSkelBase) + if(entitylist.mSkelBase && textkeys) { // Would be nice if Ogre::SkeletonInstance allowed access to the 'master' Ogre::SkeletonPtr. Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); From 8a086e3afbc510d70c66955c19a99d44b797bed7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 4 Jan 2013 20:58:35 -0800 Subject: [PATCH 295/378] Cache the mesh names from the mesh/skeleton pairs --- components/nifogre/ogre_nif_loader.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 27e4e7440..31d873489 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -1040,13 +1040,19 @@ public: NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; +typedef std::map MeshPairMap; +static MeshPairMap sMeshPairMap; + MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::string &group) { - MeshPairList meshes; - std::transform(name.begin(), name.end(), name.begin(), ::tolower); std::transform(skelName.begin(), skelName.end(), skelName.begin(), ::tolower); + MeshPairMap::const_iterator meshiter = sMeshPairMap.find(name+"@skel="+skelName); + if(meshiter != sMeshPairMap.end()) + return meshiter->second; + + MeshPairList &meshes = sMeshPairMap[name+"@skel="+skelName]; Nif::NIFFile nif(name); if (nif.numRecords() < 1) { @@ -1067,7 +1073,7 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, const std:: } NIFSkeletonLoader skelldr; - bool hasSkel = skelldr.createSkeleton(skelName, group, node); + bool hasSkel = skelldr.createSkeleton(name, group, node); NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string())); meshldr.createMeshes(node, meshes); From 3ed77ca1890b90c9c1d9fe7a4cddbfea1e06933b Mon Sep 17 00:00:00 2001 From: Jordan Milne Date: Sat, 5 Jan 2013 01:17:07 -0400 Subject: [PATCH 296/378] Include C++ header instead --- components/files/constrainedfiledatastream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/files/constrainedfiledatastream.cpp b/components/files/constrainedfiledatastream.cpp index 9e36f9075..63bd3490f 100644 --- a/components/files/constrainedfiledatastream.cpp +++ b/components/files/constrainedfiledatastream.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include namespace { From 63f09462fd1395f1550df11eb400e0e81959c14b Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sun, 6 Jan 2013 01:37:58 +0100 Subject: [PATCH 297/378] to_utf8, Utf8Encoder: pass encoding as constructor parameter Edit other files accordingly. --- apps/esmtool/esmtool.cpp | 25 ++++++++----------------- apps/launcher/model/datafilesmodel.cpp | 6 ++++-- apps/mwiniimporter/importer.cpp | 3 +-- apps/openmw/engine.cpp | 7 +++++-- apps/openmw/mwworld/worldimp.cpp | 4 ++-- apps/openmw/mwworld/worldimp.hpp | 2 +- components/esm/esmreader.cpp | 7 +++---- components/esm/esmreader.hpp | 7 +++---- components/esm/esmwriter.cpp | 20 +++----------------- components/esm/esmwriter.hpp | 8 +++----- components/to_utf8/to_utf8.cpp | 10 ++-------- components/to_utf8/to_utf8.hpp | 6 +----- components/translation/translation.cpp | 7 +++---- components/translation/translation.hpp | 5 ++--- 14 files changed, 41 insertions(+), 76 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index 0cd6e3905..fbfc884d7 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -165,23 +165,12 @@ bool parseOptions (int argc, char** argv, Arguments &info) // Font encoding settings info.encoding = variables["encoding"].as(); - if (info.encoding == "win1250") + if(info.encoding != "win1250" && info.encoding != "win1251" && info.encoding != "win1252") { - std::cout << "Using Central and Eastern European font encoding." << std::endl; - } - else if (info.encoding == "win1251") - { - std::cout << "Using Cyrillic font encoding." << std::endl; - } - else - { - if(info.encoding != "win1252") - { - std::cout << info.encoding << " is not a valid encoding option." << std::endl; - info.encoding = "win1252"; - } - std::cout << "Using default (English) font encoding." << std::endl; + std::cout << info.encoding << " is not a valid encoding option." << std::endl; + info.encoding = "win1252"; } + std::cout << ToUTF8::encodingUsingMessage(info.encoding) << std::endl; return true; } @@ -262,7 +251,8 @@ void printRaw(ESM::ESMReader &esm) int load(Arguments& info) { ESM::ESMReader& esm = info.reader; - esm.setEncoding(ToUTF8::calculateEncoding(info.encoding)); + ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding)); + esm.setEncoder(&encoder); std::string filename = info.filename; std::cout << "Loading file: " << filename << std::endl; @@ -432,7 +422,8 @@ int clone(Arguments& info) std::cout << std::endl << "Saving records to: " << info.outname << "..." << std::endl; ESM::ESMWriter& esm = info.writer; - esm.setEncoding(info.encoding); + ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding)); + esm.setEncoder(&encoder); esm.setAuthor(info.data.author); esm.setDescription(info.data.description); esm.setVersion(info.data.version); diff --git a/apps/launcher/model/datafilesmodel.cpp b/apps/launcher/model/datafilesmodel.cpp index 716c9e902..e84dbe0ac 100644 --- a/apps/launcher/model/datafilesmodel.cpp +++ b/apps/launcher/model/datafilesmodel.cpp @@ -272,7 +272,8 @@ void DataFilesModel::addMasters(const QString &path) foreach (const QString &path, dir.entryList()) { try { ESM::ESMReader fileReader; - fileReader.setEncoding(ToUTF8::calculateEncoding(mEncoding.toStdString())); + ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(mEncoding.toStdString())); + fileReader.setEncoder(&encoder); fileReader.open(dir.absoluteFilePath(path).toStdString()); ESM::ESMReader::MasterList mlist = fileReader.getMasters(); @@ -335,7 +336,8 @@ void DataFilesModel::addPlugins(const QString &path) try { ESM::ESMReader fileReader; - fileReader.setEncoding(ToUTF8::calculateEncoding(mEncoding.toStdString())); + ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(mEncoding.toStdString())); + fileReader.setEncoder(&encoder); fileReader.open(dir.absoluteFilePath(path).toStdString()); ESM::ESMReader::MasterList mlist = fileReader.getMasters(); diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 5c3dedd04..6a7274e0a 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -649,8 +649,7 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) { std::string section(""); MwIniImporter::multistrmap map; boost::iostreams::streamfile(filename.c_str()); - ToUTF8::Utf8Encoder encoder; - encoder.setEncoding(mEncoding); + ToUTF8::Utf8Encoder encoder(mEncoding); std::string line; while (std::getline(file, line)) { diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index e2d28a808..aadeb7f3a 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -331,11 +331,15 @@ void OMW::Engine::go() // cursor replacer (converts the cursor from the bsa so they can be used by mygui) MWGui::CursorReplace replacer; + // Create encoder + ToUTF8::Utf8Encoder encoder (mEncoding); + // Create the world mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster, - mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoding, mFallbackMap)); + mResDir, mCfgMgr.getCachePath(), mNewGame, &encoder, mFallbackMap)); //Load translation data + mTranslationDataStorage.setEncoder(&encoder); mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster); // Create window manager - this manages all the MW-specific GUI windows @@ -494,7 +498,6 @@ void OMW::Engine::showFPS(int level) void OMW::Engine::setEncoding(const ToUTF8::FromType& encoding) { mEncoding = encoding; - mTranslationDataStorage.setEncoding (encoding); } void OMW::Engine::setFallbackValues(std::map fallbackMap) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3995123b0..4f60f6ef4 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -170,7 +170,7 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, - const ToUTF8::FromType& encoding, std::map fallbackMap) + ToUTF8::Utf8Encoder* encoder, std::map fallbackMap) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mCells (mStore, mEsm), mNumFacing(0) @@ -187,7 +187,7 @@ namespace MWWorld std::cout << "Loading ESM " << masterPath.string() << "\n"; // This parses the ESM file and loads a sample cell - mEsm.setEncoding(encoding); + mEsm.setEncoder(encoder); mEsm.open (masterPath.string()); mStore.load (mEsm); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 71ae9597f..d29adebf4 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -95,7 +95,7 @@ namespace MWWorld World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, - const ToUTF8::FromType& encoding, std::map fallbackMap); + ToUTF8::Utf8Encoder* encoder, std::map fallbackMap); virtual ~World(); diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 5cd99a64a..99f7971b1 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -344,7 +344,7 @@ std::string ESMReader::getString(int size) getExact(ptr, size); // Convert to UTF8 and return - return mEncoder.getUtf8(ptr, size); + return mEncoder->getUtf8(ptr, size); } void ESMReader::fail(const std::string &msg) @@ -362,10 +362,9 @@ void ESMReader::fail(const std::string &msg) throw std::runtime_error(ss.str()); } -void ESMReader::setEncoding(const ToUTF8::FromType& encoding) +void ESMReader::setEncoder(ToUTF8::Utf8Encoder* encoder) { - mEncoding = encoding; - mEncoder.setEncoding(encoding); + mEncoder = encoder; } } diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 57503aea7..d52be25aa 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -235,8 +235,8 @@ public: /// Used for error handling void fail(const std::string &msg); - /// Sets font encoding for ESM strings - void setEncoding(const ToUTF8::FromType& encoding); + /// Sets font encoder for ESM strings + void setEncoder(ToUTF8::Utf8Encoder* encoder); private: Ogre::DataStreamPtr mEsm; @@ -251,8 +251,7 @@ private: SaveData mSaveData; MasterList mMasters; - ToUTF8::FromType mEncoding; - ToUTF8::Utf8Encoder mEncoder; + ToUTF8::Utf8Encoder* mEncoder; }; } #endif diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index a00c7971d..e2f878a25 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -158,7 +158,7 @@ void ESMWriter::writeHString(const std::string& data) else { // Convert to UTF8 and return - std::string ascii = m_encoder.getLegacyEnc(data); + std::string ascii = m_encoder->getLegacyEnc(data); write(ascii.c_str(), ascii.size()); } @@ -188,23 +188,9 @@ void ESMWriter::write(const char* data, int size) m_stream->write(data, size); } -void ESMWriter::setEncoding(const std::string& encoding) +void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) { - if (encoding == "win1250") - { - m_encoding = ToUTF8::WINDOWS_1250; - } - else if (encoding == "win1251") - { - m_encoding = ToUTF8::WINDOWS_1251; - } - else - { - // Default Latin encoding - m_encoding = ToUTF8::WINDOWS_1252; - } - - m_encoder.setEncoding(m_encoding); + m_encoder = encoder; } } diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index 20bc5da12..b557a29ad 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -6,7 +6,7 @@ #include #include "esmcommon.hpp" -#include "../to_utf8/to_utf8.hpp" +#include namespace ESM { @@ -24,7 +24,7 @@ public: void setVersion(int ver); int getType(); void setType(int type); - void setEncoding(const std::string& encoding); // Write strings as UTF-8? + void setEncoder(ToUTF8::Utf8Encoder *encoding); // Write strings as UTF-8? void setAuthor(const std::string& author); void setDescription(const std::string& desc); @@ -94,12 +94,10 @@ private: std::list m_records; std::ostream* m_stream; std::streampos m_headerPos; - ToUTF8::FromType m_encoding; - ToUTF8::Utf8Encoder m_encoder; + ToUTF8::Utf8Encoder* m_encoder; int m_recordCount; HEDRstruct m_header; - SaveData m_saveData; }; } diff --git a/components/to_utf8/to_utf8.cpp b/components/to_utf8/to_utf8.cpp index 5efec36a4..275f5483f 100644 --- a/components/to_utf8/to_utf8.cpp +++ b/components/to_utf8/to_utf8.cpp @@ -43,16 +43,10 @@ using namespace ToUTF8; -Utf8Encoder::Utf8Encoder(void): +Utf8Encoder::Utf8Encoder(const FromType sourceEncoding): mOutput(50*1024) { -} - -void Utf8Encoder::setEncoding(const FromType sourceEncoding) -{ - mEncoding = sourceEncoding; - - switch (mEncoding) + switch (sourceEncoding) { case ToUTF8::WINDOWS_1252: { diff --git a/components/to_utf8/to_utf8.hpp b/components/to_utf8/to_utf8.hpp index bfba8a1ac..e150cf17b 100644 --- a/components/to_utf8/to_utf8.hpp +++ b/components/to_utf8/to_utf8.hpp @@ -24,9 +24,7 @@ namespace ToUTF8 class Utf8Encoder { public: - Utf8Encoder(void); - - void setEncoding(const FromType sourceEncoding); + Utf8Encoder(FromType sourceEncoding); // Convert to UTF8 from the previously given code page. std::string getUtf8(const char *input, int size); @@ -48,9 +46,7 @@ namespace ToUTF8 size_t getLength2(const char* input, bool &ascii); void copyFromArray2(const char*& chp, char* &out); - FromType mEncoding; std::vector mOutput; - int mSize; char* translationArray; }; } diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index 002446e4f..184cf399c 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -50,7 +50,7 @@ namespace Translation if (!line.empty()) { - line = mEncoder.getUtf8(line); + line = mEncoder->getUtf8(line); size_t tab_pos = line.find('\t'); if (tab_pos != std::string::npos && tab_pos > 0 && tab_pos < line.size() - 1) @@ -101,10 +101,9 @@ namespace Translation return phrase; } - void Storage::setEncoding (const ToUTF8::FromType& encoding) + void Storage::setEncoder(ToUTF8::Utf8Encoder* encoder) { - mEncoding = encoding; - mEncoder.setEncoding(encoding); + mEncoder = encoder; } bool Storage::hasTranslation() const diff --git a/components/translation/translation.hpp b/components/translation/translation.hpp index 6c3e4df86..bca9ea255 100644 --- a/components/translation/translation.hpp +++ b/components/translation/translation.hpp @@ -19,7 +19,7 @@ namespace Translation // Standard form usually means nominative case std::string topicStandardForm(const std::string& phrase) const; - void setEncoding (const ToUTF8::FromType& encoding); + void setEncoder(ToUTF8::Utf8Encoder* encoder); bool hasTranslation() const; @@ -34,8 +34,7 @@ namespace Translation void loadDataFromStream(ContainerType& container, std::istream& stream); - ToUTF8::FromType mEncoding; - ToUTF8::Utf8Encoder mEncoder; + ToUTF8::Utf8Encoder* mEncoder; ContainerType mCellNamesTranslations, mTopicIDs, mPhraseForms; }; } From 0b7d11d38d99033a09bed6b22c75378f692653a5 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sun, 6 Jan 2013 11:39:18 +0100 Subject: [PATCH 298/378] to_utf8 test: fix Utf8Encoder constructor --- components/to_utf8/tests/to_utf8_test.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/to_utf8/tests/to_utf8_test.cpp b/components/to_utf8/tests/to_utf8_test.cpp index 4bba0cf90..3fcddd158 100644 --- a/components/to_utf8/tests/to_utf8_test.cpp +++ b/components/to_utf8/tests/to_utf8_test.cpp @@ -18,8 +18,7 @@ void testEncoder(ToUTF8::FromType encoding, const std::string &legacyEncFile, std::string utf8Line = getFirstLine(utf8File); // create an encoder for specified character encoding - ToUTF8::Utf8Encoder encoder; - encoder.setEncoding(encoding); + ToUTF8::Utf8Encoder encoder (encoding); // convert text to UTF-8 std::string convertedUtf8Line = encoder.getUtf8(legacyEncLine); From 1d3f3bcce30b211ba92b03fee3b7814042f0048c Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 6 Jan 2013 19:19:12 +0400 Subject: [PATCH 299/378] clang build fix --- components/files/constrainedfiledatastream.cpp | 4 ++++ components/translation/translation.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/components/files/constrainedfiledatastream.cpp b/components/files/constrainedfiledatastream.cpp index 63bd3490f..dd2985e56 100644 --- a/components/files/constrainedfiledatastream.cpp +++ b/components/files/constrainedfiledatastream.cpp @@ -3,7 +3,11 @@ #include #include +#ifndef __clang__ #include +#else +#include +#endif namespace { diff --git a/components/translation/translation.cpp b/components/translation/translation.cpp index fb5b03861..37bceafbf 100644 --- a/components/translation/translation.cpp +++ b/components/translation/translation.cpp @@ -30,7 +30,7 @@ namespace Translation { std::string path = dataFileCollections.getCollection (extension).getPath (fileName).string(); - std::ifstream stream (path); + std::ifstream stream (path.c_str()); if (!stream.is_open()) throw std::runtime_error ("failed to open translation file: " + fileName); From 0f3712f28435fdf2133d0c5c8eb22814349aed55 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sun, 6 Jan 2013 10:45:54 -0800 Subject: [PATCH 300/378] change flickering light's brightness pattern --- apps/openmw/mwrender/objects.cpp | 134 +++++++++++++++++++------------ apps/openmw/mwrender/objects.hpp | 13 ++- 2 files changed, 87 insertions(+), 60 deletions(-) diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 36f09e6d9..825ce2a14 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -235,8 +235,9 @@ void Objects::insertLight (const MWWorld::Ptr& ptr, float r, float g, float b, f else info.type = LT_Normal; - // random starting phase for the animation - info.time = Ogre::Math::RangeRandom(0, 2 * Ogre::Math::PI); + // randomize lights animations + info.time = Ogre::Math::RangeRandom(-500, +500); + info.phase = Ogre::Math::RangeRandom(-500, +500); // adjust the lights depending if we're in an interior or exterior cell // quadratic means the light intensity falls off quite fast, resulting in a @@ -373,6 +374,48 @@ void Objects::disableLights() } } +namespace pulse +{ + static float frequency (float x) + { + return x; + } + + static float amplitude (float phase) + { + return sin (phase); + } +} + +namespace flicker +{ + static const float fa = 0.785398f; + static const float fb = 1.17024f; + + static const float tdo = 0.94f; + static const float tdm = 2.48f; + + static const float f [3] = { 1.5708f, 4.18774f, 5.19934f }; + static const float o [3] = { 0.804248f, 2.11115f, 3.46832f }; + static const float m [3] = { 1.0f, 0.785f, 0.876f }; + static const float s = 0.394f; + + static const float phase_wavelength = 120.0f * 3.14159265359f / fa; + + static float frequency (float x) + { + return tdo + tdm * sin (fa * x); + } + + static float amplitude (float x) + { + float v = 0.0f; + for (int i = 0; i < 3; ++i) + v += sin (fb*x*f[i] + o[1])*m[i]; + return v * s; + } +} + void Objects::update(const float dt) { std::vector::iterator it = mLights.begin(); @@ -382,77 +425,64 @@ void Objects::update(const float dt) { Ogre::Light* light = mMwRoot->getCreator()->getLight(it->name); - // Light animation (pulse & flicker) - it->time += dt; - const float phase = std::fmod(static_cast (it->time), static_cast(32 * 2 * Ogre::Math::PI)) * 20; - float pulseConstant; + + float brightness; + float cycle_time; + float time_distortion; + + if ((it->type == LT_Pulse) && (it->type == LT_PulseSlow)) + { + cycle_time = 2 * Ogre::Math::PI; + time_distortion = 20.0f; + } + else + { + cycle_time = 500.0f; + it->phase = fmod (it->phase + dt, flicker::phase_wavelength); + time_distortion = flicker::frequency (it->phase); + } + + it->time += it->dir*dt*time_distortion; + if (it->dir > 0 && it->time > +cycle_time) + { + it->dir = -1.0f; + it->time = +2*cycle_time - it->time; + } + if (it->dir < 0 && it->time < -cycle_time) + { + it->dir = +1.0f; + it->time = -2*cycle_time - it->time; + } + + static const float fast = 4.0f/1.0f; + static const float slow = 1.0f/1.0f; // These formulas are just guesswork, but they work pretty well if (it->type == LT_Normal) { // Less than 1/255 light modifier for a constant light: - pulseConstant = (const float)(1.0 + sin(phase) / 255.0 ); + brightness = (const float)(1.0 + flicker::amplitude(it->time*slow) / 255.0 ); } else if (it->type == LT_Flicker) { - // Let's do a 50% -> 100% sine wave pulse over 1 second: - // This is 75% +/- 25% - pulseConstant = (const float)(0.75 + sin(phase) * 0.25); - - // Then add a 25% flicker variation: - it->resetTime -= dt; - if (it->resetTime < 0) - { - it->flickerVariation = (rand() % 1000) / 1000 * 0.25; - it->resetTime = 0.5; - } - if (it->resetTime > 0.25) - { - pulseConstant = (pulseConstant+it->flickerVariation) * (1-it->resetTime * 2.0f) + pulseConstant * it->resetTime * 2.0f; - } - else - { - pulseConstant = (pulseConstant+it->flickerVariation) * (it->resetTime * 2.0f) + pulseConstant * (1-it->resetTime * 2.0f); - } + brightness = (const float)(0.75 + flicker::amplitude(it->time*fast) * 0.25); } else if (it->type == LT_FlickerSlow) { - // Let's do a 50% -> 100% sine wave pulse over 1 second: - // This is 75% +/- 25% - pulseConstant = (const float)(0.75 + sin(phase / 4.0) * 0.25); - - // Then add a 25% flicker variation: - it->resetTime -= dt; - if (it->resetTime < 0) - { - it->flickerVariation = (rand() % 1000) / 1000 * 0.25; - it->resetTime = 0.5; - } - if (it->resetTime > 0.5) - { - pulseConstant = (pulseConstant+it->flickerVariation) * (1-it->resetTime) + pulseConstant * it->resetTime; - } - else - { - pulseConstant = (pulseConstant+it->flickerVariation) * (it->resetTime) + pulseConstant * (1-it->resetTime); - } + brightness = (const float)(0.75 + flicker::amplitude(it->time*slow) * 0.25); } else if (it->type == LT_Pulse) { - // Let's do a 75% -> 125% sine wave pulse over 1 second: - // This is 100% +/- 25% - pulseConstant = (const float)(1.0 + sin(phase) * 0.25); + brightness = (const float)(1.0 + pulse::amplitude (it->time*fast) * 0.25); } else if (it->type == LT_PulseSlow) { - // Let's do a 75% -> 125% sine wave pulse over 1 second: - // This is 100% +/- 25% - pulseConstant = (const float)(1.0 + sin(phase / 4.0) * 0.25); + brightness = (const float)(1.0 + pulse::amplitude (it->time*slow) * 0.25); } else assert(0 && "Invalid light type"); - light->setDiffuseColour( it->colour * pulseConstant ); + light->setDiffuseColour(it->colour * brightness); ++it; } diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 8594e4fe4..48632dba8 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -34,16 +34,13 @@ struct LightInfo LightType type; // Runtime variables - float flickerVariation; // 25% flicker variation, reset once every 0.5 seconds - float flickerSlowVariation; // 25% flicker variation, reset once every 1.0 seconds - float resetTime; - long double time; - + float dir; // direction time is running... + float time; // current time + float phase; // current phase LightInfo() : - flickerVariation(0), resetTime(0.5), - flickerSlowVariation(0), time(0), interior(true), - type(LT_Normal), radius(1.0) + dir(1.0f), time(0.0f), phase (0.0f), + interior(true), type(LT_Normal), radius(1.0) { } }; From 2557ef4d7d43e950b7f5db74ccde91fef35fa668 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 7 Jan 2013 12:17:46 +0100 Subject: [PATCH 301/378] post merge fixes and some misc clean up --- apps/openmw/mwrender/objects.cpp | 77 +++++++++++++++----------------- 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 825ce2a14..ee36126f8 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -17,7 +17,7 @@ using namespace MWRender; -// These are the Morrowind.ini defaults +/// \todo Replace these, once fallback values from the ini file are available. float Objects::lightLinearValue = 3; float Objects::lightLinearRadiusMult = 1; @@ -31,7 +31,6 @@ int Objects::uniqueID = 0; void Objects::clearSceneNode (Ogre::SceneNode *node) { - /// \todo This should probably be moved into OpenEngine at some point. for (int i=node->numAttachedObjects()-1; i>=0; --i) { Ogre::MovableObject *object = node->getAttachedObject (i); @@ -374,45 +373,43 @@ void Objects::disableLights() } } -namespace pulse +namespace MWRender { - static float frequency (float x) + namespace Pulse { - return x; + static float amplitude (float phase) + { + return sin (phase); + } } - static float amplitude (float phase) + namespace Flicker { - return sin (phase); - } -} + static const float fa = 0.785398f; + static const float fb = 1.17024f; -namespace flicker -{ - static const float fa = 0.785398f; - static const float fb = 1.17024f; - - static const float tdo = 0.94f; - static const float tdm = 2.48f; + static const float tdo = 0.94f; + static const float tdm = 2.48f; - static const float f [3] = { 1.5708f, 4.18774f, 5.19934f }; - static const float o [3] = { 0.804248f, 2.11115f, 3.46832f }; - static const float m [3] = { 1.0f, 0.785f, 0.876f }; - static const float s = 0.394f; + static const float f [3] = { 1.5708f, 4.18774f, 5.19934f }; + static const float o [3] = { 0.804248f, 2.11115f, 3.46832f }; + static const float m [3] = { 1.0f, 0.785f, 0.876f }; + static const float s = 0.394f; - static const float phase_wavelength = 120.0f * 3.14159265359f / fa; + static const float phase_wavelength = 120.0f * 3.14159265359f / fa; - static float frequency (float x) - { - return tdo + tdm * sin (fa * x); - } + static float frequency (float x) + { + return tdo + tdm * sin (fa * x); + } - static float amplitude (float x) - { - float v = 0.0f; - for (int i = 0; i < 3; ++i) - v += sin (fb*x*f[i] + o[1])*m[i]; - return v * s; + static float amplitude (float x) + { + float v = 0.0f; + for (int i = 0; i < 3; ++i) + v += sin (fb*x*f[i] + o[1])*m[i]; + return v * s; + } } } @@ -425,7 +422,6 @@ void Objects::update(const float dt) { Ogre::Light* light = mMwRoot->getCreator()->getLight(it->name); - float brightness; float cycle_time; float time_distortion; @@ -438,8 +434,8 @@ void Objects::update(const float dt) else { cycle_time = 500.0f; - it->phase = fmod (it->phase + dt, flicker::phase_wavelength); - time_distortion = flicker::frequency (it->phase); + it->phase = fmod (it->phase + dt, Flicker::phase_wavelength); + time_distortion = Flicker::frequency (it->phase); } it->time += it->dir*dt*time_distortion; @@ -461,23 +457,23 @@ void Objects::update(const float dt) if (it->type == LT_Normal) { // Less than 1/255 light modifier for a constant light: - brightness = (const float)(1.0 + flicker::amplitude(it->time*slow) / 255.0 ); + brightness = (const float)(1.0 + Flicker::amplitude(it->time*slow) / 255.0 ); } else if (it->type == LT_Flicker) { - brightness = (const float)(0.75 + flicker::amplitude(it->time*fast) * 0.25); + brightness = (const float)(0.75 + Flicker::amplitude(it->time*fast) * 0.25); } else if (it->type == LT_FlickerSlow) { - brightness = (const float)(0.75 + flicker::amplitude(it->time*slow) * 0.25); + brightness = (const float)(0.75 + Flicker::amplitude(it->time*slow) * 0.25); } else if (it->type == LT_Pulse) { - brightness = (const float)(1.0 + pulse::amplitude (it->time*fast) * 0.25); + brightness = (const float)(1.0 + Pulse::amplitude (it->time*fast) * 0.25); } else if (it->type == LT_PulseSlow) { - brightness = (const float)(1.0 + pulse::amplitude (it->time*slow) * 0.25); + brightness = (const float)(1.0 + Pulse::amplitude (it->time*slow) * 0.25); } else assert(0 && "Invalid light type"); @@ -506,8 +502,7 @@ void Objects::rebuildStaticGeometry() } } -void -Objects::updateObjectCell(const MWWorld::Ptr &ptr) +void Objects::updateObjectCell(const MWWorld::Ptr &ptr) { Ogre::SceneNode *node; MWWorld::CellStore *newCell = ptr.getCell(); From 35f4d09288aa703cae11a4c04e8a039f978fc378 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 7 Jan 2013 13:06:16 +0100 Subject: [PATCH 302/378] swscale handled better (cmake) --- CMakeLists.txt | 2 +- apps/openmw/CMakeLists.txt | 5 ----- cmake/FindFFmpeg.cmake | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 182cc268a..148f65bdd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,7 +146,7 @@ if (USE_FFMPEG) find_package(FFmpeg) if (FFMPEG_FOUND) set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIRS}) - set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES}) + set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARIES}) set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG) set(GOT_SOUND_INPUT 1) endif (FFMPEG_FOUND) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 57e81e5ee..4f290c46f 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -110,11 +110,6 @@ target_link_libraries(openmw components ) -if (USE_FFMPEG) - target_link_libraries(openmw - "swscale") -endif() - # Fix for not visible pthreads functions for linker with glibc 2.15 if (UNIX AND NOT APPLE) target_link_libraries(openmw ${CMAKE_THREAD_LIBS_INIT}) diff --git a/cmake/FindFFmpeg.cmake b/cmake/FindFFmpeg.cmake index 526be5f1b..c80203a25 100644 --- a/cmake/FindFFmpeg.cmake +++ b/cmake/FindFFmpeg.cmake @@ -32,7 +32,7 @@ include(FindPackageHandleStandardArgs) # The default components were taken from a survey over other FindFFMPEG.cmake files if (NOT FFmpeg_FIND_COMPONENTS) - set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL) + set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE) endif () # From 282601d6e9d2ccdbf82ab793218ac210bc270489 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 7 Jan 2013 13:19:52 +0100 Subject: [PATCH 303/378] support the allowSkipping extra parameter for playBink command. --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 6 +++--- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwrender/videoplayer.cpp | 11 ++++++++++- apps/openmw/mwrender/videoplayer.hpp | 5 ++++- apps/openmw/mwscript/miscextensions.cpp | 7 +++++-- apps/openmw/mwworld/worldimp.cpp | 4 ++-- apps/openmw/mwworld/worldimp.hpp | 2 +- 8 files changed, 27 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 198a20d45..7ec25f1ea 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -305,7 +305,7 @@ namespace MWBase /// \todo this does not belong here - virtual void playVideo(const std::string& name) = 0; + virtual void playVideo(const std::string& name, bool allowSkipping) = 0; virtual void stopVideo() = 0; }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 3abf88cca..ae7d6612b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -929,14 +929,14 @@ void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rend rendering.setup (mRendering.getScene()); } -void RenderingManager::playVideo(const std::string& name) +void RenderingManager::playVideo(const std::string& name, bool allowSkipping) { - mVideoPlayer->playVideo ("video/" + name); + mVideoPlayer->playVideo ("video/" + name, allowSkipping); } void RenderingManager::stopVideo() { - mVideoPlayer->close (); + mVideoPlayer->stopVideo (); } } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 02b4bcef6..68f2d79c3 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -196,7 +196,7 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList void setupExternalRendering (MWRender::ExternalRendering& rendering); - void playVideo(const std::string& name); + void playVideo(const std::string& name, bool allowSkipping); void stopVideo(); protected: diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index 8e05a4f82..618331c12 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -1010,6 +1010,7 @@ VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) , mVideoMaterial(NULL) , mRectangle(NULL) , mNode(NULL) + , mAllowSkipping(false) { mVideoMaterial = Ogre::MaterialManager::getSingleton().getByName("VideoMaterial", "General"); if (mVideoMaterial.isNull ()) @@ -1071,8 +1072,10 @@ VideoPlayer::~VideoPlayer() delete mBackgroundRectangle; } -void VideoPlayer::playVideo(const std::string &resourceName) +void VideoPlayer::playVideo(const std::string &resourceName, bool allowSkipping) { + mAllowSkipping = allowSkipping; + if(mState) close(); @@ -1113,6 +1116,12 @@ void VideoPlayer::update () } } +void VideoPlayer::stopVideo () +{ + if (mAllowSkipping) + close(); +} + void VideoPlayer::close() { if(mState) diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index d8c1902aa..5e9d18ba4 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -20,11 +20,12 @@ namespace MWRender VideoPlayer(Ogre::SceneManager* sceneMgr); ~VideoPlayer(); - void playVideo (const std::string& resourceName); + void playVideo (const std::string& resourceName, bool allowSkipping); void update(); void close(); + void stopVideo(); bool isPlaying(); @@ -34,6 +35,8 @@ namespace MWRender private: VideoState* mState; + bool mAllowSkipping; + Ogre::SceneManager* mSceneMgr; Ogre::MaterialPtr mVideoMaterial; Ogre::Rectangle2D* mRectangle; diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index ef9976a0b..80725b1f2 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -34,7 +34,10 @@ namespace MWScript std::string name = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWBase::Environment::get().getWorld ()->playVideo (name); + bool allowSkipping = runtime[0].mInteger; + runtime.pop(); + + MWBase::Environment::get().getWorld ()->playVideo (name, allowSkipping); } }; @@ -465,7 +468,7 @@ namespace MWScript extensions.registerInstruction ("tvm", "", opcodeToggleVanityMode); extensions.registerFunction ("getpcsleep", 'l', "", opcodeGetPcSleep); extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc); - extensions.registerInstruction ("playbink", "S", opcodePlayBink); + extensions.registerInstruction ("playbink", "Sl", opcodePlayBink); extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); extensions.registerFunction ("geteffect", 'l', "l", opcodeGetEffect, opcodeGetEffectExplicit); extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8eb121d37..6b06c60f5 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1287,9 +1287,9 @@ namespace MWWorld } - void World::playVideo (const std::string &name) + void World::playVideo (const std::string &name, bool allowSkipping) { - mRendering->playVideo(name); + mRendering->playVideo(name, allowSkipping); } void World::stopVideo () diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 1c1352913..ce0bbb1c4 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -334,7 +334,7 @@ namespace MWWorld /// 3 - enemies are nearby (not implemented) /// \todo this does not belong here - virtual void playVideo(const std::string& name); + virtual void playVideo(const std::string& name, bool allowSkipping); virtual void stopVideo(); }; } From c2901fe6ccb70b5511e631aab98946ac70d35f60 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Mon, 7 Jan 2013 18:16:50 +0000 Subject: [PATCH 304/378] added addsoulgem scripting function --- apps/openmw/mwscript/docs/vmformat.txt | 4 ++- apps/openmw/mwscript/miscextensions.cpp | 33 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 89cacf65c..8fd65bf8a 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -306,5 +306,7 @@ op 0x20001ef: GetCurrentAIPackage op 0x20001f0: GetCurrentAIPackage, explicit reference op 0x20001f1: GetDetected op 0x20001f2: GetDetected, explicit reference +op 0x20001f3: AddSoulGem +op 0x20001f4: AddSoulGem, explicit reference -opcodes 0x20001f3-0x3ffffff unused +opcodes 0x20001f5-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index b687471aa..ff273a333 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -14,6 +14,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/manualref.hpp" +#include "../mwworld/containerstore.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/creaturestats.hpp" @@ -305,6 +307,32 @@ namespace MWScript MWMechanics::EffectKey(key)).mMagnitude > 0); } }; + + template + class OpAddSoulGem : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + std::string creature = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + std::string gem = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), gem); + + ref.getPtr().getRefData().setCount (1); + + ref.getPtr().getCellRef().mSoul = creature; + + MWWorld::Class::get (ptr).getContainerStore (ptr).add (ref.getPtr()); + + } + }; template class OpGetAttacked : public Interpreter::Opcode0 @@ -414,6 +442,8 @@ namespace MWScript const int opcodeGetLockedExplicit = 0x20001c8; const int opcodeGetEffect = 0x20001cf; const int opcodeGetEffectExplicit = 0x20001d0; + const int opcodeAddSoulGem = 0x20001f3; + const int opcodeAddSoulGemExplicit = 0x20001f4; const int opcodeGetAttacked = 0x20001d3; const int opcodeGetAttackedExplicit = 0x20001d4; const int opcodeGetWeaponDrawn = 0x20001d7; @@ -452,6 +482,7 @@ namespace MWScript extensions.registerInstruction ("wakeuppc", "", opcodeWakeUpPc); extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); extensions.registerFunction ("geteffect", 'l', "l", opcodeGetEffect, opcodeGetEffectExplicit); + extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit); extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit); extensions.registerFunction ("getspelleffects", 'l', "c", opcodeGetSpellEffects, opcodeGetSpellEffectsExplicit); @@ -485,6 +516,8 @@ namespace MWScript interpreter.installSegment5 (opcodeGetLockedExplicit, new OpGetLocked); interpreter.installSegment5 (opcodeGetEffect, new OpGetEffect); interpreter.installSegment5 (opcodeGetEffectExplicit, new OpGetEffect); + interpreter.installSegment5 (opcodeAddSoulGem, new OpAddSoulGem); + interpreter.installSegment5 (opcodeAddSoulGemExplicit, new OpAddSoulGem); interpreter.installSegment5 (opcodeGetAttacked, new OpGetAttacked); interpreter.installSegment5 (opcodeGetAttackedExplicit, new OpGetAttacked); interpreter.installSegment5 (opcodeGetWeaponDrawn, new OpGetWeaponDrawn); From 4c5ed43cc9cd94e4f4438da43813fc8939d07b6d Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Mon, 7 Jan 2013 21:08:04 +0000 Subject: [PATCH 305/378] added removesoulgem, and fixed addsoulgem --- apps/openmw/mwscript/docs/vmformat.txt | 4 ++- apps/openmw/mwscript/miscextensions.cpp | 38 +++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 8fd65bf8a..f0a346c9d 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -308,5 +308,7 @@ op 0x20001f1: GetDetected op 0x20001f2: GetDetected, explicit reference op 0x20001f3: AddSoulGem op 0x20001f4: AddSoulGem, explicit reference +op 0x20001f5: RemoveSoulGem +op 0x20001f6: RemoveSoulGem, explicit reference -opcodes 0x20001f5-0x3ffffff unused +opcodes 0x20001f7-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index ff273a333..9ca3503cf 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -322,6 +322,9 @@ namespace MWScript std::string gem = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); + + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + store.get().find(creature); // This line throws an exception if it can't find the creature MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), gem); @@ -334,6 +337,36 @@ namespace MWScript } }; + template + class OpRemoveSoulGem : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + + MWWorld::Ptr ptr = R()(runtime); + + std::string soul = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); + + + for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) + { + if (::Misc::StringUtils::ciEqual(iter->getCellRef().mSoul, soul)) + { + if (iter->getRefData().getCount() <= 1) + iter->getRefData().setCount (0); + else + iter->getRefData().setCount (iter->getRefData().getCount() - 1); + break; + } + } + } + }; + template class OpGetAttacked : public Interpreter::Opcode0 { @@ -444,6 +477,8 @@ namespace MWScript const int opcodeGetEffectExplicit = 0x20001d0; const int opcodeAddSoulGem = 0x20001f3; const int opcodeAddSoulGemExplicit = 0x20001f4; + const int opcodeRemoveSoulGem = 0x20001f5; + const int opcodeRemoveSoulGemExplicit = 0x20001f6; const int opcodeGetAttacked = 0x20001d3; const int opcodeGetAttackedExplicit = 0x20001d4; const int opcodeGetWeaponDrawn = 0x20001d7; @@ -483,6 +518,7 @@ namespace MWScript extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); extensions.registerFunction ("geteffect", 'l', "l", opcodeGetEffect, opcodeGetEffectExplicit); extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit); + extensions.registerInstruction ("removesoulgem", "c", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit); extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit); extensions.registerFunction ("getspelleffects", 'l', "c", opcodeGetSpellEffects, opcodeGetSpellEffectsExplicit); @@ -518,6 +554,8 @@ namespace MWScript interpreter.installSegment5 (opcodeGetEffectExplicit, new OpGetEffect); interpreter.installSegment5 (opcodeAddSoulGem, new OpAddSoulGem); interpreter.installSegment5 (opcodeAddSoulGemExplicit, new OpAddSoulGem); + interpreter.installSegment5 (opcodeRemoveSoulGem, new OpRemoveSoulGem); + interpreter.installSegment5 (opcodeRemoveSoulGemExplicit, new OpRemoveSoulGem); interpreter.installSegment5 (opcodeGetAttacked, new OpGetAttacked); interpreter.installSegment5 (opcodeGetAttackedExplicit, new OpGetAttacked); interpreter.installSegment5 (opcodeGetWeaponDrawn, new OpGetWeaponDrawn); From 9ee823d8f847c30629af818df9907973e9da5358 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Mon, 7 Jan 2013 21:09:03 +0000 Subject: [PATCH 306/378] fixed typo in vmformat.txt --- apps/openmw/mwscript/docs/vmformat.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index f0a346c9d..66be100e5 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -301,7 +301,7 @@ op 0x20001ea: LowerRank op 0x20001eb: LowerRank, explicit op 0x20001ec: GetPCCrimeLevel op 0x20001ed: SetPCCrimeLevel -op 0x20001ee: SetPCCrimeLevel +op 0x20001ee: ModPCCrimeLevel op 0x20001ef: GetCurrentAIPackage op 0x20001f0: GetCurrentAIPackage, explicit reference op 0x20001f1: GetDetected From 86f691d3d59023d9c3fdef0550bd2a08282f0df6 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Mon, 7 Jan 2013 22:48:24 -0800 Subject: [PATCH 307/378] split MWWord::World::update into multiple functions --- apps/openmw/mwworld/worldimp.cpp | 240 +++++++++++++++++-------------- apps/openmw/mwworld/worldimp.hpp | 7 + 2 files changed, 143 insertions(+), 104 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4f60f6ef4..26614f3e1 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -884,8 +884,6 @@ namespace MWWorld void World::update (float duration, bool paused) { - /// \todo split this function up into subfunctions - mWorldScene->update (duration, paused); float pitch, yaw; @@ -895,6 +893,13 @@ namespace MWWorld mWeatherManager->update (duration); + performUpdateSceneQueries (); + + updateWindowManager (); + } + + void World::updateWindowManager () + { // inform the GUI about focused object MWWorld::Ptr object = searchPtrViaHandle(mFacedHandle); @@ -918,7 +923,10 @@ namespace MWWorld screenCoords[0], screenCoords[1], screenCoords[2], screenCoords[3]); } } + } + void World::performUpdateSceneQueries () + { if (!mRendering->occlusionQuerySupported()) { // cast a ray from player to sun to detect if the sun is visible @@ -937,119 +945,143 @@ namespace MWWorld MWRender::OcclusionQuery* query = mRendering->getOcclusionQuery(); if (!query->occlusionTestPending()) { - // get result of last query - if (mNumFacing == 0) mFacedHandle = ""; - else if (mNumFacing == 1) - { - bool result = query->getTestResult(); - mFacedHandle = result ? mFaced1Name : ""; - } - else if (mNumFacing == 2) - { - bool result = query->getTestResult(); - mFacedHandle = result ? mFaced2Name : mFaced1Name; - } + processFacedQueryResults (query); + beginFacedQueryProcess (query); + } + } + } - // send new query - // figure out which object we want to test against - std::vector < std::pair < float, std::string > > results; - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - float x, y; - MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - results = mPhysics->getFacedObjects(x, y); - } - else - results = mPhysics->getFacedObjects(); + void World::processFacedQueryResults (MWRender::OcclusionQuery* query) + { + // get result of last query + if (mNumFacing == 0) + { + mFacedHandle = ""; + } + else if (mNumFacing == 1) + { + bool result = query->getTestResult(); + mFacedHandle = result ? mFaced1Name : ""; + } + else if (mNumFacing == 2) + { + bool result = query->getTestResult(); + mFacedHandle = result ? mFaced2Name : mFaced1Name; + } + } - // ignore the player and other things we're not interested in - std::vector < std::pair < float, std::string > >::iterator it = results.begin(); - while (it != results.end()) - { - if ( (*it).second.find("HeightField") != std::string::npos // not interested in terrain - || getPtrViaHandle((*it).second) == mPlayer->getPlayer() ) // not interested in player (unless you want to talk to yourself) - { - it = results.erase(it); - } - else - ++it; - } + void World::beginFacedQueryProcess (MWRender::OcclusionQuery* query) + { + // send new query + // figure out which object we want to test against + std::vector < std::pair < float, std::string > > results; + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + float x, y; + MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); + results = mPhysics->getFacedObjects(x, y); + } + else + { + results = mPhysics->getFacedObjects(); + } - if (results.size() == 0) - { - mNumFacing = 0; - } - else if (results.size() == 1) - { - mFaced1 = getPtrViaHandle(results.front().second); - mFaced1Name = results.front().second; - mNumFacing = 1; + // ignore the player and other things we're not interested in + std::vector < std::pair < float, std::string > >::iterator it = results.begin(); + while (it != results.end()) + { + if ( (*it).second.find("HeightField") != std::string::npos // not interested in terrain + || getPtrViaHandle((*it).second) == mPlayer->getPlayer() ) // not interested in player (unless you want to talk to yourself) + { + it = results.erase(it); + } + else + ++it; + } - btVector3 p; - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - float x, y; - MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - p = mPhysics->getRayPoint(results.front().first, x, y); - } - else - p = mPhysics->getRayPoint(results.front().first); - Ogre::Vector3 pos(p.x(), p.z(), -p.y()); - Ogre::SceneNode* node = mFaced1.getRefData().getBaseNode(); + if (results.size() == 0) + { + mNumFacing = 0; + } + else if (results.size() == 1) + { + beginSingleFacedQueryProcess (query, results); + } + else + { + beginDoubleFacedQueryProcess (query, results); + } + } - //std::cout << "Num facing 1 : " << mFaced1Name << std::endl; - //std::cout << "Type 1 " << mFaced1.getTypeName() << std::endl; + void World::beginSingleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results) + { + mFaced1 = getPtrViaHandle(results.front().second); + mFaced1Name = results.front().second; + mNumFacing = 1; - query->occlusionTest(pos, node); - } - else - { - mFaced1Name = results.front().second; - mFaced2Name = results[1].second; - mFaced1 = getPtrViaHandle(results.front().second); - mFaced2 = getPtrViaHandle(results[1].second); - mNumFacing = 2; - - btVector3 p; - if (MWBase::Environment::get().getWindowManager()->isGuiMode()) - { - float x, y; - MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - p = mPhysics->getRayPoint(results[1].first, x, y); - } - else - p = mPhysics->getRayPoint(results[1].first); - Ogre::Vector3 pos(p.x(), p.z(), -p.y()); - Ogre::SceneNode* node1 = mFaced1.getRefData().getBaseNode(); - Ogre::SceneNode* node2 = mFaced2.getRefData().getBaseNode(); + btVector3 p; + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + float x, y; + MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); + p = mPhysics->getRayPoint(results.front().first, x, y); + } + else + p = mPhysics->getRayPoint(results.front().first); + Ogre::Vector3 pos(p.x(), p.z(), -p.y()); + Ogre::SceneNode* node = mFaced1.getRefData().getBaseNode(); - // no need to test if the first node is not occluder - if (!query->isPotentialOccluder(node1) && (mFaced1.getTypeName().find("Static") == std::string::npos)) - { - mFacedHandle = mFaced1Name; - //std::cout << "node1 Not an occluder" << std::endl; - return; - } + //std::cout << "Num facing 1 : " << mFaced1Name << std::endl; + //std::cout << "Type 1 " << mFaced1.getTypeName() << std::endl; - // no need to test if the second object is static (thus cannot be activated) - if (mFaced2.getTypeName().find("Static") != std::string::npos) - { - mFacedHandle = mFaced1Name; - return; - } + query->occlusionTest(pos, node); + } - // work around door problems - if (mFaced1.getTypeName().find("Static") != std::string::npos - && mFaced2.getTypeName().find("Door") != std::string::npos) - { - mFacedHandle = mFaced2Name; - return; - } + void World::beginDoubleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results) + { + mFaced1Name = results.at (0).second; + mFaced2Name = results.at (1).second; + mFaced1 = getPtrViaHandle(results.at (0).second); + mFaced2 = getPtrViaHandle(results.at (1).second); + mNumFacing = 2; - query->occlusionTest(pos, node2); - } - } + btVector3 p; + if (MWBase::Environment::get().getWindowManager()->isGuiMode()) + { + float x, y; + MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); + p = mPhysics->getRayPoint(results.at (1).first, x, y); + } + else + p = mPhysics->getRayPoint(results.at (1).first); + Ogre::Vector3 pos(p.x(), p.z(), -p.y()); + Ogre::SceneNode* node1 = mFaced1.getRefData().getBaseNode(); + Ogre::SceneNode* node2 = mFaced2.getRefData().getBaseNode(); + + // no need to test if the first node is not occluder + if (!query->isPotentialOccluder(node1) && (mFaced1.getTypeName().find("Static") == std::string::npos)) + { + mFacedHandle = mFaced1Name; + //std::cout << "node1 Not an occluder" << std::endl; + return; + } + + // no need to test if the second object is static (thus cannot be activated) + if (mFaced2.getTypeName().find("Static") != std::string::npos) + { + mFacedHandle = mFaced1Name; + return; } + + // work around door problems + if (mFaced1.getTypeName().find("Static") != std::string::npos + && mFaced2.getTypeName().find("Door") != std::string::npos) + { + mFacedHandle = mFaced2Name; + return; + } + + query->occlusionTest(pos, node2); } bool World::isCellExterior() const diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index d29adebf4..a60332e26 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -90,6 +90,13 @@ namespace MWWorld virtual void copyObjectToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos); + void updateWindowManager (); + void performUpdateSceneQueries (); + void processFacedQueryResults (MWRender::OcclusionQuery* query); + void beginFacedQueryProcess (MWRender::OcclusionQuery* query); + void beginSingleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results); + void beginDoubleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results); + public: World (OEngine::Render::OgreRenderer& renderer, From 0108be2e4f4e2e9257622667d3ecbf1666a29e1e Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Mon, 7 Jan 2013 23:00:00 -0800 Subject: [PATCH 308/378] updated MWWorld::PhysicsSystem::getFacedXXX functions * changed the names and return values to be consistent * made the distance to search a parameter * change the distance returned to world units instead of percentage of query distance --- apps/openmw/mwworld/physicssystem.cpp | 35 ++++++++++++++++++--------- apps/openmw/mwworld/physicssystem.hpp | 7 +++--- apps/openmw/mwworld/worldimp.cpp | 12 ++++----- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 5359c4eb2..9e2e94143 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -43,7 +43,7 @@ namespace MWWorld return mEngine; } - std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world) + std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world, float queryDistance) { btVector3 dir(0, 1, 0); dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch); @@ -56,11 +56,14 @@ namespace MWWorld mPlayerData.eyepos.z); origin += dir * 5; - btVector3 dest = origin + dir * 500; - return mEngine->rayTest(origin, dest); + btVector3 dest = origin + dir * queryDistance; + std::pair result; + /*auto*/ result = mEngine->rayTest(origin, dest); + result.second *= queryDistance; + return std::make_pair (result.second, result.first); } - std::vector < std::pair > PhysicsSystem::getFacedObjects () + std::vector < std::pair > PhysicsSystem::getFacedHandles (float queryDistance) { btVector3 dir(0, 1, 0); dir = dir.rotate(btVector3(1, 0, 0), mPlayerData.pitch); @@ -73,22 +76,32 @@ namespace MWWorld mPlayerData.eyepos.z); origin += dir * 5; - btVector3 dest = origin + dir * 500; - return mEngine->rayTest2(origin, dest); + btVector3 dest = origin + dir * queryDistance; + std::vector < std::pair > results; + /* auto */ results = mEngine->rayTest2(origin, dest); + std::vector < std::pair >::iterator i; + for (/* auto */ i = results.begin (); i != results.end (); ++i) + i->first *= queryDistance; + return results; } - std::vector < std::pair > PhysicsSystem::getFacedObjects (float mouseX, float mouseY) + std::vector < std::pair > PhysicsSystem::getFacedHandles (float mouseX, float mouseY, float queryDistance) { Ray ray = mRender.getCamera()->getCameraToViewportRay(mouseX, mouseY); Ogre::Vector3 from = ray.getOrigin(); - Ogre::Vector3 to = ray.getPoint(500); /// \todo make this distance (ray length) configurable + Ogre::Vector3 to = ray.getPoint(queryDistance); btVector3 _from, _to; // OGRE to MW coordinates _from = btVector3(from.x, -from.z, from.y); _to = btVector3(to.x, -to.z, to.y); - return mEngine->rayTest2(_from,_to); + std::vector < std::pair > results; + /* auto */ results = mEngine->rayTest2(_from,_to); + std::vector < std::pair >::iterator i; + for (/* auto */ i = results.begin (); i != results.end (); ++i) + i->first *= queryDistance; + return results; } void PhysicsSystem::setCurrentWater(bool hasWater, int waterHeight) @@ -110,7 +123,7 @@ namespace MWWorld Ray centerRay = mRender.getCamera()->getCameraToViewportRay( mRender.getViewport()->getWidth()/2, mRender.getViewport()->getHeight()/2); - btVector3 result(centerRay.getPoint(500*extent).x,-centerRay.getPoint(500*extent).z,centerRay.getPoint(500*extent).y); /// \todo make this distance (ray length) configurable + btVector3 result(centerRay.getPoint(extent).x,-centerRay.getPoint(extent).z,centerRay.getPoint(extent).y); return result; } @@ -118,7 +131,7 @@ namespace MWWorld { //get a ray pointing to the center of the viewport Ray centerRay = mRender.getCamera()->getCameraToViewportRay(mouseX, mouseY); - btVector3 result(centerRay.getPoint(500*extent).x,-centerRay.getPoint(500*extent).z,centerRay.getPoint(500*extent).y); /// \todo make this distance (ray length) configurable + btVector3 result(centerRay.getPoint(extent).x,-centerRay.getPoint(extent).z,centerRay.getPoint(extent).y); return result; } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 8bd73fd6c..81715c31e 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -41,14 +41,13 @@ namespace MWWorld bool toggleCollisionMode(); - std::pair getFacedHandle (MWWorld::World& world); + std::pair getFacedHandle (MWWorld::World& world, float queryDistance); + std::vector < std::pair > getFacedHandles (float queryDistance); + std::vector < std::pair > getFacedHandles (float mouseX, float mouseY, float queryDistance); btVector3 getRayPoint(float extent); btVector3 getRayPoint(float extent, float mouseX, float mouseY); - std::vector < std::pair > getFacedObjects (); - - std::vector < std::pair > getFacedObjects (float mouseX, float mouseY); // cast ray, return true if it hit something bool castRay(const Ogre::Vector3& from, const Ogre::Vector3& to); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 26614f3e1..09c48bd13 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -577,13 +577,13 @@ namespace MWWorld { if (!mRendering->occlusionQuerySupported()) { - std::pair result = mPhysics->getFacedHandle (*this); + std::pair result = mPhysics->getFacedHandle (*this, 500); - if (result.first.empty() || - result.second>getStore().get().find ("iMaxActivateDist")->getInt()) + if (result.second.empty() || + result.first>getStore().get().find ("iMaxActivateDist")->getInt()) return ""; - return result.first; + return result.second; } else { @@ -979,11 +979,11 @@ namespace MWWorld { float x, y; MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - results = mPhysics->getFacedObjects(x, y); + results = mPhysics->getFacedHandles(x, y, 500); } else { - results = mPhysics->getFacedObjects(); + results = mPhysics->getFacedHandles(500); } // ignore the player and other things we're not interested in From 05dad29005c9651829c7695cc3da8a6bf4b2ee68 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Mon, 7 Jan 2013 23:11:15 -0800 Subject: [PATCH 309/378] update MWWord::World to track distances while performing occlusion queries. This allows checking the activation distance against what is stored in GameSettings. Experimentation suggests that a second distance is required for NPCs. --- apps/openmw/mwworld/worldimp.cpp | 12 ++++++++++++ apps/openmw/mwworld/worldimp.hpp | 3 +++ 2 files changed, 15 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 09c48bd13..002ffcd93 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -587,6 +587,9 @@ namespace MWWorld } else { + if (mFacedDistance > result.first>getStore().get().find ("iMaxActivateDist")->getInt()) + return ""; + // updated every few frames in update() return mFacedHandle; } @@ -957,16 +960,19 @@ namespace MWWorld if (mNumFacing == 0) { mFacedHandle = ""; + mFacedDistance = FLT_MAX; } else if (mNumFacing == 1) { bool result = query->getTestResult(); mFacedHandle = result ? mFaced1Name : ""; + mFacedDistance = result ? mFaced1Distance : FLT_MAX; } else if (mNumFacing == 2) { bool result = query->getTestResult(); mFacedHandle = result ? mFaced2Name : mFaced1Name; + mFacedDistance = result ? mFaced1Distance : mFaced1Distance; } } @@ -1017,6 +1023,7 @@ namespace MWWorld { mFaced1 = getPtrViaHandle(results.front().second); mFaced1Name = results.front().second; + mFaced1Distance = results.front().first; mNumFacing = 1; btVector3 p; @@ -1041,6 +1048,8 @@ namespace MWWorld { mFaced1Name = results.at (0).second; mFaced2Name = results.at (1).second; + mFaced1Distance = results.at (0).first; + mFaced2Distance = results.at (1).first; mFaced1 = getPtrViaHandle(results.at (0).second); mFaced2 = getPtrViaHandle(results.at (1).second); mNumFacing = 2; @@ -1062,6 +1071,7 @@ namespace MWWorld if (!query->isPotentialOccluder(node1) && (mFaced1.getTypeName().find("Static") == std::string::npos)) { mFacedHandle = mFaced1Name; + mFacedDistance = mFaced1Distance; //std::cout << "node1 Not an occluder" << std::endl; return; } @@ -1070,6 +1080,7 @@ namespace MWWorld if (mFaced2.getTypeName().find("Static") != std::string::npos) { mFacedHandle = mFaced1Name; + mFacedDistance = mFaced1Distance; return; } @@ -1078,6 +1089,7 @@ namespace MWWorld && mFaced2.getTypeName().find("Door") != std::string::npos) { mFacedHandle = mFaced2Name; + mFacedDistance = mFaced2Distance; return; } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index a60332e26..de6689deb 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -72,10 +72,13 @@ namespace MWWorld Ptr getPtrViaHandle (const std::string& handle, Ptr::CellStore& cellStore); std::string mFacedHandle; + float mFacedDistance; Ptr mFaced1; Ptr mFaced2; std::string mFaced1Name; std::string mFaced2Name; + float mFaced1Distance; + float mFaced2Distance; int mNumFacing; std::map mFallback; From b9fbd6ae4b4be8319eb31596a6aa77e598cd90ae Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Mon, 7 Jan 2013 23:27:37 -0800 Subject: [PATCH 310/378] Factored faced object lookups into MWRender::World * Renamed MWWorld::World::getFacedHandle to getFacedObject. * Changed it to return an object pointer * Updated clients to use return object directly. --- apps/openmw/engine.cpp | 7 +------ apps/openmw/mwbase/world.hpp | 4 ++-- apps/openmw/mwgui/hud.cpp | 3 +-- apps/openmw/mwgui/tooltips.cpp | 3 +-- apps/openmw/mwworld/worldimp.cpp | 30 ++++++++++++++---------------- apps/openmw/mwworld/worldimp.hpp | 4 ++-- 6 files changed, 21 insertions(+), 30 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index aadeb7f3a..fcfaed47d 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -427,12 +427,7 @@ void OMW::Engine::activate() if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; - std::string handle = MWBase::Environment::get().getWorld()->getFacedHandle(); - - if (handle.empty()) - return; - - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaHandle (handle); + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getFacedObject(); if (ptr.isEmpty()) return; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index cc625b306..6f7b6e61a 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -199,8 +199,8 @@ namespace MWBase virtual void markCellAsUnchanged() = 0; - virtual std::string getFacedHandle() = 0; - ///< Return handle of the object the player is looking at + virtual MWWorld::Ptr getFacedObject() = 0; + ///< Return pointer to the object the player is looking at, if it is within activation range virtual void deleteObject (const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 689baf488..5ea1d1338 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -244,8 +244,7 @@ void HUD::onWorldClicked(MyGUI::Widget* _sender) if ( (mode != GM_Console) && (mode != GM_Container) && (mode != GM_Inventory) ) return; - std::string handle = MWBase::Environment::get().getWorld()->getFacedHandle(); - MWWorld::Ptr object = MWBase::Environment::get().getWorld()->searchPtrViaHandle(handle); + MWWorld::Ptr object = MWBase::Environment::get().getWorld()->getFacedObject(); if (mode == GM_Console) MWBase::Environment::get().getWindowManager()->getConsole()->setSelectedObject(object); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 97df211ac..c7acf568d 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -80,9 +80,8 @@ void ToolTips::onFrame(float frameDuration) || (mWindowManager->getMode() == GM_Container) || (mWindowManager->getMode() == GM_Inventory))) { - std::string handle = MWBase::Environment::get().getWorld()->getFacedHandle(); + mFocusObject = MWBase::Environment::get().getWorld()->getFacedObject(); - mFocusObject = MWBase::Environment::get().getWorld()->searchPtrViaHandle(handle); if (mFocusObject.isEmpty ()) return; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 002ffcd93..a893a6776 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -573,26 +573,24 @@ namespace MWWorld return mWorldScene->markCellAsUnchanged(); } - std::string World::getFacedHandle() + MWWorld::Ptr World::getFacedObject() { + std::pair result; + if (!mRendering->occlusionQuerySupported()) - { - std::pair result = mPhysics->getFacedHandle (*this, 500); + result = mPhysics->getFacedHandle (*this, 500); + else + result = std::make_pair (mFacedDistance, mFacedHandle); - if (result.second.empty() || - result.first>getStore().get().find ("iMaxActivateDist")->getInt()) - return ""; + if (result.second.empty()) + return MWWorld::Ptr (); - return result.second; - } - else - { - if (mFacedDistance > result.first>getStore().get().find ("iMaxActivateDist")->getInt()) - return ""; + float ActivationDistance = getStore().get().find ("iMaxActivateDist")->getInt(); - // updated every few frames in update() - return mFacedHandle; - } + if (result.first > ActivationDistance) + return MWWorld::Ptr (); + + return searchPtrViaHandle (result.second); } void World::deleteObject (const Ptr& ptr) @@ -904,7 +902,7 @@ namespace MWWorld void World::updateWindowManager () { // inform the GUI about focused object - MWWorld::Ptr object = searchPtrViaHandle(mFacedHandle); + MWWorld::Ptr object = getFacedObject (); MWBase::Environment::get().getWindowManager()->setFocusObject(object); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index de6689deb..62fb1d89a 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -227,8 +227,8 @@ namespace MWWorld virtual void markCellAsUnchanged(); - virtual std::string getFacedHandle(); - ///< Return handle of the object the player is looking at + virtual MWWorld::Ptr getFacedObject(); + ///< Return pointer to the object the player is looking at, if it is within activation range virtual void deleteObject (const Ptr& ptr); From b3932e3deae509f5d82647e308cc313ac17817e5 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Mon, 7 Jan 2013 23:40:17 -0800 Subject: [PATCH 311/378] added a separate activation distance for NPCs --- apps/openmw/mwworld/worldimp.cpp | 32 +++++++++++++++++++++++++++----- apps/openmw/mwworld/worldimp.hpp | 4 ++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a893a6776..739d0daab 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -573,24 +573,46 @@ namespace MWWorld return mWorldScene->markCellAsUnchanged(); } + float World::getMaxActivationDistance () + { + return (std::max) (getNpcActivationDistance (), getObjectActivationDistance ()); + } + + float World::getNpcActivationDistance () + { + return getStore().get().find ("iMaxActivateDist")->getInt()*5/4; + } + + float World::getObjectActivationDistance () + { + return getStore().get().find ("iMaxActivateDist")->getInt(); + } + MWWorld::Ptr World::getFacedObject() { std::pair result; if (!mRendering->occlusionQuerySupported()) - result = mPhysics->getFacedHandle (*this, 500); + result = mPhysics->getFacedHandle (*this, getMaxActivationDistance ()); else result = std::make_pair (mFacedDistance, mFacedHandle); if (result.second.empty()) return MWWorld::Ptr (); - float ActivationDistance = getStore().get().find ("iMaxActivateDist")->getInt(); + MWWorld::Ptr object = searchPtrViaHandle (result.second); + + float ActivationDistance; + + if (object.getTypeName ().find("NPC") != std::string::npos) + ActivationDistance = getNpcActivationDistance (); + else + ActivationDistance = getObjectActivationDistance (); if (result.first > ActivationDistance) return MWWorld::Ptr (); - return searchPtrViaHandle (result.second); + return object; } void World::deleteObject (const Ptr& ptr) @@ -983,11 +1005,11 @@ namespace MWWorld { float x, y; MWBase::Environment::get().getWindowManager()->getMousePosition(x, y); - results = mPhysics->getFacedHandles(x, y, 500); + results = mPhysics->getFacedHandles(x, y, getMaxActivationDistance ()); } else { - results = mPhysics->getFacedHandles(500); + results = mPhysics->getFacedHandles(getMaxActivationDistance ()); } // ignore the player and other things we're not interested in diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 62fb1d89a..a1ba6fcde 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -100,6 +100,10 @@ namespace MWWorld void beginSingleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results); void beginDoubleFacedQueryProcess (MWRender::OcclusionQuery* query, std::vector < std::pair < float, std::string > > const & results); + float getMaxActivationDistance (); + float getNpcActivationDistance (); + float getObjectActivationDistance (); + public: World (OEngine::Render::OgreRenderer& renderer, From e18cf452d4728adad7bdd2faad870399c7fe05b9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 8 Jan 2013 13:24:40 +0100 Subject: [PATCH 312/378] Updated shiny again. Some recent changes were accidently overwritten by eduard on 12/30/2012 --- .gitmodules | 1 - extern/shiny/CMakeLists.txt | 3 + extern/shiny/Main/Factory.cpp | 2 +- extern/shiny/Main/MaterialInstance.cpp | 8 +- extern/shiny/Main/ScriptLoader.cpp | 110 ++++++++++++------------- extern/shiny/Main/ScriptLoader.hpp | 42 +++++----- 6 files changed, 85 insertions(+), 81 deletions(-) delete mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 8b1378917..000000000 --- a/.gitmodules +++ /dev/null @@ -1 +0,0 @@ - diff --git a/extern/shiny/CMakeLists.txt b/extern/shiny/CMakeLists.txt index c27850ed6..603336413 100644 --- a/extern/shiny/CMakeLists.txt +++ b/extern/shiny/CMakeLists.txt @@ -70,3 +70,6 @@ endif() link_directories(${CMAKE_CURRENT_BINARY_DIR}) + +set(SHINY_LIBRARY ${SHINY_LIBRARY} PARENT_SCOPE) +set(SHINY_OGREPLATFORM_LIBRARY ${SHINY_OGREPLATFORM_LIBRARY} PARENT_SCOPE) diff --git a/extern/shiny/Main/Factory.cpp b/extern/shiny/Main/Factory.cpp index 678ee25c9..82d664811 100644 --- a/extern/shiny/Main/Factory.cpp +++ b/extern/shiny/Main/Factory.cpp @@ -224,7 +224,7 @@ namespace sh if (!mShadersEnabled) newInstance.setShadersEnabled (false); - newInstance.setSourceFile (it->second->m_fileName); + newInstance.setSourceFile (it->second->mFileName); std::vector props = it->second->getChildren(); for (std::vector::const_iterator propIt = props.begin(); propIt != props.end(); ++propIt) diff --git a/extern/shiny/Main/MaterialInstance.cpp b/extern/shiny/Main/MaterialInstance.cpp index 3abc781f6..0f8bcdda7 100644 --- a/extern/shiny/Main/MaterialInstance.cpp +++ b/extern/shiny/Main/MaterialInstance.cpp @@ -72,6 +72,8 @@ namespace sh allowFixedFunction = retrieveValue(getProperty("allow_fixed_function"), NULL).get(); } + bool useShaders = mShadersEnabled || !allowFixedFunction; + // get passes of the top-most parent PassVector passes = getPasses(); if (passes.size() == 0) @@ -91,7 +93,7 @@ namespace sh // create or retrieve shaders bool hasVertex = it->hasProperty("vertex_program"); bool hasFragment = it->hasProperty("fragment_program"); - if (mShadersEnabled || !allowFixedFunction) + if (useShaders) { it->setContext(context); it->mShaderProperties.setContext(context); @@ -144,7 +146,7 @@ namespace sh bool foundVertex = std::find(usedTextureSamplersVertex.begin(), usedTextureSamplersVertex.end(), texIt->getName()) != usedTextureSamplersVertex.end(); bool foundFragment = std::find(usedTextureSamplersFragment.begin(), usedTextureSamplersFragment.end(), texIt->getName()) != usedTextureSamplersFragment.end(); if ( (foundVertex || foundFragment) - || (((!mShadersEnabled || (!hasVertex || !hasFragment)) && allowFixedFunction) && texIt->hasProperty("create_in_ffp") && retrieveValue(texIt->getProperty("create_in_ffp"), this).get())) + || (((!useShaders || (!hasVertex || !hasFragment)) && allowFixedFunction) && texIt->hasProperty("create_in_ffp") && retrieveValue(texIt->getProperty("create_in_ffp"), this).get())) { boost::shared_ptr texUnit = pass->createTextureUnitState (); texIt->copyAll (texUnit.get(), context); @@ -152,7 +154,7 @@ namespace sh mTexUnits.push_back(texUnit); // set texture unit indices (required by GLSL) - if (mShadersEnabled && ((hasVertex && foundVertex) || (hasFragment && foundFragment)) && mFactory->getCurrentLanguage () == Language_GLSL) + if (useShaders && ((hasVertex && foundVertex) || (hasFragment && foundFragment)) && mFactory->getCurrentLanguage () == Language_GLSL) { pass->setTextureUnitIndex (foundVertex ? GPT_Vertex : GPT_Fragment, texIt->getName(), i); diff --git a/extern/shiny/Main/ScriptLoader.cpp b/extern/shiny/Main/ScriptLoader.cpp index a8971dc87..93d728b02 100644 --- a/extern/shiny/Main/ScriptLoader.cpp +++ b/extern/shiny/Main/ScriptLoader.cpp @@ -14,9 +14,9 @@ namespace sh for ( boost::filesystem::recursive_directory_iterator end, dir(path); dir != end; ++dir ) { boost::filesystem::path p(*dir); - if(p.extension() == c->m_fileEnding) + if(p.extension() == c->mFileEnding) { - c->m_currentFileName = (*dir).path().string(); + c->mCurrentFileName = (*dir).path().string(); std::ifstream in((*dir).path().string().c_str(), std::ios::binary); c->parseScript(in); } @@ -25,7 +25,7 @@ namespace sh ScriptLoader::ScriptLoader(const std::string& fileEnding) { - m_fileEnding = fileEnding; + mFileEnding = fileEnding; } ScriptLoader::~ScriptLoader() @@ -70,7 +70,7 @@ namespace sh { //Get first token _nextToken(stream); - if (tok == TOKEN_EOF) + if (mToken == TOKEN_EOF) { stream.close(); return; @@ -87,7 +87,7 @@ namespace sh //EOF token if (!stream.good()) { - tok = TOKEN_EOF; + mToken = TOKEN_EOF; return; } @@ -101,7 +101,7 @@ namespace sh if (!stream.good()) { - tok = TOKEN_EOF; + mToken = TOKEN_EOF; return; } @@ -115,21 +115,21 @@ namespace sh stream.unget(); - tok = TOKEN_NewLine; + mToken = TOKEN_NewLine; return; } //Open brace token else if (ch == '{') { - tok = TOKEN_OpenBrace; + mToken = TOKEN_OpenBrace; return; } //Close brace token else if (ch == '}') { - tok = TOKEN_CloseBrace; + mToken = TOKEN_CloseBrace; return; } @@ -139,8 +139,8 @@ namespace sh throw std::runtime_error("Parse Error: Invalid character, ConfigLoader::load()"); } - tokVal = ""; - tok = TOKEN_Text; + mTokenValue = ""; + mToken = TOKEN_Text; do { //Skip comments @@ -157,13 +157,13 @@ namespace sh ch = stream.get(); } while (ch != '\r' && ch != '\n' && !stream.eof()); - tok = TOKEN_NewLine; + mToken = TOKEN_NewLine; return; } } //Add valid char to tokVal - tokVal += (char)ch; + mTokenValue += (char)ch; //Next char ch = stream.get(); @@ -177,7 +177,7 @@ namespace sh void ScriptLoader::_skipNewLines(std::ifstream &stream) { - while (tok == TOKEN_NewLine) + while (mToken == TOKEN_NewLine) { _nextToken(stream); } @@ -189,7 +189,7 @@ namespace sh while (true) { - switch (tok) + switch (mToken) { //Node case TOKEN_Text: @@ -198,23 +198,23 @@ namespace sh ScriptNode *newNode; if (parent) { - newNode = parent->addChild(tokVal); + newNode = parent->addChild(mTokenValue); } else { - newNode = new ScriptNode(0, tokVal); + newNode = new ScriptNode(0, mTokenValue); } //Get values _nextToken(stream); std::string valueStr; int i=0; - while (tok == TOKEN_Text) + while (mToken == TOKEN_Text) { if (i == 0) - valueStr += tokVal; + valueStr += mTokenValue; else - valueStr += " " + tokVal; + valueStr += " " + mTokenValue; _nextToken(stream); ++i; } @@ -235,13 +235,13 @@ namespace sh _skipNewLines(stream); //Add any sub-nodes - if (tok == TOKEN_OpenBrace) + if (mToken == TOKEN_OpenBrace) { //Parse nodes _nextToken(stream); _parseNodes(stream, newNode); //Check for matching closing brace - if (tok != TOKEN_CloseBrace) + if (mToken != TOKEN_CloseBrace) { throw std::runtime_error("Parse Error: Expecting closing brace"); } @@ -249,7 +249,7 @@ namespace sh _skipNewLines(stream); } - newNode->m_fileName = m_currentFileName; + newNode->mFileName = mCurrentFileName; break; } @@ -276,16 +276,16 @@ namespace sh ScriptNode::ScriptNode(ScriptNode *parent, const std::string &name) { - m_name = name; - m_parent = parent; - _removeSelf = true; //For proper destruction - m_lastChildFound = -1; + mName = name; + mParent = parent; + mRemoveSelf = true; //For proper destruction + mLastChildFound = -1; //Add self to parent's child list (unless this is the root node being created) if (parent != NULL) { - m_parent->m_children.push_back(this); - _iter = --(m_parent->m_children.end()); + mParent->mChildren.push_back(this); + mIter = --(mParent->mChildren.end()); } } @@ -293,18 +293,18 @@ namespace sh { //Delete all children std::vector::iterator i; - for (i = m_children.begin(); i != m_children.end(); i++) + for (i = mChildren.begin(); i != mChildren.end(); i++) { ScriptNode *node = *i; - node->_removeSelf = false; + node->mRemoveSelf = false; delete node; } - m_children.clear(); + mChildren.clear(); //Remove self from parent's child list - if (_removeSelf && m_parent != NULL) + if (mRemoveSelf && mParent != NULL) { - m_parent->m_children.erase(_iter); + mParent->mChildren.erase(mIter); } } @@ -324,20 +324,20 @@ namespace sh ScriptNode *ScriptNode::findChild(const std::string &name, bool recursive) { int indx, prevC, nextC; - int childCount = (int)m_children.size(); + int childCount = (int)mChildren.size(); - if (m_lastChildFound != -1) + if (mLastChildFound != -1) { //If possible, try checking the nodes neighboring the last successful search //(often nodes searched for in sequence, so this will boost search speeds). - prevC = m_lastChildFound-1; if (prevC < 0) prevC = 0; else if (prevC >= childCount) prevC = childCount-1; - nextC = m_lastChildFound+1; if (nextC < 0) nextC = 0; else if (nextC >= childCount) nextC = childCount-1; + prevC = mLastChildFound-1; if (prevC < 0) prevC = 0; else if (prevC >= childCount) prevC = childCount-1; + nextC = mLastChildFound+1; if (nextC < 0) nextC = 0; else if (nextC >= childCount) nextC = childCount-1; for (indx = prevC; indx <= nextC; ++indx) { - ScriptNode *node = m_children[indx]; - if (node->m_name == name) + ScriptNode *node = mChildren[indx]; + if (node->mName == name) { - m_lastChildFound = indx; + mLastChildFound = indx; return node; } } @@ -346,17 +346,17 @@ namespace sh //already searched area above. for (indx = nextC + 1; indx < childCount; ++indx) { - ScriptNode *node = m_children[indx]; - if (node->m_name == name) { - m_lastChildFound = indx; + ScriptNode *node = mChildren[indx]; + if (node->mName == name) { + mLastChildFound = indx; return node; } } for (indx = 0; indx < prevC; ++indx) { - ScriptNode *node = m_children[indx]; - if (node->m_name == name) { - m_lastChildFound = indx; + ScriptNode *node = mChildren[indx]; + if (node->mName == name) { + mLastChildFound = indx; return node; } } @@ -365,9 +365,9 @@ namespace sh { //Search for the node from start to finish for (indx = 0; indx < childCount; ++indx){ - ScriptNode *node = m_children[indx]; - if (node->m_name == name) { - m_lastChildFound = indx; + ScriptNode *node = mChildren[indx]; + if (node->mName == name) { + mLastChildFound = indx; return node; } } @@ -378,7 +378,7 @@ namespace sh { for (indx = 0; indx < childCount; ++indx) { - m_children[indx]->findChild(name, recursive); + mChildren[indx]->findChild(name, recursive); } } @@ -389,13 +389,13 @@ namespace sh void ScriptNode::setParent(ScriptNode *newParent) { //Remove self from current parent - m_parent->m_children.erase(_iter); + mParent->mChildren.erase(mIter); //Set new parent - m_parent = newParent; + mParent = newParent; //Add self to new parent - m_parent->m_children.push_back(this); - _iter = --(m_parent->m_children.end()); + mParent->mChildren.push_back(this); + mIter = --(mParent->mChildren.end()); } } diff --git a/extern/shiny/Main/ScriptLoader.hpp b/extern/shiny/Main/ScriptLoader.hpp index caf743bd2..89720fb5d 100644 --- a/extern/shiny/Main/ScriptLoader.hpp +++ b/extern/shiny/Main/ScriptLoader.hpp @@ -23,7 +23,7 @@ namespace sh ScriptLoader(const std::string& fileEnding); virtual ~ScriptLoader(); - std::string m_fileEnding; + std::string mFileEnding; // For a line like // entity animals/dog @@ -38,11 +38,11 @@ namespace sh void parseScript(std::ifstream &stream); - std::string m_currentFileName; + std::string mCurrentFileName; protected: - float m_LoadOrder; + float mLoadOrder; // like "*.object" std::map m_scriptList; @@ -56,8 +56,8 @@ namespace sh TOKEN_EOF }; - Token tok, lastTok; - std::string tokVal; + Token mToken, mLastToken; + std::string mTokenValue; void _parseNodes(std::ifstream &stream, ScriptNode *parent); void _nextToken(std::ifstream &stream); @@ -74,22 +74,22 @@ namespace sh inline void setName(const std::string &name) { - this->m_name = name; + this->mName = name; } inline std::string &getName() { - return m_name; + return mName; } inline void setValue(const std::string &value) { - m_value = value; + mValue = value; } inline std::string &getValue() { - return m_value; + return mValue; } ScriptNode *addChild(const std::string &name = "untitled", bool replaceExisting = false); @@ -97,36 +97,36 @@ namespace sh inline std::vector &getChildren() { - return m_children; + return mChildren; } inline ScriptNode *getChild(unsigned int index = 0) { - assert(index < m_children.size()); - return m_children[index]; + assert(index < mChildren.size()); + return mChildren[index]; } void setParent(ScriptNode *newParent); inline ScriptNode *getParent() { - return m_parent; + return mParent; } - std::string m_fileName; + std::string mFileName; private: - std::string m_name; - std::string m_value; - std::vector m_children; - ScriptNode *m_parent; + std::string mName; + std::string mValue; + std::vector mChildren; + ScriptNode *mParent; - int m_lastChildFound; //The last child node's index found with a call to findChild() + int mLastChildFound; //The last child node's index found with a call to findChild() - std::vector::iterator _iter; - bool _removeSelf; + std::vector::iterator mIter; + bool mRemoveSelf; }; } From d4a9236ae5ef0596dcbf53eaba64948abaa7692e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 8 Jan 2013 14:59:40 +0100 Subject: [PATCH 313/378] hocking up dialogue sub view to a widget mapper --- apps/opencs/view/world/dialoguesubview.cpp | 52 ++++++++++++++++++++++ apps/opencs/view/world/dialoguesubview.hpp | 3 ++ 2 files changed, 55 insertions(+) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 354223757..6138fd3f5 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -4,6 +4,10 @@ #include #include #include +#include +#include +#include +#include #include "../../model/world/columnbase.hpp" @@ -23,6 +27,9 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM int columns = model->columnCount(); + mWidgetMapper = new QDataWidgetMapper (this); + mWidgetMapper->setModel (model); + for (int i=0; iheaderData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt(); @@ -30,8 +37,53 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM if (flags & CSMWorld::ColumnBase::Flag_Dialogue) { layout->addWidget (new QLabel (model->headerData (i, Qt::Horizontal).toString()), i, 0); + + CSMWorld::ColumnBase::Display display = static_cast + (model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + QWidget *widget = 0; + + if (model->flags (model->index (0, i)) & Qt::ItemIsEditable) + { + switch (display) + { + case CSMWorld::ColumnBase::Display_String: + + layout->addWidget (widget = new QLineEdit, i, 1); + break; + + case CSMWorld::ColumnBase::Display_Integer: + + /// \todo configure widget properly (range) + layout->addWidget (widget = new QSpinBox, i, 1); + break; + + case CSMWorld::ColumnBase::Display_Float: + + /// \todo configure widget properly (range, format?) + layout->addWidget (widget = new QDoubleSpinBox, i, 1); + break; + } + } + else + { + switch (display) + { + case CSMWorld::ColumnBase::Display_String: + case CSMWorld::ColumnBase::Display_Integer: + case CSMWorld::ColumnBase::Display_Float: + + layout->addWidget (widget = new QLabel, i, 1); + break; + } + } + + if (widget) + mWidgetMapper->addMapping (widget, i); } } + + mWidgetMapper->toFirst(); /// \todo use the correct row instead } void CSVWorld::DialogueSubView::setEditLock (bool locked) diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index c57dab108..64715f5b7 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -3,6 +3,8 @@ #include "../doc/subview.hpp" +class QDataWidgetMapper; + namespace CSMDoc { class Document; @@ -12,6 +14,7 @@ namespace CSVWorld { class DialogueSubView : public CSVDoc::SubView { + QDataWidgetMapper *mWidgetMapper; public: From 39d27b87c92bc96b335692e92066eeddfd63b3c9 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Tue, 8 Jan 2013 09:14:56 -0800 Subject: [PATCH 314/378] fixed build error with Audiere coded enabled --- apps/openmw/mwsound/audiere_decoder.cpp | 5 +++-- apps/openmw/mwsound/audiere_decoder.hpp | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwsound/audiere_decoder.cpp b/apps/openmw/mwsound/audiere_decoder.cpp index 788d5ae40..3f3e3a62d 100644 --- a/apps/openmw/mwsound/audiere_decoder.cpp +++ b/apps/openmw/mwsound/audiere_decoder.cpp @@ -64,7 +64,8 @@ void Audiere_Decoder::open(const std::string &fname) close(); mSoundFile = audiere::FilePtr(new OgreFile(mResourceMgr.openResource(fname))); - mSoundSource = audiere::OpenSampleSource(file); + mSoundSource = audiere::OpenSampleSource(mSoundFile); + mSoundFileName = fname; int channels, srate; audiere::SampleFormat format; @@ -95,7 +96,7 @@ void Audiere_Decoder::close() std::string Audiere_Decoder::getName() { - return mSoundFile->getName(); + return mSoundFileName; } void Audiere_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) diff --git a/apps/openmw/mwsound/audiere_decoder.hpp b/apps/openmw/mwsound/audiere_decoder.hpp index 91c07ccac..f432c32ec 100644 --- a/apps/openmw/mwsound/audiere_decoder.hpp +++ b/apps/openmw/mwsound/audiere_decoder.hpp @@ -12,6 +12,7 @@ namespace MWSound { class Audiere_Decoder : public Sound_Decoder { + std::string mSoundFileName; audiere::FilePtr mSoundFile; audiere::SampleSourcePtr mSoundSource; int mSampleRate; From 135f0870f77304ffdeeea9c4e9a1ecb7e5967267 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Wed, 9 Jan 2013 02:16:17 +0000 Subject: [PATCH 315/378] in actionequip, was ignoring actor param, and always using player --- apps/openmw/mwworld/actionequip.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index d8c019644..3d922a816 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -15,8 +15,7 @@ namespace MWWorld void ActionEquip::executeImp (const Ptr& actor) { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWWorld::InventoryStore& invStore = MWWorld::Class::get(player).getInventoryStore(player); + MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor); // slots that this item can be equipped in std::pair, bool> slots = MWWorld::Class::get(getTarget()).getEquipmentSlots(getTarget()); From 62a2ba1cc6cd9dbca65abf5fd174213c8cce6d32 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Wed, 9 Jan 2013 03:03:14 +0000 Subject: [PATCH 316/378] beast races cannot equip shoes/boots --- apps/openmw/mwworld/actionequip.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 3d922a816..f9e7658c8 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -2,6 +2,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "inventorystore.hpp" #include "player.hpp" @@ -31,11 +32,36 @@ namespace MWWorld } assert(it != invStore.end()); + + std::string npcRace = actor.get()->mBase->mRace; // equip the item in the first free slot for (std::vector::const_iterator slot=slots.first.begin(); slot!=slots.first.end(); ++slot) { + + // Beast races cannot equip shoes / boots + if(npcRace == "argonian" || npcRace == "khajiit") + { + if (*slot == MWWorld::InventoryStore::Slot_Boots) + { + // Only notify the player, not npcs + if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) + { + if(it.getType() == MWWorld::ContainerStore::Type_Clothing){ // It's shoes + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage15}", std::vector()); + } + + else // It's boots + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage14}", std::vector()); + } + } + } + + break; + } + // if all slots are occupied, replace the last slot if (slot == --slots.first.end()) { From 719663d86eeedd77cce0f576126dd139356e97e7 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Tue, 8 Jan 2013 19:52:18 -0800 Subject: [PATCH 317/378] added option to override the activation distance The command line option '--activate-dist ' can be used to override the in game activation distance. --- apps/openmw/engine.cpp | 9 ++++++++- apps/openmw/engine.hpp | 4 ++++ apps/openmw/main.cpp | 3 +++ apps/openmw/mwworld/worldimp.cpp | 13 +++++++++++-- apps/openmw/mwworld/worldimp.hpp | 3 ++- 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index fcfaed47d..8c288a2ee 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -336,7 +336,8 @@ void OMW::Engine::go() // Create the world mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster, - mResDir, mCfgMgr.getCachePath(), mNewGame, &encoder, mFallbackMap)); + mResDir, mCfgMgr.getCachePath(), mNewGame, &encoder, mFallbackMap, + mActivationDistanceOverride)); //Load translation data mTranslationDataStorage.setEncoder(&encoder); @@ -509,3 +510,9 @@ void OMW::Engine::setStartupScript (const std::string& path) { mStartupScript = path; } + + +void OMW::Engine::setActivationDistanceOverride (int distance) +{ + mActivationDistanceOverride = distance; +} diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 00889197e..601cc7765 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -76,6 +76,7 @@ namespace OMW std::map mFallbackMap; bool mScriptConsoleMode; std::string mStartupScript; + int mActivationDistanceOverride; Compiler::Extensions mExtensions; Compiler::Context *mScriptContext; @@ -167,6 +168,9 @@ namespace OMW /// Set path for a script that is run on startup in the console. void setStartupScript (const std::string& path); + /// Override the game setting specified activation distance. + void setActivationDistanceOverride (int distance); + private: Files::ConfigurationManager& mCfgMgr; }; diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 96dbf8987..d1a498610 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -149,6 +149,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("fallback", bpo::value()->default_value(FallbackMap(), "") ->multitoken()->composing(), "fallback values") + ("activate-dist", bpo::value ()->default_value (-1), "activation distance override"); + ; bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv) @@ -236,6 +238,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setFallbackValues(variables["fallback"].as().mMap); engine.setScriptConsoleMode (variables["script-console"].as()); engine.setStartupScript (variables["script-run"].as()); + engine.setActivationDistanceOverride (variables["activate-dist"].as()); return true; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 739d0daab..766fdafa8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -170,10 +170,10 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, - ToUTF8::Utf8Encoder* encoder, std::map fallbackMap) + ToUTF8::Utf8Encoder* encoder, std::map fallbackMap, int mActivationDistanceOverride) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mCells (mStore, mEsm), - mNumFacing(0) + mNumFacing(0), mActivationDistanceOverride (mActivationDistanceOverride) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); @@ -575,16 +575,25 @@ namespace MWWorld float World::getMaxActivationDistance () { + if (mActivationDistanceOverride >= 0) + return mActivationDistanceOverride; + return (std::max) (getNpcActivationDistance (), getObjectActivationDistance ()); } float World::getNpcActivationDistance () { + if (mActivationDistanceOverride >= 0) + return mActivationDistanceOverride; + return getStore().get().find ("iMaxActivateDist")->getInt()*5/4; } float World::getObjectActivationDistance () { + if (mActivationDistanceOverride >= 0) + return mActivationDistanceOverride; + return getStore().get().find ("iMaxActivateDist")->getInt(); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index a1ba6fcde..6f8bacf72 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -71,6 +71,7 @@ namespace MWWorld Ptr getPtrViaHandle (const std::string& handle, Ptr::CellStore& cellStore); + int mActivationDistanceOverride; std::string mFacedHandle; float mFacedDistance; Ptr mFaced1; @@ -109,7 +110,7 @@ namespace MWWorld World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame, - ToUTF8::Utf8Encoder* encoder, std::map fallbackMap); + ToUTF8::Utf8Encoder* encoder, std::map fallbackMap, int mActivationDistanceOverride); virtual ~World(); From 857bb42297f92d2fa3f911cfb9e7e2c0d1660558 Mon Sep 17 00:00:00 2001 From: Jordan Milne Date: Wed, 9 Jan 2013 01:44:15 -0400 Subject: [PATCH 318/378] Create a separate scenemanager for each CharacterPreview instance --- apps/openmw/mwgui/inventorywindow.cpp | 2 +- apps/openmw/mwgui/race.cpp | 2 +- apps/openmw/mwrender/characterpreview.cpp | 13 ++++++++++--- apps/openmw/mwrender/characterpreview.hpp | 4 ++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index ffb6c5282..5390f1602 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -67,7 +67,7 @@ namespace MWGui setCoord(0, 342, 498, 258); - MWBase::Environment::get().getWorld ()->setupExternalRendering (mPreview); + mPreview.setup(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); openContainer(player); diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index d2714fb51..df6c99340 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -106,7 +106,7 @@ void RaceDialog::open() updateSpellPowers(); mPreview = new MWRender::RaceSelectionPreview(); - MWBase::Environment::get().getWorld ()->setupExternalRendering (*mPreview); + mPreview->setup(); mPreview->update (0); const ESM::NPC proto = mPreview->getPrototype(); diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 0a11dc281..b034e098b 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -2,6 +2,7 @@ #include +#include #include #include @@ -35,13 +36,18 @@ namespace MWRender } - void CharacterPreview::setup (Ogre::SceneManager *sceneManager) + void CharacterPreview::setup () { - mSceneMgr = sceneManager; + mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); mCamera = mSceneMgr->createCamera (mName); mCamera->setAspectRatio (float(mSizeX) / float(mSizeY)); - mNode = static_cast(mSceneMgr->getRootSceneNode()->getChild("mwRoot"))->createChildSceneNode (); + Ogre::SceneNode* renderRoot = mSceneMgr->getRootSceneNode()->createChildSceneNode("renderRoot"); + + //we do this with mwRoot in renderingManager, do it here too. + renderRoot->pitch(Ogre::Degree(-90)); + + mNode = renderRoot->createChildSceneNode(); mAnimation = new NpcAnimation(mCharacter, mNode, MWWorld::Class::get(mCharacter).getInventoryStore (mCharacter), RV_PlayerPreview); @@ -79,6 +85,7 @@ namespace MWRender //Ogre::TextureManager::getSingleton().remove(mName); mSceneMgr->destroyCamera (mName); delete mAnimation; + Ogre::Root::getSingleton().destroySceneManager(mSceneMgr); } void CharacterPreview::rebuild() diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp index 18362d1db..d07a03be7 100644 --- a/apps/openmw/mwrender/characterpreview.hpp +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -23,14 +23,14 @@ namespace MWRender class NpcAnimation; - class CharacterPreview : public ExternalRendering + class CharacterPreview { public: CharacterPreview(MWWorld::Ptr character, int sizeX, int sizeY, const std::string& name, Ogre::Vector3 position, Ogre::Vector3 lookAt); virtual ~CharacterPreview(); - virtual void setup (Ogre::SceneManager *sceneManager); + virtual void setup (); virtual void onSetup(); virtual void rebuild(); From 43cd88a24eece09e6f83e586fd3248f4a211636a Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Fri, 4 Jan 2013 00:02:57 -0800 Subject: [PATCH 319/378] include members of BSAArchives in Ogres resource indices --- components/bsa/bsa_archive.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 8380b0838..671744a77 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -249,6 +249,13 @@ public: //std::cout << "find(" << pattern << ", " << recursive // << ", " << dirs << ")\n"; StringVectorPtr ptr = StringVectorPtr(new StringVector()); + + BSAFile::FileList const & files = arc.getList (); + + if (pattern == "*") + for (BSAFile::FileList::const_iterator i = files.begin (); i != files.end (); ++i) + ptr->push_back (i->name); + return ptr; } From 67491f6c49b37f02ce376053195c73c9929ec76b Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Fri, 4 Jan 2013 09:32:42 -0800 Subject: [PATCH 320/378] reworked DirArchive to improve performance Replaced old file index with a simple map. The map works by storing the file's relative path with path seperators normalized, and in lower case if not in strict mode. Incoming searches are normalized in the same way then the name is searched in the map. The value in the map is the original full path to the file which is then used to created a ConstrainedDataStream. In addition to changing the index, the commonly used Archive methods are implemented so that they don't fall back on the default FileSystemArchive implementations. --- components/bsa/bsa_archive.cpp | 200 +++++++++++++++++---------------- 1 file changed, 106 insertions(+), 94 deletions(-) diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 671744a77..b067c5290 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -29,6 +29,8 @@ #include #include "bsa_file.hpp" +#include "../files/constrainedfiledatastream.hpp" + namespace { @@ -60,104 +62,64 @@ public: static bool fsstrict = false; /// An OGRE Archive wrapping a BSAFile archive -class DirArchive: public Ogre::FileSystemArchive +class DirArchive: public Ogre::Archive { - boost::filesystem::path currentdir; - std::map, ciLessBoost> m; - unsigned int cutoff; - - bool findFile(const String& filename, std::string& copy) const - { - copy = filename; - std::replace(copy.begin(), copy.end(), '\\', '/'); - - if(copy.at(0) == '/') - copy.erase(0, 1); + typedef std::map index; - if(fsstrict == true) - return true; + index mIndex; - std::string folder; - //int delimiter = 0; - size_t lastSlash = copy.rfind('/'); - if (lastSlash != std::string::npos) - { - folder = copy.substr(0, lastSlash); - //delimiter = lastSlash+1; - } + static char strict_normalize_char(char ch) + { + return ch == '\\' ? '/' : ch; + } - std::vector current; - { - std::map,ciLessBoost>::const_iterator found = m.find(folder); - - if (found == m.end()) - { - return false; - } - else - current = found->second; - } + static char nonstrict_normalize_char(char ch) + { + return ch == '\\' ? '/' : std::tolower (ch); + } - std::vector::iterator find = std::lower_bound(current.begin(), current.end(), copy, ciLessBoost()); - if (find != current.end() && !ciLessBoost()(copy, current.front())) - { - if (!boost::iequals(copy, *find)) - if ((find = std::find_if(current.begin(), current.end(), pathComparer(copy))) == current.end()) //\todo Check if this line is actually needed - return false; + static std::string normalize_path (std::string::const_iterator begin, std::string::const_iterator end) + { + std::string normalized; + normalized.reserve (end-begin); + char (*normalize_char) (char) = fsstrict ? &strict_normalize_char : &nonstrict_normalize_char; + std::transform (begin, end, std::back_inserter (normalized), normalize_char); + return normalized; + } - copy = *find; - return true; - } + index::const_iterator lookup_filename (std::string const & filename) const + { + std::string normalized = normalize_path (filename.begin (), filename.end ()); - return false; + return mIndex.find (normalized); } - public: +public: DirArchive(const String& name) - : FileSystemArchive(name, "Dir"), currentdir (name) + : Archive(name, "Dir") { - mType = "Dir"; - std::string s = name; - cutoff = s.size() + 1; - if(fsstrict == false) - populateMap(currentdir); + typedef boost::filesystem::recursive_directory_iterator directory_iterator; - } - void populateMap(boost::filesystem::path d){ - //need to cut off first - boost::filesystem::directory_iterator dir_iter(d), dir_end; - std::vector filesind; - for(;dir_iter != dir_end; dir_iter++) - { - if(boost::filesystem::is_directory(*dir_iter)) - populateMap(*dir_iter); - else + directory_iterator end; + + size_t prefix = name.size (); + + if (name.size () > 0 && name [prefix - 1] != '\\' && name [prefix - 1] != '/') + ++prefix; + + for (directory_iterator i (name); i != end; ++i) { - std::string s = dir_iter->path().string(); - std::replace(s.begin(), s.end(), '\\', '/'); + if(boost::filesystem::is_directory (*i)) + continue; + + std::string proper = i->path ().string (); - std::string small; - if(cutoff < s.size()) - small = s.substr(cutoff, s.size() - cutoff); - else - small = s.substr(cutoff - 1, s.size() - cutoff); + std::string searchable = normalize_path (proper.begin () + prefix, proper.end ()); - filesind.push_back(small); + mIndex.insert (std::make_pair (std::move (searchable), std::move (proper))); } } - std::sort(filesind.begin(), filesind.end(), ciLessBoost()); - - std::string small; - std::string original = d.string(); - std::replace(original.begin(), original.end(), '\\', '/'); - if(cutoff < original.size()) - small = original.substr(cutoff, original.size() - cutoff); - else - small = original.substr(cutoff - 1, original.size() - cutoff); - - m[small] = filesind; - } bool isCaseSensitive() const { return fsstrict; } @@ -165,26 +127,76 @@ class DirArchive: public Ogre::FileSystemArchive void load() {} void unload() {} - bool exists(const String& filename) { - std::string copy; + DataStreamPtr open(const String& filename, bool readonly = true) const + { + index::const_iterator i = lookup_filename (filename); - if (findFile(filename, copy)) - return FileSystemArchive::exists(copy); + if (i == mIndex.end ()) + { + std::ostringstream os; + os << "The file '" << filename << "' could not be found."; + throw std::runtime_error (os.str ()); + } - return false; - } + return openConstrainedFileDataStream (i->second.c_str ()); + } - DataStreamPtr open(const String& filename, bool readonly = true) const - { - std::string copy; + StringVectorPtr list(bool recursive = true, bool dirs = false) + { + StringVectorPtr ptr = StringVectorPtr(new StringVector()); + std::cout << "DirArchive<" << getName () << ">::list" << std::endl; + return ptr; + } - if (findFile(filename, copy)) - return FileSystemArchive::open(copy, readonly); + FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false) + { + FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); + std::cout << "DirArchive<" << getName () << ">::listFileInfo" << std::endl; + return ptr; + } - DataStreamPtr p; - return p; - } + StringVectorPtr find(const String& pattern, bool recursive = true, + bool dirs = false) + { + StringVectorPtr ptr = StringVectorPtr(new StringVector()); + + if (pattern == "*") + for (index::const_iterator i = mIndex.begin (); i != mIndex.end (); ++i) + ptr->push_back (i->first); + + return ptr; + } + bool exists(const String& filename) + { + return lookup_filename (filename) != mIndex.end (); + } + + time_t getModifiedTime(const String&) { return 0; } + + FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true, + bool dirs = false) const + { + FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); + + index::const_iterator i = lookup_filename (pattern); + + if (i != mIndex.end ()) + { + FileInfo fi; + + std::size_t npos = i->first.rfind ('/'); + + fi.archive = this; + fi.filename = npos != -1 ? i->first.substr (npos) : i->first; + fi.path = npos != -1 ? i->first.substr (0, npos) : ""; + fi.compressedSize = fi.uncompressedSize = 0; + + ptr->push_back(fi); + } + + return ptr; + } }; class BSAArchive : public Archive From d1e51ebf426b47e6c1d4e35775ca66407ef474fd Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 9 Jan 2013 12:25:45 +0100 Subject: [PATCH 321/378] silenced some warnings --- components/bsa/bsa_archive.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index b067c5290..011b81351 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -188,8 +188,8 @@ public: std::size_t npos = i->first.rfind ('/'); fi.archive = this; - fi.filename = npos != -1 ? i->first.substr (npos) : i->first; - fi.path = npos != -1 ? i->first.substr (0, npos) : ""; + fi.filename = npos != std::string::npos ? i->first.substr (npos) : i->first; + fi.path = npos != std::string::npos ? i->first.substr (0, npos) : ""; fi.compressedSize = fi.uncompressedSize = 0; ptr->push_back(fi); From b035a5aa4a3cb4be7df7a5ac7befc25a10f1202b Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Wed, 9 Jan 2013 13:18:05 +0000 Subject: [PATCH 322/378] beast races cannot equip anything they oughtn't to be able to equip --- apps/openmw/mwworld/actionequip.cpp | 45 ++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index f9e7658c8..0b4ca95ed 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -43,23 +43,46 @@ namespace MWWorld // Beast races cannot equip shoes / boots if(npcRace == "argonian" || npcRace == "khajiit") { + if(*slot == MWWorld::InventoryStore::Slot_Helmet){ + std::vector parts; + + if(it.getType() == MWWorld::ContainerStore::Type_Clothing) + parts = it->get()->mBase->mParts.mParts; + else + parts = it->get()->mBase->mParts.mParts; + + bool allow = true; + for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) + { + if((*itr).mPart == ESM::PartReferenceType::PRT_Head) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage13}", std::vector()); + allow = false; + break; + } + } + + if(!allow) + break; + } + if (*slot == MWWorld::InventoryStore::Slot_Boots) { - // Only notify the player, not npcs - if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) + // Only notify the player, not npcs + if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) + { + if(it.getType() == MWWorld::ContainerStore::Type_Clothing){ // It's shoes + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage15}", std::vector()); + } + + else // It's boots { - if(it.getType() == MWWorld::ContainerStore::Type_Clothing){ // It's shoes - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage15}", std::vector()); - } - - else // It's boots - { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage14}", std::vector()); - } + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage14}", std::vector()); } + } + break; } - break; } // if all slots are occupied, replace the last slot From 375961fe5ea4c516878f08e11fbe492d3aadf6b6 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Wed, 9 Jan 2013 13:27:12 +0000 Subject: [PATCH 323/378] small fix --- apps/openmw/mwworld/actionequip.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 0b4ca95ed..60260a812 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -40,7 +40,7 @@ namespace MWWorld slot!=slots.first.end(); ++slot) { - // Beast races cannot equip shoes / boots + // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) if(npcRace == "argonian" || npcRace == "khajiit") { if(*slot == MWWorld::InventoryStore::Slot_Helmet){ @@ -56,7 +56,9 @@ namespace MWWorld { if((*itr).mPart == ESM::PartReferenceType::PRT_Head) { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage13}", std::vector()); + if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage13}", std::vector()); + allow = false; break; } From 3e41a1118a51bb38682a38b01ac56bf7473d7c27 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Wed, 9 Jan 2013 15:14:17 +0100 Subject: [PATCH 324/378] GetDisposition: return calculated disposition instead of base disposition --- apps/openmw/mwscript/statsextensions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 9e630aedf..fc138d351 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -674,7 +674,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - runtime.push (MWWorld::Class::get (ptr).getNpcStats (ptr).getBaseDisposition()); + runtime.push (MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(ptr)); } }; From 44031ec3d73aa1bf3f03c22bfe9b515be56e9d0d Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Wed, 9 Jan 2013 08:11:20 -0800 Subject: [PATCH 325/378] fleshed out BSAArchive and DirArchive. Implemented both lists & finds, with pattern matching. Conflicts: components/bsa/bsa_archive.cpp --- apps/openmw/engine.cpp | 3 - components/bsa/bsa_archive.cpp | 246 +++++++++++++++++++++------------ 2 files changed, 154 insertions(+), 95 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 8c288a2ee..b19cafab7 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -164,9 +164,6 @@ void OMW::Engine::loadBSA() dataDirectory = iter->string(); std::cout << "Data dir " << dataDirectory << std::endl; Bsa::addDir(dataDirectory, mFSStrict); - - // Workaround until resource listing capabilities are added to DirArchive, we need those to list available splash screens - addResourcesDirectory (dataDirectory); } } diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 011b81351..9913fb8aa 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -37,28 +37,142 @@ namespace using namespace Ogre; using namespace Bsa; -struct ciLessBoost : std::binary_function +struct PathPatternMatcher { - bool operator() (const std::string & s1, const std::string & s2) const { - //case insensitive version of is_less - return boost::ilexicographical_compare(s1, s2); + PathPatternMatcher (char const * pattern) : pattern (pattern) + { + } + + bool operator () (char const * input) + { + char const * p = pattern; + char const * i = input; + + while (*p && *i) + { + if (*p == '*') + { + ++p; + + while (*i && *i != *p && *i != '/' && *i != '\\') + ++i; + } + else + if (*p == '?') + { + if (*i == '/' || *i == '\\') + break; + + ++i, ++p; + } + if (*p == '/' || *p == '\\') + { + if (*i != '/' && *i != '\\') + break; + + ++i, ++p; + } + else + { + if (*i != *p) + break; + + ++i, ++p; + } + } + + return *p == 0 && *i == 0; } + +private: + char const * pattern; }; -struct pathComparer +struct FileNameGatherer { -private: - std::string find; + StringVectorPtr ptr; -public: - pathComparer(const std::string& toFind) : find(toFind) { } + FileNameGatherer (StringVectorPtr ptr) : ptr (ptr) + { + } - bool operator() (const std::string& other) + void operator () (std::string const & filename) const { - return boost::iequals(find, other); + ptr->push_back (filename); } }; +struct FileInfoGatherer +{ + Archive const * archive; + FileInfoListPtr ptr; + + FileInfoGatherer (Archive const * archive, FileInfoListPtr ptr) : + archive (archive), ptr (ptr) + { + } + + void operator () (std::string filename) const + { + FileInfo fi; + + std::size_t pt = filename.rfind ('/'); + if (pt == std::string::npos) + pt = 0; + + fi.archive = const_cast (archive); + fi.path = filename.substr (0, pt); + fi.filename = filename.substr (pt); + fi.compressedSize = fi.uncompressedSize = 0; + + ptr->push_back(fi); + } +}; + +template +void matchFiles (bool recursive, std::string const & pattern, file_iterator begin, file_iterator end, filename_extractor filenameExtractor, match_handler matchHandler) +{ + if (recursive && pattern == "*") + { + for (file_iterator i = begin; i != end; ++i) + matchHandler (filenameExtractor (*i)); + } + else + { + PathPatternMatcher matcher (pattern.c_str ()); + + if (recursive) + { + for (file_iterator i = begin; i != end; ++i) + { + char const * filename = filenameExtractor (*i); + char const * matchable_part = filename; + + for (char const * j = matchable_part; *j; ++j) + { + if (*j == '/' || *j == '\\') + matchable_part = j + 1; + } + + if (matcher (matchable_part)) + matchHandler (filename); + } + } + else + { + for (file_iterator i = begin; i != end; ++i) + { + char const * filename = filenameExtractor (*i); + + if (matcher (filename)) + matchHandler (filename); + } + } + } +} + + + static bool fsstrict = false; /// An OGRE Archive wrapping a BSAFile archive @@ -94,6 +208,11 @@ class DirArchive: public Ogre::Archive return mIndex.find (normalized); } + static char const * extractFilename (index::value_type const & entry) + { + return entry.first.c_str (); + } + public: DirArchive(const String& name) @@ -143,27 +262,20 @@ public: StringVectorPtr list(bool recursive = true, bool dirs = false) { - StringVectorPtr ptr = StringVectorPtr(new StringVector()); - std::cout << "DirArchive<" << getName () << ">::list" << std::endl; - return ptr; + return find ("*", recursive, dirs); } FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false) { - FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); - std::cout << "DirArchive<" << getName () << ">::listFileInfo" << std::endl; - return ptr; + return findFileInfo ("*", recursive, dirs); } StringVectorPtr find(const String& pattern, bool recursive = true, bool dirs = false) { + std::string normalizedPattern = normalize_path (pattern.begin (), pattern.end ()); StringVectorPtr ptr = StringVectorPtr(new StringVector()); - - if (pattern == "*") - for (index::const_iterator i = mIndex.begin (); i != mIndex.end (); ++i) - ptr->push_back (i->first); - + matchFiles (recursive, normalizedPattern, mIndex.begin (), mIndex.end (), extractFilename, FileNameGatherer (ptr)); return ptr; } @@ -178,21 +290,20 @@ public: bool dirs = false) const { FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); + FileInfoGatherer gatherer (this, ptr); + + std::string normalizedPattern = normalize_path (pattern.begin (), pattern.end ()); - index::const_iterator i = lookup_filename (pattern); + index::const_iterator i = mIndex.find (normalizedPattern); if (i != mIndex.end ()) { - FileInfo fi; - - std::size_t npos = i->first.rfind ('/'); - - fi.archive = this; - fi.filename = npos != std::string::npos ? i->first.substr (npos) : i->first; - fi.path = npos != std::string::npos ? i->first.substr (0, npos) : ""; - fi.compressedSize = fi.uncompressedSize = 0; + gatherer (i->first); + } + else + { - ptr->push_back(fi); + matchFiles (recursive, normalizedPattern, mIndex.begin (), mIndex.end (), extractFilename, gatherer); } return ptr; @@ -203,6 +314,11 @@ class BSAArchive : public Archive { BSAFile arc; + static char const * extractFilename (BSAFile::FileStruct const & entry) + { + return entry.name; + } + public: BSAArchive(const String& name) : Archive(name, "BSA") @@ -230,26 +346,18 @@ public: return arc.exists(filename.c_str()); } - bool cexists(const String& filename) const { - return arc.exists(filename.c_str()); - } - time_t getModifiedTime(const String&) { return 0; } // This is never called as far as I can see. StringVectorPtr list(bool recursive = true, bool dirs = false) { - //std::cout << "list(" << recursive << ", " << dirs << ")\n"; - StringVectorPtr ptr = StringVectorPtr(new StringVector()); - return ptr; + return find ("*", recursive, dirs); } // Also never called. FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false) { - //std::cout << "listFileInfo(" << recursive << ", " << dirs << ")\n"; - FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); - return ptr; + return findFileInfo ("*", recursive, dirs); } // After load() is called, find("*") is called once. It doesn't seem @@ -258,17 +366,9 @@ public: StringVectorPtr find(const String& pattern, bool recursive = true, bool dirs = false) { - //std::cout << "find(" << pattern << ", " << recursive - // << ", " << dirs << ")\n"; - StringVectorPtr ptr = StringVectorPtr(new StringVector()); - - BSAFile::FileList const & files = arc.getList (); - - if (pattern == "*") - for (BSAFile::FileList::const_iterator i = files.begin (); i != files.end (); ++i) - ptr->push_back (i->name); - - return ptr; + StringVectorPtr ptr = StringVectorPtr(new StringVector()); + matchFiles (recursive, pattern, arc.getList ().begin (), arc.getList ().end (), extractFilename, FileNameGatherer (ptr)); + return ptr; } /* Gets called once for each of the ogre formats, *.program, @@ -281,47 +381,9 @@ public: */ FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true, bool dirs = false) const - { - FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); - - // Check if the file exists (only works for single files - wild - // cards and recursive search isn't implemented.) - if(cexists(pattern)) - { - FileInfo fi; - fi.archive = this; - fi.filename = pattern; - // It apparently doesn't matter that we return bogus - // information - fi.path = ""; - fi.compressedSize = fi.uncompressedSize = 0; - - ptr->push_back(fi); - } - - return ptr; - } - - FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true, - bool dirs = false) { FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); - - // Check if the file exists (only works for single files - wild - // cards and recursive search isn't implemented.) - if(cexists(pattern)) - { - FileInfo fi; - fi.archive = this; - fi.filename = pattern; - // It apparently doesn't matter that we return bogus - // information - fi.path = ""; - fi.compressedSize = fi.uncompressedSize = 0; - - ptr->push_back(fi); - } - + matchFiles (recursive, pattern, arc.getList ().begin (), arc.getList ().end (), extractFilename, FileInfoGatherer (this, ptr)); return ptr; } }; From 6ca87e108fb17f0f73d181439d7330d3bf321f6d Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Wed, 9 Jan 2013 08:12:19 -0800 Subject: [PATCH 326/378] updated loading screen to use the resource managers pattern matching when finding files --- apps/openmw/mwgui/loadingscreen.cpp | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index a508bcd34..cb80e0c36 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -214,27 +214,14 @@ namespace MWGui void LoadingScreen::changeWallpaper () { if (mResources.isNull ()) - { - mResources = Ogre::StringVectorPtr (new Ogre::StringVector); - - Ogre::StringVectorPtr resources = Ogre::ResourceGroupManager::getSingleton ().listResourceNames ("General", false); - for (Ogre::StringVector::const_iterator it = resources->begin(); it != resources->end(); ++it) - { - if (it->size() < 6) - continue; - std::string start = it->substr(0, 6); - boost::to_lower(start); - - if (start == "splash") - mResources->push_back (*it); - } - } + mResources = Ogre::ResourceGroupManager::getSingleton ().findResourceNames ("General", "Splash_*.tga"); if (mResources->size()) { - std::string randomSplash = mResources->at (rand() % mResources->size()); + std::string const & randomSplash = mResources->at (rand() % mResources->size()); Ogre::TexturePtr tex = Ogre::TextureManager::getSingleton ().load (randomSplash, "General"); + mBackgroundImage->setImageTexture (randomSplash); } else From 5bfdb2449dbbb468340f7b817fa752ac4a6738e9 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Wed, 9 Jan 2013 18:53:14 +0000 Subject: [PATCH 327/378] allowed dropObjectOnGround to be used on npcs, not just player --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwgui/hud.cpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 +- apps/openmw/mwworld/worldimp.hpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 3401494ef..a576912c0 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -280,7 +280,7 @@ namespace MWBase /// @param cursor Y (relative 0-1) /// @return true if the object was placed, or false if it was rejected because the position is too far away - virtual void dropObjectOnGround (const MWWorld::Ptr& object) = 0; + virtual void dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object) = 0; virtual bool canPlaceObject (float cursorX, float cursorY) = 0; ///< @return true if it is possible to place on object at specified cursor location diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 5ea1d1338..a2c3a318b 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -220,7 +220,7 @@ void HUD::onWorldClicked(MyGUI::Widget* _sender) if (world->canPlaceObject(mouseX, mouseY)) world->placeObject(object, mouseX, mouseY); else - world->dropObjectOnGround(object); + world->dropObjectOnGround(world->getPlayer().getPlayer(), object); MyGUI::PointerManager::getInstance().setPointer("arrow"); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 7e0f1d7d3..6e2e5f3b7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1286,7 +1286,7 @@ namespace MWWorld } } - void World::dropObjectOnGround (const Ptr& object) + void World::dropObjectOnGround (const Ptr& actor, const Ptr& object) { MWWorld::Ptr::CellStore* cell = getPlayer().getPlayer().getCell(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index e6d377e60..7e89c1d87 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -311,7 +311,7 @@ namespace MWWorld /// @param cursor Y (relative 0-1) /// @return true if the object was placed, or false if it was rejected because the position is too far away - virtual void dropObjectOnGround (const Ptr& object); + virtual void dropObjectOnGround (const Ptr& actor, const Ptr& object); virtual bool canPlaceObject(float cursorX, float cursorY); ///< @return true if it is possible to place on object at specified cursor location From 2dc0064cc252da5249d36d2963b129115a3edc69 Mon Sep 17 00:00:00 2001 From: eduard Date: Wed, 9 Jan 2013 20:51:52 +0100 Subject: [PATCH 328/378] more string lowercase --- apps/mwiniimporter/importer.cpp | 4 +- apps/opencs/model/world/idcollection.hpp | 11 ++--- apps/openmw/mwclass/container.cpp | 4 +- apps/openmw/mwclass/door.cpp | 4 +- apps/openmw/mwclass/npc.cpp | 2 +- apps/openmw/mwdialogue/filter.cpp | 41 +++++++------------ apps/openmw/mwdialogue/selectwrapper.cpp | 14 ++----- apps/openmw/mwgui/dialogue.cpp | 5 +-- apps/openmw/mwgui/loadingscreen.cpp | 6 ++- apps/openmw/mwmechanics/activespells.cpp | 4 +- .../mwmechanics/mechanicsmanagerimp.cpp | 28 ++++--------- .../mwmechanics/mechanicsmanagerimp.hpp | 1 + apps/openmw/mwrender/npcanimation.cpp | 2 +- apps/openmw/mwscript/guiextensions.cpp | 4 +- apps/openmw/mwscript/statsextensions.cpp | 24 +++++------ apps/openmw/mwworld/cells.cpp | 5 +-- apps/openmw/mwworld/cellstore.cpp | 10 +---- apps/openmw/mwworld/containerstore.cpp | 2 +- apps/openmw/mwworld/weather.cpp | 2 +- components/compiler/exprparser.cpp | 7 ++-- components/compiler/lineparser.cpp | 13 +++--- components/compiler/parser.cpp | 7 ++-- components/compiler/scanner.cpp | 8 ++-- components/compiler/stringparser.cpp | 3 +- components/files/filelibrary.cpp | 10 ++--- components/files/fileops.cpp | 5 ++- components/nifogre/ogre_nif_loader.cpp | 9 ++-- components/nifoverrides/nifoverrides.cpp | 5 ++- 28 files changed, 101 insertions(+), 139 deletions(-) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 6a7274e0a..077b62be1 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -7,6 +7,8 @@ #include #include #include +#include + MwIniImporter::MwIniImporter() : mVerbose(false) @@ -793,7 +795,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) { for(std::vector::iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) { std::string filetype(entry->substr(entry->length()-4, 3)); - std::transform(filetype.begin(), filetype.end(), filetype.begin(), ::tolower); + Misc::StringUtils::toLower(filetype); if(filetype.compare("esm") == 0) { esmFiles.push_back(*entry); diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 1b2d1e349..963997924 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -12,6 +12,7 @@ #include #include "columnbase.hpp" +#include namespace CSMWorld { @@ -152,10 +153,7 @@ namespace CSMWorld template void IdCollection::add (const ESXRecordT& record) { - std::string id; - - std::transform (record.mId.begin(), record.mId.end(), std::back_inserter (id), - (int(*)(int)) std::tolower); + std::string id = Misc::StringUtils::lowerCase(record.mId); std::map::iterator iter = mIndex.find (id); @@ -281,10 +279,7 @@ namespace CSMWorld template int IdCollection::searchId (const std::string& id) const { - std::string id2; - - std::transform (id.begin(), id.end(), std::back_inserter (id2), - (int(*)(int)) std::tolower); + std::string id2 = Misc::StringUtils::lowerCase(id); std::map::const_iterator iter = mIndex.find (id2); diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 76c1c40f1..bbe005955 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -97,11 +97,11 @@ namespace MWClass // make key id lowercase std::string keyId = ptr.getCellRef().mKey; - std::transform(keyId.begin(), keyId.end(), keyId.begin(), ::tolower); + Misc::StringUtils::toLower(keyId); for (MWWorld::ContainerStoreIterator it = invStore.begin(); it != invStore.end(); ++it) { std::string refId = it->getCellRef().mRefID; - std::transform(refId.begin(), refId.end(), refId.begin(), ::tolower); + Misc::StringUtils::toLower(refId); if (refId == keyId) { hasKey = true; diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 09a15a2cb..fb6329939 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -84,11 +84,11 @@ namespace MWClass // make key id lowercase std::string keyId = ptr.getCellRef().mKey; - std::transform(keyId.begin(), keyId.end(), keyId.begin(), ::tolower); + Misc::StringUtils::toLower(keyId); for (MWWorld::ContainerStoreIterator it = invStore.begin(); it != invStore.end(); ++it) { std::string refId = it->getCellRef().mRefID; - std::transform(refId.begin(), refId.end(), refId.begin(), ::tolower); + Misc::StringUtils::toLower(refId); if (refId == keyId) { hasKey = true; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 83a002447..586f9638d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -65,7 +65,7 @@ namespace MWClass if (!ref->mBase->mFaction.empty()) { std::string faction = ref->mBase->mFaction; - boost::algorithm::to_lower(faction); + Misc::StringUtils::toLower(faction); if(ref->mBase->mNpdt52.mGold != -10) { data->mNpcStats.getFactionRanks()[faction] = (int)ref->mBase->mNpdt52.mRank; diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 0deef7fd0..7c590c8ef 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -17,24 +17,11 @@ #include "selectwrapper.hpp" -namespace -{ - std::string toLower (const std::string& name) - { - std::string lowerCase; - - std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); - - return lowerCase; - } -} - bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const { // actor id if (!info.mActor.empty()) - if (toLower (info.mActor)!=MWWorld::Class::get (mActor).getId (mActor)) + if ( Misc::StringUtils::lowerCase (info.mActor)!=MWWorld::Class::get (mActor).getId (mActor)) return false; bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name()); @@ -47,7 +34,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const MWWorld::LiveCellRef *cellRef = mActor.get(); - if (toLower (info.mRace)!=toLower (cellRef->mBase->mRace)) + if (Misc::StringUtils::lowerCase (info.mRace)!= Misc::StringUtils::lowerCase (cellRef->mBase->mRace)) return false; } @@ -59,7 +46,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const MWWorld::LiveCellRef *cellRef = mActor.get(); - if (toLower (info.mClass)!=toLower (cellRef->mBase->mClass)) + if ( Misc::StringUtils::lowerCase (info.mClass)!= Misc::StringUtils::lowerCase (cellRef->mBase->mClass)) return false; } @@ -70,7 +57,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const return false; MWMechanics::NpcStats& stats = MWWorld::Class::get (mActor).getNpcStats (mActor); - std::map::iterator iter = stats.getFactionRanks().find (toLower (info.mNpcFaction)); + std::map::iterator iter = stats.getFactionRanks().find ( Misc::StringUtils::lowerCase (info.mNpcFaction)); if (iter==stats.getFactionRanks().end()) return false; @@ -99,7 +86,7 @@ bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const if (!info.mPcFaction.empty()) { MWMechanics::NpcStats& stats = MWWorld::Class::get (player).getNpcStats (player); - std::map::iterator iter = stats.getFactionRanks().find (toLower (info.mPcFaction)); + std::map::iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase (info.mPcFaction)); if(iter==stats.getFactionRanks().end()) return false; @@ -111,7 +98,7 @@ bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const // check cell if (!info.mCell.empty()) - if (toLower (player.getCell()->mCell->mName) != toLower (info.mCell)) + if (Misc::StringUtils::lowerCase (player.getCell()->mCell->mName) != Misc::StringUtils::lowerCase (info.mCell)) return false; return true; @@ -242,7 +229,7 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con std::string name = select.getName(); for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) - if (toLower(iter->getCellRef().mRefID) == name) + if (Misc::StringUtils::lowerCase(iter->getCellRef().mRefID) == name) sum += iter->getRefData().getCount(); return sum; @@ -408,23 +395,23 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_Id: - return select.getName()==toLower (MWWorld::Class::get (mActor).getId (mActor)); + return select.getName()==Misc::StringUtils::lowerCase (MWWorld::Class::get (mActor).getId (mActor)); case SelectWrapper::Function_Faction: - return toLower (mActor.get()->mBase->mFaction)==select.getName(); + return Misc::StringUtils::lowerCase (mActor.get()->mBase->mFaction)==select.getName(); case SelectWrapper::Function_Class: - return toLower (mActor.get()->mBase->mClass)==select.getName(); + return Misc::StringUtils::lowerCase (mActor.get()->mBase->mClass)==select.getName(); case SelectWrapper::Function_Race: - return toLower (mActor.get()->mBase->mRace)==select.getName(); + return Misc::StringUtils::lowerCase (mActor.get()->mBase->mRace)==select.getName(); case SelectWrapper::Function_Cell: - return toLower (mActor.getCell()->mCell->mName)==select.getName(); + return Misc::StringUtils::lowerCase (mActor.getCell()->mCell->mName)==select.getName(); case SelectWrapper::Function_SameGender: @@ -433,8 +420,8 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co case SelectWrapper::Function_SameRace: - return toLower (mActor.get()->mBase->mRace)!= - toLower (player.get()->mBase->mRace); + return Misc::StringUtils::lowerCase (mActor.get()->mBase->mRace)!= + Misc::StringUtils::lowerCase (player.get()->mBase->mRace); case SelectWrapper::Function_SameFaction: diff --git a/apps/openmw/mwdialogue/selectwrapper.cpp b/apps/openmw/mwdialogue/selectwrapper.cpp index 1462ee8ba..9cc528a11 100644 --- a/apps/openmw/mwdialogue/selectwrapper.cpp +++ b/apps/openmw/mwdialogue/selectwrapper.cpp @@ -8,18 +8,10 @@ #include #include +#include + namespace { - std::string toLower (const std::string& name) - { - std::string lowerCase; - - std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); - - return lowerCase; - } - template bool selectCompareImp (char comp, T1 value1, T2 value2) { @@ -307,5 +299,5 @@ bool MWDialogue::SelectWrapper::selectCompare (bool value) const std::string MWDialogue::SelectWrapper::getName() const { - return toLower (mSelect.mSelectRule.substr (5)); + return Misc::StringUtils::lowerCase (mSelect.mSelectRule.substr (5)); } diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 5b04cc37e..f62bf2a05 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -37,10 +37,7 @@ namespace { std::string lower_string(const std::string& str) { - std::string lowerCase; - - std::transform (str.begin(), str.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); + std::string lowerCase = Misc::StringUtils::lowerCase (str); return lowerCase; } diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index a508bcd34..41283842d 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -6,6 +6,7 @@ #include #include + #include #include @@ -16,6 +17,9 @@ #include "../mwbase/windowmanager.hpp" +#include + + namespace MWGui { @@ -223,7 +227,7 @@ namespace MWGui if (it->size() < 6) continue; std::string start = it->substr(0, 6); - boost::to_lower(start); + Misc::StringUtils::toLower(start); if (start == "splash") mResources->push_back (*it); diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index f53ccdce3..ee1e9da36 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -263,11 +263,11 @@ namespace MWMechanics bool ActiveSpells::isSpellActive(std::string id) const { - boost::algorithm::to_lower(id); + Misc::StringUtils::toLower(id); for (TContainer::iterator iter = mSpells.begin(); iter != mSpells.end(); ++iter) { std::string left = iter->first; - boost::algorithm::to_lower(left); + Misc::StringUtils::toLower(left); if (iter->first == id) return true; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 079f8520b..dae417d44 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -377,16 +377,6 @@ namespace MWMechanics mUpdatePlayer = true; } - std::string toLower (const std::string& name) - { - std::string lowerCase; - - std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); - - return lowerCase; - } - int MechanicsManager::getDerivedDisposition(const MWWorld::Ptr& ptr) { MWMechanics::NpcStats npcSkill = MWWorld::Class::get(ptr).getNpcStats(ptr); @@ -398,7 +388,7 @@ namespace MWMechanics MWMechanics::CreatureStats playerStats = MWWorld::Class::get(playerPtr).getCreatureStats(playerPtr); MWMechanics::NpcStats playerNpcStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); - if (toLower(npc->mBase->mRace) == toLower(player->mBase->mRace)) x += MWBase::Environment::get().getWorld()->getStore().get().find("fDispRaceMod")->getFloat(); + if (Misc::StringUtils::lowerCase(npc->mBase->mRace) == Misc::StringUtils::lowerCase(player->mBase->mRace)) x += MWBase::Environment::get().getWorld()->getStore().get().find("fDispRaceMod")->getFloat(); x += MWBase::Environment::get().getWorld()->getStore().get().find("fDispPersonalityMult")->getFloat() * (playerStats.getAttribute(ESM::Attribute::Personality).getModified() - MWBase::Environment::get().getWorld()->getStore().get().find("fDispPersonalityBase")->getFloat()); @@ -408,21 +398,21 @@ namespace MWMechanics std::string npcFaction = ""; if(!npcSkill.getFactionRanks().empty()) npcFaction = npcSkill.getFactionRanks().begin()->first; - if (playerNpcStats.getFactionRanks().find(toLower(npcFaction)) != playerNpcStats.getFactionRanks().end()) + if (playerNpcStats.getFactionRanks().find(Misc::StringUtils::lowerCase(npcFaction)) != playerNpcStats.getFactionRanks().end()) { - for(std::vector::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get().find(toLower(npcFaction))->mReactions.begin(); - it != MWBase::Environment::get().getWorld()->getStore().get().find(toLower(npcFaction))->mReactions.end(); it++) + for(std::vector::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.begin(); + it != MWBase::Environment::get().getWorld()->getStore().get().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.end(); it++) { - if(toLower(it->mFaction) == toLower(npcFaction)) reaction = it->mReaction; + if(Misc::StringUtils::lowerCase(it->mFaction) == Misc::StringUtils::lowerCase(npcFaction)) reaction = it->mReaction; } - rank = playerNpcStats.getFactionRanks().find(toLower(npcFaction))->second; + rank = playerNpcStats.getFactionRanks().find(Misc::StringUtils::lowerCase(npcFaction))->second; } else if (npcFaction != "") { - for(std::vector::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get().find(toLower(npcFaction))->mReactions.begin(); - it != MWBase::Environment::get().getWorld()->getStore().get().find(toLower(npcFaction))->mReactions.end();it++) + for(std::vector::const_iterator it = MWBase::Environment::get().getWorld()->getStore().get().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.begin(); + it != MWBase::Environment::get().getWorld()->getStore().get().find(Misc::StringUtils::lowerCase(npcFaction))->mReactions.end();it++) { - if(playerNpcStats.getFactionRanks().find(toLower(it->mFaction)) != playerNpcStats.getFactionRanks().end() ) + if(playerNpcStats.getFactionRanks().find(Misc::StringUtils::lowerCase(it->mFaction)) != playerNpcStats.getFactionRanks().end() ) { if(it->mReactionmReaction; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index f8d470a4e..d3a97db36 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -90,6 +90,7 @@ namespace MWMechanics virtual void getPersuasionDispositionChange (const MWWorld::Ptr& npc, PersuasionType type, float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange); + void toLower(std::string npcFaction); ///< Perform a persuasion action on NPC }; } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index e6a8006e2..d33bdda91 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -80,7 +80,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor mHairModel = "meshes\\" + store.get().find(mNpc->mHair)->mModel; mBodyPrefix = "b_n_" + mNpc->mRace; - std::transform(mBodyPrefix.begin(), mBodyPrefix.end(), mBodyPrefix.begin(), ::tolower); + Misc::StringUtils::toLower(mBodyPrefix); mInsert = node; assert(mInsert); diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index 7d437f3c0..bfe69c79e 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -102,7 +102,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { std::string cell = (runtime.getStringLiteral (runtime[0].mInteger)); - boost::algorithm::to_lower(cell); + Misc::StringUtils::toLower(cell); runtime.pop(); // "Will match complete or partial cells, so ShowMap, "Vivec" will show cells Vivec and Vivec, Fred's House as well." @@ -115,7 +115,7 @@ namespace MWScript for (; it != cells.extEnd(); ++it) { std::string name = it->mName; - boost::algorithm::to_lower(name); + Misc::StringUtils::toLower(name); if (name.find(cell) != std::string::npos) MWBase::Environment::get().getWindowManager()->addVisitedLocation ( it->mName, diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 9e630aedf..e2e260c32 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -512,7 +512,7 @@ namespace MWScript factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } - boost::algorithm::to_lower(factionID); + Misc::StringUtils::toLower(factionID); if(factionID != "") { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); @@ -541,7 +541,7 @@ namespace MWScript factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } - boost::algorithm::to_lower(factionID); + Misc::StringUtils::toLower(factionID); if(factionID != "") { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); @@ -574,7 +574,7 @@ namespace MWScript factionID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); } - boost::algorithm::to_lower(factionID); + Misc::StringUtils::toLower(factionID); if(factionID != "") { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); @@ -612,7 +612,7 @@ namespace MWScript factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; } } - boost::algorithm::to_lower(factionID); + Misc::StringUtils::toLower(factionID); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); if(factionID!="") { @@ -714,7 +714,7 @@ namespace MWScript if (factionId.empty()) throw std::runtime_error ("failed to determine faction"); - boost::algorithm::to_lower (factionId); + Misc::StringUtils::toLower (factionId); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); runtime.push ( @@ -750,7 +750,7 @@ namespace MWScript if (factionId.empty()) throw std::runtime_error ("failed to determine faction"); - boost::algorithm::to_lower (factionId); + Misc::StringUtils::toLower (factionId); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::Class::get (player).getNpcStats (player).setFactionReputation (factionId, value); @@ -785,7 +785,7 @@ namespace MWScript if (factionId.empty()) throw std::runtime_error ("failed to determine faction"); - boost::algorithm::to_lower (factionId); + Misc::StringUtils::toLower (factionId); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::Class::get (player).getNpcStats (player).setFactionReputation (factionId, @@ -830,11 +830,11 @@ namespace MWScript MWWorld::Ptr ptr = R()(runtime); std::string race = runtime.getStringLiteral(runtime[0].mInteger); - boost::algorithm::to_lower(race); + Misc::StringUtils::toLower(race); runtime.pop(); std::string npcRace = ptr.get()->mBase->mRace; - boost::algorithm::to_lower(npcRace); + Misc::StringUtils::toLower(npcRace); runtime.push (npcRace == race); } @@ -878,7 +878,7 @@ namespace MWScript factionID = MWWorld::Class::get(ptr).getNpcStats(ptr).getFactionRanks().begin()->first; } } - boost::algorithm::to_lower(factionID); + Misc::StringUtils::toLower(factionID); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); if(factionID!="") { @@ -929,7 +929,7 @@ namespace MWScript if(factionID!="") { std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); - boost::algorithm::to_lower(factionID); + Misc::StringUtils::toLower(factionID); expelled.insert(factionID); } } @@ -965,7 +965,7 @@ namespace MWScript if(factionID!="") { std::set& expelled = MWWorld::Class::get(player).getNpcStats(player).getExpelled (); - boost::algorithm::to_lower(factionID); + Misc::StringUtils::toLower(factionID); expelled.erase (factionID); } } diff --git a/apps/openmw/mwworld/cells.cpp b/apps/openmw/mwworld/cells.cpp index 696a469f7..622c8a10a 100644 --- a/apps/openmw/mwworld/cells.cpp +++ b/apps/openmw/mwworld/cells.cpp @@ -153,10 +153,7 @@ MWWorld::Ptr MWWorld::Cells::getPtr (const std::string& name, Ptr::CellStore& ce if (cell.mState==Ptr::CellStore::State_Preloaded) { - std::string lowerCase; - - std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); + std::string lowerCase = Misc::StringUtils::lowerCase(name); if (std::binary_search (cell.mIds.begin(), cell.mIds.end(), lowerCase)) { diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 1ef2d36a2..fb5e45556 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -56,10 +56,7 @@ namespace MWWorld // Get each reference in turn while (mCell->getNextRef (esm, ref)) { - std::string lowerCase; - - std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); + std::string lowerCase = Misc::StringUtils::lowerCase (ref.mRefID); mIds.push_back (lowerCase); } @@ -82,10 +79,7 @@ namespace MWWorld // Get each reference in turn while(mCell->getNextRef(esm, ref)) { - std::string lowerCase; - - std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); + std::string lowerCase = Misc::StringUtils::lowerCase(ref.mRefID); int rec = store.find(ref.mRefID); diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index e47f2191a..db4537daf 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -37,7 +37,7 @@ namespace bool compare_string_ci(std::string str1, std::string str2) { - boost::algorithm::to_lower(str1); + Misc::StringUtils::toLower(str1); return str1 == str2; } } diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index 19bc881f7..917a8d7d4 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -497,7 +497,7 @@ void WeatherManager::update(float duration) if (exterior) { std::string regionstr = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()->mCell->mRegion; - boost::algorithm::to_lower(regionstr); + Misc::StringUtils::toLower(regionstr); if (mWeatherUpdateTime <= 0 || regionstr != mCurrentRegion) { diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 52192625b..027f3de71 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -14,6 +14,7 @@ #include "stringparser.hpp" #include "extensions.hpp" #include "context.hpp" +#include namespace Compiler { @@ -199,8 +200,8 @@ namespace Compiler { mMemberOp = false; - std::string name2 = toLower (name); - std::string id = toLower (mExplicit); + std::string name2 = Misc::StringUtils::lowerCase (name); + std::string id = Misc::StringUtils::lowerCase (mExplicit); char type = getContext().getMemberType (name2, id); @@ -285,7 +286,7 @@ namespace Compiler { start(); - std::string name2 = toLower (name); + std::string name2 = Misc::StringUtils::lowerCase (name); char type = mLocals.getType (name2); diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index a4cbc1ffe..210d18dc9 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -8,6 +8,7 @@ #include "locals.hpp" #include "generator.hpp" #include "extensions.hpp" +#include namespace Compiler { @@ -91,13 +92,13 @@ namespace Compiler return false; } - std::string name2 = toLower (name); + std::string name2 = Misc::StringUtils::lowerCase (name); char type = mLocals.getType (name2); if (type!=' ') { - getErrorHandler().error ("can't re-declare local variable", loc); + getErrorHandler().error ("catoLowern't re-declare local variable", loc); SkipParser skip (getErrorHandler(), getContext()); scanner.scan (skip); return false; @@ -112,7 +113,7 @@ namespace Compiler if (mState==SetState) { - std::string name2 = toLower (name); + std::string name2 = Misc::StringUtils::lowerCase (name); mName = name2; // local variable? @@ -138,7 +139,7 @@ namespace Compiler if (mState==SetMemberVarState) { - mMemberName = toLower (name); + mMemberName = Misc::StringUtils::lowerCase (name); char type = getContext().getMemberType (mMemberName, mName); if (type!=' ') @@ -205,13 +206,13 @@ namespace Compiler if (mState==BeginState && getContext().isId (name)) { mState = PotentialExplicitState; - mExplicit = toLower (name); + mExplicit = Misc::StringUtils::lowerCase (name); return true; } if (mState==BeginState && mAllowExpression) { - std::string name2 = toLower (name); + std::string name2 = Misc::StringUtils::lowerCase (name); char type = mLocals.getType (name2); diff --git a/components/compiler/parser.cpp b/components/compiler/parser.cpp index 90368eee0..896458e48 100644 --- a/components/compiler/parser.cpp +++ b/components/compiler/parser.cpp @@ -9,6 +9,8 @@ #include "exception.hpp" #include "scanner.hpp" +#include + namespace Compiler { // Report the error and throw an exception. @@ -57,10 +59,7 @@ namespace Compiler std::string Parser::toLower (const std::string& name) { - std::string lowerCase; - - std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); + std::string lowerCase = Misc::StringUtils::lowerCase(name); return lowerCase; } diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 962699dfa..7f43c36a5 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -11,6 +11,8 @@ #include "parser.hpp" #include "extensions.hpp" +#include + namespace Compiler { bool Scanner::get (char& c) @@ -268,11 +270,7 @@ namespace Compiler int i = 0; - std::string lowerCase; - lowerCase.reserve (name.size()); - - std::transform (name.begin(), name.end(), std::back_inserter (lowerCase), - (int(*)(int)) std::tolower); + std::string lowerCase = Misc::StringUtils::lowerCase(name); for (; keywords[i]; ++i) if (lowerCase==keywords[i]) diff --git a/components/compiler/stringparser.cpp b/components/compiler/stringparser.cpp index 396a88c78..09c902131 100644 --- a/components/compiler/stringparser.cpp +++ b/components/compiler/stringparser.cpp @@ -6,6 +6,7 @@ #include "scanner.hpp" #include "generator.hpp" +#include namespace Compiler { @@ -22,7 +23,7 @@ namespace Compiler { start(); if (mSmashCase) - Generator::pushString (mCode, mLiterals, toLower (name)); + Generator::pushString (mCode, mLiterals, Misc::StringUtils::lowerCase (name)); else Generator::pushString (mCode, mLiterals, name); diff --git a/components/files/filelibrary.cpp b/components/files/filelibrary.cpp index ce67f0c66..ce2d95f57 100644 --- a/components/files/filelibrary.cpp +++ b/components/files/filelibrary.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include <../components/misc/stringops.hpp> namespace Files { @@ -45,14 +45,14 @@ namespace Files if( !acceptableExtensions.empty() ) { fileExtension = boost::filesystem::path (listIter->extension()).string(); - boost::algorithm::to_lower(fileExtension); + Misc::StringUtils::toLower(fileExtension); if(!containsVectorString(acceptableExtensions, fileExtension)) continue; } type = boost::filesystem::path (listIter->parent_path().leaf()).string(); if (!strict) - boost::algorithm::to_lower(type); + Misc::StringUtils::toLower(type); mMap[type].push_back(*listIter); // std::cout << "Added path: " << listIter->string() << " in section "<< type < #include +#include <../components/misc/stringops.hpp> namespace Files { @@ -87,7 +88,7 @@ bool isFile(const char *name) if (!strict) { - boost::algorithm::to_lower(toFindStr); + Misc::StringUtils::toLower(toFindStr); } for (Files::PathContainer::const_iterator it = list.begin(); it != list.end(); ++it) @@ -99,7 +100,7 @@ bool isFile(const char *name) if (!strict) { - boost::algorithm::to_lower(fullPath); + Misc::StringUtils::toLower(fullPath); } if(endingMatches(fullPath, toFindStr)) { diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 31d873489..489acb108 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -44,6 +44,7 @@ #include #include +#include #include #include @@ -1003,7 +1004,7 @@ public: if(mSkelName.length() > 0 && mName != mSkelName) fullname += "@skel="+mSkelName; - std::transform(fullname.begin(), fullname.end(), fullname.begin(), ::tolower); + Misc::StringUtils::toLower(fullname); Ogre::MeshPtr mesh = meshMgr.getByName(fullname); if(mesh.isNull()) { @@ -1045,8 +1046,8 @@ static MeshPairMap sMeshPairMap; MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::string &group) { - std::transform(name.begin(), name.end(), name.begin(), ::tolower); - std::transform(skelName.begin(), skelName.end(), skelName.begin(), ::tolower); + Misc::StringUtils::toLower(name); + Misc::StringUtils::toLower(skelName); MeshPairMap::const_iterator meshiter = sMeshPairMap.find(name+"@skel="+skelName); if(meshiter != sMeshPairMap.end()) @@ -1159,7 +1160,7 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first); if(ent->hasSkeleton()) { - std::transform(meshes[i].second.begin(), meshes[i].second.end(), meshes[i].second.begin(), ::tolower); + Misc::StringUtils::toLower(meshes[i].second); if(meshes[i].second.length() < filter.length() || meshes[i].second.compare(0, filter.length(), filter) != 0) diff --git a/components/nifoverrides/nifoverrides.cpp b/components/nifoverrides/nifoverrides.cpp index 1c8fefd24..191b4ac2f 100644 --- a/components/nifoverrides/nifoverrides.cpp +++ b/components/nifoverrides/nifoverrides.cpp @@ -2,7 +2,8 @@ #include -#include +#include <../components/misc/stringops.hpp> + using namespace NifOverrides; @@ -19,7 +20,7 @@ TransparencyResult Overrides::getTransparencyOverride(const std::string& texture result.first = false; std::string tex = texture; - boost::to_lower(tex); + Misc::StringUtils::toLower(tex); Ogre::ConfigFile::SectionIterator seci = mTransparencyOverrides.getSectionIterator(); while (seci.hasMoreElements()) From 3517635cfd86b017ad6e842be388bbb9149a5bce Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Wed, 9 Jan 2013 21:16:45 +0000 Subject: [PATCH 329/378] added Drop and DropSoulGem functions --- apps/openmw/mwscript/docs/vmformat.txt | 7 ++- apps/openmw/mwscript/miscextensions.cpp | 64 +++++++++++++++++++++++++ apps/openmw/mwworld/worldimp.cpp | 4 +- 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 7c009f095..283f149de 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -311,4 +311,9 @@ op 0x20001f4: AddSoulGem, explicit reference op 0x20001f5: RemoveSoulGem op 0x20001f6: RemoveSoulGem, explicit reference op 0x20001f7: PlayBink -opcodes 0x20001f8-0x3ffffff unused +op 0x20001f8: Drop +op 0x20001f9: Drop, explicit reference +op 0x20001fa: DropSoulGem +op 0x20001fb: DropSoulGem, explicit reference + +opcodes 0x20001fa-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index a348a0a49..53c3c2f7b 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -383,6 +383,60 @@ namespace MWScript } }; + template + class OpDrop : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + + MWWorld::Ptr ptr = R()(runtime); + + std::string item = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); + + + for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) + { + if (::Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item)) + { + MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter); + break; + } + } + } + }; + + template + class OpDropSoulGem : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + + MWWorld::Ptr ptr = R()(runtime); + + std::string soul = runtime.getStringLiteral (runtime[0].mInteger); + runtime.pop(); + + MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); + + + for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) + { + if (::Misc::StringUtils::ciEqual(iter->getCellRef().mSoul, soul)) + { + MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter); + break; + } + } + } + }; + template class OpGetAttacked : public Interpreter::Opcode0 { @@ -495,6 +549,10 @@ namespace MWScript const int opcodeAddSoulGemExplicit = 0x20001f4; const int opcodeRemoveSoulGem = 0x20001f5; const int opcodeRemoveSoulGemExplicit = 0x20001f6; + const int opcodeDrop = 0x20001f8; + const int opcodeDropExplicit = 0x20001f9; + const int opcodeDropSoulGem = 0x20001fa; + const int opcodeDropSoulGemExplicit = 0x20001fb; const int opcodeGetAttacked = 0x20001d3; const int opcodeGetAttackedExplicit = 0x20001d4; const int opcodeGetWeaponDrawn = 0x20001d7; @@ -538,6 +596,8 @@ namespace MWScript extensions.registerFunction ("geteffect", 'l', "l", opcodeGetEffect, opcodeGetEffectExplicit); extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit); extensions.registerInstruction ("removesoulgem", "c", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit); + extensions.registerInstruction ("drop", "c", opcodeDrop, opcodeDropExplicit); + extensions.registerInstruction ("dropsoulgem", "c", opcodeDropSoulGem, opcodeDropSoulGemExplicit); extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit); extensions.registerFunction ("getspelleffects", 'l', "c", opcodeGetSpellEffects, opcodeGetSpellEffectsExplicit); @@ -576,6 +636,10 @@ namespace MWScript interpreter.installSegment5 (opcodeAddSoulGemExplicit, new OpAddSoulGem); interpreter.installSegment5 (opcodeRemoveSoulGem, new OpRemoveSoulGem); interpreter.installSegment5 (opcodeRemoveSoulGemExplicit, new OpRemoveSoulGem); + interpreter.installSegment5 (opcodeDrop, new OpDrop); + interpreter.installSegment5 (opcodeDropExplicit, new OpDrop); + interpreter.installSegment5 (opcodeDropSoulGem, new OpDropSoulGem); + interpreter.installSegment5 (opcodeDropSoulGemExplicit, new OpDropSoulGem); interpreter.installSegment5 (opcodeGetAttacked, new OpGetAttacked); interpreter.installSegment5 (opcodeGetAttackedExplicit, new OpGetAttacked); interpreter.installSegment5 (opcodeGetWeaponDrawn, new OpGetWeaponDrawn); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 6e2e5f3b7..f49b4bf9b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1288,10 +1288,10 @@ namespace MWWorld void World::dropObjectOnGround (const Ptr& actor, const Ptr& object) { - MWWorld::Ptr::CellStore* cell = getPlayer().getPlayer().getCell(); + MWWorld::Ptr::CellStore* cell = actor.getCell(); ESM::Position pos = - getPlayer().getPlayer().getRefData().getPosition(); + actor.getRefData().getPosition(); Ogre::Vector3 orig = Ogre::Vector3(pos.pos[0], pos.pos[1], pos.pos[2]); From 8486a5153592ee0505c0b5072e6dd7a48e65d54c Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Wed, 9 Jan 2013 22:55:28 +0000 Subject: [PATCH 330/378] fixed drop amounts thanks to zinnschlag --- apps/openmw/mwscript/miscextensions.cpp | 34 ++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 53c3c2f7b..f329e30d9 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -396,6 +396,9 @@ namespace MWScript std::string item = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); + Interpreter::Type_Integer amount = runtime[0].mInteger; + runtime.pop(); + MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); @@ -403,7 +406,19 @@ namespace MWScript { if (::Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item)) { - MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter); + if(iter->getRefData().getCount() <= amount) + { + MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter); + iter->getRefData().setCount(0); + } + else + { + int original = iter->getRefData().getCount(); + iter->getRefData().setCount(amount); + MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter); + iter->getRefData().setCount(original - amount); + } + break; } } @@ -430,7 +445,20 @@ namespace MWScript { if (::Misc::StringUtils::ciEqual(iter->getCellRef().mSoul, soul)) { - MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter); + + if(iter->getRefData().getCount() <= 1) + { + MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter); + iter->getRefData().setCount(0); + } + else + { + int original = iter->getRefData().getCount(); + iter->getRefData().setCount(1); + MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter); + iter->getRefData().setCount(original - 1); + } + break; } } @@ -596,7 +624,7 @@ namespace MWScript extensions.registerFunction ("geteffect", 'l', "l", opcodeGetEffect, opcodeGetEffectExplicit); extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit); extensions.registerInstruction ("removesoulgem", "c", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit); - extensions.registerInstruction ("drop", "c", opcodeDrop, opcodeDropExplicit); + extensions.registerInstruction ("drop", "cl", opcodeDrop, opcodeDropExplicit); extensions.registerInstruction ("dropsoulgem", "c", opcodeDropSoulGem, opcodeDropSoulGemExplicit); extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); extensions.registerFunction ("getweapondrawn", 'l', "", opcodeGetWeaponDrawn, opcodeGetWeaponDrawnExplicit); From c71656493da4c5a5634d6510b851de0b8c26ac3a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 11 Jan 2013 01:23:24 +0100 Subject: [PATCH 331/378] Ingredient effects now correctly hide until the player has discovered them --- apps/openmw/mwclass/ingredient.cpp | 13 +++++++++++++ apps/openmw/mwgui/widgets.cpp | 8 ++++++-- apps/openmw/mwgui/widgets.hpp | 3 +++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 1be8d66b3..7ad8f1b47 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -12,6 +12,9 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/actioneat.hpp" +#include "../mwworld/player.hpp" + +#include "../mwmechanics/npcstats.hpp" #include "../mwgui/tooltips.hpp" @@ -154,6 +157,10 @@ namespace MWClass text += MWGui::ToolTips::getMiscString(ref->mBase->mScript, "Script"); } + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer(); + MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats (player); + int alchemySkill = npcStats.getSkill (ESM::Skill::Alchemy).getBase(); + MWGui::Widgets::SpellEffectList list; for (int i=0; i<4; ++i) { @@ -163,6 +170,12 @@ namespace MWClass params.mEffectID = ref->mBase->mData.mEffectID[i]; params.mAttribute = ref->mBase->mData.mAttributes[i]; params.mSkill = ref->mBase->mData.mSkills[i]; + + params.mKnown = ( (i == 0 && alchemySkill >= 15) + || (i == 1 && alchemySkill >= 30) + || (i == 2 && alchemySkill >= 45) + || (i == 3 && alchemySkill >= 60)); + list.push_back(params); } info.effects = list; diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 82e112826..f932c1f03 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -390,8 +390,13 @@ void MWSpellEffect::setSpellEffect(const SpellEffectParams& params) void MWSpellEffect::updateWidgets() { - if (!mWindowManager) + if (!mEffectParams.mKnown) + { + mTextWidget->setCaption ("?"); + mRequestedWidth = mTextWidget->getTextSize().width + 24; + mImageWidget->setImageTexture (""); return; + } const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); @@ -400,7 +405,6 @@ void MWSpellEffect::updateWidgets() store.get().search(mEffectParams.mEffectID); assert(magicEffect); - assert(mWindowManager); std::string pt = mWindowManager->getGameSettingString("spoint", ""); std::string pts = mWindowManager->getGameSettingString("spoints", ""); diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 4ac5383f7..7cbb5e53a 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -37,12 +37,15 @@ namespace MWGui , mEffectID(-1) , mNoTarget(false) , mIsConstant(false) + , mKnown(true) { } bool mNoTarget; // potion effects for example have no target (target is always the player) bool mIsConstant; // constant effect means that duration will not be displayed + bool mKnown; // is this effect known to the player? (If not, will display as a question mark instead) + // value of -1 here means the effect is unknown to the player short mEffectID; From d469415e59ab55e16f01b24a83c422d8dfa1f60e Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Tue, 8 Jan 2013 09:45:01 -0800 Subject: [PATCH 332/378] factored out two function from OMW::Engine::go The initialization code inside Engine::go is now in two function it calls prior to running the main loop. --- apps/openmw/engine.cpp | 54 +++++++++++++++++++++++++++--------------- apps/openmw/engine.hpp | 7 ++++++ 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index b19cafab7..661bf4a52 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -240,18 +240,9 @@ void OMW::Engine::setNewGame(bool newGame) mNewGame = newGame; } -// Initialise and enter main loop. - -void OMW::Engine::go() +std::string OMW::Engine::loadSettings (Settings::Manager & settings) { - assert (!mCellName.empty()); - assert (!mMaster.empty()); - assert (!mOgre); - - mOgre = new OEngine::Render::OgreRenderer; - // Create the settings manager and load default settings file - Settings::Manager settings; const std::string localdefault = mCfgMgr.getLocalPath().string() + "/settings-default.cfg"; const std::string globaldefault = mCfgMgr.getGlobalPath().string() + "/settings-default.cfg"; @@ -272,10 +263,6 @@ void OMW::Engine::go() else if (boost::filesystem::exists(globaldefault)) settings.loadUser(globaldefault); - // Get the path for the keybinder xml file - std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); - bool keybinderUserExists = boost::filesystem::exists(keybinderUser); - mFpsLevel = settings.getInt("fps", "HUD"); // load nif overrides @@ -285,6 +272,11 @@ void OMW::Engine::go() else if (boost::filesystem::exists(mCfgMgr.getGlobalPath().string() + "/transparency-overrides.cfg")) nifOverrides.loadTransparencyOverrides(mCfgMgr.getGlobalPath().string() + "/transparency-overrides.cfg"); + return settingspath; +} + +void OMW::Engine::prepareEngine (Settings::Manager & settings) +{ std::string renderSystem = settings.getString("render system", "Video"); if (renderSystem == "") { @@ -294,6 +286,9 @@ void OMW::Engine::go() renderSystem = "OpenGL Rendering Subsystem"; #endif } + + mOgre = new OEngine::Render::OgreRenderer; + mOgre->configure( mCfgMgr.getLogPath().string(), renderSystem, @@ -365,6 +360,11 @@ void OMW::Engine::go() mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mVerboseScripts, mTranslationDataStorage)); // Sets up the input system + + // Get the path for the keybinder xml file + std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); + bool keybinderUserExists = boost::filesystem::exists(keybinderUser); + mEnvironment.setInputManager (new MWInput::InputManager (*mOgre, MWBase::Environment::get().getWorld()->getPlayer(), *MWBase::Environment::get().getWindowManager(), mDebug, *this, keybinderUser, keybinderUserExists)); @@ -388,13 +388,8 @@ void OMW::Engine::go() MWBase::Environment::get().getWorld()->changeToInteriorCell (mCellName, pos); } - std::cout << "\nPress Q/ESC or close window to exit.\n"; - mOgre->getRoot()->addFrameListener (this); - // Play some good 'ol tunes - MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore")); - // scripts if (mCompileAll) { @@ -407,10 +402,31 @@ void OMW::Engine::go() << "%)" << std::endl; } +} + +// Initialise and enter main loop. + +void OMW::Engine::go() +{ + assert (!mCellName.empty()); + assert (!mMaster.empty()); + assert (!mOgre); + + Settings::Manager settings; + std::string settingspath; + + settingspath = loadSettings (settings); + + prepareEngine (settings); + + // Play some good 'ol tunes + MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore")); if (!mStartupScript.empty()) MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript); + std::cout << "\nPress Q/ESC or close window to exit.\n"; + // Start the main rendering loop mOgre->start(); diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 601cc7765..363bb8a10 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "mwbase/environment.hpp" @@ -103,6 +104,12 @@ namespace OMW virtual bool frameRenderingQueued (const Ogre::FrameEvent& evt); + /// Load settings from various files, returns the path to the user settings file + std::string loadSettings (Settings::Manager & settings); + + /// Prepare engine for game play + void prepareEngine (Settings::Manager & settings); + public: Engine(Files::ConfigurationManager& configurationManager); virtual ~Engine(); From 9d4f8c67223625e5a21d9095888532847e2e8be5 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Thu, 3 Jan 2013 08:57:04 -0800 Subject: [PATCH 333/378] changed bullet_nif_loader to not modifiy NIF file The code in bullet_nif_loader was modifing the loaded NIF file as part of processing. Currently this is OK since the NIF file will be thrown away immediately, but it causes problems when trying to share loaded NIF files. --- components/nifbullet/bullet_nif_loader.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index 42f6a8e68..0c8de0c27 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -165,7 +165,7 @@ bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node* node) } void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, - const Nif::Transformation *trafo,bool hasCollisionNode,bool isCollisionNode,bool raycastingOnly) + const Nif::Transformation *parentTrafo,bool hasCollisionNode,bool isCollisionNode,bool raycastingOnly) { // Accumulate the flags from all the child nodes. This works for all @@ -208,23 +208,23 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, } } + Nif::Transformation childTrafo = node->trafo; - if (trafo) + if (parentTrafo) { // Get a non-const reference to the node's data, since we're // overwriting it. TODO: Is this necessary? - Nif::Transformation &final = node->trafo; // For both position and rotation we have that: // final_vector = old_vector + old_rotation*new_vector*old_scale - final.pos = trafo->pos + trafo->rotation*final.pos*trafo->scale; + childTrafo.pos = parentTrafo->pos + parentTrafo->rotation*childTrafo.pos*parentTrafo->scale; // Merge the rotations together - final.rotation = trafo->rotation * final.rotation; + childTrafo.rotation = parentTrafo->rotation * childTrafo.rotation; // Scale - final.scale *= trafo->scale; + childTrafo.scale *= parentTrafo->scale; } @@ -249,14 +249,14 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, { if (!list[i].empty()) { - handleNode(list[i].getPtr(), flags,&node->trafo,hasCollisionNode,isCollisionNode,raycastingOnly); + handleNode(list[i].getPtr(), flags,&childTrafo,hasCollisionNode,isCollisionNode,raycastingOnly); } } } else if (node->recType == Nif::RC_NiTriShape && (isCollisionNode || !hasCollisionNode)) { cShape->mCollide = !(flags&0x800); - handleNiTriShape(dynamic_cast(node), flags,node->trafo.rotation,node->trafo.pos,node->trafo.scale,raycastingOnly); + handleNiTriShape(dynamic_cast(node), flags,childTrafo.rotation,childTrafo.pos,childTrafo.scale,raycastingOnly); } else if(node->recType == Nif::RC_RootCollisionNode) { @@ -265,7 +265,7 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, for (int i=0; itrafo, hasCollisionNode,true,raycastingOnly); + handleNode(list[i].getPtr(), flags,&childTrafo, hasCollisionNode,true,raycastingOnly); } } } From 0989b44b415d56246a02d754733e971b7a8458bf Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Thu, 3 Jan 2013 09:33:19 -0800 Subject: [PATCH 334/378] spread some const around NIF loading --- components/nif/nif_file.cpp | 4 ++-- components/nif/node.hpp | 4 ++-- components/nifbullet/bullet_nif_loader.cpp | 18 +++++++-------- components/nifbullet/bullet_nif_loader.hpp | 10 ++++----- components/nifogre/ogre_nif_loader.cpp | 26 +++++++++++----------- 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 3313d89ab..38ff5b270 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -211,14 +211,14 @@ void NiSkinInstance::post(NIFFile *nif) } } -Ogre::Matrix4 Node::getLocalTransform() +Ogre::Matrix4 Node::getLocalTransform() const { Ogre::Matrix4 mat4(Ogre::Matrix4::IDENTITY); mat4.makeTransform(trafo.pos, Ogre::Vector3(trafo.scale), Ogre::Quaternion(trafo.rotation)); return mat4; } -Ogre::Matrix4 Node::getWorldTransform() +Ogre::Matrix4 Node::getWorldTransform() const { if(parent != NULL) return parent->getWorldTransform() * getLocalTransform(); diff --git a/components/nif/node.hpp b/components/nif/node.hpp index f7d3c6e96..07e7868cc 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -111,8 +111,8 @@ public: boneIndex = ind; } - Ogre::Matrix4 getLocalTransform(); - Ogre::Matrix4 getWorldTransform(); + Ogre::Matrix4 getLocalTransform() const; + Ogre::Matrix4 getWorldTransform() const; }; struct NiNode : Node diff --git a/components/nifbullet/bullet_nif_loader.cpp b/components/nifbullet/bullet_nif_loader.cpp index 0c8de0c27..cea8c4b52 100644 --- a/components/nifbullet/bullet_nif_loader.cpp +++ b/components/nifbullet/bullet_nif_loader.cpp @@ -51,7 +51,7 @@ ManualBulletShapeLoader::~ManualBulletShapeLoader() } -btQuaternion ManualBulletShapeLoader::getbtQuat(Ogre::Matrix3 &m) +btQuaternion ManualBulletShapeLoader::getbtQuat(Ogre::Matrix3 const &m) { Ogre::Quaternion oquat(m); btQuaternion quat; @@ -62,7 +62,7 @@ btQuaternion ManualBulletShapeLoader::getbtQuat(Ogre::Matrix3 &m) return quat; } -btVector3 ManualBulletShapeLoader::getbtVector(Ogre::Vector3 &v) +btVector3 ManualBulletShapeLoader::getbtVector(Ogre::Vector3 const &v) { return btVector3(v[0], v[1], v[2]); } @@ -138,7 +138,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) } } -bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node* node) +bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node const * node) { if (node->recType == Nif::RC_NiNode) { @@ -164,7 +164,7 @@ bool ManualBulletShapeLoader::hasRootCollisionNode(Nif::Node* node) return false; } -void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, +void ManualBulletShapeLoader::handleNode(Nif::Node const *node, int flags, const Nif::Transformation *parentTrafo,bool hasCollisionNode,bool isCollisionNode,bool raycastingOnly) { @@ -181,7 +181,7 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, } // Check for extra data - Nif::Extra *e = node; + Nif::Extra const *e = node; while (!e->extra.empty()) { // Get the next extra data in the list @@ -232,7 +232,7 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, { - btVector3 boxsize = getbtVector((node->boundXYZ)); + btVector3 boxsize = getbtVector(node->boundXYZ); cShape->boxTranslation = node->boundPos; cShape->boxRotation = node->boundRot; @@ -243,7 +243,7 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, // For NiNodes, loop through children if (node->recType == Nif::RC_NiNode) { - Nif::NodeList &list = ((Nif::NiNode*)node)->children; + Nif::NodeList const &list = ((Nif::NiNode const *)node)->children; int n = list.length(); for (int i=0; irecType == Nif::RC_NiTriShape && (isCollisionNode || !hasCollisionNode)) { cShape->mCollide = !(flags&0x800); - handleNiTriShape(dynamic_cast(node), flags,childTrafo.rotation,childTrafo.pos,childTrafo.scale,raycastingOnly); + handleNiTriShape(dynamic_cast(node), flags,childTrafo.rotation,childTrafo.pos,childTrafo.scale,raycastingOnly); } else if(node->recType == Nif::RC_RootCollisionNode) { @@ -270,7 +270,7 @@ void ManualBulletShapeLoader::handleNode(Nif::Node *node, int flags, } } -void ManualBulletShapeLoader::handleNiTriShape(Nif::NiTriShape *shape, int flags,Ogre::Matrix3 parentRot,Ogre::Vector3 parentPos,float parentScale, +void ManualBulletShapeLoader::handleNiTriShape(Nif::NiTriShape const *shape, int flags,Ogre::Matrix3 parentRot,Ogre::Vector3 parentPos,float parentScale, bool raycastingOnly) { assert(shape != NULL); diff --git a/components/nifbullet/bullet_nif_loader.hpp b/components/nifbullet/bullet_nif_loader.hpp index 2190fda1b..520878ce1 100644 --- a/components/nifbullet/bullet_nif_loader.hpp +++ b/components/nifbullet/bullet_nif_loader.hpp @@ -79,25 +79,25 @@ public: void load(const std::string &name,const std::string &group); private: - btQuaternion getbtQuat(Ogre::Matrix3 &m); + btQuaternion getbtQuat(Ogre::Matrix3 const &m); - btVector3 getbtVector(Ogre::Vector3 &v); + btVector3 getbtVector(Ogre::Vector3 const &v); /** *Parse a node. */ - void handleNode(Nif::Node *node, int flags, + void handleNode(Nif::Node const *node, int flags, const Nif::Transformation *trafo, bool hasCollisionNode,bool isCollisionNode,bool raycastingOnly); /** *Helper function */ - bool hasRootCollisionNode(Nif::Node* node); + bool hasRootCollisionNode(Nif::Node const * node); /** *convert a NiTriShape to a bullet trishape. */ - void handleNiTriShape(Nif::NiTriShape *shape, int flags,Ogre::Matrix3 parentRot,Ogre::Vector3 parentPos,float parentScales,bool raycastingOnly); + void handleNiTriShape(Nif::NiTriShape const *shape, int flags,Ogre::Matrix3 parentRot,Ogre::Vector3 parentPos,float parentScales,bool raycastingOnly); std::string resourceName; std::string resourceGroup; diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 489acb108..207d01ff6 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -187,7 +187,7 @@ static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk) } -void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vector &ctrls, Ogre::Bone *parent=NULL) +void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vector &ctrls, Ogre::Bone *parent=NULL) { Ogre::Bone *bone; if(!skel->hasBone(node->name)) @@ -255,7 +255,7 @@ void loadResource(Ogre::Resource *resource) Nif::NIFFile nif(skel->getName()); const Nif::Node *node = dynamic_cast(nif.getRecord(0)); - std::vector ctrls; + std::vector ctrls; buildBones(skel, node, ctrls); std::vector targets; @@ -266,7 +266,7 @@ void loadResource(Ogre::Resource *resource) float maxtime = 0.0f; for(size_t i = 0;i < ctrls.size();i++) { - Nif::NiKeyframeController *ctrl = ctrls[i]; + Nif::NiKeyframeController const *ctrl = ctrls[i]; maxtime = std::max(maxtime, ctrl->timeStop); Nif::Named *target = dynamic_cast(ctrl->target.getPtr()); if(target != NULL) @@ -289,8 +289,8 @@ void loadResource(Ogre::Resource *resource) for(size_t i = 0;i < ctrls.size();i++) { - Nif::NiKeyframeController *kfc = ctrls[i]; - Nif::NiKeyframeData *kf = kfc->data.getPtr(); + Nif::NiKeyframeController const *kfc = ctrls[i]; + Nif::NiKeyframeData const *kf = kfc->data.getPtr(); /* Get the keyframes and make sure they're sorted first to last */ Nif::QuaternionKeyList quatkeys = kf->mRotations; @@ -711,7 +711,7 @@ class NIFMeshLoader : Ogre::ManualResourceLoader // Convert NiTriShape to Ogre::SubMesh - void handleNiTriShape(Ogre::Mesh *mesh, Nif::NiTriShape *shape) + void handleNiTriShape(Ogre::Mesh *mesh, Nif::NiTriShape const *shape) { Ogre::SkeletonPtr skel; const Nif::NiTriShapeData *data = shape->data.getPtr(); @@ -909,18 +909,18 @@ class NIFMeshLoader : Ogre::ManualResourceLoader sub->setMaterialName(mMaterialName); } - bool findTriShape(Ogre::Mesh *mesh, Nif::Node *node) + bool findTriShape(Ogre::Mesh *mesh, Nif::Node const *node) { if(node->recType == Nif::RC_NiTriShape && mShapeName == node->name) { - handleNiTriShape(mesh, dynamic_cast(node)); + handleNiTriShape(mesh, dynamic_cast(node)); return true; } - Nif::NiNode *ninode = dynamic_cast(node); + Nif::NiNode const *ninode = dynamic_cast(node); if(ninode) { - Nif::NodeList &children = ninode->children; + Nif::NodeList const &children = ninode->children; for(size_t i = 0;i < children.length();i++) { if(!children[i].empty()) @@ -957,7 +957,7 @@ public: } Nif::NIFFile nif(mName); - Nif::Node *node = dynamic_cast(nif.getRecord(0)); + Nif::Node const *node = dynamic_cast(nif.getRecord(0)); findTriShape(mesh, node); } @@ -1062,10 +1062,10 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, const std:: } // The first record is assumed to be the root node - Nif::Record *r = nif.getRecord(0); + Nif::Record const *r = nif.getRecord(0); assert(r != NULL); - Nif::Node *node = dynamic_cast(r); + Nif::Node const *node = dynamic_cast(r); if(node == NULL) { nif.warn("First record in file was not a node, but a "+ From d5ebd6654dc87de27bcc01f5634503acd7c71a6a Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sat, 5 Jan 2013 10:58:50 -0800 Subject: [PATCH 335/378] cache loaded NIF files to eliminate reloads Created a NIF file caching mechanism to prevent the system from reloading a NIF during a startup and cell changes. --- apps/openmw/engine.cpp | 2 + apps/openmw/mwworld/scene.cpp | 2 + components/nif/nif_file.cpp | 149 +++++++++++++++++++++ components/nif/nif_file.hpp | 35 +++-- components/nifbullet/bullet_nif_loader.cpp | 3 +- components/nifogre/ogre_nif_loader.cpp | 10 +- 6 files changed, 186 insertions(+), 15 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 661bf4a52..4413e98f4 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -277,6 +277,8 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings) void OMW::Engine::prepareEngine (Settings::Manager & settings) { + Nif::NIFFile::CacheLock cachelock; + std::string renderSystem = settings.getString("render system", "Video"); if (renderSystem == "") { diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 6b9abf508..b917a8916 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -172,6 +172,8 @@ namespace MWWorld void Scene::changeCell (int X, int Y, const ESM::Position& position, bool adjustPlayerPos) { + Nif::NIFFile::CacheLock cachelock; + mRendering.preCellChange(mCurrentCell); // remove active diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 38ff5b270..07d34b9ea 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -34,10 +34,159 @@ #include "controller.hpp" #include + +//TODO: when threading is needed, enable these +//#include +//#include + using namespace std; using namespace Nif; using namespace Misc; +class NIFFile::LoadedCache +{ + //TODO: enable this to make cache thread safe... + //typedef boost::mutex mutex; + + struct mutex + { + void lock () {}; + void unlock () {} + }; + + typedef boost::lock_guard lock_guard; + typedef std::map < std::string, boost::weak_ptr > loaded_map; + typedef std::vector < boost::shared_ptr > locked_files; + + static int sLockLevel; + static mutex sProtector; + static loaded_map sLoadedMap; + static locked_files sLockedFiles; + +public: + + static ptr create (const std::string &name) + { + lock_guard _ (sProtector); + + ptr result; + + // lookup the resource + loaded_map::iterator i = sLoadedMap.find (name); + + if (i == sLoadedMap.end ()) // it doesn't existing currently, + { // or hasn't in the very near past + + // create it now, for smoother threading if needed, the + // loading should be performed outside of the sLoaderMap + // lock and an alternate mechanism should be used to + // synchronize threads competing to load the same resource + result = boost::make_shared (name, psudo_private_modifier()); + + // if we are locking the cache add an extra reference + // to keep the file in memory + if (sLockLevel > 0) + sLockedFiles.push_back (result); + + // stash a reference to the resource so that future + // calls can benefit + sLoadedMap [name] = boost::weak_ptr (result); + } + else // it may (probably) still exists + { + // attempt to get the reference + result = i->second.lock (); + + if (!result) // resource is in the process of being destroyed + { + // create a new instance, to replace the one that has + // begun the irreversible process of being destroyed + result = boost::make_shared (name, psudo_private_modifier()); + + // respect the cache lock... + if (sLockLevel > 0) + sLockedFiles.push_back (result); + + // we potentially overwrite an expired pointer here + // but the other thread performing the delete on + // the previous copy of this resource will detect it + // and make sure not to erase the new reference + sLoadedMap [name] = boost::weak_ptr (result); + } + } + + // we made it! + return result; + } + + static void release (NIFFile * file) + { + lock_guard _ (sProtector); + + loaded_map::iterator i = sLoadedMap.find (file->filename); + + // its got to be in here, it just might not be us... + assert (i != sLoadedMap.end ()); + + // if weak_ptr is still expired, this resource hasn't been recreated + // between the initiation of the final release due to destruction + // of the last shared pointer and this thread acquiring the lock on + // the loader map + if (i->second.expired ()) + sLoadedMap.erase (i); + } + + static void lockCache () + { + lock_guard _ (sProtector); + + sLockLevel++; + } + + static void unlockCache () + { + locked_files resetList; + + { + lock_guard _ (sProtector); + + if (--sLockLevel) + sLockedFiles.swap(resetList); + } + + // this not necessary, but makes it clear that the + // deletion of the locked cache entries is being done + // outside the protection of sProtector + resetList.clear (); + } +}; + +int NIFFile::LoadedCache::sLockLevel = 0; +NIFFile::LoadedCache::mutex NIFFile::LoadedCache::sProtector; +NIFFile::LoadedCache::loaded_map NIFFile::LoadedCache::sLoadedMap; +NIFFile::LoadedCache::locked_files NIFFile::LoadedCache::sLockedFiles; + +// these three calls are forwarded to the cache implementation... +void NIFFile::lockCache () { LoadedCache::lockCache (); } +void NIFFile::unlockCache () { LoadedCache::unlockCache (); } +NIFFile::ptr NIFFile::create (const std::string &name) { return LoadedCache::create (name); } + +/// Open a NIF stream. The name is used for error messages. +NIFFile::NIFFile(const std::string &name, psudo_private_modifier) + : filename(name) +{ + inp = Ogre::ResourceGroupManager::getSingleton().openResource(name); + parse(); +} + +NIFFile::~NIFFile() +{ + LoadedCache::release (this); + + for(std::size_t i=0; i #include +#include +#include +#include +#include + #include #include "record.hpp" @@ -93,6 +98,14 @@ class NIFFile return u.f; } + class LoadedCache; + friend class LoadedCache; + + // attempt to protect NIFFile from misuse... + struct psudo_private_modifier {}; // this dirty little trick should optimize out + NIFFile (NIFFile const &); + void operator = (NIFFile const &); + public: /// Used for error handling void fail(const std::string &msg) @@ -108,19 +121,21 @@ public: << "File: "< ptr; + /// Open a NIF stream. The name is used for error messages. - NIFFile(const std::string &name) - : filename(name) - { - inp = Ogre::ResourceGroupManager::getSingleton().openResource(name); - parse(); - } + NIFFile(const std::string &name, psudo_private_modifier); + ~NIFFile(); + + static ptr create (const std::string &name); + static void lockCache (); + static void unlockCache (); - ~NIFFile() + struct CacheLock { - for(std::size_t i=0; i(resource); OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!"); - Nif::NIFFile nif(skel->getName()); + Nif::NIFFile::ptr pnif(Nif::NIFFile::create (skel->getName())); + Nif::NIFFile & nif = *pnif.get (); const Nif::Node *node = dynamic_cast(nif.getRecord(0)); std::vector ctrls; @@ -956,8 +957,8 @@ public: return; } - Nif::NIFFile nif(mName); - Nif::Node const *node = dynamic_cast(nif.getRecord(0)); + Nif::NIFFile::ptr nif = Nif::NIFFile::create (mName); + Nif::Node const *node = dynamic_cast(nif->getRecord(0)); findTriShape(mesh, node); } @@ -1054,7 +1055,8 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, const std:: return meshiter->second; MeshPairList &meshes = sMeshPairMap[name+"@skel="+skelName]; - Nif::NIFFile nif(name); + Nif::NIFFile::ptr pnif = Nif::NIFFile::create (name); + Nif::NIFFile &nif = *pnif.get (); if (nif.numRecords() < 1) { nif.warn("Found no records in NIF."); From 85bdb49d1b18b9653f5e35cbee397342b92edf2c Mon Sep 17 00:00:00 2001 From: Michal Sciubidlo Date: Fri, 11 Jan 2013 09:10:48 +0100 Subject: [PATCH 336/378] Mix debug and release if necessary under non Windows systems. --- components/files/ogreplugin.cpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/components/files/ogreplugin.cpp b/components/files/ogreplugin.cpp index ca90fd30e..c319f7758 100644 --- a/components/files/ogreplugin.cpp +++ b/components/files/ogreplugin.cpp @@ -6,11 +6,6 @@ namespace Files { bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre::Root &ogreRoot) { - // Append plugin suffix if debugging. -#if defined(DEBUG) - pluginName = pluginName + OGRE_PLUGIN_DEBUG_SUFFIX; -#endif - #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE std::ostringstream verStream; verStream << "." << OGRE_VERSION_MAJOR << "." << OGRE_VERSION_MINOR << "." << OGRE_VERSION_PATCH; @@ -28,13 +23,28 @@ bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre:: pluginExt = ".so"; #endif - std::string pluginPath = pluginDir + "/" + pluginName + pluginExt; + // Append plugin suffix if debugging. + std::string pluginPath; +#if defined(DEBUG) + pluginPath = pluginDir + "/" + pluginName + OGRE_PLUGIN_DEBUG_SUFFIX + pluginExt; + if (boost::filesystem::exists(pluginPath)) { + ogreRoot.loadPlugin(pluginPath); + return true; + } + else { +#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 + return false; +#endif //OGRE_PLATFORM == OGRE_PLATFORM_WIN32 + } +#endif //defined(DEBUG) + + pluginPath = pluginDir + "/" + pluginName + pluginExt; if (boost::filesystem::exists(pluginPath)) { - ogreRoot.loadPlugin(pluginPath); - return true; + ogreRoot.loadPlugin(pluginPath); + return true; } else { - return false; + return false; } } From ed3c23ad9af2eb310c32fb2563ae55777c2e4608 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 12 Jan 2013 01:18:36 +0100 Subject: [PATCH 337/378] Fixed crash on changing cell due to Utf8Encoder going out of scope --- apps/openmw/engine.cpp | 11 ++++++----- apps/openmw/engine.hpp | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 4413e98f4..4f149efa1 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -325,16 +325,13 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) // cursor replacer (converts the cursor from the bsa so they can be used by mygui) MWGui::CursorReplace replacer; - // Create encoder - ToUTF8::Utf8Encoder encoder (mEncoding); - // Create the world mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster, - mResDir, mCfgMgr.getCachePath(), mNewGame, &encoder, mFallbackMap, + mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoder, mFallbackMap, mActivationDistanceOverride)); //Load translation data - mTranslationDataStorage.setEncoder(&encoder); + mTranslationDataStorage.setEncoder(mEncoder); mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster); // Create window manager - this manages all the MW-specific GUI windows @@ -419,6 +416,10 @@ void OMW::Engine::go() settingspath = loadSettings (settings); + // Create encoder + ToUTF8::Utf8Encoder encoder (mEncoding); + mEncoder = &encoder; + prepareEngine (settings); // Play some good 'ol tunes diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 363bb8a10..e320c6991 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -62,6 +62,7 @@ namespace OMW { MWBase::Environment mEnvironment; ToUTF8::FromType mEncoding; + ToUTF8::Utf8Encoder* mEncoder; Files::PathContainer mDataDirs; boost::filesystem::path mResDir; OEngine::Render::OgreRenderer *mOgre; From 31e6d28b19f1afedcdefb0caa6a5664e413c72cf Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 12 Jan 2013 06:37:43 +0100 Subject: [PATCH 338/378] Close NIF file resource after loading it into memory --- components/nif/nif_file.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 07d34b9ea..3db34e0cc 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -177,6 +177,8 @@ NIFFile::NIFFile(const std::string &name, psudo_private_modifier) { inp = Ogre::ResourceGroupManager::getSingleton().openResource(name); parse(); + // Make sure to close the file after it was loaded into memory + inp.setNull(); } NIFFile::~NIFFile() From f4ee8e26429fe8b27247b47ce88994e9e5c3937d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 12 Jan 2013 13:10:20 +0100 Subject: [PATCH 339/378] Issue #479: Added additional magnitude parameter to known spells --- apps/openmw/mwgui/quickkeysmenu.cpp | 2 +- apps/openmw/mwgui/spellbuyingwindow.cpp | 12 ++++++------ apps/openmw/mwgui/spellcreationdialog.cpp | 2 +- apps/openmw/mwgui/spellwindow.cpp | 2 +- apps/openmw/mwmechanics/spells.cpp | 24 +++++++++++------------ apps/openmw/mwmechanics/spells.hpp | 10 +++++----- apps/openmw/mwscript/statsextensions.cpp | 6 +++--- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 02512425d..6d51420f0 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -440,7 +440,7 @@ namespace MWGui for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { - spellList.push_back(*it); + spellList.push_back (it->first); } const MWWorld::ESMStore &esmStore = diff --git a/apps/openmw/mwgui/spellbuyingwindow.cpp b/apps/openmw/mwgui/spellbuyingwindow.cpp index a41f401a5..11f090494 100644 --- a/apps/openmw/mwgui/spellbuyingwindow.cpp +++ b/apps/openmw/mwgui/spellbuyingwindow.cpp @@ -98,19 +98,19 @@ namespace MWGui MWMechanics::Spells& playerSpells = MWWorld::Class::get (player).getCreatureStats (player).getSpells(); MWMechanics::Spells& merchantSpells = MWWorld::Class::get (actor).getCreatureStats (actor).getSpells(); - + for (MWMechanics::Spells::TIterator iter = merchantSpells.begin(); iter!=merchantSpells.end(); ++iter) { const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find (*iter); - + MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); + if (spell->mData.mType!=ESM::Spell::ST_Spell) continue; // don't try to sell diseases, curses or powers - + if (std::find (playerSpells.begin(), playerSpells.end(), *iter)!=playerSpells.end()) continue; // we have that spell already - - addSpell (*iter); + + addSpell (iter->first); } updateLabels(); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 69d69519f..839586452 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -436,7 +436,7 @@ namespace MWGui for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { const ESM::Spell* spell = - MWBase::Environment::get().getWorld()->getStore().get().find(*it); + MWBase::Environment::get().getWorld()->getStore().get().find (it->first); // only normal spells count if (spell->mData.mType != ESM::Spell::ST_Spell) diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index d62b23de4..47e1d739a 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -139,7 +139,7 @@ namespace MWGui for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) { - spellList.push_back(*it); + spellList.push_back (it->first); } const MWWorld::ESMStore &esmStore = diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index ef084f479..32acca2f0 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -29,13 +29,13 @@ namespace MWMechanics void Spells::add (const std::string& spellId) { - if (std::find (mSpells.begin(), mSpells.end(), spellId)==mSpells.end()) - mSpells.push_back (spellId); + if (mSpells.find (spellId)==mSpells.end()) + mSpells.insert (std::make_pair (spellId, -1)); } void Spells::remove (const std::string& spellId) { - TContainer::iterator iter = std::find (mSpells.begin(), mSpells.end(), spellId); + TContainer::iterator iter = mSpells.find (spellId); if (iter!=mSpells.end()) mSpells.erase (iter); @@ -51,7 +51,7 @@ namespace MWMechanics for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter) { const ESM::Spell *spell = - MWBase::Environment::get().getWorld()->getStore().get().find (*iter); + MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); if (spell->mData.mType==ESM::Spell::ST_Ability || spell->mData.mType==ESM::Spell::ST_Blight || spell->mData.mType==ESM::Spell::ST_Disease || spell->mData.mType==ESM::Spell::ST_Curse) @@ -75,18 +75,18 @@ namespace MWMechanics { return mSelectedSpell; } - + bool Spells::hasCommonDisease() const { for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter) { const ESM::Spell *spell = - MWBase::Environment::get().getWorld()->getStore().get().find (*iter); - + MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); + if (spell->mData.mFlags & ESM::Spell::ST_Disease) return true; } - + return false; } @@ -95,12 +95,12 @@ namespace MWMechanics for (TIterator iter = mSpells.begin(); iter!=mSpells.end(); ++iter) { const ESM::Spell *spell = - MWBase::Environment::get().getWorld()->getStore().get().find (*iter); - + MWBase::Environment::get().getWorld()->getStore().get().find (iter->first); + if (spell->mData.mFlags & ESM::Spell::ST_Blight) return true; } - - return false; + + return false; } } diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index 12308661b..b4d058896 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -1,7 +1,7 @@ #ifndef GAME_MWMECHANICS_SPELLS_H #define GAME_MWMECHANICS_SPELLS_H -#include +#include #include namespace ESM @@ -21,12 +21,12 @@ namespace MWMechanics { public: - typedef std::vector TContainer; + typedef std::map TContainer; // ID, magnitude typedef TContainer::const_iterator TIterator; private: - std::vector mSpells; + TContainer mSpells; std::string mSelectedSpell; void addSpell (const ESM::Spell *, MagicEffects& effects) const; @@ -55,10 +55,10 @@ namespace MWMechanics const std::string getSelectedSpell() const; ///< May return an empty string. - + bool hasCommonDisease() const; - bool hasBlightDisease() const; + bool hasBlightDisease() const; }; } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 31ea7518b..530d44c9d 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -485,7 +485,7 @@ namespace MWScript for (MWMechanics::Spells::TIterator iter ( MWWorld::Class::get (ptr).getCreatureStats (ptr).getSpells().begin()); iter!=MWWorld::Class::get (ptr).getCreatureStats (ptr).getSpells().end(); ++iter) - if (*iter==id) + if (iter->first==id) { value = 1; break; @@ -1188,7 +1188,7 @@ namespace MWScript extensions.registerFunction ("getpccrimelevel", 'f', "", opcodeGetPCCrimeLevel); extensions.registerInstruction ("setpccrimelevel", "f", opcodeSetPCCrimeLevel); extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel); - + extensions.registerInstruction ("addspell", "c", opcodeAddSpell, opcodeAddSpellExplicit); extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, opcodeRemoveSpellExplicit); @@ -1286,7 +1286,7 @@ namespace MWScript interpreter.installSegment5 (opcodeGetPCCrimeLevel, new OpGetPCCrimeLevel); interpreter.installSegment5 (opcodeSetPCCrimeLevel, new OpSetPCCrimeLevel); interpreter.installSegment5 (opcodeModPCCrimeLevel, new OpModPCCrimeLevel); - + interpreter.installSegment5 (opcodeAddSpell, new OpAddSpell); interpreter.installSegment5 (opcodeAddSpellExplicit, new OpAddSpell); interpreter.installSegment5 (opcodeRemoveSpell, new OpRemoveSpell); From d4ca954d47c0e19ee767efd2f263183ddd0b2839 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Sat, 12 Jan 2013 18:31:57 +0000 Subject: [PATCH 340/378] scripts on items in containers added to script list on cell change --- apps/openmw/mwworld/localscripts.cpp | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index a821ad486..9c9131008 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -3,6 +3,10 @@ #include "esmstore.hpp" #include "cellstore.hpp" +#include "class.hpp" +#include "containerstore.hpp" + + namespace { template @@ -19,6 +23,32 @@ namespace } } } + + // Adds scripts for items in containers (containers/npcs/creatures) + template + void listCellScriptsCont (MWWorld::LocalScripts& localScripts, + MWWorld::CellRefList& cellRefList, MWWorld::Ptr::CellStore *cell) + { + for (typename MWWorld::CellRefList::List::iterator iter ( + cellRefList.mList.begin()); + iter!=cellRefList.mList.end(); ++iter) + { + + MWWorld::Ptr containerPtr (&*iter, cell); + + MWWorld::ContainerStore& container = MWWorld::Class::get(containerPtr).getContainerStore(containerPtr); + for(MWWorld::ContainerStoreIterator it3 = container.begin(); it3 != container.end(); ++it3) + { + std::string script = MWWorld::Class::get(*it3).getScript(*it3); + if(script != "") + { + MWWorld::Ptr item = *it3; + item.mCell = cell; + localScripts.add (script, item); + } + } + } + } } MWWorld::LocalScripts::LocalScripts (const MWWorld::ESMStore& store) : mStore (store) {} @@ -78,13 +108,16 @@ void MWWorld::LocalScripts::addCell (Ptr::CellStore *cell) listCellScripts (*this, cell->mBooks, cell); listCellScripts (*this, cell->mClothes, cell); listCellScripts (*this, cell->mContainers, cell); + listCellScriptsCont (*this, cell->mContainers, cell); listCellScripts (*this, cell->mCreatures, cell); + listCellScriptsCont (*this, cell->mCreatures, cell); listCellScripts (*this, cell->mDoors, cell); listCellScripts (*this, cell->mIngreds, cell); listCellScripts (*this, cell->mLights, cell); listCellScripts (*this, cell->mLockpicks, cell); listCellScripts (*this, cell->mMiscItems, cell); listCellScripts (*this, cell->mNpcs, cell); + listCellScriptsCont (*this, cell->mNpcs, cell); listCellScripts (*this, cell->mProbes, cell); listCellScripts (*this, cell->mRepairs, cell); listCellScripts (*this, cell->mWeapons, cell); From 654cd3ab9bef7aad11a8c5e3a55a26d4100065cc Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 13 Jan 2013 14:52:55 +0100 Subject: [PATCH 341/378] Issue #479: Use magnitude specified when adding disease --- apps/openmw/mwmechanics/magiceffects.cpp | 10 +++++++--- apps/openmw/mwmechanics/magiceffects.hpp | 3 ++- apps/openmw/mwmechanics/spells.cpp | 13 +++++-------- apps/openmw/mwmechanics/spells.hpp | 4 +--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwmechanics/magiceffects.cpp b/apps/openmw/mwmechanics/magiceffects.cpp index 94363cb79..1a7b34817 100644 --- a/apps/openmw/mwmechanics/magiceffects.cpp +++ b/apps/openmw/mwmechanics/magiceffects.cpp @@ -68,7 +68,7 @@ namespace MWMechanics } } - void MagicEffects::add (const ESM::EffectList& list) + void MagicEffects::add (const ESM::EffectList& list, float magnitude) { for (std::vector::const_iterator iter (list.mList.begin()); iter!=list.mList.end(); ++iter) @@ -78,9 +78,13 @@ namespace MWMechanics if (iter->mMagnMin>=iter->mMagnMax) param.mMagnitude = iter->mMagnMin; else + { + if (magnitude==-1) + magnitude = static_cast (std::rand()) / RAND_MAX; + param.mMagnitude = static_cast ( - (iter->mMagnMax-iter->mMagnMin+1)* - (static_cast (std::rand()) / RAND_MAX) + iter->mMagnMin); + (iter->mMagnMax-iter->mMagnMin+1)*magnitude + iter->mMagnMin); + } add (*iter, param); } diff --git a/apps/openmw/mwmechanics/magiceffects.hpp b/apps/openmw/mwmechanics/magiceffects.hpp index 2f61d7eeb..b80b5a863 100644 --- a/apps/openmw/mwmechanics/magiceffects.hpp +++ b/apps/openmw/mwmechanics/magiceffects.hpp @@ -67,7 +67,8 @@ namespace MWMechanics void add (const EffectKey& key, const EffectParam& param); - void add (const ESM::EffectList& list); + void add (const ESM::EffectList& list, float magnitude = -1); + ///< \param magnitude normalised magnitude (-1: random) MagicEffects& operator+= (const MagicEffects& effects); diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index 32acca2f0..e2da7cdc8 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -1,22 +1,19 @@ #include "spells.hpp" -#include "../mwworld/esmstore.hpp" +#include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/esmstore.hpp" + #include "magiceffects.hpp" namespace MWMechanics { - void Spells::addSpell (const ESM::Spell *spell, MagicEffects& effects) const - { - effects.add (spell->mEffects); - } - Spells::TIterator Spells::begin() const { return mSpells.begin(); @@ -30,7 +27,7 @@ namespace MWMechanics void Spells::add (const std::string& spellId) { if (mSpells.find (spellId)==mSpells.end()) - mSpells.insert (std::make_pair (spellId, -1)); + mSpells.insert (std::make_pair (spellId, static_cast (std::rand()) / RAND_MAX)); } void Spells::remove (const std::string& spellId) @@ -55,7 +52,7 @@ namespace MWMechanics if (spell->mData.mType==ESM::Spell::ST_Ability || spell->mData.mType==ESM::Spell::ST_Blight || spell->mData.mType==ESM::Spell::ST_Disease || spell->mData.mType==ESM::Spell::ST_Curse) - addSpell (spell, effects); + effects.add (spell->mEffects, iter->second); } return effects; diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index b4d058896..e00ac259f 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -21,7 +21,7 @@ namespace MWMechanics { public: - typedef std::map TContainer; // ID, magnitude + typedef std::map TContainer; // ID, normalised magnitude typedef TContainer::const_iterator TIterator; private: @@ -29,8 +29,6 @@ namespace MWMechanics TContainer mSpells; std::string mSelectedSpell; - void addSpell (const ESM::Spell *, MagicEffects& effects) const; - public: TIterator begin() const; From 660d73fd888f185b31bff036298a1d405060944f Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 13 Jan 2013 06:56:58 -0800 Subject: [PATCH 342/378] Store the NIF index in the Record object --- components/nif/nif_file.cpp | 11 +++++------ components/nif/record.hpp | 3 ++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 3db34e0cc..6b024ab68 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -39,9 +39,8 @@ //#include //#include -using namespace std; -using namespace Nif; -using namespace Misc; +namespace Nif +{ class NIFFile::LoadedCache { @@ -311,6 +310,7 @@ void NIFFile::parse() assert(r != NULL); assert(r->recType != RC_MISSING); r->recName = rec; + r->recIndex = i; records[i] = r; r->read(this); @@ -329,9 +329,6 @@ void NIFFile::parse() tree, but for the moment we ignore it. */ - // TODO: Set up kf file here first, if applicable. It needs its own - // code to link it up with the main NIF structure. - // Once parsing is done, do post-processing. for(int i=0; ipost(this); @@ -375,3 +372,5 @@ Ogre::Matrix4 Node::getWorldTransform() const return parent->getWorldTransform() * getLocalTransform(); return getLocalTransform(); } + +} diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 5c4141fa9..d5f65e83a 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -91,8 +91,9 @@ struct Record // Record type and type name int recType; std::string recName; + size_t recIndex; - Record() : recType(RC_MISSING) {} + Record() : recType(RC_MISSING), recIndex(~(size_t)0) {} /// Parses the record from file virtual void read(NIFFile *nif) = 0; From b23fcb260cc75aebd5f6f27485320e14e78c3d1c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sun, 13 Jan 2013 08:00:04 -0800 Subject: [PATCH 343/378] Use the record index for finding the NiTriShape instead of the name --- components/nif/nif_file.cpp | 6 ++--- components/nif/nif_file.hpp | 2 +- components/nifogre/ogre_nif_loader.cpp | 32 +++++++++++++++----------- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/components/nif/nif_file.cpp b/components/nif/nif_file.cpp index 6b024ab68..e6d3182ed 100644 --- a/components/nif/nif_file.cpp +++ b/components/nif/nif_file.cpp @@ -206,7 +206,7 @@ void NIFFile::parse() fail("Unsupported NIF version"); // Number of records - int recNum = getInt(); + size_t recNum = getInt(); records.resize(recNum); /* The format for 10.0.1.0 seems to be a bit different. After the @@ -218,7 +218,7 @@ void NIFFile::parse() we do not support or plan to support other versions yet. */ - for(int i=0;ipost(this); } diff --git a/components/nif/nif_file.hpp b/components/nif/nif_file.hpp index ded3f7362..e8884cd4d 100644 --- a/components/nif/nif_file.hpp +++ b/components/nif/nif_file.hpp @@ -146,7 +146,7 @@ public: } /// Number of records - int numRecords() { return records.size(); } + size_t numRecords() { return records.size(); } /************************************************* Parser functions diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index 777deabdf..dfbc93ee9 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -695,9 +695,10 @@ class NIFMeshLoader : Ogre::ManualResourceLoader { std::string mName; std::string mGroup; - std::string mShapeName; - std::string mMaterialName; + size_t mShapeIndex; std::string mSkelName; + std::string mMaterialName; + std::string mShapeName; void warn(const std::string &msg) { @@ -805,7 +806,8 @@ class NIFMeshLoader : Ogre::ManualResourceLoader Ogre::VertexDeclaration *decl; int nextBuf = 0; - Ogre::SubMesh *sub = mesh->createSubMesh(shape->name); + Ogre::SubMesh *sub = ((mShapeName.length() > 0) ? mesh->createSubMesh(mShapeName) : + mesh->createSubMesh()); // Add vertices sub->useSharedVertices = false; @@ -912,13 +914,13 @@ class NIFMeshLoader : Ogre::ManualResourceLoader bool findTriShape(Ogre::Mesh *mesh, Nif::Node const *node) { - if(node->recType == Nif::RC_NiTriShape && mShapeName == node->name) + if(node->recType == Nif::RC_NiTriShape && mShapeIndex == node->recIndex) { - handleNiTriShape(mesh, dynamic_cast(node)); + handleNiTriShape(mesh, dynamic_cast(node)); return true; } - Nif::NiNode const *ninode = dynamic_cast(node); + const Nif::NiNode *ninode = dynamic_cast(node); if(ninode) { Nif::NodeList const &children = ninode->children; @@ -942,7 +944,7 @@ public: NIFMeshLoader() { } NIFMeshLoader(const std::string &name, const std::string &group, const std::string skelName) - : mName(name), mGroup(group), mSkelName(skelName) + : mName(name), mGroup(group), mShapeIndex(~(size_t)0), mSkelName(skelName) { } virtual void loadResource(Ogre::Resource *resource) @@ -950,15 +952,14 @@ public: Ogre::Mesh *mesh = dynamic_cast(resource); assert(mesh && "Attempting to load a mesh into a non-mesh resource!"); - if(!mShapeName.length()) + Nif::NIFFile::ptr nif = Nif::NIFFile::create(mName); + if(mShapeIndex >= nif->numRecords()) { - if(mSkelName.length() > 0) - mesh->setSkeletonName(mSkelName); + mesh->setSkeletonName(mSkelName); return; } - Nif::NIFFile::ptr nif = Nif::NIFFile::create (mName); - Nif::Node const *node = dynamic_cast(nif->getRecord(0)); + Nif::Node const *node = dynamic_cast(nif->getRecord(mShapeIndex)); findTriShape(mesh, node); } @@ -999,9 +1000,12 @@ public: if(node->recType == Nif::RC_NiTriShape) { const Nif::NiTriShape *shape = dynamic_cast(node); + mShapeName = shape->name; Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); - std::string fullname = mName+"@shape="+shape->name; + std::string fullname = mName+"@index="+Ogre::StringConverter::toString(shape->recIndex); + if(mShapeName.length() > 0) + fullname += "@shape="+mShapeName; if(mSkelName.length() > 0 && mName != mSkelName) fullname += "@skel="+mSkelName; @@ -1013,7 +1017,7 @@ public: *loader = *this; if(!(flags&0x01)) // Not hidden { - loader->mShapeName = shape->name; + loader->mShapeIndex = shape->recIndex; loader->mMaterialName = NIFMaterialLoader::getMaterial(shape, fullname, mGroup); } From c138e00aa250b1cca9cd8484b085df6830218685 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Sun, 13 Jan 2013 17:05:12 +0000 Subject: [PATCH 344/378] objects scripts are now stopped when they are removed from a container --- apps/openmw/mwbase/world.hpp | 4 ++++ apps/openmw/mwworld/localscripts.cpp | 14 ++++++++++++++ apps/openmw/mwworld/localscripts.hpp | 3 +++ apps/openmw/mwworld/refdata.cpp | 6 ++++++ apps/openmw/mwworld/worldimp.cpp | 5 +++++ apps/openmw/mwworld/worldimp.hpp | 3 +++ 6 files changed, 35 insertions(+) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index a576912c0..2ce7ce2c7 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -45,6 +45,7 @@ namespace MWWorld class Ptr; class TimeStamp; class ESMStore; + class RefData; } namespace MWBase @@ -138,6 +139,9 @@ namespace MWBase virtual std::string getCurrentCellName() const = 0; + virtual void removeRefScript (MWWorld::RefData *ref) = 0; + //< Remove the script attached to ref from mLocalScripts + virtual MWWorld::Ptr getPtr (const std::string& name, bool activeOnly) = 0; ///< Return a pointer to a liveCellRef with the given name. /// \param activeOnly do non search inactive cells. diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index 9c9131008..2fa0d4086 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -146,6 +146,20 @@ void MWWorld::LocalScripts::clearCell (Ptr::CellStore *cell) } } +void MWWorld::LocalScripts::remove (RefData *ref) +{ + for (std::list >::iterator iter = mScripts.begin(); + iter!=mScripts.end(); ++iter) + if (&(iter->second.getRefData()) == ref) + { + if (iter==mIter) + ++mIter; + + mScripts.erase (iter); + break; + } +} + void MWWorld::LocalScripts::remove (const Ptr& ptr) { for (std::list >::iterator iter = mScripts.begin(); diff --git a/apps/openmw/mwworld/localscripts.hpp b/apps/openmw/mwworld/localscripts.hpp index 028dcdeda..840243fff 100644 --- a/apps/openmw/mwworld/localscripts.hpp +++ b/apps/openmw/mwworld/localscripts.hpp @@ -10,6 +10,7 @@ namespace MWWorld { struct ESMStore; class CellStore; + class RefData; /// \brief List of active local scripts class LocalScripts @@ -47,6 +48,8 @@ namespace MWWorld void clearCell (CellStore *cell); ///< Remove all scripts belonging to \a cell. + + void remove (RefData *ref); void remove (const Ptr& ptr); ///< Remove script for given reference (ignored if reference does not have a scirpt listed). diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp index 5630bfd5c..4be287810 100644 --- a/apps/openmw/mwworld/refdata.cpp +++ b/apps/openmw/mwworld/refdata.cpp @@ -6,6 +6,9 @@ #include "customdata.hpp" #include "cellstore.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + namespace MWWorld { void RefData::copy (const RefData& refData) @@ -107,6 +110,9 @@ namespace MWWorld void RefData::setCount (int count) { + if(count == 0) + MWBase::Environment::get().getWorld()->removeRefScript(this); + mCount = count; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f49b4bf9b..716cd6e96 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -341,6 +341,11 @@ namespace MWWorld return name; } + void World::removeRefScript (MWWorld::RefData *ref) + { + mLocalScripts.remove (ref); + } + Ptr World::getPtr (const std::string& name, bool activeOnly) { // the player is always in an active cell. diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7e89c1d87..905e6fd96 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -172,6 +172,9 @@ namespace MWWorld virtual std::vector getGlobals () const; virtual std::string getCurrentCellName () const; + + virtual void removeRefScript (MWWorld::RefData *ref); + //< Remove the script attached to ref from mLocalScripts virtual Ptr getPtr (const std::string& name, bool activeOnly); ///< Return a pointer to a liveCellRef with the given name. From 6fc64e8a4e9d374040f29343029ec197f5323cdd Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Sun, 13 Jan 2013 19:49:56 +0000 Subject: [PATCH 345/378] scripts run for items added to containers, and scripted items in player inv handled correctly --- apps/openmw/mwworld/containerstore.cpp | 28 +++++++++++++++++++++++++- apps/openmw/mwworld/containerstore.hpp | 1 + apps/openmw/mwworld/localscripts.cpp | 2 +- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index db4537daf..6884aa6c8 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -15,6 +15,8 @@ #include "manualref.hpp" #include "refdata.hpp" #include "class.hpp" +#include "localscripts.hpp" +#include "player.hpp" namespace { @@ -71,6 +73,30 @@ bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2) } MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr) +{ + MWWorld::ContainerStoreIterator it = realAdd(ptr); + MWWorld::Ptr item = *it; + + std::string script = MWWorld::Class::get(item).getScript(item); + if(script != "") + { + CellStore *cell; + + Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + // Items in players inventory have cell set to 0, so their scripts will never be removed + if(&(MWWorld::Class::get (player).getContainerStore (player)) == this) + cell = 0; + else + cell = player.getCell(); + + item.mCell = cell; + MWBase::Environment::get().getWorld()->getLocalScripts().add(script, item); + } + + return it; +} + +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::realAdd (const Ptr& ptr) { int type = getType(ptr); @@ -162,7 +188,7 @@ void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const MWWor } ref.getPtr().getRefData().setCount (std::abs(iter->mCount)); /// \todo implement item restocking (indicated by negative count) - add (ref.getPtr()); + realAdd (ref.getPtr()); } flagAsModified(); diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 1297fc53c..e274ee130 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -52,6 +52,7 @@ namespace MWWorld int mStateId; mutable float mCachedWeight; mutable bool mWeightUpToDate; + ContainerStoreIterator realAdd (const Ptr& ptr); public: diff --git a/apps/openmw/mwworld/localscripts.cpp b/apps/openmw/mwworld/localscripts.cpp index 2fa0d4086..5ec5ca9b5 100644 --- a/apps/openmw/mwworld/localscripts.cpp +++ b/apps/openmw/mwworld/localscripts.cpp @@ -134,7 +134,7 @@ void MWWorld::LocalScripts::clearCell (Ptr::CellStore *cell) while (iter!=mScripts.end()) { - if (iter->second.getCell()==cell) + if (iter->second.mCell==cell) { if (iter==mIter) ++mIter; From aa514a53d998462695256aa121e77df0a03cc67e Mon Sep 17 00:00:00 2001 From: lazydev Date: Tue, 15 Jan 2013 02:22:14 +0400 Subject: [PATCH 346/378] Console font change to provide possibility to show cyrillic symbols --- Bitstream Vera License.txt | 123 ------------------------------ Dejavu_lgc_font_license.txt | 99 ++++++++++++++++++++++++ credits.txt | 4 +- files/mygui/CMakeLists.txt | 2 +- files/mygui/DejaVuLGCSansMono.ttf | Bin 0 -> 285304 bytes files/mygui/VeraMono.ttf | Bin 49224 -> 0 bytes files/mygui/openmw_font.xml | 2 +- 7 files changed, 103 insertions(+), 127 deletions(-) delete mode 100644 Bitstream Vera License.txt create mode 100644 Dejavu_lgc_font_license.txt create mode 100644 files/mygui/DejaVuLGCSansMono.ttf delete mode 100644 files/mygui/VeraMono.ttf diff --git a/Bitstream Vera License.txt b/Bitstream Vera License.txt deleted file mode 100644 index 2b37cc1df..000000000 --- a/Bitstream Vera License.txt +++ /dev/null @@ -1,123 +0,0 @@ -Bitstream Vera Fonts Copyright - -The fonts have a generous copyright, allowing derivative works (as -long as "Bitstream" or "Vera" are not in the names), and full -redistribution (so long as they are not *sold* by themselves). They -can be be bundled, redistributed and sold with any software. - -The fonts are distributed under the following copyright: - -Copyright -========= - -Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream -Vera is a trademark of Bitstream, Inc. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of the fonts accompanying this license ("Fonts") and associated -documentation files (the "Font Software"), to reproduce and distribute -the Font Software, including without limitation the rights to use, -copy, merge, publish, distribute, and/or sell copies of the Font -Software, and to permit persons to whom the Font Software is furnished -to do so, subject to the following conditions: - -The above copyright and trademark notices and this permission notice -shall be included in all copies of one or more of the Font Software -typefaces. - -The Font Software may be modified, altered, or added to, and in -particular the designs of glyphs or characters in the Fonts may be -modified and additional glyphs or characters may be added to the -Fonts, only if the fonts are renamed to names not containing either -the words "Bitstream" or the word "Vera". - -This License becomes null and void to the extent applicable to Fonts -or Font Software that has been modified and is distributed under the -"Bitstream Vera" names. - -The Font Software may be sold as part of a larger software package but -no copy of one or more of the Font Software typefaces may be sold by -itself. - -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL -BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, -OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT -SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. - -Except as contained in this notice, the names of Gnome, the Gnome -Foundation, and Bitstream Inc., shall not be used in advertising or -otherwise to promote the sale, use or other dealings in this Font -Software without prior written authorization from the Gnome Foundation -or Bitstream Inc., respectively. For further information, contact: -fonts at gnome dot org. - -Copyright FAQ -============= - - 1. I don't understand the resale restriction... What gives? - - Bitstream is giving away these fonts, but wishes to ensure its - competitors can't just drop the fonts as is into a font sale system - and sell them as is. It seems fair that if Bitstream can't make money - from the Bitstream Vera fonts, their competitors should not be able to - do so either. You can sell the fonts as part of any software package, - however. - - 2. I want to package these fonts separately for distribution and - sale as part of a larger software package or system. Can I do so? - - Yes. A RPM or Debian package is a "larger software package" to begin - with, and you aren't selling them independently by themselves. - See 1. above. - - 3. Are derivative works allowed? - Yes! - - 4. Can I change or add to the font(s)? - Yes, but you must change the name(s) of the font(s). - - 5. Under what terms are derivative works allowed? - - You must change the name(s) of the fonts. This is to ensure the - quality of the fonts, both to protect Bitstream and Gnome. We want to - ensure that if an application has opened a font specifically of these - names, it gets what it expects (though of course, using fontconfig, - substitutions could still could have occurred during font - opening). You must include the Bitstream copyright. Additional - copyrights can be added, as per copyright law. Happy Font Hacking! - - 6. If I have improvements for Bitstream Vera, is it possible they might get - adopted in future versions? - - Yes. The contract between the Gnome Foundation and Bitstream has - provisions for working with Bitstream to ensure quality additions to - the Bitstream Vera font family. Please contact us if you have such - additions. Note, that in general, we will want such additions for the - entire family, not just a single font, and that you'll have to keep - both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add - glyphs to the font, they must be stylistically in keeping with Vera's - design. Vera cannot become a "ransom note" font. Jim Lyles will be - providing a document describing the design elements used in Vera, as a - guide and aid for people interested in contributing to Vera. - - 7. I want to sell a software package that uses these fonts: Can I do so? - - Sure. Bundle the fonts with your software and sell your software - with the fonts. That is the intent of the copyright. - - 8. If applications have built the names "Bitstream Vera" into them, - can I override this somehow to use fonts of my choosing? - - This depends on exact details of the software. Most open source - systems and software (e.g., Gnome, KDE, etc.) are now converting to - use fontconfig (see www.fontconfig.org) to handle font configuration, - selection and substitution; it has provisions for overriding font - names and subsituting alternatives. An example is provided by the - supplied local.conf file, which chooses the family Bitstream Vera for - "sans", "serif" and "monospace". Other software (e.g., the XFree86 - core server) has other mechanisms for font substitution. diff --git a/Dejavu_lgc_font_license.txt b/Dejavu_lgc_font_license.txt new file mode 100644 index 000000000..254e2cc42 --- /dev/null +++ b/Dejavu_lgc_font_license.txt @@ -0,0 +1,99 @@ +Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. +Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) + +Bitstream Vera Fonts Copyright +------------------------------ + +Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is +a trademark of Bitstream, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of the fonts accompanying this license ("Fonts") and associated +documentation files (the "Font Software"), to reproduce and distribute the +Font Software, including without limitation the rights to use, copy, merge, +publish, distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to the +following conditions: + +The above copyright and trademark notices and this permission notice shall +be included in all copies of one or more of the Font Software typefaces. + +The Font Software may be modified, altered, or added to, and in particular +the designs of glyphs or characters in the Fonts may be modified and +additional glyphs or characters may be added to the Fonts, only if the fonts +are renamed to names not containing either the words "Bitstream" or the word +"Vera". + +This License becomes null and void to the extent applicable to Fonts or Font +Software that has been modified and is distributed under the "Bitstream +Vera" names. + +The Font Software may be sold as part of a larger software package but no +copy of one or more of the Font Software typefaces may be sold by itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, +TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME +FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING +ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE +FONT SOFTWARE. + +Except as contained in this notice, the names of Gnome, the Gnome +Foundation, and Bitstream Inc., shall not be used in advertising or +otherwise to promote the sale, use or other dealings in this Font Software +without prior written authorization from the Gnome Foundation or Bitstream +Inc., respectively. For further information, contact: fonts at gnome dot +org. + +Arev Fonts Copyright +------------------------------ + +Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the fonts accompanying this license ("Fonts") and +associated documentation files (the "Font Software"), to reproduce +and distribute the modifications to the Bitstream Vera Font Software, +including without limitation the rights to use, copy, merge, publish, +distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to +the following conditions: + +The above copyright and trademark notices and this permission notice +shall be included in all copies of one or more of the Font Software +typefaces. + +The Font Software may be modified, altered, or added to, and in +particular the designs of glyphs or characters in the Fonts may be +modified and additional glyphs or characters may be added to the +Fonts, only if the fonts are renamed to names not containing either +the words "Tavmjong Bah" or the word "Arev". + +This License becomes null and void to the extent applicable to Fonts +or Font Software that has been modified and is distributed under the +"Tavmjong Bah Arev" names. + +The Font Software may be sold as part of a larger software package but +no copy of one or more of the Font Software typefaces may be sold by +itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL +TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +Except as contained in this notice, the name of Tavmjong Bah shall not +be used in advertising or otherwise to promote the sale, use or other +dealings in this Font Software without prior written authorization +from Tavmjong Bah. For further information, contact: tavmjong @ free +. fr. + +$Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $ diff --git a/credits.txt b/credits.txt index e1905c88b..878cbf915 100644 --- a/credits.txt +++ b/credits.txt @@ -118,5 +118,5 @@ for the open-source EB Garamond fontface. Thanks to Dongle, for his Daedric fontface, see Daedric Font License.txt for his license terms. -Thanks to Bitstream Inc. -for their Bitstream Vera fontface, see Bitstream Vera License.txt for their license terms. +Thanks to DejaVu team, +for their DejaVuLGCSansMono fontface, see Dejavu_lgc_font_license.txt for their license terms. diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 871be93d1..e7e5b695e 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -80,7 +80,7 @@ set(MYGUI_FILES openmw_travel_window.layout openmw_persuasion_dialog.layout smallbars.png - VeraMono.ttf + DejaVuLGCSansMono.ttf markers.png ) diff --git a/files/mygui/DejaVuLGCSansMono.ttf b/files/mygui/DejaVuLGCSansMono.ttf new file mode 100644 index 0000000000000000000000000000000000000000..80c45b73e9d1d8990a842ca3688579a403556bce GIT binary patch literal 285304 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%0AN*c~gCA(Zc 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 z zySW9`H4V2~Tw`{425q-u@%dlPRCTlvT$KXGF?mAdH zCfJap?V@=7wy3kF?YDsBn~OZ;wQvLin&Z(fi3 zvYtgv2PYKN4h_uef9CT=!u$U1nK~YGGr8~vvK)nZ{eHaD8$RO#c4Qj7uRv4^?XaLX~ob+P@ zCq6HWg3*O(3wbEF|q>Pw+-EiX(l8XSc^HxI64WAMAqTg8u+ ztFX2!n2V2##vjfTvcoM2El08V@huxxUp1$qqM~H(?6n(i8Qn5ytv@~8->bfP$hfIh zWmnB!d&92X>#tidr?M(iR&;vBwL7+5yJF_F@^aRm7xKzbD4YfS3?iBgjl#~61n@$p z?wmbCV}CbpNOOHJhJWp#meIFtSUY=e39_DZ)#?pXBW2~&X0Et)+m35joW`APRh4rV zT)ScSuIty$zOuZE3X{L@xPF{M;;|t{e_p4T!FNcl|gR*Sm1YnyFpj`QC5>ct{~d07gkp7>=i#KE;-Sh@oKsUt!V|KLLYn>y+P9<)60Y1N$z*K_b9I%gkj3dB~`x6*1 zf^z~)OI{~mQU?wTe@uOGFplq)&Ib4tICTM^B+@xJ60nclOvgSY_7(eew6p&DYiCbM zRKCXGWPE=mcLVGu+(bA2DX|{|10W$U($7VWauMc<0#@7+CVLVZPmKJ>KSrK7KJxF< zvA>Tz&g=Uk{U_kqoqGy)V>J^>37cg&X%ZmM*D^>#T(n(mSB}Tsz6#vAm%%O?!1+~n z*D;>3*yA0$#Zd+4$@p;;i|w}@yk_9koSbP**Brd1a7sPE!eN6 zSM=&LVt6pn>x$BQkM-;s&AKW-|H_u0J&)b{@{sF3*|O5*XlU5_@ya2q8hgI@?%I;l zRWmlc{bFM;)|SIazO+L89I|WJKnDm+ML}Ga(|bQ9s+dvup8oo&uz}u7R0jw+Fu*TR z+$4>YmNQ-pat!zc2#KcHl5)gO_RlCs>NC)looP4yX?Nq@c$mv4Ogucs$YeEMI!%>7B0v`(tQ zU7nBwV9GF($uEoGG!PcjkbFgO0<~6v#21at7hAYk!MrC9X&wBC&^MAT=7?W!P{S3T zeq(H+)$fLrPL11d74F=qhu226L67s?7PxjY2+xI07Cn+^@RHfPzzK?elOfe&!uwR? zM!f}o0!?Pbam)r@#$n_L+A1vbu-G1;t@^|a!oOf%`=an#1zaw$hGKQU2KURdn_3mD zvq}LsqT`I1Ne0qG!o2;z!b`_Nrtc{u3t@-PJPa}(-vVeH&c+QC{0mN+r6gw$AO5bj zb!Tp_@GX5EZI0s{LMFQw`oi}hZKp?pZy4E&@+G0olX3qJ2n8u+T5yy!ft3IDS$+8M zYWivA0}p&QeE61VRJ?!EJLAT`Gl@JvhhO;ev9Si@?nk*EyfoeFn@7^_=k*m76UhQ; zu9SjRkJmWl)Cxi}*&o|Hmp&+&&wndCck(1G@E@cPq8S0Tl-whYkb-e|b;u8R)6>E* znnIrz-|YwqpFQ&o;rTW9V&1PeKaa|Bju zP1NZMAj!aPclfx{r}=P@}=3F&ir>AMveog z7k&drsp6=#7rwywc*Yr>atMbAZjSYb;OS0ckGv$##l@^GKsvjWK!(F zCh{#S&dpnC`xf`}i4w7TU9Najx=MUN#%C?{v-^s15p~3mGZQ#95xShbXPitv+|n>6 zc7~jYomnYlw$7!k!fi{POX+7z>4}?&_}We9&of-h74J$d(m+_*n8C3}Knu`F?A{{Y zN@n5aYC3lSd8wIPF@WB5IVqxlrDgA4y_0-%HN9ykcG3N>CPk#^YPtcqS`@3KTBB(5mHu19EeU)(iFmgqR-qak9-5GjxxWJ1SyF*m}jKZO~ zm*q75{DfRS3?2a*ofu@?g$bPHe%xd%7czl!@4T=uGdzqe2-8i&Vpp|~6lRk$`ciDW zF!%hxoF=*n{}-QsYB5%j^2KcI;trHQOZCzpxkQICtYFzMX1n5hXI|zT)64pgZk;}9 zJWMY`;c!M%pRujeCL>hrCx2&Liui}Bvi{+4IMCFuwY6>X*x>`=KoX&%9xLJdX2ErA z4rmpl&^C)jSom=oX~bPM8IW;CvG>Ahqhj3W-0fQ$LG#B+o*HXp1pXv1dDKM?;{`TX;1*S2!S!X8TVKtiSCiH+=zW;% zqsh%%E6c85kfFkBoZ;C|M&*A&|L{4hvj{qV3Uqf?oQ`)<@lvH4t6j}3+$)lVUbv)*+R~D0W5~)F0ER=t*S@ez<`~ebNT#@@cl|Rv*VQE zXxQU{U5n8}=BL4Ty*`ELL_>O=>hYkeHp3tgE$m8QZbas%8(_TX^M#NkGwh|CJZ=~! z`Ms(1XIsYL+S=+Zayg@9z4QmMH$Q`;;x~%=GC@ndO zw!;9}0Xq;{syC*l1k*#I^k7P=QBO|7GQf=C6JaDhy)|SKpGkC@cQcEnfaW}*T1Tw>;~zU z`PY|KRrOD|oO8pd%a`tP(m%LyJ4hgs;qmBMS~AK3IX3Dt)J;%$$Q9CI@nQIh0Tp4A z{R^}VwP5Z}SOsu{TcR3_i;6#cN>frz>FEKeDhspLu;bBdaz|%nrRQ1TOjzvg1utpz z4u0j8!Td?s>dP-SdEjW*k~*xmb{GumQ(*C&?@JE^v$8?~-|=zdj{5?ktgK)_9o@$Lm#U@wifW%#aN15?r%N%4D^u9SXPn9Iq%y z@U49lY`fr*TnF*zV@eag#w6s)iXdojV(zzrP)>l9qBQVl>Oz~}kp*z}@dVt>a48%lAZAgqYDmyoLF z?KMM0^Kx*w$wc+~CfZ9Sk={yCI0#E>C4G;z0;&$rgX)7SbO0RrAt%;m$2DT2&AvpN z{o1|FUQe_c=Au%qI*8PAgNhP;Pl<^wg4alv?!f&KR(LkZX7mRz0X_pT!1D=HN09&< zAK17cj;pxwh{%X^$Mm|H<;|L!#h$V<&*B10KK^NvkOG^4qo$3+RdOTC7_AM@rq7>kZE9gOS zk17lerUIANMuK8k!KH+d)|5d*g!(vOFTY3IRt~=egv^n|@m0C&icfz)A66%mwC3J6 zdN64iX?{@rY3Shfg=YwP`L2$pt?%`-6M^9`2mal0{H3^LFBl0!3x&y{W-FLVp%#>? z3{r0E-AvMw)%4*HP8YAstr`!*&pGhy+poNh9^5Jrd%yR#iihudnUFJu>jw|*NWz|G z7!*xdYiU3lt6GRB71Ic5xv&LWjUQ<;)sr9SF5with0KWMs}`Pr_x$Tpu2>k;bZik> z*>0f!5(lU(Ojg20IRUbkK+O7g{1H|$T~)_byp1k`W5OZvGWz!e5PAF^a2;2P+r;?# z(p44(91+LMkjNM7_bB)yTrAy;F#sJZy z;{iAozz=!y0Q-^WUIGU^rCAw{#7A~r#&B_bFYVul&;0rPXP%5s#?=iiM-3g%PQ`<` zo3_QIVmAkYPOCZ}kxs_%mQ+Al<`<#QyGH!D!#7WQHb}mU-7cLt--DIs5?Jdcu!43R#vTB-b=#o>#;(#cm^=i}dx5AHeq-G~?F;h*&E{MJ1P?in#}#mnT8zI|}u(#g-{++lz;fxZ>lLtBCZ`RU=wrrw2Ce*gk0Bz(L!0G$|Xl|C;^G z?8?elPeJNCo$SfYq1VOky>-vt-2~U-?Y?ynGEt16Bc#QsFXS0)D}l+xq_<2-$)%ZC z%EhPXx!v@q$8pQI00ppdbnCsZ9Bs|bQARqx7OlU&tvt;x=la!oe8$YZwY5xV6N1p` zkH9Hl*QyA?fd%+~T+3Q4%s367$%B8Q<)^L?l4hSeD0Fl*D^Js<UlRe##S>Mvw>tN%^gm8a$E)9rLOotB$Jt`o+v>eJWhDPDcl z9}O?{Ls=p3wL*JuhgZw>Cxstl$tO{-y$52IfH@ABRB0lY38R4+7+KohF5DrAv6wWG zR_>H(Llb-kU5_*WI<76tuqW(m*@X>e6M7PN>hb6?=Pzx7nM~n z>Ho(g11Fz)|ImSVuf8@T_+(!6%*(>I=|guv2+2BIYt~~t_LG*P-fTaN(}*gYqo}Mz zfkS>mx?m!vbWDbL4>`+(Kix2X?AnQRK6&lub2Rsy&=GrH%~<1TI@IzFbXyF6Y?+}QePg)7&pNigi$Ed&41Bp7XB%=S=0hHfw3ugNx-o06<<`! zvMjhd#b^@R-d{t%zLkExMmR5g-tnY})q*HCQj@e5^QQsoc@9Ph)>%* zF(1PpY(~6gO2Cb9o*RZD4)MpakB#4Y-<%yYUweAZ%%iIw-?nOCo!kB|{y||Qs>#-A7Op%k7JOSki>=k~F`NS7H zew=ps8@WA)4$d4pFTH8U8I&Vd8u`*q4u>&_P_NBz40f51t>bsbaw58#CF*2Q-HAHb0(*U^`M z3ogXU2ND-A-@0`uB1-q$^}wVP&L22HA7fCwBL5REUXlL^7q7_wgo{_?Kj2tB{N2jO zN{BI==nL>#hy`6Cht~;q_Kw(EbxbWFJj24x8` z3Rg@Hlka0|d6TnzS}?zsDL-TUB)ri=ITnE0OK7S(*L2g&?M=$h-tVSUU%Jf z$v6-l-6A&98((+n3;6hz5-P zqslj+k5Kqa(}nog>QFnZ7O=U?Y;+A-=A~B&gALjT+f}-|g~;9H*?n}==ac6!XH={m zt^khPLFfMyhtmSrzb!WUN7CY@e-z**a?MtiZZ}!Bo22fd|G0hN=qD*7nndwWa#H*e zG>EmO2|C77li!|B?p=7#Mo}AkM0|zP*YZf=8$$ZK0B7)u5#o=avA+!uH{A*NKg;mz zv0k(F%8ta{S81?}d>EOfer$pM0~YAmTPIHnS<2I0OSABNtdgy20wb%)`#5m9v0iu( z02c#t$#HNms`l>4#Yr>guH4w(Ubuh$1NSrZj{&`Ku#3^u||(3e%4#3b?h|Qx77+ zX@PC|!0)fe`rW8JZBdEiSy>dfq247bAJ)lqXm4#YI~OiB2VgbXKqbnj zXrWI@J5W9xw*vEnLC*#=hVg8cjV4F}$%n&ZmT=ZmFRemzZXq^I=C`C_D7LO+ql zV=OQaS8^IG+lg+m^KKDe+qCncZk-_{n40nNB3^PPj)FHHO+O}=e=55e&&<G$j z^Ypv#o__P4y@Z~kpU{uFlF;}0nP~o&!e!gZ&b9rqf(zj06%tlH^o>g~s6J}bIs}X1 zh)%gl5>6;?m2TA?GbO8BT1l@^5y_~OIl#jToS$wabi)dKd_5Qiu( zGxj#?dpmfsc&WMy#mW>f5i15S591pXrIE_p>FLP>Nr=8Hm&NwWSIVU*%;v6=h>GUU z7HNe#1>>A)8Nho(LuZEYjR|1EQn6hH&RNTyjvWY>zA2@skb?nzK5;wspA2r#`7pq0SBib&|86;&Va}Kmqzh$77tHD`XM|$Y8#)EpkqjnNs91`fLHM_R!)THbbcy6$-}u_ z@GtT(cwNN51^D?qoX^7}5XL?R_zHM9PJ`ngYtP#)Ze;H3-#>2=}+Q;LuZ$pUf)jWKm{UK;T{9eT0$m45x_tP83!)`2@FJsAk<7E(5F(9NrH+PR}8eNCAx=v{j zZ-kvSTdRlG>m|hpDnX;XTq2h%G^RH?_)7amkFSaf6ayYMw2b;Q6o~a8tr~@<^XUq6 zdBteL-Vbq9EO;-CmAOC!h@*Mp)FNY8HKd3wl&gE3S*I_b>)?_tNh#Ki-XR`HWx5jHCMupmu z0w2OC+Z!j$Se>w5ncnEpfo3r~;oi$xDL2l5ht0eZyu5r~g*Q>Y1zOO2oEpcyzPOtY zzQK)Vqs3@7+KhIi!{{`oB)L)*aKW17N%rWy2A|Pq@|k@WpVepc*?kV5)0g5m;ubBV zXq1eQ%`-%aq;iFQl27SN@+JH9i3BOEw%n5ime2s!%_6=#=B8!ETwEYbsdV|01DVUG z4-eK9RyqPkU*>XQT1!R`Wu@PcRFtcly2#}A>XVDJR8y`tq0bg$T%F^6HseuTYw`c4 z&-kceeSB$O>d|GaA6XxYm~aH5IHB;_J)g23BO2ak<Gir zru**Qr2IEMckm$E;X?j1>TwY>5Q$3CmH&yN_;^6|F3l$~v@b1Jy^r;;j`uI?(NvN3 zC>z94F<;788pjb_Xjxtik1(%qy7#_KuS?$$-NAztln3>2lYdLC;!MVe)$1^L1xZlg zR0nGi-XiZ|-dl0(tvE#r0(Lf0&F1ZxuUabZQ_q34RzYe>y2BaZf)p6xB;8KZ??}4Z zc=@pADaop(*DP6jbwzn)8S0)$qgVJ2sufZ@5$mPpm>SdY6^>~@(jok$Mj>TMGg0FfO z8$Eo^3yLRHlf}hx9PrK>5dHX)h0pH!ocw}630RCafq+(vJFv$KGRsWZ<8Y=8SC#PN z@sKo?f`Mb|P$qWbjGLWZwIV!t%xWu#$Hi!nyHkWy!{AagpcQt+rG!G;yrytu z?5j7^Gk@fMVk#341a?uuEVW zEr;WO4^VLhq3)OLDgN4i@#BR=nwNTi4})wU3ugg7q|Z%ixunrk`iaw#~44gjE2n zo|#=Tw&Fd%S%eJ@Ls+nckpH<9y6}T~V5) zB1MGqo-O9y%iThyz&eXDT(;z?}e{xVvcN=w=R0_=>S3$ACcu^I^`8A7?h{lN+d;HDGJhOy(bY*G_sBn^=4z0-Vj&PE~vkW3JjU`#UC!0Y8K;w&?q(@p>a|Qb@sjF|VtFNzXuYWHy%~O#TO>Y>_ZIWJO-_cNeKqAylD+g)Pj>m-3#}d-G*vZErQMyH% z(IxGZW}Lr;wW&rt1Pmi-XX<^s=-ChV$aHMYlQWmP1b5#xOBY{B zNM-fMar)KJG_xM}U@3fbxwL@Os|sM{R`<>55eg)7~H@1A2{InOn>VSL@Z& z?(EodX!UkN{xWm<;WJO{?w9#6-w*Ys$DZIfB4Ct*N?xOENY)b zgcG|yAi{@JOGJki;ubbH<#Hd0+p#LF|D%BM$u;V~7KqGblU0oT_xyzhc{WRzlmT*| z|ASH_7Uus^lEsO2`+t>2tWK=!Lg&j=gFDAm*dt(1Ei=zaeCYCn^{_`%!p(P39D8iT zs#Tj#%+i&%7iqHSqx4(2o`3Q^MW^sI>2v%zeLVI~RlgPM7OiQnrH}q;tG4~Rn^>C* zoku$Fkv56fz}GL<^!S_r`^J}Wxai{(LJVKTOqBoUEHR09^GPuTqb;Z3Z8QFG@ZP)U z{U`{*_TV+GtFLW&vY;V+VUqBLowVmPRhFg-Ek(`2RC-@aIP?Fa?R@~VoZiR(>zw=l zBN>vV=+PfWt0u`xX{%KmtEN^KVXLjST5YyfD5W-OH z_d3tBrxiZ$&-e5B{=VJ4_T2Y5*SW6i{JGCL_x;?@xsSA z;>6xd(;{R0!rf0Be`WCQ_4c3MI?AKdUggn_`|f;M{V(oXcSK9(`6KFj6&GB8$HZAb zE0t20dUrxbQT9alf@tad@mgP%U{Cq$`rUJeEjup#*s;pGVtje|1%u)`McO96q1gSH zeWhL*`QE{NGaD&`1NR#5VzVs!%KTwhExKsMdEcH;cjE1RFUlTxOTSJ->(Zs^>Dr3^ zCk?Dr2R(SzgIBE@9N*^ndnV-mQGHva?Pqe%9&e|UuY*``XirTu&PQe?bnYsWLB1q1 zCunTW5bdS(RnbhQ4or+czUP{;T~&`2-Jg1P_Y_rr`oN-$T3&p&2@5_rBmVd^ubM=8x7+=T{Z{1n zI{jl_S!QZNN7*HkeRI*UXOh|6)kM59Z%8x+74aV-H8kp^zHR=NhtIkwefLKL>jo)Z zd+O-Q;;E@8weK{4f>LJ>&s}^Xlc7}v#p`<{8$se{g|e(CWm)wOrc&pqv)uG#w7(}$eL z+mQP97&APz^3j^POV^(|rt>L3J~rY>6dPOFbLK7@TYt_0{eP8?<^o^jl_dw8E0)OT zu7SIh^Q_OEKW;ijQ$+rH({}T1ll|7NflY0kXCsPY zeR-bYJ~dwM=9d81s(kg0Op;5vi&xFJN0VeR`wO|Jc%k}+=|WCq+riUIU6Egi#EabT zp2X)Xtc9|Gd(&eHok8dfQZD4V@M}$vKx^zO%ha1PNu43-ID9BtpXW~Nd2HtIT2_mD zO14Q2TGpcHSy{k+&Udl9*4(e;o@AlCCLc4Q`PkgW)HZPo<#p*w$jgdn090$4h5yK^ z0je8&gLq%YN2H#D-wS!Heh2p2N4DqjrDYTJyx6?GLN3haNAfU(Jecizj<0Rrx)Qq@ z-&?k0uwimxwnv(`1(7@~ARlJSp%m6OZ-wNn5bKxjVIeVlvj9Cly!bCnB5ZGr9ZQDd z(<6VUsi|ovW%(5Lx3R6KWZ!S0={HJrZ>#a-Z!LLUu!nT|^2OA_Vik^jE{v48DfK8l zshb!k?Y}|Akx!G~Wz=4bwRqfy*7H2qd;QVZU(P1SKKgEvsz}ROzI!y{h05~g<>%hN8;Mq1gsQYka{_+#Dyb&A}?Q+Dy#T*k8|hw;0l2WZdc z^94CyTdJ?rG9NYdV9L(q```SZv!|AXC-qFpo6?MxlF{~5By>QeERBp$nZ#=M>E;@I zy18~@=@zKMNQuTQni4f7dA>Y|ep8YMMas(PF|8n$F7|0kw1&45T!QsVHdA&z`BwZ$ z^t{Gg&1b(aTtyk2Cu=C@I<%VhIfv~CxtA?jR+9QWN_-AEm_(a4E%YjR3C%f#)^LBP zkZm%)SMgTe1$@_XAAhUaCaEd3iyAhgZ#90KZ6-B(3H5yrb(T&)nU2;MSOxNYRxfy= zc{@km!fqe*y~^48E8j?(n8P_M`|v#Kjq(`lwS6TYQKyTs`=Ux^`%2x&b{cPaT+FwG z+tizE+tsIRXR{rnj?rDX;REV%HWPj_-?;bW0p_u)mN)+7@H(Nc?4PL)LOwCJ$vf&w zewX$U8agVjJtHXwVri_(L1!m5R^H~_K|R&G>PPH-q-L`%QyWydx>NnhiqU7QbJ+h) z9>>x^bXC*KJcu7-PK5QwINSm=_BZQKcXLN zr@m?GxcmF2j!lp4E^T^4^`SN0&6(SkmavTSpFw+Fz>(=?qvz_2v;foQX3%P8(C%Mh ze<(G)mbAyScLV3j0*-H_eisn7jc?J`^1G@#2+iXBF}>|=NM`bF&?Pc7l4?OD<@HE) z+Fi;FX&UXRH`*6q^=9+^rsT?e+S@onjfSU)yMyf&juztEg))Xuv(d9QJ&XTl-<-+Q zn!ac4Cwg~|3;AnuXYzL>k_SXWO}=b&#ByiGkV1YRa}eGxkTQCUFfHsw()Y*bOKD$k zDMcGQk8cynHQP4Y_<%^h)<(ShjPw(colDsHXgPvoBR4{pN6w1Qq+iRqV|wqWc!DvN zcDaoZBNay4tD7iJHEkqfC%<(Q5kAp7I&_86V*w>@6bfm1gN}NUt>^ z>Sf}iy}#)%qUZMqtW=2N|^D-$e4e}u>T0X_X5sb zv+`Rv*(tx8pBVC6$V}RQKgRg8IU9!Xv-SRLJ!rq?GCftc5;jB)lfTq3w&U1FvJF!i zDw%WWOj71RKSGA9Go^{J&MKXA=1kR@Ul8}Bh1L+4!`a%8bf#} zLyu_oO+RmHEXH1=VPs?+ODm5NEB)A~DcKikONQu}XvW0>5n|(EtQU`siRLrb!^b}R zGM6#@+dtD{45oLFjg;ndZKPKD*DQva7mh7Im zd!p(?Z}KZ`?`Yos$oDvrw6(ixP1?v$)uZ#H$ls<{oAutxZ-0;9cia69I*06z|C^UX zNi!+p%M-ir-2DeXeK)x(o{KNZlmP$x;7XI(?L+D-l z(F+}iWHO^}S3XT|VSYP)9Fkm|69I_61c8^kt_ZBLvDiOQM3du`{+ z6V2OG^s}e2^&-|>BO#^RMy2^2#__R{bmnh#uUXGt_HSlvHQSz;z2Zz*JNbpbzwEOO zVqP{R626bth_p#z<^_*OMz)NmKb!uPi`Xu1{#(r~%lJA;?&ilG0~vkH_7vlXna|ZW zZx@lX6Xa=Gts3~7Lz$i)Nfo^k{I|b*$DfY;DSwk6^0(t0`rYW(k}?+3^4TI~!v4)} zENmpt-R!$%(WcGTEWs6_-@I=$#cagV$BwfkgZaiavCXu6gYGK(8%UW)%X4WlcgrxY zIJ?pEK`Uk&g@m6^Yj7o9Nkqy>{@d2IIkfpRDjogHH)F11g-;}mZ(Ysb=J-DJ2SW*8 z7C9cum5sUQW42MveS)jvBU?NDn`+m*AFqE^@w$~9s9W(jj`y9gWD>1}?6<^KFwS%=1pW?$MN2hkruO03e%GQb<3j-Kg4G4gZ6@PT#fZ(TTf5WjTz}dT$%OY zYTcI-=FsM9xdZd&WcuBsz-+CV)f!qcyYSI4f@_1>Xx+fP>j*+7qoq9(?IWXx89A28 zb@B`=U0lsI>M*W(2Q%w=pKJ9Kkm=3Ly(cs6Uf4JUZ_Xxmtdz>H0Ko3>M7047hkkWpb^2o*Sp3- zhqt{cvo3hjmNdO2OlUhkk0JC>eCk5~KY-Fol5V=adY!rV$1E%++`pSFP4p5y=)+Rc zvt72UNvw}H7=Pbpd!6glSJ>LgAFSzF5urOh-e_{uhrgpKL37WbkMvO^`CGtt0`+=6 z*ImcJ*^a*i?{GMb+#Gt_13GD36M%=@1IzAdJv#v(n15qd54&%H*j z^v!*@Ww9&t|Kc;7-)tn9jrqcKenaq`(Lj%0#bZ8k?B9ZabNKAc7W+2}OaJ4$m9+N% z`5nrCyKl;MJ#(^-+&?hqX?t^LfO`fx@-+1^fL87DX#Clnv(bO_E^Oa7O^G=l`Mi!= z?id;CO^N0-ZD@L&*3*ZQYe$QW{^Pk#j$5c^pKtOMpBk(h?)>iLxsF7b55&2Zu#uN{ zw1Q+{e&`_^gn_tF0yOZtV!{!TR&^r#^Hk^lgtcAp*yiMv=qk1D_q^ zM8a(V`$w{WWHu}U(sd%dQwkv4xl}kgpo*K2@vuhZsAP#u9FHdM=z5WaEH0Nbp_VD; zN|A11s1`{~1mwFD-=jYe*OR!OE0|U`iX699q}N7%!=Cw^^IAOzkLq05nH6kNYL`I@_B(fvXI}$x3(L0j(k*h>bv!Mf! z?zBuG?Pv`o}DVbwJ$MQm6uS zjokn{MNV%6iI4{4p#l~`Ev$x3u#FpZHgo{waXK%U0s0J_Jadt43F!A77g zcwk!2h=(Ldhg_(HC9oXUz;=-{yTD43@#r5<{CMKWqi6g^XcRds4iX>*a)7jFXPKL+ z5D!U!{&UcuJ09l2Dj=V^TVbb2UK=2H4b`1CX_-IpkpdJr*4D> zF-yryaX!?GOsfX+JG~VUHhq&wNiOhNngUsX{&RgGVOcy>h?KX1^&%CdtEdF>Fe3-B zF*6RvL!HPh;%8+8K2)NkG82{n^0Uz~I}tdZ&GDRK*e-G&Y0ujvG8fso?9au{`N*AL z2HuptcnVL6cYLTp@!y?FxZuu9}2WG-3+ja&fd!$y%yl3*z`h|EXNd}QVmH@^aq zonH&9VH0cQ>3a5Bm(79MgFSRKs``43w*%m1<8;Fg)k4QVI{1GEkK^Hh=T-3 zfgC7>DyV@v*a!{g_5s905~M>eRKgNi4r@dfWx^I{6uB}E$itO8M6ODMrLY3l0r|a( z@WpL_JS-;N;(VA3$Sqz48-TnnCa+7dwPc&f)qGx!oomJeHm=P9!k6O5(v^Vhb<1Eo zk9cD9dSq`%fkN0MQk?*`A~*Jibs{&F0(x#H{mtlEmJjPiZbAMQblh4ea$B)T4e4r@ z17Ww%gB>DwsVGk1a;0nN>hjM_tKeR<;Wjc`VVe~$X z%)|3x9U%8ef2bCDRA32g6Iq4KD)c-ShFX!wk$D{XC$fRGPc(?s5m%Q2gg@CEkb8=- zr%3-adR7y+dV|O_RU*%}fi@)Ax}RSPn?zpd4amGuFS0fs z5+E7K$6C^?MQ-gzk#&iH{&ibLUQB^fm0LiQ!nzElD80Ua-+^W|kC z>sP@#o&XTY26Vnkx>x%{HPpdok=F=&4Lz@A!gwf#xv&V9!3wAc>~81}#BU&e1G+bC z5_!D~^oA@T?sas(j<2sH{|36>NCe{EAnpzF@y1fX_8XgFr^uTrPzr>N8Vk#~K-=XbM# zwC~me;qQfE9+2ie(!94C@bCQ&kPP_pekmaHJ~HpGgf&nP_*0MG`Y<3{p9O_b2~~i6 zeWS<+$b1kF39ugOp+V%s6d=uqxq!|OH^LS`??>eMqY9YM)SUgz?0<}okCFSB^dBSh z33@*XLo%S}lR{_|`Lr08i)_h+@vu(hv)-^-pi}BHv-_J8W!2?>6GU z4+D0;N7oN=Pz==~KemEGk?l5Az=j15x*N=fnXLIcZ&^@mJY0yVG_NbA|K8Ibo= zpcWcM1><3(s1RvFt3~ZsDymgHtblbun*EXAADgY?APh;61-XFC0d1fd=EE{r1zY(! ze}7mCwLn-L_6{WMKx`aH*g*p5IA}FLAi?Ir=slQxA6y0Nc}0>9DNq5+VGY!aI>d)$ zNQX)w{UMD!s*7yfDrf+X4@K_KFd%zqHS81>p9YIyhp2X>Z{78LWa0fc#;|w@1D`^6imtk9>P{wx0{gwci5UMRgE}2lChfyB%_&5|+Sn zSOfKd?BV!*co@)iI5LMLb2u`G*TH%~?(ha^6mM30p*+&;cr8 z1?=RZQ;z%8@}M&5Pt1i4KzMSosJ`gxi(Fsy^vi)YqE15oq*ADc^`iQ>0gn5VZa^34 z57|Ka0rO!QtO65{zbD%e2K+iX3kqQ#pzmaSJQLkODbS3RO@8b+8c{L=6&%ha^acT&RR4upHJv zJ!}^>*oQDALlzXmJgA11upSyjr6HGwTpDs|$fY5dhFltQX~?A^m$n5OMGc9A1W17# zD1|Dhg*8wQ+eHoaAq>fo1%)sVs$nIphb_=3YFHd30dm8T8;0C4DyV@v*a!`xG6dow3DO}KDq#sMhc!?S+eMw?Ll}|)*;9}`1=&-OJq6iQ z*25NP6m@DGBtQz}Kq*u~4b;I#Xb{D4p)%tk3DO}KDq#sMhc!?S+eHocAq>fo1%)sV zs$nIphb_=3YD63)KnmnQDO5oX)WJq*5N_~7JS0InS4R6EFZ#<3|UYJ z^Pn15!g|;OjiOGAg9J!{94Lh^)2I^tEsIfkT zAsMou5avNOtc3Nj1sX-29tR1K0y$6$RZs(Uun`&n*&JkZ!jKHAg!Qlm8bzHM2MLe@IZz5! zPy=NQYdgge9;X)<8XM7j>2oVMvB7D1>=X4J%{7Db&Jh*aX`| zO|+o{^akW7jtAr?B0mxNiTLq9Vk`1Hc(@}BnNR`CU=?f^RgegUun5T8By3Kqg?dqw zDT~R-O(uO|93W>X!iOp7ooWMmr=|dYPOXG$*et5J3lLts8Ys7E0_dBD57W>wZ9WiZ zn4Si6VVkIuY}f=lMU`UvTy&gU3ix-fImXAb4loav!g4@I88*tcLZhhiRuG2XkPhPk z9p%_8$G`Fwum&~(`pU6Y5eHo$88RUkDxeCM!Aemxh@U~4nfXvFiu-(O)*?}rq^;a8 zYBqk%A>ABw!69=hiy0Xi;d1L(Mr;|te`n#bq7jiN3>*G2erG4U58cS#q(?j^*{ z$CvqYp;6SOD@9!vhB{G~7mBLt0OO$;kg39t1?X7N8?pf%3zotv*aSO7UC{=5LpD^v zQdj|-MJQe?(Gno+O2V$pgdL);S_WH1Elvc|EGB;OPJT|@A1YxL zY!`Jk@mF(vjld#N*Jg=Y8ZYX)BtY+Vn?zmT3esRMknZ|zqHgE{D@9eO0O8fxx{>rZ z^@bdn2g_k2G>W>Jvbh-@H&Zq@uNJi|4)S3I>=1Pee%&%3)`+@QfVf+?h`Nn5w-J8Z zI#D$?a9mRZJ4M}2n%n2XYT)<|j_=_34s70;0Oae=^`dIARXZMPVY{fi5&=K%S`J%9 z-5mzv?+$~;Y`wBo)T@QC49L%G$xsbDL~USyLnYLUdc8l?LZhfR z@coTdJm3}&OGIttb7P$-?o+9^(Ek>Gzg-H*zSA3u0ehR!v1y~IcPW#1*Nb{D2`XW= zsP_|KDKv2N*QmeZU_2~`Mo~MfpkCB2Z0thct~H`|w*u1aMrJoMO{8f;e-pZz2ya5J ziF8dhPzM{KLA2P=0eZtm(Q27!Esz4NF{;-9;d%>f7j3a_h82cn$bwv0DcVkl9ikm@ z*NOJ{^s7Y&X~1zn+E4;;9GVNOV58{$(6=Aq`w_lhAuNLBupV}bZj}v$wIZw)KJ1U( z{fnUrHi>R6K-$*$(Hgz2vDq5kts6ui5QZFB3L8YnwF30T)c`)lA#)&M2POgG2UY_4 zKX46f5q*#i36KfJun1NFejn5*`e5`QOx(ewI~bh@6Mis0x9I|ebAL#;A-v57*dh85 z!Vk#;&W)nGv;k~&$%eT=+AiyX_%6hAUr2W)zH25F!hArc>uRV6Y#fD+ zqY@z<@aL#`uvGNX94BlPeM}`R0@58rx?@b(R?)`_#6xe$0@5Cf564!+D%c{r8*$yR z-K|!1BJ$li?v9QgX`*|gBZ;siY;&(jCsn`_AUGzV&JpeS8~8f$=aOYG6Gyi0<71a$%e36OcKfR&<|bqEEz5vJL$K zKa#hK?#q7PrGUx_6Fo2vur(0bf$K%5_>czkf%sJ9Q&)%{)CI6Ph_FH1L=PsPgHvEUplfg~ zY!IEcTJ(?v*ep7uL3HMFV1GDzh9fteFzy%VksN0Ux^KBr!pjz~#ZK9{(=aeO)r^W+% zrxI3NDw^vGJv~Kq3HD2{RZ85sNutYYM3;{jUC{wnik`7v^h}OtZWld^<4R;ISBRdC z|Ff|%o3wM5i9RnK2%n3cxywbL@536=7t9rXA$Be#?Yt}?%|+H@1oFXnUcI?+p70rsvY&D9)VjlOH!K%?lT z$$*Zf>|d7y_;Vfm*Dn%%L#F6z?BA3i`et-2>md4;G|{&bb{l@y1?0@AM;FZ%gL(QC2ua4h?8bp7Mk6&*R-OwM1 zZy@ZOOu)vs=>E1E2>UJ>(6^1aA4C|+MgNo{`WO896}!J@#A@3C(xC#7X}eLZ zL)kxc8LShFvC@jq2mFa&4Wwxo2ZXh&gRNp6#_?gG7OP7#;0NQ7)pZG=hjGU`sskX$cw-&43AT!L zG`5c>?C5;J$D>yRJ|-ZWkO|wxI;I%5h{d>L9m_uBjMWW0-4bC1>=Y}p6y^iR-Lt`b zVxv28J-R?9RKPM=2iwHz*#_{nXAaDT8X%5w%1Xkor2bF{OJO;X=C~xtg(a|AtX`z= zl@8eNgcr|zn%*15IsrTXfO0*dQLH{b#6tok13LR4-=`2Np$e*D zIn=>=sD}n<6zfDE;voT&Asup{5GtVxs$n_Q!Fs5N251y3*@t*YfMiIA94LfJsDf%( z4t1~|>Y)J|#p>%rJS0Feq(crALM2o|H7tiZSPwhI>PH#%gIELG06GRz#sg^s19ys*5)afzN)^-qaVh9aEfi~z4TKHO7b~p;WCHb+wj9<& zgIGh7fiy$WGnBBQgbfp@6DytiPv0O`Mj}wxr)0w_u}(!#W-F)=#S^vP>Q+1fK2;p9 zAmaFCt}o)8t`6kb7Lm59wULVaJBQzINB5PqQKvNTa}A=3n)fZjtDE<2_TO*bcchhe zoA+IQ@o{4FzR&)&=KcNTaQ$@i{{F$WZFvwmVzssO?${)jC_CWzS%~f{Nl@YDea)|# z&uQMb2%p}(Z?peE^S&eTYFqO@zcA1TH}CU10iDsjzn}Ee7dG$jfACS-ti5XNwwA(< z*YS*ViImE0DU%|ZEQL}bVM&mQJbxbMDfA?^ET{Ijz4~1C!-mTW%qBHC9fu{ z(VXt<+oXsI(^$eZmuO>&715-U=~P4o)gzGxS3wo`R@3Z5|8fe2ISEX)~Yr@pRB>w)hzV_~TOwO_*WjKwdUCtJ3 z;8ER$k+_29PHb|-%JfL9%#U`*r~t^(

uYxJ5{X7bTeTCsDbrNs8` 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 XH2;!U?0EYBKmT3y|9Drv@uGhLW8jS} literal 0 HcmV?d00001 diff --git a/files/mygui/VeraMono.ttf b/files/mygui/VeraMono.ttf deleted file mode 100644 index 139f0b4311ad2e0369a347b3be6c46e6c2b730d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49224 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 diff --git a/files/mygui/openmw_font.xml b/files/mygui/openmw_font.xml index cb5dd648f..0383c9942 100644 --- a/files/mygui/openmw_font.xml +++ b/files/mygui/openmw_font.xml @@ -35,7 +35,7 @@ - + From f1f9173f2430167024fe93909809987a98cda7e7 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 15 Jan 2013 00:53:32 +0100 Subject: [PATCH 347/378] Prevent closing dialogue window when an answer is expected --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index f9d8c3459..8692573b4 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -381,6 +381,10 @@ namespace MWDialogue void DialogueManager::goodbyeSelected() { + // Do not close the dialogue window if the player has to answer a question + if (mIsInChoice) + return; + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); // Apply disposition change to NPC's base disposition From 82287445af3d8667fe68e94e1e709becff02d947 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 15 Jan 2013 00:59:48 +0100 Subject: [PATCH 348/378] DialogueWindow: do not execute onFrame() when not visible --- apps/openmw/mwgui/dialogue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index f62bf2a05..c7918ceb7 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -490,7 +490,7 @@ void DialogueWindow::onReferenceUnavailable() void DialogueWindow::onFrame() { - if(mEnabled && mPtr.getTypeName() == typeid(ESM::NPC).name()) + if(mMainWidget->getVisible() && mEnabled && mPtr.getTypeName() == typeid(ESM::NPC).name()) { int disp = std::max(0, std::min(100, MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr) From 30534404621cdce5ee9c04e027345af2a4a310ae Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 15 Jan 2013 12:40:44 +0100 Subject: [PATCH 349/378] Issue #539: fixed AiWander --- apps/openmw/mwscript/aiextensions.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 35243533c..8402a5b4c 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -181,10 +181,14 @@ namespace MWScript runtime.pop(); std::vector idleList; - for (unsigned int i=0; i Date: Tue, 15 Jan 2013 11:10:41 -0800 Subject: [PATCH 350/378] Use typedefs for some maps and some cleanup --- apps/openmw/mwrender/actors.cpp | 124 ++++++++++++++++++-------------- apps/openmw/mwrender/actors.hpp | 18 +++-- 2 files changed, 80 insertions(+), 62 deletions(-) diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 05bb030d7..92f5cbe28 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -6,41 +6,42 @@ #include "renderconst.hpp" -using namespace Ogre; -using namespace MWRender; -using namespace NifOgre; +namespace MWRender +{ Actors::~Actors(){ - std::map::iterator it = mAllActors.begin(); - for (; it != mAllActors.end(); ++it) { + PtrAnimationMap::iterator it = mAllActors.begin(); + for(;it != mAllActors.end();++it) + { delete it->second; it->second = NULL; } } -void Actors::setMwRoot(Ogre::SceneNode* root){ - mMwRoot = root; -} -void Actors::insertNPC(const MWWorld::Ptr& ptr, MWWorld::InventoryStore& inv){ +void Actors::setMwRoot(Ogre::SceneNode* root) +{ mMwRoot = root; } +void Actors::insertNPC(const MWWorld::Ptr &ptr, MWWorld::InventoryStore &inv) +{ insertBegin(ptr, true, true); NpcAnimation* anim = new MWRender::NpcAnimation(ptr, ptr.getRefData ().getBaseNode (), inv, RV_Actors); mAllActors[ptr] = anim; } -void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_){ + +void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) +{ Ogre::SceneNode* cellnode; - if(mCellSceneNodes.find(ptr.getCell()) == mCellSceneNodes.end()) + CellSceneNodeMap::const_iterator celliter = mCellSceneNodes.find(ptr.getCell()); + if(celliter != mCellSceneNodes.end()) + cellnode = celliter->second; + else { //Create the scenenode and put it in the map cellnode = mMwRoot->createChildSceneNode(); mCellSceneNodes[ptr.getCell()] = cellnode; } - else - { - cellnode = mCellSceneNodes[ptr.getCell()]; - } Ogre::SceneNode* insert = cellnode->createChildSceneNode(); const float *f = ptr.getRefData().getPosition().pos; @@ -51,13 +52,13 @@ void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_){ f = ptr.getCellRef().mPos.rot; // Rotate around X axis - Quaternion xr(Radian(-f[0]), Vector3::UNIT_X); + Ogre::Quaternion xr(Ogre::Radian(-f[0]), Ogre::Vector3::UNIT_X); // Rotate around Y axis - Quaternion yr(Radian(-f[1]), Vector3::UNIT_Y); + Ogre::Quaternion yr(Ogre::Radian(-f[1]), Ogre::Vector3::UNIT_Y); // Rotate around Z axis - Quaternion zr(Radian(-f[2]), Vector3::UNIT_Z); + Ogre::Quaternion zr(Ogre::Radian(-f[2]), Ogre::Vector3::UNIT_Z); // Rotates first around z, then y, then x insert->setOrientation(xr*yr*zr); @@ -71,30 +72,30 @@ void Actors::insertCreature (const MWWorld::Ptr& ptr){ insertBegin(ptr, true, true); CreatureAnimation* anim = new MWRender::CreatureAnimation(ptr); - //mAllActors.insert(std::pair(ptr,anim)); + delete mAllActors[ptr]; mAllActors[ptr] = anim; - //mAllActors.push_back(&anim);*/ } bool Actors::deleteObject (const MWWorld::Ptr& ptr) { - delete mAllActors[ptr]; - mAllActors.erase(ptr); - if (Ogre::SceneNode *base = ptr.getRefData().getBaseNode()) - { + delete mAllActors[ptr]; + mAllActors.erase(ptr); + if(Ogre::SceneNode *base=ptr.getRefData().getBaseNode()) + { Ogre::SceneNode *parent = base->getParentSceneNode(); - - for (std::map::const_iterator iter ( - mCellSceneNodes.begin()); iter!=mCellSceneNodes.end(); ++iter) - if (iter->second==parent) + CellSceneNodeMap::const_iterator iter(mCellSceneNodes.begin()); + for(;iter != mCellSceneNodes.end();++iter) + { + if(iter->second == parent) { base->removeAndDestroyAllChildren(); mRend.getScene()->destroySceneNode (base); ptr.getRefData().setBaseNode (0); return true; } + } return false; } @@ -102,57 +103,70 @@ bool Actors::deleteObject (const MWWorld::Ptr& ptr) return true; } -void Actors::removeCell(MWWorld::Ptr::CellStore* store){ - if(mCellSceneNodes.find(store) != mCellSceneNodes.end()) - { - Ogre::SceneNode* base = mCellSceneNodes[store]; - base->removeAndDestroyAllChildren(); - mCellSceneNodes.erase(store); - mRend.getScene()->destroySceneNode(base); - base = 0; - } - for(std::map::iterator iter = mAllActors.begin(); iter != mAllActors.end(); ) +void Actors::removeCell(MWWorld::Ptr::CellStore* store) +{ + for(PtrAnimationMap::iterator iter = mAllActors.begin();iter != mAllActors.end();) { - if(iter->first.getCell() == store){ + if(iter->first.getCell() == store) + { delete iter->second; mAllActors.erase(iter++); } else ++iter; } + CellSceneNodeMap::iterator celliter = mCellSceneNodes.find(store); + if(celliter != mCellSceneNodes.end()) + { + Ogre::SceneNode *base = celliter->second; + base->removeAndDestroyAllChildren(); + mRend.getScene()->destroySceneNode(base); + mCellSceneNodes.erase(celliter); + } } -void Actors::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number){ - if(mAllActors.find(ptr) != mAllActors.end()) - mAllActors[ptr]->playGroup(groupName, mode, number); +void Actors::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) +{ + PtrAnimationMap::const_iterator iter = mAllActors.find(ptr); + if(iter != mAllActors.end()) + iter->second->playGroup(groupName, mode, number); } -void Actors::skipAnimation (const MWWorld::Ptr& ptr){ - if(mAllActors.find(ptr) != mAllActors.end()) - mAllActors[ptr]->skipAnim(); +void Actors::skipAnimation (const MWWorld::Ptr& ptr) +{ + PtrAnimationMap::const_iterator iter = mAllActors.find(ptr); + if(iter != mAllActors.end()) + iter->second->skipAnim(); } -void Actors::update (float duration){ - for(std::map::iterator iter = mAllActors.begin(); iter != mAllActors.end(); iter++) +void Actors::update (float duration) +{ + for(PtrAnimationMap::const_iterator iter = mAllActors.begin();iter != mAllActors.end();iter++) iter->second->runAnimation(duration); } -void -Actors::updateObjectCell(const MWWorld::Ptr &ptr) +void Actors::updateObjectCell(const MWWorld::Ptr &ptr) { Ogre::SceneNode *node; MWWorld::CellStore *newCell = ptr.getCell(); - if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) { + CellSceneNodeMap::const_iterator celliter = mCellSceneNodes.find(newCell); + if(celliter != mCellSceneNodes.end()) + node = celliter->second; + else + { node = mMwRoot->createChildSceneNode(); mCellSceneNodes[newCell] = node; - } else { - node = mCellSceneNodes[newCell]; } node->addChild(ptr.getRefData().getBaseNode()); - if (mAllActors.find(ptr) != mAllActors.end()) { + + PtrAnimationMap::iterator iter = mAllActors.find(ptr); + if(iter != mAllActors.end()) + { /// \note Update key (Ptr's are compared only with refdata so mCell /// on key is outdated), maybe redundant - Animation *anim = mAllActors[ptr]; - mAllActors.erase(ptr); + Animation *anim = iter->second; + mAllActors.erase(iter); mAllActors[ptr] = anim; } } + +} diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index 073c5d51f..efb6247e4 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -10,18 +10,22 @@ namespace MWWorld class CellStore; } -namespace MWRender{ - class Actors{ +namespace MWRender +{ + class Actors + { + typedef std::map CellSceneNodeMap; + typedef std::map PtrAnimationMap; + OEngine::Render::OgreRenderer &mRend; - std::map mCellSceneNodes; Ogre::SceneNode* mMwRoot; - std::map mAllActors; - + CellSceneNodeMap mCellSceneNodes; + PtrAnimationMap mAllActors; - - public: + public: Actors(OEngine::Render::OgreRenderer& _rend): mRend(_rend) {} ~Actors(); + void setMwRoot(Ogre::SceneNode* root); void insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_); void insertCreature (const MWWorld::Ptr& ptr); From 8a073c113e277c65fc92598b451de6d336c95244 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 15 Jan 2013 12:07:15 -0800 Subject: [PATCH 351/378] Use const references where appropriate --- apps/openmw/mwbase/soundmanager.hpp | 14 +++++++------- apps/openmw/mwsound/soundmanagerimp.cpp | 16 ++++++++-------- apps/openmw/mwsound/soundmanagerimp.hpp | 18 ++++++++---------- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 0c706ab49..5d396fac0 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -84,7 +84,7 @@ namespace MWBase ///< Start playing music from the selected folder /// \param name of the folder that contains the playlist - virtual void say(MWWorld::Ptr reference, const std::string& filename) = 0; + virtual void say(const MWWorld::Ptr &reference, const std::string& filename) = 0; ///< Make an actor say some text. /// \param filename name of a sound file in "Sound/" in the data directory. @@ -92,10 +92,10 @@ namespace MWBase ///< Say some text, without an actor ref /// \param filename name of a sound file in "Sound/" in the data directory. - virtual bool sayDone(MWWorld::Ptr reference=MWWorld::Ptr()) const = 0; + virtual bool sayDone(const MWWorld::Ptr &reference=MWWorld::Ptr()) const = 0; ///< Is actor not speaking? - virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()) = 0; + virtual void stopSay(const MWWorld::Ptr &reference=MWWorld::Ptr()) = 0; ///< Stop an actor speaking virtual SoundPtr playTrack(const MWSound::DecoderPtr& decoder, PlayType type) = 0; @@ -105,14 +105,14 @@ namespace MWBase PlayMode mode=Play_Normal) = 0; ///< Play a sound, independently of 3D-position - virtual SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId, + virtual SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId, float volume, float pitch, PlayMode mode=Play_Normal) = 0; ///< Play a sound from an object - virtual void stopSound3D(MWWorld::Ptr reference, const std::string& soundId) = 0; + virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId) = 0; ///< Stop the given object from playing the given sound, - virtual void stopSound3D(MWWorld::Ptr reference) = 0; + virtual void stopSound3D(const MWWorld::Ptr &reference) = 0; ///< Stop the given object from playing all sounds. virtual void stopSound(const MWWorld::CellStore *cell) = 0; @@ -121,7 +121,7 @@ namespace MWBase virtual void stopSound(const std::string& soundId) = 0; ///< Stop a non-3d looping sound - virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const = 0; + virtual bool getSoundPlaying(const MWWorld::Ptr &reference, const std::string& soundId) const = 0; ///< Is the given sound currently playing on the given object? virtual void pauseSounds(int types=Play_TypeMask) = 0; diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 8a69fc96b..c50290680 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -160,7 +160,7 @@ namespace MWSound return volume; } - bool SoundManager::isPlaying(MWWorld::Ptr ptr, const std::string &id) const + bool SoundManager::isPlaying(const MWWorld::Ptr &ptr, const std::string &id) const { SoundMap::const_iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) @@ -229,7 +229,7 @@ namespace MWSound startRandomTitle(); } - void SoundManager::say(MWWorld::Ptr ptr, const std::string& filename) + void SoundManager::say(const MWWorld::Ptr &ptr, const std::string& filename) { if(!mOutput->isInitialized()) return; @@ -269,12 +269,12 @@ namespace MWSound } } - bool SoundManager::sayDone(MWWorld::Ptr ptr) const + bool SoundManager::sayDone(const MWWorld::Ptr &ptr) const { return !isPlaying(ptr, "_say_sound"); } - void SoundManager::stopSay(MWWorld::Ptr ptr) + void SoundManager::stopSay(const MWWorld::Ptr &ptr) { SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) @@ -328,7 +328,7 @@ namespace MWSound return sound; } - MWBase::SoundPtr SoundManager::playSound3D(MWWorld::Ptr ptr, const std::string& soundId, + MWBase::SoundPtr SoundManager::playSound3D(const MWWorld::Ptr &ptr, const std::string& soundId, float volume, float pitch, PlayMode mode) { MWBase::SoundPtr sound; @@ -356,7 +356,7 @@ namespace MWSound return sound; } - void SoundManager::stopSound3D(MWWorld::Ptr ptr, const std::string& soundId) + void SoundManager::stopSound3D(const MWWorld::Ptr &ptr, const std::string& soundId) { SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) @@ -371,7 +371,7 @@ namespace MWSound } } - void SoundManager::stopSound3D(MWWorld::Ptr ptr) + void SoundManager::stopSound3D(const MWWorld::Ptr &ptr) { SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) @@ -418,7 +418,7 @@ namespace MWSound } } - bool SoundManager::getSoundPlaying(MWWorld::Ptr ptr, const std::string& soundId) const + bool SoundManager::getSoundPlaying(const MWWorld::Ptr &ptr, const std::string& soundId) const { return isPlaying(ptr, soundId); } diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index b475449d9..2af26d3db 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -14,8 +14,6 @@ #include "../mwbase/soundmanager.hpp" -#include "../mwworld/ptr.hpp" - namespace MWSound { class Sound_Output; @@ -57,7 +55,7 @@ namespace MWSound std::string lookup(const std::string &soundId, float &volume, float &min, float &max); void streamMusicFull(const std::string& filename); - bool isPlaying(MWWorld::Ptr ptr, const std::string &id) const; + bool isPlaying(const MWWorld::Ptr &ptr, const std::string &id) const; void updateSounds(float duration); void updateRegionSound(float duration); @@ -93,7 +91,7 @@ namespace MWSound ///< Start playing music from the selected folder /// \param name of the folder that contains the playlist - virtual void say(MWWorld::Ptr reference, const std::string& filename); + virtual void say(const MWWorld::Ptr &reference, const std::string& filename); ///< Make an actor say some text. /// \param filename name of a sound file in "Sound/" in the data directory. @@ -101,10 +99,10 @@ namespace MWSound ///< Say some text, without an actor ref /// \param filename name of a sound file in "Sound/" in the data directory. - virtual bool sayDone(MWWorld::Ptr reference=MWWorld::Ptr()) const; + virtual bool sayDone(const MWWorld::Ptr &reference=MWWorld::Ptr()) const; ///< Is actor not speaking? - virtual void stopSay(MWWorld::Ptr reference=MWWorld::Ptr()); + virtual void stopSay(const MWWorld::Ptr &reference=MWWorld::Ptr()); ///< Stop an actor speaking virtual MWBase::SoundPtr playTrack(const DecoderPtr& decoder, PlayType type); @@ -113,14 +111,14 @@ namespace MWSound virtual MWBase::SoundPtr playSound(const std::string& soundId, float volume, float pitch, PlayMode mode=Play_Normal); ///< Play a sound, independently of 3D-position - virtual MWBase::SoundPtr playSound3D(MWWorld::Ptr reference, const std::string& soundId, + virtual MWBase::SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId, float volume, float pitch, PlayMode mode=Play_Normal); ///< Play a sound from an object - virtual void stopSound3D(MWWorld::Ptr reference, const std::string& soundId); + virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId); ///< Stop the given object from playing the given sound, - virtual void stopSound3D(MWWorld::Ptr reference); + virtual void stopSound3D(const MWWorld::Ptr &reference); ///< Stop the given object from playing all sounds. virtual void stopSound(const MWWorld::CellStore *cell); @@ -129,7 +127,7 @@ namespace MWSound virtual void stopSound(const std::string& soundId); ///< Stop a non-3d looping sound - virtual bool getSoundPlaying(MWWorld::Ptr reference, const std::string& soundId) const; + virtual bool getSoundPlaying(const MWWorld::Ptr &reference, const std::string& soundId) const; ///< Is the given sound currently playing on the given object? virtual void pauseSounds(int types=Play_TypeMask); From 9e00c6694fac95cf66e360ee3905abd5e59c1814 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 15 Jan 2013 21:40:44 +0100 Subject: [PATCH 352/378] Allow only ID filtered topics for dialogue with creatures --- apps/openmw/mwdialogue/filter.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 7c590c8ef..99be5554a 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -19,12 +19,19 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const { + bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name()); + // actor id if (!info.mActor.empty()) + { if ( Misc::StringUtils::lowerCase (info.mActor)!=MWWorld::Class::get (mActor).getId (mActor)) return false; - - bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name()); + } + else if (isCreature) + { + // Creatures must not have topics aside of those specific to their id + return false; + } // NPC race if (!info.mRace.empty()) From e5f040a06f1ec85beb05a547cae8fce1fa4b3815 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 15 Jan 2013 14:10:52 -0800 Subject: [PATCH 353/378] Improve filename pattern matching --- components/bsa/bsa_archive.cpp | 293 +++++++++++---------------------- 1 file changed, 97 insertions(+), 196 deletions(-) diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 9913fb8aa..5274564a6 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -31,150 +31,30 @@ #include "../files/constrainedfiledatastream.hpp" -namespace -{ - using namespace Ogre; -using namespace Bsa; - -struct PathPatternMatcher -{ - PathPatternMatcher (char const * pattern) : pattern (pattern) - { - } - - bool operator () (char const * input) - { - char const * p = pattern; - char const * i = input; - while (*p && *i) - { - if (*p == '*') - { - ++p; - - while (*i && *i != *p && *i != '/' && *i != '\\') - ++i; - } - else - if (*p == '?') - { - if (*i == '/' || *i == '\\') - break; - - ++i, ++p; - } - if (*p == '/' || *p == '\\') - { - if (*i != '/' && *i != '\\') - break; - - ++i, ++p; - } - else - { - if (*i != *p) - break; - - ++i, ++p; - } - } - - return *p == 0 && *i == 0; - } - -private: - char const * pattern; -}; +static bool fsstrict = false; -struct FileNameGatherer +static char strict_normalize_char(char ch) { - StringVectorPtr ptr; - - FileNameGatherer (StringVectorPtr ptr) : ptr (ptr) - { - } - - void operator () (std::string const & filename) const - { - ptr->push_back (filename); - } -}; + return ch == '\\' ? '/' : ch; +} -struct FileInfoGatherer +static char nonstrict_normalize_char(char ch) { - Archive const * archive; - FileInfoListPtr ptr; - - FileInfoGatherer (Archive const * archive, FileInfoListPtr ptr) : - archive (archive), ptr (ptr) - { - } - - void operator () (std::string filename) const - { - FileInfo fi; - - std::size_t pt = filename.rfind ('/'); - if (pt == std::string::npos) - pt = 0; - - fi.archive = const_cast (archive); - fi.path = filename.substr (0, pt); - fi.filename = filename.substr (pt); - fi.compressedSize = fi.uncompressedSize = 0; - - ptr->push_back(fi); - } -}; + return ch == '\\' ? '/' : std::tolower(ch); +} -template -void matchFiles (bool recursive, std::string const & pattern, file_iterator begin, file_iterator end, filename_extractor filenameExtractor, match_handler matchHandler) +template +static std::string normalize_path(T1 begin, T2 end) { - if (recursive && pattern == "*") - { - for (file_iterator i = begin; i != end; ++i) - matchHandler (filenameExtractor (*i)); - } - else - { - PathPatternMatcher matcher (pattern.c_str ()); - - if (recursive) - { - for (file_iterator i = begin; i != end; ++i) - { - char const * filename = filenameExtractor (*i); - char const * matchable_part = filename; - - for (char const * j = matchable_part; *j; ++j) - { - if (*j == '/' || *j == '\\') - matchable_part = j + 1; - } - - if (matcher (matchable_part)) - matchHandler (filename); - } - } - else - { - for (file_iterator i = begin; i != end; ++i) - { - char const * filename = filenameExtractor (*i); - - if (matcher (filename)) - matchHandler (filename); - } - } - } + std::string normalized; + normalized.reserve(std::distance(begin, end)); + char (*normalize_char)(char) = fsstrict ? &strict_normalize_char : &nonstrict_normalize_char; + std::transform(begin, end, std::back_inserter(normalized), normalize_char); + return normalized; } - - -static bool fsstrict = false; - /// An OGRE Archive wrapping a BSAFile archive class DirArchive: public Ogre::Archive { @@ -182,37 +62,12 @@ class DirArchive: public Ogre::Archive index mIndex; - static char strict_normalize_char(char ch) - { - return ch == '\\' ? '/' : ch; - } - - static char nonstrict_normalize_char(char ch) - { - return ch == '\\' ? '/' : std::tolower (ch); - } - - static std::string normalize_path (std::string::const_iterator begin, std::string::const_iterator end) - { - std::string normalized; - normalized.reserve (end-begin); - char (*normalize_char) (char) = fsstrict ? &strict_normalize_char : &nonstrict_normalize_char; - std::transform (begin, end, std::back_inserter (normalized), normalize_char); - return normalized; - } - index::const_iterator lookup_filename (std::string const & filename) const { std::string normalized = normalize_path (filename.begin (), filename.end ()); - return mIndex.find (normalized); } - static char const * extractFilename (index::value_type const & entry) - { - return entry.first.c_str (); - } - public: DirArchive(const String& name) @@ -273,15 +128,20 @@ public: StringVectorPtr find(const String& pattern, bool recursive = true, bool dirs = false) { - std::string normalizedPattern = normalize_path (pattern.begin (), pattern.end ()); + std::string normalizedPattern = normalize_path(pattern.begin(), pattern.end()); StringVectorPtr ptr = StringVectorPtr(new StringVector()); - matchFiles (recursive, normalizedPattern, mIndex.begin (), mIndex.end (), extractFilename, FileNameGatherer (ptr)); + for(index::const_iterator iter = mIndex.begin();iter != mIndex.end();iter++) + { + if(Ogre::StringUtil::match(iter->first, normalizedPattern) || + (recursive && Ogre::StringUtil::match(iter->first, "*/"+normalizedPattern))) + ptr->push_back(iter->first); + } return ptr; } bool exists(const String& filename) { - return lookup_filename (filename) != mIndex.end (); + return lookup_filename(filename) != mIndex.end (); } time_t getModifiedTime(const String&) { return 0; } @@ -289,21 +149,44 @@ public: FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true, bool dirs = false) const { + std::string normalizedPattern = normalize_path(pattern.begin(), pattern.end()); FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); - FileInfoGatherer gatherer (this, ptr); - std::string normalizedPattern = normalize_path (pattern.begin (), pattern.end ()); + index::const_iterator i = mIndex.find(normalizedPattern); + if(i != mIndex.end()) + { + std::string::size_type pt = i->first.rfind('/'); + if(pt == std::string::npos) + pt = 0; - index::const_iterator i = mIndex.find (normalizedPattern); + FileInfo fi; + fi.archive = const_cast(this); + fi.path = i->first.substr(0, pt); + fi.filename = i->first.substr((i->first[pt]=='/') ? pt+1 : pt); + fi.compressedSize = fi.uncompressedSize = 0; - if (i != mIndex.end ()) - { - gatherer (i->first); + ptr->push_back(fi); } else { + for(index::const_iterator iter = mIndex.begin();iter != mIndex.end();iter++) + { + if(Ogre::StringUtil::match(iter->first, normalizedPattern) || + (recursive && Ogre::StringUtil::match(iter->first, "*/"+normalizedPattern))) + { + std::string::size_type pt = iter->first.rfind('/'); + if(pt == std::string::npos) + pt = 0; + + FileInfo fi; + fi.archive = const_cast(this); + fi.path = iter->first.substr(0, pt); + fi.filename = iter->first.substr((iter->first[pt]=='/') ? pt+1 : pt); + fi.compressedSize = fi.uncompressedSize = 0; - matchFiles (recursive, normalizedPattern, mIndex.begin (), mIndex.end (), extractFilename, gatherer); + ptr->push_back(fi); + } + } } return ptr; @@ -312,9 +195,9 @@ public: class BSAArchive : public Archive { - BSAFile arc; + Bsa::BSAFile arc; - static char const * extractFilename (BSAFile::FileStruct const & entry) + static const char *extractFilename(const Bsa::BSAFile::FileStruct &entry) { return entry.name; } @@ -330,13 +213,13 @@ public: void load() {} void unload() {} - DataStreamPtr open(const String& filename, bool recursive = true) const + DataStreamPtr open(const String& filename, bool readonly = true) const { // Get a non-const reference to arc. This is a hack and it's all // OGRE's fault. You should NOT expect an open() command not to // have any side effects on the archive, and hence this function // should not have been declared const in the first place. - BSAFile *narc = const_cast(&arc); + Bsa::BSAFile *narc = const_cast(&arc); // Open the file return narc->getFile(filename.c_str()); @@ -360,32 +243,51 @@ public: return findFileInfo ("*", recursive, dirs); } - // After load() is called, find("*") is called once. It doesn't seem - // to matter that we return an empty list, exists() gets called on - // the correct files anyway. - StringVectorPtr find(const String& pattern, bool recursive = true, - bool dirs = false) - { + StringVectorPtr find(const String& pattern, bool recursive = true, + bool dirs = false) + { + std::string normalizedPattern = normalize_path(pattern.begin(), pattern.end()); + const Bsa::BSAFile::FileList &filelist = arc.getList(); StringVectorPtr ptr = StringVectorPtr(new StringVector()); - matchFiles (recursive, pattern, arc.getList ().begin (), arc.getList ().end (), extractFilename, FileNameGatherer (ptr)); + for(Bsa::BSAFile::FileList::const_iterator iter = filelist.begin();iter != filelist.end();iter++) + { + std::string ent = normalize_path(iter->name, iter->name+std::strlen(iter->name)); + if(Ogre::StringUtil::match(ent, normalizedPattern) || + (recursive && Ogre::StringUtil::match(ent, "*/"+normalizedPattern))) + ptr->push_back(iter->name); + } return ptr; - } + } + + FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true, + bool dirs = false) const + { + std::string normalizedPattern = normalize_path(pattern.begin(), pattern.end()); + FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); + const Bsa::BSAFile::FileList &filelist = arc.getList(); - /* Gets called once for each of the ogre formats, *.program, - *.material etc. We ignore all these. + for(Bsa::BSAFile::FileList::const_iterator iter = filelist.begin();iter != filelist.end();iter++) + { + std::string ent = normalize_path(iter->name, iter->name+std::strlen(iter->name)); + if(Ogre::StringUtil::match(ent, normalizedPattern) || + (recursive && Ogre::StringUtil::match(ent, "*/"+normalizedPattern))) + { + std::string::size_type pt = ent.rfind('/'); + if(pt == std::string::npos) + pt = 0; - However, it's also called by MyGUI to find individual textures, - and we can't ignore these since many of the GUI textures are - located in BSAs. So instead we channel it through exists() and - set up a single-element result list if the file is found. - */ - FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true, - bool dirs = false) const - { - FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); - matchFiles (recursive, pattern, arc.getList ().begin (), arc.getList ().end (), extractFilename, FileInfoGatherer (this, ptr)); - return ptr; - } + FileInfo fi; + fi.archive = const_cast(this); + fi.path = std::string(iter->name, pt); + fi.filename = std::string(iter->name + ((ent[pt]=='/') ? pt+1 : pt)); + fi.compressedSize = fi.uncompressedSize = iter->fileSize; + + ptr->push_back(fi); + } + } + + return ptr; + } }; // An archive factory for BSA archives @@ -445,7 +347,6 @@ static void insertDirFactory() } } -} namespace Bsa { From 3220330ce0e0bcaf7899ed1492563330f79f0e91 Mon Sep 17 00:00:00 2001 From: Thoronador Date: Wed, 16 Jan 2013 04:19:16 +0100 Subject: [PATCH 354/378] fix spelling in comment --- apps/openmw/mwscript/userextensions.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwscript/userextensions.hpp b/apps/openmw/mwscript/userextensions.hpp index 3642eb5f4..4bc3b46ec 100644 --- a/apps/openmw/mwscript/userextensions.hpp +++ b/apps/openmw/mwscript/userextensions.hpp @@ -13,7 +13,7 @@ namespace Interpreter namespace MWScript { - /// \brief Temporaty script functionality limited to the console + /// \brief Temporary script functionality limited to the console namespace User { void registerExtensions (Compiler::Extensions& extensions); From 4c0e0fc279e833b5092caf3f7198dc6b50b20c7c Mon Sep 17 00:00:00 2001 From: pvdk Date: Thu, 17 Jan 2013 01:30:15 +0100 Subject: [PATCH 355/378] Modified license filename of the DejaVu font, more similar to the other one now --- Dejavu_lgc_font_license.txt => DejaVu Font License.txt | 0 credits.txt | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename Dejavu_lgc_font_license.txt => DejaVu Font License.txt (100%) diff --git a/Dejavu_lgc_font_license.txt b/DejaVu Font License.txt similarity index 100% rename from Dejavu_lgc_font_license.txt rename to DejaVu Font License.txt diff --git a/credits.txt b/credits.txt index 878cbf915..5d486c74e 100644 --- a/credits.txt +++ b/credits.txt @@ -119,4 +119,4 @@ Thanks to Dongle, for his Daedric fontface, see Daedric Font License.txt for his license terms. Thanks to DejaVu team, -for their DejaVuLGCSansMono fontface, see Dejavu_lgc_font_license.txt for their license terms. +for their DejaVuLGCSansMono fontface, see DejaVu Font License.txt for their license terms. From 4feaa66897e55ff9ffc2a011bceed5fee1156799 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Thu, 17 Jan 2013 01:58:44 +0100 Subject: [PATCH 356/378] Do not open the dialogue window if no greeting is found --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index f9d8c3459..535145a00 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -122,15 +122,9 @@ namespace MWDialogue MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get (actor).getCreatureStats (actor); mTalkedTo = creatureStats.hasTalkedToPlayer(); - creatureStats.talkedToPlayer(); mActorKnownTopics.clear(); - //initialise the GUI - MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue); - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - win->startDialogue(actor, MWWorld::Class::get (actor).getName (actor)); - //setup the list of topics known by the actor. Topics who are also on the knownTopics list will be added to the GUI updateTopics(); @@ -146,6 +140,13 @@ namespace MWDialogue { if (const ESM::DialInfo *info = filter.search (*it)) { + //initialise the GUI + MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue); + MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); + win->startDialogue(actor, MWWorld::Class::get (actor).getName (actor)); + + creatureStats.talkedToPlayer(); + if (!info->mSound.empty()) { // TODO play sound From 2d96f528646e0799837ad29463b97c548f1d3df9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 19 Jan 2013 14:29:14 +0100 Subject: [PATCH 357/378] select correct record when opening a dialogue sub view --- apps/opencs/view/world/dialoguesubview.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 6138fd3f5..2bf6577b1 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -10,6 +10,7 @@ #include #include "../../model/world/columnbase.hpp" +#include "../../model/world/idtable.hpp" CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete) @@ -83,7 +84,8 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM } } - mWidgetMapper->toFirst(); /// \todo use the correct row instead + mWidgetMapper->setCurrentModelIndex ( + dynamic_cast (*model).getModelIndex (id.getId(), 0)); } void CSVWorld::DialogueSubView::setEditLock (bool locked) From d6b956cdcf45715329d080deb0783c74244821dd Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Tue, 15 Jan 2013 08:27:58 -0800 Subject: [PATCH 358/378] fixed swapped day & month fields when created a stamped journal entry --- apps/openmw/mwdialogue/journalentry.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index e6141884c..5ffde5499 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -61,8 +61,8 @@ namespace MWDialogue StampedJournalEntry StampedJournalEntry::makeFromQuest (const std::string& topic, int index) { int day = MWBase::Environment::get().getWorld()->getGlobalVariable ("dayspassed").mLong; - int month = MWBase::Environment::get().getWorld()->getGlobalVariable ("day").mLong; - int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalVariable ("month").mLong; + int month = MWBase::Environment::get().getWorld()->getGlobalVariable ("month").mLong; + int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalVariable ("day").mLong; return StampedJournalEntry (topic, idFromIndex (topic, index), day, month, dayOfMonth); } From 528c3da6dae85a8c4c1903b16b3322742ca02292 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sat, 19 Jan 2013 16:20:22 -0800 Subject: [PATCH 359/378] record heard topics in journal --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index b83f495ad..a3bb640e6 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -20,6 +20,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/journal.hpp" #include "../mwbase/scriptmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -269,6 +270,7 @@ namespace MWDialogue MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); win->addText (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); + MWBase::Environment::get().getJournal()->addTopic (topic, info->mId); executeScript (info->mResultScript); @@ -420,6 +422,7 @@ namespace MWDialogue MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addText (Interpreter::fixDefinesDialog(text, interpreterContext)); + MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId); executeScript (info->mResultScript); mLastTopic = mLastTopic; mLastDialogue = *info; From 4c7ae3d1ff296b4c7cc96fcd2c533de3ace00443 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sat, 19 Jan 2013 16:21:15 -0800 Subject: [PATCH 360/378] prevent duplicate journal entries from being recorded --- apps/openmw/mwdialogue/journalimp.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index 2b2c60381..c623ddc85 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -31,6 +31,12 @@ namespace MWDialogue void Journal::addEntry (const std::string& id, int index) { + // bail out of we already have heard this... + auto infoId = JournalEntry::idFromIndex (id, index); + for (auto i = mJournal.begin (); i != mJournal.end (); ++i) + if (i->mTopic == id && i->mInfoId == infoId) + return; + StampedJournalEntry entry = StampedJournalEntry::makeFromQuest (id, index); mJournal.push_back (entry); From f55da179337a2591438f8ccad5cd9231203dce77 Mon Sep 17 00:00:00 2001 From: Nathan Jeffords Date: Sat, 19 Jan 2013 16:21:41 -0800 Subject: [PATCH 361/378] made some journal accessor methods constant --- apps/openmw/mwdialogue/topic.cpp | 6 +++--- apps/openmw/mwdialogue/topic.hpp | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwdialogue/topic.cpp b/apps/openmw/mwdialogue/topic.cpp index b6e7c07ae..3253b20d6 100644 --- a/apps/openmw/mwdialogue/topic.cpp +++ b/apps/openmw/mwdialogue/topic.cpp @@ -27,17 +27,17 @@ namespace MWDialogue mEntries.push_back (entry.mInfoId); } - Topic::TEntryIter Topic::begin() + Topic::TEntryIter Topic::begin() const { return mEntries.begin(); } - Topic::TEntryIter Topic::end() + Topic::TEntryIter Topic::end() const { return mEntries.end(); } - JournalEntry Topic::getEntry (const std::string& infoId) + JournalEntry Topic::getEntry (const std::string& infoId) const { return JournalEntry (mTopic, infoId); } diff --git a/apps/openmw/mwdialogue/topic.hpp b/apps/openmw/mwdialogue/topic.hpp index 566f60ab0..c3f0baabc 100644 --- a/apps/openmw/mwdialogue/topic.hpp +++ b/apps/openmw/mwdialogue/topic.hpp @@ -34,13 +34,15 @@ namespace MWDialogue /// /// \note Redundant entries are ignored. - TEntryIter begin(); + std::string const & getName () const { return mTopic; } + + TEntryIter begin() const; ///< Iterator pointing to the begin of the journal for this topic. - TEntryIter end(); + TEntryIter end() const; ///< Iterator pointing past the end of the journal for this topic. - JournalEntry getEntry (const std::string& infoId); + JournalEntry getEntry (const std::string& infoId) const; }; } From 3c6ddd7fa783068477a5d0e91605b2037803558e Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Sun, 20 Jan 2013 14:10:04 +0000 Subject: [PATCH 362/378] fixed isInCell method --- apps/openmw/mwworld/containerstore.cpp | 1 + apps/openmw/mwworld/ptr.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 6884aa6c8..7a4120d0a 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -90,6 +90,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr) cell = player.getCell(); item.mCell = cell; + item.mContainerStore = 0; MWBase::Environment::get().getWorld()->getLocalScripts().add(script, item); } diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index 594ddef2d..d97ebcc6e 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -74,7 +74,7 @@ namespace MWWorld bool isInCell() const { - return (mCell != 0); + return (mContainerStore == 0); } void setContainerStore (ContainerStore *store); From 665a530e10c912e5159929f7e4fcb5300d9559a4 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Sun, 20 Jan 2013 14:14:34 +0000 Subject: [PATCH 363/378] renamed realAdd to addImp --- apps/openmw/mwworld/containerstore.cpp | 6 +++--- apps/openmw/mwworld/containerstore.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 7a4120d0a..bca4073b5 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -74,7 +74,7 @@ bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2) MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr) { - MWWorld::ContainerStoreIterator it = realAdd(ptr); + MWWorld::ContainerStoreIterator it = addImp(ptr); MWWorld::Ptr item = *it; std::string script = MWWorld::Class::get(item).getScript(item); @@ -97,7 +97,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr) return it; } -MWWorld::ContainerStoreIterator MWWorld::ContainerStore::realAdd (const Ptr& ptr) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr) { int type = getType(ptr); @@ -189,7 +189,7 @@ void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const MWWor } ref.getPtr().getRefData().setCount (std::abs(iter->mCount)); /// \todo implement item restocking (indicated by negative count) - realAdd (ref.getPtr()); + addImp (ref.getPtr()); } flagAsModified(); diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index e274ee130..e4f75d547 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -52,7 +52,7 @@ namespace MWWorld int mStateId; mutable float mCachedWeight; mutable bool mWeightUpToDate; - ContainerStoreIterator realAdd (const Ptr& ptr); + ContainerStoreIterator addImp (const Ptr& ptr); public: From abe25c5f662def09b98ac7afe076258e439294a0 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Sun, 20 Jan 2013 14:24:55 +0000 Subject: [PATCH 364/378] removed use of c++11 auto --- apps/openmw/mwdialogue/journalimp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index c623ddc85..5e2bc6bc0 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -32,8 +32,8 @@ namespace MWDialogue void Journal::addEntry (const std::string& id, int index) { // bail out of we already have heard this... - auto infoId = JournalEntry::idFromIndex (id, index); - for (auto i = mJournal.begin (); i != mJournal.end (); ++i) + std::string infoId = JournalEntry::idFromIndex (id, index); + for (TEntryIter i = mJournal.begin (); i != mJournal.end (); ++i) if (i->mTopic == id && i->mInfoId == infoId) return; From bbac63bff7cd2f16f00c2c9cde102f1dbdbb7c02 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Sun, 20 Jan 2013 14:25:44 +0000 Subject: [PATCH 365/378] Ignore vim swap files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 9734ac35c..776e2b659 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ makefile data *.kdev4 CMakeLists.txt.user +*.swp +*.swo From 28c580d2805ee7a88a6a00b2fae573eac3f8807f Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Sun, 20 Jan 2013 15:43:52 +0000 Subject: [PATCH 366/378] disabling and enabling containers causes scripts on contents to be disabled and enabled accordingly --- apps/openmw/mwworld/worldimp.cpp | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 716cd6e96..bd48875e7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -406,6 +406,24 @@ namespace MWWorld if (!reference.getRefData().isEnabled()) { reference.getRefData().enable(); + + /* run scripts on container contents */ + if( reference.getTypeName()==typeid (ESM::Container).name() || + reference.getTypeName()==typeid (ESM::NPC).name() || + reference.getTypeName()==typeid (ESM::Creature).name()) + { + MWWorld::ContainerStore& container = MWWorld::Class::get(reference).getContainerStore(reference); + for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it) + { + std::string script = MWWorld::Class::get(*it).getScript(*it); + if(script != "") + { + MWWorld::Ptr item = *it; + item.mCell = reference.getCell(); + mLocalScripts.add (script, item); + } + } + } if(mWorldScene->getActiveCells().find (reference.getCell()) != mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) mWorldScene->addObjectToScene (reference); @@ -417,6 +435,23 @@ namespace MWWorld if (reference.getRefData().isEnabled()) { reference.getRefData().disable(); + + /* remove scripts on container contents */ + if( reference.getTypeName()==typeid (ESM::Container).name() || + reference.getTypeName()==typeid (ESM::NPC).name() || + reference.getTypeName()==typeid (ESM::Creature).name()) + { + MWWorld::ContainerStore& container = MWWorld::Class::get(reference).getContainerStore(reference); + for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it) + { + std::string script = MWWorld::Class::get(*it).getScript(*it); + if(script != "") + { + MWWorld::Ptr item = *it; + mLocalScripts.remove (item); + } + } + } if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) mWorldScene->removeObjectFromScene (reference); From 23dada0ee4e4d35ff14983a6ff515b30d228b7cb Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Sun, 20 Jan 2013 16:38:56 +0000 Subject: [PATCH 367/378] moved script handling on enable / disable into their own functions --- apps/openmw/mwworld/worldimp.cpp | 74 ++++++++++++++++++-------------- apps/openmw/mwworld/worldimp.hpp | 3 ++ 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index bd48875e7..ee18e5d95 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -401,32 +401,55 @@ namespace MWWorld return MWWorld::Ptr(); } + void World::addContainerScripts(const Ptr& reference, Ptr::CellStore * cell) + { + if( reference.getTypeName()==typeid (ESM::Container).name() || + reference.getTypeName()==typeid (ESM::NPC).name() || + reference.getTypeName()==typeid (ESM::Creature).name()) + { + MWWorld::ContainerStore& container = MWWorld::Class::get(reference).getContainerStore(reference); + for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it) + { + std::string script = MWWorld::Class::get(*it).getScript(*it); + if(script != "") + { + MWWorld::Ptr item = *it; + item.mCell = cell; + mLocalScripts.add (script, item); + } + } + } + } + void World::enable (const Ptr& reference) { if (!reference.getRefData().isEnabled()) { reference.getRefData().enable(); - /* run scripts on container contents */ - if( reference.getTypeName()==typeid (ESM::Container).name() || - reference.getTypeName()==typeid (ESM::NPC).name() || - reference.getTypeName()==typeid (ESM::Creature).name()) + addContainerScripts(reference, reference.getCell()); + + if(mWorldScene->getActiveCells().find (reference.getCell()) != mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) + mWorldScene->addObjectToScene (reference); + } + } + + void World::removeContainerScripts(const Ptr& reference) + { + if( reference.getTypeName()==typeid (ESM::Container).name() || + reference.getTypeName()==typeid (ESM::NPC).name() || + reference.getTypeName()==typeid (ESM::Creature).name()) + { + MWWorld::ContainerStore& container = MWWorld::Class::get(reference).getContainerStore(reference); + for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it) { - MWWorld::ContainerStore& container = MWWorld::Class::get(reference).getContainerStore(reference); - for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it) + std::string script = MWWorld::Class::get(*it).getScript(*it); + if(script != "") { - std::string script = MWWorld::Class::get(*it).getScript(*it); - if(script != "") - { - MWWorld::Ptr item = *it; - item.mCell = reference.getCell(); - mLocalScripts.add (script, item); - } + MWWorld::Ptr item = *it; + mLocalScripts.remove (item); } } - - if(mWorldScene->getActiveCells().find (reference.getCell()) != mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) - mWorldScene->addObjectToScene (reference); } } @@ -436,22 +459,9 @@ namespace MWWorld { reference.getRefData().disable(); - /* remove scripts on container contents */ - if( reference.getTypeName()==typeid (ESM::Container).name() || - reference.getTypeName()==typeid (ESM::NPC).name() || - reference.getTypeName()==typeid (ESM::Creature).name()) - { - MWWorld::ContainerStore& container = MWWorld::Class::get(reference).getContainerStore(reference); - for(MWWorld::ContainerStoreIterator it = container.begin(); it != container.end(); ++it) - { - std::string script = MWWorld::Class::get(*it).getScript(*it); - if(script != "") - { - MWWorld::Ptr item = *it; - mLocalScripts.remove (item); - } - } - } + removeContainerScripts(reference); + if(MWWorld::Class::get(reference).getScript(reference) != "") + mLocalScripts.remove(reference); if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) mWorldScene->removeObjectFromScene (reference); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 905e6fd96..fa5b41038 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -105,6 +105,9 @@ namespace MWWorld float getNpcActivationDistance (); float getObjectActivationDistance (); + void removeContainerScripts(const Ptr& reference); + void addContainerScripts(const Ptr& reference, Ptr::CellStore* cell); + public: World (OEngine::Render::OgreRenderer& renderer, From 951eb1b236b600ad6c35888649c79bbe8b59e5e8 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Wed, 16 Jan 2013 20:10:20 +0100 Subject: [PATCH 368/378] Dialogue: return a response from "Info Refusal" when disposition is not satisfied --- apps/openmw/mwdialogue/filter.cpp | 42 +++++++++++++++++++++++++------ apps/openmw/mwdialogue/filter.hpp | 30 +++++++++++----------- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 99be5554a..d515c3ac4 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -121,6 +121,13 @@ bool MWDialogue::Filter::testSelectStructs (const ESM::DialInfo& info) const return true; } +bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info) const +{ + int actorDisposition = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor); + + return actorDisposition >= info.mData.mDisposition; +} + bool MWDialogue::Filter::testSelectStruct (const SelectWrapper& select) const { if (select.isNpcOnly() && mActor.getTypeName()!=typeid (ESM::NPC).name()) @@ -547,17 +554,38 @@ MWDialogue::Filter::Filter (const MWWorld::Ptr& actor, int choice, bool talkedTo : mActor (actor), mChoice (choice), mTalkedToPlayer (talkedToPlayer) {} -bool MWDialogue::Filter::operator() (const ESM::DialInfo& info) const -{ - return testActor (info) && testPlayer (info) && testSelectStructs (info); -} - const ESM::DialInfo *MWDialogue::Filter::search (const ESM::Dialogue& dialogue) const { + bool infoRefusal = false; + + // Iterate over topic responses to find a matching one for (std::vector::const_iterator iter = dialogue.mInfo.begin(); iter!=dialogue.mInfo.end(); ++iter) - if ((*this) (*iter)) - return &*iter; + { + if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter)) + { + if (testDisposition (*iter)) + return &*iter; + else + infoRefusal = true; + } + } + + if (infoRefusal) + { + // No response is valid because of low NPC disposition, + // search a response in the topic "Info Refusal" + + const MWWorld::Store &dialogues = + MWBase::Environment::get().getWorld()->getStore().get(); + + const ESM::Dialogue& infoRefusalDialogue = *dialogues.find ("Info Refusal"); + + for (std::vector::const_iterator iter = infoRefusalDialogue.mInfo.begin(); + iter!=infoRefusalDialogue.mInfo.end(); ++iter) + if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter) && testDisposition(*iter)) + return &*iter; + } return 0; } diff --git a/apps/openmw/mwdialogue/filter.hpp b/apps/openmw/mwdialogue/filter.hpp index 7c8f1116f..aa8934bfc 100644 --- a/apps/openmw/mwdialogue/filter.hpp +++ b/apps/openmw/mwdialogue/filter.hpp @@ -18,39 +18,39 @@ namespace MWDialogue MWWorld::Ptr mActor; int mChoice; bool mTalkedToPlayer; - + bool testActor (const ESM::DialInfo& info) const; ///< Is this the right actor for this \a info? - + bool testPlayer (const ESM::DialInfo& info) const; ///< Do the player and the cell the player is currently in match \a info? - + bool testSelectStructs (const ESM::DialInfo& info) const; ///< Are all select structs matching? - + + bool testDisposition (const ESM::DialInfo& info) const; + ///< Is the actor disposition toward the player high enough? + bool testSelectStruct (const SelectWrapper& select) const; - + bool testSelectStructNumeric (const SelectWrapper& select) const; - + int getSelectStructInteger (const SelectWrapper& select) const; - + bool getSelectStructBoolean (const SelectWrapper& select) const; - + int getFactionRank (const MWWorld::Ptr& actor, const std::string& factionId) const; - + bool hasFactionRankSkillRequirements (const MWWorld::Ptr& actor, const std::string& factionId, int rank) const; bool hasFactionRankReputationRequirements (const MWWorld::Ptr& actor, const std::string& factionId, int rank) const; - + public: - - Filter (const MWWorld::Ptr& actor, int choice, bool talkedToPlayer); - bool operator() (const ESM::DialInfo& info) const; - ///< \return does the dialogue match? - + Filter (const MWWorld::Ptr& actor, int choice, bool talkedToPlayer); + const ESM::DialInfo *search (const ESM::Dialogue& dialogue) const; }; } From 05796d85a4e4d659ba4a0d61f5e7fa6be3dfacb3 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sat, 19 Jan 2013 02:43:55 +0100 Subject: [PATCH 369/378] NPC: take stats from NPDT12 into account Some available stats (level, reputation and disposition) were not used for NPC with auto-calculated stats. --- apps/openmw/mwclass/npc.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 586f9638d..cfbc64b87 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -100,14 +100,15 @@ namespace MWClass } else { - /// \todo do something with mNpdt12 maybe:p for (int i=0; i<8; ++i) data->mCreatureStats.getAttribute (i).set (10); for (int i=0; i<3; ++i) data->mCreatureStats.setDynamic (i, 10); - data->mCreatureStats.setLevel (1); + data->mCreatureStats.setLevel(ref->mBase->mNpdt12.mLevel); + data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt12.mDisposition); + data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation); } data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello); From 43e85ea0c65301c28f83b053ca65847460815e72 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sun, 20 Jan 2013 01:54:35 +0100 Subject: [PATCH 370/378] Disallow redirection to info refusal for greetings --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 9 +++++---- apps/openmw/mwdialogue/filter.cpp | 15 +++++++++++++-- apps/openmw/mwdialogue/filter.hpp | 7 ++++++- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index a3bb640e6..a8e6a98c9 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -139,7 +139,8 @@ namespace MWDialogue { if(it->mType == ESM::Dialogue::Greeting) { - if (const ESM::DialInfo *info = filter.search (*it)) + // Search a response (we do not accept a fallback to "Info refusal" here) + if (const ESM::DialInfo *info = filter.search (*it, false)) { //initialise the GUI MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Dialogue); @@ -248,7 +249,7 @@ namespace MWDialogue const ESM::Dialogue& dialogue = *dialogues.find (topic); - if (const ESM::DialInfo *info = filter.search (dialogue)) + if (const ESM::DialInfo *info = filter.search (dialogue, true)) { parseText (info->mResponse); @@ -295,7 +296,7 @@ namespace MWDialogue { if (iter->mType == ESM::Dialogue::Topic) { - if (filter.search (*iter)) + if (filter.responseAvailable (*iter)) { std::string lower = Misc::StringUtils::lowerCase(iter->mId); mActorKnownTopics.push_back (lower); @@ -412,7 +413,7 @@ namespace MWDialogue { Filter filter (mActor, mChoice, mTalkedTo); - if (const ESM::DialInfo *info = filter.search (mDialogueMap[mLastTopic])) + if (const ESM::DialInfo *info = filter.search (mDialogueMap[mLastTopic], true)) { mChoiceMap.clear(); mChoice = -1; diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index d515c3ac4..5d56e94b0 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -554,7 +554,7 @@ MWDialogue::Filter::Filter (const MWWorld::Ptr& actor, int choice, bool talkedTo : mActor (actor), mChoice (choice), mTalkedToPlayer (talkedToPlayer) {} -const ESM::DialInfo *MWDialogue::Filter::search (const ESM::Dialogue& dialogue) const +const ESM::DialInfo *MWDialogue::Filter::search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const { bool infoRefusal = false; @@ -571,7 +571,7 @@ const ESM::DialInfo *MWDialogue::Filter::search (const ESM::Dialogue& dialogue) } } - if (infoRefusal) + if (infoRefusal && fallbackToInfoRefusal) { // No response is valid because of low NPC disposition, // search a response in the topic "Info Refusal" @@ -590,3 +590,14 @@ const ESM::DialInfo *MWDialogue::Filter::search (const ESM::Dialogue& dialogue) return 0; } +bool MWDialogue::Filter::responseAvailable (const ESM::Dialogue& dialogue) const +{ + for (std::vector::const_iterator iter = dialogue.mInfo.begin(); + iter!=dialogue.mInfo.end(); ++iter) + { + if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter)) + return true; + } + + return false; +} diff --git a/apps/openmw/mwdialogue/filter.hpp b/apps/openmw/mwdialogue/filter.hpp index aa8934bfc..707c0154b 100644 --- a/apps/openmw/mwdialogue/filter.hpp +++ b/apps/openmw/mwdialogue/filter.hpp @@ -51,7 +51,12 @@ namespace MWDialogue Filter (const MWWorld::Ptr& actor, int choice, bool talkedToPlayer); - const ESM::DialInfo *search (const ESM::Dialogue& dialogue) const; + const ESM::DialInfo *search (const ESM::Dialogue& dialogue, const bool fallbackToInfoRefusal) const; + ///< Get a matching response for the requested dialogue. + /// Redirect to "Info Refusal" topic if a response fulfills all conditions but disposition. + + bool responseAvailable (const ESM::Dialogue& dialogue) const; + ///< Does a matching response exist? (disposition is ignored for this check) }; } From 736e4716131238669105001cadd10f0308cc1a98 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sun, 20 Jan 2013 15:57:34 +0100 Subject: [PATCH 371/378] Print a fallback text when no topic response is found --- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index a8e6a98c9..f548c46f7 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -249,12 +249,12 @@ namespace MWDialogue const ESM::Dialogue& dialogue = *dialogues.find (topic); + MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); + if (const ESM::DialInfo *info = filter.search (dialogue, true)) { parseText (info->mResponse); - MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); - if (dialogue.mType==ESM::Dialogue::Persuasion) { std::string modifiedTopic = "s" + topic; @@ -278,6 +278,13 @@ namespace MWDialogue mLastTopic = topic; mLastDialogue = *info; } + else + { + // no response found, print a fallback text + win->addTitle (topic); + win->addText ("…"); + + } } void DialogueManager::updateTopics() From af9e126487704ccf69090e7f4851e2ea8828d2f1 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sat, 19 Jan 2013 22:53:41 +0100 Subject: [PATCH 372/378] =?UTF-8?q?Add=20unicode=20number=20for=20ellipsis?= =?UTF-8?q?=20(=E2=80=A6)=20to=20code=20range?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- files/mygui/openmw_font.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/files/mygui/openmw_font.xml b/files/mygui/openmw_font.xml index 0383c9942..b1446fae1 100644 --- a/files/mygui/openmw_font.xml +++ b/files/mygui/openmw_font.xml @@ -11,6 +11,7 @@ + From 31c71c029d9bfb662b1661c0422d6e325682ff1f Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Sun, 20 Jan 2013 17:01:30 +0000 Subject: [PATCH 373/378] objects with scripts attached, that are inside containers will behave correctly when the container is moved --- apps/openmw/mwworld/worldimp.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ee18e5d95..46c58ecab 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -685,6 +685,7 @@ namespace MWWorld { mWorldScene->removeObjectFromScene (ptr); mLocalScripts.remove (ptr); + removeContainerScripts (ptr); } } } @@ -698,6 +699,8 @@ namespace MWWorld CellStore *currCell = ptr.getCell(); bool isPlayer = ptr == mPlayer->getPlayer(); bool haveToMove = mWorldScene->isCellActive(*currCell) || isPlayer; + + removeContainerScripts(ptr); if (*currCell != newCell) { @@ -725,6 +728,8 @@ namespace MWWorld MWWorld::Ptr copy = MWWorld::Class::get(ptr).copyToCell(ptr, newCell); + addContainerScripts(copy, &newCell); + mRendering->moveObjectToCell(copy, vec, currCell); if (MWWorld::Class::get(ptr).isActor()) @@ -1333,6 +1338,7 @@ namespace MWWorld if (!script.empty()) { mLocalScripts.add(script, dropped); } + addContainerScripts(dropped, &cell); } } From f5f3c2e62d167943cc4542e9400067c246137291 Mon Sep 17 00:00:00 2001 From: Tom Mason Date: Mon, 21 Jan 2013 20:06:08 +0000 Subject: [PATCH 374/378] enabling / disabling should not affect scripts --- apps/openmw/mwworld/worldimp.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 46c58ecab..24d139b37 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -427,8 +427,6 @@ namespace MWWorld { reference.getRefData().enable(); - addContainerScripts(reference, reference.getCell()); - if(mWorldScene->getActiveCells().find (reference.getCell()) != mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) mWorldScene->addObjectToScene (reference); } @@ -459,10 +457,6 @@ namespace MWWorld { reference.getRefData().disable(); - removeContainerScripts(reference); - if(MWWorld::Class::get(reference).getScript(reference) != "") - mLocalScripts.remove(reference); - if(mWorldScene->getActiveCells().find (reference.getCell())!=mWorldScene->getActiveCells().end() && reference.getRefData().getCount()) mWorldScene->removeObjectFromScene (reference); } From 90d05858efe026d1b08252303f9c6b3534eeaa06 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 22 Jan 2013 11:50:08 +0100 Subject: [PATCH 375/378] disabling dialogue sub-views for now --- apps/opencs/view/world/tablesubview.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index bb4bb76c6..f4deceb49 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -21,5 +21,6 @@ void CSVWorld::TableSubView::setEditLock (bool locked) void CSVWorld::TableSubView::rowActivated (const QModelIndex& index) { - focusId (mTable->getUniversalId (index.row())); + /// \todo re-enable, after dialogue sub views have been fixed up +// focusId (mTable->getUniversalId (index.row())); } \ No newline at end of file From cac68c9e87fe50eb8b28644ecfcead4c3ca3f548 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 23 Jan 2013 11:48:47 +0100 Subject: [PATCH 376/378] Removed an outdated section from CMakeLists --- CMakeLists.txt | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 208d348fb..2313d2d95 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,35 +67,6 @@ endif() # We probably support older versions than this. cmake_minimum_required(VERSION 2.6) -# -# Pre-built binaries being used? -# -IF(EXISTS "${CMAKE_SOURCE_DIR}/prebuilt/vc100-mt-gd/ogre_1_7_1") - set(PREBUILT_DIR "${CMAKE_SOURCE_DIR}/prebuilt/vc100-mt-gd") - message (STATUS "OpenMW pre-built binaries found at ${PREBUILT_DIR}.") - - SET(ENV{OGRE_HOME} "${PREBUILT_DIR}/ogre_1_7_1") - - SET(ENV{BOOST_ROOT} "${PREBUILT_DIR}/boost_1_42_0") - set(Boost_USE_STATIC_LIBS ON) - set(Boost_USE_MULTITHREADED ON) - set(ENV{BOOST_INCLUDEDIR} "${BOOST_ROOT}/include") - set(ENV{BOOST_LIBRARYDIR} "${BOOST_ROOT}/lib") - - set(ENV{FREETYPE_DIR} "${PREBUILT_DIR}/freetype-2.3.5-1") - - set(USE_MPG123 OFF) - set(USE_AUDIERE ON) - set(AUDIERE_INCLUDE_DIR "${PREBUILT_DIR}/audiere-1.9.4/include") - set(AUDIERE_LIBRARY "${PREBUILT_DIR}/audiere-1.9.4/lib/audiere.lib") - - set(ENV{OPENALDIR} "${PREBUILT_DIR}/OpenAL 1.1 SDK") - - set(BULLET_ROOT "${PREBUILT_DIR}/bullet") -ELSE() - message (STATUS "OpenMW pre-built binaries not found. Using standard locations.") -ENDIF() - # source directory: libs set(LIBDIR ${CMAKE_SOURCE_DIR}/libs) From ed9a9904b431bb27ed9725e7c83023cf5fd11bab Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Thu, 24 Jan 2013 19:39:31 +0100 Subject: [PATCH 377/378] Dialogue filter: search script variables case-insensitively --- apps/openmw/mwdialogue/filter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 5d56e94b0..c1542515a 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -169,7 +169,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c int i = 0; for (; i (script->mVarNames.size()); ++i) - if (script->mVarNames[i]==name) + if (Misc::StringUtils::lowerCase(script->mVarNames[i]) == name) break; if (i>=static_cast (script->mVarNames.size())) From 19dff822f4f963fdab88d5f8f80c9b026edd6f91 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Thu, 24 Jan 2013 19:43:21 +0100 Subject: [PATCH 378/378] Dialogue: do not filter on disposition for creatures --- apps/openmw/mwdialogue/filter.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index c1542515a..09bb0ddc4 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -123,6 +123,11 @@ bool MWDialogue::Filter::testSelectStructs (const ESM::DialInfo& info) const bool MWDialogue::Filter::testDisposition (const ESM::DialInfo& info) const { + bool isCreature = (mActor.getTypeName() != typeid (ESM::NPC).name()); + + if (isCreature) + return true; + int actorDisposition = MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mActor); return actorDisposition >= info.mData.mDisposition;