From 1a9320fafb23940d0463e6f384713d0f99fc0c61 Mon Sep 17 00:00:00 2001 From: Bjørn Lindeijer Date: Wed, 19 Sep 2007 17:28:33 +0000 Subject: Merged 0.0 changes from revision 3362 to 3580 to trunk. --- src/resources/equipmentdb.cpp | 156 ------------------------------------------ src/resources/equipmentdb.h | 55 --------------- src/resources/equipmentinfo.h | 52 -------------- src/resources/itemdb.cpp | 126 +++++++++++++++++++++++++++------- src/resources/iteminfo.cpp | 86 ++++++++++++++++++++--- src/resources/iteminfo.h | 118 ++++++++++++++++++++------------ src/resources/mapreader.cpp | 2 +- src/resources/monsterdb.cpp | 8 +-- src/resources/monsterinfo.cpp | 12 ++-- src/resources/monsterinfo.h | 16 ++--- src/resources/spritedef.h | 4 +- 11 files changed, 271 insertions(+), 364 deletions(-) delete mode 100644 src/resources/equipmentdb.cpp delete mode 100644 src/resources/equipmentdb.h delete mode 100644 src/resources/equipmentinfo.h (limited to 'src/resources') diff --git a/src/resources/equipmentdb.cpp b/src/resources/equipmentdb.cpp deleted file mode 100644 index 38ac6415..00000000 --- a/src/resources/equipmentdb.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * The Mana World - * Copyright 2006 The Mana World Development Team - * - * This file is part of The Mana World. - * - * The Mana World 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 - * any later version. - * - * The Mana World 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 The Mana World; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * $Id$ - */ - -#include "equipmentdb.h" - -#include "resourcemanager.h" - -#include "../log.h" - -#include "../utils/dtor.h" -#include "../utils/xml.h" - -namespace -{ - EquipmentDB::EquipmentInfos mEquipmentInfos; - EquipmentInfo mUnknown; - bool mLoaded = false; -} - -void -EquipmentDB::load() -{ - if (mLoaded) - return; - - logger->log("Initializing equipment database..."); - mUnknown.setSprite("error.xml", 0); - mUnknown.setSprite("error.xml", 1); - - ResourceManager *resman = ResourceManager::getInstance(); - int size; - char *data = (char*)resman->loadFile("equipment.xml", size); - - if (!data) - { - logger->error("Equipment Database: Could not find equipment.xml!"); - } - - xmlDocPtr doc = xmlParseMemory(data, size); - free(data); - - if (!doc) - { - logger->error("Equipment Database: Error while parsing equipment database (equipment.xml)!"); - } - - xmlNodePtr rootNode = xmlDocGetRootElement(doc); - if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "equipments")) - { - logger->error("Equipment Database: equipment.xml is not a valid database file!"); - } - - //iterate s - for_each_xml_child_node(equipmentNode, rootNode) - { - if (!xmlStrEqual(equipmentNode->name, BAD_CAST "equipment")) - { - continue; - } - - EquipmentInfo *currentInfo = new EquipmentInfo(); - - currentInfo->setSlot (XML::getProperty(equipmentNode, "slot", 0)); - - //iterate s - for_each_xml_child_node(spriteNode, equipmentNode) - { - if (!xmlStrEqual(spriteNode->name, BAD_CAST "sprite")) - { - continue; - } - - std::string gender = XML::getProperty(spriteNode, "gender", "unisex"); - std::string filename = (const char*) spriteNode->xmlChildrenNode->content; - - if (gender == "male" || gender == "unisex") - { - currentInfo->setSprite(filename, 0); - } - - if (gender == "female" || gender == "unisex") - { - currentInfo->setSprite(filename, 1); - } - } - - setEquipment( XML::getProperty(equipmentNode, "id", 0), - currentInfo); - } - - mLoaded = true; -} - -void -EquipmentDB::unload() -{ - // kill EquipmentInfos - for_each ( mEquipmentInfos.begin(), mEquipmentInfos.end(), - make_dtor(mEquipmentInfos)); - mEquipmentInfos.clear(); - - mLoaded = false; -} - -EquipmentInfo* -EquipmentDB::get(int id) -{ - if (!mLoaded) { - logger->error("Error: Equipment database used before initialization!"); - } - - EquipmentInfoIterator i = mEquipmentInfos.find(id); - - if (i == mEquipmentInfos.end() ) - { - logger->log("EquipmentDB: Error, unknown equipment ID# %d", id); - return &mUnknown; - } - else - { - return i->second; - } -} - -void -EquipmentDB::setEquipment(int id, EquipmentInfo* equipmentInfo) -{ - if (mEquipmentInfos.find(id) != mEquipmentInfos.end()) { - logger->log("Warning: Equipment Piece with ID %d defined multiple times", - id); - delete equipmentInfo; - } - else { - mEquipmentInfos[id] = equipmentInfo; - }; -} diff --git a/src/resources/equipmentdb.h b/src/resources/equipmentdb.h deleted file mode 100644 index 1c1db7d1..00000000 --- a/src/resources/equipmentdb.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * The Mana World - * Copyright 2006 The Mana World Development Team - * - * This file is part of The Mana World. - * - * The Mana World 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 - * any later version. - * - * The Mana World 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 The Mana World; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * $Id$ - */ - -#ifndef _TMW_EQUIPMENT_DB_H -#define _TMW_EQUIPMENT_DB_H - -#include - -#include "equipmentinfo.h" - -/** - * Equipment information database. - */ -namespace EquipmentDB -{ - /** - * Loads the equipment info from Items.xml - */ - void load(); - - /** - * Frees equipment data - */ - void unload(); - - void setEquipment(int id, EquipmentInfo* equipmentInfo); - - EquipmentInfo* get(int id); - - // Equipment database types - typedef std::map EquipmentInfos; - typedef EquipmentInfos::iterator EquipmentInfoIterator; -} - -#endif diff --git a/src/resources/equipmentinfo.h b/src/resources/equipmentinfo.h deleted file mode 100644 index 75ed1b8a..00000000 --- a/src/resources/equipmentinfo.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * The Mana World - * Copyright 2006 The Mana World Development Team - * - * This file is part of The Mana World. - * - * The Mana World 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 - * any later version. - * - * The Mana World 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 The Mana World; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * $Id: - */ - -#ifndef _TMW_EQUIPMENTINFO_H_ -#define _TMW_EQUIPMENTINFO_H_ - -#include -#include - -class EquipmentInfo -{ - public: - EquipmentInfo(): - mSlot (0) - { - }; - - void - setSlot (int slot) { mSlot = slot; }; - - const std::string& - getSprite(int gender) {return animationFiles[gender]; }; - - void - setSprite(std::string animationFile, int gender) {animationFiles[gender] = animationFile; }; - - private: - int mSlot; //not used at the moment but maybe useful on our own server - std::map animationFiles; -}; - -#endif diff --git a/src/resources/itemdb.cpp b/src/resources/itemdb.cpp index 7b16339c..18952ae9 100644 --- a/src/resources/itemdb.cpp +++ b/src/resources/itemdb.cpp @@ -18,9 +18,11 @@ * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * $Id: + * $Id$ */ +#include + #include "itemdb.h" #include @@ -36,10 +38,13 @@ namespace { ItemDB::ItemInfos mItemInfos; - ItemInfo mUnknown; + ItemInfo *mUnknown; bool mLoaded = false; } +// Forward declarations +static void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node); +static void loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node); void ItemDB::load() { @@ -47,11 +52,16 @@ void ItemDB::load() return; logger->log("Initializing item database..."); - mUnknown.setName("Unknown item"); + + mUnknown = new ItemInfo(); + mUnknown->setName("Unknown item"); + mUnknown->setImage(""); + mUnknown->setSprite("error.xml", 0); + mUnknown->setSprite("error.xml", 1); ResourceManager *resman = ResourceManager::getInstance(); int size; - char *data = (char*)resman->loadFile("items.xml", size); + char *data = (char*) resman->loadFile("items.xml", size); if (!data) { logger->error("ItemDB: Could not find items.xml!"); @@ -73,48 +83,66 @@ void ItemDB::load() for_each_xml_child_node(node, rootNode) { - if (!xmlStrEqual(node->name, BAD_CAST "item")) { + if (!xmlStrEqual(node->name, BAD_CAST "item")) continue; - } int id = XML::getProperty(node, "id", 0); - int art = XML::getProperty(node, "art", 0); + + if (id == 0) + { + logger->log("ItemDB: Invalid or missing item ID in items.xml!"); + continue; + } + else if (mItemInfos.find(id) != mItemInfos.end()) + { + logger->log("ItemDB: Redefinition of item ID %d", id); + } + int type = XML::getProperty(node, "type", 0); int weight = XML::getProperty(node, "weight", 0); + int view = XML::getProperty(node, "view", 0); int slot = XML::getProperty(node, "slot", 0); std::string name = XML::getProperty(node, "name", ""); std::string image = XML::getProperty(node, "image", ""); std::string description = XML::getProperty(node, "description", ""); std::string effect = XML::getProperty(node, "effect", ""); + std::string attackType = XML::getProperty(node, "attacktype", ""); - if (id && name != "") + if (id) { ItemInfo *itemInfo = new ItemInfo(); itemInfo->setImage(image); - itemInfo->setArt(art); - itemInfo->setName(name); + itemInfo->setName((name == "") ? "Unnamed" : name); itemInfo->setDescription(description); itemInfo->setEffect(effect); itemInfo->setType(type); + itemInfo->setView(view); itemInfo->setWeight(weight); itemInfo->setSlot(slot); - mItemInfos[id] = itemInfo; - } + itemInfo->setAttackType(attackType); + + for_each_xml_child_node(itemChild, node) + { + if (xmlStrEqual(itemChild->name, BAD_CAST "sprite")) + { + loadSpriteRef(itemInfo, itemChild); + } + else if (xmlStrEqual(itemChild->name, BAD_CAST "sound")) + { + loadSoundRef(itemInfo, itemChild); + } + } - if (id == 0) - { - logger->log("ItemDB: An item has no ID in items.xml!"); + mItemInfos[id] = itemInfo; } #define CHECK_PARAM(param, error_value) \ if (param == error_value) \ - logger->log("ItemDB: Missing" #param " parameter for item %i! %s", \ - id, name.c_str()) + logger->log("ItemDB: Missing " #param " attribute for item %i!",id) CHECK_PARAM(name, ""); CHECK_PARAM(image, ""); - // CHECK_PARAM(art, 0); // CHECK_PARAM(description, ""); // CHECK_PARAM(effect, ""); // CHECK_PARAM(type, 0); @@ -131,19 +159,65 @@ void ItemDB::load() void ItemDB::unload() { - for (ItemInfoIterator i = mItemInfos.begin(); i != mItemInfos.end(); i++) - { - delete i->second; - } - mItemInfos.clear(); + logger->log("Unloading item database..."); + + delete mUnknown; + mUnknown = NULL; + for_each(mItemInfos.begin(), mItemInfos.end(), make_dtor(mItemInfos)); + mItemInfos.clear(); mLoaded = false; } -const ItemInfo& -ItemDB::get(int id) +const ItemInfo& ItemDB::get(int id) { + assert(mLoaded); + ItemInfoIterator i = mItemInfos.find(id); - return (i != mItemInfos.end()) ? *(i->second) : mUnknown; + if (i == mItemInfos.end()) + { + logger->log("ItemDB: Error, unknown item ID# %d", id); + return *mUnknown; + } + else + { + return *(i->second); + } +} + +void loadSpriteRef(ItemInfo *itemInfo, xmlNodePtr node) +{ + std::string gender = XML::getProperty(node, "gender", "unisex"); + std::string filename = (const char*) node->xmlChildrenNode->content; + + if (gender == "male" || gender == "unisex") + { + itemInfo->setSprite(filename, 0); + } + + if (gender == "female" || gender == "unisex") + { + itemInfo->setSprite(filename, 1); + } +} + +void loadSoundRef(ItemInfo *itemInfo, xmlNodePtr node) +{ + std::string event = XML::getProperty(node, "event", ""); + std::string filename = (const char*) node->xmlChildrenNode->content; + + if (event == "hit") + { + itemInfo->addSound(EQUIP_EVENT_HIT, filename); + } + else if (event == "strike") + { + itemInfo->addSound(EQUIP_EVENT_STRIKE, filename); + } + else + { + logger->log("ItemDB: Ignoring unknown sound event '%s'", + event.c_str()); + } } diff --git a/src/resources/iteminfo.cpp b/src/resources/iteminfo.cpp index 3a41c657..b5b25ac0 100644 --- a/src/resources/iteminfo.cpp +++ b/src/resources/iteminfo.cpp @@ -25,11 +25,11 @@ #include "resourcemanager.h" #include "image.h" - +#include "itemdb.h" ItemInfo::~ItemInfo() { - if (mImage != NULL) + if (mImage) { mImage->decRef(); } @@ -38,19 +38,87 @@ ItemInfo::~ItemInfo() void ItemInfo::setImage(const std::string &image) { + if (mImage) + { + mImage->decRef(); + } + + ResourceManager *resman = ResourceManager::getInstance(); mImageName = "graphics/items/" + image; + mImage = ResourceManager::getInstance()->getImage(mImageName); - if (mImageName != "") + if (!mImage) { - if (mImage != NULL) - { - mImage->decRef(); - } + mImage = resman->getImage("graphics/gui/unknown-item.png"); + } +} - mImage = ResourceManager::getInstance()->getImage(mImageName); +const std::string& +ItemInfo::getSprite(int gender) const +{ + if (mView) + { + // Forward the request to the item defining how to view this item + return ItemDB::get(mView).getSprite(gender); } else { - mImage = NULL; + static const std::string empty = ""; + std::map::const_iterator i = + mAnimationFiles.find(gender); + + return (i != mAnimationFiles.end()) ? i->second : empty; + } +} + +void +ItemInfo::setAttackType(const std::string &attackType) +{ + if (attackType == "swing") + { + mAttackType = ACTION_ATTACK_SWING; + } + else if (attackType == "stab") + { + mAttackType = ACTION_ATTACK_STAB; + } + else if (attackType == "bow") + { + mAttackType = ACTION_ATTACK_BOW; } + else if (attackType == "throw") + { + mAttackType = ACTION_ATTACK_THROW; + } + else if (attackType == "none") + { + mAttackType = ACTION_DEFAULT; + } + else + { + mAttackType = ACTION_ATTACK; + } +} + +void +ItemInfo::addSound(EquipmentSoundEvent event, const std::string &filename) +{ + if (mSounds.find(event) == mSounds.end()) + { + mSounds[event] = new std::vector; + } + + mSounds[event]->push_back("sfx/" + filename); +} + + +const std::string& +ItemInfo::getSound(EquipmentSoundEvent event) const +{ + static const std::string empty = ""; + std::map*>::const_iterator i; + i = mSounds.find(event); + + return (i == mSounds.end()) ? empty : + i->second->at(rand() % i->second->size()); } diff --git a/src/resources/iteminfo.h b/src/resources/iteminfo.h index e4f851bb..4fd1638e 100644 --- a/src/resources/iteminfo.h +++ b/src/resources/iteminfo.h @@ -24,12 +24,23 @@ #ifndef _TMW_ITEMINFO_H_ #define _TMW_ITEMINFO_H_ +#include #include +#include + +#include "spritedef.h" class Image; +enum EquipmentSoundEvent +{ + EQUIP_EVENT_STRIKE, + EQUIP_EVENT_HIT +}; + /** - * Defines a class for storing item infos. + * Defines a class for storing item infos. This includes information used when + * the item is equipped. */ class ItemInfo { @@ -40,10 +51,11 @@ class ItemInfo ItemInfo(): mImageName(""), mImage(NULL), - mArt(0), mType(0), mWeight(0), - mSlot(0) + mView(0), + mSlot(0), + mAttackType(ACTION_DEFAULT) { } @@ -52,73 +64,89 @@ class ItemInfo */ ~ItemInfo(); - void - setArt(short art) { mArt = art; } + void setName(const std::string &name) + { mName = name; } - short - getArt() const { return mArt; } + const std::string& getName() const + { return mName; } - void - setName(const std::string &name) { mName = name; } + void setImage(const std::string &image); - const std::string& - getName() const { return mName; } + Image* getImage() const + { return mImage; } - void - setImage(const std::string &image); + void setDescription(const std::string &description) + { mDescription = description; } - Image* - getImage() const { return mImage; } + const std::string& getDescription() const + { return mDescription; } - void - setDescription(const std::string &description) - { - mDescription = description; - } + void setEffect(const std::string &effect) + { mEffect = effect; } const std::string& - getDescription() const { return mDescription; } + getEffect() const { return mEffect; } - void - setEffect(const std::string &effect) { mEffect = effect; } + void setType(short type) + { mType = type; } - const std::string& - getEffect() const { return mEffect; } + short getType() const + { return mType; } + + void setWeight(short weight) + { mWeight = weight; } + + short getWeight() const + { return mWeight; } + + void setView(int view) + { mView = view; } + + void setSlot(char slot) + { mSlot = slot; } - void - setType(short type) { mType = type; } + char getSlot() const + { return mSlot; } - short - getType() const { return mType; } + void setSprite(const std::string &animationFile, int gender) + { mAnimationFiles[gender] = animationFile; } - void - setWeight(short weight) { mWeight = weight; } + const std::string& getSprite(int gender) const; - short - getWeight() const { return mWeight; } + void setAttackType(const std::string &attackType); - void - setSlot(char slot) { mSlot = slot; } + const SpriteAction getAttackType() const + { return mAttackType; } - char - getSlot() const { return mSlot; } + void addSound(EquipmentSoundEvent event, const std::string &filename); + + const std::string& getSound(EquipmentSoundEvent event) const; protected: - std::string mImageName; + std::string mImageName; /**< The filename of the icon image. */ /* TODO (BL): I do not think the item info should keep a reference to * the item icon. It would probably be better if this was kept in the * Item class, so that the images can be lazily instantiated and also * unloaded when no longer used. */ - Image *mImage; - short mArt; + Image *mImage; /**< The loaded icon image. */ std::string mName; - std::string mDescription; - std::string mEffect; - short mType; - short mWeight; - char mSlot; + std::string mDescription; /**< Short description. */ + std::string mEffect; /**< Description of effects. */ + short mType; /**< Item type (never used). */ + short mWeight; /**< Weight in grams. */ + int mView; /**< Item ID of how this item looks. */ + + // Equipment related members + char mSlot; /**< Equipment slot. */ + SpriteAction mAttackType; /**< Attack type, in case of weapon. */ + + /** Maps gender to sprite filenames. */ + std::map mAnimationFiles; + + /** Stores the names of sounds to be played at certain event. */ + std::map* > mSounds; }; #endif diff --git a/src/resources/mapreader.cpp b/src/resources/mapreader.cpp index 260d5aa9..940ded36 100644 --- a/src/resources/mapreader.cpp +++ b/src/resources/mapreader.cpp @@ -156,7 +156,7 @@ MapReader::readMap(const std::string &filename) if (buffer == NULL) { - logger->log("Map file not found (%s)\n", filename.c_str()); + logger->log("Map file not found (%s)", filename.c_str()); return NULL; } diff --git a/src/resources/monsterdb.cpp b/src/resources/monsterdb.cpp index 339ed6ba..f4864eea 100644 --- a/src/resources/monsterdb.cpp +++ b/src/resources/monsterdb.cpp @@ -120,19 +120,19 @@ MonsterDB::load() if (event == "hit") { - currentInfo->addSound(EVENT_HIT, filename); + currentInfo->addSound(MONSTER_EVENT_HIT, filename); } else if (event == "miss") { - currentInfo->addSound(EVENT_MISS, filename); + currentInfo->addSound(MONSTER_EVENT_MISS, filename); } else if (event == "hurt") { - currentInfo->addSound(EVENT_HURT, filename); + currentInfo->addSound(MONSTER_EVENT_HURT, filename); } else if (event == "die") { - currentInfo->addSound(EVENT_DIE, filename); + currentInfo->addSound(MONSTER_EVENT_DIE, filename); } else { diff --git a/src/resources/monsterinfo.cpp b/src/resources/monsterinfo.cpp index 2a59419e..2e896237 100644 --- a/src/resources/monsterinfo.cpp +++ b/src/resources/monsterinfo.cpp @@ -33,15 +33,15 @@ MonsterInfo::MonsterInfo(): MonsterInfo::~MonsterInfo() { - //kill vectors in mSoundEffects - for_each ( mSounds.begin(), mSounds.end(), - make_dtor(mSounds)); + // kill vectors in mSoundEffects + for_each (mSounds.begin(), mSounds.end(), + make_dtor(mSounds)); mSounds.clear(); } void -MonsterInfo::addSound (SoundEvent event, std::string filename) +MonsterInfo::addSound(MonsterSoundEvent event, std::string filename) { if (mSounds.find(event) == mSounds.end()) { @@ -53,9 +53,9 @@ MonsterInfo::addSound (SoundEvent event, std::string filename) std::string -MonsterInfo::getSound (SoundEvent event) const +MonsterInfo::getSound(MonsterSoundEvent event) const { - std::map* >::const_iterator i; + std::map* >::const_iterator i; i = mSounds.find(event); diff --git a/src/resources/monsterinfo.h b/src/resources/monsterinfo.h index aa7db9f0..c9fbd4c9 100644 --- a/src/resources/monsterinfo.h +++ b/src/resources/monsterinfo.h @@ -31,12 +31,12 @@ #include "../being.h" -enum SoundEvent +enum MonsterSoundEvent { - EVENT_HIT, - EVENT_MISS, - EVENT_HURT, - EVENT_DIE + MONSTER_EVENT_HIT, + MONSTER_EVENT_MISS, + MONSTER_EVENT_HURT, + MONSTER_EVENT_DIE }; /** @@ -69,7 +69,7 @@ class MonsterInfo { mTargetCursorSize = targetCursorSize; } void - addSound(SoundEvent event, std::string filename); + addSound(MonsterSoundEvent event, std::string filename); const std::string& getName () const { return mName; }; @@ -81,13 +81,13 @@ class MonsterInfo getTargetCursorSize() const { return mTargetCursorSize; } std::string - getSound (SoundEvent event) const; + getSound(MonsterSoundEvent event) const; private: std::string mName; std::string mSprite; Being::TargetCursorSize mTargetCursorSize; - std::map* > mSounds; + std::map* > mSounds; }; #endif diff --git a/src/resources/spritedef.h b/src/resources/spritedef.h index 6d335b02..55d7f459 100644 --- a/src/resources/spritedef.h +++ b/src/resources/spritedef.h @@ -24,11 +24,11 @@ #ifndef _TMW_SPRITEDEF_H #define _TMW_SPRITEDEF_H -#include "resource.h" - #include #include +#include "resource.h" + #include class Action; -- cgit v1.2.3-70-g09d2