logfilereader: Fix RAM parser issues (#82)

* Create size helper

* Replace fixed RAM units with size helper

* Rename CommonErrors to CommonError

Enum names should be singular

* Apply black formatting
This commit is contained in:
TSRBerry 2023-11-20 19:48:35 +01:00 committed by GitHub
parent 9669556a39
commit 72fd725a94
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 83 additions and 24 deletions

View file

@ -3,9 +3,10 @@ from enum import IntEnum, auto
from typing import Optional, Union from typing import Optional, Union
from robocop_ng.helpers.disabled_ids import is_build_id_valid from robocop_ng.helpers.disabled_ids import is_build_id_valid
from robocop_ng.helpers.size import Size
class CommonErrors(IntEnum): class CommonError(IntEnum):
SHADER_CACHE_COLLISION = auto() SHADER_CACHE_COLLISION = auto()
DUMP_HASH = auto() DUMP_HASH = auto()
SHADER_CACHE_CORRUPTION = auto() SHADER_CACHE_CORRUPTION = auto()
@ -209,24 +210,29 @@ class LogAnalyser:
self._hardware_info[setting] = cpu_match.group(1).rstrip() self._hardware_info[setting] = cpu_match.group(1).rstrip()
case "ram": case "ram":
sizes = "|".join(Size.names())
ram_match = re.search( ram_match = re.search(
r"RAM: Total ([\d.]+) (MiB|GB) ; Available ([\d.]+) (MiB|GB)", rf"RAM: Total ([\d.]+) ({sizes}) ; Available ([\d.]+) ({sizes})",
self._log_text, self._log_text,
re.MULTILINE, re.MULTILINE,
) )
if ram_match is not None: if ram_match is not None:
try: try:
dest_unit = Size.MiB
ram_available = float(ram_match.group(3)) ram_available = float(ram_match.group(3))
if ram_match.group(4) == "GB": ram_available = Size.from_name(ram_match.group(4)).convert(
ram_available *= 1024 ram_available, dest_unit
)
ram_total = float(ram_match.group(1)) ram_total = float(ram_match.group(1))
if ram_match.group(2) == "GB": ram_total = Size.from_name(ram_match.group(2)).convert(
ram_total *= 1024 ram_total, dest_unit
)
self._hardware_info[ self._hardware_info[
setting setting
] = f"{ram_available}/{ram_total} MiB" ] = f"{ram_available:.0f}/{ram_total:.0f} {dest_unit.name}"
except ValueError: except ValueError:
# ram_match.group(1) or ram_match.group(3) couldn't be parsed as a float. # ram_match.group(1) or ram_match.group(3) couldn't be parsed as a float.
self._hardware_info[setting] = "Error" self._hardware_info[setting] = "Error"
@ -500,36 +506,36 @@ class LogAnalyser:
def __get_notes(self): def __get_notes(self):
for common_error in self.get_common_errors(): for common_error in self.get_common_errors():
match common_error: match common_error:
case CommonErrors.SHADER_CACHE_COLLISION: case CommonError.SHADER_CACHE_COLLISION:
self._notes.append( self._notes.append(
"⚠️ Cache collision detected. Investigate possible shader cache issues" "⚠️ Cache collision detected. Investigate possible shader cache issues"
) )
case CommonErrors.SHADER_CACHE_CORRUPTION: case CommonError.SHADER_CACHE_CORRUPTION:
self._notes.append( self._notes.append(
"⚠️ Cache corruption detected. Investigate possible shader cache issues" "⚠️ Cache corruption detected. Investigate possible shader cache issues"
) )
case CommonErrors.DUMP_HASH: case CommonError.DUMP_HASH:
self._notes.append( self._notes.append(
"⚠️ Dump error detected. Investigate possible bad game/firmware dump issues" "⚠️ Dump error detected. Investigate possible bad game/firmware dump issues"
) )
case CommonErrors.UPDATE_KEYS: case CommonError.UPDATE_KEYS:
self._notes.append( self._notes.append(
"⚠️ Keys or firmware out of date, consider updating them" "⚠️ Keys or firmware out of date, consider updating them"
) )
case CommonErrors.FILE_PERMISSIONS: case CommonError.FILE_PERMISSIONS:
self._notes.append( self._notes.append(
"⚠️ File permission error. Consider deleting save directory and allowing Ryujinx to make a new one" "⚠️ File permission error. Consider deleting save directory and allowing Ryujinx to make a new one"
) )
case CommonErrors.FILE_NOT_FOUND: case CommonError.FILE_NOT_FOUND:
self._notes.append( self._notes.append(
"⚠️ Save not found error. Consider starting game without a save file or using a new save file⚠ Save not found error. Consider starting game without a save file or using a new save file" "⚠️ Save not found error. Consider starting game without a save file or using a new save file⚠ Save not found error. Consider starting game without a save file or using a new save file"
) )
case CommonErrors.MISSING_SERVICES: case CommonError.MISSING_SERVICES:
if self._settings["ignore_missing_services"] == "False": if self._settings["ignore_missing_services"] == "False":
self._notes.append( self._notes.append(
"⚠️ Consider enabling `Ignore Missing Services` in Ryujinx settings" "⚠️ Consider enabling `Ignore Missing Services` in Ryujinx settings"
) )
case CommonErrors.VULKAN_OUT_OF_MEMORY: case CommonError.VULKAN_OUT_OF_MEMORY:
if self._settings["texture_recompression"] == "Disabled": if self._settings["texture_recompression"] == "Disabled":
self._notes.append( self._notes.append(
"⚠️ Consider enabling `Texture Recompression` in Ryujinx settings" "⚠️ Consider enabling `Texture Recompression` in Ryujinx settings"
@ -591,11 +597,11 @@ class LogAnalyser:
def get_last_error(self) -> Optional[list[str]]: def get_last_error(self) -> Optional[list[str]]:
return self._log_errors[-1] if len(self._log_errors) > 0 else None return self._log_errors[-1] if len(self._log_errors) > 0 else None
def get_common_errors(self) -> list[CommonErrors]: def get_common_errors(self) -> list[CommonError]:
errors = [] errors = []
if self.contains_errors(["Cache collision found"], self._log_errors): if self.contains_errors(["Cache collision found"], self._log_errors):
errors.append(CommonErrors.SHADER_CACHE_COLLISION) errors.append(CommonError.SHADER_CACHE_COLLISION)
if self.contains_errors( if self.contains_errors(
[ [
"ResultFsInvalidIvfcHash", "ResultFsInvalidIvfcHash",
@ -603,7 +609,7 @@ class LogAnalyser:
], ],
self._log_errors, self._log_errors,
): ):
errors.append(CommonErrors.DUMP_HASH) errors.append(CommonError.DUMP_HASH)
if self.contains_errors( if self.contains_errors(
[ [
"Ryujinx.Graphics.Gpu.Shader.ShaderCache.Initialize()", "Ryujinx.Graphics.Gpu.Shader.ShaderCache.Initialize()",
@ -612,17 +618,17 @@ class LogAnalyser:
], ],
self._log_errors, self._log_errors,
): ):
errors.append(CommonErrors.SHADER_CACHE_CORRUPTION) errors.append(CommonError.SHADER_CACHE_CORRUPTION)
if self.contains_errors(["MissingKeyException"], self._log_errors): if self.contains_errors(["MissingKeyException"], self._log_errors):
errors.append(CommonErrors.UPDATE_KEYS) errors.append(CommonError.UPDATE_KEYS)
if self.contains_errors(["ResultFsPermissionDenied"], self._log_errors): if self.contains_errors(["ResultFsPermissionDenied"], self._log_errors):
errors.append(CommonErrors.FILE_PERMISSIONS) errors.append(CommonError.FILE_PERMISSIONS)
if self.contains_errors(["ResultFsTargetNotFound"], self._log_errors): if self.contains_errors(["ResultFsTargetNotFound"], self._log_errors):
errors.append(CommonErrors.FILE_NOT_FOUND) errors.append(CommonError.FILE_NOT_FOUND)
if self.contains_errors(["ServiceNotImplementedException"], self._log_errors): if self.contains_errors(["ServiceNotImplementedException"], self._log_errors):
errors.append(CommonErrors.MISSING_SERVICES) errors.append(CommonError.MISSING_SERVICES)
if self.contains_errors(["ErrorOutOfDeviceMemory"], self._log_errors): if self.contains_errors(["ErrorOutOfDeviceMemory"], self._log_errors):
errors.append(CommonErrors.VULKAN_OUT_OF_MEMORY) errors.append(CommonError.VULKAN_OUT_OF_MEMORY)
return errors return errors

View file

@ -0,0 +1,53 @@
from enum import IntEnum, auto
from typing import Self
class Size(IntEnum):
KB = auto()
KiB = auto()
MB = auto()
MiB = auto()
GB = auto()
GiB = auto()
@classmethod
def names(cls) -> list[str]:
return [size.name for size in cls]
@classmethod
def from_name(cls, name: str) -> Self:
for size in cls:
if size.name.lower() == name.lower():
return size
raise ValueError(f"No matching member found for: {name}")
@property
def _is_si_unit(self) -> bool:
return self.value % 2 != 0
@property
def _unit_value(self) -> int:
return self.value // 2 + (1 if self._is_si_unit else 0)
@property
def _base_factor(self) -> int:
return 10**3 if self._is_si_unit else 2**10
@property
def _byte_factor(self) -> int:
return (
10 ** (3 * self._unit_value)
if self._is_si_unit
else 2 ** (10 * self._unit_value)
)
def convert(self, value: float, fmt: Self) -> float:
if self == fmt:
return value
if self._is_si_unit == fmt._is_si_unit:
if self < fmt:
return value / self._base_factor ** (fmt._unit_value - self._unit_value)
else:
return value * self._base_factor ** (self._unit_value - fmt._unit_value)
else:
return value * (self._byte_factor / fmt._byte_factor)