예제 #1
0
void SetWindowIcon(photon_window &window, const std::string &filename){
    if(PHYSFS_exists(filename.c_str())){
        auto fp = PHYSFS_openRead(filename.c_str());
        intmax_t length = PHYSFS_fileLength(fp);
        if(length > 0){
            uint8_t *buffer = new uint8_t[length];

            PHYSFS_read(fp, buffer, 1, length);

            PHYSFS_close(fp);

            SDL_RWops *rw = SDL_RWFromMem(buffer, length);
            SDL_Surface *icon = IMG_Load_RW(rw, 1);

            if(icon == nullptr){
                PrintToLog("ERROR: icon loading failed! %s", IMG_GetError());
            }

            SDL_SetWindowIcon(window.window_SDL, icon);

            SDL_FreeSurface(icon);
            delete[] buffer;
        }else{
            PrintToLog("ERROR: Unable to open image file \"%s\"!");
        }
    }else{
        PrintToLog("ERROR: Image file \"%s\" does not exist!", filename.c_str());
    }
}
예제 #2
0
photon_window CreateSDLWindow(const photon_settings &settings){
    PrintToLog("INFO: Initializing SDL.");
    photon_window window;

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);

    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) == -1) {
        PrintToLog("ERROR: Unable to init SDL! \"%s\"", SDL_GetError());
        // TODO - error handling.
        abort();
    }

    SDL_GL_SetAttribute(SDL_GL_RED_SIZE,            8);
    SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,          8);
    SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,           8);
    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE,          8);

    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,          16);
    SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE,         32);

    if(settings.multisamples > 0){
        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS,  1);
        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES,  settings.multisamples);
    }

    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, settings.doublebuffer);

    window.window_SDL = SDL_CreateWindow("Photon", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, PHOTON_WINDOW_FLAGS);

    if (!window.window_SDL){
        PrintToLog("ERROR: Unable to create window!");
        // TODO - error handling.
        abort();
    }

    window.context_SDL = SDL_GL_CreateContext(window.window_SDL);

    SDL_GL_SetSwapInterval(settings.vsync);

    SDL_ShowCursor(SDL_ENABLE);

    if(settings.fullscreen){
        ToggleFullscreen(window);
    }

    SetWindowIcon(window, "/textures/gui/window_icon.png");

    return window;
}
예제 #3
0
/**
 * Updates the number of tokens for the given tally type.
 *
 * Negative balances are only permitted for pending balances.
 *
 * @param propertyId  The identifier of the tally to update
 * @param amount      The amount to add
 * @param ttype       The tally type
 * @return True, if the update was successful
 */
bool CMPTally::updateMoney(uint32_t propertyId, int64_t amount, TallyType ttype)
{
    if (TALLY_TYPE_COUNT <= ttype || amount == 0) {
        return false;
    }
    bool fUpdated = false;
    int64_t now64 = mp_token[propertyId].balance[ttype];

    if (isOverflow(now64, amount)) {
        PrintToLog("%s(): ERROR: arithmetic overflow [%d + %d]\n", __func__, now64, amount);
        return false;
    }

    if (PENDING != ttype && (now64 + amount) < 0) {
        // NOTE:
        // Negative balances are only permitted for pending balances
    } else {

        now64 += amount;
        mp_token[propertyId].balance[ttype] = now64;

        fUpdated = true;
    }

    return fUpdated;
}
예제 #4
0
/** Obtains a hash of the balances for a specific property. */
uint256 GetBalancesHash(const uint32_t hashPropertyId)
{
    SHA256_CTX shaCtx;
    SHA256_Init(&shaCtx);

    LOCK(cs_tally);

    std::map<std::string, CMPTally> tallyMapSorted;
    for (std::unordered_map<string, CMPTally>::iterator uoit = mp_tally_map.begin(); uoit != mp_tally_map.end(); ++uoit) {
        tallyMapSorted.insert(std::make_pair(uoit->first,uoit->second));
    }
    for (std::map<string, CMPTally>::iterator my_it = tallyMapSorted.begin(); my_it != tallyMapSorted.end(); ++my_it) {
        const std::string& address = my_it->first;
        CMPTally& tally = my_it->second;
        tally.init();
        uint32_t propertyId = 0;
        while (0 != (propertyId = (tally.next()))) {
            if (propertyId != hashPropertyId) continue;
            std::string dataStr = GenerateConsensusString(tally, address, propertyId);
            if (dataStr.empty()) continue;
            if (msc_debug_consensus_hash) PrintToLog("Adding data to balances hash: %s\n", dataStr);
            SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
        }
    }

    uint256 balancesHash;
    SHA256_Final((unsigned char*)&balancesHash, &shaCtx);

    return balancesHash;
}
예제 #5
0
photon_input InitInput(){
    PrintToLog("INFO: Initializing Input System.");
    photon_input input;

    SDL_GameControllerEventState(SDL_ENABLE);
    SDL_JoystickEventState(SDL_ENABLE);

    for(int i = 0; i < SDL_NumJoysticks(); i++){
        if(SDL_IsGameController(i)){
            input.open_controllers.push_back(SDL_GameControllerOpen(i));
        }else{
            input.open_joysticks.push_back(SDL_JoystickOpen(i));
        }
    }
    PrintToLog("INFO: Listening for input, press a button on the device you want to use.");

    return input;
}
예제 #6
0
void GarbageCollect(photon_window &window, bool quitSDL){
    SDL_GL_DeleteContext(window.context_SDL);
    SDL_DestroyWindow(window.window_SDL);

    if(quitSDL){
        SDL_Quit();
    }

    PrintToLog("INFO: SDL garbage collection complete.");
}
예제 #7
0
void ToggleFullscreen(photon_window &window){
    window.fullscreen = !window.fullscreen;

    if(window.fullscreen){
        SDL_DisplayMode mode;
        SDL_GetDesktopDisplayMode(0, &mode);
        SDL_SetWindowDisplayMode(window.window_SDL, &mode);
    }
    SDL_SetWindowFullscreen(window.window_SDL, SDL_bool(window.fullscreen));

    PrintToLog("INFO: Window toggled fullscreen.");
}
예제 #8
0
void GarbageCollect(photon_input &input){
    for(auto controller : input.open_controllers){
        if(controller == input.controller){
            input.controller = nullptr;
        }
        SDL_GameControllerClose(controller);
    }
    for(auto joystick : input.open_joysticks){
        if(joystick == input.joystick){
            input.joystick = nullptr;
        }
        SDL_JoystickClose(joystick);
    }
    PrintToLog("INFO: Input System garbage collection complete.");
}
예제 #9
0
void AdvanceFrame(photon_level &level, photon_player &player, float time){
    level.beams.clear();
    for(auto &block : level.grid){
        blocks::OnFrame(glm::uvec2(block.first.first, block.first.second), level, time);
    }
    for(photon_laserbeam &beam : level.beams){
        tracer::TraceBeam(beam, level, time);
    }

    level.time += time;

    lua::AdvanceFrame();

    // if victory_state is 0 before checking but not after then defeat occured. (keeps it from repeating message over and over)
    if(level.victory_state == 0 && level::CheckVictory(level, player) < 0){
        PrintToLog("INFO: DEFEAT! (game is unwinnable)");
    }
}
예제 #10
0
void DoEvents(photon_instance &instance){
    SDL_Event event;
    while(SDL_PollEvent(&event)){
        switch (event.type) {
        case SDL_TEXTINPUT:
            if(instance.gui.load_save_menu.loading || instance.gui.load_save_menu.saving){

                instance.gui.load_save_menu.filename.insert(instance.gui.load_save_menu.cursor, event.text.text);

                for(char* c = event.text.text; *c != '\0'; c++){
                    instance.gui.load_save_menu.cursor++;
                }
                instance.gui.load_save_menu.current_file_index = -1;
            }else{
                // if we don't need text input anymore...
                SDL_StopTextInput();
            }
            break;
        case SDL_TEXTEDITING:
            if(instance.gui.load_save_menu.loading || instance.gui.load_save_menu.saving){
                instance.gui.load_save_menu.filename.insert(event.edit.start, event.edit.text);
            }else{
                // if we don't need text input anymore...
                SDL_StopTextInput();
            }
            break;
        case SDL_KEYDOWN:
            if(!instance.input.is_valid){
                input::LoadConfig("/config/keyboard.xml", instance.input);

                PrintToLog("INFO: Using keyboard input.");
            }
            if(event.key.keysym.sym == SDLK_f && event.key.keysym.mod & KMOD_CTRL){
                window_managment::ToggleFullscreen(instance.window);
            }else if(event.key.keysym.sym == SDLK_i && event.key.keysym.mod & KMOD_CTRL){
                // re-detect input. by doing a garbage collect and an init over again it will detect newly connected controllers.
                PrintToLog("INFO: Reinitilizing input system to redetect available devices...");
                GarbageCollect(instance.input);
                instance.input = InitInput();
            }else if((instance.gui.load_save_menu.loading || instance.gui.load_save_menu.saving)){
                photon_gui_load_save_menu &load_save_menu = instance.gui.load_save_menu;
                if(event.key.keysym.sym == SDLK_BACKSPACE){
                    if(load_save_menu.cursor > 0 && load_save_menu.cursor <= load_save_menu.filename.length()){
                        load_save_menu.filename.erase(--load_save_menu.cursor, 1);
                        load_save_menu.current_file_index = -1;
                    }
                }else if(event.key.keysym.sym == SDLK_DELETE){
                    if(load_save_menu.cursor >= 0 && load_save_menu.cursor < load_save_menu.filename.length()){
                        load_save_menu.filename.erase(load_save_menu.cursor, 1);
                        load_save_menu.current_file_index = -1;
                    }
                }else if(event.key.keysym.sym == SDLK_LEFT && instance.input.left.type != photon_input_state::keyboard){
                    if(--load_save_menu.cursor < 0){
                        load_save_menu.cursor = 0;
                    }
                }else if(event.key.keysym.sym == SDLK_RIGHT && instance.input.right.type != photon_input_state::keyboard){
                    if(++load_save_menu.cursor > load_save_menu.filename.length()){
                        load_save_menu.cursor = load_save_menu.filename.length();
                    }
                }else if(event.key.keysym.sym == SDLK_HOME){
                    if(load_save_menu.current_file_index == -1){
                        load_save_menu.cursor = 0;
                    }else{
                        load_save_menu.current_file_index = 0;
                        load_save_menu.filename = load_save_menu.file_list[load_save_menu.current_file_index];
                    }
                }else if(event.key.keysym.sym == SDLK_END){
                    if(load_save_menu.current_file_index == -1){
                        load_save_menu.cursor = load_save_menu.filename.length();
                    }else{
                        load_save_menu.current_file_index = load_save_menu.file_list.size() - 1;
                        load_save_menu.filename = load_save_menu.file_list[load_save_menu.current_file_index];
                    }
                }else if(event.key.keysym.sym == SDLK_ESCAPE){
                    gui::CancelLoadSave(instance);
                }
            }else if(event.key.keysym.sym == SDLK_ESCAPE){
                instance.paused = !instance.paused;
            }else if(event.key.keysym.sym == SDLK_s && event.key.keysym.mod & KMOD_CTRL){
                instance.paused = true;
                gui::StartSavingGUI(instance);
            }
            break;
        case SDL_QUIT:
            Close(instance);
            break;
        case SDL_WINDOWEVENT:
            switch(event.window.event){
            case SDL_WINDOWEVENT_RESIZED:
//            case SDL_WINDOWEVENT_SIZE_CHANGED:
                if(event.window.data1 < 1){
                    // prevent window dissapearing if some weirdo decides to resize the window to a width of 0...
                    SDL_SetWindowSize(instance.window.window_SDL, 1, event.window.data2);
                    break;
                }
                if(event.window.data2 < 1){
                    // prevent window dissapearing if some weirdo decides to resize the window to a height of 0...
                    SDL_SetWindowSize(instance.window.window_SDL, event.window.data1, 1);
                    break;
                }
                opengl::OnResize(event.window.data1,event.window.data2, instance.window);
                break;
            }
            break;
        case SDL_MOUSEWHEEL:
            if(!instance.paused){
                instance.camera_offset.z -= event.wheel.y * instance.camera_offset.z * 0.02f;
            }
            break;
        case SDL_MOUSEBUTTONUP:
            if(!instance.input.is_valid){
                input::LoadConfig("/config/keyboard.xml", instance.input);

                PrintToLog("INFO: Using mouse & keyboard input.");
            }else if(event.button.button == SDL_BUTTON_LEFT){
                if(!gui::HandleMouseClick(instance, event.button.x, event.button.y)){
                    if(glm::length(instance.player.location - WindowToWorldCoord(instance, event.button.x, event.button.y)) < 0.5f){
                        blocks::OnPhotonInteract(glm::uvec2(instance.player.location + 0.5f), instance.level, instance.player);
                    }
                }
            }
            break;
        case SDL_CONTROLLERBUTTONUP:
            if(!instance.input.is_valid){
                SDL_GameController *controller = SDL_GameControllerOpen(event.cbutton.which);

                if(controller != nullptr){
                    input::LoadConfig("/config/controller.xml", instance.input);
                    instance.input.controller = controller;

                    PrintToLog("INFO: Using Game Controller (device index %i)", event.cbutton.which);
                }
            }
            break;
        case SDL_CONTROLLERAXISMOTION:
            if(!instance.input.is_valid){
                SDL_GameController *controller = SDL_GameControllerOpen(event.caxis.which);

                if(controller != nullptr){
                    input::LoadConfig("/config/controller.xml", instance.input);
                    instance.input.controller = controller;

                    PrintToLog("INFO: Using Game Controller (device index %i)", event.caxis.which);
                }
            }
            break;
        }
    }
}
예제 #11
0
void InitFreeType(){
    PrintToLog("INFO: Initializing FreeType.");
    FT_Library ft;
    FT_Face face;

    if(FT_Init_FreeType(&ft)) {
        PrintToLog("ERROR: Could not init FreeType library!");
        return;
    }

    // TODO - load from some configuration file...
    const char *font_filename = "/Orbitron Bold.otf";

    PHYSFS_File *file;
    intmax_t length;
    uint8_t *font_buffer;

    file = PHYSFS_openRead(font_filename);
    if(!file){
        PrintToLog("ERROR: unable to open Font File: \"%s\"", font_filename);
        return;
    }

    length = PHYSFS_fileLength(file);
    font_buffer = new uint8_t[length];

    PHYSFS_read(file, font_buffer, 1, length);
    PHYSFS_close(file);

    FT_Error err = FT_New_Memory_Face(ft, font_buffer, length, 0, &face);

    if(err){
        PrintToLog("ERROR: Could not load font \"%s\"! error code %x!", font_filename, err);
        delete[] font_buffer;
        return;
    }

    FT_Set_Pixel_Sizes(face, 0, FONT_SIZE);

    FT_GlyphSlot g = face->glyph;
    int width = 0;
    int height = 0;

    for(unsigned char c = FONT_FIRST_CHAR; c <= FONT_LAST_CHAR; c++) {
        if(FT_Load_Char(face, c, FT_LOAD_RENDER)) {
            PrintToLog("WARNING: Loading character %c failed!", c);
            continue;
        }

        width += g->bitmap.width + 1;
        height = std::max(height, int(g->bitmap.rows));
    }

    main_atlas.width = width;

    glGenTextures(1, &main_atlas.texture);
    glBindTexture(GL_TEXTURE_2D, main_atlas.texture);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    std::vector<GLubyte> empty_image(width * height, 0);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &empty_image[0]);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    uint32_t x = 0;

    for(unsigned char i = FONT_FIRST_CHAR; i <= FONT_LAST_CHAR; i++) {
        if(FT_Load_Char(face, i, FT_LOAD_RENDER))
            continue;

        glTexSubImage2D(GL_TEXTURE_2D, 0, x, 0, g->bitmap.width, g->bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer);

        main_atlas.characters[i].advance.x = g->advance.x >> 6;
        main_atlas.characters[i].advance.y = g->advance.y >> 6;

        main_atlas.characters[i].width = g->bitmap.width;
        main_atlas.characters[i].rows = g->bitmap.rows;

        main_atlas.characters[i].left = g->bitmap_left;
        main_atlas.characters[i].top = g->bitmap_top;

        main_atlas.characters[i].x = (float)x / (float)width;

        x += g->bitmap.width + 1;
    }

    PrintToLog("INFO: Created character atlas size %ix%i", width,height);

    FT_Done_Face(face);
    delete[] font_buffer;

    // TODO - perhaps leave this open for loading other fonts and close in a GarbageCollect() function?
    FT_Done_FreeType(ft);
}
예제 #12
0
/**
 * Obtains a hash of the active state to use for consensus verification and checkpointing.
 *
 * For increased flexibility, so other implementations like OmniWallet and OmniChest can
 * also apply this methodology without necessarily using the same exact data types (which
 * would be needed to hash the data bytes directly), create a string in the following
 * format for each entry to use for hashing:
 *
 * ---STAGE 1 - BALANCES---
 * Format specifiers & placeholders:
 *   "%s|%d|%d|%d|%d|%d" - "address|propertyid|balance|selloffer_reserve|accept_reserve|metadex_reserve"
 *
 * Note: empty balance records and the pending tally are ignored. Addresses are sorted based
 * on lexicographical order, and balance records are sorted by the property identifiers.
 *
 * ---STAGE 2 - DEX SELL OFFERS---
 * Format specifiers & placeholders:
 *   "%s|%s|%d|%d|%d|%d|%d" - "txid|address|propertyid|offeramount|btcdesired|minfee|timelimit"
 *
 * Note: ordered ascending by txid.
 *
 * ---STAGE 3 - DEX ACCEPTS---
 * Format specifiers & placeholders:
 *   "%s|%s|%d|%d|%d" - "matchedselloffertxid|buyer|acceptamount|acceptamountremaining|acceptblock"
 *
 * Note: ordered ascending by matchedselloffertxid followed by buyer.
 *
 * ---STAGE 4 - METADEX TRADES---
 * Format specifiers & placeholders:
 *   "%s|%s|%d|%d|%d|%d|%d" - "txid|address|propertyidforsale|amountforsale|propertyiddesired|amountdesired|amountremaining"
 *
 * Note: ordered ascending by txid.
 *
 * ---STAGE 5 - CROWDSALES---
 * Format specifiers & placeholders:
 *   "%d|%d|%d|%d|%d" - "propertyid|propertyiddesired|deadline|usertokens|issuertokens"
 *
 * Note: ordered by property ID.
 *
 * ---STAGE 6 - PROPERTIES---
 * Format specifiers & placeholders:
 *   "%d|%s" - "propertyid|issueraddress"
 *
 * Note: ordered by property ID.
 *
 * The byte order is important, and we assume:
 *   SHA256("abc") = "ad1500f261ff10b49c7a1796a36103b02322ae5dde404141eacf018fbf1678ba"
 *
 */
uint256 GetConsensusHash()
{
    // allocate and init a SHA256_CTX
    SHA256_CTX shaCtx;
    SHA256_Init(&shaCtx);

    LOCK(cs_tally);

    if (msc_debug_consensus_hash) PrintToLog("Beginning generation of current consensus hash...\n");

    // Balances - loop through the tally map, updating the sha context with the data from each balance and tally type
    // Placeholders:  "address|propertyid|balance|selloffer_reserve|accept_reserve|metadex_reserve"
    // Sort alphabetically first
    std::map<std::string, CMPTally> tallyMapSorted;
    for (std::unordered_map<string, CMPTally>::iterator uoit = mp_tally_map.begin(); uoit != mp_tally_map.end(); ++uoit) {
        tallyMapSorted.insert(std::make_pair(uoit->first,uoit->second));
    }
    for (std::map<string, CMPTally>::iterator my_it = tallyMapSorted.begin(); my_it != tallyMapSorted.end(); ++my_it) {
        const std::string& address = my_it->first;
        CMPTally& tally = my_it->second;
        tally.init();
        uint32_t propertyId = 0;
        while (0 != (propertyId = (tally.next()))) {
            std::string dataStr = GenerateConsensusString(tally, address, propertyId);
            if (dataStr.empty()) continue; // skip empty balances
            if (msc_debug_consensus_hash) PrintToLog("Adding balance data to consensus hash: %s\n", dataStr);
            SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
        }
    }

    // DEx sell offers - loop through the DEx and add each sell offer to the consensus hash (ordered by txid)
    // Placeholders: "txid|address|propertyid|offeramount|btcdesired|minfee|timelimit"
    std::vector<std::pair<arith_uint256, std::string> > vecDExOffers;
    for (OfferMap::iterator it = my_offers.begin(); it != my_offers.end(); ++it) {
        const CMPOffer& selloffer = it->second;
        const std::string& sellCombo = it->first;
        std::string seller = sellCombo.substr(0, sellCombo.size() - 2);
        std::string dataStr = GenerateConsensusString(selloffer, seller);
        vecDExOffers.push_back(std::make_pair(arith_uint256(selloffer.getHash().ToString()), dataStr));
    }
    std::sort (vecDExOffers.begin(), vecDExOffers.end());
    for (std::vector<std::pair<arith_uint256, std::string> >::iterator it = vecDExOffers.begin(); it != vecDExOffers.end(); ++it) {
        const std::string& dataStr = it->second;
        if (msc_debug_consensus_hash) PrintToLog("Adding DEx offer data to consensus hash: %s\n", dataStr);
        SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
    }

    // DEx accepts - loop through the accepts map and add each accept to the consensus hash (ordered by matchedtxid then buyer)
    // Placeholders: "matchedselloffertxid|buyer|acceptamount|acceptamountremaining|acceptblock"
    std::vector<std::pair<std::string, std::string> > vecAccepts;
    for (AcceptMap::const_iterator it = my_accepts.begin(); it != my_accepts.end(); ++it) {
        const CMPAccept& accept = it->second;
        const std::string& acceptCombo = it->first;
        std::string buyer = acceptCombo.substr((acceptCombo.find("+") + 1), (acceptCombo.size()-(acceptCombo.find("+") + 1)));
        std::string dataStr = GenerateConsensusString(accept, buyer);
        std::string sortKey = strprintf("%s-%s", accept.getHash().GetHex(), buyer);
        vecAccepts.push_back(std::make_pair(sortKey, dataStr));
    }
    std::sort (vecAccepts.begin(), vecAccepts.end());
    for (std::vector<std::pair<std::string, std::string> >::iterator it = vecAccepts.begin(); it != vecAccepts.end(); ++it) {
        const std::string& dataStr = it->second;
        if (msc_debug_consensus_hash) PrintToLog("Adding DEx accept to consensus hash: %s\n", dataStr);
        SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
    }

    // MetaDEx trades - loop through the MetaDEx maps and add each open trade to the consensus hash (ordered by txid)
    // Placeholders: "txid|address|propertyidforsale|amountforsale|propertyiddesired|amountdesired|amountremaining"
    std::vector<std::pair<arith_uint256, std::string> > vecMetaDExTrades;
    for (md_PropertiesMap::const_iterator my_it = metadex.begin(); my_it != metadex.end(); ++my_it) {
        const md_PricesMap& prices = my_it->second;
        for (md_PricesMap::const_iterator it = prices.begin(); it != prices.end(); ++it) {
            const md_Set& indexes = it->second;
            for (md_Set::const_iterator it = indexes.begin(); it != indexes.end(); ++it) {
                const CMPMetaDEx& obj = *it;
                std::string dataStr = GenerateConsensusString(obj);
                vecMetaDExTrades.push_back(std::make_pair(arith_uint256(obj.getHash().ToString()), dataStr));
            }
        }
    }
    std::sort (vecMetaDExTrades.begin(), vecMetaDExTrades.end());
    for (std::vector<std::pair<arith_uint256, std::string> >::iterator it = vecMetaDExTrades.begin(); it != vecMetaDExTrades.end(); ++it) {
        const std::string& dataStr = it->second;
        if (msc_debug_consensus_hash) PrintToLog("Adding MetaDEx trade data to consensus hash: %s\n", dataStr);
        SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
    }

    // Crowdsales - loop through open crowdsales and add to the consensus hash (ordered by property ID)
    // Note: the variables of the crowdsale (amount, bonus etc) are not part of the crowdsale map and not included here to
    // avoid additionalal loading of SP entries from the database
    // Placeholders: "propertyid|propertyiddesired|deadline|usertokens|issuertokens"
    std::vector<std::pair<uint32_t, std::string> > vecCrowds;
    for (CrowdMap::const_iterator it = my_crowds.begin(); it != my_crowds.end(); ++it) {
        const CMPCrowd& crowd = it->second;
        uint32_t propertyId = crowd.getPropertyId();
        std::string dataStr = GenerateConsensusString(crowd);
        vecCrowds.push_back(std::make_pair(propertyId, dataStr));
    }
    std::sort (vecCrowds.begin(), vecCrowds.end());
    for (std::vector<std::pair<uint32_t, std::string> >::iterator it = vecCrowds.begin(); it != vecCrowds.end(); ++it) {
        std::string dataStr = (*it).second;
        if (msc_debug_consensus_hash) PrintToLog("Adding Crowdsale entry to consensus hash: %s\n", dataStr);
        SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
    }

    // Properties - loop through each property and store the issuer (to capture state changes via change issuer transactions)
    // Note: we are loading every SP from the DB to check the issuer, if using consensus_hash_every_block debug option this
    //       will slow things down dramatically.  Not an issue to do it once every 10,000 blocks for checkpoint verification.
    // Placeholders: "propertyid|issueraddress"
    for (uint8_t ecosystem = 1; ecosystem <= 2; ecosystem++) {
        uint32_t startPropertyId = (ecosystem == 1) ? 1 : TEST_ECO_PROPERTY_1;
        for (uint32_t propertyId = startPropertyId; propertyId < _my_sps->peekNextSPID(ecosystem); propertyId++) {
            CMPSPInfo::Entry sp;
            if (!_my_sps->getSP(propertyId, sp)) {
                PrintToLog("Error loading property ID %d for consensus hashing, hash should not be trusted!\n");
                continue;
            }
            std::string dataStr = GenerateConsensusString(propertyId, sp.issuer);
            if (msc_debug_consensus_hash) PrintToLog("Adding property to consensus hash: %s\n", dataStr);
            SHA256_Update(&shaCtx, dataStr.c_str(), dataStr.length());
        }
    }

    // extract the final result and return the hash
    uint256 consensusHash;
    SHA256_Final((unsigned char*)&consensusHash, &shaCtx);
    if (msc_debug_consensus_hash) PrintToLog("Finished generation of consensus hash.  Result: %s\n", consensusHash.GetHex());

    return consensusHash;
}