HD pack builder and BGM improvement

Added option in HD pack builder to save screen information when tiles are first shown. User can look up which screen the tiles are added in the tileIndex.csv and open the screen_XXX.csv and screen_XXX.png to see the actual usage of the tiles.

Added playback option and volume option to <bgm> tag. Use 1 for looping playback, 0 for single playback, -1 for no change. Use 0 to 128 for volume control and -1 for no volume change.
This commit is contained in:
mkwong98 2020-12-20 22:26:12 +08:00
parent 5b52546cac
commit 2d0468eff5
13 changed files with 816 additions and 559 deletions

View file

@ -41,10 +41,15 @@ bool HdAudioDevice::PlayBgmTrack(uint8_t track, uint32_t startOffset)
{
int trackId = _album * 256 + track;
auto result = _hdData->BgmFilesById.find(trackId);
auto result2 = _hdData->BgmLoopPointsById.find(trackId);
if(result != _hdData->BgmFilesById.end()) {
if(_oggMixer->Play(result->second, false, startOffset, result2->second)) {
if(_oggMixer->Play(result->second.File, false, startOffset, result->second.LoopPoint)) {
_lastBgmTrack = trackId;
if (result->second.PlaybackOptions != -1) {
_oggMixer->SetPlaybackOptions(result->second.PlaybackOptions);
}
if (result->second.Volume != -1) {
_oggMixer->SetBgmVolume(result->second.Volume);
}
return true;
}
} else {

View file

@ -65,6 +65,16 @@ protected:
sprite.TileIndex = (isChrRam ? (_lastSprite->TileAddr & _chrRamIndexMask) : _lastSprite->AbsoluteTileAddr) / 16;
sprite.PaletteColors = ReadPaletteRAM(_lastSprite->PaletteOffset + 3) | (ReadPaletteRAM(_lastSprite->PaletteOffset + 2) << 8) | (ReadPaletteRAM(_lastSprite->PaletteOffset + 1) << 16) | 0xFF000000;
sprite.IsChrRamTile = isChrRam;
sprite.BackgroundPriority = _lastSprite->BackgroundPriority;
sprite.HorizontalMirroring = _lastSprite->HorizontalMirror;
sprite.VerticalMirroring = _lastSprite->VerticalMirror;
sprite.OffsetX = (int32_t)_cycle - _lastSprite->SpriteX - 1;
if (_lastSprite->OffsetY >= 8) {
sprite.OffsetY = _lastSprite->OffsetY - 8;
}
else {
sprite.OffsetY = _lastSprite->OffsetY;
}
_console->GetMapper()->CopyChrTile(_lastSprite->AbsoluteTileAddr & 0xFFFFFFF0, sprite.TileData);
_hdPackBuilder->ProcessTile(_cycle - 1, _scanline, _lastSprite->AbsoluteTileAddr, sprite, mapper, false, _bankHashes[_lastSprite->TileAddr / _chrRamBankSize], false);
@ -77,6 +87,8 @@ protected:
tile.TileIndex = (isChrRam ? (lastTile->TileAddr & _chrRamIndexMask) : lastTile->AbsoluteTileAddr) / 16;
tile.PaletteColors = ReadPaletteRAM(lastTile->PaletteOffset + 3) | (ReadPaletteRAM(lastTile->PaletteOffset + 2) << 8) | (ReadPaletteRAM(lastTile->PaletteOffset + 1) << 16) | (ReadPaletteRAM(0) << 24);
tile.IsChrRamTile = isChrRam;
tile.OffsetX = (_state.XScroll + ((_cycle - 1) & 0x07)) & 0x07;
tile.OffsetY = lastTile->OffsetY;
_console->GetMapper()->CopyChrTile(lastTile->AbsoluteTileAddr & 0xFFFFFFF0, tile.TileData);
_hdPackBuilder->ProcessTile(_cycle - 1, _scanline, lastTile->AbsoluteTileAddr, tile, mapper, false, _bankHashes[lastTile->TileAddr / _chrRamBankSize], hasBgSprite);
@ -121,10 +133,12 @@ public:
void SendFrame()
{
if(_hdData) {
HdPpu::SendFrame();
} else {
PPU::SendFrame();
}
_hdPackBuilder->endFrame();
}
};

View file

@ -346,6 +346,14 @@ struct HdBackgroundInfo
}
};
struct HdBGMInfo {
string File;
uint32_t LoopPoint;
int16_t Volume;
int16_t PlaybackOptions;
};
struct HdPackData
{
vector<HdBackgroundInfo> Backgrounds;
@ -355,8 +363,7 @@ struct HdPackData
std::unordered_set<uint32_t> WatchedMemoryAddresses;
std::unordered_map<HdTileKey, vector<HdPackTileInfo*>> TileByKey;
std::unordered_map<string, string> PatchesByHash;
std::unordered_map<int, string> BgmFilesById;
std::unordered_map<int, uint32_t> BgmLoopPointsById;
std::unordered_map<int, HdBGMInfo> BgmFilesById;
std::unordered_map<int, string> SfxFilesById;
vector<uint32_t> Palette;
@ -381,4 +388,14 @@ enum class HdPackOptions
AlternateRegisterRange = 2,
DisableCache = 8,
DontRenderOriginalTiles = 16
};
};
struct HdScreenTileInfo : public HdTileKey
{
int16_t ScreenX;
int16_t ScreenY;
bool HorizontalMirroring;
bool VerticalMirroring;
bool BackgroundPriority;
bool IsNew;
};

View file

@ -5,6 +5,7 @@
#include "HdNesPack.h"
#include "Console.h"
#include "ConsolePauseHelper.h"
#include "VideoDecoder.h"
HdPackBuilder* HdPackBuilder::_instance = nullptr;
@ -15,6 +16,7 @@ enum HdPackRecordFlags
SortByUsageFrequency = 2,
GroupBlankTiles = 4,
IgnoreOverscan = 8,
SaveFrame = 16,
};
HdPackBuilder::HdPackBuilder(shared_ptr<Console> console, string saveFolder, ScaleFilterType filterType, uint32_t scale, uint32_t flags, uint32_t chrRamBankSize, bool isChrRam)
@ -25,7 +27,8 @@ HdPackBuilder::HdPackBuilder(shared_ptr<Console> console, string saveFolder, Sca
_chrRamBankSize = chrRamBankSize;
_flags = flags;
_isChrRam = isChrRam;
_hasNewTile = false;
_frameID = 0;
string existingPackDefinition = FolderUtilities::CombinePath(saveFolder, "hires.txt");
if(ifstream(existingPackDefinition)) {
HdPackLoader::LoadHdNesPack(existingPackDefinition, _hdData);
@ -41,18 +44,130 @@ HdPackBuilder::HdPackBuilder(shared_ptr<Console> console, string saveFolder, Sca
_hdData.Scale = scale;
}
if (_flags & (HdPackRecordFlags::SaveFrame)) {
uint32_t foundID;
vector<string> screenFiles = FolderUtilities::GetFilesInFolder(saveFolder, { ".png", ".csv" }, false);
string filePrefix = FolderUtilities::CombinePath(_saveFolder, "");
for (string path : screenFiles) {
if (path.compare(filePrefix.length(), 7, "screen_") == 0) {
foundID = std::stoi(path.substr(filePrefix.length() + 7, path.length() - filePrefix.length() - 11));
if (foundID >= _frameID) {
_frameID = foundID + 1;
}
}
}
FolderUtilities::CreateFolder(_saveFolder);
}
_romName = FolderUtilities::GetFilename(_console->GetRomInfo().RomName, false);
_instance = this;
}
HdPackBuilder::~HdPackBuilder()
{
SaveHdPack();
if(_instance == this) {
_instance = nullptr;
}
}
void HdPackBuilder::endFrame() {
if (_hasNewTile && (spritesOnScreen.size() > 0 || bgTilesOnScreen.size() > 0) && _flags & (HdPackRecordFlags::SaveFrame)) {
//generate file name suffix
string counterStr = std::to_string(_frameID);
while (counterStr.length() < 3) {
counterStr = "0" + counterStr;
}
//save screen
_console->GetVideoDecoder()->TakeScreenshot(FolderUtilities::CombinePath(_saveFolder, "screen_" + counterStr + ".png"));
//save screen info
stringstream ss;
ss << "Type,X,Y,Tile,Palette,Background Priority,Horizontal Mirroring,Vertical Mirroring,Is New," << std::endl;
stringstream ssidx;
if (_frameID == 0) {
ssidx << "Type,Tile,Palette,Screen ID," << std::endl;
}
for (HdScreenTileInfo t:spritesOnScreen) {
ss << "Sprite," << t.ScreenX << "," << t.ScreenY << ",";
if (t.IsChrRamTile) {
for (int i = 0; i < 16; i++) {
ss << HexUtilities::ToHex(t.TileData[i]);
}
}
else {
ss << HexUtilities::ToHex(t.TileIndex);
}
ss << "," << HexUtilities::ToHex(t.PaletteColors, true) << "," <<
(t.BackgroundPriority ? "Y" : "N") << "," <<
(t.HorizontalMirroring ? "Y" : "N") << "," <<
(t.VerticalMirroring ? "Y" : "N") << "," <<
(t.IsNew ? "Y" : "N") << "," << std::endl;
if (t.IsNew) {
ssidx << "Sprite,";
if (t.IsChrRamTile) {
for (int i = 0; i < 16; i++) {
ssidx << HexUtilities::ToHex(t.TileData[i]);
}
}
else {
ssidx << HexUtilities::ToHex(t.TileIndex);
}
ssidx << "," << HexUtilities::ToHex(t.PaletteColors, true) << "," << counterStr << "," << std::endl;
}
}
for (HdScreenTileInfo t : bgTilesOnScreen) {
ss << "Background," << t.ScreenX << "," << t.ScreenY << ",";
if (t.IsChrRamTile) {
for (int i = 0; i < 16; i++) {
ss << HexUtilities::ToHex(t.TileData[i]);
}
}
else {
ss << HexUtilities::ToHex(t.TileIndex);
}
ss << "," << HexUtilities::ToHex(t.PaletteColors, true) << ",,,," << (t.IsNew ? "Y" : "N") << "," << std::endl;
if (t.IsNew) {
ssidx << "Background,";
if (t.IsChrRamTile) {
for (int i = 0; i < 16; i++) {
ssidx << HexUtilities::ToHex(t.TileData[i]);
}
}
else {
ssidx << HexUtilities::ToHex(t.TileIndex);
}
ssidx << "," << HexUtilities::ToHex(t.PaletteColors, true) << "," << counterStr << "," << std::endl;
}
}
ofstream screenInfoFile(FolderUtilities::CombinePath(_saveFolder, "screen_" + counterStr + ".csv"), ios::out);
screenInfoFile << ss.str();
screenInfoFile.close();
ofstream tileIndexFile(FolderUtilities::CombinePath(_saveFolder, "tileIndex.csv"), ios::app);
tileIndexFile << ssidx.str();
tileIndexFile.close();
_frameID++;
}
//reset for next frame
_hasNewTile = false;
spritesOnScreen.clear();
bgTilesOnScreen.clear();
}
void HdPackBuilder::AddTile(HdPackTileInfo *tile, uint32_t usageCount)
{
bool isTileBlank = (_flags & HdPackRecordFlags::GroupBlankTiles) ? tile->Blank : false;
@ -93,6 +208,7 @@ void HdPackBuilder::AddTile(HdPackTileInfo *tile, uint32_t usageCount)
_tilesByKey[tile->GetKey(false)] = tile;
_tileUsageCount[tile->GetKey(false)] = usageCount;
_hasNewTile = true;
}
void HdPackBuilder::ProcessTile(uint32_t x, uint32_t y, uint16_t tileAddr, HdPpuTileInfo &tile, BaseMapper *mapper, bool isSprite, uint32_t chrBankHash, bool transparencyRequired)
@ -110,7 +226,7 @@ void HdPackBuilder::ProcessTile(uint32_t x, uint32_t y, uint16_t tileAddr, HdPpu
//Check to see if a default tile matches
result = _tileUsageCount.find(tile.GetKey(true));
}
bool isNew = false;
if(result == _tileUsageCount.end()) {
//First time seeing this tile/palette combination, store it
HdPackTileInfo* hdTile = new HdPackTileInfo();
@ -126,6 +242,7 @@ void HdPackBuilder::ProcessTile(uint32_t x, uint32_t y, uint16_t tileAddr, HdPpu
_hdData.Tiles.push_back(unique_ptr<HdPackTileInfo>(hdTile));
AddTile(hdTile, 1);
isNew = true;
} else {
if(transparencyRequired) {
auto existingTile = _tilesByKey.find(tile.GetKey(false));
@ -139,6 +256,26 @@ void HdPackBuilder::ProcessTile(uint32_t x, uint32_t y, uint16_t tileAddr, HdPpu
result->second++;
}
}
if ((x == 0 || ((tile.OffsetX & 0x07) == 0)) && (y == 0 || ((tile.OffsetY & 0x07) == 0))) {
HdScreenTileInfo t;
t.IsChrRamTile = tile.IsChrRamTile;
t.PaletteColors = tile.PaletteColors;
t.ScreenX = x - tile.OffsetX;
t.ScreenY = y - tile.OffsetY - (tile.IsSpriteTile() ? 1 : 0);
memcpy(t.TileData, tile.TileData, 16);
t.TileIndex = tile.TileIndex;
t.IsNew = isNew;
if (tile.IsSpriteTile()) {
t.BackgroundPriority = tile.BackgroundPriority;
t.HorizontalMirroring = tile.HorizontalMirroring;
t.VerticalMirroring = tile.VerticalMirroring;
spritesOnScreen.push_back(t);
}
else {
bgTilesOnScreen.push_back(t);
}
}
}
void HdPackBuilder::GenerateHdTile(HdPackTileInfo *tile)
@ -352,7 +489,7 @@ void HdPackBuilder::SaveHdPack()
}
for(auto &bgmInfo : _hdData.BgmFilesById) {
ss << "<bgm>" << std::to_string(bgmInfo.first >> 8) << "," << std::to_string(bgmInfo.first & 0xFF) << "," << VirtualFile(bgmInfo.second).GetFileName() << std::endl;
ss << "<bgm>" << std::to_string(bgmInfo.first >> 8) << "," << std::to_string(bgmInfo.first & 0xFF) << "," << VirtualFile(bgmInfo.second.File).GetFileName() << "," << bgmInfo.second.LoopPoint << "," << bgmInfo.second.PlaybackOptions << "," << bgmInfo.second.Volume << std::endl;
}
for(auto &sfxInfo : _hdData.SfxFilesById) {

View file

@ -31,6 +31,11 @@ private:
string _saveFolder;
string _romName;
uint32_t _flags;
bool _hasNewTile;
uint32_t _frameID;
vector<HdScreenTileInfo> spritesOnScreen;
vector<HdScreenTileInfo> bgTilesOnScreen;
//Used to group blank tiles together
uint32_t _blankTileIndex = 0;
@ -44,6 +49,7 @@ public:
HdPackBuilder(shared_ptr<Console> console, string saveFolder, ScaleFilterType filterType, uint32_t scale, uint32_t flags, uint32_t chrRamBankSize, bool isChrRam);
~HdPackBuilder();
void endFrame();
void ProcessTile(uint32_t x, uint32_t y, uint16_t tileAddr, HdPpuTileInfo& tile, BaseMapper* mapper, bool isSprite, uint32_t chrBankHash, bool transparencyRequired);
void SaveHdPack();

View file

@ -651,18 +651,30 @@ void HdPackLoader::ProcessBgmTag(vector<string> &tokens)
if(trackId >= 0) {
if(_loadFromZip) {
VirtualFile file(_hdPackFolder, tokens[2]);
_data->BgmFilesById[trackId] = file;
_data->BgmFilesById[trackId].File = file;
} else {
_data->BgmFilesById[trackId] = FolderUtilities::CombinePath(_hdPackFolder, tokens[2]);
_data->BgmFilesById[trackId].File = FolderUtilities::CombinePath(_hdPackFolder, tokens[2]);
}
if (tokens.size() >= 4 && _data->Version >= 105) {
_data->BgmFilesById[trackId].LoopPoint = 0;
_data->BgmFilesById[trackId].PlaybackOptions = -1;
_data->BgmFilesById[trackId].Volume = -1;
if (tokens.size() >= 4) {
stringstream pt(tokens[3]);
uint32_t loopPoint;
pt >> loopPoint;
_data->BgmLoopPointsById[trackId] = loopPoint;
}
else {
_data->BgmLoopPointsById[trackId] = 0;
_data->BgmFilesById[trackId].LoopPoint = loopPoint;
if (tokens.size() >= 5) {
stringstream pt(tokens[4]);
int16_t pbo;
pt >> pbo;
_data->BgmFilesById[trackId].PlaybackOptions = pbo;
if (tokens.size() >= 6) {
stringstream pt(tokens[5]);
int16_t vol;
pt >> vol;
_data->BgmFilesById[trackId].Volume = vol;
}
}
}
}
}

View file

@ -253,6 +253,13 @@ void VideoDecoder::TakeScreenshot()
}
}
void VideoDecoder::TakeScreenshot(string filePath)
{
if (_videoFilter) {
_videoFilter->TakeScreenshot(_videoFilterType, filePath);
}
}
void VideoDecoder::TakeScreenshot(std::stringstream &stream, bool rawScreenshot)
{
if(!_ppuOutputBuffer) {

View file

@ -61,6 +61,7 @@ public:
void DecodeFrame(bool synchronous = false);
void TakeScreenshot();
void TakeScreenshot(string filePath);
void TakeScreenshot(std::stringstream &stream, bool rawScreenshot = false);
uint32_t GetFrameCount();

View file

@ -624,6 +624,7 @@
<Control ID="chkLargeSprites">Use 8x16 sprite display mode</Control>
<Control ID="chkGroupBlankTiles">Group blank tiles</Control>
<Control ID="chkIgnoreOverscan">Ignore tiles at the edges of the screen (overscan)</Control>
<Control ID="chkSaveFrame">Save frames which the tiles are first shown</Control>
<Control ID="lblFolder">Save Folder:</Control>
<Control ID="btnSelectFolder">Browse...</Control>
<Control ID="btnStartRecording">Start Recording</Control>
@ -869,6 +870,7 @@
<Message ID="HdPackBuilderGroupBlankHelp">This option groups all the blank tiles sequentially into the same PNG&#xA;files - this helps reduce the number of PNG files produced by removing &#xA;almost-empty PNG files containing only blank tiles.</Message>
<Message ID="HdPackBuilderLargeSpritesHelp">When enabled, this option will alter the display order of CHR banks&#xA;that contain only sprites to make the sprites easier to edit in the PNG file.</Message>
<Message ID="HdPackBuilderIgnoreOverscanHelp">When enabled, this will make the builder ignore any pixels in the overscan area.&#xA;This is useful in games that contain glitches on the outer edge of the screen.&#xA;Incorrect palette combinations due to these glitches will be ignored and won't be shown in the PNG files.</Message>
<Message ID="HdPackBuilderSaveFrameHelp">When enabled, the builder will save a screenshot and the composition of the screen in the pack folder when it encounters new tile in a frame.&#xA;This is useful for checking where that tile is used.&#xA;These files are not needed in the pack and should be deleted when sharing the pack.</Message>
<Message ID="InstallHdPackWrongRom">The selected HD Pack is not compatible with the currently running game and cannot be installed.</Message>
<Message ID="InstallHdPackError">An error occurred while trying to install the HD Pack: &#xA;&#xA;{0}</Message>

File diff suppressed because it is too large Load diff

View file

@ -43,8 +43,9 @@ namespace Mesen.GUI.Forms.HdPackEditor
toolTip.SetToolTip(picGroupBlankHelp, ResourceHelper.GetMessage("HdPackBuilderGroupBlankHelp"));
toolTip.SetToolTip(picLargeSpritesHelp, ResourceHelper.GetMessage("HdPackBuilderLargeSpritesHelp"));
toolTip.SetToolTip(picIgnoreOverscanHelp, ResourceHelper.GetMessage("HdPackBuilderIgnoreOverscanHelp"));
toolTip.SetToolTip(picSaveFrameHelp, ResourceHelper.GetMessage("HdPackBuilderSaveFrameHelp"));
UpdateUI(false);
UpdateUI(false);
}
protected override void OnFormClosing(FormClosingEventArgs e)
@ -123,6 +124,8 @@ namespace Mesen.GUI.Forms.HdPackEditor
chkLargeSprites.Enabled = !isRecording;
chkSortByFrequency.Enabled = !isRecording;
chkGroupBlankTiles.Enabled = !isRecording;
chkIgnoreOverscan.Enabled = !isRecording;
chkSaveFrame.Enabled = !isRecording;
cboChrBankSize.Enabled = !isRecording;
cboScale.Enabled = !isRecording;
@ -156,7 +159,10 @@ namespace Mesen.GUI.Forms.HdPackEditor
if(chkIgnoreOverscan.Checked) {
flags |= HdPackRecordFlags.IgnoreOverscan;
}
InteropEmu.HdBuilderStartRecording(txtSaveFolder.Text, ((FilterInfo)cboScale.SelectedItem).FilterType, ((FilterInfo)cboScale.SelectedItem).Scale, flags, (UInt32)Math.Pow(2, cboChrBankSize.SelectedIndex) * 0x400);
if(chkSaveFrame.Checked) {
flags |= HdPackRecordFlags.SaveFrame;
}
InteropEmu.HdBuilderStartRecording(txtSaveFolder.Text, ((FilterInfo)cboScale.SelectedItem).FilterType, ((FilterInfo)cboScale.SelectedItem).Scale, flags, (UInt32)Math.Pow(2, cboChrBankSize.SelectedIndex) * 0x400);
tmrRefresh.Start();
UpdateUI(true);

View file

@ -121,6 +121,9 @@
<value>17, 17</value>
</metadata>
<metadata name="tmrRefresh.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>107, 17</value>
<value>110, 17</value>
</metadata>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>194</value>
</metadata>
</root>

View file

@ -2548,9 +2548,10 @@ namespace Mesen.GUI
SortByUsageFrequency = 2,
GroupBlankTiles = 4,
IgnoreOverscan = 8,
}
SaveFrame = 16,
}
[StructLayout(LayoutKind.Sequential)]
[StructLayout(LayoutKind.Sequential)]
public class AddressTypeInfo
{
public Int32 Address;