lots of style fixes
This commit is contained in:
parent
452564aed2
commit
e535683235
12 changed files with 55 additions and 150 deletions
|
@ -91,9 +91,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
|||
ContentDictionary.Add((Nca.Header.TitleId, Nca.Header.ContentType), NcaName);
|
||||
|
||||
NcaFile.Close();
|
||||
|
||||
Nca.Dispose();
|
||||
|
||||
NcaFile.Dispose();
|
||||
}
|
||||
}
|
||||
|
@ -125,9 +123,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
|||
ContentDictionary.Add((Nca.Header.TitleId, Nca.Header.ContentType), NcaName);
|
||||
|
||||
NcaFile.Close();
|
||||
|
||||
Nca.Dispose();
|
||||
|
||||
NcaFile.Dispose();
|
||||
}
|
||||
}
|
||||
|
@ -173,13 +169,10 @@ namespace Ryujinx.HLE.FileSystem.Content
|
|||
{
|
||||
if (ContentDictionary.ContainsValue(NcaId))
|
||||
{
|
||||
var Content = ContentDictionary.FirstOrDefault(x => x.Value == NcaId);
|
||||
|
||||
long TitleId = (long)Content.Key.Item1;
|
||||
|
||||
var Content = ContentDictionary.FirstOrDefault(x => x.Value == NcaId);
|
||||
long TitleId = (long)Content.Key.Item1;
|
||||
ContentType ContentType = Content.Key.Item2;
|
||||
|
||||
StorageId Storage = GetInstalledStorage(TitleId, ContentType, StorageId);
|
||||
StorageId Storage = GetInstalledStorage(TitleId, ContentType, StorageId);
|
||||
|
||||
return Storage == StorageId;
|
||||
}
|
||||
|
@ -231,22 +224,18 @@ namespace Ryujinx.HLE.FileSystem.Content
|
|||
|
||||
private bool VerifyContentType(LocationEntry LocationEntry, ContentType ContentType)
|
||||
{
|
||||
StorageId StorageId = LocationHelper.GetStorageId(LocationEntry.ContentPath);
|
||||
|
||||
string InstalledPath = Device.FileSystem.SwitchPathToSystemPath(LocationEntry.ContentPath);
|
||||
StorageId StorageId = LocationHelper.GetStorageId(LocationEntry.ContentPath);
|
||||
string InstalledPath = Device.FileSystem.SwitchPathToSystemPath(LocationEntry.ContentPath);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(InstalledPath))
|
||||
{
|
||||
if (File.Exists(InstalledPath))
|
||||
{
|
||||
FileStream File = new FileStream(InstalledPath, FileMode.Open, FileAccess.Read);
|
||||
|
||||
Nca Nca = new Nca(Device.System.KeySet, File, false);
|
||||
|
||||
bool ContentCheck = Nca.Header.ContentType == ContentType;
|
||||
FileStream File = new FileStream(InstalledPath, FileMode.Open, FileAccess.Read);
|
||||
Nca Nca = new Nca(Device.System.KeySet, File, false);
|
||||
bool ContentCheck = Nca.Header.ContentType == ContentType;
|
||||
|
||||
Nca.Dispose();
|
||||
|
||||
File.Dispose();
|
||||
|
||||
return ContentCheck;
|
||||
|
@ -290,7 +279,6 @@ namespace Ryujinx.HLE.FileSystem.Content
|
|||
LocationEntry Entry =
|
||||
LocationList.ToList().Find(x => x.TitleId == TitleId && x.ContentType == ContentType);
|
||||
|
||||
|
||||
if (Entry.ContentPath != null)
|
||||
{
|
||||
LocationList.Remove(Entry);
|
||||
|
|
|
@ -68,7 +68,6 @@ namespace Ryujinx.HLE.FileSystem
|
|||
{
|
||||
return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
File.Delete(Name);
|
||||
|
@ -106,8 +105,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||
|
||||
foreach (string File in Directory.EnumerateFiles(Path))
|
||||
{
|
||||
FileInfo FileInfo = new FileInfo(File);
|
||||
|
||||
FileInfo FileInfo = new FileInfo(File);
|
||||
DirectoryEntry DirectoryEntry = new DirectoryEntry(File, DirectoryEntryType.File, FileInfo.Length);
|
||||
|
||||
Entries.Add(DirectoryEntry);
|
||||
|
@ -123,8 +121,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||
|
||||
foreach (string File in Directory.EnumerateFiles(Path))
|
||||
{
|
||||
FileInfo FileInfo = new FileInfo(File);
|
||||
|
||||
FileInfo FileInfo = new FileInfo(File);
|
||||
DirectoryEntry DirectoryEntry = new DirectoryEntry(File, DirectoryEntryType.File, FileInfo.Length);
|
||||
|
||||
Entries.Add(DirectoryEntry);
|
||||
|
@ -252,17 +249,5 @@ namespace Ryujinx.HLE.FileSystem
|
|||
|
||||
throw new InvalidOperationException($"Path {Path} is not a child directory of {RootPath}");
|
||||
}
|
||||
|
||||
public bool Disposed { get; private set; }
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
Disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,8 @@ using System;
|
|||
|
||||
namespace Ryujinx.HLE.FileSystem
|
||||
{
|
||||
interface IFileSystemProvider : IDisposable
|
||||
interface IFileSystemProvider
|
||||
{
|
||||
bool Disposed { get; }
|
||||
|
||||
long CreateFile(string Name, long Size);
|
||||
|
||||
long CreateDirectory(string Name);
|
||||
|
|
|
@ -14,8 +14,6 @@ namespace Ryujinx.HLE.FileSystem
|
|||
{
|
||||
private Pfs Pfs;
|
||||
|
||||
public bool Disposed { get; private set; }
|
||||
|
||||
public PFsProvider(Pfs Pfs)
|
||||
{
|
||||
this.Pfs = Pfs;
|
||||
|
@ -145,15 +143,5 @@ namespace Ryujinx.HLE.FileSystem
|
|||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,6 @@ namespace Ryujinx.HLE.FileSystem
|
|||
{
|
||||
private Romfs RomFs;
|
||||
|
||||
public bool Disposed { get; private set; }
|
||||
|
||||
public RomFsProvider(Stream StorageStream)
|
||||
{
|
||||
RomFs = new Romfs(StorageStream);
|
||||
|
@ -161,15 +159,5 @@ namespace Ryujinx.HLE.FileSystem
|
|||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
Disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,9 +87,8 @@ namespace Ryujinx.HLE.FileSystem
|
|||
|
||||
if (SystemPath.StartsWith(BaseSystemPath))
|
||||
{
|
||||
string RawPath = SystemPath.Replace(BaseSystemPath, "");
|
||||
|
||||
int FirstSeparatorOffset = RawPath.IndexOf(Path.DirectorySeparatorChar);
|
||||
string RawPath = SystemPath.Replace(BaseSystemPath, "");
|
||||
int FirstSeparatorOffset = RawPath.IndexOf(Path.DirectorySeparatorChar);
|
||||
|
||||
if (FirstSeparatorOffset == -1)
|
||||
{
|
||||
|
|
|
@ -55,8 +55,7 @@ namespace Ryujinx.HLE.HOS.Font
|
|||
if (ContentManager.TryGetFontTitle(Name, out long FontTitle))
|
||||
{
|
||||
string ContentPath = ContentManager.GetInstalledContentPath(FontTitle, StorageId.NandSystem, ContentType.Data);
|
||||
|
||||
string FontPath = Device.FileSystem.SwitchPathToSystemPath(ContentPath);
|
||||
string FontPath = Device.FileSystem.SwitchPathToSystemPath(ContentPath);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(FontPath))
|
||||
{
|
||||
|
@ -69,16 +68,12 @@ namespace Ryujinx.HLE.HOS.Font
|
|||
}
|
||||
|
||||
FileStream NcaFileStream = new FileStream(FontPath, FileMode.Open, FileAccess.Read);
|
||||
Nca Nca = new Nca(Device.System.KeySet, NcaFileStream, false);
|
||||
NcaSection RomfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs);
|
||||
Romfs Romfs = new Romfs(Nca.OpenSection(RomfsSection.SectionNum, false, Device.System.FsIntegrityCheckLevel));
|
||||
Stream FontFile = Romfs.OpenFile(Romfs.Files[FileIndex]);
|
||||
|
||||
Nca Nca = new Nca(Device.System.KeySet, NcaFileStream, false);
|
||||
|
||||
NcaSection RomfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs);
|
||||
|
||||
Romfs Romfs = new Romfs(Nca.OpenSection(RomfsSection.SectionNum, false, Device.System.FsIntegrityCheckLevel));
|
||||
|
||||
Stream EncryptedFontFile = Romfs.OpenFile(Romfs.Files[FileIndex]);
|
||||
|
||||
byte[] Data = DecryptFont(EncryptedFontFile);
|
||||
byte[] Data = DecryptFont(FontFile);
|
||||
|
||||
FontInfo Info = new FontInfo((int)FontOffset, Data.Length);
|
||||
|
||||
|
|
|
@ -33,8 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
{ 1, GetEntryCount }
|
||||
};
|
||||
|
||||
this.Provider = Provider;
|
||||
|
||||
this.Provider = Provider;
|
||||
this.DirectoryPath = DirectoryPath;
|
||||
|
||||
DirectoryEntries = new List<DirectoryEntry>();
|
||||
|
|
|
@ -9,7 +9,7 @@ using static Ryujinx.HLE.Utilities.StringUtils;
|
|||
|
||||
namespace Ryujinx.HLE.HOS.Services.FspSrv
|
||||
{
|
||||
class IFileSystem : IpcService, IDisposable
|
||||
class IFileSystem : IpcService
|
||||
{
|
||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||
|
||||
|
@ -44,8 +44,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
|
||||
OpenPaths = new HashSet<string>();
|
||||
|
||||
this.Path = Path;
|
||||
|
||||
this.Path = Path;
|
||||
this.Provider = Provider;
|
||||
}
|
||||
|
||||
|
@ -386,15 +385,5 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
OpenPaths.Remove(DirInterface.DirectoryPath);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
Provider.Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -42,10 +42,8 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
|
||||
public long OpenBisFileSystem(ServiceCtx Context)
|
||||
{
|
||||
int BisPartitionId = Context.RequestData.ReadInt32();
|
||||
|
||||
int BisPartitionId = Context.RequestData.ReadInt32();
|
||||
string PartitionString = ReadUtf8String(Context);
|
||||
|
||||
string BisPartitonPath = string.Empty;
|
||||
|
||||
switch (BisPartitionId)
|
||||
|
@ -76,12 +74,9 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
public long OpenFileSystemWithId(ServiceCtx Context)
|
||||
{
|
||||
FileSystemType FileSystemType = (FileSystemType)Context.RequestData.ReadInt32();
|
||||
|
||||
long TitleId = Context.RequestData.ReadInt64();
|
||||
|
||||
string SwitchPath = ReadUtf8String(Context);
|
||||
|
||||
string FullPath = Context.Device.FileSystem.SwitchPathToSystemPath(SwitchPath);
|
||||
long TitleId = Context.RequestData.ReadInt64();
|
||||
string SwitchPath = ReadUtf8String(Context);
|
||||
string FullPath = Context.Device.FileSystem.SwitchPathToSystemPath(SwitchPath);
|
||||
|
||||
if (!File.Exists(FullPath))
|
||||
{
|
||||
|
@ -94,8 +89,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
}
|
||||
|
||||
FileStream FileStream = new FileStream(FullPath, FileMode.Open, FileAccess.Read);
|
||||
|
||||
string Extension = Path.GetExtension(FullPath);
|
||||
string Extension = Path.GetExtension(FullPath);
|
||||
|
||||
if (Extension == ".nca")
|
||||
{
|
||||
|
@ -144,10 +138,8 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
public long OpenDataStorageByDataId(ServiceCtx Context)
|
||||
{
|
||||
StorageId StorageId = (StorageId)Context.RequestData.ReadByte();
|
||||
|
||||
byte[] Padding = Context.RequestData.ReadBytes(7);
|
||||
|
||||
long TitleId = Context.RequestData.ReadInt64();
|
||||
byte[] Padding = Context.RequestData.ReadBytes(7);
|
||||
long TitleId = Context.RequestData.ReadInt64();
|
||||
|
||||
StorageId InstalledStorage =
|
||||
Context.Device.System.ContentManager.GetInstalledStorage(TitleId, ContentType.Data, StorageId);
|
||||
|
@ -166,6 +158,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
{
|
||||
ContentPath = Context.Device.System.ContentManager.GetInstalledContentPath(TitleId, StorageId, ContentType.AocData);
|
||||
}
|
||||
|
||||
string InstallPath = Context.Device.FileSystem.SwitchPathToSystemPath(ContentPath);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(InstallPath))
|
||||
|
@ -174,23 +167,24 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
|
||||
if (File.Exists(NcaPath))
|
||||
{
|
||||
FileStream NcaStream = new FileStream(NcaPath, FileMode.Open, FileAccess.Read);
|
||||
|
||||
Nca Nca = new Nca(Context.Device.System.KeySet, NcaStream, false);
|
||||
|
||||
FileStream NcaStream = new FileStream(NcaPath, FileMode.Open, FileAccess.Read);
|
||||
Nca Nca = new Nca(Context.Device.System.KeySet, NcaStream, false);
|
||||
NcaSection RomfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs);
|
||||
|
||||
Stream RomfsStream = Nca.OpenSection(RomfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel);
|
||||
Stream RomfsStream = Nca.OpenSection(RomfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel);
|
||||
|
||||
MakeObject(Context, new IStorage(RomfsStream));
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FileNotFoundException($"No Nca found in Path `{NcaPath}`.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new DirectoryNotFoundException($"Path for title id {TitleId:x16} on Storage {StorageId} was not found in Path {InstallPath}.");
|
||||
}
|
||||
}
|
||||
|
||||
throw new FileNotFoundException($"System archive with titleid {TitleId:x16} was not found on Storage {StorageId}. Found in {InstalledStorage}.");
|
||||
|
@ -220,14 +214,10 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
Context.RequestData.ReadInt64(),
|
||||
Context.RequestData.ReadInt64());
|
||||
|
||||
long SaveId = Context.RequestData.ReadInt64();
|
||||
|
||||
SaveDataType SaveDataType = (SaveDataType)Context.RequestData.ReadByte();
|
||||
|
||||
SaveInfo SaveInfo = new SaveInfo(TitleId, SaveId, SaveDataType, UserId, SaveSpaceId);
|
||||
|
||||
string SavePath = Context.Device.FileSystem.GetGameSavePath(SaveInfo, Context);
|
||||
|
||||
long SaveId = Context.RequestData.ReadInt64();
|
||||
SaveDataType SaveDataType = (SaveDataType)Context.RequestData.ReadByte();
|
||||
SaveInfo SaveInfo = new SaveInfo(TitleId, SaveId, SaveDataType, UserId, SaveSpaceId);
|
||||
string SavePath = Context.Device.FileSystem.GetGameSavePath(SaveInfo, Context);
|
||||
FileSystemProvider FileSystemProvider = new FileSystemProvider(SavePath, Context.Device.FileSystem.GetBasePath());
|
||||
|
||||
MakeObject(Context, new IFileSystem(SavePath, FileSystemProvider));
|
||||
|
@ -235,10 +225,8 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
|
||||
private long OpenNsp(ServiceCtx Context, string PfsPath)
|
||||
{
|
||||
FileStream PfsFile = new FileStream(PfsPath, FileMode.Open, FileAccess.Read);
|
||||
|
||||
Pfs Nsp = new Pfs(PfsFile);
|
||||
|
||||
FileStream PfsFile = new FileStream(PfsPath, FileMode.Open, FileAccess.Read);
|
||||
Pfs Nsp = new Pfs(PfsFile);
|
||||
PfsFileEntry TicketFile = Nsp.Files.FirstOrDefault(x => x.Name.EndsWith(".tik"));
|
||||
|
||||
if (TicketFile != null)
|
||||
|
@ -265,18 +253,15 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
|
||||
if (RomfsSection != null)
|
||||
{
|
||||
Stream RomfsStream = Nca.OpenSection(RomfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel);
|
||||
|
||||
Stream RomfsStream = Nca.OpenSection(RomfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel);
|
||||
IFileSystem NcaFileSystem = new IFileSystem(NcaPath, new RomFsProvider(RomfsStream));
|
||||
|
||||
MakeObject(Context, NcaFileSystem);
|
||||
}
|
||||
else if(PfsSection !=null)
|
||||
{
|
||||
Stream PfsStream = Nca.OpenSection(PfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel);
|
||||
|
||||
Pfs Pfs = new Pfs(PfsStream);
|
||||
|
||||
Stream PfsStream = Nca.OpenSection(PfsSection.SectionNum, false, Context.Device.System.FsIntegrityCheckLevel);
|
||||
Pfs Pfs = new Pfs(PfsStream);
|
||||
IFileSystem NcaFileSystem = new IFileSystem(NcaPath, new PFsProvider(Pfs));
|
||||
|
||||
MakeObject(Context, NcaFileSystem);
|
||||
|
@ -305,8 +290,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
FileMode.Open,
|
||||
FileAccess.Read);
|
||||
|
||||
Pfs Nsp = new Pfs(PfsFile);
|
||||
|
||||
Pfs Nsp = new Pfs(PfsFile);
|
||||
PfsFileEntry TicketFile = Nsp.Files.FirstOrDefault(x => x.Name.EndsWith(".tik"));
|
||||
|
||||
if (TicketFile != null)
|
||||
|
|
|
@ -220,8 +220,7 @@ namespace Ryujinx.HLE.HOS.Services.Lr
|
|||
private bool ResolvePath(ServiceCtx Context, long TitleId,ContentType ContentType)
|
||||
{
|
||||
ContentManager ContentManager = Context.Device.System.ContentManager;
|
||||
|
||||
string ContentPath = ContentManager.GetInstalledContentPath(TitleId, StorageId, ContentType.Program);
|
||||
string ContentPath = ContentManager.GetInstalledContentPath(TitleId, StorageId, ContentType.Program);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(ContentPath))
|
||||
{
|
||||
|
@ -243,8 +242,7 @@ namespace Ryujinx.HLE.HOS.Services.Lr
|
|||
private void DeleteContentPath(ServiceCtx Context, long TitleId, ContentType ContentType)
|
||||
{
|
||||
ContentManager ContentManager = Context.Device.System.ContentManager;
|
||||
|
||||
string ContentPath = ContentManager.GetInstalledContentPath(TitleId, StorageId, ContentType.Manual);
|
||||
string ContentPath = ContentManager.GetInstalledContentPath(TitleId, StorageId, ContentType.Manual);
|
||||
|
||||
ContentManager.ClearEntry(TitleId, ContentType.Manual, StorageId);
|
||||
}
|
||||
|
|
|
@ -168,10 +168,8 @@ namespace Ryujinx.HLE.HOS.Services.Set
|
|||
|
||||
public static byte[] GetFirmwareData(Switch Device)
|
||||
{
|
||||
byte[] Data = null;
|
||||
|
||||
long TitleId = 0x0100000000000809;
|
||||
|
||||
byte[] Data = null;
|
||||
long TitleId = 0x0100000000000809;
|
||||
string ContentPath = Device.System.ContentManager.GetInstalledContentPath(TitleId, StorageId.NandSystem, ContentType.Data);
|
||||
|
||||
if(string.IsNullOrWhiteSpace(ContentPath))
|
||||
|
@ -179,13 +177,10 @@ namespace Ryujinx.HLE.HOS.Services.Set
|
|||
return null;
|
||||
}
|
||||
|
||||
string FirmwareTitlePath = Device.FileSystem.SwitchPathToSystemPath(ContentPath);
|
||||
|
||||
FileStream FirmwareStream = File.Open(FirmwareTitlePath, FileMode.Open, FileAccess.Read);
|
||||
|
||||
Nca FirmwareContent = new Nca(Device.System.KeySet, FirmwareStream, false);
|
||||
|
||||
Stream RomFsStream = FirmwareContent.OpenSection(0, false, Device.System.FsIntegrityCheckLevel);
|
||||
string FirmwareTitlePath = Device.FileSystem.SwitchPathToSystemPath(ContentPath);
|
||||
FileStream FirmwareStream = File.Open(FirmwareTitlePath, FileMode.Open, FileAccess.Read);
|
||||
Nca FirmwareContent = new Nca(Device.System.KeySet, FirmwareStream, false);
|
||||
Stream RomFsStream = FirmwareContent.OpenSection(0, false, Device.System.FsIntegrityCheckLevel);
|
||||
|
||||
if(RomFsStream == null)
|
||||
{
|
||||
|
@ -205,7 +200,6 @@ namespace Ryujinx.HLE.HOS.Services.Set
|
|||
}
|
||||
|
||||
FirmwareContent.Dispose();
|
||||
|
||||
FirmwareStream.Dispose();
|
||||
|
||||
return Data;
|
||||
|
|
Loading…
Add table
Reference in a new issue