/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program 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 3 of the License, or * (at your option) any later version. * * This program 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 this program. If not, see . * */ #include "lastexpress/memory.h" #include "lastexpress/lastexpress.h" #include "common/memstream.h" namespace LastExpress { MemoryManager::MemoryManager(LastExpressEngine *engine) { _engine = engine; } void MemoryManager::initMem() { // Memory structure of _globalMemoryPool // (total: 1800 pages = 3,686,400 bytes = 3.51 MB): // // +-------------------------------------------+ // | Sound Memory Pool (pages 0-269) | // | 270 pages = 552,960 bytes = 540 KB | // | [0x0-0x86FFF] | // +-------------------------------------------+ // | Background Buffer (pages 270-569) | // | 300 pages = 614,400 bytes = 600 KB | // | [0x87000-0x11CFFF] | // +-------------------------------------------+ // | Generic Mem Pages Pool (pages 570-1799) | // | 1230 pages = 2,519,040 bytes = 2.4 MB | // | [0x11D000-0x389FFF] | // | Used for dynamic allocations of sequences | // | via allocMem | // +-------------------------------------------+ // // Each page weighs MEM_PAGE_SIZE bytes (0x800 = 2048 bytes = 2 KB). _engine->_globalMemoryPool = (byte *)malloc(1800 * MEM_PAGE_SIZE); memset(_engine->_globalMemoryPool, 0, 1800 * MEM_PAGE_SIZE); _engine->getGraphicsManager()->_backBuffer = (PixMap *)malloc(640 * 480 * sizeof(PixMap)); _engine->getLogicManager()->_trainData = (Node *)malloc(2048 * sizeof(Node)); _engine->_cursorsMemoryPool = (byte *)malloc(49 * MEM_PAGE_SIZE); _engine->_characters = new Characters(); _engine->getSubtitleManager()->_font = new FontData(); _engine->getSubtitleManager()->_subtitlesData = (uint16 *)malloc(0x4400 * sizeof(uint16)); _engine->getGraphicsManager()->_backgroundCompBuffer = (byte *)malloc(8 * MEM_PAGE_SIZE); _engine->getLogicManager()->_items = (Item *)malloc(32 * sizeof(Item)); _engine->getLogicManager()->_doors = (Door *)malloc(128 * sizeof(Door)); _engine->getLogicManager()->_blockedViews = (int32 *)malloc(1000 * sizeof(int32)); _engine->getLogicManager()->_blockedX = (int32 *)malloc(16 * sizeof(int32)); _engine->getLogicManager()->_softBlockedX = (int32 *)malloc(16 * sizeof(int32)); _engine->getLogicManager()->_globals = (int32 *)malloc(128 * sizeof(int32)); _engine->getLogicManager()->_doneNIS = (byte *)malloc(512); _engine->getMessageManager()->_autoMessages = (Message *)malloc(128 * sizeof(Message)); for (int i = 0; i < 16; i++) { _engine->getLogicManager()->_blockedX[i] = 0; _engine->getLogicManager()->_softBlockedX[i] = 0; } for (int i = 0; i < 32; i++) { _engine->getLogicManager()->_items[i].clear(); } for (int i = 0; i < 128; i++) { _engine->getLogicManager()->_doors[i].clear(); _engine->getMessageManager()->_autoMessages[i].clear(); } memset(_engine->getLogicManager()->_globals, 0, 128 * sizeof(int32)); memset(_engine->getLogicManager()->_doneNIS, 0, 512); _engine->getGraphicsManager()->_cursorsDataHeader = (CursorHeader *)_engine->_cursorsMemoryPool; _engine->getGraphicsManager()->_iconsBitmapData = (PixMap *)(_engine->_cursorsMemoryPool + sizeof(CursorHeader) * kCursorMAX); _engine->_soundMemoryPool = _engine->_globalMemoryPool; _engine->getGraphicsManager()->_frontBuffer = (PixMap *)(_engine->_globalMemoryPool + (270 * MEM_PAGE_SIZE)); _memoryPages[0].memPageSize = 1230 * MEM_PAGE_SIZE; _memoryPages[0].memPagePtr = _engine->_globalMemoryPool + (570 * MEM_PAGE_SIZE); _memoryPages[0].allocatedFlag = 0; _nisSeqMemFlag = kMemoryFlagSeqFree | kMemoryFlagInit; _nisSeqMemAvailForLocking = 1230 * MEM_PAGE_SIZE; } void *MemoryManager::allocMem(uint32 size, const char *name, int character) { void *memPagePtr = nullptr; uint32 sizeToAlloc = (size + 15) & 0xFFFFFFF0; for (int i = 0; !memPagePtr && i < _numAllocatedMemPages; i++) { if (!_memoryPages[i].allocatedFlag && _memoryPages[i].memPageSize >= sizeToAlloc) { if (_memoryPages[i].memPageSize > sizeToAlloc && _numAllocatedMemPages < 127) { memmove(&_memoryPages[i + 1], &_memoryPages[i], sizeof(MemPage) * (_numAllocatedMemPages - i)); _numAllocatedMemPages++; _memoryPages[i].memPageSize = sizeToAlloc; _memoryPages[i + 1].memPageSize -= sizeToAlloc; _memoryPages[i].memPagePtr = (byte *)_memoryPages[i].memPagePtr + _memoryPages[i + 1].memPageSize; } _memoryPages[i].character = character; _memoryPages[i].allocatedFlag = true; Common::strcpy_s(_memoryPages[i].pageName, name); memPagePtr = _memoryPages[i].memPagePtr; } } return memPagePtr; } void MemoryManager::freeMem(void *data) { for (int i = 0; i < _numAllocatedMemPages; i++) { if (_memoryPages[i].memPagePtr == data) { _memoryPages[i].allocatedFlag = false; _memoryPages[i].character = 0; Common::strcpy_s(_memoryPages[i].pageName, "FREE MEM"); if (i > 0 && !_memoryPages[i - 1].allocatedFlag) { i--; _memoryPages[i].memPageSize += _memoryPages[i + 1].memPageSize; _memoryPages[i].memPagePtr = _memoryPages[i + 1].memPagePtr; _numAllocatedMemPages--; memmove(&_memoryPages[i + 1], &_memoryPages[i + 2], sizeof(MemPage) * (_numAllocatedMemPages - i - 1)); } if (i < _numAllocatedMemPages - 1 && !_memoryPages[i + 1].allocatedFlag) { _memoryPages[i].memPageSize += _memoryPages[i + 1].memPageSize; _memoryPages[i].memPagePtr = _memoryPages[i + 1].memPagePtr; _numAllocatedMemPages--; memmove(&_memoryPages[i + 1], &_memoryPages[i + 2], sizeof(MemPage) * (_numAllocatedMemPages - i - 1)); } break; } } } void MemoryManager::releaseMemory() { SAFE_FREE(_engine->_globalMemoryPool); SAFE_FREE(_engine->getGraphicsManager()->_backBuffer); for (int i = 0; i < _engine->getLogicManager()->_numberOfScenes; i++) { if (_engine->getLogicManager()->_trainData[i].link) { SAFE_DELETE(_engine->getLogicManager()->_trainData[i].link); } } SAFE_FREE(_engine->getLogicManager()->_trainData); SAFE_FREE(_engine->_cursorsMemoryPool); SAFE_DELETE(_engine->_characters); SAFE_DELETE(_engine->getSubtitleManager()->_font); SAFE_FREE(_engine->getSubtitleManager()->_subtitlesData); SAFE_FREE(_engine->getGraphicsManager()->_backgroundCompBuffer); SAFE_FREE(_engine->getLogicManager()->_items); SAFE_FREE(_engine->getLogicManager()->_doors); SAFE_FREE(_engine->getLogicManager()->_blockedViews); SAFE_FREE(_engine->getLogicManager()->_blockedX); SAFE_FREE(_engine->getLogicManager()->_softBlockedX); SAFE_FREE(_engine->getLogicManager()->_globals); SAFE_FREE(_engine->getLogicManager()->_doneNIS); } void MemoryManager::freeFX() { if ((_nisSeqMemFlag & kMemoryFlagFXFree) == 0) { _nisSeqMemFlag |= kMemoryFlagFXFree; _memoryPages[_numAllocatedMemPages - 1].memPageSize += (300 * MEM_PAGE_SIZE); _memoryPages[_numAllocatedMemPages - 1].memPagePtr = (byte *)_memoryPages[_numAllocatedMemPages - 1].memPagePtr - (300 * MEM_PAGE_SIZE); _nisSeqMemAvailForLocking += 614400; } } void MemoryManager::lockFX() { if ((_nisSeqMemFlag & kMemoryFlagFXFree) != 0) { _nisSeqMemFlag &= ~kMemoryFlagFXFree; _memoryPages[_numAllocatedMemPages - 1].memPageSize -= (300 * MEM_PAGE_SIZE); _memoryPages[_numAllocatedMemPages - 1].memPagePtr = (byte *)_memoryPages[_numAllocatedMemPages - 1].memPagePtr + (300 * MEM_PAGE_SIZE); _nisSeqMemAvailForLocking -= (300 * MEM_PAGE_SIZE); } } void MemoryManager::lockSeqMem(uint32 size) { _engine->getOtisManager()->wipeLooseSprites(); _nisSeqMemFlag &= ~kMemoryFlagSeqFree; uint32 curSize = 0; int idx = 1; while (curSize < size) { int invIdx = _numAllocatedMemPages - idx; if (_memoryPages[_numAllocatedMemPages - idx].allocatedFlag) { if (_memoryPages[invIdx].character) { void *memPagePtr = _memoryPages[invIdx].memPagePtr; _engine->getOtisManager()->wipeGSysInfo(_memoryPages[invIdx].character); for (int i = 0; i < _numAllocatedMemPages; ++i) { if (_memoryPages[i].memPagePtr == memPagePtr) freeMem(memPagePtr); } } else { freeMem(_memoryPages[invIdx].memPagePtr); } curSize = 0; idx = 1; } else { curSize += _memoryPages[invIdx].memPageSize; idx++; } } } void MemoryManager::freeSeqMem() { _nisSeqMemFlag |= kMemoryFlagSeqFree; } Seq *MemoryManager::copySeq(Seq *sequenceToCopy) { MemPage tmpPage; int oldSeqMemPageIdx = -1; for (int i = 0; oldSeqMemPageIdx == -1 && i < _numAllocatedMemPages; ++i) { if (_memoryPages[i].memPagePtr == sequenceToCopy->rawSeqData) oldSeqMemPageIdx = i; } if (oldSeqMemPageIdx == -1) return nullptr; tmpPage.copyFrom(_memoryPages[oldSeqMemPageIdx]); byte *newSeqDataRaw = (byte *)allocMem(tmpPage.memPageSize, tmpPage.pageName, tmpPage.character); if (!newSeqDataRaw) return nullptr; memcpy(newSeqDataRaw, tmpPage.memPagePtr, tmpPage.memPageSize); Seq *newSeq = new Seq(); newSeq->rawSeqData = newSeqDataRaw; Common::SeekableReadStream *newSeqDataStream = new Common::MemoryReadStream(newSeqDataRaw, tmpPage.memPageSize, DisposeAfterUse::NO); newSeq->numFrames = newSeqDataStream->readUint32LE(); newSeqDataStream->readUint32LE(); // Empty sprite pointer newSeq->sprites = new Sprite[newSeq->numFrames]; uint32 paletteOffset = 0; for (int frame = 0; frame < newSeq->numFrames; frame++) { uint32 compDataOffset = newSeqDataStream->readUint32LE(); if (compDataOffset) newSeq->sprites[frame].compData = &newSeqDataRaw[compDataOffset]; uint32 eraseDataOffset = newSeqDataStream->readUint32LE(); if (eraseDataOffset) newSeq->sprites[frame].eraseMask = &newSeqDataRaw[eraseDataOffset]; paletteOffset = newSeqDataStream->readUint32LE(); if (paletteOffset) newSeq->sprites[frame].colorPalette = (uint16 *)&newSeqDataRaw[paletteOffset]; newSeq->sprites[frame].rect.left = newSeqDataStream->readUint32LE(); newSeq->sprites[frame].rect.top = newSeqDataStream->readUint32LE(); newSeq->sprites[frame].rect.right = newSeqDataStream->readUint32LE(); newSeq->sprites[frame].rect.bottom = newSeqDataStream->readUint32LE(); newSeq->sprites[frame].rect.width = newSeqDataStream->readUint32LE(); newSeq->sprites[frame].rect.height = newSeqDataStream->readUint32LE(); newSeq->sprites[frame].hotspotX1 = newSeqDataStream->readUint16LE(); newSeq->sprites[frame].hotspotX2 = newSeqDataStream->readUint16LE(); newSeq->sprites[frame].hotspotY1 = newSeqDataStream->readUint16LE(); newSeq->sprites[frame].hotspotY2 = newSeqDataStream->readUint16LE(); newSeq->sprites[frame].compBits = newSeqDataStream->readByte(); newSeq->sprites[frame].compType = newSeqDataStream->readByte(); newSeq->sprites[frame].copyScreenAndRedrawFlag = newSeqDataStream->readByte(); newSeq->sprites[frame].spritesUnk3 = newSeqDataStream->readByte(); newSeq->sprites[frame].ticksToWaitUntilCycleRestart = newSeqDataStream->readByte(); // This variable could have been edited afterwards... if (newSeq->sprites[frame].ticksToWaitUntilCycleRestart != sequenceToCopy->sprites[frame].ticksToWaitUntilCycleRestart) { newSeq->sprites[frame].ticksToWaitUntilCycleRestart = sequenceToCopy->sprites[frame].ticksToWaitUntilCycleRestart; } newSeq->sprites[frame].soundDelay = newSeqDataStream->readByte(); newSeq->sprites[frame].soundAction = newSeqDataStream->readByte(); newSeq->sprites[frame].flags = newSeqDataStream->readByte(); newSeq->sprites[frame].position = newSeqDataStream->readByte(); newSeq->sprites[frame].spritesUnk9 = newSeqDataStream->readByte(); newSeq->sprites[frame].spritesUnk10 = newSeqDataStream->readByte(); newSeq->sprites[frame].spritesUnk11 = newSeqDataStream->readByte(); newSeq->sprites[frame].spritesUnk8 = (int)newSeqDataStream->readUint32LE(); newSeq->sprites[frame].visibilityDist = newSeqDataStream->readUint16LE(); newSeq->sprites[frame].hotspotPriority = newSeqDataStream->readUint16LE(); // This variable could have been edited afterwards... if (newSeq->sprites[frame].hotspotPriority != sequenceToCopy->sprites[frame].hotspotPriority) { newSeq->sprites[frame].hotspotPriority = sequenceToCopy->sprites[frame].hotspotPriority; } newSeqDataStream->readUint32LE(); // Empty "next" sprite pointer } delete newSeqDataStream; return newSeq; } } // End of namespace LastExpress