1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . IO ;
4
+ using System . Linq ;
5
+ using System . Text ;
6
+ using System . Text . RegularExpressions ;
7
+ using System . Windows . Forms ;
8
+
9
+ namespace CelesteStudio ;
10
+
11
+ internal static class IntegrateReadFiles {
12
+ public static void Generate ( ) {
13
+ string mainFilePath = Studio . Instance . richText . CurrentFileName ;
14
+ if ( ! File . Exists ( mainFilePath ) ) {
15
+ MessageBox . Show ( mainFilePath , "Opened file does not exist" ) ;
16
+ return ;
17
+ }
18
+
19
+ string saveFilePath = ShowSaveDialog ( mainFilePath ) ;
20
+ if ( saveFilePath == null ) {
21
+ return ;
22
+ }
23
+
24
+ try {
25
+ string integratedText = ReadAllFiles ( mainFilePath ) ;
26
+ File . WriteAllText ( saveFilePath , integratedText ) ;
27
+ Studio . Instance . OpenFile ( saveFilePath ) ;
28
+ } catch ( ReadFileNotExistException e ) {
29
+ MessageBox . Show ( e . Message , "Read file does not exist" ) ;
30
+ }
31
+ }
32
+
33
+ private static string ShowSaveDialog ( string filePath ) {
34
+ using SaveFileDialog dialog = new ( ) ;
35
+ dialog . DefaultExt = ".tas" ;
36
+ dialog . AddExtension = true ;
37
+ dialog . Filter = "TAS|*.tas" ;
38
+ dialog . FilterIndex = 0 ;
39
+ dialog . InitialDirectory = Path . GetDirectoryName ( filePath ) ;
40
+ dialog . FileName = Path . GetFileNameWithoutExtension ( filePath ) + "_Integrated.tas" ;
41
+
42
+ if ( dialog . ShowDialog ( ) == DialogResult . OK ) {
43
+ return dialog . FileName ;
44
+ } else {
45
+ return null ;
46
+ }
47
+ }
48
+
49
+ private static string ReadAllFiles ( string filePath , IEnumerable < string > lines = null ) {
50
+ StringBuilder result = new ( ) ;
51
+ lines ??= File . ReadLines ( filePath ) ;
52
+ foreach ( string lineText in lines ) {
53
+ if ( TryParseReadCommand ( filePath , lineText , out string readText ) ) {
54
+ result . AppendLine ( $ "#{ lineText . Trim ( ) } ") ;
55
+ result . AppendLine ( readText ) ;
56
+ } else {
57
+ result . AppendLine ( lineText ) ;
58
+ }
59
+ }
60
+
61
+ return result . ToString ( ) ;
62
+ }
63
+
64
+ private static bool TryParseReadCommand ( string filePath , string readCommand , out string readText ) {
65
+ readText = null ;
66
+ readCommand = readCommand . Trim ( ) ;
67
+ if ( ! readCommand . StartsWith ( "read" , StringComparison . InvariantCultureIgnoreCase ) ) {
68
+ return false ;
69
+ }
70
+
71
+ Regex spaceRegex = new ( @"^[^,]+?\s+[^,]" ) ;
72
+ string [ ] args = spaceRegex . IsMatch ( readCommand ) ? readCommand . Split ( ) : readCommand . Split ( ',' ) ;
73
+ args = args . Select ( text => text . Trim ( ) ) . ToArray ( ) ;
74
+ if ( ! args [ 0 ] . Equals ( "read" , StringComparison . InvariantCultureIgnoreCase ) || args . Length < 2 ) {
75
+ return false ;
76
+ }
77
+
78
+ string readFilePath = args [ 1 ] ;
79
+ string fileDirectory = Path . GetDirectoryName ( filePath ) ;
80
+ readFilePath = FindReadFile ( filePath , fileDirectory , readFilePath ) ;
81
+
82
+ if ( ! File . Exists ( readFilePath ) ) {
83
+ // for compatibility with tas files downloaded from discord
84
+ // discord will replace spaces in the file name with underscores
85
+ readFilePath = args [ 1 ] . Replace ( " " , "_" ) ;
86
+ readFilePath = FindReadFile ( filePath , fileDirectory , readFilePath ) ;
87
+ }
88
+
89
+ if ( ! File . Exists ( readFilePath ) ) {
90
+ throw new ReadFileNotExistException ( readCommand , filePath ) ;
91
+ }
92
+
93
+ int startLine = 0 ;
94
+ int endLine = int . MaxValue - 1 ;
95
+
96
+ if ( args . Length >= 3 ) {
97
+ startLine = GetLineNumber ( readFilePath , args [ 2 ] ) ;
98
+ }
99
+
100
+ if ( args . Length >= 4 ) {
101
+ endLine = GetLineNumber ( readFilePath , args [ 3 ] ) ;
102
+ }
103
+
104
+ readText = ReadAllFiles ( filePath , File . ReadLines ( readFilePath ) . Take ( endLine + 1 ) . Skip ( startLine ) ) ;
105
+ return true ;
106
+ }
107
+
108
+ private static string FindReadFile ( string filePath , string fileDirectory , string readFilePath ) {
109
+ // Check for full and shortened Read versions
110
+ if ( fileDirectory != null ) {
111
+ // Path.Combine can handle the case when filePath is an absolute path
112
+ string absoluteOrRelativePath = Path . Combine ( fileDirectory , readFilePath ) ;
113
+ if ( File . Exists ( absoluteOrRelativePath ) && absoluteOrRelativePath != filePath ) {
114
+ readFilePath = absoluteOrRelativePath ;
115
+ } else if ( Directory . GetParent ( absoluteOrRelativePath ) is { } directoryInfo && Directory . Exists ( directoryInfo . ToString ( ) ) ) {
116
+ string [ ] files = Directory . GetFiles ( directoryInfo . ToString ( ) , $ "{ Path . GetFileName ( readFilePath ) } *.tas") ;
117
+ if ( files . FirstOrDefault ( path => path != filePath ) is { } shortenedFilePath ) {
118
+ readFilePath = shortenedFilePath ;
119
+ }
120
+ }
121
+ }
122
+
123
+ return readFilePath ;
124
+ }
125
+
126
+ private static int GetLineNumber ( string path , string labelOrLineNumber ) {
127
+ if ( int . TryParse ( labelOrLineNumber , out int lineNumber ) ) {
128
+ return lineNumber - 1 ;
129
+ }
130
+
131
+ int currentLine = 0 ;
132
+ foreach ( string readLine in File . ReadLines ( path ) ) {
133
+ string line = readLine . Trim ( ) ;
134
+ if ( line == $ "#{ labelOrLineNumber } ") {
135
+ return currentLine ;
136
+ }
137
+
138
+ currentLine ++ ;
139
+ }
140
+
141
+ return 0 ;
142
+ }
143
+ }
144
+
145
+ class ReadFileNotExistException : Exception {
146
+ public ReadFileNotExistException ( string readCommand , string filePath ) : base ( $ "{ readCommand } \n { filePath } ") { }
147
+ }
0 commit comments