diff options
23 files changed, 682 insertions, 241 deletions
diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp index 34d30e1d69..e4cb67134a 100644 --- a/devtools/create_project/create_project.cpp +++ b/devtools/create_project/create_project.cpp @@ -20,7 +20,7 @@ * */ -//#define ENABLE_XCODE +#define ENABLE_XCODE // HACK to allow building with the SDL backend on MinGW // see bug #1800764 "TOOLS: MinGW tools building broken" @@ -336,7 +336,12 @@ int main(int argc, char *argv[]) { setup.defines.splice(setup.defines.begin(), featureDefines); // Windows only has support for the SDL backend, so we hardcode it here (along with winmm) - setup.defines.push_back("WIN32"); + if (projectType != kProjectXcode) { + setup.defines.push_back("WIN32"); + } else { + setup.defines.push_back("POSIX"); + setup.defines.push_back("MACOSX"); // This will break iOS, but allows OS X to catch up on browser_osx. + } setup.defines.push_back("SDL_BACKEND"); if (!useSDL2) { cout << "\nLinking to SDL 1.2\n\n"; @@ -410,7 +415,6 @@ int main(int argc, char *argv[]) { globalWarnings.push_back("-Wwrite-strings"); // The following are not warnings at all... We should consider adding them to // a different list of parameters. - globalWarnings.push_back("-fno-rtti"); globalWarnings.push_back("-fno-exceptions"); globalWarnings.push_back("-fcheck-new"); @@ -572,7 +576,7 @@ int main(int argc, char *argv[]) { globalWarnings.push_back("-fno-exceptions"); globalWarnings.push_back("-fcheck-new"); - provider = new CreateProjectTool::XCodeProvider(globalWarnings, projectWarnings); + provider = new CreateProjectTool::XcodeProvider(globalWarnings, projectWarnings); break; } @@ -1039,7 +1043,7 @@ bool producesObjectFile(const std::string &fileName) { std::string n, ext; splitFilename(fileName, n, ext); - if (ext == "cpp" || ext == "c" || ext == "asm") + if (ext == "cpp" || ext == "c" || ext == "asm" || ext == "m" || ext == "mm") return true; else return false; @@ -1279,8 +1283,9 @@ void ProjectProvider::createProject(BuildSetup &setup) { for (UUIDMap::const_iterator i = _uuidMap.begin(); i != _uuidMap.end(); ++i) { if (i->first == setup.projectName) continue; - + // Retain the files between engines if we're creating a single project in.clear(); ex.clear(); + const std::string moduleDir = setup.srcDir + targetFolder + i->first; createModuleList(moduleDir, setup.defines, setup.testDirs, in, ex); @@ -1290,7 +1295,6 @@ void ProjectProvider::createProject(BuildSetup &setup) { if (setup.tests) { // Create the main project file. in.clear(); ex.clear(); - createModuleList(setup.srcDir + "/backends", setup.defines, setup.testDirs, in, ex); createModuleList(setup.srcDir + "/backends/platform/sdl", setup.defines, setup.testDirs, in, ex); createModuleList(setup.srcDir + "/base", setup.defines, setup.testDirs, in, ex); @@ -1305,7 +1309,6 @@ void ProjectProvider::createProject(BuildSetup &setup) { } else if (!setup.devTools) { // Last but not least create the main project file. in.clear(); ex.clear(); - // File list for the Project file createModuleList(setup.srcDir + "/backends", setup.defines, setup.testDirs, in, ex); createModuleList(setup.srcDir + "/backends/platform/sdl", setup.defines, setup.testDirs, in, ex); diff --git a/devtools/create_project/xcode.cpp b/devtools/create_project/xcode.cpp index d95bf3e9ee..babd530ad7 100644 --- a/devtools/create_project/xcode.cpp +++ b/devtools/create_project/xcode.cpp @@ -30,6 +30,14 @@ namespace CreateProjectTool { #define DEBUG_XCODE_HASH 0 +#ifdef ENABLE_IOS +#define IOS_TARGET 0 +#define OSX_TARGET 1 +#define SIM_TARGET 2 +#else +#define OSX_TARGET 0 +#endif + #define ADD_DEFINE(defines, name) \ defines.push_back(name); @@ -54,39 +62,172 @@ namespace CreateProjectTool { #define REMOVE_SETTING(config, key) \ config.settings.erase(key); -#define ADD_BUILD_FILE(id, name, comment) { \ +#define ADD_BUILD_FILE(id, name, fileRefId, comment) { \ Object *buildFile = new Object(this, id, name, "PBXBuildFile", "PBXBuildFile", comment); \ - buildFile->addProperty("fileRef", getHash(name), name, SettingsNoValue); \ + buildFile->addProperty("fileRef", fileRefId, name, SettingsNoValue); \ _buildFile.add(buildFile); \ _buildFile.flags = SettingsSingleItem; \ } -#define ADD_FILE_REFERENCE(name, properties) { \ - Object *fileRef = new Object(this, name, name, "PBXFileReference", "PBXFileReference", name); \ +#define ADD_FILE_REFERENCE(id, name, properties) { \ + Object *fileRef = new Object(this, id, name, "PBXFileReference", "PBXFileReference", name); \ if (!properties.fileEncoding.empty()) fileRef->addProperty("fileEncoding", properties.fileEncoding, "", SettingsNoValue); \ - if (!properties.lastKnownFileType.empty()) fileRef->addProperty("lastKnownFileType", properties.lastKnownFileType, "", SettingsNoValue); \ - if (!properties.fileName.empty()) fileRef->addProperty("name", properties.fileName, "", SettingsNoValue); \ - if (!properties.filePath.empty()) fileRef->addProperty("path", properties.filePath, "", SettingsNoValue); \ + if (!properties.lastKnownFileType.empty()) fileRef->addProperty("lastKnownFileType", properties.lastKnownFileType, "", SettingsNoValue|SettingsQuoteVariable); \ + if (!properties.fileName.empty()) fileRef->addProperty("name", properties.fileName, "", SettingsNoValue|SettingsQuoteVariable); \ + if (!properties.filePath.empty()) fileRef->addProperty("path", properties.filePath, "", SettingsNoValue|SettingsQuoteVariable); \ if (!properties.sourceTree.empty()) fileRef->addProperty("sourceTree", properties.sourceTree, "", SettingsNoValue); \ _fileReference.add(fileRef); \ _fileReference.flags = SettingsSingleItem; \ } -XCodeProvider::XCodeProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version) +bool producesObjectFileOnOSX(const std::string &fileName) { + std::string n, ext; + splitFilename(fileName, n, ext); + + // Note that the difference between this and the general producesObjectFile is that + // this one adds Objective-C(++), and removes asm-support. + if (ext == "cpp" || ext == "c" || ext == "m" || ext == "mm") + return true; + else + return false; +} + +XcodeProvider::Group::Group(XcodeProvider *objectParent, const std::string &groupName, const std::string &uniqueName, const std::string &path) : Object(objectParent, uniqueName, groupName, "PBXGroup", "", groupName) { + addProperty("name", name, "", SettingsNoValue|SettingsQuoteVariable); + addProperty("sourceTree", "<group>", "", SettingsNoValue|SettingsQuoteVariable); + + if (path != "") { + addProperty("path", path, "", SettingsNoValue|SettingsQuoteVariable); + } + _childOrder = 0; + _treeName = uniqueName; +} + +void XcodeProvider::Group::ensureChildExists(const std::string &name) { + std::map<std::string, Group*>::iterator it = _childGroups.find(name); + if (it == _childGroups.end()) { + Group *child = new Group(parent, name, this->_treeName + '/' + name, name); + _childGroups[name] = child; + addChildGroup(child); + parent->_groups.add(child); + } +} + +void XcodeProvider::Group::addChildInternal(const std::string &id, const std::string &comment) { + if (properties.find("children") == properties.end()) { + Property children; + children.hasOrder = true; + children.flags = SettingsAsList; + properties["children"] = children; + } + properties["children"].settings[id] = Setting("", comment + " in Sources", SettingsNoValue, 0, _childOrder++); + if (_childOrder == 1) { + // Force children to use () even when there is only 1 child. + // Also this enforces the use of "," after the single item, instead of ; (see writeProperty) + properties["children"].flags |= SettingsSingleItem; + } else { + properties["children"].flags ^= SettingsSingleItem; + } + +} + +void XcodeProvider::Group::addChildGroup(const Group* group) { + addChildInternal(parent->getHash(group->_treeName), group->_treeName); +} + +void XcodeProvider::Group::addChildFile(const std::string &name) { + std::string id = "FileReference_" + _treeName + "/" + name; + addChildInternal(parent->getHash(id), name); + FileProperty property = FileProperty(name, name, name, "\"<group>\""); + + parent->addFileReference(id, name, property); + if (producesObjectFileOnOSX(name)) { + parent->addBuildFile(_treeName + "/" + name, name, parent->getHash(id), name + " in Sources"); + } +} + +void XcodeProvider::Group::addChildByHash(const std::string &hash, const std::string &name) { + addChildInternal(hash, name); +} + +XcodeProvider::Group *XcodeProvider::Group::getChildGroup(const std::string &name) { + std::map<std::string, Group*>::iterator it = _childGroups.find(name); + assert(it != _childGroups.end()); + return it->second; +} + +XcodeProvider::Group *XcodeProvider::touchGroupsForPath(const std::string &path) { + if (_rootSourceGroup == NULL) { + assert (path == _projectRoot); + _rootSourceGroup = new Group(this, "Sources", path, path); + _groups.add(_rootSourceGroup); + return _rootSourceGroup; + } else { + assert(path.find(_projectRoot) == 0); + std::string subPath = path.substr(_projectRoot.size() + 1); + Group *currentGroup = _rootSourceGroup; + size_t firstPathComponent = subPath.find_first_of('/'); + // We assume here that all paths have trailing '/', otherwise this breaks. + while (firstPathComponent != std::string::npos) { + currentGroup->ensureChildExists(subPath.substr(0, firstPathComponent)); + currentGroup = currentGroup->getChildGroup(subPath.substr(0, firstPathComponent)); + subPath = subPath.substr(firstPathComponent + 1); + firstPathComponent = subPath.find_first_of('/'); + } + return currentGroup; + } +} + +void XcodeProvider::addFileReference(const std::string &id, const std::string &name, FileProperty properties) { + Object *fileRef = new Object(this, id, name, "PBXFileReference", "PBXFileReference", name); + if (!properties.fileEncoding.empty()) fileRef->addProperty("fileEncoding", properties.fileEncoding, "", SettingsNoValue); + if (!properties.lastKnownFileType.empty()) fileRef->addProperty("lastKnownFileType", properties.lastKnownFileType, "", SettingsNoValue|SettingsQuoteVariable); + if (!properties.fileName.empty()) fileRef->addProperty("name", properties.fileName, "", SettingsNoValue|SettingsQuoteVariable); + if (!properties.filePath.empty()) fileRef->addProperty("path", properties.filePath, "", SettingsNoValue|SettingsQuoteVariable); + if (!properties.sourceTree.empty()) fileRef->addProperty("sourceTree", properties.sourceTree, "", SettingsNoValue); + _fileReference.add(fileRef); + _fileReference.flags = SettingsSingleItem; +} + +void XcodeProvider::addProductFileReference(const std::string &id, const std::string &name) { + Object *fileRef = new Object(this, id, name, "PBXFileReference", "PBXFileReference", name); + fileRef->addProperty("explicitFileType", "compiled.mach-o.executable", "", SettingsNoValue|SettingsQuoteVariable); + fileRef->addProperty("includeInIndex", "0", "", SettingsNoValue); + fileRef->addProperty("path", name, "", SettingsNoValue|SettingsQuoteVariable); + fileRef->addProperty("sourceTree", "BUILT_PRODUCTS_DIR", "", SettingsNoValue); + _fileReference.add(fileRef); + _fileReference.flags = SettingsSingleItem; +} + +void XcodeProvider::addBuildFile(const std::string &id, const std::string &name, const std::string &fileRefId, const std::string &comment) { + + Object *buildFile = new Object(this, id, name, "PBXBuildFile", "PBXBuildFile", comment); + buildFile->addProperty("fileRef", fileRefId, name, SettingsNoValue); + _buildFile.add(buildFile); + _buildFile.flags = SettingsSingleItem; +} + +XcodeProvider::XcodeProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version) : ProjectProvider(global_warnings, project_warnings, version) { + _rootSourceGroup = NULL; } -void XCodeProvider::createWorkspace(const BuildSetup &setup) { +void XcodeProvider::createWorkspace(const BuildSetup &setup) { // Create project folder std::string workspace = setup.outputDir + '/' + PROJECT_NAME ".xcodeproj"; createDirectory(workspace); - + _projectRoot = setup.srcDir; + touchGroupsForPath(_projectRoot); + // Setup global objects setupDefines(setup); +#ifdef ENABLE_IOS _targets.push_back(PROJECT_DESCRIPTION "-iPhone"); +#endif _targets.push_back(PROJECT_DESCRIPTION "-OS X"); +#ifdef ENABLE_IOS _targets.push_back(PROJECT_DESCRIPTION "-Simulator"); - +#endif setupCopyFilesBuildPhase(); setupFrameworksBuildPhase(); setupNativeTarget(); @@ -97,7 +238,7 @@ void XCodeProvider::createWorkspace(const BuildSetup &setup) { // We are done with constructing all the object graph and we got through every project, output the main project file // (this is kind of a hack since other providers use separate project files) -void XCodeProvider::createOtherBuildFiles(const BuildSetup &setup) { +void XcodeProvider::createOtherBuildFiles(const BuildSetup &setup) { // This needs to be done at the end when all build files have been accounted for setupSourcesBuildPhase(); @@ -105,7 +246,7 @@ void XCodeProvider::createOtherBuildFiles(const BuildSetup &setup) { } // Store information about a project here, for use at the end -void XCodeProvider::createProjectFile(const std::string &, const std::string &, const BuildSetup &setup, const std::string &moduleDir, +void XcodeProvider::createProjectFile(const std::string &, const std::string &, const BuildSetup &setup, const std::string &moduleDir, const StringList &includeList, const StringList &excludeList) { std::string modulePath; if (!moduleDir.compare(0, setup.srcDir.size(), setup.srcDir)) { @@ -124,7 +265,7 @@ void XCodeProvider::createProjectFile(const std::string &, const std::string &, ////////////////////////////////////////////////////////////////////////// // Main Project file ////////////////////////////////////////////////////////////////////////// -void XCodeProvider::ouputMainProjectFile(const BuildSetup &setup) { +void XcodeProvider::ouputMainProjectFile(const BuildSetup &setup) { std::ofstream project((setup.outputDir + '/' + PROJECT_NAME ".xcodeproj" + '/' + "project.pbxproj").c_str()); if (!project) error("Could not open \"" + setup.outputDir + '/' + PROJECT_NAME ".xcodeproj" + '/' + "project.pbxproj\" for writing"); @@ -164,92 +305,93 @@ void XCodeProvider::ouputMainProjectFile(const BuildSetup &setup) { ////////////////////////////////////////////////////////////////////////// // Files ////////////////////////////////////////////////////////////////////////// -void XCodeProvider::writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation, +void XcodeProvider::writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation, const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix) { - // Add comments for shared lists - _buildFile.comment = "PBXBuildFile"; - _fileReference.comment = "PBXFileReference"; - - // Init root group - _groups.comment = "PBXGroup"; - - // Create group - std::string name = getLastPathComponent(dir.name); - Object *group = new Object(this, "PBXGroup_" + name , "PBXGroup", "PBXGroup", "", name); - - // List of children - Property children; - children.hasOrder = true; - children.flags = SettingsAsList; - - group->addProperty("name", name, "", SettingsNoValue|SettingsQuoteVariable); - group->addProperty("sourceTree", "<group>", "", SettingsNoValue|SettingsQuoteVariable); - - int order = 0; + // Ensure that top-level groups are generated for i.e. engines/ + Group *group = touchGroupsForPath(filePrefix); for (FileNode::NodeList::const_iterator i = dir.children.begin(); i != dir.children.end(); ++i) { const FileNode *node = *i; - std::string id = "FileReference_" + node->name; - FileProperty property = FileProperty(node->name, node->name, node->name, "<group>"); - - ADD_SETTING_ORDER_NOVALUE(children, getHash(id), node->name, order++); - ADD_BUILD_FILE(id, node->name, node->name + " in Sources"); - ADD_FILE_REFERENCE(node->name, property); - + // Iff it is a file, then add (build) file references. Since we're using Groups and not File References + // for folders, we shouldn't add folders as file references, obviously. + if (node->children.empty()) { + group->addChildFile(node->name); + } // Process child nodes if (!node->children.empty()) writeFileListToProject(*node, projectFile, indentation + 1, duplicate, objPrefix + node->name + '_', filePrefix + node->name + '/'); } - - group->properties["children"] = children; - - _groups.add(group); } ////////////////////////////////////////////////////////////////////////// // Setup functions ////////////////////////////////////////////////////////////////////////// -void XCodeProvider::setupCopyFilesBuildPhase() { +void XcodeProvider::setupCopyFilesBuildPhase() { // Nothing to do here } +#define DEF_SYSFRAMEWORK(framework) properties[framework".framework"] = FileProperty("wrapper.framework", framework".framework", "System/Library/Frameworks/" framework ".framework", "SDKROOT"); \ + ADD_SETTING_ORDER_NOVALUE(children, getHash(framework".framework"), framework".framework", fwOrder++); + +#define DEF_LOCALLIB_STATIC(lib) properties[lib".a"] = FileProperty("archive.ar", lib".a", "/opt/local/lib/" lib ".a", "\"<group>\""); \ + ADD_SETTING_ORDER_NOVALUE(children, getHash(lib".a"), lib".a", fwOrder++); + /** * Sets up the frameworks build phase. * * (each native target has different build rules) */ -void XCodeProvider::setupFrameworksBuildPhase() { +void XcodeProvider::setupFrameworksBuildPhase() { _frameworksBuildPhase.comment = "PBXFrameworksBuildPhase"; + // Just use a hardcoded id for the Frameworks-group + Group *frameworksGroup = new Group(this, "Frameworks", "PBXGroup_CustomTemplate_Frameworks_", ""); + + Property children; + children.hasOrder = true; + children.flags = SettingsAsList; + // Setup framework file properties std::map<std::string, FileProperty> properties; - + int fwOrder = 0; // Frameworks - properties["ApplicationServices.framework"] = FileProperty("wrapper.framework", "ApplicationServices.framework", "System/Library/Frameworks/ApplicationServices.framework", "SDKROOT"); - properties["AudioToolbox.framework"] = FileProperty("wrapper.framework", "AudioToolbox.framework", "System/Library/Frameworks/AudioToolbox.framework", "SDKROOT"); - properties["AudioUnit.framework"] = FileProperty("wrapper.framework", "AudioUnit.framework", "System/Library/Frameworks/AudioUnit.framework", "SDKROOT"); - properties["Carbon.framework"] = FileProperty("wrapper.framework", "Carbon.framework", "System/Library/Frameworks/Carbon.framework", "SDKROOT"); - properties["Cocoa.framework"] = FileProperty("wrapper.framework", "Cocoa.framework", "System/Library/Frameworks/Cocoa.framework", "SDKROOT"); - properties["CoreAudio.framework"] = FileProperty("wrapper.framework", "CoreAudio.framework", "System/Library/Frameworks/CoreAudio.framework", "SDKROOT"); - properties["CoreFoundation.framework"] = FileProperty("wrapper.framework", "CoreFoundation.framework", "System/Library/Frameworks/CoreFoundation.framework", "SDKROOT"); - properties["CoreMIDI.framework"] = FileProperty("wrapper.framework", "CoreMIDI.framework", "System/Library/Frameworks/CoreMIDI.framework", "SDKROOT"); - properties["Foundation.framework"] = FileProperty("wrapper.framework", "Foundation.framework", "System/Library/Frameworks/Foundation.framework", "SDKROOT"); - properties["IOKit.framework"] = FileProperty("wrapper.framework", "IOKit.framework", "System/Library/Frameworks/IOKit.framework", "SDKROOT"); - properties["OpenGLES.framework"] = FileProperty("wrapper.framework", "OpenGLES.framework", "System/Library/Frameworks/OpenGLES.framework", "SDKROOT"); - properties["QuartzCore.framework"] = FileProperty("wrapper.framework", "QuartzCore.framework", "System/Library/Frameworks/QuartzCore.framework", "SDKROOT"); - properties["QuickTime.framework"] = FileProperty("wrapper.framework", "QuickTime.framework", "System/Library/Frameworks/QuickTime.framework", "SDKROOT"); - properties["UIKit.framework"] = FileProperty("wrapper.framework", "UIKit.framework", "System/Library/Frameworks/UIKit.framework", "SDKROOT"); + DEF_SYSFRAMEWORK("ApplicationServices"); + DEF_SYSFRAMEWORK("AudioToolbox"); + DEF_SYSFRAMEWORK("AudioUnit"); + DEF_SYSFRAMEWORK("Carbon"); + DEF_SYSFRAMEWORK("Cocoa"); + DEF_SYSFRAMEWORK("CoreAudio"); + DEF_SYSFRAMEWORK("CoreFoundation"); + DEF_SYSFRAMEWORK("CoreMIDI"); + DEF_SYSFRAMEWORK("Foundation"); + DEF_SYSFRAMEWORK("IOKit"); + DEF_SYSFRAMEWORK("OpenGLES"); + DEF_SYSFRAMEWORK("QuartzCore"); + DEF_SYSFRAMEWORK("QuickTime"); + DEF_SYSFRAMEWORK("UIKit"); + // Optionals: + DEF_SYSFRAMEWORK("OpenGL"); // Local libraries - properties["libFLAC.a"] = FileProperty("archive.ar", "libFLAC.a", "lib/libFLAC.a", "\"<group>\""); - properties["libmad.a"] = FileProperty("archive.ar", "libmad.a", "lib/libmad.a", "\"<group>\""); - //properties["libmpeg2.a"] = FileProperty("archive.ar", "libmpeg2.a", "lib/libmpeg2.a", "\"<group>\""); - properties["libvorbisidec.a"] = FileProperty("archive.ar", "libvorbisidec.a", "lib/libvorbisidec.a", "\"<group>\""); + DEF_LOCALLIB_STATIC("libFLAC"); + DEF_LOCALLIB_STATIC("libmad"); + DEF_LOCALLIB_STATIC("libvorbisidec"); + DEF_LOCALLIB_STATIC("libfreetype"); +// DEF_LOCALLIB_STATIC("libmpeg2"); + + frameworksGroup->properties["children"] = children; + _groups.add(frameworksGroup); + // Force this to be added as a sub-group in the root. + _rootSourceGroup->addChildGroup(frameworksGroup); + + // Declare this here, as it's used across the three targets + int order = 0; +#ifdef ENABLE_IOS ////////////////////////////////////////////////////////////////////////// // iPhone - Object *framework_iPhone = new Object(this, "PBXFrameworksBuildPhase_" + _targets[0], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks"); + Object *framework_iPhone = new Object(this, "PBXFrameworksBuildPhase_" + _targets[IOS_TARGET], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks"); framework_iPhone->addProperty("buildActionMask", "2147483647", "", SettingsNoValue); framework_iPhone->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue); @@ -272,23 +414,22 @@ void XCodeProvider::setupFrameworksBuildPhase() { frameworks_iPhone.push_back("libvorbisidec.a"); frameworks_iPhone.push_back("OpenGLES.framework"); - int order = 0; for (ValueList::iterator framework = frameworks_iPhone.begin(); framework != frameworks_iPhone.end(); framework++) { std::string id = "Frameworks_" + *framework + "_iphone"; std::string comment = *framework + " in Frameworks"; ADD_SETTING_ORDER_NOVALUE(iPhone_files, getHash(id), comment, order++); - ADD_BUILD_FILE(id, *framework, comment); - ADD_FILE_REFERENCE(*framework, properties[*framework]); + ADD_BUILD_FILE(id, *framework, getHash(*framework), comment); + ADD_FILE_REFERENCE(*framework, *framework, properties[*framework]); } framework_iPhone->properties["files"] = iPhone_files; _frameworksBuildPhase.add(framework_iPhone); - +#endif ////////////////////////////////////////////////////////////////////////// // ScummVM-OS X - Object *framework_OSX = new Object(this, "PBXFrameworksBuildPhase_" + _targets[1], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks"); + Object *framework_OSX = new Object(this, "PBXFrameworksBuildPhase_" + _targets[OSX_TARGET], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks"); framework_OSX->addProperty("buildActionMask", "2147483647", "", SettingsNoValue); framework_OSX->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue); @@ -311,6 +452,8 @@ void XCodeProvider::setupFrameworksBuildPhase() { frameworks_osx.push_back("IOKit.framework"); frameworks_osx.push_back("Cocoa.framework"); frameworks_osx.push_back("AudioUnit.framework"); + // Optionals: + frameworks_osx.push_back("OpenGL.framework"); order = 0; for (ValueList::iterator framework = frameworks_osx.begin(); framework != frameworks_osx.end(); framework++) { @@ -318,17 +461,17 @@ void XCodeProvider::setupFrameworksBuildPhase() { std::string comment = *framework + " in Frameworks"; ADD_SETTING_ORDER_NOVALUE(osx_files, getHash(id), comment, order++); - ADD_BUILD_FILE(id, *framework, comment); - ADD_FILE_REFERENCE(*framework, properties[*framework]); + ADD_BUILD_FILE(id, *framework, getHash(*framework), comment); + ADD_FILE_REFERENCE(*framework, *framework, properties[*framework]); } framework_OSX->properties["files"] = osx_files; _frameworksBuildPhase.add(framework_OSX); - +#ifdef ENABLE_IOS ////////////////////////////////////////////////////////////////////////// // Simulator - Object *framework_simulator = new Object(this, "PBXFrameworksBuildPhase_" + _targets[2], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks"); + Object *framework_simulator = new Object(this, "PBXFrameworksBuildPhase_" + _targets[SIM_TARGET], "PBXFrameworksBuildPhase", "PBXFrameworksBuildPhase", "", "Frameworks"); framework_simulator->addProperty("buildActionMask", "2147483647", "", SettingsNoValue); framework_simulator->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue); @@ -353,20 +496,28 @@ void XCodeProvider::setupFrameworksBuildPhase() { std::string comment = *framework + " in Frameworks"; ADD_SETTING_ORDER_NOVALUE(simulator_files, getHash(id), comment, order++); - ADD_BUILD_FILE(id, *framework, comment); - ADD_FILE_REFERENCE(*framework, properties[*framework]); + ADD_BUILD_FILE(id, *framework, getHash(*framework), comment); + ADD_FILE_REFERENCE(*framework, *framework, properties[*framework]); } framework_simulator->properties["files"] = simulator_files; _frameworksBuildPhase.add(framework_simulator); +#endif } -void XCodeProvider::setupNativeTarget() { +void XcodeProvider::setupNativeTarget() { _nativeTarget.comment = "PBXNativeTarget"; + // Just use a hardcoded id for the Products-group + Group *productsGroup = new Group(this, "Products", "PBXGroup_CustomTemplate_Products_" , ""); // Output native target section for (unsigned int i = 0; i < _targets.size(); i++) { +#ifndef ENABLE_IOS + if (i != OSX_TARGET) { // TODO: Fix iOS-targets, for now just disable them. + continue; + } +#endif Object *target = new Object(this, "PBXNativeTarget_" + _targets[i], "PBXNativeTarget", "PBXNativeTarget", "", _targets[i]); target->addProperty("buildConfigurationList", getHash("XCConfigurationList_" + _targets[i]), "Build configuration list for PBXNativeTarget \"" + _targets[i] + "\"", SettingsNoValue); @@ -385,14 +536,18 @@ void XCodeProvider::setupNativeTarget() { target->addProperty("name", _targets[i], "", SettingsNoValue|SettingsQuoteVariable); target->addProperty("productName", PROJECT_NAME, "", SettingsNoValue); + addProductFileReference("PBXFileReference_" PROJECT_DESCRIPTION ".app_" + _targets[i], PROJECT_DESCRIPTION ".app"); + productsGroup->addChildByHash(getHash("PBXFileReference_" PROJECT_DESCRIPTION ".app_" + _targets[i]), PROJECT_DESCRIPTION ".app"); target->addProperty("productReference", getHash("PBXFileReference_" PROJECT_DESCRIPTION ".app_" + _targets[i]), PROJECT_DESCRIPTION ".app", SettingsNoValue); target->addProperty("productType", "com.apple.product-type.application", "", SettingsNoValue|SettingsQuoteVariable); _nativeTarget.add(target); } + _rootSourceGroup->addChildGroup(productsGroup); + _groups.add(productsGroup); } -void XCodeProvider::setupProject() { +void XcodeProvider::setupProject() { _project.comment = "PBXProject"; Object *project = new Object(this, "PBXProject", "PBXProject", "PBXProject", "", "Project object"); @@ -411,22 +566,30 @@ void XCodeProvider::setupProject() { ADD_SETTING_ORDER_NOVALUE(regions, "German", "", 3); project->properties["knownRegions"] = regions; - project->addProperty("mainGroup", getHash("PBXGroup_CustomTemplate"), "CustomTemplate", SettingsNoValue); - project->addProperty("projectDirPath", "", "", SettingsNoValue|SettingsQuoteVariable); + project->addProperty("mainGroup", _rootSourceGroup->getHashRef(), "CustomTemplate", SettingsNoValue); + project->addProperty("projectDirPath", _projectRoot, "", SettingsNoValue|SettingsQuoteVariable); project->addProperty("projectRoot", "", "", SettingsNoValue|SettingsQuoteVariable); // List of targets Property targets; targets.flags = SettingsAsList; - targets.settings[getHash("PBXNativeTarget_" + _targets[0])] = Setting("", _targets[0], SettingsNoValue, 0, 0); - targets.settings[getHash("PBXNativeTarget_" + _targets[1])] = Setting("", _targets[1], SettingsNoValue, 0, 1); - targets.settings[getHash("PBXNativeTarget_" + _targets[2])] = Setting("", _targets[2], SettingsNoValue, 0, 2); +#ifdef ENABLE_IOS + targets.settings[getHash("PBXNativeTarget_" + _targets[IOS_TARGET])] = Setting("", _targets[IOS_TARGET], SettingsNoValue, 0, 0); +#endif + targets.settings[getHash("PBXNativeTarget_" + _targets[OSX_TARGET])] = Setting("", _targets[OSX_TARGET], SettingsNoValue, 0, 1); +#ifdef ENABLE_IOS + targets.settings[getHash("PBXNativeTarget_" + _targets[SIM_TARGET])] = Setting("", _targets[SIM_TARGET], SettingsNoValue, 0, 2); +#endif project->properties["targets"] = targets; +#ifndef ENABLE_IOS + // Force list even when there is only a single target + project->properties["targets"].flags |= SettingsSingleItem; +#endif _project.add(project); } -void XCodeProvider::setupResourcesBuildPhase() { +void XcodeProvider::setupResourcesBuildPhase() { _resourcesBuildPhase.comment = "PBXResourcesBuildPhase"; // Setup resource file properties @@ -483,7 +646,7 @@ void XCodeProvider::setupResourcesBuildPhase() { ADD_SETTING_ORDER_NOVALUE(files, getHash(id), comment, order++); // TODO Fix crash when adding build file for data //ADD_BUILD_FILE(id, *file, comment); - ADD_FILE_REFERENCE(*file, properties[*file]); + ADD_FILE_REFERENCE(*file, *file, properties[*file]); } // Add custom files depending on the target @@ -503,12 +666,41 @@ void XCodeProvider::setupResourcesBuildPhase() { } } -void XCodeProvider::setupSourcesBuildPhase() { - // TODO +void XcodeProvider::setupSourcesBuildPhase() { + _sourcesBuildPhase.comment = "PBXSourcesBuildPhase"; + + // Setup source file properties + std::map<std::string, FileProperty> properties; + + // Same as for containers: a rule for each native target + for (unsigned int i = 0; i < _targets.size(); i++) { + Object *source = new Object(this, "PBXSourcesBuildPhase_" + _targets[i], "PBXSourcesBuildPhase", "PBXSourcesBuildPhase", "", "Sources"); + + source->addProperty("buildActionMask", "2147483647", "", SettingsNoValue); + + Property files; + files.hasOrder = true; + files.flags = SettingsAsList; + + int order = 0; + for (std::vector<Object*>::iterator file = _buildFile.objects.begin(); file !=_buildFile.objects.end(); ++file) { + if (!producesObjectFileOnOSX((*file)->name)) { + continue; + } + std::string comment = (*file)->name + " in Sources"; + ADD_SETTING_ORDER_NOVALUE(files, getHash((*file)->id), comment, order++); + } + + source->properties["files"] = files; + + source->addProperty("runOnlyForDeploymentPostprocessing", "0", "", SettingsNoValue); + + _sourcesBuildPhase.add(source); + } } // Setup all build configurations -void XCodeProvider::setupBuildConfiguration() { +void XcodeProvider::setupBuildConfiguration() { _buildConfiguration.comment = "XCBuildConfiguration"; _buildConfiguration.flags = SettingsAsList; @@ -516,9 +708,9 @@ void XCodeProvider::setupBuildConfiguration() { ///**************************************** // * iPhone // ****************************************/ - +#ifdef ENABLE_IOS // Debug - Object *iPhone_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-iPhone_Debug", _targets[0] /* ScummVM-iPhone */, "XCBuildConfiguration", "PBXNativeTarget", "Debug"); + Object *iPhone_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-iPhone_Debug", _targets[IOS_TARGET] /* ScummVM-iPhone */, "XCBuildConfiguration", "PBXNativeTarget", "Debug"); Property iPhone_Debug; ADD_SETTING_QUOTE(iPhone_Debug, "ARCHS", "$(ARCHS_UNIVERSAL_IPHONE_OS)"); ADD_SETTING_QUOTE(iPhone_Debug, "CODE_SIGN_IDENTITY", "iPhone Developer"); @@ -539,10 +731,10 @@ void XCodeProvider::setupBuildConfiguration() { ADD_SETTING(iPhone_Debug, "GCC_THUMB_SUPPORT", "NO"); ADD_SETTING(iPhone_Debug, "GCC_UNROLL_LOOPS", "YES"); ValueList iPhone_HeaderSearchPaths; - iPhone_HeaderSearchPaths.push_back("../../engines/"); - iPhone_HeaderSearchPaths.push_back("../../"); + iPhone_HeaderSearchPaths.push_back("$(SRCROOT)/engines/"); + iPhone_HeaderSearchPaths.push_back("$(SRCROOT)"); iPhone_HeaderSearchPaths.push_back("include/"); - ADD_SETTING_LIST(iPhone_Debug, "HEADER_SEARCH_PATHS", iPhone_HeaderSearchPaths, SettingsAsList|SettingsNoQuote, 5); + ADD_SETTING_LIST(iPhone_Debug, "HEADER_SEARCH_PATHS", iPhone_HeaderSearchPaths, SettingsAsList|SettingsQuoteVariable, 5); ADD_SETTING(iPhone_Debug, "INFOPLIST_FILE", "Info.plist"); ValueList iPhone_LibPaths; iPhone_LibPaths.push_back("$(inherited)"); @@ -560,7 +752,7 @@ void XCodeProvider::setupBuildConfiguration() { iPhone_Debug_Object->properties["buildSettings"] = iPhone_Debug; // Release - Object *iPhone_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-iPhone_Release", _targets[0] /* ScummVM-iPhone */, "XCBuildConfiguration", "PBXNativeTarget", "Release"); + Object *iPhone_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-iPhone_Release", _targets[IOS_TARGET] /* ScummVM-iPhone */, "XCBuildConfiguration", "PBXNativeTarget", "Release"); Property iPhone_Release(iPhone_Debug); ADD_SETTING(iPhone_Release, "GCC_OPTIMIZATION_LEVEL", "3"); ADD_SETTING(iPhone_Release, "COPY_PHASE_STRIP", "YES"); @@ -572,7 +764,7 @@ void XCodeProvider::setupBuildConfiguration() { _buildConfiguration.add(iPhone_Debug_Object); _buildConfiguration.add(iPhone_Release_Object); - +#endif /**************************************** * scummvm ****************************************/ @@ -581,13 +773,14 @@ void XCodeProvider::setupBuildConfiguration() { Object *scummvm_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_NAME "_Debug", PROJECT_NAME, "XCBuildConfiguration", "PBXProject", "Debug"); Property scummvm_Debug; ADD_SETTING(scummvm_Debug, "ALWAYS_SEARCH_USER_PATHS", "NO"); + ADD_SETTING_QUOTE(scummvm_Debug, "USER_HEADER_SEARCH_PATHS", "$(SRCROOT) $(SRCROOT)/engines"); ADD_SETTING_QUOTE(scummvm_Debug, "ARCHS", "$(ARCHS_STANDARD_32_BIT)"); ADD_SETTING_QUOTE(scummvm_Debug, "CODE_SIGN_IDENTITY", "Don't Code Sign"); ADD_SETTING_QUOTE_VAR(scummvm_Debug, "CODE_SIGN_IDENTITY[sdk=iphoneos*]", "Don't Code Sign"); ADD_SETTING_QUOTE(scummvm_Debug, "FRAMEWORK_SEARCH_PATHS", ""); ADD_SETTING(scummvm_Debug, "GCC_C_LANGUAGE_STANDARD", "c99"); ADD_SETTING(scummvm_Debug, "GCC_ENABLE_CPP_EXCEPTIONS", "NO"); - ADD_SETTING(scummvm_Debug, "GCC_ENABLE_CPP_RTTI", "NO"); + ADD_SETTING(scummvm_Debug, "GCC_ENABLE_CPP_RTTI", "YES"); ADD_SETTING(scummvm_Debug, "GCC_INPUT_FILETYPE", "automatic"); ADD_SETTING(scummvm_Debug, "GCC_OPTIMIZATION_LEVEL", "0"); ValueList scummvm_defines(_defines); @@ -601,15 +794,15 @@ void XCodeProvider::setupBuildConfiguration() { ADD_SETTING(scummvm_Debug, "GCC_WARN_UNUSED_VARIABLE", "YES"); ValueList scummvm_HeaderPaths; scummvm_HeaderPaths.push_back("include/"); - scummvm_HeaderPaths.push_back("../../engines/"); - scummvm_HeaderPaths.push_back("../../"); - ADD_SETTING_LIST(scummvm_Debug, "HEADER_SEARCH_PATHS", scummvm_HeaderPaths, SettingsNoQuote|SettingsAsList, 5); + scummvm_HeaderPaths.push_back("$(SRCROOT)/engines/"); + scummvm_HeaderPaths.push_back("$(SRCROOT)"); + ADD_SETTING_LIST(scummvm_Debug, "HEADER_SEARCH_PATHS", scummvm_HeaderPaths, SettingsQuoteVariable|SettingsAsList, 5); ADD_SETTING_QUOTE(scummvm_Debug, "LIBRARY_SEARCH_PATHS", ""); ADD_SETTING(scummvm_Debug, "ONLY_ACTIVE_ARCH", "YES"); ADD_SETTING_QUOTE(scummvm_Debug, "OTHER_CFLAGS", ""); ADD_SETTING_QUOTE(scummvm_Debug, "OTHER_LDFLAGS", "-lz"); ADD_SETTING(scummvm_Debug, "PREBINDING", "NO"); - ADD_SETTING(scummvm_Debug, "SDKROOT", "macosx10.6"); + ADD_SETTING(scummvm_Debug, "SDKROOT", "macosx"); scummvm_Debug_Object->addProperty("name", "Debug", "", SettingsNoValue); scummvm_Debug_Object->properties["buildSettings"] = scummvm_Debug; @@ -633,7 +826,7 @@ void XCodeProvider::setupBuildConfiguration() { ****************************************/ // Debug - Object *scummvmOSX_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-OSX_Debug", _targets[1] /* ScummVM-OS X */, "XCBuildConfiguration", "PBXNativeTarget", "Debug"); + Object *scummvmOSX_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-OSX_Debug", _targets[OSX_TARGET] /* ScummVM-OS X */, "XCBuildConfiguration", "PBXNativeTarget", "Debug"); Property scummvmOSX_Debug; ADD_SETTING_QUOTE(scummvmOSX_Debug, "ARCHS", "$(NATIVE_ARCH)"); ADD_SETTING(scummvmOSX_Debug, "COMPRESS_PNG_FILES", "NO"); @@ -642,7 +835,7 @@ void XCodeProvider::setupBuildConfiguration() { ADD_SETTING_QUOTE(scummvmOSX_Debug, "FRAMEWORK_SEARCH_PATHS", ""); ADD_SETTING(scummvmOSX_Debug, "GCC_C_LANGUAGE_STANDARD", "c99"); ADD_SETTING(scummvmOSX_Debug, "GCC_ENABLE_CPP_EXCEPTIONS", "NO"); - ADD_SETTING(scummvmOSX_Debug, "GCC_ENABLE_CPP_RTTI", "NO"); + ADD_SETTING(scummvmOSX_Debug, "GCC_ENABLE_CPP_RTTI", "YES"); ADD_SETTING(scummvmOSX_Debug, "GCC_DYNAMIC_NO_PIC", "NO"); ADD_SETTING(scummvmOSX_Debug, "GCC_ENABLE_FIX_AND_CONTINUE", "NO"); ADD_SETTING(scummvmOSX_Debug, "GCC_OPTIMIZATION_LEVEL", "0"); @@ -656,11 +849,12 @@ void XCodeProvider::setupBuildConfiguration() { ValueList scummvmOSX_HeaderPaths; scummvmOSX_HeaderPaths.push_back("/opt/local/include/SDL"); scummvmOSX_HeaderPaths.push_back("/opt/local/include"); + scummvmOSX_HeaderPaths.push_back("/opt/local/include/freetype2"); scummvmOSX_HeaderPaths.push_back("include/"); - scummvmOSX_HeaderPaths.push_back("../../engines/"); - scummvmOSX_HeaderPaths.push_back("../../"); - ADD_SETTING_LIST(scummvmOSX_Debug, "HEADER_SEARCH_PATHS", scummvmOSX_HeaderPaths, SettingsNoQuote|SettingsAsList, 5); - ADD_SETTING_QUOTE(scummvmOSX_Debug, "INFOPLIST_FILE", "$(SRCROOT)/../macosx/Info.plist"); + scummvmOSX_HeaderPaths.push_back("$(SRCROOT)/engines/"); + scummvmOSX_HeaderPaths.push_back("$(SRCROOT)"); + ADD_SETTING_LIST(scummvmOSX_Debug, "HEADER_SEARCH_PATHS", scummvmOSX_HeaderPaths, SettingsQuoteVariable|SettingsAsList, 5); + ADD_SETTING_QUOTE(scummvmOSX_Debug, "INFOPLIST_FILE", "$(SRCROOT)/dists/macosx/Info.plist"); ValueList scummvmOSX_LibPaths; scummvmOSX_LibPaths.push_back("/sw/lib"); scummvmOSX_LibPaths.push_back("/opt/local/lib"); @@ -671,6 +865,10 @@ void XCodeProvider::setupBuildConfiguration() { ValueList scummvmOSX_LdFlags; scummvmOSX_LdFlags.push_back("-lSDLmain"); scummvmOSX_LdFlags.push_back("-logg"); + scummvmOSX_LdFlags.push_back("-lpng"); + scummvmOSX_LdFlags.push_back("-ljpeg"); + scummvmOSX_LdFlags.push_back("-ltheora"); + scummvmOSX_LdFlags.push_back("-lfreetype"); scummvmOSX_LdFlags.push_back("-lvorbisfile"); scummvmOSX_LdFlags.push_back("-lvorbis"); scummvmOSX_LdFlags.push_back("-lmad"); @@ -685,7 +883,7 @@ void XCodeProvider::setupBuildConfiguration() { scummvmOSX_Debug_Object->properties["buildSettings"] = scummvmOSX_Debug; // Release - Object *scummvmOSX_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-OSX_Release", _targets[1] /* ScummVM-OS X */, "XCBuildConfiguration", "PBXNativeTarget", "Release"); + Object *scummvmOSX_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-OSX_Release", _targets[OSX_TARGET] /* ScummVM-OS X */, "XCBuildConfiguration", "PBXNativeTarget", "Release"); Property scummvmOSX_Release(scummvmOSX_Debug); ADD_SETTING(scummvmOSX_Release, "COPY_PHASE_STRIP", "YES"); REMOVE_SETTING(scummvmOSX_Release, "GCC_DYNAMIC_NO_PIC"); @@ -697,13 +895,13 @@ void XCodeProvider::setupBuildConfiguration() { _buildConfiguration.add(scummvmOSX_Debug_Object); _buildConfiguration.add(scummvmOSX_Release_Object); - +#ifdef ENABLE_IOS /**************************************** * ScummVM-Simulator ****************************************/ // Debug - Object *scummvmSimulator_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-Simulator_Debug", _targets[2] /* ScummVM-Simulator */, "XCBuildConfiguration", "PBXNativeTarget", "Debug"); + Object *scummvmSimulator_Debug_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-Simulator_Debug", _targets[SIM_TARGET] /* ScummVM-Simulator */, "XCBuildConfiguration", "PBXNativeTarget", "Debug"); Property scummvmSimulator_Debug(iPhone_Debug); ADD_SETTING_QUOTE(scummvmSimulator_Debug, "FRAMEWORK_SEARCH_PATHS", "$(inherited)"); ADD_SETTING_LIST(scummvmSimulator_Debug, "GCC_PREPROCESSOR_DEFINITIONS", scummvm_defines, SettingsNoQuote|SettingsAsList, 5); @@ -715,7 +913,7 @@ void XCodeProvider::setupBuildConfiguration() { scummvmSimulator_Debug_Object->properties["buildSettings"] = scummvmSimulator_Debug; // Release - Object *scummvmSimulator_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-Simulator_Release", _targets[2] /* ScummVM-Simulator */, "XCBuildConfiguration", "PBXNativeTarget", "Release"); + Object *scummvmSimulator_Release_Object = new Object(this, "XCBuildConfiguration_" PROJECT_DESCRIPTION "-Simulator_Release", _targets[SIM_TARGET] /* ScummVM-Simulator */, "XCBuildConfiguration", "PBXNativeTarget", "Release"); Property scummvmSimulator_Release(scummvmSimulator_Debug); ADD_SETTING(scummvmSimulator_Release, "COPY_PHASE_STRIP", "YES"); ADD_SETTING(scummvmSimulator_Release, "GCC_OPTIMIZATION_LEVEL", "3"); @@ -732,7 +930,7 @@ void XCodeProvider::setupBuildConfiguration() { // Configuration List _configurationList.comment = "XCConfigurationList"; _configurationList.flags = SettingsAsList; - +#endif // Warning: This assumes we have all configurations with a Debug & Release pair for (std::vector<Object *>::iterator config = _buildConfiguration.objects.begin(); config != _buildConfiguration.objects.end(); config++) { @@ -758,7 +956,7 @@ void XCodeProvider::setupBuildConfiguration() { ////////////////////////////////////////////////////////////////////////// // Setup global defines -void XCodeProvider::setupDefines(const BuildSetup &setup) { +void XcodeProvider::setupDefines(const BuildSetup &setup) { for (StringList::const_iterator i = setup.defines.begin(); i != setup.defines.end(); ++i) { if (*i == "HAVE_NASM") // Not supported on Mac (TODO: change how it's handled in main class or add it only in MSVC/CodeBlocks providers?) @@ -772,7 +970,6 @@ void XCodeProvider::setupDefines(const BuildSetup &setup) { ADD_DEFINE(_defines, "SCUMM_LITTLE_ENDIAN"); ADD_DEFINE(_defines, "UNIX"); ADD_DEFINE(_defines, "SCUMMVM"); - ADD_DEFINE(_defines, "USE_TREMOR"); } ////////////////////////////////////////////////////////////////////////// @@ -780,7 +977,7 @@ void XCodeProvider::setupDefines(const BuildSetup &setup) { ////////////////////////////////////////////////////////////////////////// // TODO use md5 to compute a file hash (and fall back to standard key generation if not passed a file) -std::string XCodeProvider::getHash(std::string key) { +std::string XcodeProvider::getHash(std::string key) { #if DEBUG_XCODE_HASH return key; @@ -800,7 +997,7 @@ std::string XCodeProvider::getHash(std::string key) { bool isSeparator (char s) { return (s == '-'); } -std::string XCodeProvider::newHash() const { +std::string XcodeProvider::newHash() const { std::string hash = createUUID(); // Remove { and - from UUID and resize to 96-bits uppercase hex string @@ -832,7 +1029,7 @@ std::string replace(std::string input, const std::string find, std::string repla return input; } -std::string XCodeProvider::writeProperty(const std::string &variable, Property &prop, int flags) const { +std::string XcodeProvider::writeProperty(const std::string &variable, Property &prop, int flags) const { std::string output; output += (flags & SettingsSingleItem ? "" : "\t\t\t") + variable + " = "; @@ -847,7 +1044,9 @@ std::string XCodeProvider::writeProperty(const std::string &variable, Property & output += writeSetting((*setting).first, (*setting).second); - if ((prop.flags & SettingsAsList) && prop.settings.size() > 1) { + // The combination of SettingsAsList, and SettingsSingleItem should use "," and not ";" (i.e children + // in PBXGroup, so we special case that case here. + if ((prop.flags & SettingsAsList) && (prop.settings.size() > 1 || (prop.flags & SettingsSingleItem))) { output += (prop.settings.size() > 0) ? ",\n" : "\n"; } else { output += ";"; @@ -861,13 +1060,13 @@ std::string XCodeProvider::writeProperty(const std::string &variable, Property & return output; } -std::string XCodeProvider::writeSetting(const std::string &variable, std::string value, std::string comment, int flags, int indent) const { +std::string XcodeProvider::writeSetting(const std::string &variable, std::string value, std::string comment, int flags, int indent) const { return writeSetting(variable, Setting(value, comment, flags, indent)); } // Heavily modified (not in a good way) function, imported from the QMake // XCode project generator pbuilder_pbx.cpp, writeSettings() (under LGPL 2.1) -std::string XCodeProvider::writeSetting(const std::string &variable, const Setting &setting) const { +std::string XcodeProvider::writeSetting(const std::string &variable, const Setting &setting) const { std::string output; const std::string quote = (setting.flags & SettingsNoQuote) ? "" : "\""; const std::string escape_quote = quote.empty() ? "" : "\\" + quote; diff --git a/devtools/create_project/xcode.h b/devtools/create_project/xcode.h index f86e7c555c..2686d14986 100644 --- a/devtools/create_project/xcode.h +++ b/devtools/create_project/xcode.h @@ -30,9 +30,9 @@ namespace CreateProjectTool { -class XCodeProvider : public ProjectProvider { +class XcodeProvider : public ProjectProvider { public: - XCodeProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version = 0); + XcodeProvider(StringList &global_warnings, std::map<std::string, StringList> &project_warnings, const int version = 0); protected: @@ -45,7 +45,6 @@ protected: void writeFileListToProject(const FileNode &dir, std::ofstream &projectFile, const int indentation, const StringList &duplicate, const std::string &objPrefix, const std::string &filePrefix); - private: enum { SettingsAsList = 0x01, @@ -169,7 +168,7 @@ private: PropertyList properties; // List of object properties, including output configuration // Constructs an object and add a default type property - Object(XCodeProvider *objectParent, std::string objectId, std::string objectName, std::string objectType, std::string objectRefType = "", std::string objectComment = "") + Object(XcodeProvider *objectParent, std::string objectId, std::string objectName, std::string objectType, std::string objectRefType = "", std::string objectComment = "") : id(objectId), name(objectName), refType(objectRefType), comment(objectComment), parent(objectParent) { assert(objectParent); assert(!objectId.empty()); @@ -210,9 +209,10 @@ private: return output; } + // Slight hack, to allow Group access to parent. + protected: + XcodeProvider *parent; private: - XCodeProvider *parent; - // Returns the type property (should always be the first in the properties map) std::string getType() { assert(!properties.empty()); @@ -258,6 +258,36 @@ private: } }; + // A class to maintain a folder-reference group-hierarchy, which together with the functionality below + // allows for breaking up sub-paths into a chain of groups. This helps with merging engines into the + // overall group-layout. + class Group : public Object { + int _childOrder; + std::map<std::string, Group *> _childGroups; + std::string _treeName; + void addChildInternal(const std::string &id, const std::string &comment); + public: + Group(XcodeProvider *objectParent, const std::string &groupName, const std::string &uniqueName, const std::string &path); + void addChildFile(const std::string &name); + void addChildByHash(const std::string &hash, const std::string &name); + // Should be passed the hash for the entry + void addChildGroup(const Group* group); + void ensureChildExists(const std::string &name); + Group *getChildGroup(const std::string &name); + std::string getHashRef() const { return parent->getHash(id); } + }; + + // The path used by the root-source group + std::string _projectRoot; + // The base source group, currently also re-purposed for containing the various support-groups. + Group *_rootSourceGroup; + // Helper function to create the chain of groups for the various subfolders. Necessary as + // create_project likes to start in engines/ + Group *touchGroupsForPath(const std::string &path); + // Functionality for adding file-refs and build-files, as Group-objects need to be able to do this. + void addFileReference(const std::string &id, const std::string &name, FileProperty properties); + void addProductFileReference(const std::string &id, const std::string &name); + void addBuildFile(const std::string &id, const std::string &name, const std::string &fileRefId, const std::string &comment); // All objects std::map<std::string, std::string> _hashDictionnary; ValueList _defines; diff --git a/engines/made/pmvplayer.cpp b/engines/made/pmvplayer.cpp index 6ea0dc24d0..453e2a4872 100644 --- a/engines/made/pmvplayer.cpp +++ b/engines/made/pmvplayer.cpp @@ -118,6 +118,8 @@ bool PmvPlayer::play(const char *filename) { // get it to work well? _audioStream = Audio::makeQueuingAudioStream(soundFreq, false); + SoundDecoderData *soundDecoderData = new SoundDecoderData(); + while (!_vm->shouldQuit() && !_aborted && !_fd->eos() && frameNumber < frameCount) { int32 frameTime = _vm->_system->getMillis(); @@ -153,7 +155,7 @@ bool PmvPlayer::play(const char *filename) { soundSize = chunkCount * chunkSize; soundData = (byte *)malloc(soundSize); - decompressSound(audioData + 8, soundData, chunkSize, chunkCount); + decompressSound(audioData + 8, soundData, chunkSize, chunkCount, NULL, soundDecoderData); _audioStream->queueBuffer(soundData, soundSize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED); } @@ -213,6 +215,7 @@ bool PmvPlayer::play(const char *filename) { } + delete soundDecoderData; delete[] frameData; _audioStream->finish(); diff --git a/engines/made/screenfx.cpp b/engines/made/screenfx.cpp index 3f98cbb9ab..bae59f05cc 100644 --- a/engines/made/screenfx.cpp +++ b/engines/made/screenfx.cpp @@ -201,7 +201,7 @@ void ScreenEffects::startBlendedPalette(byte *palette, byte *newPalette, int col } void ScreenEffects::stepBlendedPalette() { - if (_blendedPaletteStatus._active && _blendedPaletteStatus._value < _blendedPaletteStatus._maxValue) { + if (_blendedPaletteStatus._active && _blendedPaletteStatus._value <= _blendedPaletteStatus._maxValue) { setBlendedPalette(_blendedPaletteStatus._palette, _blendedPaletteStatus._newPalette, _blendedPaletteStatus._colorCount, _blendedPaletteStatus._value, _blendedPaletteStatus._maxValue); if (_blendedPaletteStatus._value == _blendedPaletteStatus._maxValue) diff --git a/engines/made/sound.cpp b/engines/made/sound.cpp index 91e855cbf5..ad49031e7b 100644 --- a/engines/made/sound.cpp +++ b/engines/made/sound.cpp @@ -133,10 +133,10 @@ void ManholeEgaSoundDecompressor::update3() { _sample2 += _sample1; } -void decompressSound(byte *source, byte *dest, uint16 chunkSize, uint16 chunkCount, SoundEnergyArray *soundEnergyArray) { +void decompressSound(byte *source, byte *dest, uint16 chunkSize, uint16 chunkCount, SoundEnergyArray *soundEnergyArray, SoundDecoderData *soundDecoderData) { - int16 prevSample = 0, workSample = 0; - byte soundBuffer[1025]; + int16 prevSample, workSample; + byte* soundBuffer; byte deltaSoundBuffer[1024]; int16 soundBuffer2[16]; byte deltaType, type; @@ -159,6 +159,15 @@ void decompressSound(byte *source, byte *dest, uint16 chunkSize, uint16 chunkCou if (soundEnergyArray) soundEnergyArray->clear(); + if (soundDecoderData) { + soundBuffer = soundDecoderData->_soundBuffer; + prevSample = soundDecoderData->_prevSample; + } else { + soundBuffer = new byte[1025]; + memset(soundBuffer, 0x80, 1025); + prevSample = 0; + } + while (chunkCount--) { deltaType = (*source) >> 6; workChunkSize = chunkSize; @@ -233,6 +242,11 @@ void decompressSound(byte *source, byte *dest, uint16 chunkSize, uint16 chunkCou } if (deltaType > 0) { + // NB: The original did not add this extra value at the end (as far + // as I can tell), and so technically read past the filled part of + // soundBuffer. + soundBuffer[workChunkSize] = soundBuffer[workChunkSize - 1]; + if (deltaType == 1) { for (i = 0; i < chunkSize - 1; i += 2) { l = i / 2; @@ -255,9 +269,13 @@ void decompressSound(byte *source, byte *dest, uint16 chunkSize, uint16 chunkCou prevSample = workSample; memcpy(dest, soundBuffer, chunkSize); dest += chunkSize; - } + if (soundDecoderData) { + soundDecoderData->_prevSample = prevSample; + } else { + delete[] soundBuffer; + } } } // End of namespace Made diff --git a/engines/made/sound.h b/engines/made/sound.h index 6ffca13aaa..72537322f9 100644 --- a/engines/made/sound.h +++ b/engines/made/sound.h @@ -53,7 +53,22 @@ struct SoundEnergyItem { typedef Common::Array<SoundEnergyItem> SoundEnergyArray; -void decompressSound(byte *source, byte *dest, uint16 chunkSize, uint16 chunkCount, SoundEnergyArray *soundEnergyArray = NULL); + +// Persistent data for decompressSound(). When calling decompressSound() +// repeatedly (for the same stream), pass the same SoundDecoderData object to +// ensure decoding properly resumes. +class SoundDecoderData { +public: + SoundDecoderData() { + memset(_soundBuffer, 0x80, sizeof(_soundBuffer)); + _prevSample = 0; + } + + byte _soundBuffer[1025]; + int16 _prevSample; +}; + +void decompressSound(byte *source, byte *dest, uint16 chunkSize, uint16 chunkCount, SoundEnergyArray *soundEnergyArray = NULL, SoundDecoderData *decoderData = NULL); } // End of namespace Made diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp index c859248055..7ca41e253c 100644 --- a/engines/mohawk/riven_external.cpp +++ b/engines/mohawk/riven_external.cpp @@ -2573,26 +2573,47 @@ void RivenExternal::xt7500_checkmarbles(uint16 argc, uint16 *argv) { void RivenExternal::xt7600_setupmarbles(uint16 argc, uint16 *argv) { // Draw the small marbles when we're a step away from the waffle - uint16 baseBitmapId = _vm->findResourceID(ID_TBMP, "*tsmallred"); + + // Convert from marble X coordinate to screen X coordinate + static const uint16 xPosOffsets[] = { + 246, 245, 244, 243, 243, 241, 240, 240, 239, 238, 237, 237, 236, 235, 234, 233, 232, 231, 230, 229, 228, 227, 226, 226, 225 + }; + + // Convert from marble Y coordinate to screen Y coordinate + static const uint16 yPosOffsets[] = { + 261, 263, 265, 267, 268, 270, 272, 274, 276, 278, 281, 284, 285, 288, 290, 293, 295, 298, 300, 303, 306, 309, 311, 314, 316 + }; + + // Handle spacing for y coordinates due to the angle + static const double yAdjusts[] = { + 4.56, 4.68, 4.76, 4.84, 4.84, 4.96, 5.04, 5.04, 5.12, 5.2, 5.28, 5.28, 5.36, 5.44, 5.4, 5.6, 5.72, 5.8, 5.88, 5.96, 6.04, 6.12, 6.2, 6.2, 6.28 + }; + + // Waffle state of 0 is up, 1 down bool waffleDown = _vm->_vars["twaffle"] != 0; // Note that each of the small marble images is exactly 4x2 + // The original seems to scale the marble images from extras.mhk, but + // we're using the pre-scaled images in the stack. + uint16 baseBitmapId = _vm->findResourceID(ID_TBMP, "*tsmallred"); for (uint16 i = 0; i < kMarbleCount; i++) { - uint32 &var = _vm->_vars[s_marbleNames[i]]; + uint32 var = _vm->_vars[s_marbleNames[i]]; if (var == 0) { // The marble is still in its initial place // (Note that this is still drawn even if the waffle is down) - int marbleX = 376 + i * 2; - int marbleY = 253 + i * 4; - _vm->_gfx->copyImageToScreen(baseBitmapId + i, marbleX, marbleY, marbleX + kSmallMarbleWidth, marbleY + kSmallMarbleHeight); + static const uint16 defaultX[] = { 375, 377, 379, 381, 383, 385 }; + static const uint16 defaultY[] = { 253, 257, 261, 265, 268, 273 }; + _vm->_gfx->copyImageToScreen(baseBitmapId + i, defaultX[i], defaultY[i], defaultX[i] + kSmallMarbleWidth, defaultY[i] + kSmallMarbleHeight); } else if (waffleDown) { // The marble is on the grid and the waffle is down // (Nothing to draw here) } else { // The marble is on the grid and the waffle is up - // TODO: Draw them onto the grid + int marbleX = (int)round(getMarbleX(var) * yAdjusts[getMarbleY(var)] + xPosOffsets[getMarbleY(var)]); + int marbleY = yPosOffsets[getMarbleY(var)]; + _vm->_gfx->copyImageToScreen(baseBitmapId + i, marbleX, marbleY, marbleX + kSmallMarbleWidth, marbleY + kSmallMarbleHeight); } } } diff --git a/engines/sherlock/events.cpp b/engines/sherlock/events.cpp index 3f49e347ae..a8912f6f1e 100644 --- a/engines/sherlock/events.cpp +++ b/engines/sherlock/events.cpp @@ -89,6 +89,8 @@ void Events::setCursor(CursorId cursorId) { void Events::setCursor(const Graphics::Surface &src, int hotspotX, int hotspotY) { _cursorId = INVALID_CURSOR; + _hotspotPos = Common::Point(hotspotX, hotspotY); + if (!IS_3DO) { // PC 8-bit palettized CursorMan.replaceCursor(src.getPixels(), src.w, src.h, hotspotX, hotspotY, 0xff); @@ -99,6 +101,38 @@ void Events::setCursor(const Graphics::Surface &src, int hotspotX, int hotspotY) showCursor(); } +void Events::setCursor(CursorId cursorId, const Graphics::Surface &surface) { + _cursorId = cursorId; + + int hotspotX, hotspotY; + if (cursorId == MAGNIFY) { + hotspotX = 8; + hotspotY = 8; + } else { + hotspotX = 0; + hotspotY = 0; + } + + // Get the standard cursor frame + Graphics::Surface &surface2 = (*_cursorImages)[cursorId]._frame; + + // Form a single surface containing both frames + int maxWidth = MAX(surface.w, surface2.w); + Graphics::Surface s; + s.create(maxWidth, surface.h + surface2.h, Graphics::PixelFormat::createFormatCLUT8()); + s.fillRect(Common::Rect(0, 0, maxWidth, surface.h + surface2.h), TRANSPARENCY); + + s.copyRectToSurface(surface, (maxWidth - surface.w) / 2, 0, Common::Rect(0, 0, surface.w, surface.h)); + s.copyRectToSurface(surface2, (maxWidth - surface2.w) / 2, surface.h, Common::Rect(0, 0, surface2.w, surface2.h)); + + // Adjust hotspot position + hotspotX += (maxWidth - surface2.w) / 2; + hotspotY += surface.h; + + // Set the cursor + setCursor(s, hotspotX, hotspotY); +} + void Events::animateCursorIfNeeded() { if (_cursorId >= WAIT && _cursorId < (WAIT + 3)) { CursorId newId = (_cursorId == WAIT + 2) ? WAIT : (CursorId)((int)_cursorId + 1); diff --git a/engines/sherlock/events.h b/engines/sherlock/events.h index ffe6584ae6..93a5e54f81 100644 --- a/engines/sherlock/events.h +++ b/engines/sherlock/events.h @@ -59,6 +59,7 @@ public: bool _oldRightButton; bool _firstPress; Common::Stack<Common::KeyState> _pendingKeys; + Common::Point _hotspotPos; public: Events(SherlockEngine *vm); ~Events(); @@ -79,6 +80,11 @@ public: void setCursor(const Graphics::Surface &src, int hotspotX = 0, int hotspotY = 0); /** + * Set both a standard cursor as well as an inventory item above it + */ + void setCursor(CursorId cursorId, const Graphics::Surface &surface); + + /** * Animates the mouse cursor if the Wait cursor is showing */ void animateCursorIfNeeded(); diff --git a/engines/sherlock/module.mk b/engines/sherlock/module.mk index 73067fdc14..32c5d3acc3 100644 --- a/engines/sherlock/module.mk +++ b/engines/sherlock/module.mk @@ -34,6 +34,7 @@ MODULE_OBJS = \ tattoo/tattoo_user_interface.o \ tattoo/widget_base.o \ tattoo/widget_inventory.o \ + tattoo/widget_lab.o \ tattoo/widget_talk.o \ tattoo/widget_text.o \ tattoo/widget_tooltip.o \ diff --git a/engines/sherlock/objects.cpp b/engines/sherlock/objects.cpp index 2d939ce6cf..6ef08c28cc 100644 --- a/engines/sherlock/objects.cpp +++ b/engines/sherlock/objects.cpp @@ -508,6 +508,10 @@ int BaseObject::checkNameForCodes(const Common::String &name, FixedTextActionId break; } + case 'V': + // Do nothing for Verb codes. This is only a flag for Inventory syntax + break; + default: if (ch >= '0' && ch <= '9') { scene._goToScene = atoi(name.c_str() + 1); diff --git a/engines/sherlock/scene.cpp b/engines/sherlock/scene.cpp index a33574030f..328bf647d4 100644 --- a/engines/sherlock/scene.cpp +++ b/engines/sherlock/scene.cpp @@ -1210,6 +1210,15 @@ void Scene::transitionToScene() { // Standard info, so set it people[HOLMES]._position = hSavedPos; people[HOLMES]._sequenceNumber = hSavedFacing; + + if (saves._justLoaded && IS_ROSE_TATTOO) { + Tattoo::TattooUserInterface &ui = *(Tattoo::TattooUserInterface *)_vm->_ui; + + // For scrolling scenes, make sure the player is on-screen + ui._targetScroll.x = CLIP(people[HOLMES]._position.x / FIXED_INT_MULTIPLIER - + SHERLOCK_SCREEN_WIDTH / 8 - 250, 0, screen._backBuffer1.w() - SHERLOCK_SCREEN_WIDTH); + screen._currentScroll = ui._targetScroll; + } } else { // It's canimation information cAnimNum = hSavedFacing - 101; @@ -1283,7 +1292,7 @@ void Scene::transitionToScene() { screen.fadeIntoScreen3DO(3); } } else { - screen.blitFrom(screen._backBuffer1); + screen.slamArea(screen._currentScroll.x, screen._currentScroll.y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT); } screen.update(); diff --git a/engines/sherlock/surface.cpp b/engines/sherlock/surface.cpp index 729b4e8c44..42194de7a3 100644 --- a/engines/sherlock/surface.cpp +++ b/engines/sherlock/surface.cpp @@ -112,7 +112,7 @@ void Surface::transBlitFrom(const Surface &src, const Common::Point &pt, void Surface::transBlitFrom(const Graphics::Surface &src, const Common::Point &pt, bool flipped, int overrideColor, int scaleVal) { - if (scaleVal == 256) { + if (scaleVal == SCALE_THRESHOLD) { transBlitFromUnscaled(src, pt, flipped, overrideColor); return; } @@ -120,8 +120,11 @@ void Surface::transBlitFrom(const Graphics::Surface &src, const Common::Point &p int scaleX = SCALE_THRESHOLD * SCALE_THRESHOLD / scaleVal; int scaleY = scaleX; int scaleXCtr = 0, scaleYCtr = 0; + int destX, destY; + int xCtr, yCtr; + int maxX = pt.x; - for (int yCtr = 0, destY = pt.y; yCtr < src.h && destY < this->h(); ++yCtr) { + for (yCtr = 0, destY = pt.y; yCtr < src.h && destY < this->h(); ++yCtr) { // Handle skipping lines if Y scaling scaleYCtr += scaleY; @@ -134,7 +137,7 @@ void Surface::transBlitFrom(const Graphics::Surface &src, const Common::Point &p byte *pDest = (byte *)getBasePtr(pt.x, destY); scaleXCtr = 0; - for (int xCtr = 0, destX = pt.x; xCtr < src.w && destX < this->w(); ++xCtr) { + for (xCtr = 0, destX = pt.x; xCtr < src.w && destX < this->w(); ++xCtr) { // Handle horizontal scaling scaleXCtr += scaleX; @@ -149,6 +152,7 @@ void Surface::transBlitFrom(const Graphics::Surface &src, const Common::Point &p ++destX; } + maxX = MAX(maxX, destX); pSrc = pSrc + (flipped ? -1 : 1); } } @@ -156,6 +160,9 @@ void Surface::transBlitFrom(const Graphics::Surface &src, const Common::Point &p ++destY; } } + + // Mark the affected area + addDirtyRect(Common::Rect(pt.x, pt.y, maxX, destY)); } void Surface::transBlitFromUnscaled(const Graphics::Surface &src, const Common::Point &pt, diff --git a/engines/sherlock/tattoo/tattoo_scene.cpp b/engines/sherlock/tattoo/tattoo_scene.cpp index 0f61109c9d..a171da10cc 100644 --- a/engines/sherlock/tattoo/tattoo_scene.cpp +++ b/engines/sherlock/tattoo/tattoo_scene.cpp @@ -101,6 +101,9 @@ bool TattooScene::loadScene(const Common::String &filename) { // Set the menu/ui mode and whether we're in a lab table close-up scene _labTableScene = _currentScene > 91 && _currentScene < 100; ui._menuMode = _labTableScene ? LAB_MODE : STD_MODE; + + if (_labTableScene) + ui._labWidget.summonWindow(); } return result; diff --git a/engines/sherlock/tattoo/tattoo_user_interface.cpp b/engines/sherlock/tattoo/tattoo_user_interface.cpp index bffdb550cf..e846db2701 100644 --- a/engines/sherlock/tattoo/tattoo_user_interface.cpp +++ b/engines/sherlock/tattoo/tattoo_user_interface.cpp @@ -30,7 +30,8 @@ namespace Sherlock { namespace Tattoo { TattooUserInterface::TattooUserInterface(SherlockEngine *vm): UserInterface(vm), - _inventoryWidget(vm), _messageWidget(vm), _textWidget(vm), _tooltipWidget(vm), _verbsWidget(vm) { + _inventoryWidget(vm), _messageWidget(vm), _textWidget(vm), _tooltipWidget(vm), _verbsWidget(vm), + _labWidget(vm) { Common::fill(&_lookupTable[0], &_lookupTable[PALETTE_COUNT], 0); Common::fill(&_lookupTable1[0], &_lookupTable1[PALETTE_COUNT], 0); _scrollSize = 0; @@ -222,6 +223,8 @@ void TattooUserInterface::doJournal() { void TattooUserInterface::reset() { UserInterface::reset(); _lookPos = Common::Point(SHERLOCK_SCREEN_WIDTH / 2, SHERLOCK_SCREEN_HEIGHT / 2); + _tooltipWidget.setText(""); + _widgets.clear(); } void TattooUserInterface::handleInput() { @@ -280,9 +283,6 @@ void TattooUserInterface::handleInput() { case FILES_MODE: doFileControl(); break; - case LAB_MODE: - doLabControl(); - break; default: break; } @@ -356,7 +356,6 @@ void TattooUserInterface::doStandardControl() { Events &events = *_vm->_events; People &people = *_vm->_people; TattooScene &scene = *(TattooScene *)_vm->_scene; - Screen &screen = *_vm->_screen; Talk &talk = *_vm->_talk; Common::Point mousePos = events.mousePos(); bool noDesc = false; @@ -477,7 +476,7 @@ void TattooUserInterface::doStandardControl() { events._pressed = events._released = false; } else { // Walk to where the mouse was clicked - people[HOLMES]._walkDest = mousePos + screen._currentScroll; + people[HOLMES]._walkDest = mousePos; people[HOLMES].goAllTheWay(); } } @@ -527,10 +526,6 @@ void TattooUserInterface::doFileControl() { warning("TODO: ui control (file)"); } -void TattooUserInterface::doLabControl() { - warning("TODO: ui control (lab)"); -} - void TattooUserInterface::displayObjectNames() { Events &events = *_vm->_events; Scene &scene = *_vm->_scene; @@ -583,6 +578,7 @@ void TattooUserInterface::putMessage(const char *formatStr, ...) { va_end(args); // Open the message widget + _menuMode = MESSAGE_MODE; _messageWidget.load(str, 25); _messageWidget.summonWindow(); } diff --git a/engines/sherlock/tattoo/tattoo_user_interface.h b/engines/sherlock/tattoo/tattoo_user_interface.h index 8dcfaddbd2..7f284531de 100644 --- a/engines/sherlock/tattoo/tattoo_user_interface.h +++ b/engines/sherlock/tattoo/tattoo_user_interface.h @@ -29,6 +29,7 @@ #include "sherlock/screen.h" #include "sherlock/user_interface.h" #include "sherlock/tattoo/widget_inventory.h" +#include "sherlock/tattoo/widget_lab.h" #include "sherlock/tattoo/widget_text.h" #include "sherlock/tattoo/widget_tooltip.h" #include "sherlock/tattoo/widget_verbs.h" @@ -77,17 +78,6 @@ private: * Handle input while the verb menu is open */ void doVerbControl(); - - /** - * Handles input when the player is in the Lab Table scene - */ - void doLabControl(); - - /** - * If the mouse cursor is point at the cursor, then display the name of the object on the screen. - * If there is no object being pointed it, clear any previously displayed name - */ - void displayObjectNames(); /** * Set up to display the Files menu @@ -119,7 +109,7 @@ public: int _maskCounter; ImageFile *_interfaceImages; WidgetText _textWidget; - Common::String _action; + WidgetLab _labWidget; public: TattooUserInterface(SherlockEngine *vm); virtual ~TattooUserInterface(); @@ -197,6 +187,12 @@ public: * Draws all the dialog rectangles for any items that need them */ void drawDialogRect(Surface &s, const Common::Rect &r, bool raised); + + /** + * If the mouse cursor is point at the cursor, then display the name of the object on the screen. + * If there is no object being pointed it, clear any previously displayed name + */ + void displayObjectNames(); public: /** * Resets the user interface diff --git a/engines/sherlock/tattoo/widget_base.cpp b/engines/sherlock/tattoo/widget_base.cpp index 5f16e8800d..66ade474c2 100644 --- a/engines/sherlock/tattoo/widget_base.cpp +++ b/engines/sherlock/tattoo/widget_base.cpp @@ -35,6 +35,14 @@ WidgetBase::WidgetBase(SherlockEngine *vm) : _vm(vm) { void WidgetBase::summonWindow() { TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui; + + // Double-check that the same widget isn't added twice + for (Common::List<WidgetBase *>::iterator i = ui._widgets.begin(); i != ui._widgets.end(); ++i) { + if ((*i) == this) + error("Tried to add a widget twice"); + } + + // Add widget to the screen ui._widgets.push_back(this); _outsideMenu = false; diff --git a/engines/sherlock/tattoo/widget_inventory.cpp b/engines/sherlock/tattoo/widget_inventory.cpp index 241eaca182..170fb02481 100644 --- a/engines/sherlock/tattoo/widget_inventory.cpp +++ b/engines/sherlock/tattoo/widget_inventory.cpp @@ -122,17 +122,6 @@ void WidgetInventoryTooltip::handleEvents() { Common::String strWith = fixedText.getText(kFixedText_With); Common::String strUse = fixedText.getText(kFixedText_Use); - // If there's a floating graphic for a selected inventory item, update it's bounds - if (_owner->_invVerbMode == 2 || _owner->_invVerbMode == 3) { - _oldInvGraphicBounds = _invGraphicBounds; - - // Set the New position of the graphic - int xp = CLIP(mousePos.x - _invGraphicBounds.width() / 2, 0, SHERLOCK_SCENE_WIDTH - _invGraphicBounds.width()); - int yp = CLIP(mousePos.y - _invGraphicBounds.height() / 2, 0, SHERLOCK_SCREEN_HEIGHT - _invGraphicBounds.height()); - - _invGraphicBounds.moveTo(xp, yp); - } - // If we are using an inventory item on an object in the room, display the appropriate text above the mouse cursor if (_owner->_invVerbMode == 3) { select = ui._bgFound; @@ -147,18 +136,18 @@ void WidgetInventoryTooltip::handleEvents() { if (_vm->getLanguage() == Common::GR_GRE) { if (!_owner->_swapItems) - str = Common::String::format("%s %s %s %s", ui._action.c_str(), obj._description.c_str(), - inv[_owner->_invSelect]._name.c_str(), _owner->_invVerb.c_str()); + str = Common::String::format("%s %s %s %s", _owner->_action.c_str(), obj._description.c_str(), + inv[_owner->_invSelect]._name.c_str(), _owner->_verb.c_str()); else - str = Common::String::format("%s %s %s %s", ui._action.c_str(), inv[_owner->_invSelect]._name.c_str(), - obj._description.c_str(), _owner->_invVerb.c_str()); + str = Common::String::format("%s %s %s %s", _owner->_action.c_str(), inv[_owner->_invSelect]._name.c_str(), + obj._description.c_str(), _owner->_verb.c_str()); } else { if (_owner->_swapItems) - str = Common::String::format("%s %s %s %s", _owner->_invVerb.c_str(), obj._description.c_str(), ui._action.c_str(), + str = Common::String::format("%s %s %s %s", _owner->_verb.c_str(), obj._description.c_str(), _owner->_action.c_str(), inv[_owner->_invSelect]._name.c_str()); else - str = Common::String::format("%s %s %s %s", _owner->_invVerb.c_str(), inv[_owner->_invSelect]._name.c_str(), - ui._action.c_str(), obj._description.c_str()); + str = Common::String::format("%s %s %s %s", _owner->_verb.c_str(), inv[_owner->_invSelect]._name.c_str(), + _owner->_action.c_str(), obj._description.c_str()); } } } else { @@ -167,19 +156,19 @@ void WidgetInventoryTooltip::handleEvents() { if (!person._description.empty() && !person._description.hasPrefix(" ")) { if (_vm->getLanguage() == Common::GR_GRE) { if (!_owner->_swapItems) - str = Common::String::format("%s %s %s %s", ui._action.c_str(), person._description.c_str(), - inv[_owner->_invSelect]._name.c_str(), _owner->_invVerb.c_str()); + str = Common::String::format("%s %s %s %s", _owner->_action.c_str(), person._description.c_str(), + inv[_owner->_invSelect]._name.c_str(), _owner->_verb.c_str()); else - str = Common::String::format("%s %s %s %s", ui._action.c_str(), inv[_owner->_invSelect]._name.c_str(), - person._description.c_str(), _owner->_invVerb.c_str()); + str = Common::String::format("%s %s %s %s", _owner->_action.c_str(), inv[_owner->_invSelect]._name.c_str(), + person._description.c_str(), _owner->_verb.c_str()); } else { if (_owner->_swapItems) - str = Common::String::format("%s %s %s %s", _owner->_invVerb.c_str(), person._description.c_str(), - ui._action.c_str(), inv[_owner->_invSelect]._name.c_str()); + str = Common::String::format("%s %s %s %s", _owner->_verb.c_str(), person._description.c_str(), + _owner->_action.c_str(), inv[_owner->_invSelect]._name.c_str()); else - str = Common::String::format("%s %s %s %s", _owner->_invVerb.c_str(), - inv[_owner->_invSelect]._name.c_str(), ui._action.c_str(), person._description.c_str()); + str = Common::String::format("%s %s %s %s", _owner->_verb.c_str(), + inv[_owner->_invSelect]._name.c_str(), _owner->_action.c_str(), person._description.c_str()); } } } @@ -241,6 +230,10 @@ void WidgetInventoryTooltip::handleEvents() { return; } + if (_owner->_invVerbMode == 3) + // Adjust tooltip to be above the inventory item being shown above the standard cursor + mousePos.y -= events._hotspotPos.y; + // Update the position of the tooltip int xs = CLIP(mousePos.x - _bounds.width() / 2, 0, SHERLOCK_SCENE_WIDTH - _bounds.width()); int ys = CLIP(mousePos.y - _bounds.height(), 0, SHERLOCK_SCREEN_HEIGHT - _bounds.height()); @@ -267,7 +260,7 @@ void WidgetInventoryVerbs::load() { _inventCommands.push_back(FIXED(Look)); // Default the Action word to "with" - _action = _vm->getLanguage() == Common::GR_GRE ? "" : FIXED(With); + _owner->_action = _vm->getLanguage() == Common::GR_GRE ? "" : FIXED(With); // Search all the bgshapes for any matching Target Fields for (uint idx = 0; idx < scene._bgShapes.size(); ++idx) { @@ -275,7 +268,7 @@ void WidgetInventoryVerbs::load() { if (obj._type != INVALID && obj._type != HIDDEN) { for (int useNum = 0; useNum < 6; ++useNum) { - if (obj._use[useNum]._verb.hasPrefix("*") && + if (!obj._use[useNum]._verb.hasPrefix("*") && !obj._use[useNum]._target.compareToIgnoreCase(inv[_owner->_invSelect]._name)) { // Make sure the Verb is not already in the list bool found1 = false; @@ -293,7 +286,7 @@ void WidgetInventoryVerbs::load() { if (!scumm_strnicmp(obj._use[useNum]._names[nameNum].c_str(), "*VSWAP", 6)) _owner->_swapItems = true; else - _action = Common::String(obj._use[useNum]._names[nameNum].c_str() + 2); + _owner->_action = Common::String(obj._use[useNum]._names[nameNum].c_str() + 2); } } } @@ -428,29 +421,19 @@ void WidgetInventoryVerbs::handleEvents() { events.clearEvents(); ui.checkAction(inv[_owner->_invSelect]._verb, 2000); } else { - _owner->_invVerb = _inventCommands[_invVerbSelect]; + _owner->_verb = _inventCommands[_invVerbSelect]; } // If we are still in Inventory Mode, setup the graphic to float in front of the mouse cursor if (ui._menuMode == INV_MODE) { + // Add the inventory item to the cursor ImageFrame &imgFrame = (*inv._invShapes[_owner->_invSelect - inv._invIndex])[0]; - _owner->_invGraphicBounds = Common::Rect(imgFrame._width, imgFrame._height); - _owner->_invGraphicBounds.moveTo(mousePos.x - _owner->_invGraphicBounds.width() / 2, - mousePos.y - _owner->_invGraphicBounds.height() / 2); - - // Constrain it to the screen - if (_owner->_invGraphicBounds.left < 0) - _owner->_invGraphicBounds.moveTo(0, _owner->_invGraphicBounds.top); - if (_owner->_invGraphicBounds.top < 0) - _owner->_invGraphicBounds.moveTo(_owner->_invGraphicBounds.left, 0); - if (_owner->_invGraphicBounds.right > SHERLOCK_SCREEN_WIDTH) - _owner->_invGraphicBounds.moveTo(SHERLOCK_SCREEN_WIDTH - _owner->_invGraphicBounds.width(), _owner->_invGraphicBounds.top); - if (_owner->_invGraphicBounds.bottom > SHERLOCK_SCREEN_HEIGHT) - _owner->_invGraphicBounds.moveTo(_owner->_invGraphicBounds.left, SHERLOCK_SCREEN_HEIGHT - _owner->_invGraphicBounds.height()); - - // Make a copy of the inventory image - _owner->_invGraphic.create(imgFrame._width, imgFrame._height); - _owner->_invGraphic.blitFrom(imgFrame, Common::Point(0, 0)); + events.setCursor(ARROW, imgFrame._frame); + + // Close the inventory dialog without banishing it, so it can keep getting events + // to handle tooltips and actually making the selection of what object to use them item on + inv.freeInv(); + _owner->_surface.free(); } } } @@ -624,11 +607,7 @@ void WidgetInventory::handleEvents() { if (_invVerbMode == 3) { // Selecting object after inventory verb has been selected _tooltipWidget.banishWindow(); - _invGraphic.free(); - inv.freeInv(); - - ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE; - events.clearEvents(); + close(); if (ui._keyState.keycode != Common::KEYCODE_ESCAPE) { // If user pointed at an item, use the selected inventory item with this item @@ -636,7 +615,7 @@ void WidgetInventory::handleEvents() { if (ui._bgFound != -1) { if (ui._personFound) { for (int idx = 0; idx < 2; ++idx) { - if (!people[ui._bgFound - 1000]._use[idx]._verb.compareToIgnoreCase(_invVerb) && + if (!people[ui._bgFound - 1000]._use[idx]._verb.compareToIgnoreCase(_verb) && !people[ui._bgFound - 1000]._use[idx]._target.compareToIgnoreCase(_invTarget)) { ui.checkAction(people[ui._bgFound - 1000]._use[idx], ui._bgFound); found = true; @@ -644,7 +623,7 @@ void WidgetInventory::handleEvents() { } } else { for (int idx = 0; idx < 6; ++idx) { - if (!ui._bgShape->_use[idx]._verb.compareToIgnoreCase(_invVerb) && + if (!ui._bgShape->_use[idx]._verb.compareToIgnoreCase(_verb) && !ui._bgShape->_use[idx]._target.compareToIgnoreCase(_invTarget)) { ui.checkAction(ui._bgShape->_use[idx], ui._bgFound); found = true; @@ -658,13 +637,8 @@ void WidgetInventory::handleEvents() { } } else if ((_outsideMenu && !_bounds.contains(mousePos)) || ui._keyState.keycode == Common::KEYCODE_ESCAPE) { // Want to close the window (clicked outside of it). So close the window and return to Standard - banishWindow(); - inv.freeInv(); + close(); - events.clearEvents(); - events.setCursor(ARROW); - banishWindow(); - ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE; } else if (_bounds.contains(mousePos)) { // Mouse button was released inside the inventory window _outsideMenu = false; @@ -769,6 +743,20 @@ void WidgetInventory::erase() { _tooltipWidget.erase(); } +void WidgetInventory::close() { + Events &events = *_vm->_events; + Inventory &inv = *_vm->_inventory; + TattooScene &scene = *(TattooScene *)_vm->_scene; + TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui; + + banishWindow(); + inv.freeInv(); + events.clearEvents(); + + events.setCursor(ARROW); + ui._menuMode = scene._labTableScene ? LAB_MODE : STD_MODE; +} + } // End of namespace Tattoo } // End of namespace Sherlock diff --git a/engines/sherlock/tattoo/widget_inventory.h b/engines/sherlock/tattoo/widget_inventory.h index bfc1c7f50f..53bc2038e0 100644 --- a/engines/sherlock/tattoo/widget_inventory.h +++ b/engines/sherlock/tattoo/widget_inventory.h @@ -40,7 +40,6 @@ class WidgetInventory; class WidgetInventoryTooltip: public WidgetTooltipBase { private: WidgetInventory *_owner; - Common::Rect _oldInvGraphicBounds, _invGraphicBounds; protected: /** * Overriden from base class, since tooltips have a completely transparent background @@ -65,7 +64,6 @@ class WidgetInventoryVerbs : public WidgetBase { private: WidgetInventory *_owner; Common::StringArray _inventCommands; - Common::String _action; void highlightControls(); public: @@ -92,12 +90,9 @@ private: int _dialogTimer; WidgetInventoryTooltip _tooltipWidget; WidgetInventoryVerbs _verbList; - Common::Rect _invGraphicBounds; - Surface _invGraphic; bool _swapItems; Surface _menuSurface; Common::String _invTarget; - Common::String _invVerb; /** * Draw the bars within the dialog @@ -115,10 +110,15 @@ private: void highlightControls(); public: int _invMode; + Common::String _action; + Common::String _verb; public: WidgetInventory(SherlockEngine *vm); virtual ~WidgetInventory() {} + /** + * Load the inventory window + */ void load(int mode); /** @@ -127,6 +127,11 @@ public: void drawInventory(); /** + * Close the window + */ + void close(); + + /** * Handle events whilst the widget is on-screen */ virtual void handleEvents(); diff --git a/engines/sherlock/tattoo/widget_lab.cpp b/engines/sherlock/tattoo/widget_lab.cpp new file mode 100644 index 0000000000..47955a0d26 --- /dev/null +++ b/engines/sherlock/tattoo/widget_lab.cpp @@ -0,0 +1,43 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "sherlock/tattoo/widget_lab.h" +#include "sherlock/tattoo/tattoo_user_interface.h" +#include "sherlock/tattoo/tattoo.h" + +namespace Sherlock { + +namespace Tattoo { + +WidgetLab::WidgetLab(SherlockEngine *vm) : WidgetBase(vm) { +} + +void WidgetLab::handleEvents() { + Events &events = *_vm->_events; + TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui; + WidgetBase::handleEvents(); + +} + +} // End of namespace Tattoo + +} // End of namespace Sherlock diff --git a/engines/sherlock/tattoo/widget_lab.h b/engines/sherlock/tattoo/widget_lab.h new file mode 100644 index 0000000000..344dae7934 --- /dev/null +++ b/engines/sherlock/tattoo/widget_lab.h @@ -0,0 +1,52 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SHERLOCK_TATTOO_WIDGET_LAB_H +#define SHERLOCK_TATTOO_WIDGET_LAB_H + +#include "common/scummsys.h" +#include "sherlock/tattoo/widget_base.h" + +namespace Sherlock { + +class SherlockEngine; + +namespace Tattoo { + +class WidgetLab: public WidgetBase { +public: + Common::String _remainingText; +public: + WidgetLab(SherlockEngine *vm); + virtual ~WidgetLab() {} + + /** + * Handle event processing + */ + virtual void handleEvents(); +}; + +} // End of namespace Tattoo + +} // End of namespace Sherlock + +#endif diff --git a/engines/sherlock/user_interface.cpp b/engines/sherlock/user_interface.cpp index 9db29023c8..9df3f1dc24 100644 --- a/engines/sherlock/user_interface.cpp +++ b/engines/sherlock/user_interface.cpp @@ -179,7 +179,7 @@ void UserInterface::checkAction(ActionType &action, int objNum, FixedTextActionI } // Unless we're leaving the scene, print a "Done" message unless the printed flag has been set - if (scene._goToScene != 1 && !printed && !talk._talkToAbort) { + if (IS_SERRATED_SCALPEL && scene._goToScene != 1 && !printed && !talk._talkToAbort) { _infoFlag = true; clearInfo(); screen.print(Common::Point(0, INFO_LINE + 1), COL_INFO_FOREGROUND, "Done..."); |