From 41cf359468d6379bc0e033bee9ac148d1b9255dd Mon Sep 17 00:00:00 2001 From: Guillaume Melquiond Date: Fri, 19 Oct 2007 17:46:46 +0000 Subject: Factored code between resource handlers. Implemented failure-friendly sprite loader. --- ChangeLog | 12 ++++ src/gui/truetypefont.cpp | 2 +- src/resources/image.cpp | 35 +++------ src/resources/image.h | 12 ++-- src/resources/imageloader.cpp | 2 +- src/resources/imageset.cpp | 5 +- src/resources/imageset.h | 2 +- src/resources/music.cpp | 23 +++--- src/resources/music.h | 8 +-- src/resources/resource.cpp | 6 -- src/resources/resource.h | 2 +- src/resources/resourcemanager.cpp | 144 ++++++++++++++++---------------------- src/resources/resourcemanager.h | 34 +++++---- src/resources/soundeffect.cpp | 14 +--- src/resources/soundeffect.h | 6 +- src/resources/spritedef.cpp | 47 ++++++------- src/resources/spritedef.h | 27 +++---- src/tileset.h | 2 +- 18 files changed, 170 insertions(+), 213 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9aaa1c5d..88dfff79 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,18 @@ of already selected items. * src/gui/buy.cpp, src/gui/sell.cpp, po/POTFILES.in: Marked buy and sell dialog boxes as translatable. + * src/resources/resource.cpp, src/resources/resource.h, + src/resources/imageset.cpp, src/resources/imageset.h: Removed mandatory + identifier path. + * src/resources/resourcemanager.cpp, src/resources/resourcemanager.h: + Factored code between resource handlers. + * src/resources/soundeffect.h, src/resources/soundeffect.cpp, + src/resources/music.h, src/resources/music.cpp, src/resources/image.h, + src/resources/image.cpp: Reworked resource loaders. + * src/resources/spritedef.h, src/resources/spritedef.cpp: Implemented + a failure-friendly loader. + * src/gui/truetypefont.cpp, src/resources/imageloader.cpp, + src/tileset.h: Removed dummy parameter. 2007-10-19 Philipp Sehmisch diff --git a/src/gui/truetypefont.cpp b/src/gui/truetypefont.cpp index 4f17b135..91c86da9 100644 --- a/src/gui/truetypefont.cpp +++ b/src/gui/truetypefont.cpp @@ -67,7 +67,7 @@ class TextChunk std::string(TTF_GetError()); } - img = Image::load(surface, std::string()); + img = Image::load(surface); SDL_FreeSurface(surface); } diff --git a/src/resources/image.cpp b/src/resources/image.cpp index 995e34b0..7a394edb 100644 --- a/src/resources/image.cpp +++ b/src/resources/image.cpp @@ -33,8 +33,7 @@ int Image::mTextureType = 0; int Image::mTextureSize = 0; #endif -Image::Image(const std::string &idPath, SDL_Surface *image): - Resource(idPath), +Image::Image(SDL_Surface *image): #ifdef USE_OPENGL mGLImage(0), #endif @@ -48,9 +47,8 @@ Image::Image(const std::string &idPath, SDL_Surface *image): } #ifdef USE_OPENGL -Image::Image(const std::string &idPath, GLuint glimage, int width, int height, +Image::Image(GLuint glimage, int width, int height, int texWidth, int texHeight): - Resource(idPath), mGLImage(glimage), mTexWidth(texWidth), mTexHeight(texHeight), @@ -69,36 +67,25 @@ Image::~Image() unload(); } -Image* Image::load(void *buffer, unsigned int bufferSize, - const std::string &idPath) +Resource *Image::load(void *buffer, unsigned bufferSize) { // Load the raw file data from the buffer in an RWops structure SDL_RWops *rw = SDL_RWFromMem(buffer, bufferSize); - SDL_Surface *tmpImage; + SDL_Surface *tmpImage = IMG_Load_RW(rw, 1); - // Use SDL_Image to load the raw image data and have it free the data - if (!idPath.compare(idPath.length() - 4, 4, ".tga")) + if (!tmpImage) { - tmpImage = IMG_LoadTyped_RW(rw, 1, const_cast("TGA")); - } - else - { - tmpImage = IMG_Load_RW(rw, 1); - } - - if (!tmpImage) { logger->log("Error, image load failed: %s", IMG_GetError()); return NULL; } - Image *image = load(tmpImage, idPath); + Image *image = load(tmpImage); SDL_FreeSurface(tmpImage); - return image; } -Image *Image::load(SDL_Surface *tmpImage, std::string const &idPath) +Image *Image::load(SDL_Surface *tmpImage) { #ifdef USE_OPENGL if (mUseOpenGL) @@ -198,7 +185,7 @@ Image *Image::load(SDL_Surface *tmpImage, std::string const &idPath) return NULL; } - return new Image(idPath, texture, width, height, realWidth, realHeight); + return new Image(texture, width, height, realWidth, realHeight); } #endif @@ -238,7 +225,7 @@ Image *Image::load(SDL_Surface *tmpImage, std::string const &idPath) return NULL; } - return new Image(idPath, image); + return new Image(image); } void Image::unload() @@ -324,7 +311,7 @@ Image::powerOfTwo(int input) SubImage::SubImage(Image *parent, SDL_Surface *image, int x, int y, int width, int height): - Image("", image), mParent(parent) + Image(image), mParent(parent) { mParent->incRef(); @@ -339,7 +326,7 @@ SubImage::SubImage(Image *parent, SDL_Surface *image, SubImage::SubImage(Image *parent, GLuint image, int x, int y, int width, int height, int texWidth, int texHeight): - Image("", image, width, height, texWidth, texHeight), mParent(parent) + Image(image, width, height, texWidth, texHeight), mParent(parent) { mParent->incRef(); diff --git a/src/resources/image.h b/src/resources/image.h index 34515dda..485ca227 100644 --- a/src/resources/image.h +++ b/src/resources/image.h @@ -62,18 +62,16 @@ class Image : public Resource * * @param buffer The memory buffer containing the image data. * @param bufferSize The size of the memory buffer in bytes. - * @param idPath The path identifying the resource. * * @return NULL if the an error occurred, a valid pointer * otherwise. */ - static Image* - load(void* buffer, unsigned int bufferSize, const std::string &idPath); + static Resource *load(void *buffer, unsigned bufferSize); /** * Loads an image from an SDL surface. */ - static Image *load(SDL_Surface *, std::string const &idPath); + static Image *load(SDL_Surface *); /** * Frees the resources created by SDL. @@ -129,8 +127,8 @@ class Image : public Resource * Constructor. */ #ifdef USE_OPENGL - Image(const std::string &idPath, GLuint glimage, int width, int height, - int texWidth, int texHeight); + Image(GLuint glimage, int width, int height, + int texWidth, int texHeight); /** * Returns the first power of two equal or bigger than the input. @@ -138,7 +136,7 @@ class Image : public Resource static int powerOfTwo(int input); #endif - Image(const std::string &idPath, SDL_Surface *image); + Image(SDL_Surface *image); SDL_Rect mBounds; bool mLoaded; diff --git a/src/resources/imageloader.cpp b/src/resources/imageloader.cpp index b2053dcc..6ec5fe8f 100644 --- a/src/resources/imageloader.cpp +++ b/src/resources/imageloader.cpp @@ -85,7 +85,7 @@ void ProxyImage::convertToDisplayFormat() font. Get rid of them. */ SDL_SetColorKey(mSDLImage, SDL_SRCCOLORKEY, SDL_MapRGB(mSDLImage->format, 255, 0, 255)); - mImage = ::Image::load(mSDLImage, std::string()); + mImage = ::Image::load(mSDLImage); SDL_FreeSurface(mSDLImage); mSDLImage = NULL; } diff --git a/src/resources/imageset.cpp b/src/resources/imageset.cpp index 677c024b..565e8860 100644 --- a/src/resources/imageset.cpp +++ b/src/resources/imageset.cpp @@ -29,10 +29,7 @@ #include "../utils/dtor.h" -ImageSet::ImageSet(const std::string& idPath, - Image *img, - int width, int height): - Resource(idPath) +ImageSet::ImageSet(Image *img, int width, int height) { for (int y = 0; y + height <= img->getHeight(); y += height) { diff --git a/src/resources/imageset.h b/src/resources/imageset.h index 3469a3bb..15c21d90 100644 --- a/src/resources/imageset.h +++ b/src/resources/imageset.h @@ -40,7 +40,7 @@ class ImageSet : public Resource /* * Cuts the passed image in a grid of sub images. */ - ImageSet(const std::string &idPath, Image *img, int w, int h); + ImageSet(Image *img, int w, int h); /** * Destructor. diff --git a/src/resources/music.cpp b/src/resources/music.cpp index 09e2752a..161d8b01 100644 --- a/src/resources/music.cpp +++ b/src/resources/music.cpp @@ -23,8 +23,9 @@ #include "music.h" -Music::Music(const std::string &idPath, Mix_Chunk *music): - Resource(idPath), +#include "../log.h" + +Music::Music(Mix_Chunk *music): mChunk(music), mChannel(-1) { @@ -36,20 +37,24 @@ Music::~Music() Mix_FreeChunk(mChunk); } -Music* -Music::load(void *buffer, unsigned int bufferSize, const std::string &idPath) +Resource *Music::load(void *buffer, unsigned bufferSize) { // Load the raw file data from the buffer in an RWops structure SDL_RWops *rw = SDL_RWFromMem(buffer, bufferSize); // Use Mix_LoadMUS to load the raw music data //Mix_Music* music = Mix_LoadMUS_RW(rw); Need to be implemeted - Mix_Chunk *tmpMusic = Mix_LoadWAV_RW(rw, 0); - - // Now free the SDL_RWops data - SDL_FreeRW(rw); + Mix_Chunk *tmpMusic = Mix_LoadWAV_RW(rw, 1); - return new Music(idPath, tmpMusic); + if (tmpMusic) + { + return new Music(tmpMusic); + } + else + { + logger->log("Error, failed to load music: %s", Mix_GetError()); + return NULL; + } } bool diff --git a/src/resources/music.h b/src/resources/music.h index 2888eaa0..72e76295 100644 --- a/src/resources/music.h +++ b/src/resources/music.h @@ -40,17 +40,15 @@ class Music : public Resource virtual ~Music(); /** - * Loads an image from a buffer in memory. + * Loads a music from a buffer in memory. * * @param buffer The memory buffer containing the music data. * @param bufferSize The size of the memory buffer in bytes. - * @param idPath The path identifying the resource. * * @return NULL if the an error occurred, a valid pointer * otherwise. */ - static Music* - load(void* buffer, unsigned int bufferSize, const std::string &idPath); + static Resource *load(void *buffer, unsigned bufferSize); /** * Plays the music. @@ -73,7 +71,7 @@ class Music : public Resource /** * Constructor. */ - Music(const std::string &idPath, Mix_Chunk *music); + Music(Mix_Chunk *music); //Mix_Music *music; Mix_Chunk *mChunk; diff --git a/src/resources/resource.cpp b/src/resources/resource.cpp index bd8cd437..07f31b8f 100644 --- a/src/resources/resource.cpp +++ b/src/resources/resource.cpp @@ -27,12 +27,6 @@ #include "resourcemanager.h" - -Resource::Resource(const std::string &idPath): - mRefCount(0), mIdPath(idPath) -{ -} - Resource::~Resource() { } diff --git a/src/resources/resource.h b/src/resources/resource.h index d8b93e2d..7c39f176 100644 --- a/src/resources/resource.h +++ b/src/resources/resource.h @@ -37,7 +37,7 @@ class Resource /** * Constructor */ - Resource(const std::string &idPath); + Resource(): mRefCount(0) {} /** * Increments the internal reference count. diff --git a/src/resources/resourcemanager.cpp b/src/resources/resourcemanager.cpp index 5e3f3bc7..e507835a 100644 --- a/src/resources/resourcemanager.cpp +++ b/src/resources/resourcemanager.cpp @@ -157,56 +157,23 @@ ResourceManager::isDirectory(const std::string &path) return PHYSFS_isDirectory(path.c_str()); } -Resource* -ResourceManager::get(const E_RESOURCE_TYPE &type, const std::string &idPath) +Resource *ResourceManager::get(std::string const &idPath, generator fun, void *data) { // Check if the id exists, and return the value if it does. ResourceIterator resIter = mResources.find(idPath); - if (resIter != mResources.end() && resIter->second) { + if (resIter != mResources.end()) + { resIter->second->incRef(); return resIter->second; } - int fileSize; - void *buffer = loadFile(idPath, fileSize); - if (!buffer) { - return NULL; - } - - Resource *resource = NULL; - - // Create an object of the specified type. - switch (type) - { - case MUSIC: - { - // Let the music class load it - resource = Music::load(buffer, fileSize, idPath); - } - break; - case IMAGE: - { - // Let the image class load it - resource = Image::load(buffer, fileSize, idPath); - } - break; - case SOUND_EFFECT: - { - // Let the sound effect class load it - resource = SoundEffect::load(buffer, fileSize, idPath); - } - break; - default: - /* Nothing to do here, just avoid compiler warnings... */ - break; - } - - free(buffer); + Resource *resource = fun(data); if (resource) { resource->incRef(); + resource->mIdPath = idPath; mResources[idPath] = resource; } @@ -214,79 +181,90 @@ ResourceManager::get(const E_RESOURCE_TYPE &type, const std::string &idPath) return resource; } +struct ResourceLoader +{ + ResourceManager *manager; + std::string path; + ResourceManager::loader fun; + static Resource *load(void *v) + { + ResourceLoader *l = static_cast< ResourceLoader * >(v); + int fileSize; + void *buffer = l->manager->loadFile(l->path, fileSize); + if (!buffer) return NULL; + Resource *res = l->fun(buffer, fileSize); + free(buffer); + return res; + } +}; + +Resource *ResourceManager::load(std::string const &path, loader fun) +{ + ResourceLoader l = { this, path, fun }; + return get(path, ResourceLoader::load, &l); +} + Image* ResourceManager::getImage(const std::string &idPath) { - return dynamic_cast(get(IMAGE, idPath)); + return static_cast(load(idPath, Image::load)); } Music* ResourceManager::getMusic(const std::string &idPath) { - return dynamic_cast(get(MUSIC, idPath)); + return static_cast(load(idPath, Music::load)); } SoundEffect* ResourceManager::getSoundEffect(const std::string &idPath) { - return dynamic_cast(get(SOUND_EFFECT, idPath)); + return static_cast(load(idPath, SoundEffect::load)); } +struct ImageSetLoader +{ + ResourceManager *manager; + std::string path; + int w, h; + static Resource *load(void *v) + { + ImageSetLoader *l = static_cast< ImageSetLoader * >(v); + Image *img = l->manager->getImage(l->path); + if (!img) return NULL; + ImageSet *res = new ImageSet(img, l->w, l->h); + img->decRef(); + return res; + } +}; + ImageSet* ResourceManager::getImageSet(const std::string &imagePath, int w, int h) { + ImageSetLoader l = { this, imagePath, w, h }; std::stringstream ss; ss << imagePath << "[" << w << "x" << h << "]"; - const std::string idPath = ss.str(); - - ResourceIterator resIter = mResources.find(idPath); - - if (resIter != mResources.end()) { - resIter->second->incRef(); - return dynamic_cast(resIter->second); - } - - Image *img = getImage(imagePath); + return static_cast(get(ss.str(), ImageSetLoader::load, &l)); +} - if (!img) { - return NULL; +struct SpriteDefLoader +{ + std::string path; + int variant; + static Resource *load(void *v) + { + SpriteDefLoader *l = static_cast< SpriteDefLoader * >(v); + return SpriteDef::load(l->path, l->variant); } - - ImageSet *imageSet = new ImageSet(idPath, img, w, h); - imageSet->incRef(); - mResources[idPath] = imageSet; - - img->decRef(); - - return imageSet; -} +}; SpriteDef* ResourceManager::getSprite(const std::string &path, int variant) { + SpriteDefLoader l = { path, variant }; std::stringstream ss; ss << path << "[" << variant << "]"; - const std::string idPath = ss.str(); - - ResourceIterator resIter = mResources.find(idPath); - - if (resIter != mResources.end()) { - resIter->second->incRef(); - return dynamic_cast(resIter->second); - } - - // FIXME: modify SpriteDef so that it gracefully fails on missing sprite. - if (!exists(path) || isDirectory(path)) - { - logger->log("Failed to load file: %s", path.c_str()); - return NULL; - } - - SpriteDef *sprite = new SpriteDef(idPath, path, variant); - sprite->incRef(); - mResources[idPath] = sprite; - - return sprite; + return static_cast(get(ss.str(), SpriteDefLoader::load, &l)); } void diff --git a/src/resources/resourcemanager.h b/src/resources/resourcemanager.h index db29e6d3..150b773c 100644 --- a/src/resources/resourcemanager.h +++ b/src/resources/resourcemanager.h @@ -42,18 +42,8 @@ class SpriteDef; class ResourceManager { public: - /** - * An enumeration of resource types. - */ - enum E_RESOURCE_TYPE - { - //MAP, - MUSIC, - IMAGE, - //SCRIPT, - //TILESET, - SOUND_EFFECT - }; + typedef Resource *(*loader)(void *, unsigned); + typedef Resource *(*generator)(void *); /** * Constructor. @@ -109,17 +99,25 @@ class ResourceManager isDirectory(const std::string &path); /** - * Creates a resource and adds it to the resource map. The idPath is - * converted into the appropriate path for the current operating system - * and the resource is loaded. + * Creates a resource and adds it to the resource map. * - * @param type The type of resource to load. * @param idPath The resource identifier path. + * @param fun A function for generating the resource. + * @param data Extra parameters for the generator. + * @return A valid resource or NULL if the resource could + * not be generated. + */ + Resource *get(std::string const &idPath, generator fun, void *data); + + /** + * Loads a resource from a file and adds it to the resource map. + * + * @param path The file name. + * @param fun A function for parsing the file. * @return A valid resource or NULL if the resource could * not be loaded. */ - Resource* - get(const E_RESOURCE_TYPE &type, const std::string &idPath); + Resource *load(std::string const &path, loader fun); /** * Convenience wrapper around ResourceManager::get for loading diff --git a/src/resources/soundeffect.cpp b/src/resources/soundeffect.cpp index bb35218e..ec9bc65c 100644 --- a/src/resources/soundeffect.cpp +++ b/src/resources/soundeffect.cpp @@ -25,20 +25,12 @@ #include "../log.h" -SoundEffect::SoundEffect(const std::string &idPath, Mix_Chunk *soundEffect): - Resource(idPath), - mChunk(soundEffect) -{ -} - SoundEffect::~SoundEffect() { Mix_FreeChunk(mChunk); } -SoundEffect* -SoundEffect::load(void *buffer, unsigned int bufferSize, - const std::string &idPath) +Resource *SoundEffect::load(void *buffer, unsigned bufferSize) { // Load the raw file data from the buffer in an RWops structure SDL_RWops *rw = SDL_RWFromMem(buffer, bufferSize); @@ -48,11 +40,11 @@ SoundEffect::load(void *buffer, unsigned int bufferSize, if (tmpSoundEffect) { - return new SoundEffect(idPath, tmpSoundEffect); + return new SoundEffect(tmpSoundEffect); } else { - logger->log("Error while loading sound effect (%s)", idPath.c_str()); + logger->log("Error, failed to load sound effect: %s", Mix_GetError()); return NULL; } } diff --git a/src/resources/soundeffect.h b/src/resources/soundeffect.h index 007f5a77..866c53ec 100644 --- a/src/resources/soundeffect.h +++ b/src/resources/soundeffect.h @@ -45,13 +45,11 @@ class SoundEffect : public Resource * * @param buffer The memory buffer containing the sample data. * @param bufferSize The size of the memory buffer in bytes. - * @param idPath The path identifying the resource. * * @return NULL if the an error occurred, a valid pointer * otherwise. */ - static SoundEffect* - load(void* buffer, unsigned int bufferSize, const std::string &idPath); + static Resource *load(void *buffer, unsigned bufferSize); /** * Plays the sample. @@ -69,7 +67,7 @@ class SoundEffect : public Resource /** * Constructor. */ - SoundEffect(const std::string &idPath, Mix_Chunk *soundEffect); + SoundEffect(Mix_Chunk *soundEffect): mChunk(soundEffect) {} Mix_Chunk *mChunk; }; diff --git a/src/resources/spritedef.cpp b/src/resources/spritedef.cpp index d90d4067..0daa0cc0 100644 --- a/src/resources/spritedef.cpp +++ b/src/resources/spritedef.cpp @@ -33,16 +33,6 @@ #include "../utils/xml.h" -SpriteDef::SpriteDef(const std::string &idPath, - const std::string &file, int variant): - Resource(idPath), - mAction(NULL), - mDirection(DIRECTION_DOWN), - mLastTime(0) -{ - load(file, variant); -} - Action* SpriteDef::getAction(SpriteAction action) const { @@ -57,29 +47,29 @@ SpriteDef::getAction(SpriteAction action) const return i->second; } -void -SpriteDef::load(const std::string &animationFile, int variant) +SpriteDef *SpriteDef::load(std::string const &animationFile, int variant) { int size; ResourceManager *resman = ResourceManager::getInstance(); char *data = (char*) resman->loadFile(animationFile.c_str(), size); - if (!data) { - logger->error("Animation: Could not find " + animationFile + "!"); - } + if (!data) return NULL; xmlDocPtr doc = xmlParseMemory(data, size); free(data); - if (!doc) { - logger->error( - "Animation: Error while parsing " + animationFile + " file!"); + if (!doc) + { + logger->log("Error, failed to parse %s.", animationFile.c_str()); + return NULL; } xmlNodePtr rootNode = xmlDocGetRootElement(doc); - if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "sprite")) { - logger->error( - "Animation: this is not a valid " + animationFile + " file!"); + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "sprite")) + { + logger->log("Error, failed to parse %s.", animationFile.c_str()); + xmlFreeDoc(doc); + return NULL; } // Get the variant @@ -91,25 +81,32 @@ SpriteDef::load(const std::string &animationFile, int variant) variant_offset = variant * XML::getProperty(rootNode, "variant_offset", 0); } + SpriteDef *def = new SpriteDef; + for_each_xml_child_node(node, rootNode) { if (xmlStrEqual(node->name, BAD_CAST "imageset")) { - loadImageSet(node); + def->loadImageSet(node); } else if (xmlStrEqual(node->name, BAD_CAST "action")) { - loadAction(node, variant_offset); + def->loadAction(node, variant_offset); } else if (xmlStrEqual(node->name, BAD_CAST "include")) { - includeSprite(node); + def->includeSprite(node); } } xmlFreeDoc(doc); - // Complete missing actions + def->substituteActions(); + return def; +} + +void SpriteDef::substituteActions() +{ substituteAction(ACTION_STAND, ACTION_DEFAULT); substituteAction(ACTION_WALK, ACTION_STAND); substituteAction(ACTION_WALK, ACTION_RUN); diff --git a/src/resources/spritedef.h b/src/resources/spritedef.h index 55d7f459..121f23cc 100644 --- a/src/resources/spritedef.h +++ b/src/resources/spritedef.h @@ -71,28 +71,26 @@ class SpriteDef : public Resource { public: /** - * Constructor. + * Loads a sprite definition file. */ - SpriteDef(const std::string &idPath, - const std::string &file, int variant); + static SpriteDef *load(std::string const &file, int variant); /** - * Destructor. + * Returns the specified action. */ - ~SpriteDef(); + Action *getAction(SpriteAction action) const; + + private: /** - * Returns the specified action. + * Constructor. */ - Action* - getAction(SpriteAction action) const; + SpriteDef(): mAction(NULL), mDirection(DIRECTION_DOWN), mLastTime(0) {} - private: /** - * Loads a sprite definition file. + * Destructor. */ - void - load(const std::string &file, int variant); + ~SpriteDef(); /** * Loads an imageset element. @@ -120,6 +118,11 @@ class SpriteDef : public Resource void includeSprite(xmlNodePtr includeNode); + /** + * Complete missing actions by copying existing ones. + */ + void substituteActions(); + /** * When there are no animations defined for the action "complete", its * animations become a copy of those of the action "with". diff --git a/src/tileset.h b/src/tileset.h index 7521a3e5..6af69235 100644 --- a/src/tileset.h +++ b/src/tileset.h @@ -36,7 +36,7 @@ class Tileset : public ImageSet * Constructor. */ Tileset(Image *img, int w, int h, int firstGid): - ImageSet("", img, w, h), + ImageSet(img, w, h), mFirstGid(firstGid) { } -- cgit v1.2.3-70-g09d2