Skip to content
This repository was archived by the owner on Oct 12, 2025. It is now read-only.

Commit 50ee32d

Browse files
authored
[core] fix some images could not determine their size. (#820)
1 parent e6415e8 commit 50ee32d

File tree

4 files changed

+189
-63
lines changed

4 files changed

+189
-63
lines changed

Lagrange.Core/Internal/Service/Message/ImageGroupUploadService.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,17 @@ protected override bool Build(ImageGroupUploadEvent input, BotKeystore keystore,
2424
string md5 = input.Entity.ImageStream.Value.Md5(true);
2525
string sha1 = input.Entity.ImageStream.Value.Sha1(true);
2626

27-
var buffer = new byte[1024]; // parse image header
28-
int _ = input.Entity.ImageStream.Value.Read(buffer.AsSpan());
29-
var type = ImageResolver.Resolve(buffer, out var size);
27+
// var buffer = new byte[1024]; // parse image header
28+
// int _ = input.Entity.ImageStream.Value.Read(buffer.AsSpan());
29+
var type = ImageResolver.Resolve(input.Entity.ImageStream.Value, out var size);
3030
string imageExt = type switch
3131
{
3232
ImageFormat.Jpeg => ".jpg",
3333
ImageFormat.Png => ".png",
3434
ImageFormat.Gif => ".gif",
3535
ImageFormat.Webp => ".webp",
3636
ImageFormat.Bmp => ".bmp",
37-
ImageFormat.Tiff => ".tiff",
37+
// ImageFormat.Tiff => ".tiff",
3838
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
3939
};
4040
input.Entity.ImageStream.Value.Position = 0;

Lagrange.Core/Internal/Service/Message/ImageUploadService.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,17 @@ protected override bool Build(ImageUploadEvent input, BotKeystore keystore, BotA
2323
string md5 = input.Entity.ImageStream.Value.Md5(true);
2424
string sha1 = input.Entity.ImageStream.Value.Sha1(true);
2525

26-
var buffer = new byte[1024]; // parse image header
27-
int _ = input.Entity.ImageStream.Value.Read(buffer.AsSpan());
28-
var type = ImageResolver.Resolve(buffer, out var size);
26+
// var buffer = new byte[1024]; // parse image header
27+
// int _ = input.Entity.ImageStream.Value.Read(buffer.AsSpan());
28+
var type = ImageResolver.Resolve(input.Entity.ImageStream.Value, out var size);
2929
string imageExt = type switch
3030
{
3131
ImageFormat.Jpeg => ".jpg",
3232
ImageFormat.Png => ".png",
3333
ImageFormat.Gif => ".gif",
3434
ImageFormat.Webp => ".webp",
3535
ImageFormat.Bmp => ".bmp",
36-
ImageFormat.Tiff => ".tiff",
36+
// ImageFormat.Tiff => ".tiff",
3737
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
3838
};
3939
input.Entity.ImageStream.Value.Position = 0;
Lines changed: 173 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Buffers;
12
using System.Numerics;
23
using System.Runtime.CompilerServices;
34
using BitConverter = Lagrange.Core.Utility.Binary.BitConverter;
@@ -6,59 +7,192 @@ namespace Lagrange.Core.Utility;
67

78
internal static class ImageResolver
89
{
9-
public static ImageFormat Resolve(byte[] image, out Vector2 size)
10+
// GIF
11+
private static readonly byte[] GIF87A_0_6 = new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 };
12+
private static readonly byte[] GIF89A_0_6 = new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 };
13+
// JPEG
14+
private static readonly byte[] JPEG_0_2 = new byte[] { 0xFF, 0xD8 };
15+
// PNG
16+
private static readonly byte[] PNG_0_8 = new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
17+
// WEBP
18+
private static readonly byte[] RIFF_0_4 = new byte[] { 0x52, 0x49, 0x46, 0x46 };
19+
private static readonly byte[] WEBP_8_12 = new byte[] { 0x57, 0x45, 0x42, 0x50 };
20+
private static readonly byte[] VP8_12_16 = new byte[] { 0x56, 0x50, 0x38, 0x20 };
21+
private static readonly byte[] VP8L_12_16 = new byte[] { 0x56, 0x50, 0x38, 0x4C };
22+
private static readonly byte[] VP8X_12_16 = new byte[] { 0x56, 0x50, 0x38, 0x58 };
23+
// BMP
24+
private static readonly byte[] BMP_0_2 = new byte[] { 0x42, 0x4D };
25+
// TIFF
26+
private static readonly byte[] LLTIFF_0_2 = new byte[] { 0x49, 0x49 };
27+
private static readonly byte[] MMTIFF_0_2 = new byte[] { 0x4D, 0x4D };
28+
29+
public static ImageFormat Resolve(Stream image, out Vector2 size)
1030
{
11-
ReadOnlySpan<byte> readOnlySpan = image.AsSpan();
12-
if (readOnlySpan[..6].SequenceEqual(new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }) ||
13-
readOnlySpan[..6].SequenceEqual(new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 })) // GIF89a / GIF87a
14-
{
15-
size = new Vector2(BitConverter.ToUInt16(readOnlySpan[6..8]), BitConverter.ToUInt16(readOnlySpan[8..10]));
16-
return ImageFormat.Gif;
17-
}
31+
Span<byte> buffer = stackalloc byte[1024];
32+
int length = image.Read(buffer);
1833

19-
if (readOnlySpan[..2].SequenceEqual(new byte[] { 0xFF, 0xD8 })) // JPEG
20-
{
21-
size = Vector2.Zero;
22-
for (int i = 2; i < readOnlySpan.Length - 10; i++)
34+
{ // JPEG
35+
if (buffer[..2].SequenceEqual(JPEG_0_2))
2336
{
24-
if ((Unsafe.ReadUnaligned<ushort>(ref image[i]) & 0xFCFF) == 0xC0FF) // SOF0 ~ SOF3
37+
int read = 2;
38+
while (true)
2539
{
26-
size = new Vector2(BitConverter.ToUInt16(readOnlySpan[(i + 7)..(i + 9)], false), BitConverter.ToUInt16(readOnlySpan[(i + 5)..(i + 7)], false));
27-
break;
40+
int remaining = length - read;
41+
if (remaining < 9)
42+
{
43+
buffer[read..length].CopyTo(buffer[..remaining]);
44+
if ((length = image.Read(buffer[remaining..])) == 0)
45+
{
46+
size = Vector2.Zero;
47+
return ImageFormat.Unknown;
48+
}
49+
read = 0;
50+
51+
continue;
52+
}
53+
54+
if ((buffer[read + 1] & 0xFC) == 0xC0) // SOF0 - SOF3
55+
{
56+
size = new Vector2(BitConverter.ToUInt16(buffer[(read + 7)..(read + 9)], false), BitConverter.ToUInt16(buffer[(read + 5)..(read + 7)], false));
57+
return ImageFormat.Jpeg;
58+
}
59+
60+
int len = 2 + BitConverter.ToUInt16(buffer[(read + 2)..(read + 4)], false);
61+
if (len > remaining)
62+
{
63+
if (image.CanSeek)
64+
{
65+
image.Seek(len - remaining, SeekOrigin.Current);
66+
if ((length = image.Read(buffer)) == 0)
67+
{
68+
size = Vector2.Zero;
69+
return ImageFormat.Unknown;
70+
}
71+
}
72+
else
73+
{
74+
int skipped = remaining;
75+
while (skipped + 1024 > len)
76+
{
77+
if ((length = image.Read(buffer)) == 0)
78+
{
79+
size = Vector2.Zero;
80+
return ImageFormat.Unknown;
81+
}
82+
skipped += length;
83+
}
84+
85+
if (image.Read(buffer[..(len - skipped)]) == 0)
86+
{
87+
size = Vector2.Zero;
88+
return ImageFormat.Unknown;
89+
}
90+
91+
if ((length = image.Read(buffer)) == 0)
92+
{
93+
size = Vector2.Zero;
94+
return ImageFormat.Unknown;
95+
}
96+
}
97+
read = 0;
98+
99+
continue;
100+
}
101+
read += len;
28102
}
29103
}
30-
return ImageFormat.Jpeg;
31104
}
32105

33-
if (readOnlySpan[..8].SequenceEqual(new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A })) // PNG
34-
{
35-
size = new Vector2(BitConverter.ToUInt32(readOnlySpan[16..20], false), BitConverter.ToUInt32(readOnlySpan[20..24], false));
36-
return ImageFormat.Png;
106+
{ // GIF
107+
Span<byte> header = buffer[..6];
108+
if (header.SequenceEqual(GIF87A_0_6) || header.SequenceEqual(GIF89A_0_6))
109+
{
110+
size = new Vector2(BitConverter.ToUInt16(buffer[6..8]), BitConverter.ToUInt16(buffer[8..10]));
111+
return ImageFormat.Gif;
112+
}
37113
}
38114

39-
if (readOnlySpan[..4].SequenceEqual(new byte[] { 0x52, 0x49, 0x46, 0x46 }) && readOnlySpan[8..12].SequenceEqual(new byte[] { 0x57, 0x45, 0x42, 0x50 })) // RIFF WEBP
40-
{
41-
if (readOnlySpan[12..16].SequenceEqual(new byte[] { 0x56, 0x50, 0x38, 0x58 })) // VP8X
42-
size = new Vector2(BitConverter.ToUInt16(readOnlySpan[24..27]) + 1, BitConverter.ToUInt16(readOnlySpan[27..30]) + 1);
43-
else if (readOnlySpan[12..16].SequenceEqual(new byte[] { 0x56, 0x50, 0x38, 0x4C })) // VP8L
44-
size = new Vector2((BitConverter.ToInt32(readOnlySpan[21..25]) & 0x3FFF) + 1, ((BitConverter.ToInt32(readOnlySpan[21..25]) & 0xFFFC000) >> 0x0E) + 1);
45-
else // VP8
46-
size = new Vector2(BitConverter.ToUInt16(readOnlySpan[26..28]), BitConverter.ToUInt16(readOnlySpan[28..30]));
47-
return ImageFormat.Webp;
115+
{ // PNG
116+
if (buffer[..8].SequenceEqual(PNG_0_8))
117+
{
118+
size = new Vector2(BitConverter.ToUInt32(buffer[16..20], false), BitConverter.ToUInt32(buffer[20..24], false));
119+
return ImageFormat.Png;
120+
}
48121
}
49122

50-
if (readOnlySpan[..2].SequenceEqual(new byte[] { 0x42, 0x4D })) // BMP
51-
{
52-
size = new Vector2(BitConverter.ToUInt16(readOnlySpan[18..20]), BitConverter.ToUInt16(readOnlySpan[22..24]));
53-
return ImageFormat.Bmp;
123+
{ // BMP
124+
if (buffer[..2].SequenceEqual(BMP_0_2))
125+
{
126+
size = new Vector2(BitConverter.ToUInt16(buffer[18..20]), BitConverter.ToUInt16(buffer[22..24]));
127+
return ImageFormat.Bmp;
128+
}
54129
}
55130

56-
if (readOnlySpan[..2].SequenceEqual(new byte[] { 0x49, 0x49 }) || readOnlySpan[..2].SequenceEqual(new byte[] { 0x4D, 0x4D })) // TIFF
57-
{
58-
size = new Vector2(BitConverter.ToUInt16(readOnlySpan[18..20]), BitConverter.ToUInt16(readOnlySpan[30..32]));
59-
return ImageFormat.Tiff;
131+
{ // WEBP
132+
if (length < 30)
133+
{
134+
size = Vector2.Zero;
135+
return ImageFormat.Unknown;
136+
}
137+
138+
if (buffer[..4].SequenceEqual(RIFF_0_4) && buffer[8..12].SequenceEqual(WEBP_8_12)) // RIFF WEBP
139+
{
140+
Span<byte> fourCC = buffer[12..16];
141+
142+
// VP8
143+
if (fourCC.SequenceEqual(VP8_12_16))
144+
{
145+
size = new Vector2(BitConverter.ToUInt16(buffer[26..28]), BitConverter.ToUInt16(buffer[28..30]));
146+
return ImageFormat.Webp;
147+
}
148+
149+
// VP8L
150+
if (fourCC.SequenceEqual(VP8L_12_16))
151+
{
152+
size = new Vector2((BitConverter.ToInt32(buffer[21..25]) & 0x3FFF) + 1, ((BitConverter.ToInt32(buffer[21..25]) & 0xFFFC000) >> 0x0E) + 1);
153+
return ImageFormat.Webp;
154+
}
155+
156+
// VP8X
157+
if (fourCC.SequenceEqual(VP8X_12_16))
158+
{
159+
size = new Vector2(BitConverter.ToUInt16(buffer[24..27]), BitConverter.ToUInt16(buffer[27..30]));
160+
return ImageFormat.Webp;
161+
}
162+
}
60163
}
61164

165+
// { // TODO TIFF
166+
// bool ll = false;
167+
// if (ll = buffer[..2].SequenceEqual(LLTIFF_0_2) || buffer[..2].SequenceEqual(MMTIFF_0_2))
168+
// {
169+
// uint offset = BitConverter.ToUInt32(buffer[4..8], ll);
170+
// int read = (int)offset;
171+
// while (true)
172+
// {
173+
// int remaining = length - read;
174+
// if (remaining < 2)
175+
// {
176+
// buffer[read..length].CopyTo(buffer[..remaining]);
177+
// if ((length = image.Read(buffer[remaining..])) == 0)
178+
// {
179+
// size = Vector2.Zero;
180+
// return ImageFormat.Unknown;
181+
// }
182+
// read = 0;
183+
184+
// continue;
185+
// }
186+
187+
// int count = BitConverter.ToUInt16(buffer[read..2]);
188+
// for (int i = 0; i < count; i++)
189+
// {
190+
// // TODO: read fd
191+
// }
192+
// }
193+
// }
194+
// }
195+
62196
size = Vector2.Zero;
63197
return ImageFormat.Unknown;
64198
}
@@ -71,6 +205,6 @@ internal enum ImageFormat : uint
71205
Jpeg = 1000,
72206
Gif = 2000,
73207
Webp = 1002,
74-
Bmp = 1005,
75-
Tiff
208+
Bmp = 1005
209+
// Tiff
76210
}

Lagrange.OneBot/Updater/GithubUpdater.cs

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,13 @@ public GithubUpdater()
6969

7070
public async Task GetConfig()
7171
{
72-
if(Environment.GetEnvironmentVariable("RUNNING_IN_DOCKER") == "true")
72+
if (Environment.GetEnvironmentVariable("RUNNING_IN_DOCKER") == "true")
7373
{
7474
Config = new UpdaterConfig();
7575
return;
7676
}
77-
78-
string configPath = Path.Combine(_executableDirectory, "AutoUpdaterConfig.json");
77+
78+
string configPath = "AutoUpdaterConfig.json";
7979

8080
if (File.Exists(configPath))
8181
{
@@ -88,30 +88,22 @@ public async Task GetConfig()
8888
}
8989
else
9090
{
91-
Console.WriteLine("Failed to parse config file, resetting to default.");
91+
Console.WriteLine("Failed to parse config file, fallback to default config.");
9292
var defaultConfig = new UpdaterConfig();
93-
await File.WriteAllTextAsync(
94-
configPath,
95-
JsonSerializer.Serialize(defaultConfig, _jsonSerializerOptions)
96-
);
9793
Config = defaultConfig;
9894
}
9995
}
10096
else
10197
{
10298
var defaultConfig = new UpdaterConfig();
103-
await File.WriteAllTextAsync(
104-
configPath,
105-
JsonSerializer.Serialize(defaultConfig, _jsonSerializerOptions)
106-
);
10799
Config = defaultConfig;
108100
}
109101
}
110102

111103
private async Task SetLastUpdateTime()
112104
{
113105
await File.WriteAllTextAsync(
114-
Path.Combine(_executableDirectory, "AutoUpdaterConfig.json"),
106+
"AutoUpdaterConfig.json",
115107
JsonSerializer.Serialize(Config, _jsonSerializerOptions)
116108
);
117109
}
@@ -243,7 +235,7 @@ goto waitloop
243235
xcopy /y /q ""{sourceDirectory}\\*"" ""{_executableDirectory}\\*"" /s /i
244236
245237
echo Update completed, starting new process...
246-
start "" "%targetPath%"
238+
cd "{Environment.CurrentDirectory}" && start "" "%targetPath%"
247239
248240
""";
249241
string batFilePath = Path.Combine(tempDirectory, "update.bat");
@@ -272,7 +264,7 @@ sleep 1
272264
cp "$sourcePath" "$targetPath"
273265
274266
echo Update completed, starting new process...
275-
exec "$targetPath"
267+
cd {Environment.CurrentDirectory} exec "$targetPath"
276268
277269
""";
278270
string shellFilePath = Path.Combine(tempDirectory, "update.sh");
@@ -309,7 +301,7 @@ sleep 1
309301
cp "$dylibSourcePath" "$targetDylibPath"
310302
311303
echo Update completed, starting new process...
312-
exec "$targetPath"
304+
cd "{Environment.CurrentDirectory}" && exec "$targetPath"
313305
314306
""";
315307
string shellFilePath = Path.Combine(tempDirectory, "update.sh");

0 commit comments

Comments
 (0)