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; }
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; }
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; }
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; }
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(); }
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); }
/** * 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; }
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); } }
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); } }
/** * 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; }
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(); }
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; } } }
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; }
/** * 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]); }
/** * 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; } }
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 }
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 }
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(); }
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 } }
/** * 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(); }