A few additions and fixes
Add custom color option for image editor background. Start on character exporting for bffnt. as xlor. Fix sarc hashes. Fix bflims in archive editors
This commit is contained in:
parent
6aa135adf0
commit
c0c1e621a4
@ -124,7 +124,12 @@ namespace FirstPlugin
|
||||
sarcData.endianness = GetByteOrder(stream);
|
||||
|
||||
foreach (var file in SzsFiles.Files)
|
||||
files.Add(SetupFileEntry(file.Key, file.Value));
|
||||
{
|
||||
string fileName = file.Key;
|
||||
if (SzsFiles.HashOnly)
|
||||
fileName = SARCExt.SARC.TryGetNameFromHashTable(fileName);
|
||||
files.Add(SetupFileEntry(fileName, file.Value));
|
||||
}
|
||||
|
||||
sarcData.Files.Clear();
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ using FirstPlugin.Forms;
|
||||
|
||||
namespace FirstPlugin
|
||||
{
|
||||
public class BFFNT : IFileFormat, IEditor<BffntEditor>
|
||||
public class BFFNT : IFileFormat, IEditor<BffntEditor>, IConvertableTextFormat
|
||||
{
|
||||
public FileType FileType { get; set; } = FileType.Font;
|
||||
|
||||
@ -57,6 +57,22 @@ namespace FirstPlugin
|
||||
((BffntEditor)control).LoadFontFile(this);
|
||||
}
|
||||
|
||||
|
||||
#region Text Converter Interface
|
||||
public TextFileType TextFileType => TextFileType.Xml;
|
||||
public bool CanConvertBack => false;
|
||||
|
||||
public string ConvertToString()
|
||||
{
|
||||
return BffntCharSet2Xlor.ToXlor(this);
|
||||
}
|
||||
|
||||
public void ConvertFromString(string text)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Load(System.IO.Stream stream)
|
||||
{
|
||||
CanSave = true;
|
||||
@ -166,8 +182,8 @@ namespace FirstPlugin
|
||||
reader.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian;
|
||||
|
||||
string Signature = reader.ReadString(4, Encoding.ASCII);
|
||||
if (Signature != "FFNT")
|
||||
throw new Exception($"Invalid signature {Signature}! Expected FFNT.");
|
||||
if (Signature != "FFNT" && Signature != "CFNT")
|
||||
throw new Exception($"Invalid signature {Signature}! Expected FFNT or CFNT.");
|
||||
|
||||
BOM = reader.ReadUInt16();
|
||||
reader.CheckByteOrderMark(BOM);
|
||||
@ -187,6 +203,8 @@ namespace FirstPlugin
|
||||
else
|
||||
Platform = PlatformType.Cafe;
|
||||
|
||||
if (Signature == "CFNT")
|
||||
Platform = PlatformType.Ctr;
|
||||
|
||||
reader.Seek(HeaderSize, SeekOrigin.Begin);
|
||||
FontSection = new FINF();
|
||||
@ -280,6 +298,8 @@ namespace FirstPlugin
|
||||
//https://github.com/dnasdw/3dsfont/blob/4ead538d225d5d05929dce9d736bec91a6158052/src/bffnt/ResourceFormat.h
|
||||
public class FontKerningTable
|
||||
{
|
||||
private byte[] Data;
|
||||
|
||||
public KerningFirstTable FirstTable { get; set; }
|
||||
|
||||
public void Read(FileReader reader, FFNT Header)
|
||||
@ -620,37 +640,81 @@ namespace FirstPlugin
|
||||
if (Signature != "FINF")
|
||||
throw new Exception($"Invalid signature {Signature}! Expected FINF.");
|
||||
Size = reader.ReadUInt32();
|
||||
Type = reader.ReadEnum<FontType>(true);
|
||||
Height = reader.ReadByte();
|
||||
Width = reader.ReadByte();
|
||||
Ascent = reader.ReadByte();
|
||||
LineFeed = reader.ReadUInt16();
|
||||
AlterCharIndex = reader.ReadUInt16();
|
||||
DefaultLeftWidth = reader.ReadByte();
|
||||
DefaultGlyphWidth = reader.ReadByte();
|
||||
DefaultCharWidth = reader.ReadByte();
|
||||
CharEncoding = reader.ReadEnum<CharacterCode>(true);
|
||||
uint tglpOffset = reader.ReadUInt32();
|
||||
uint cwdhOffset = reader.ReadUInt32();
|
||||
uint cmapOffset = reader.ReadUInt32();
|
||||
|
||||
//Add counter for TGLP
|
||||
//Note the other counters are inside sections due to recusive setup
|
||||
Header.BlockCounter += 1;
|
||||
if (Header.Platform == FFNT.PlatformType.Ctr)
|
||||
{
|
||||
Type = reader.ReadEnum<FontType>(true);
|
||||
LineFeed = reader.ReadUInt16();
|
||||
AlterCharIndex = reader.ReadUInt16();
|
||||
DefaultLeftWidth = reader.ReadByte();
|
||||
DefaultGlyphWidth = reader.ReadByte();
|
||||
DefaultCharWidth = reader.ReadByte();
|
||||
CharEncoding = reader.ReadEnum<CharacterCode>(true);
|
||||
uint tglpOffset = reader.ReadUInt32();
|
||||
uint cwdhOffset = reader.ReadUInt32();
|
||||
uint cmapOffset = reader.ReadUInt32();
|
||||
|
||||
TextureGlyph = new TGLP();
|
||||
using (reader.TemporarySeek(tglpOffset - 8, SeekOrigin.Begin))
|
||||
TextureGlyph.Read(reader);
|
||||
Height = reader.ReadByte();
|
||||
Width = reader.ReadByte();
|
||||
Ascent = reader.ReadByte();
|
||||
reader.ReadByte(); //Padding
|
||||
|
||||
CharacterWidth = new CWDH();
|
||||
CharacterWidths.Add(CharacterWidth);
|
||||
using (reader.TemporarySeek(cwdhOffset - 8, SeekOrigin.Begin))
|
||||
CharacterWidth.Read(reader, Header, CharacterWidths);
|
||||
|
||||
CodeMap = new CMAP();
|
||||
CodeMaps.Add(CodeMap);
|
||||
using (reader.TemporarySeek(cmapOffset - 8, SeekOrigin.Begin))
|
||||
CodeMap.Read(reader, Header, CodeMaps);
|
||||
//Add counter for TGLP
|
||||
//Note the other counters are inside sections due to recusive setup
|
||||
Header.BlockCounter += 1;
|
||||
|
||||
TextureGlyph = new TGLP();
|
||||
using (reader.TemporarySeek(tglpOffset - 8, SeekOrigin.Begin))
|
||||
TextureGlyph.Read(reader);
|
||||
|
||||
CharacterWidth = new CWDH();
|
||||
CharacterWidths.Add(CharacterWidth);
|
||||
using (reader.TemporarySeek(cwdhOffset - 8, SeekOrigin.Begin))
|
||||
CharacterWidth.Read(reader, Header, CharacterWidths);
|
||||
|
||||
CodeMap = new CMAP();
|
||||
CodeMaps.Add(CodeMap);
|
||||
using (reader.TemporarySeek(cmapOffset - 8, SeekOrigin.Begin))
|
||||
CodeMap.Read(reader, Header, CodeMaps);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Type = reader.ReadEnum<FontType>(true);
|
||||
Height = reader.ReadByte();
|
||||
Width = reader.ReadByte();
|
||||
Ascent = reader.ReadByte();
|
||||
LineFeed = reader.ReadUInt16();
|
||||
AlterCharIndex = reader.ReadUInt16();
|
||||
DefaultLeftWidth = reader.ReadByte();
|
||||
DefaultGlyphWidth = reader.ReadByte();
|
||||
DefaultCharWidth = reader.ReadByte();
|
||||
CharEncoding = reader.ReadEnum<CharacterCode>(true);
|
||||
uint tglpOffset = reader.ReadUInt32();
|
||||
uint cwdhOffset = reader.ReadUInt32();
|
||||
uint cmapOffset = reader.ReadUInt32();
|
||||
|
||||
//Add counter for TGLP
|
||||
//Note the other counters are inside sections due to recusive setup
|
||||
Header.BlockCounter += 1;
|
||||
|
||||
TextureGlyph = new TGLP();
|
||||
using (reader.TemporarySeek(tglpOffset - 8, SeekOrigin.Begin))
|
||||
TextureGlyph.Read(reader);
|
||||
|
||||
CharacterWidth = new CWDH();
|
||||
CharacterWidths.Add(CharacterWidth);
|
||||
using (reader.TemporarySeek(cwdhOffset - 8, SeekOrigin.Begin))
|
||||
CharacterWidth.Read(reader, Header, CharacterWidths);
|
||||
|
||||
CodeMap = new CMAP();
|
||||
CodeMaps.Add(CodeMap);
|
||||
using (reader.TemporarySeek(cmapOffset - 8, SeekOrigin.Begin))
|
||||
CodeMap.Read(reader, Header, CodeMaps);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(FileWriter writer, FFNT header)
|
||||
|
77
File_Format_Library/FileFormats/Font/BffntCharSet2Xlor.cs
Normal file
77
File_Format_Library/FileFormats/Font/BffntCharSet2Xlor.cs
Normal file
@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
|
||||
namespace FirstPlugin
|
||||
{
|
||||
public class BffntCharSet2Xlor
|
||||
{
|
||||
public static string ToXlor(BFFNT bffnt)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
using (var texWriter = new StringWriter(sb))
|
||||
{
|
||||
texWriter.WriteLine("<?xml version =\"1.0\" encoding=\"UTF-8\" ?>");
|
||||
texWriter.WriteLine("<!DOCTYPE letter-order SYSTEM \"letter-order.dtd\">");
|
||||
texWriter.WriteLine("\r");
|
||||
texWriter.WriteLine("<letter-order version=\"1.0\">");
|
||||
texWriter.WriteLine(" <head>");
|
||||
texWriter.WriteLine($" <create user=\"\" date=\"{DateTime.Now.Year}-{DateTime.Now.Month}-{DateTime.Now.Day}T{DateTime.Now.Hour}:{DateTime.Now.Minute}:{DateTime.Now.Second}\" />\r");
|
||||
texWriter.WriteLine($" <title>{bffnt.FileName}</title>");
|
||||
texWriter.WriteLine(" <comment></comment>");
|
||||
texWriter.WriteLine(" </head>");
|
||||
texWriter.WriteLine(" <body>");
|
||||
texWriter.WriteLine(" <area width=\"16\" />");
|
||||
texWriter.WriteLine(" <order>");
|
||||
|
||||
int row_pos = 0;
|
||||
foreach (var item in bffnt.bffnt.FontSection.CodeMapDictionary)
|
||||
{
|
||||
ushort CharCode = (ushort)item.Key;
|
||||
if (row_pos != 16)
|
||||
{
|
||||
if (CharCode != 0x20)
|
||||
texWriter.Write($" &#x{CharCode.ToString("X4")};");
|
||||
|
||||
else
|
||||
texWriter.Write("<sp/> ");
|
||||
|
||||
row_pos++;
|
||||
}
|
||||
if (row_pos == 16)
|
||||
{
|
||||
texWriter.Write("\n");
|
||||
row_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
texWriter.WriteLine("\n");
|
||||
|
||||
/* foreach (var item in bffnt.bffnt.FontSection.CodeMapDictionary)
|
||||
{
|
||||
if (row_pos != 16)
|
||||
{
|
||||
texWriter.Write(item.Key);
|
||||
row_pos++;
|
||||
}
|
||||
if (row_pos == 16)
|
||||
{
|
||||
texWriter.Write("\n");
|
||||
row_pos = 0;
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
texWriter.WriteLine("\n");
|
||||
texWriter.WriteLine(" </order>");
|
||||
texWriter.WriteLine(" </body>");
|
||||
texWriter.WriteLine("</letter-order>");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -99,7 +99,6 @@ namespace FirstPlugin
|
||||
}
|
||||
}
|
||||
|
||||
ImageEditorBase form;
|
||||
public ImageEditorBase OpenForm()
|
||||
{
|
||||
bool IsDialog = IFileInfo != null && IFileInfo.InArchive;
|
||||
@ -115,7 +114,7 @@ namespace FirstPlugin
|
||||
prop.TileMode = image.TileMode;
|
||||
prop.Swizzle = image.Swizzle;
|
||||
|
||||
form = new ImageEditorBase();
|
||||
ImageEditorBase form = new ImageEditorBase();
|
||||
form.Text = Text;
|
||||
form.Dock = DockStyle.Fill;
|
||||
form.AddFileContextEvent("Save", Save);
|
||||
@ -126,14 +125,22 @@ namespace FirstPlugin
|
||||
return form;
|
||||
}
|
||||
|
||||
private ImageEditorBase form;
|
||||
|
||||
public void UpdateForm()
|
||||
{
|
||||
UpdateForm(form);
|
||||
}
|
||||
|
||||
public void FillEditor(UserControl control)
|
||||
{
|
||||
form = (ImageEditorBase)control;
|
||||
UpdateForm();
|
||||
}
|
||||
|
||||
private void UpdateForm()
|
||||
private void UpdateForm(ImageEditorBase form)
|
||||
{
|
||||
if (form != null && image != null)
|
||||
if (image != null)
|
||||
{
|
||||
Properties prop = new Properties();
|
||||
prop.Width = Width;
|
||||
@ -629,7 +636,7 @@ namespace FirstPlugin
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
|
||||
form.Dispose();
|
||||
}
|
||||
public byte[] Save()
|
||||
{
|
||||
|
@ -253,6 +253,7 @@
|
||||
<Compile Include="FileFormats\Effects\PTCL_WiiU.cs" />
|
||||
<Compile Include="FileFormats\Font\BFFNT.cs" />
|
||||
<Compile Include="FileFormats\Audio\Archives\BFGRP.cs" />
|
||||
<Compile Include="FileFormats\Font\BffntCharSet2Xlor.cs" />
|
||||
<Compile Include="FileFormats\Font\BFTTF.cs" />
|
||||
<Compile Include="FileFormats\Texture\TPL.cs" />
|
||||
<Compile Include="FileFormats\Rom\GCDisk.cs" />
|
||||
|
@ -237,6 +237,9 @@ namespace Toolbox.Library
|
||||
case "AlwaysCompressOnSave":
|
||||
bool.TryParse(node.InnerText, out Runtime.AlwaysCompressOnSave);
|
||||
break;
|
||||
case "CustomPicureBoxBGColor":
|
||||
TryParseHexColor(node, ref Runtime.CustomPicureBoxBGColor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -379,6 +382,7 @@ namespace Toolbox.Library
|
||||
PathsNode.AppendChild(createNode(doc, "UseComponetSelector", Runtime.ImageEditor.UseComponetSelector.ToString()));
|
||||
PathsNode.AppendChild(createNode(doc, "EnablePixelGrid", Runtime.ImageEditor.EnablePixelGrid.ToString()));
|
||||
PathsNode.AppendChild(createNode(doc, "EnableImageZoom", Runtime.ImageEditor.EnableImageZoom.ToString()));
|
||||
parentNode.AppendChild(createNode(doc, "CustomPicureBoxBGColor", ColorTranslator.ToHtml(Runtime.CustomPicureBoxBGColor)));
|
||||
}
|
||||
private static void AppendPathSettings(XmlDocument doc, XmlNode parentNode)
|
||||
{
|
||||
|
@ -71,6 +71,7 @@
|
||||
this.displayAlphaToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.useComponentSelectorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.previewCubemapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.previewCubemap3DToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.imageToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.generateMipmapsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.resizeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
@ -84,7 +85,6 @@
|
||||
this.hueToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.brightnessContrastToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.previewCubemap3DToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
|
||||
this.splitContainer1.Panel1.SuspendLayout();
|
||||
this.splitContainer1.SuspendLayout();
|
||||
@ -331,6 +331,7 @@
|
||||
this.imageBGComboBox.Size = new System.Drawing.Size(95, 21);
|
||||
this.imageBGComboBox.TabIndex = 2;
|
||||
this.imageBGComboBox.SelectedIndexChanged += new System.EventHandler(this.imageBGComboBox_SelectedIndexChanged);
|
||||
this.imageBGComboBox.MouseClick += new System.Windows.Forms.MouseEventHandler(this.imageBGComboBox_MouseClick);
|
||||
//
|
||||
// stContextMenuStrip1
|
||||
//
|
||||
@ -508,6 +509,13 @@
|
||||
this.previewCubemapToolStripMenuItem.Text = "Preview Cubemap";
|
||||
this.previewCubemapToolStripMenuItem.Click += new System.EventHandler(this.previewCubemapToolStripMenuItem_Click);
|
||||
//
|
||||
// previewCubemap3DToolStripMenuItem
|
||||
//
|
||||
this.previewCubemap3DToolStripMenuItem.Name = "previewCubemap3DToolStripMenuItem";
|
||||
this.previewCubemap3DToolStripMenuItem.Size = new System.Drawing.Size(205, 22);
|
||||
this.previewCubemap3DToolStripMenuItem.Text = "Preview Cubemap (3D)";
|
||||
this.previewCubemap3DToolStripMenuItem.Click += new System.EventHandler(this.previewCubemap3DToolStripMenuItem_Click);
|
||||
//
|
||||
// imageToolStripMenuItem
|
||||
//
|
||||
this.imageToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
@ -603,13 +611,6 @@
|
||||
this.brightnessContrastToolStripMenuItem.Size = new System.Drawing.Size(185, 22);
|
||||
this.brightnessContrastToolStripMenuItem.Text = "Brightness / Contrast";
|
||||
//
|
||||
// previewCubemap3DToolStripMenuItem
|
||||
//
|
||||
this.previewCubemap3DToolStripMenuItem.Name = "previewCubemap3DToolStripMenuItem";
|
||||
this.previewCubemap3DToolStripMenuItem.Size = new System.Drawing.Size(205, 22);
|
||||
this.previewCubemap3DToolStripMenuItem.Text = "Preview Cubemap (3D)";
|
||||
this.previewCubemap3DToolStripMenuItem.Click += new System.EventHandler(this.previewCubemap3DToolStripMenuItem_Click);
|
||||
//
|
||||
// ImageEditorBase
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
|
@ -246,13 +246,21 @@ namespace Toolbox.Library.Forms
|
||||
case Runtime.PictureBoxBG.Black:
|
||||
pictureBoxCustom1.GridDisplayMode = Cyotek.Windows.Forms.ImageBoxGridDisplayMode.None;
|
||||
pictureBoxCustom1.BackColor = Color.Black;
|
||||
imageBGComboBox.BackColor =FormThemes.BaseTheme.ComboBoxBackColor;
|
||||
break;
|
||||
case Runtime.PictureBoxBG.White:
|
||||
pictureBoxCustom1.GridDisplayMode = Cyotek.Windows.Forms.ImageBoxGridDisplayMode.None;
|
||||
pictureBoxCustom1.BackColor = Color.White;
|
||||
imageBGComboBox.BackColor = FormThemes.BaseTheme.ComboBoxBackColor;
|
||||
break;
|
||||
case Runtime.PictureBoxBG.Checkerboard:
|
||||
pictureBoxCustom1.GridDisplayMode = Cyotek.Windows.Forms.ImageBoxGridDisplayMode.Client;
|
||||
imageBGComboBox.BackColor = FormThemes.BaseTheme.ComboBoxBackColor;
|
||||
break;
|
||||
case Runtime.PictureBoxBG.Custom:
|
||||
pictureBoxCustom1.GridDisplayMode = Cyotek.Windows.Forms.ImageBoxGridDisplayMode.None;
|
||||
pictureBoxCustom1.BackColor = Runtime.CustomPicureBoxBGColor;
|
||||
imageBGComboBox.BackColor = Runtime.CustomPicureBoxBGColor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1298,5 +1306,19 @@ namespace Toolbox.Library.Forms
|
||||
viewer.LoadTexture(ActiveTexture);
|
||||
viewer.Show();
|
||||
}
|
||||
|
||||
private void imageBGComboBox_MouseClick(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (Runtime.pictureBoxStyle == Runtime.PictureBoxBG.Custom)
|
||||
{
|
||||
ColorDialog dlg = new ColorDialog();
|
||||
dlg.Color = Runtime.CustomPicureBoxBGColor;
|
||||
if (dlg.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
Runtime.CustomPicureBoxBGColor = dlg.Color;
|
||||
UpdateBackgroundImage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,6 +138,8 @@ namespace Toolbox.Library
|
||||
public static bool EnableVersionCheck = true;
|
||||
public static bool EnablePBR = true;
|
||||
|
||||
public static Color CustomPicureBoxBGColor = Color.DarkCyan;
|
||||
|
||||
public static bool enableVSync = false;
|
||||
public static float floorSize = 30f;
|
||||
public static Color floorColor = Color.Gray;
|
||||
@ -222,6 +224,7 @@ namespace Toolbox.Library
|
||||
Checkerboard,
|
||||
Black,
|
||||
White,
|
||||
Custom,
|
||||
}
|
||||
|
||||
public enum CameraMovement
|
||||
|
@ -183,6 +183,8 @@ namespace Toolbox.Library
|
||||
Palette Palette = new Palette();
|
||||
Palette.Load(PaletteData);
|
||||
|
||||
Console.WriteLine($"Decoding GC {FormatGC}");
|
||||
|
||||
return DecodeData(new FileReader(ImageData), width, height, FormatGC, Palette, PalleteFormatGC);
|
||||
}
|
||||
|
||||
|
@ -294,6 +294,10 @@
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BrawlboxHelper\BrawlboxHelper.csproj">
|
||||
<Project>{fa690685-3370-44d5-b138-f538c8d4c2a3}</Project>
|
||||
<Name>BrawlboxHelper</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Switch_Toolbox_Library\Toolbox_Library.csproj">
|
||||
<Project>{96820047-2a39-4e5a-bfa4-e84fff5c66cf}</Project>
|
||||
<Name>Toolbox_Library</Name>
|
||||
|
Loading…
x
Reference in New Issue
Block a user