mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-25 03:09:13 +01:00
Merge pull request #21097 from hrydgard/more-assorted-fixes
A crashfix and some cleanup
This commit is contained in:
15 files changed
+235
-195
No files matched your search
@@ -6,6 +6,7 @@
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <condition_variable>
|
||||
@@ -83,6 +84,7 @@ private:
|
||||
|
||||
class GLRShader {
|
||||
public:
|
||||
explicit GLRShader(std::string_view _desc) : desc(_desc) {}
|
||||
~GLRShader() {
|
||||
if (shader) {
|
||||
glDeleteShader(shader);
|
||||
@@ -275,11 +277,10 @@ public:
|
||||
return step.create_buffer.buffer;
|
||||
}
|
||||
|
||||
GLRShader *CreateShader(GLuint stage, const std::string &code, const std::string &desc) {
|
||||
GLRShader *CreateShader(GLuint stage, const std::string &code, std::string_view desc) {
|
||||
GLRInitStep &step = initSteps_.push_uninitialized();
|
||||
step.stepType = GLRInitStepType::CREATE_SHADER;
|
||||
step.create_shader.shader = new GLRShader();
|
||||
step.create_shader.shader->desc = desc;
|
||||
step.create_shader.shader = new GLRShader(desc);
|
||||
step.create_shader.stage = stage;
|
||||
step.create_shader.code = new char[code.size() + 1];
|
||||
memcpy(step.create_shader.code, code.data(), code.size() + 1);
|
||||
|
||||
@@ -567,7 +567,7 @@ OpenGLContext::OpenGLContext(bool canChangeSwapInterval) : renderManager_(frameT
|
||||
caps_.fragmentShaderInt32Supported = true;
|
||||
}
|
||||
}
|
||||
caps_.texture3DSupported = gl_extensions.OES_texture_3D;
|
||||
caps_.texture3DSupported = gl_extensions.GLES3 || gl_extensions.OES_texture_3D;
|
||||
caps_.textureDepthSupported = gl_extensions.GLES3 || gl_extensions.OES_depth_texture;
|
||||
} else {
|
||||
if (gl_extensions.VersionGEThan(3, 3, 0)) {
|
||||
@@ -1310,6 +1310,12 @@ bool OpenGLPipeline::LinkShaders(const PipelineDesc &desc) {
|
||||
}
|
||||
}
|
||||
|
||||
if (linkShaders.empty()) {
|
||||
// Can't proceed.
|
||||
ERROR_LOG(Log::G3D, "LinkShaders: No valid shaders to link");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<GLRProgram::Semantic> semantics;
|
||||
semantics.reserve(8);
|
||||
// Bind all the common vertex data points. Mismatching ones will be ignored.
|
||||
|
||||
@@ -1225,6 +1225,11 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char
|
||||
|
||||
for (auto &iter : desc.shaders) {
|
||||
VKShaderModule *vkshader = (VKShaderModule *)iter;
|
||||
if (!iter) {
|
||||
ERROR_LOG(Log::G3D, "Null shader in pipeline creation");
|
||||
delete pipeline;
|
||||
return nullptr;
|
||||
}
|
||||
vkshader->AddRef();
|
||||
pipeline->deps.push_back(vkshader);
|
||||
if (vkshader->GetStage() == ShaderStage::Vertex) {
|
||||
|
||||
@@ -25,7 +25,7 @@ TextDrawerAndroid::TextDrawerAndroid(Draw::DrawContext *draw) : TextDrawer(draw)
|
||||
method_allocFont = env->GetStaticMethodID(cls_textRenderer, "allocFont", "(Ljava/lang/String;)I");
|
||||
method_freeAllFonts = env->GetStaticMethodID(cls_textRenderer, "freeAllFonts", "()V");
|
||||
method_measureText = env->GetStaticMethodID(cls_textRenderer, "measureText", "(Ljava/lang/String;ID)I");
|
||||
method_renderText = env->GetStaticMethodID(cls_textRenderer, "renderText", "(Ljava/lang/String;ID)[I");
|
||||
method_renderText = env->GetStaticMethodID(cls_textRenderer, "renderText", "(Ljava/lang/String;IDII)[I");
|
||||
} else {
|
||||
ERROR_LOG(Log::G3D, "Failed to find class: '%s'", textRendererClassName);
|
||||
}
|
||||
@@ -126,7 +126,7 @@ bool TextDrawerAndroid::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextS
|
||||
// WARN_LOG(Log::G3D, "Text: '%.*s' (%02x)", (int)str.length(), str.data(), str[0]);
|
||||
|
||||
// TODO: Handle bold/italic
|
||||
jintArray imageData = (jintArray)env->CallStaticObjectMethod(cls_textRenderer, method_renderText, jstr, iter->second.font, iter->second.size);
|
||||
jintArray imageData = (jintArray)env->CallStaticObjectMethod(cls_textRenderer, method_renderText, jstr, iter->second.font, iter->second.size, imageWidth, imageHeight);
|
||||
env->DeleteLocalRef(jstr);
|
||||
|
||||
entry.texture = nullptr;
|
||||
@@ -137,7 +137,12 @@ bool TextDrawerAndroid::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextS
|
||||
entry.lastUsedFrame = frameCount_;
|
||||
|
||||
jint *jimage = env->GetIntArrayElements(imageData, nullptr);
|
||||
_assert_(env->GetArrayLength(imageData) == imageWidth * imageHeight);
|
||||
if (env->GetArrayLength(imageData) != imageWidth * imageHeight) {
|
||||
ERROR_LOG(Log::G3D, "TextRenderer returned bad image size");
|
||||
env->ReleaseIntArrayElements(imageData, jimage, JNI_ABORT);
|
||||
env->DeleteLocalRef(imageData);
|
||||
return false;
|
||||
}
|
||||
if (texFormat == Draw::DataFormat::B4G4R4A4_UNORM_PACK16 || texFormat == Draw::DataFormat::R4G4B4A4_UNORM_PACK16) {
|
||||
bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint16_t));
|
||||
uint16_t *bitmapData16 = (uint16_t *)&bitmapData[0];
|
||||
|
||||
@@ -571,7 +571,12 @@ void __DisplaySetDisplayLayoutConfig(const DisplayLayoutConfig &config) {
|
||||
}
|
||||
|
||||
void __DisplayFlip(int cyclesLate) {
|
||||
_dbg_assert_(gpu);
|
||||
if (!gpu) {
|
||||
_dbg_assert_(gpu);
|
||||
// Something has gone wrong.
|
||||
flippedThisFrame = true;
|
||||
return;
|
||||
}
|
||||
|
||||
__DisplaySetFramerate();
|
||||
|
||||
|
||||
@@ -596,8 +596,7 @@ Draw::ShaderModule *PresentationCommon::CompileShaderModule(ShaderStage stage, S
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
Draw::ShaderModule *shader = draw_->CreateShaderModule(stage, lang_, (const uint8_t *)translated.c_str(), translated.size(), "postshader");
|
||||
return shader;
|
||||
return draw_->CreateShaderModule(stage, lang_, (const uint8_t *)translated.c_str(), translated.size(), "postshader");
|
||||
}
|
||||
|
||||
void PresentationCommon::SourceTexture(Draw::Texture *texture, int bufferWidth, int bufferHeight) {
|
||||
|
||||
@@ -248,6 +248,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry) {
|
||||
if (plan.depth == 1) {
|
||||
entry->textureName = render_->CreateTexture(GL_TEXTURE_2D, tw, th, 1, plan.levelsToCreate);
|
||||
} else {
|
||||
_dbg_assert_(draw_->GetDeviceCaps().texture3DSupported);
|
||||
entry->textureName = render_->CreateTexture(GL_TEXTURE_3D, tw, th, plan.depth, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -1252,6 +1252,7 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Keep going if these commands don't change state.
|
||||
case GE_CMD_TEXBUFWIDTH0:
|
||||
case GE_CMD_TEXADDR0:
|
||||
if (data != gstate.cmdmem[data >> 24])
|
||||
|
||||
+4
-5
@@ -682,7 +682,10 @@ public:
|
||||
ShaderDepalMode shaderDepalMode;
|
||||
GEBufferFormat depalFramebufferFormat;
|
||||
|
||||
u32 getRelativeAddress(u32 data) const;
|
||||
u32 getRelativeAddress(u32 data) const {
|
||||
u32 baseExtended = ((gstate.base & 0x000F0000) << 8) | data;
|
||||
return (offsetAddr + baseExtended) & 0x0FFFFFFF;
|
||||
}
|
||||
static void Reset();
|
||||
void DoState(PointerWrap &p);
|
||||
};
|
||||
@@ -692,7 +695,3 @@ class GPUDebugInterface;
|
||||
|
||||
extern GPUStateCache gstate_c;
|
||||
|
||||
inline u32 GPUStateCache::getRelativeAddress(u32 data) const {
|
||||
u32 baseExtended = ((gstate.base & 0x000F0000) << 8) | data;
|
||||
return (gstate_c.offsetAddr + baseExtended) & 0x0FFFFFFF;
|
||||
}
|
||||
+158
-152
@@ -1,180 +1,186 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Android Crash Dump Cleaner</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
|
||||
margin: 18px;
|
||||
}
|
||||
<meta charset="utf-8" />
|
||||
<title>Android Crash Dump Cleaner</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
|
||||
margin: 18px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0 0 12px 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
h1 {
|
||||
margin: 0 0 12px 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
min-height: 320px;
|
||||
resize: vertical;
|
||||
padding: 10px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, "Roboto Mono", monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
min-height: 320px;
|
||||
resize: vertical;
|
||||
padding: 10px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, "Roboto Mono", monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin: 10px 0 14px 0;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
.controls {
|
||||
margin: 10px 0 14px 0;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #bbb;
|
||||
background: #f6f6f6;
|
||||
cursor: pointer;
|
||||
}
|
||||
button {
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #bbb;
|
||||
background: #f6f6f6;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
button:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: 12px;
|
||||
color: #555;
|
||||
}
|
||||
.small {
|
||||
font-size: 12px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.col {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
.col {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 13px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
label {
|
||||
font-size: 13px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.top-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
</style>
|
||||
.top-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Android crash dump cleaner</h1>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label for="input">Input (paste crash dump here)</label>
|
||||
<textarea id="input" placeholder="Paste Android backtrace here..."></textarea>
|
||||
<div class="controls">
|
||||
<div class="top-actions">
|
||||
<button id="convert">Convert →</button>
|
||||
<button id="clearInput">Clear input</button>
|
||||
<button id="example">Insert example</button>
|
||||
</div>
|
||||
<div style="margin-left:auto" class="small">Automatically removes BuildId and shortens paths to the arch + filename.</div>
|
||||
</div>
|
||||
</div>
|
||||
<h1>Android crash dump cleaner</h1>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label for="input">Input (paste crash dump here)</label>
|
||||
<textarea id="input" placeholder="Paste Android backtrace here..."></textarea>
|
||||
<div class="controls">
|
||||
<div class="top-actions">
|
||||
<button id="convert">Convert →</button>
|
||||
<button id="clearInput">Clear input</button>
|
||||
<button id="example">Insert example</button>
|
||||
</div>
|
||||
<div style="margin-left:auto" class="small">Automatically removes BuildId and shortens paths to the arch
|
||||
+ filename.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<label for="output">Output (copy from here)</label>
|
||||
<textarea id="output" readonly placeholder="Converted output will appear here..."></textarea>
|
||||
<div class="controls">
|
||||
<button id="copy">Copy output</button>
|
||||
<button id="clearOutput">Clear output</button>
|
||||
<div style="margin-left:auto" class="small" id="copystatus"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<label for="output">Output (copy from here)</label>
|
||||
<textarea id="output" readonly placeholder="Converted output will appear here..."></textarea>
|
||||
<div class="controls">
|
||||
<button id="copy">Copy output</button>
|
||||
<button id="clearOutput">Clear output</button>
|
||||
<div style="margin-left:auto" class="small" id="copystatus"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Core cleaning function
|
||||
function cleanCrashDump(text) {
|
||||
// Split lines and process each.
|
||||
const lines = text.split(/\r?\n/);
|
||||
const archPattern = /(\/(?:arm64|arm|aarch64|x86_64|x86)\/[^)\s]+)/i;
|
||||
const buildIdPattern = /\s*\(BuildId:[^)]+\)\s*$/g;
|
||||
<script>
|
||||
function cleanCrashDump(text) {
|
||||
const lines = text.split(/\r?\n/);
|
||||
const archPattern = /(\/(?:arm64|arm|aarch64|x86_64|x86)\/[^)\s]+)/i;
|
||||
const buildIdPattern = /\s*\(BuildId:[^)]+\)\s*$/g;
|
||||
const pcPattern = /(pc\s+0x)([0-9a-fA-F]+)/;
|
||||
|
||||
return lines.map(line => {
|
||||
// 1) Remove trailing "(BuildId: ...)" occurrences
|
||||
line = line.replace(buildIdPattern, '');
|
||||
return lines.map(line => {
|
||||
// 1) Remove trailing BuildId
|
||||
line = line.replace(buildIdPattern, '');
|
||||
|
||||
// 2) Find an architecture-based path inside the line (like /arm64/libfoo.so or /arm/libfoo.so)
|
||||
// If found, replace the long absolute path (the first slash..until space or '(') with that arch-based substring.
|
||||
const archMatch = line.match(archPattern);
|
||||
if (archMatch) {
|
||||
// Replace the first full absolute path (starting with slash up to space or '(') with the short archMatch[0].
|
||||
// This aims to turn: /data/app/.../lib/arm64/libppsspp_jni.so => /arm64/libppsspp_jni.so
|
||||
line = line.replace(/\/[^()\s]+/, archMatch[0]);
|
||||
}
|
||||
// 1.5) Shorten pc to 32-bit
|
||||
line = line.replace(pcPattern, (_, prefix, hex) => {
|
||||
return prefix + hex.slice(-8);
|
||||
});
|
||||
|
||||
return line;
|
||||
}).join("\n");
|
||||
}
|
||||
// 2) Shorten absolute paths to /arch/libfoo.so
|
||||
const archMatch = line.match(archPattern);
|
||||
if (archMatch) {
|
||||
line = line.replace(/\/[^()\s]+/, archMatch[0]);
|
||||
}
|
||||
|
||||
// UI wiring
|
||||
const inputEl = document.getElementById('input');
|
||||
const outputEl = document.getElementById('output');
|
||||
const convertBtn = document.getElementById('convert');
|
||||
const copyBtn = document.getElementById('copy');
|
||||
const clearInputBtn = document.getElementById('clearInput');
|
||||
const clearOutputBtn = document.getElementById('clearOutput');
|
||||
const exampleBtn = document.getElementById('example');
|
||||
const copyStatus = document.getElementById('copystatus');
|
||||
return line;
|
||||
}).join("\n");
|
||||
}
|
||||
|
||||
convertBtn.addEventListener('click', () => {
|
||||
const inText = inputEl.value;
|
||||
outputEl.value = cleanCrashDump(inText);
|
||||
copyStatus.textContent = '';
|
||||
});
|
||||
|
||||
copyBtn.addEventListener('click', async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(outputEl.value);
|
||||
copyStatus.textContent = 'Copied ✓';
|
||||
setTimeout(() => { copyStatus.textContent = ''; }, 1500);
|
||||
} catch (e) {
|
||||
copyStatus.textContent = 'Copy failed';
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
// UI wiring
|
||||
const inputEl = document.getElementById('input');
|
||||
const outputEl = document.getElementById('output');
|
||||
const convertBtn = document.getElementById('convert');
|
||||
const copyBtn = document.getElementById('copy');
|
||||
const clearInputBtn = document.getElementById('clearInput');
|
||||
const clearOutputBtn = document.getElementById('clearOutput');
|
||||
const exampleBtn = document.getElementById('example');
|
||||
const copyStatus = document.getElementById('copystatus');
|
||||
|
||||
clearInputBtn.addEventListener('click', () => { inputEl.value = ''; });
|
||||
clearOutputBtn.addEventListener('click', () => { outputEl.value = ''; copyStatus.textContent = ''; });
|
||||
convertBtn.addEventListener('click', () => {
|
||||
const inText = inputEl.value;
|
||||
outputEl.value = cleanCrashDump(inText);
|
||||
copyStatus.textContent = '';
|
||||
});
|
||||
|
||||
exampleBtn.addEventListener('click', () => {
|
||||
inputEl.value = `backtrace:
|
||||
#00 pc 0x0000000001310974 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (ff_seek_frame_binary+360) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
|
||||
#01 pc 0x0000000001311700 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (av_seek_frame+572) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
|
||||
#02 pc 0x0000000000ab4e9c /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (MediaEngine::openContext(bool)+760) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
|
||||
#03 pc 0x0000000000ab5be4 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (MediaEngine::addStreamData(unsigned char const*, int)+388) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
|
||||
#04 pc 0x0000000000a1b048 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (PostPutAction::run(MipsCall&)+1072) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
|
||||
#05 pc 0x000000000096b7cc /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (HLEReturnFromMipsCall()+652) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
|
||||
#06 pc 0x000000000096bfd8 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (CallSyscallWithoutFlags(HLEFunction const*)) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
|
||||
#07 pc 0x00000000000c2068`;
|
||||
});
|
||||
copyBtn.addEventListener('click', async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(outputEl.value);
|
||||
copyStatus.textContent = 'Copied ✓';
|
||||
setTimeout(() => { copyStatus.textContent = ''; }, 1500);
|
||||
} catch (e) {
|
||||
copyStatus.textContent = 'Copy failed';
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
|
||||
// Optional: convert on paste/drop for convenience
|
||||
inputEl.addEventListener('paste', (e) => {
|
||||
// small convenience: automatically convert after paste
|
||||
setTimeout(() => {
|
||||
outputEl.value = cleanCrashDump(inputEl.value);
|
||||
}, 50);
|
||||
});
|
||||
clearInputBtn.addEventListener('click', () => { inputEl.value = ''; });
|
||||
clearOutputBtn.addEventListener('click', () => { outputEl.value = ''; copyStatus.textContent = ''; });
|
||||
|
||||
</script>
|
||||
exampleBtn.addEventListener('click', () => {
|
||||
inputEl.value = `backtrace:
|
||||
#00 pc 0x0000000001310974 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (ff_seek_frame_binary+360) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
|
||||
#01 pc 0x0000000001311700 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (av_seek_frame+572) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
|
||||
#02 pc 0x0000000000ab4e9c /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (MediaEngine::openContext(bool)+760) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
|
||||
#03 pc 0x0000000000ab5be4 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (MediaEngine::addStreamData(unsigned char const*, int)+388) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
|
||||
#04 pc 0x0000000000a1b048 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (PostPutAction::run(MipsCall&)+1072) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
|
||||
#05 pc 0x000000000096b7cc /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (HLEReturnFromMipsCall()+652) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
|
||||
#06 pc 0x000000000096bfd8 /data/app/~~Hyfi4AvhSN3X30k-enAkTA==/org.ppsspp.ppsspp-ICO6UNv4-Hyh2OmM_FmvZA==/lib/arm64/libppsspp_jni.so (CallSyscallWithoutFlags(HLEFunction const*)) (BuildId: bf1ef0a0943c53c1f57b19b0e3c71c5d7135877e)
|
||||
#07 pc 0x00000000000c2068`;
|
||||
});
|
||||
|
||||
// Optional: convert on paste/drop for convenience
|
||||
inputEl.addEventListener('paste', (e) => {
|
||||
// small convenience: automatically convert after paste
|
||||
setTimeout(() => {
|
||||
outputEl.value = cleanCrashDump(inputEl.value);
|
||||
}, 50);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</html>
|
||||
@@ -935,6 +935,14 @@ void GameInfoCache::Clear() {
|
||||
CancelAll();
|
||||
|
||||
std::lock_guard<std::mutex> lock(mapLock_);
|
||||
// NOTE: Some shared_pointers might have other owners. We still need to wipe their textures here.
|
||||
for (auto &[key, value] : info_) {
|
||||
std::lock_guard<std::mutex> lock(value->lock);
|
||||
value->pic0.Clear();
|
||||
value->pic1.Clear();
|
||||
value->icon.Clear();
|
||||
value->hasFlags &= ~(GameInfoFlags::PIC0 | GameInfoFlags::PIC1 | GameInfoFlags::ICON);
|
||||
}
|
||||
info_.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -185,6 +185,8 @@ GameInfoTex *GameImageView::GetTex(std::shared_ptr<GameInfo> info) const {
|
||||
case GameInfoFlags::PIC1:
|
||||
tex = &info->pic1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return tex;
|
||||
}
|
||||
|
||||
@@ -104,6 +104,5 @@ private:
|
||||
|
||||
UI::ViewGroup *scrollItemView_ = nullptr;
|
||||
UI::ViewGroup *productPanel_ = nullptr;
|
||||
UI::TextView *titleText_ = nullptr;
|
||||
};
|
||||
|
||||
@@ -116,16 +116,16 @@ public class ShortcutActivity extends Activity {
|
||||
|
||||
PpssppActivity.CheckABIAndLoadLibrary();
|
||||
String gameName = name;
|
||||
byte[] iconData = null;
|
||||
byte[] iconData;
|
||||
Object[] result = queryGameInfo(this, path);
|
||||
if (result != null && result.length >= 2) {
|
||||
if (result[0] != null) {
|
||||
gameName = (String) result[0]; // index 0 = name
|
||||
}
|
||||
iconData = (byte[]) result[1]; // index 1 = raw PNG/JPEG bytes, or null
|
||||
|
||||
Log.i(TAG, "Game name: " + gameName);
|
||||
} else {
|
||||
iconData = null;
|
||||
Log.e(TAG, "Bad return value from queryGameInfo");
|
||||
}
|
||||
|
||||
@@ -138,29 +138,37 @@ public class ShortcutActivity extends Activity {
|
||||
responseIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
|
||||
|
||||
if (iconData != null) {
|
||||
// Try to create a PNG from the iconData.
|
||||
Bitmap bmp = BitmapFactory.decodeByteArray(iconData, 0, iconData.length);
|
||||
if (bmp != null) {
|
||||
// Pad the bitmap into a square, to keep it a nice 2:1 aspect ratio.
|
||||
Bitmap paddedBitmap = Bitmap.createBitmap(
|
||||
bmp.getWidth(), bmp.getWidth(),
|
||||
Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(paddedBitmap);
|
||||
canvas.drawARGB(0, 0, 0, 0);
|
||||
int y = (bmp.getWidth() - bmp.getHeight()) / 2;
|
||||
if (y < 0) {
|
||||
// To be safe from wacky-aspect-ratio bitmaps.
|
||||
y = 0;
|
||||
try {
|
||||
// Try to create a PNG from the iconData.
|
||||
Bitmap bmp = BitmapFactory.decodeByteArray(iconData, 0, iconData.length);
|
||||
if (bmp != null) {
|
||||
// Pad the bitmap into a square, to keep it a nice 2:1 aspect ratio.
|
||||
Bitmap paddedBitmap = Bitmap.createBitmap(
|
||||
bmp.getWidth(), bmp.getWidth(),
|
||||
Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(paddedBitmap);
|
||||
canvas.drawARGB(0, 0, 0, 0);
|
||||
int y = (bmp.getWidth() - bmp.getHeight()) / 2;
|
||||
if (y < 0) {
|
||||
// To be safe from wacky-aspect-ratio bitmaps.
|
||||
y = 0;
|
||||
}
|
||||
canvas.drawBitmap(bmp, 0, y, new Paint(Paint.FILTER_BITMAP_FLAG));
|
||||
// Bitmap scaledBitmap = Bitmap.createScaledBitmap(bmp, 144, 72, true);
|
||||
responseIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, paddedBitmap);
|
||||
}
|
||||
canvas.drawBitmap(bmp, 0, y, new Paint(Paint.FILTER_BITMAP_FLAG));
|
||||
// Bitmap scaledBitmap = Bitmap.createScaledBitmap(bmp, 144, 72, true);
|
||||
responseIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, paddedBitmap);
|
||||
} catch (Exception e) {
|
||||
NativeApp.reportException(e, "Error assigning generated icon");
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "No icon available, falling back to PPSSPP icon");
|
||||
// Fall back to the PPSSPP icon.
|
||||
ShortcutIconResource iconResource = ShortcutIconResource.fromContext(this, R.mipmap.ic_launcher);
|
||||
responseIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
|
||||
try {
|
||||
// Fall back to the PPSSPP icon.
|
||||
ShortcutIconResource iconResource = ShortcutIconResource.fromContext(this, R.mipmap.ic_launcher);
|
||||
responseIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
|
||||
} catch (Exception e) {
|
||||
NativeApp.reportException(e, "Error assigning default icon");
|
||||
}
|
||||
}
|
||||
|
||||
setResult(RESULT_OK, responseIntent);
|
||||
|
||||
@@ -109,14 +109,9 @@ public class TextRenderer {
|
||||
Point s = measure(string, font, textSize);
|
||||
return (s.x << 16) | s.y;
|
||||
}
|
||||
|
||||
public static int[] renderText(String string, int font, double textSize) {
|
||||
public static int[] renderText(String string, int font, double textSize, int w, int h) {
|
||||
textPaint.setTypeface(fontMap.get(font));
|
||||
textPaint.setTextSize((float) textSize);
|
||||
Point s = measure(string, font, textSize);
|
||||
|
||||
int w = s.x;
|
||||
int h = s.y;
|
||||
|
||||
Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bmp);
|
||||
|
||||
Reference in new issue
Block a user