예제 #1
0
int DM_Music_PlayFile(const char *filename, int looped)
{
    if(!filename) return false;

    if(!fluid_is_midifile(filename))
    {
        // It doesn't look like MIDI.
        App_Log(DE2_LOG_VERBOSE, "[FluidSynth] Cannot play \"%s\": not a MIDI file", filename);
        return false;
    }

    if(sfontId < 0)
    {
        App_Log(DE2_LOG_VERBOSE, "[FluidSynth] Cannot play \"%s\" without an SF2 soundfont", filename);
        return false;
    }

    // If we are playing something, make sure it's stopped.
    stopPlayer();

    DENG_ASSERT(fsPlayer == NULL);

    // Create a new player.
    fsPlayer = new_fluid_player(DMFluid_Synth());
    fluid_player_add(fsPlayer, filename);
    fluid_player_set_loop(fsPlayer, looped? -1 /*infinite times*/ : 1);
    fluid_player_play(fsPlayer);

    startPlayer();

    DSFLUIDSYNTH_TRACE("PlayFile: playing '" << filename << "' using player "
                       << fsPlayer << " looped:" << looped << " sfont:" << sfontId);
    return true;
}
예제 #2
0
static dd_bool Reader_Check(Reader1 const *reader, size_t len)
{
#ifdef DENG_WRITER_TYPECHECK
    // One byte for the code.
    if (len) len++;
#endif

    DENG_ASSERT(reader);
    DENG_ASSERT(reader->data || reader->useCustomFuncs);

    if (!reader || (!reader->data && !reader->useCustomFuncs))
        return false;

    if (reader->useCustomFuncs)
    {
        // Not our responsibility.
        return true;
    }
    if (reader->pos > reader->size - len)
    {
        App_Log(DE2_LOG_ERROR,
            "Reader_Check: Position %lu[+%lu] out of bounds, size=%lu.",
                (unsigned long) reader->pos,
                (unsigned long) len,
                (unsigned long) reader->size);
        App_FatalError("Reader1 bounds check failed.");
    }
    return true;
}
예제 #3
0
int DS_Init(void)
{
    // Already initialized?
    if(initOk) return true;

    // Open the default playback device.
    device = alcOpenDevice(NULL);
    if(!device)
    {
        App_Log(DE2_AUDIO_ERROR, "OpenAL init failed (using default playback device)");
        return false;
    }

    // Create and make current a new context.
    alcMakeContextCurrent(context = alcCreateContext(device, NULL));
    DSOPENAL_ERRCHECK(alGetError());

    // Attempt to load and configure the EAX extensions.
    loadExtensions();

    // Configure the listener and global OpenAL properties/state.
    alListenerf(AL_GAIN, 1);
    alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
    headYaw = headPitch = 0;
    unitsPerMeter = 36;

    // Everything is OK.
    DSOPENAL_TRACE("DS_Init: OpenAL initialized%s." << (hasEAX? " (EAX 2.0 available)" : ""));
    initOk = true;
    return true;
}
예제 #4
0
void Z_Shutdown(void)
{
    int             numVolumes = 0;
    size_t          totalMemory = 0;

    // Get rid of possible zone-allocated memory in the garbage.
    Garbage_RecycleAllWithDestructor(Z_Free);

    // Destroy all the memory volumes.
    while (volumeRoot)
    {
        memvolume_t *vol = volumeRoot;
        volumeRoot = vol->next;

        // Calculate stats.
        numVolumes++;
        totalMemory += vol->size;

#ifdef LIBDENG_FAKE_MEMORY_ZONE
        Z_FreeTags(0, DDMAXINT);
#endif

        M_Free(vol->zone);
        M_Free(vol);
    }

    App_Log(DE2_LOG_NOTE,
            "Z_Shutdown: Used %i volumes, total %u bytes.", numVolumes, totalMemory);

    Sys_DestroyMutex(zoneMutex);
    zoneMutex = 0;
}
예제 #5
0
void Z_FreeTags(int lowTag, int highTag)
{
    memvolume_t *volume;
    memblock_t *block, *next;

    App_Log(DE2_LOG_DEBUG,
            "MemoryZone: Freeing all blocks in tag range:[%i, %i)",
            lowTag, highTag+1);

    for (volume = volumeRoot; volume; volume = volume->next)
    {
        for (block = volume->zone->blockList.next;
            block != &volume->zone->blockList;
            block = next)
        {
            next = block->next;

            if (block->user) // An allocated block?
            {
                if (block->tag >= lowTag && block->tag <= highTag)
#ifdef LIBDENG_FAKE_MEMORY_ZONE
                    Z_Free(block->area);
#else
                    Z_Free((byte *) block + sizeof(memblock_t));
#endif
            }
        }
    }

    // Now that there's plenty of new free space, let's keep the static
    // rover near the beginning of the volume.
    rewindStaticRovers();
}
예제 #6
0
void Z_PrintStatus(void)
{
    size_t allocated = Z_AllocatedMemory();
    size_t wasted    = Z_FreeMemory();

    App_Log(DE2_LOG_DEBUG,
            "Memory zone status: %u volumes, %u bytes allocated, %u bytes free (%f%% in use)",
            Z_VolumeCount(), (uint)allocated, (uint)wasted, (float)allocated/(float)(allocated+wasted)*100.f);
}
예제 #7
0
/**
 * This function will be called ASAP after Doomsday has completed startup.
 * In this example, the function has been declared static to highlight that
 * it isn't exported from the plugin.
 *
 * @return  This operation should return non-zero if successful.
 */
static int ExampleHook(int hookType, int parm, void *data)
{
    DENG_UNUSED(hookType);
    DENG_UNUSED(parm);
    DENG_UNUSED(data);

    App_Log(DE2_LOG_DEV, "ExampleHook: Hook successful!");
    return true;
}
예제 #8
0
void DMFluid_SetSoundFont(const char* fileName)
{
    if(sfontId >= 0)
    {
        // First unload the previous font.
        fluid_synth_sfunload(DMFluid_Synth(), sfontId, false);
        sfontId = -1;
    }

    if(!fileName) return;

    // Load the new one.
    sfontId = fluid_synth_sfload(DMFluid_Synth(), fileName, true);
    if(sfontId >= 0)
    {
        App_Log(DE2_LOG_VERBOSE, "FluidSynth: Loaded SF2 soundfont \"%s\" with id:%i", fileName, sfontId);
    }
    else
    {
        App_Log(DE2_LOG_VERBOSE, "FluidSynth: Failed to load soundfont \"%s\" (not SF2 or not found)", fileName);
    }
}
예제 #9
0
void P_InitTerrainTypes(void)
{
    struct matttypedef_s {
        const char* materialUri; ///< Percent encoded.
        const char* ttName;
    } defs[] = {
#if __JDOOM__ || __JDOOM64__
        { "Flats:FWATER1",  "Water" },
        { "Flats:LAVA1",    "Lava" },
        { "Flats:BLOOD1",   "Blood" },
        { "Flats:NUKAGE1",  "Nukage" },
        { "Flats:SLIME01",  "Slime" },
#endif
#if __JHERETIC__
        { "Flats:FLTWAWA1", "Water" },
        { "Flats:FLTFLWW1", "Water" },
        { "Flats:FLTLAVA1", "Lava" },
        { "Flats:FLATHUH1", "Lava" },
        { "Flats:FLTSLUD1", "Sludge" },
#endif
#if __JHEXEN__
        { "Flats:X_005",    "Water" },
        { "Flats:X_001",    "Lava" },
        { "Flats:X_009",    "Sludge" },
        { "Flats:F_033",    "Ice" },
#endif
        { 0, 0 }
    };
    uint i;

    if(materialTTypes)
        Z_Free(materialTTypes);
    materialTTypes = 0;
    numMaterialTTypes = maxMaterialTTypes = 0;

    for(i = 0; defs[i].materialUri; ++i)
    {
        Material* mat;
        uint idx = findTerrainTypeNumForName(defs[i].ttName);
        if(!idx) continue;

        mat = P_ToPtr(DMU_MATERIAL, Materials_ResolveUriCString(defs[i].materialUri));
        if(!mat) continue;

        App_Log(DE2_DEV_RES_VERBOSE, "P_InitTerrainTypes: Material \"%s\" linked to terrain type '%s'",
                defs[i].materialUri, defs[i].ttName);

        getMaterialTerrainType(mat, idx);
    }
}
예제 #10
0
/**
 * Create a new memory volume.  The new volume is added to the list of
 * memory volumes.
 */
static memvolume_t *createVolume(size_t volumeSize)
{
    memblock_t     *block;
    memvolume_t    *vol = M_Calloc(sizeof(memvolume_t));

    lockZone();

    // Append to the end of the volume list.
    if (volumeLast)
        volumeLast->next = vol;
    volumeLast = vol;
    vol->next = 0;
    if (!volumeRoot)
        volumeRoot = vol;

    // Allocate memory for the zone volume.
    vol->size = volumeSize;
    vol->zone = M_Malloc(vol->size);
    vol->allocatedBytes = 0;

    // Clear the start of the zone.
    memset(vol->zone, 0, sizeof(memzone_t) + sizeof(memblock_t));
    vol->zone->size = vol->size;

    // Set the entire zone to one free block.
    vol->zone->blockList.next
        = vol->zone->blockList.prev
        = block
        = (memblock_t *) ((byte *) vol->zone + sizeof(memzone_t));

    vol->zone->blockList.user = (void *) vol->zone;
    vol->zone->blockList.volume = vol;
    vol->zone->blockList.tag = PU_APPSTATIC;
    vol->zone->rover = vol->zone->staticRover = block;

    block->prev = block->next = &vol->zone->blockList;
    block->user = NULL;         // free block
    block->seqFirst = block->seqLast = NULL;
    block->size = vol->zone->size - sizeof(memzone_t);

    unlockZone();

    App_Log(DE2_LOG_MESSAGE,
            "Created a new %.1f MB memory volume.", vol->size / 1024.0 / 1024.0);

    Z_CheckHeap();

    return vol;
}
예제 #11
0
void Z_ChangeTag2(void *ptr, int tag)
{
    lockZone();
    {
        memblock_t *block = Z_GetBlock(ptr);

        DENG_ASSERT(block->id == LIBDENG_ZONEID);

        if (tag >= PU_PURGELEVEL && PTR2INT(block->user) < 0x100)
        {
            App_Log(DE2_LOG_ERROR,
                "Z_ChangeTag: An owner is required for purgable blocks.");
        }
        else
        {
            block->tag = tag;
        }
    }
    unlockZone();
}
예제 #12
0
    EventSequence(const char* _sequence, ISequenceCompleteHandler& _handler)
        : sequence(), handler(_handler), pos(0), numArgs(0), args(0)
    {
        int len = strlen(_sequence);

        if(strchr(_sequence, '%'))
        {
            // Count and validate arguments defined within the sequence.
            const char* ch = _sequence;
            while(ch + 1 < _sequence + len)
            {
                if(ch[0] == '%' && ch[1] && ch[1] != '%')
                {
                    int arg = ch[1] - '0';
                    if(arg < 1 || arg > 9)
                    {
                        App_Log(DE2_DEV_WARNING, "EventSequence: Sequence %s truncated due to bad suffix %c",
                                _sequence, ch[1]);
                        len = ch - _sequence;
                        break;
                    }
                    numArgs += 1;
                    ch += 2;
                }
                else
                {
                    ch++;
                }
            }
        }

        Str_PartAppend(Str_Init(&sequence), _sequence, 0, len);
        if(numArgs)
        {
            args = new EventSequenceArg[numArgs];
            for(int i = 0; i < numArgs; ++i)
            {
                args[i] = 0;
            }
        }
    }
예제 #13
0
void P_TouchSpecialMobj(mobj_t* special, mobj_t* toucher)
{
    player_t* player;
    coord_t delta;
    itemtype_t item;

    delta = special->origin[VZ] - toucher->origin[VZ];
    if(delta > toucher->height || delta < -8)
    {   // Out of reach.
        return;
    }

    // Dead thing touching (can happen with a sliding player corpse).
    if(toucher->health <= 0)
        return;

    player = toucher->player;

    // Identify by sprite.
    if((item = getItemTypeBySprite(special->sprite)) != IT_NONE)
    {
        if(!pickupItem(player, item, (special->flags & MF_DROPPED)? true : false))
            return; // Don't destroy the item.
    }
    else
    {
        App_Log(DE2_MAP_WARNING, "P_TouchSpecialMobj: Unknown gettable thing %i",
                (int) special->type);
    }

    if(special->flags & MF_COUNTITEM)
    {
        player->itemCount++;
        player->update |= PSF_COUNTERS;
    }

    P_MobjRemove(special, false);

    if(!mapSetup)
        player->bonusCount += BONUSADD;
}
예제 #14
0
/**
 * Starts bringing the pending weapon up from the bottom of the screen.
 */
void P_BringUpWeapon(player_t* player)
{
    weapontype_t const oldPendingWeapon = player->pendingWeapon;

    weaponmodeinfo_t* wminfo = NULL;
    weapontype_t raiseWeapon;

    if(!player) return;

    if(player->plr->flags & DDPF_UNDEFINED_WEAPON)
    {
        // We'll do this when the server informs us about the client's current weapon.
        return;
    }

    raiseWeapon = player->pendingWeapon;
    if(raiseWeapon == WT_NOCHANGE)
        raiseWeapon = player->readyWeapon;

    player->pendingWeapon = WT_NOCHANGE;
    player->pSprites[ps_weapon].pos[VY] = WEAPONBOTTOM;

    if(!VALID_WEAPONTYPE(raiseWeapon))
    {
        return;
    }

    wminfo = WEAPON_INFO(raiseWeapon, player->class_, 0);

    App_Log(DE2_MAP_XVERBOSE, "P_BringUpWeapon: Player %i, pending weapon was %i, weapon pspr to %i",
            (int)(player - players), oldPendingWeapon, wminfo->states[WSN_UP]);

    if(wminfo->raiseSound)
        S_StartSoundEx(wminfo->raiseSound, player->plr->mo);

    P_SetPsprite(player, ps_weapon, wminfo->states[WSN_UP]);
}
예제 #15
0
/**
 * Allocate a new block of memory to be used for linear object allocations.
 * A "zblock" (its from the zone).
 *
 * @param set   Block set into which the new block is added.
 */
static void addBlockToSet(zblockset_t *set)
{
    assert(set);
    {
    zblockset_block_t *block = 0;

    // Get a new block by resizing the blocks array. This is done relatively
    // seldom, since there is a large number of elements per each block.
    set->_blockCount++;
    set->_blocks = Z_Recalloc(set->_blocks, sizeof(zblockset_block_t) * set->_blockCount, set->_tag);

    App_Log(DE2_LOG_DEBUG,
            "addBlockToSet: set=%p blockCount=%u elemSize=%u elemCount=%u (total=%u)",
             set, set->_blockCount, (uint)set->_elementSize, set->_elementsPerBlock,
            (uint)(set->_blockCount * set->_elementSize * set->_elementsPerBlock));

    // Initialize the block's data.
    block = &set->_blocks[set->_blockCount - 1];
    block->max = set->_elementsPerBlock;
    block->elementSize = set->_elementSize;
    block->elements = Z_Malloc(block->elementSize * block->max, set->_tag, NULL);
    block->count = 0;
    }
}
예제 #16
0
void SV_LoadGameClient(uint /*sessionId*/)
{
    throw de::Error("SV_LoadGameClient", "Not currently implemented");

#if 0
#if !__JHEXEN__ // unsupported in libhexen
    player_t *cpl = players + CONSOLEPLAYER;
    mobj_t *mo    = cpl->plr->mo;

    if(!IS_CLIENT || !mo)
        return;

    de::game::GameStateFolder *session = new de::game::GameStateFolder(saveNameForClientSessionId(sessionId));
    de::game::GameStateMetadata *metadata = new de::game::GameStateMetadata;
    //G_ReadLegacySessionMetadata(metadata, reader);
    metadata->set("sessionId", sessionId);
    session->replaceMetadata(metadata);

    de::Path path = de::String("/savegame") / "client" / session->path();
    if(!SV_OpenFileForRead(path))
    {
        delete session;
        App_Log(DE2_RES_WARNING, "SV_LoadGameClient: Failed opening \"%s\" for reading",
                path.toString().toLatin1().constData());
        return;
    }

    if((*metadata)["magic"].value().asNumber() != MY_CLIENT_SAVE_MAGIC)
    {
        SV_CloseFile();
        delete session;
        App_Log(DE2_RES_ERROR, "Client save file format not recognized");
        return;
    }

    Reader1 *reader = SV_NewReader();

    int const saveVersion = (*metadata)["version"].value().asNumber();
    Uri *mapUri           = Uri_NewWithPath2((*metadata)["mapUri"].value().asText().toUtf8().constData(), RC_NULL);
    GameRules *rules    = 0;
    if(metadata->hasSubrecord("gameRules"))
    {
        rules = GameRules::fromRecord(metadata->subrecord("gameRules"));
    }

    // Do we need to change the map?
    if(gfw_Session()->mapUri() != *reinterpret_cast<de::Uri *>(mapUri))
    {
        gfw_Session()->begin(*mapUri, 0/*default*/, *rules);
    }
    else if(rules)
    {
        gfw_Session()->rules() = *rules;
    }

    delete rules; rules = 0;
    Uri_Delete(mapUri); mapUri = 0;

    mapTime = (*metadata)["mapTime"].value().asNumber();

    P_MobjUnlink(mo);
    mo->origin[VX] = FIX2FLT(Reader_ReadInt32(reader));
    mo->origin[VY] = FIX2FLT(Reader_ReadInt32(reader));
    mo->origin[VZ] = FIX2FLT(Reader_ReadInt32(reader));
    P_MobjLink(mo);
    mo->floorZ     = FIX2FLT(Reader_ReadInt32(reader));
    mo->ceilingZ   = FIX2FLT(Reader_ReadInt32(reader));
    mo->angle      = Reader_ReadInt32(reader); /* $unifiedangles */

    cpl->plr->lookDir = Reader_ReadFloat(reader); /* $unifiedangles */

#if __JHEXEN__
    if(saveVersion >= 4)
#else
    if(saveVersion >= 5)
#endif
    {
        SV_AssertSegment(ASEG_PLAYER_HEADER);
    }
    playerheader_t plrHdr;
    plrHdr.read(reader, saveVersion);

    cpl->read(reader, plrHdr);

    MapStateReader(*session).read(Str_Text(Uri_Resolved(mapUri)));

    SV_CloseFile();
    Reader_Delete(reader);
    delete session;
#else
    DENG2_UNUSED(sessionId);
#endif
#endif
}
예제 #17
0
void SV_SaveGameClient(uint /*sessionId*/)
{
    throw de::Error("SV_SaveGameClient", "Not currently implemented");

#if 0
#if !__JHEXEN__ // unsupported in libhexen
    player_t *pl = &players[CONSOLEPLAYER];
    mobj_t *mo   = pl->plr->mo;

    if(!IS_CLIENT || !mo)
        return;

    // Prepare new saved game session session.
    de::game::GameStateFolder *session = new de::game::GameStateFolder(saveNameForClientSessionId(sessionId));
    de::game::GameStateMetadata *metadata = G_CurrentSessionMetadata();
    metadata->set("sessionId", sessionId);
    session->replaceMetadata(metadata);

    de::Path path = de::String("/savegame") / "client" / session->path();
    if(!SV_OpenFileForWrite(path))
    {
        App_Log(DE2_RES_WARNING, "SV_SaveGameClient: Failed opening \"%s\" for writing",
                path.toString().toLatin1().constData());

        // Discard the useless session.
        delete session;
        return;
    }

    Writer1 *writer = SV_NewWriter();
    SV_WriteSessionMetadata(*metadata, writer);

    // Some important information.
    // Our position and look angles.
    Writer_WriteInt32(writer, FLT2FIX(mo->origin[VX]));
    Writer_WriteInt32(writer, FLT2FIX(mo->origin[VY]));
    Writer_WriteInt32(writer, FLT2FIX(mo->origin[VZ]));
    Writer_WriteInt32(writer, FLT2FIX(mo->floorZ));
    Writer_WriteInt32(writer, FLT2FIX(mo->ceilingZ));
    Writer_WriteInt32(writer, mo->angle); /* $unifiedangles */

    Writer_WriteFloat(writer, pl->plr->lookDir); /* $unifiedangles */

    SV_BeginSegment(ASEG_PLAYER_HEADER);
    playerheader_t plrHdr;
    plrHdr.write(writer);

    players[CONSOLEPLAYER].write(writer, plrHdr);

    ThingArchive thingArchive;
    MapStateWriter(thingArchive).write(writer);
    /// @todo No consistency bytes in client saves?

    SV_CloseFile();
    Writer_Delete(writer);
    delete session;
#else
    DENG2_UNUSED(sessionId);
#endif
#endif
}
예제 #18
0
void Z_CheckHeap(void)
{
    memvolume_t *volume;
    memblock_t *block;
    dd_bool     isDone;

    App_Log(DE2_LOG_TRACE, "Z_CheckHeap");

    lockZone();

    for (volume = volumeRoot; volume; volume = volume->next)
    {
        size_t total = 0;

        // Validate the counter.
        if (allocatedMemoryInVolume(volume) != volume->allocatedBytes)
        {
            App_Log(DE2_LOG_CRITICAL,
                "Z_CheckHeap: allocated bytes counter is off (counter:%u != actual:%u)",
                volume->allocatedBytes, allocatedMemoryInVolume(volume));
            App_FatalError("Z_CheckHeap: zone book-keeping is wrong");
        }

        // Does the memory in the blocks sum up to the total volume size?
        for (block = volume->zone->blockList.next;
            block != &volume->zone->blockList; block = block->next)
        {
            total += block->size;
        }
        if (total != volume->size - sizeof(memzone_t))
        {
            App_Log(DE2_LOG_CRITICAL,
                    "Z_CheckHeap: invalid total size of blocks (%u != %u)",
                    total, volume->size - sizeof(memzone_t));
            App_FatalError("Z_CheckHeap: zone book-keeping is wrong");
        }

        // Does the last block extend all the way to the end?
        block = volume->zone->blockList.prev;
        if ((byte *)block - ((byte *)volume->zone + sizeof(memzone_t)) + block->size != volume->size - sizeof(memzone_t))
        {
            App_Log(DE2_LOG_CRITICAL,
                    "Z_CheckHeap: last block does not cover the end (%u != %u)",
                     (byte *)block - ((byte *)volume->zone + sizeof(memzone_t)) + block->size,
                     volume->size - sizeof(memzone_t));
            App_FatalError("Z_CheckHeap: zone is corrupted");
        }

        block = volume->zone->blockList.next;
        isDone = false;

        while (!isDone)
        {
            if (block->next != &volume->zone->blockList)
            {
                if (block->size == 0)
                    App_FatalError("Z_CheckHeap: zero-size block");
                if ((byte *) block + block->size != (byte *) block->next)
                    App_FatalError("Z_CheckHeap: block size does not touch the "
                              "next block");
                if (block->next->prev != block)
                    App_FatalError("Z_CheckHeap: next block doesn't have proper "
                              "back link");
                if (!block->user && !block->next->user)
                    App_FatalError("Z_CheckHeap: two consecutive free blocks");
                if (block->user == (void **) -1)
                {
                    DENG_ASSERT(block->user != (void **) -1);
                    App_FatalError("Z_CheckHeap: bad user pointer");
                }

                /*
                if (block->seqFirst == block)
                {
                    // This is the first.
                    printf("sequence begins at (%p): start=%p, end=%p\n", block,
                           block->seqFirst, block->seqLast);
                }
                 */
                if (block->seqFirst)
                {
                    //printf("  seq member (%p): start=%p\n", block, block->seqFirst);
                    if (block->seqFirst->seqLast == block)
                    {
                        //printf("  -=- last member of seq %p -=-\n", block->seqFirst);
                    }
                    else
                    {
                        if (block->next->seqFirst != block->seqFirst)
                        {
                            App_FatalError("Z_CheckHeap: disconnected sequence");
                        }
                    }
                }

                block = block->next;
            }
            else
                isDone = true; // all blocks have been hit
        }
    }

    unlockZone();
}
예제 #19
0
void *Z_Malloc(size_t size, int tag, void *user)
{
    memblock_t *start, *iter;
    memvolume_t *volume;

    if (tag < PU_APPSTATIC || tag > PU_PURGELEVEL)
    {
        App_Log(DE2_LOG_WARNING, "Z_Malloc: Invalid purgelevel %i, cannot allocate memory.", tag);
        return NULL;
    }
    if (!size)
    {
        // You can't allocate "nothing."
        return NULL;
    }

    lockZone();

    // Align to pointer size.
    size = ALIGNED(size);

    // Account for size of block header.
    size += sizeof(memblock_t);

    // Iterate through memory volumes until we can find one with enough free
    // memory. (Note: we *will *find one that's large enough.)
    for (volume = volumeRoot; ; volume = volume->next)
    {
        uint numChecked = 0;
        dd_bool gotoNextVolume = false;

        if (volume == NULL)
        {
            // We've run out of volumes.  Let's allocate a new one
            // with enough memory.
            size_t newVolumeSize = MEMORY_VOLUME_SIZE;

            if (newVolumeSize < size + 0x1000)
                newVolumeSize = size + 0x1000; // with some spare memory

            volume = createVolume(newVolumeSize);
        }

        if (isVolumeTooFull(volume))
        {
            // We should skip this one.
            continue;
        }

        DENG_ASSERT(volume->zone);

        // Scan through the block list looking for the first free block of
        // sufficient size, throwing out any purgable blocks along the
        // way.

        if (tag == PU_APPSTATIC || tag == PU_GAMESTATIC)
        {
            // Appstatic allocations may be around for a long time so make sure
            // they don't litter the volume. Their own rover will keep them as
            // tightly packed as possible.
            iter = volume->zone->staticRover;
        }
        else
        {
            // Everything else is allocated using the rover.
            iter = volume->zone->rover;
        }
        assert(iter->prev);

        // Back up a little to see if we have some space available nearby.
        start = iter = rewindRover(volume, iter, 3, size);
        numChecked = 0;

        // If the start is in a sequence, move it to the beginning of the
        // entire sequence. Sequences are handled as a single unpurgable entity,
        // so we can stop checking at its start.
        if (start->seqFirst)
        {
            start = start->seqFirst;
        }

        // We will scan ahead until we find something big enough.
        for ( ; !(isFreeBlock(iter) && iter->size >= size); numChecked++)
        {
            // Check for purgable blocks we can dispose of.
            if (!isFreeBlock(iter))
            {
                if (iter->tag >= PU_PURGELEVEL)
                {
                    memblock_t *old = iter;
                    iter = iter->prev; // Step back.
#ifdef LIBDENG_FAKE_MEMORY_ZONE
                    freeBlock(old->area, &start);
#else
                    freeBlock((byte *) old + sizeof(memblock_t), &start);
#endif
                }
                else
                {
                    if (iter->seqFirst)
                    {
                        // This block is part of a sequence of blocks, none of
                        // which can be purged. Skip the entire sequence.
                        iter = iter->seqFirst->seqLast;
                    }
                }
            }

            // Move to the next block.
            iter = advanceBlock(volume, iter);

            // Ensure that iter will eventually touch start.
            assert(!start->seqFirst || start->seqFirst == start ||
                   !start->seqFirst->prev->seqFirst ||
                   start->seqFirst->prev->seqFirst == start->seqFirst->prev->seqLast);

            if (iter == start && numChecked > 0)
            {
                // Scanned all the way through, no suitable space found.
                gotoNextVolume = true;
                App_Log(DE2_LOG_DEBUG,
                        "Z_Malloc: gave up on volume after %i checks", numChecked);
                break;
            }
        }

        // At this point we've found/created a big enough block or we are
        // skipping this volume entirely.

        if (gotoNextVolume) continue;

        // Found a block big enough.
        if (iter->size - size > MINFRAGMENT)
        {
            splitFreeBlock(iter, size);
        }

#ifdef LIBDENG_FAKE_MEMORY_ZONE
        iter->areaSize = size - sizeof(memblock_t);
        iter->area = M_Malloc(iter->areaSize);
#endif

        if (user)
        {
            iter->user = user;      // mark as an in use block
#ifdef LIBDENG_FAKE_MEMORY_ZONE
            *(void **) user = iter->area;
#else
            *(void **) user = (void *) ((byte *) iter + sizeof(memblock_t));
#endif
        }
        else
        {
            // An owner is required for purgable blocks.
            DENG_ASSERT(tag < PU_PURGELEVEL);

            iter->user = MEMBLOCK_USER_ANONYMOUS; // mark as in use, but unowned
        }
        iter->tag = tag;

        if (tag == PU_MAPSTATIC)
        {
            // Level-statics are linked into unpurgable sequences so they can
            // be skipped en masse.
            iter->seqFirst = iter;
            iter->seqLast = iter;
            if (iter->prev->seqFirst)
            {
                iter->seqFirst = iter->prev->seqFirst;
                iter->seqFirst->seqLast = iter;
            }
        }
        else
        {
            // Not part of a sequence.
            iter->seqLast = iter->seqFirst = NULL;
        }

        // Next allocation will start looking here, at the rover.
        if (tag == PU_APPSTATIC || tag == PU_GAMESTATIC)
        {
            volume->zone->staticRover = advanceBlock(volume, iter);
        }
        else
        {
            volume->zone->rover = advanceBlock(volume, iter);
        }

        // Keep tabs on how much memory is used.
        volume->allocatedBytes += iter->size;

        iter->volume = volume;
        iter->id = LIBDENG_ZONEID;

        unlockZone();

#ifdef LIBDENG_FAKE_MEMORY_ZONE
        return iter->area;
#else
        return (void *) ((byte *) iter + sizeof(memblock_t));
#endif
    }
}
예제 #20
0
/**
 * Frees a block of memory allocated with Z_Malloc.
 *
 * @param ptr      Memory area to free.
 * @param tracked  Pointer to a tracked memory block. Will be updated
 *                 if affected by the operation.
 */
static void freeBlock(void *ptr, memblock_t **tracked)
{
    memblock_t     *block, *other;
    memvolume_t    *volume;

    if (!ptr) return;

    lockZone();

    block = Z_GetBlock(ptr);
    if (block->id != LIBDENG_ZONEID)
    {
        unlockZone();
        DENG_ASSERT(block->id == LIBDENG_ZONEID);
        App_Log(DE2_LOG_WARNING,
                "Attempted to free pointer without ZONEID.");
        return;
    }

    // The block was allocated from this volume.
    volume = block->volume;

    if (block->user > (void **) 0x100) // Smaller values are not pointers.
        *block->user = 0; // Clear the user's mark.
    block->user = NULL; // Mark as free.
    block->tag = 0;
    block->volume = NULL;
    block->id = 0;

#ifdef LIBDENG_FAKE_MEMORY_ZONE
    M_Free(block->area);
    block->area = NULL;
    block->areaSize = 0;
#endif

    /**
     * Erase the entire sequence, if there is one.
     * It would also be possible to carefully break the sequence in two
     * parts, but since PU_LEVELSTATICs aren't supposed to be freed one by
     * one, this this sufficient.
     */
    if (block->seqFirst)
    {
        memblock_t *first = block->seqFirst;
        memblock_t *iter = first;
        while (iter->seqFirst == first)
        {
            iter->seqFirst = iter->seqLast = NULL;
            iter = iter->next;
        }
    }

    // Keep tabs on how much memory is used.
    volume->allocatedBytes -= block->size;

    other = block->prev;
    if (!other->user)
    {
        // Merge with previous free block.
        other->size += block->size;
        other->next = block->next;
        other->next->prev = other;
        if (block == volume->zone->rover)
            volume->zone->rover = other;
        if (block == volume->zone->staticRover)
            volume->zone->staticRover = other;
        block = other;

        // Keep track of what happens to the referenced block.
        if (tracked && *tracked == block)
        {
            *tracked = other;
        }
    }

    other = block->next;
    if (!other->user)
    {
        // Merge the next free block onto the end.
        block->size += other->size;
        block->next = other->next;
        block->next->prev = block;
        if (other == volume->zone->rover)
            volume->zone->rover = block;
        if (other == volume->zone->staticRover)
            volume->zone->staticRover = block;

        // Keep track of what happens to the referenced block.
        if (tracked && *tracked == other)
        {
            *tracked = block;
        }
    }

    unlockZone();
}